summaryrefslogtreecommitdiffstats
path: root/ansible_collections/containers/podman/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-26 06:22:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-26 06:22:20 +0000
commit18bd2207b6c1977e99a93673a7be099e23f0f547 (patch)
tree40fd9e5913462a88be6ba24be6953383c5b39874 /ansible_collections/containers/podman/plugins
parentReleasing progress-linux version 10.0.1+dfsg-1~progress7.99u1. (diff)
downloadansible-18bd2207b6c1977e99a93673a7be099e23f0f547.tar.xz
ansible-18bd2207b6c1977e99a93673a7be099e23f0f547.zip
Merging upstream version 10.1.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/containers/podman/plugins')
-rw-r--r--ansible_collections/containers/podman/plugins/module_utils/podman/common.py85
-rw-r--r--ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py998
-rw-r--r--ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py406
-rw-r--r--ansible_collections/containers/podman/plugins/module_utils/podman/quadlet.py111
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_container.py196
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_image.py187
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_network.py190
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_pod.py128
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_search.py131
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_secret.py84
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_volume.py54
11 files changed, 1789 insertions, 781 deletions
diff --git a/ansible_collections/containers/podman/plugins/module_utils/podman/common.py b/ansible_collections/containers/podman/plugins/module_utils/podman/common.py
index cbb6b080e..aac7b60eb 100644
--- a/ansible_collections/containers/podman/plugins/module_utils/podman/common.py
+++ b/ansible_collections/containers/podman/plugins/module_utils/podman/common.py
@@ -337,3 +337,88 @@ def get_podman_version(module, fail=True):
(executable, err))
return None
return out.split("version")[1].strip()
+
+
+def createcommand(argument, info_config, boolean_type=False):
+ """Returns list of values for given argument from CreateCommand
+ from Podman container inspect output.
+
+ Args:
+ argument (str): argument name
+ info_config (dict): dictionary with container info
+ boolean_type (bool): if True, then argument is boolean type
+
+ Returns:
+
+ all_values: list of values for given argument from createcommand
+ """
+ if "createcommand" not in info_config:
+ return []
+ cr_com = info_config["createcommand"]
+ argument_values = ARGUMENTS_OPTS_DICT.get(argument, [argument])
+ all_values = []
+ for arg in argument_values:
+ for ind, cr_opt in enumerate(cr_com):
+ if arg == cr_opt:
+ if boolean_type:
+ # This is a boolean argument and doesn't have value
+ return [True]
+ if not cr_com[ind + 1].startswith("-"):
+ # This is a key=value argument
+ all_values.append(cr_com[ind + 1])
+ else:
+ # This is also a false/true switching argument
+ return [True]
+ if cr_opt.startswith("%s=" % arg):
+ all_values.append(cr_opt.split("=", 1)[1])
+ return all_values
+
+
+def diff_generic(params, info_config, module_arg, cmd_arg, boolean_type=False):
+ """
+ Generic diff function for module arguments from CreateCommand
+ in Podman inspection output.
+
+ Args:
+ params (dict): module parameters
+ info_config (dict): dictionary with container info
+ module_arg (str): module argument name
+ cmd_arg (str): command line argument name
+ boolean_type (bool): if True, then argument is boolean type
+
+ Returns:
+ bool: True if there is a difference, False otherwise
+
+ """
+ before = createcommand(cmd_arg, info_config, boolean_type=boolean_type)
+ if before == []:
+ before = None
+ after = params[module_arg]
+ if boolean_type and (before, after) in [(None, False), (False, None)]:
+ before, after = False, False
+ return before, after
+ if before is None and after is None:
+ return before, after
+ if after is not None:
+ if isinstance(after, list):
+ after = ",".join(sorted([str(i).lower() for i in after]))
+ if before:
+ before = ",".join(sorted([str(i).lower() for i in before]))
+ elif isinstance(after, dict):
+ after = ",".join(sorted(
+ [str(k).lower() + "=" + str(v).lower() for k, v in after.items() if v is not None]))
+ if before:
+ before = ",".join(sorted([j.lower() for j in before]))
+ elif isinstance(after, bool):
+ after = str(after).capitalize()
+ if before is not None:
+ before = str(before[0]).capitalize()
+ elif isinstance(after, int):
+ after = str(after)
+ if before is not None:
+ before = str(before[0])
+ else:
+ before = before[0] if before else None
+ else:
+ before = ",".join(sorted(before)) if len(before) > 1 else before[0]
+ return before, after
diff --git a/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py b/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py
index bf42ffdee..76458f144 100644
--- a/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py
+++ b/ansible_collections/containers/podman/plugins/module_utils/podman/podman_container_lib.py
@@ -8,8 +8,8 @@ from ansible_collections.containers.podman.plugins.module_utils.podman.common im
from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
from ansible_collections.containers.podman.plugins.module_utils.podman.common import generate_systemd
from ansible_collections.containers.podman.plugins.module_utils.podman.common import delete_systemd
-from ansible_collections.containers.podman.plugins.module_utils.podman.common import normalize_signal
-from ansible_collections.containers.podman.plugins.module_utils.podman.common import ARGUMENTS_OPTS_DICT
+from ansible_collections.containers.podman.plugins.module_utils.podman.common import diff_generic
+from ansible_collections.containers.podman.plugins.module_utils.podman.common import createcommand
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ContainerQuadlet
@@ -23,15 +23,18 @@ ARGUMENTS_SPEC_CONTAINER = dict(
'absent', 'present', 'stopped', 'started', 'created', 'quadlet']),
image=dict(type='str'),
annotation=dict(type='dict'),
+ arch=dict(type='str'),
attach=dict(type='list', elements='str', choices=['stdout', 'stderr', 'stdin']),
authfile=dict(type='path'),
blkio_weight=dict(type='int'),
blkio_weight_device=dict(type='dict'),
cap_add=dict(type='list', elements='str', aliases=['capabilities']),
cap_drop=dict(type='list', elements='str'),
+ cgroup_conf=dict(type='dict'),
cgroup_parent=dict(type='path'),
cgroupns=dict(type='str'),
cgroups=dict(type='str'),
+ chrootdirs=dict(type='str'),
cidfile=dict(type='path'),
cmd_args=dict(type='list', elements='str'),
conmon_pidfile=dict(type='path'),
@@ -44,6 +47,7 @@ ARGUMENTS_SPEC_CONTAINER = dict(
cpus=dict(type='str'),
cpuset_cpus=dict(type='str'),
cpuset_mems=dict(type='str'),
+ decryption_key=dict(type='str', no_log=False),
delete_depend=dict(type='bool'),
delete_time=dict(type='str'),
delete_volumes=dict(type='bool'),
@@ -51,6 +55,7 @@ ARGUMENTS_SPEC_CONTAINER = dict(
debug=dict(type='bool', default=False),
detach_keys=dict(type='str', no_log=False),
device=dict(type='list', elements='str'),
+ device_cgroup_rule=dict(type='str'),
device_read_bps=dict(type='list', elements='str'),
device_read_iops=dict(type='list', elements='str'),
device_write_bps=dict(type='list', elements='str'),
@@ -62,6 +67,7 @@ ARGUMENTS_SPEC_CONTAINER = dict(
env=dict(type='dict'),
env_file=dict(type='list', elements='path', aliases=['env_files']),
env_host=dict(type='bool'),
+ env_merge=dict(type='dict'),
etc_hosts=dict(type='dict', aliases=['add_hosts']),
expose=dict(type='list', elements='str', aliases=[
'exposed', 'exposed_ports']),
@@ -70,23 +76,33 @@ ARGUMENTS_SPEC_CONTAINER = dict(
force_delete=dict(type='bool', default=True),
generate_systemd=dict(type='dict', default={}),
gidmap=dict(type='list', elements='str'),
+ gpus=dict(type='str'),
group_add=dict(type='list', elements='str', aliases=['groups']),
- healthcheck=dict(type='str'),
- healthcheck_interval=dict(type='str'),
- healthcheck_retries=dict(type='int'),
- healthcheck_start_period=dict(type='str'),
- healthcheck_timeout=dict(type='str'),
+ group_entry=dict(type='str'),
+ healthcheck=dict(type='str', aliases=['health_cmd']),
+ healthcheck_interval=dict(type='str', aliases=['health_interval']),
+ healthcheck_retries=dict(type='int', aliases=['health_retries']),
+ healthcheck_start_period=dict(type='str', aliases=['health_start_period']),
+ health_startup_cmd=dict(type='str'),
+ health_startup_interval=dict(type='str'),
+ health_startup_retries=dict(type='int'),
+ health_startup_success=dict(type='int'),
+ health_startup_timeout=dict(type='str'),
+ healthcheck_timeout=dict(type='str', aliases=['health_timeout']),
healthcheck_failure_action=dict(type='str', choices=[
- 'none', 'kill', 'restart', 'stop']),
+ 'none', 'kill', 'restart', 'stop'], aliases=['health_on_failure']),
hooks_dir=dict(type='list', elements='str'),
hostname=dict(type='str'),
+ hostuser=dict(type='str'),
http_proxy=dict(type='bool'),
image_volume=dict(type='str', choices=['bind', 'tmpfs', 'ignore']),
image_strict=dict(type='bool', default=False),
init=dict(type='bool'),
+ init_ctr=dict(type='str', choices=['once', 'always']),
init_path=dict(type='str'),
interactive=dict(type='bool'),
ip=dict(type='str'),
+ ip6=dict(type='str'),
ipc=dict(type='str', aliases=['ipc_mode']),
kernel_memory=dict(type='str'),
label=dict(type='dict', aliases=['labels']),
@@ -108,32 +124,49 @@ ARGUMENTS_SPEC_CONTAINER = dict(
memory_swappiness=dict(type='int'),
mount=dict(type='list', elements='str', aliases=['mounts']),
network=dict(type='list', elements='str', aliases=['net', 'network_mode']),
- network_aliases=dict(type='list', elements='str'),
+ network_aliases=dict(type='list', elements='str', aliases=['network_alias']),
+ no_healthcheck=dict(type='bool'),
no_hosts=dict(type='bool'),
oom_kill_disable=dict(type='bool'),
oom_score_adj=dict(type='int'),
+ os=dict(type='str'),
+ passwd=dict(type='bool', no_log=False),
+ passwd_entry=dict(type='str', no_log=False),
+ personality=dict(type='str'),
pid=dict(type='str', aliases=['pid_mode']),
+ pid_file=dict(type='path'),
pids_limit=dict(type='str'),
+ platform=dict(type='str'),
pod=dict(type='str'),
+ pod_id_file=dict(type='path'),
+ preserve_fd=dict(type='list', elements='str'),
+ preserve_fds=dict(type='str'),
privileged=dict(type='bool'),
publish=dict(type='list', elements='str', aliases=[
'ports', 'published', 'published_ports']),
publish_all=dict(type='bool'),
+ pull=dict(type='str', choices=['always', 'missing', 'never', 'newer']),
quadlet_dir=dict(type='path'),
quadlet_filename=dict(type='str'),
quadlet_options=dict(type='list', elements='str'),
+ rdt_class=dict(type='str'),
read_only=dict(type='bool'),
read_only_tmpfs=dict(type='bool'),
recreate=dict(type='bool', default=False),
requires=dict(type='list', elements='str'),
restart_policy=dict(type='str'),
restart_time=dict(type='str'),
+ retry=dict(type='int'),
+ retry_delay=dict(type='str'),
rm=dict(type='bool', aliases=['remove', 'auto_remove']),
+ rmi=dict(type='bool'),
rootfs=dict(type='bool'),
+ seccomp_policy=dict(type='str'),
secrets=dict(type='list', elements='str', no_log=True),
sdnotify=dict(type='str'),
security_opt=dict(type='list', elements='str'),
shm_size=dict(type='str'),
+ shm_size_systemd=dict(type='str'),
sig_proxy=dict(type='bool'),
stop_signal=dict(type='int'),
stop_timeout=dict(type='int'),
@@ -142,14 +175,20 @@ ARGUMENTS_SPEC_CONTAINER = dict(
subuidname=dict(type='str'),
sysctl=dict(type='dict'),
systemd=dict(type='str'),
+ timeout=dict(type='int'),
timezone=dict(type='str'),
+ tls_verify=dict(type='bool'),
tmpfs=dict(type='dict'),
tty=dict(type='bool'),
uidmap=dict(type='list', elements='str'),
ulimit=dict(type='list', elements='str', aliases=['ulimits']),
+ umask=dict(type='str'),
+ unsetenv=dict(type='list', elements='str'),
+ unsetenv_all=dict(type='bool'),
user=dict(type='str'),
userns=dict(type='str', aliases=['userns_mode']),
uts=dict(type='str'),
+ variant=dict(type='str'),
volume=dict(type='list', elements='str', aliases=['volumes']),
volumes_from=dict(type='list', elements='str'),
workdir=dict(type='str', aliases=['working_dir'])
@@ -292,6 +331,9 @@ class PodmanModuleParams:
c += ['--annotation', '='.join(annotate)]
return c
+ def addparam_arch(self, c):
+ return c + ['--arch=%s' % self.params['arch']]
+
def addparam_attach(self, c):
for attach in self.params['attach']:
c += ['--attach=%s' % attach]
@@ -329,6 +371,14 @@ class PodmanModuleParams:
def addparam_cgroup_parent(self, c):
return c + ['--cgroup-parent', self.params['cgroup_parent']]
+ def addparam_cgroup_conf(self, c):
+ for cgroup in self.params['cgroup_conf'].items():
+ c += ['--cgroup-conf=%s' % '='.join([str(i) for i in cgroup])]
+ return c
+
+ def addparam_chrootdirs(self, c):
+ return c + ['--chrootdirs', self.params['chrootdirs']]
+
def addparam_cidfile(self, c):
return c + ['--cidfile', self.params['cidfile']]
@@ -359,6 +409,9 @@ class PodmanModuleParams:
def addparam_cpuset_mems(self, c):
return c + ['--cpuset-mems', self.params['cpuset_mems']]
+ def addparam_decryption_key(self, c):
+ return c + ['--decryption-key=%s' % self.params['decryption_key']]
+
def addparam_detach(self, c):
# Remove detach from create command and don't set if attach is true
if self.action == 'create' or self.params['attach']:
@@ -373,6 +426,9 @@ class PodmanModuleParams:
c += ['--device', dev]
return c
+ def addparam_device_cgroup_rule(self, c):
+ return c + ['--device-cgroup-rule=%s' % self.params['device_cgroup_rule']]
+
def addparam_device_read_bps(self, c):
for dev in self.params['device_read_bps']:
c += ['--device-read-bps', dev]
@@ -426,6 +482,13 @@ class PodmanModuleParams:
c += ['--add-host', ':'.join(host_ip)]
return c
+ def addparam_env_merge(self, c):
+ for env_merge in self.params['env_merge'].items():
+ c += ['--env-merge',
+ b"=".join([to_bytes(k, errors='surrogate_or_strict')
+ for k in env_merge])]
+ return c
+
def addparam_expose(self, c):
for exp in self.params['expose']:
c += ['--expose', exp]
@@ -436,11 +499,17 @@ class PodmanModuleParams:
c += ['--gidmap', gidmap]
return c
+ def addparam_gpus(self, c):
+ return c + ['--gpus', self.params['gpus']]
+
def addparam_group_add(self, c):
for g in self.params['group_add']:
c += ['--group-add', g]
return c
+ def addparam_group_entry(self, c):
+ return c + ['--group-entry', self.params['group_entry']]
+
def addparam_healthcheck(self, c):
return c + ['--healthcheck-command', self.params['healthcheck']]
@@ -456,10 +525,25 @@ class PodmanModuleParams:
return c + ['--healthcheck-start-period',
self.params['healthcheck_start_period']]
+ def addparam_health_startup_cmd(self, c):
+ return c + ['--health-startup-command', self.params['health_startup_cmd']]
+
+ def addparam_health_startup_interval(self, c):
+ return c + ['--health-startup-interval', self.params['health_startup_interval']]
+
def addparam_healthcheck_timeout(self, c):
return c + ['--healthcheck-timeout',
self.params['healthcheck_timeout']]
+ def addparam_health_startup_retries(self, c):
+ return c + ['--health-startup-retries', self.params['health_startup_retries']]
+
+ def addparam_health_startup_success(self, c):
+ return c + ['--health-startup-success', self.params['health_startup_success']]
+
+ def addparam_health_startup_timeout(self, c):
+ return c + ['--health-startup-timeout', self.params['health_startup_timeout']]
+
def addparam_healthcheck_failure_action(self, c):
return c + ['--health-on-failure',
self.params['healthcheck_failure_action']]
@@ -472,6 +556,9 @@ class PodmanModuleParams:
def addparam_hostname(self, c):
return c + ['--hostname', self.params['hostname']]
+ def addparam_hostuser(self, c):
+ return c + ['--hostuser', self.params['hostuser']]
+
def addparam_http_proxy(self, c):
return c + ['--http-proxy=%s' % self.params['http_proxy']]
@@ -486,12 +573,18 @@ class PodmanModuleParams:
def addparam_init_path(self, c):
return c + ['--init-path', self.params['init_path']]
+ def addparam_init_ctr(self, c):
+ return c + ['--init-ctr', self.params['init_ctr']]
+
def addparam_interactive(self, c):
return c + ['--interactive=%s' % self.params['interactive']]
def addparam_ip(self, c):
return c + ['--ip', self.params['ip']]
+ def addparam_ip6(self, c):
+ return c + ['--ip6', self.params['ip6']]
+
def addparam_ipc(self, c):
return c + ['--ipc', self.params['ipc']]
@@ -559,21 +652,57 @@ class PodmanModuleParams:
def addparam_no_hosts(self, c):
return c + ['--no-hosts=%s' % self.params['no_hosts']]
+ def addparam_no_healthcheck(self, c):
+ if self.params['no_healthcheck']:
+ c += ['--no-healthcheck']
+ return c
+
def addparam_oom_kill_disable(self, c):
return c + ['--oom-kill-disable=%s' % self.params['oom_kill_disable']]
def addparam_oom_score_adj(self, c):
return c + ['--oom-score-adj', self.params['oom_score_adj']]
+ def addparam_os(self, c):
+ return c + ['--os', self.params['os']]
+
+ def addparam_passwd(self, c):
+ if self.params['passwd']:
+ c += ['--passwd']
+ return c
+
+ def addparam_passwd_entry(self, c):
+ return c + ['--passwd-entry', self.params['passwd_entry']]
+
+ def addparam_personality(self, c):
+ return c + ['--personality', self.params['personality']]
+
def addparam_pid(self, c):
return c + ['--pid', self.params['pid']]
+ def addparam_pid_file(self, c):
+ return c + ['--pid-file', self.params['pid_file']]
+
def addparam_pids_limit(self, c):
return c + ['--pids-limit', self.params['pids_limit']]
+ def addparam_platform(self, c):
+ return c + ['--platform', self.params['platform']]
+
def addparam_pod(self, c):
return c + ['--pod', self.params['pod']]
+ def addparam_pod_id_file(self, c):
+ return c + ['--pod-id-file', self.params['pod_id_file']]
+
+ def addparam_preserve_fd(self, c):
+ for fd in self.params['preserve_fd']:
+ c += ['--preserve-fd', fd]
+ return c
+
+ def addparam_preserve_fds(self, c):
+ return c + ['--preserve-fds', self.params['preserve_fds']]
+
def addparam_privileged(self, c):
return c + ['--privileged=%s' % self.params['privileged']]
@@ -585,6 +714,12 @@ class PodmanModuleParams:
def addparam_publish_all(self, c):
return c + ['--publish-all=%s' % self.params['publish_all']]
+ def addparam_pull(self, c):
+ return c + ['--pull=%s' % self.params['pull']]
+
+ def addparam_rdt_class(self, c):
+ return c + ['--rdt-class', self.params['rdt_class']]
+
def addparam_read_only(self, c):
return c + ['--read-only=%s' % self.params['read_only']]
@@ -597,17 +732,31 @@ class PodmanModuleParams:
def addparam_restart_policy(self, c):
return c + ['--restart=%s' % self.params['restart_policy']]
+ def addparam_retry(self, c):
+ return c + ['--retry', self.params['retry']]
+
+ def addparam_retry_delay(self, c):
+ return c + ['--retry-delay', self.params['retry_delay']]
+
def addparam_rm(self, c):
if self.params['rm']:
c += ['--rm']
return c
+ def addparam_rmi(self, c):
+ if self.params['rmi']:
+ c += ['--rmi']
+ return c
+
def addparam_rootfs(self, c):
return c + ['--rootfs=%s' % self.params['rootfs']]
def addparam_sdnotify(self, c):
return c + ['--sdnotify=%s' % self.params['sdnotify']]
+ def addparam_seccomp_policy(self, c):
+ return c + ['--seccomp-policy', self.params['seccomp_policy']]
+
def addparam_secrets(self, c):
for secret in self.params['secrets']:
c += ['--secret', secret]
@@ -621,6 +770,9 @@ class PodmanModuleParams:
def addparam_shm_size(self, c):
return c + ['--shm-size', self.params['shm_size']]
+ def addparam_shm_size_systemd(self, c):
+ return c + ['--shm-size-systemd', self.params['shm_size_systemd']]
+
def addparam_sig_proxy(self, c):
return c + ['--sig-proxy=%s' % self.params['sig_proxy']]
@@ -646,14 +798,20 @@ class PodmanModuleParams:
def addparam_systemd(self, c):
return c + ['--systemd=%s' % str(self.params['systemd']).lower()]
+ def addparam_timeout(self, c):
+ return c + ['--timeout', self.params['timeout']]
+
+ def addparam_timezone(self, c):
+ return c + ['--tz=%s' % self.params['timezone']]
+
+ def addparam_tls_verify(self, c):
+ return c + ['--tls-verify=%s' % self.params['tls_verify']]
+
def addparam_tmpfs(self, c):
for tmpfs in self.params['tmpfs'].items():
c += ['--tmpfs', ':'.join(tmpfs)]
return c
- def addparam_timezone(self, c):
- return c + ['--tz=%s' % self.params['timezone']]
-
def addparam_tty(self, c):
return c + ['--tty=%s' % self.params['tty']]
@@ -667,6 +825,19 @@ class PodmanModuleParams:
c += ['--ulimit', u]
return c
+ def addparam_umask(self, c):
+ return c + ['--umask', self.params['umask']]
+
+ def addparam_unsetenv(self, c):
+ for unsetenv in self.params['unsetenv']:
+ c += ['--unsetenv', unsetenv]
+ return c
+
+ def addparam_unsetenv_all(self, c):
+ if self.params['unsetenv_all']:
+ c += ['--unsetenv-all']
+ return c
+
def addparam_user(self, c):
return c + ['--user', self.params['user']]
@@ -676,6 +847,9 @@ class PodmanModuleParams:
def addparam_uts(self, c):
return c + ['--uts', self.params['uts']]
+ def addparam_variant(self, c):
+ return c + ['--variant', self.params['variant']]
+
def addparam_volume(self, c):
for vol in self.params['volume']:
if vol:
@@ -700,42 +874,9 @@ class PodmanDefaults:
self.version = podman_version
self.image_info = image_info
self.defaults = {
- "blkio_weight": 0,
- "cgroups": "default",
- "cidfile": "",
- "cpus": 0.0,
- "cpu_shares": 0,
- "cpu_quota": 0,
- "cpu_period": 0,
- "cpu_rt_runtime": 0,
- "cpu_rt_period": 0,
- "cpuset_cpus": "",
- "cpuset_mems": "",
"detach": True,
- "device": [],
- "env_host": False,
- "etc_hosts": {},
- "group_add": [],
- "ipc": "",
- "kernelmemory": "0",
"log_level": "error",
- "memory": "0",
- "memory_swap": "0",
- "memory_reservation": "0",
- # "memory_swappiness": -1,
- "no_hosts": False,
- # libpod issue with networks in inspection
- "oom_score_adj": 0,
- "pid": "",
- "privileged": False,
- "read_only": False,
- "rm": False,
- "security_opt": [],
- "stop_signal": self.image_info.get('config', {}).get('stopsignal', "15"),
"tty": False,
- "user": self.image_info.get('user', ''),
- "workdir": self.image_info.get('config', {}).get('workingdir', '/'),
- "uts": "",
}
def default_dict(self):
@@ -744,17 +885,8 @@ class PodmanDefaults:
if (LooseVersion(self.version) >= LooseVersion('1.8.0')
and LooseVersion(self.version) < LooseVersion('1.9.0')):
self.defaults['cpu_shares'] = 1024
- if (LooseVersion(self.version) >= LooseVersion('2.0.0')):
- self.defaults['network'] = ["slirp4netns"]
- self.defaults['ipc'] = "private"
- self.defaults['uts'] = "private"
- self.defaults['pid'] = "private"
- if (LooseVersion(self.version) >= LooseVersion('5.0.0')):
- self.defaults['network'] = ["pasta"]
if (LooseVersion(self.version) >= LooseVersion('3.0.0')):
self.defaults['log_level'] = "warning"
- if (LooseVersion(self.version) >= LooseVersion('4.1.0')):
- self.defaults['ipc'] = "shareable"
return self.defaults
@@ -781,35 +913,6 @@ class PodmanContainerDiff:
params_with_defaults[p] = self.module_params[p]
return params_with_defaults
- def _createcommand(self, argument):
- """Returns list of values for given argument from CreateCommand
- from Podman container inspect output.
-
- Args:
- argument (str): argument name
-
- Returns:
-
- all_values: list of values for given argument from createcommand
- """
- if "createcommand" not in self.info["config"]:
- return []
- cr_com = self.info["config"]["createcommand"]
- argument_values = ARGUMENTS_OPTS_DICT.get(argument, [argument])
- all_values = []
- for arg in argument_values:
- for ind, cr_opt in enumerate(cr_com):
- if arg == cr_opt:
- # This is a key=value argument
- if not cr_com[ind + 1].startswith("-"):
- all_values.append(cr_com[ind + 1])
- else:
- # This is a false/true switching argument
- return [True]
- if cr_opt.startswith("%s=" % arg):
- all_values.append(cr_opt.split("=", 1)[1])
- return all_values
-
def _diff_update_and_compare(self, param_name, before, after):
if before != after:
self.diff['before'].update({param_name: before})
@@ -817,6 +920,24 @@ class PodmanContainerDiff:
return True
return False
+ def _diff_generic(self, module_arg, cmd_arg, boolean_type=False):
+ """
+ Generic diff function for module arguments from CreateCommand
+ in Podman inspection output.
+
+ Args:
+ module_arg (str): module argument name
+ cmd_arg (str): command line argument name
+ boolean_type (bool): if True, then argument is boolean type
+
+ Returns:
+ bool: True if there is a difference, False otherwise
+
+ """
+ info_config = self.info["config"]
+ before, after = diff_generic(self.params, info_config, module_arg, cmd_arg, boolean_type)
+ return self._diff_update_and_compare(module_arg, before, after)
+
def diffparam_annotation(self):
before = self.info['config']['annotations'] or {}
after = before.copy()
@@ -824,24 +945,17 @@ class PodmanContainerDiff:
after.update(self.params['annotation'])
return self._diff_update_and_compare('annotation', before, after)
- def diffparam_env_host(self):
- # It's impossible to get from inspect, recreate it if not default
- before = False
- after = self.params['env_host']
- return self._diff_update_and_compare('env_host', before, after)
+ def diffparam_arch(self):
+ return self._diff_generic('arch', '--arch')
+
+ def diffparam_authfile(self):
+ return self._diff_generic('authfile', '--authfile')
def diffparam_blkio_weight(self):
- before = self.info['hostconfig']['blkioweight']
- after = self.params['blkio_weight']
- return self._diff_update_and_compare('blkio_weight', before, after)
+ return self._diff_generic('blkio_weight', '--blkio-weight')
def diffparam_blkio_weight_device(self):
- before = self.info['hostconfig']['blkioweightdevice']
- if before == [] and self.module_params['blkio_weight_device'] is None:
- after = []
- else:
- after = self.params['blkio_weight_device']
- return self._diff_update_and_compare('blkio_weight_device', before, after)
+ return self._diff_generic('blkio_weight_device', '--blkio-weight-device')
def diffparam_cap_add(self):
before = self.info['effectivecaps'] or []
@@ -869,30 +983,27 @@ class PodmanContainerDiff:
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('cap_drop', before, after)
+ def diffparam_cgroup_conf(self):
+ return self._diff_generic('cgroup_conf', '--cgroup-conf')
+
def diffparam_cgroup_parent(self):
- before = self.info['hostconfig']['cgroupparent']
- after = self.params['cgroup_parent']
- if after is None:
- after = before
- return self._diff_update_and_compare('cgroup_parent', before, after)
-
- def diffparam_cgroups(self):
- # Cgroups output is not supported in all versions
- if 'cgroups' in self.info['hostconfig']:
- before = self.info['hostconfig']['cgroups']
- after = self.params['cgroups']
- return self._diff_update_and_compare('cgroups', before, after)
- return False
+ return self._diff_generic('cgroup_parent', '--cgroup-parent')
- def diffparam_cidfile(self):
- before = self.info['hostconfig']['containeridfile']
- after = self.params['cidfile']
- labels = self.info['config']['labels'] or {}
- # Ignore cidfile that is coming from systemd files
- # https://github.com/containers/ansible-podman-collections/issues/276
- if 'podman_systemd_unit' in labels:
- after = before
- return self._diff_update_and_compare('cidfile', before, after)
+ def diffparam_cgroupns(self):
+ return self._diff_generic('cgroupns', '--cgroupns')
+
+ # Disabling idemotency check for cgroups as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/775
+ # def diffparam_cgroups(self):
+ # return self._diff_generic('cgroups', '--cgroups')
+
+ def diffparam_chrootdirs(self):
+ return self._diff_generic('chrootdirs', '--chrootdirs')
+
+ # Disabling idemotency check for cidfile as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/775
+ # def diffparam_cidfile(self):
+ # return self._diff_generic('cidfile', '--cidfile')
def diffparam_command(self):
# TODO(sshnaidm): to inspect image to get the default command
@@ -905,107 +1016,73 @@ class PodmanContainerDiff:
return False
def diffparam_conmon_pidfile(self):
- before = self.info['conmonpidfile']
- if self.module_params['conmon_pidfile'] is None:
- after = before
- else:
- after = self.params['conmon_pidfile']
- return self._diff_update_and_compare('conmon_pidfile', before, after)
+ return self._diff_generic('conmon_pidfile', '--conmon-pidfile')
def diffparam_cpu_period(self):
- before = self.info['hostconfig']['cpuperiod']
- # if cpu_period left to default keep settings
- after = self.params['cpu_period'] or before
- return self._diff_update_and_compare('cpu_period', before, after)
+ return self._diff_generic('cpu_period', '--cpu-period')
def diffparam_cpu_quota(self):
- before = self.info['hostconfig']['cpuquota']
- # if cpu_quota left to default keep settings
- after = self.params['cpu_quota'] or before
- return self._diff_update_and_compare('cpu_quota', before, after)
+ return self._diff_generic('cpu_quota', '--cpu-quota')
def diffparam_cpu_rt_period(self):
- before = self.info['hostconfig']['cpurealtimeperiod']
- after = self.params['cpu_rt_period']
- return self._diff_update_and_compare('cpu_rt_period', before, after)
+ return self._diff_generic('cpu_rt_period', '--cpu-rt-period')
def diffparam_cpu_rt_runtime(self):
- before = self.info['hostconfig']['cpurealtimeruntime']
- after = self.params['cpu_rt_runtime']
- return self._diff_update_and_compare('cpu_rt_runtime', before, after)
+ return self._diff_generic('cpu_rt_runtime', '--cpu-rt-runtime')
def diffparam_cpu_shares(self):
- before = self.info['hostconfig']['cpushares']
- after = self.params['cpu_shares']
- return self._diff_update_and_compare('cpu_shares', before, after)
+ return self._diff_generic('cpu_shares', '--cpu-shares')
def diffparam_cpus(self):
- before = self.info['hostconfig']['nanocpus'] / 1000000000
- # if cpus left to default keep settings
- after = float(self.params['cpus'] or before)
- return self._diff_update_and_compare('cpus', before, after)
+ return self._diff_generic('cpus', '--cpus')
def diffparam_cpuset_cpus(self):
- before = self.info['hostconfig']['cpusetcpus']
- after = self.params['cpuset_cpus']
- return self._diff_update_and_compare('cpuset_cpus', before, after)
+ return self._diff_generic('cpuset_cpus', '--cpuset-cpus')
def diffparam_cpuset_mems(self):
- before = self.info['hostconfig']['cpusetmems']
- after = self.params['cpuset_mems']
- return self._diff_update_and_compare('cpuset_mems', before, after)
+ return self._diff_generic('cpuset_mems', '--cpuset-mems')
+
+ def diffparam_decryption_key(self):
+ return self._diff_generic('decryption_key', '--decryption-key')
def diffparam_device(self):
- before = [":".join([i['pathonhost'], i['pathincontainer']])
- for i in self.info['hostconfig']['devices']]
- if not before and 'createcommand' in self.info['config']:
- before = [i.lower() for i in self._createcommand('--device')]
- before = [":".join((i, i))
- if len(i.split(":")) == 1 else i for i in before]
- after = [":".join(i.split(":")[:2]) for i in self.params['device']]
- after = [":".join((i, i))
- if len(i.split(":")) == 1 else i for i in after]
- before, after = [i.lower() for i in before], [i.lower() for i in after]
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('devices', before, after)
+ return self._diff_generic('device', '--device')
+
+ def diffparam_device_cgroup_rule(self):
+ return self._diff_generic('device_cgroup_rule', '--device-cgroup-rule')
def diffparam_device_read_bps(self):
- before = self.info['hostconfig']['blkiodevicereadbps'] or []
- before = ["%s:%s" % (i['path'], i['rate']) for i in before]
- after = self.params['device_read_bps'] or []
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('device_read_bps', before, after)
+ return self._diff_generic('device_read_bps', '--device-read-bps')
def diffparam_device_read_iops(self):
- before = self.info['hostconfig']['blkiodevicereadiops'] or []
- before = ["%s:%s" % (i['path'], i['rate']) for i in before]
- after = self.params['device_read_iops'] or []
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('device_read_iops', before, after)
+ return self._diff_generic('device_read_iops', '--device-read-iops')
def diffparam_device_write_bps(self):
- before = self.info['hostconfig']['blkiodevicewritebps'] or []
- before = ["%s:%s" % (i['path'], i['rate']) for i in before]
- after = self.params['device_write_bps'] or []
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('device_write_bps', before, after)
+ return self._diff_generic('device_write_bps', '--device-write-bps')
def diffparam_device_write_iops(self):
- before = self.info['hostconfig']['blkiodevicewriteiops'] or []
- before = ["%s:%s" % (i['path'], i['rate']) for i in before]
- after = self.params['device_write_iops'] or []
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('device_write_iops', before, after)
+ return self._diff_generic('device_write_iops', '--device-write-iops')
+
+ def diffparam_dns(self):
+ return self._diff_generic('dns', '--dns')
+
+ def diffparam_dns_option(self):
+ return self._diff_generic('dns_option', '--dns-option')
+
+ def diffparam_dns_search(self):
+ return self._diff_generic('dns_search', '--dns-search')
- # Limited idempotency, it can't guess default values
def diffparam_env(self):
- env_before = self.info['config']['env'] or {}
- before = {i.split("=")[0]: "=".join(i.split("=")[1:])
- for i in env_before}
- after = before.copy()
- if self.params['env']:
- after.update({k: str(v) for k, v in self.params['env'].items()})
- return self._diff_update_and_compare('env', before, after)
+ return self._diff_generic('env', '--env')
+
+ def diffparam_env_file(self):
+ return self._diff_generic('env_file', '--env-file')
+
+ def diffparam_env_merge(self):
+ return self._diff_generic('env_merge', '--env-merge')
+
+ def diffparam_env_host(self):
+ return self._diff_generic('env_host', '--env-host')
def diffparam_etc_hosts(self):
if self.info['hostconfig']['extrahosts']:
@@ -1013,13 +1090,23 @@ class PodmanContainerDiff:
for i in self.info['hostconfig']['extrahosts']])
else:
before = {}
- after = self.params['etc_hosts']
+ after = self.params['etc_hosts'] or {}
return self._diff_update_and_compare('etc_hosts', before, after)
+ def diffparam_expose(self):
+ return self._diff_generic('expose', '--expose')
+
+ def diffparam_gidmap(self):
+ return self._diff_generic('gidmap', '--gidmap')
+
+ def diffparam_gpus(self):
+ return self._diff_generic('gpus', '--gpus')
+
def diffparam_group_add(self):
- before = self.info['hostconfig']['groupadd']
- after = self.params['group_add']
- return self._diff_update_and_compare('group_add', before, after)
+ return self._diff_generic('group_add', '--group-add')
+
+ def diffparam_group_entry(self):
+ return self._diff_generic('group_entry', '--group-entry')
# Healthcheck is only defined in container config if a healthcheck
# was configured; otherwise the config key isn't part of the config.
@@ -1041,11 +1128,44 @@ class PodmanContainerDiff:
after = self.params['healthcheck_failure_action'] or before
return self._diff_update_and_compare('healthcheckonfailureaction', before, after)
- # Because of hostname is random generated, this parameter has partial idempotency only.
+ def diffparam_healthcheck_interval(self):
+ return self._diff_generic('healthcheck_interval', '--healthcheck-interval')
+
+ def diffparam_healthcheck_retries(self):
+ return self._diff_generic('healthcheck_retries', '--healthcheck-retries')
+
+ def diffparam_healthcheck_start_period(self):
+ return self._diff_generic('healthcheck_start_period', '--healthcheck-start-period')
+
+ def diffparam_health_startup_cmd(self):
+ return self._diff_generic('health_startup_cmd', '--health-startup-cmd')
+
+ def diffparam_health_startup_interval(self):
+ return self._diff_generic('health_startup_interval', '--health-startup-interval')
+
+ def diffparam_health_startup_retries(self):
+ return self._diff_generic('health_startup_retries', '--health-startup-retries')
+
+ def diffparam_health_startup_success(self):
+ return self._diff_generic('health_startup_success', '--health-startup-success')
+
+ def diffparam_health_startup_timeout(self):
+ return self._diff_generic('health_startup_timeout', '--health-startup-timeout')
+
+ def diffparam_healthcheck_timeout(self):
+ return self._diff_generic('healthcheck_timeout', '--healthcheck-timeout')
+
+ def diffparam_hooks_dir(self):
+ return self._diff_generic('hooks_dir', '--hooks-dir')
+
def diffparam_hostname(self):
- before = self.info['config']['hostname']
- after = self.params['hostname'] or before
- return self._diff_update_and_compare('hostname', before, after)
+ return self._diff_generic('hostname', '--hostname')
+
+ def diffparam_hostuser(self):
+ return self._diff_generic('hostuser', '--hostuser')
+
+ def diffparam_http_proxy(self):
+ return self._diff_generic('http_proxy', '--http-proxy')
def diffparam_image(self):
before_id = self.info['image'] or self.info['rootfs']
@@ -1066,12 +1186,29 @@ class PodmanContainerDiff:
return self._diff_update_and_compare('image', before_id, after_id)
return self._diff_update_and_compare('image', before, after)
+ def diffparam_image_volume(self):
+ return self._diff_generic('image_volume', '--image-volume')
+
+ def diffparam_init(self):
+ return self._diff_generic('init', '--init', boolean_type=True)
+
+ def diffparam_init_ctr(self):
+ return self._diff_generic('init_ctr', '--init-ctr')
+
+ def diffparam_init_path(self):
+ return self._diff_generic('init_path', '--init-path')
+
+ def diffparam_interactive(self):
+ return self._diff_generic('interactive', '--interactive')
+
+ def diffparam_ip(self):
+ return self._diff_generic('ip', '--ip')
+
+ def diffparam_ip6(self):
+ return self._diff_generic('ip6', '--ip6')
+
def diffparam_ipc(self):
- before = self.info['hostconfig']['ipcmode']
- after = self.params['ipc']
- if self.params['pod'] and not self.module_params['ipc']:
- after = before
- return self._diff_update_and_compare('ipc', before, after)
+ return self._diff_generic('ipc', '--ipc')
def diffparam_label(self):
before = self.info['config']['labels'] or {}
@@ -1088,282 +1225,208 @@ class PodmanContainerDiff:
before.pop('podman_systemd_unit', None)
return self._diff_update_and_compare('label', before, after)
+ def diffparam_label_file(self):
+ return self._diff_generic('label_file', '--label-file')
+
def diffparam_log_driver(self):
- before = self.info['hostconfig']['logconfig']['type']
- if self.module_params['log_driver'] is not None:
- after = self.params['log_driver']
- else:
- after = before
- return self._diff_update_and_compare('log_driver', before, after)
+ return self._diff_generic('log_driver', '--log-driver')
- # Parameter has limited idempotency, unable to guess the default log_path
def diffparam_log_opt(self):
- before, after = {}, {}
-
- # Log path
- path_before = None
- if 'logpath' in self.info:
- path_before = self.info['logpath']
- # For Podman v3
- if ('logconfig' in self.info['hostconfig'] and
- 'path' in self.info['hostconfig']['logconfig']):
- path_before = self.info['hostconfig']['logconfig']['path']
- if path_before is not None:
- if (self.module_params['log_opt'] and
- 'path' in self.module_params['log_opt'] and
- self.module_params['log_opt']['path'] is not None):
- path_after = self.params['log_opt']['path']
- else:
- path_after = path_before
- if path_before != path_after:
- before.update({'log-path': path_before})
- after.update({'log-path': path_after})
-
- # Log tag
- tag_before = None
- if 'logtag' in self.info:
- tag_before = self.info['logtag']
- # For Podman v3
- if ('logconfig' in self.info['hostconfig'] and
- 'tag' in self.info['hostconfig']['logconfig']):
- tag_before = self.info['hostconfig']['logconfig']['tag']
- if tag_before is not None:
- if (self.module_params['log_opt'] and
- 'tag' in self.module_params['log_opt'] and
- self.module_params['log_opt']['tag'] is not None):
- tag_after = self.params['log_opt']['tag']
- else:
- tag_after = ''
- if tag_before != tag_after:
- before.update({'log-tag': tag_before})
- after.update({'log-tag': tag_after})
-
- # Log size
- # For Podman v3
- # size_before = '0B'
- # TODO(sshnaidm): integrate B/KB/MB/GB calculation for sizes
- # if ('logconfig' in self.info['hostconfig'] and
- # 'size' in self.info['hostconfig']['logconfig']):
- # size_before = self.info['hostconfig']['logconfig']['size']
- # if size_before != '0B':
- # if (self.module_params['log_opt'] and
- # 'max_size' in self.module_params['log_opt'] and
- # self.module_params['log_opt']['max_size'] is not None):
- # size_after = self.params['log_opt']['max_size']
- # else:
- # size_after = ''
- # if size_before != size_after:
- # before.update({'log-size': size_before})
- # after.update({'log-size': size_after})
-
- return self._diff_update_and_compare('log_opt', before, after)
+ return self._diff_generic('log_opt', '--log-opt')
def diffparam_mac_address(self):
- before = str(self.info['networksettings']['macaddress'])
- if not before and self.info['networksettings'].get('networks'):
- nets = self.info['networksettings']['networks']
- macs = [
- nets[i]["macaddress"] for i in nets if nets[i]["macaddress"]]
- if macs:
- before = macs[0]
- if not before and 'createcommand' in self.info['config']:
- before = [i.lower() for i in self._createcommand('--mac-address')]
- before = before[0] if before else ''
- if self.module_params['mac_address'] is not None:
- after = self.params['mac_address']
- else:
- after = before
- return self._diff_update_and_compare('mac_address', before, after)
+ return self._diff_generic('mac_address', '--mac-address')
+
+ def diffparam_memory(self):
+ return self._diff_generic('memory', '--memory')
+
+ def diffparam_memory_reservation(self):
+ return self._diff_generic('memory_reservation', '--memory-reservation')
+
+ def diffparam_memory_swap(self):
+ return self._diff_generic('memory_swap', '--memory-swap')
+
+ def diffparam_memory_swappiness(self):
+ return self._diff_generic('memory_swappiness', '--memory-swappiness')
+
+ def diffparam_mount(self):
+ return self._diff_generic('mount', '--mount')
def diffparam_network(self):
- net_mode_before = self.info['hostconfig']['networkmode']
- net_mode_after = ''
- before = list(self.info['networksettings'].get('networks', {}))
- # Remove default 'podman' network in v3 for comparison
- if before == ['podman']:
- before = []
- # Special case for options for slirp4netns rootless networking from v2
- if net_mode_before == 'slirp4netns' and 'createcommand' in self.info['config']:
- cr_net = [i.lower() for i in self._createcommand('--network')]
- for cr_net_opt in cr_net:
- if 'slirp4netns:' in cr_net_opt:
- before = [cr_net_opt]
- if net_mode_before == 'pasta':
- cr_net = [i.lower() for i in self._createcommand('--network')]
- for cr_net_opt in cr_net:
- if 'pasta:' in cr_net_opt:
- before = [cr_net_opt]
- after = self.params['network'] or []
- after = [i.lower() for i in after]
- # If container is in pod and no networks are provided
- if not self.module_params['network'] and self.params['pod']:
- after = before
- return self._diff_update_and_compare('network', before, after)
- # Check special network modes
- if after in [['bridge'], ['host'], ['slirp4netns'], ['none'], ['pasta']]:
- net_mode_after = after[0]
- # If changes are only for network mode and container has no networks
- if net_mode_after and not before:
- # Remove differences between v1 and v2
- net_mode_after = net_mode_after.replace('bridge', 'default')
- net_mode_after = net_mode_after.replace('slirp4netns', 'default')
- net_mode_after = net_mode_after.replace('pasta', 'default')
- net_mode_before = net_mode_before.replace('bridge', 'default')
- net_mode_before = net_mode_before.replace('slirp4netns', 'default')
- net_mode_before = net_mode_before.replace('pasta', 'default')
- return self._diff_update_and_compare('network', net_mode_before, net_mode_after)
- # If container is attached to network of a different container
- if "container" in net_mode_before:
- for netw in after:
- if "container" in netw:
- before = after = netw
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('network', before, after)
+ return self._diff_generic('network', '--network')
+
+ def diffparam_network_aliases(self):
+ return self._diff_generic('network_aliases', '--network-alias')
+
+ def diffparam_no_healthcheck(self):
+ return self._diff_generic('no_healthcheck', '--no-healthcheck', boolean_type=True)
+
+ def diffparam_no_hosts(self):
+ return self._diff_generic('no_hosts', '--no-hosts')
+
+ def diffparam_oom_kill_disable(self):
+ return self._diff_generic('oom_kill_disable', '--oom-kill-disable')
def diffparam_oom_score_adj(self):
- before = self.info['hostconfig']['oomscoreadj']
- after = self.params['oom_score_adj']
- return self._diff_update_and_compare('oom_score_adj', before, after)
+ return self._diff_generic('oom_score_adj', '--oom-score-adj')
- def diffparam_privileged(self):
- before = self.info['hostconfig']['privileged']
- after = self.params['privileged']
- return self._diff_update_and_compare('privileged', before, after)
+ def diffparam_os(self):
+ return self._diff_generic('os', '--os')
+
+ def diffparam_passwd(self):
+ return self._diff_generic('passwd', '--passwd', boolean_type=True)
+
+ def diffparam_passwd_entry(self):
+ return self._diff_generic('passwd_entry', '--passwd-entry')
+
+ def diffparam_personality(self):
+ return self._diff_generic('personality', '--personality')
def diffparam_pid(self):
- def get_container_id_by_name(name):
- rc, podman_inspect_info, err = self.module.run_command(
- [self.module.params["executable"], "inspect", name, "-f", "{{.Id}}"])
- if rc != 0:
- return None
- return podman_inspect_info.strip()
-
- before = self.info['hostconfig']['pidmode']
- after = self.params['pid']
- if after is not None and "container:" in after and "container:" in before:
- if after.split(":")[1] == before.split(":")[1]:
- return self._diff_update_and_compare('pid', before, after)
- after = "container:" + get_container_id_by_name(after.split(":")[1])
- return self._diff_update_and_compare('pid', before, after)
-
- # TODO(sshnaidm) Need to add port ranges support
+ return self._diff_generic('pid', '--pid')
+
+ def diffparam_pid_file(self):
+ return self._diff_generic('pid_file', '--pid-file')
+
+ def diffparam_pids_limit(self):
+ return self._diff_generic('pids_limit', '--pids-limit')
+
+ def diffparam_platform(self):
+ return self._diff_generic('platform', '--platform')
+
+ # def diffparam_pod(self):
+ # return self._diff_generic('pod', '--pod')
+
+ def diffparam_pod_id_file(self):
+ return self._diff_generic('pod_id_file', '--pod-id-file')
+
+ def diffparam_privileged(self):
+ return self._diff_generic('privileged', '--privileged')
+
def diffparam_publish(self):
- def compose(p, h):
- s = ":".join(
- [str(h["hostport"]), p.replace('/tcp', '')]
- ).strip(":")
- if h['hostip'] == '0.0.0.0' and LooseVersion(self.version) >= LooseVersion('5.0.0'):
- return s
- if h['hostip']:
- return ":".join([h['hostip'], s])
- return s
-
- ports = self.info['hostconfig']['portbindings']
- before = []
- for port, hosts in ports.items():
- if hosts:
- for h in hosts:
- before.append(compose(port, h))
- after = self.params['publish'] or []
- if self.params['publish_all']:
- image_ports = self.image_info.get('config', {}).get('exposedports', {})
- if image_ports:
- after += list(image_ports.keys())
- after = [
- i.replace("/tcp", "").replace("[", "").replace("]", "").replace("0.0.0.0:", "")
- for i in after]
- # No support for port ranges yet
- for ports in after:
- if "-" in ports:
- return self._diff_update_and_compare('publish', '', '')
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('publish', before, after)
+ return self._diff_generic('publish', '--publish')
+
+ def diffparam_publish_all(self):
+ return self._diff_generic('publish_all', '--publish-all')
+
+ def diffparam_pull(self):
+ return self._diff_generic('pull', '--pull')
+
+ def diffparam_rdt_class(self):
+ return self._diff_generic('rdt_class', '--rdt-class')
def diffparam_read_only(self):
- before = self.info['hostconfig']['readonlyrootfs']
- after = self.params['read_only']
- return self._diff_update_and_compare('read_only', before, after)
+ return self._diff_generic('read_only', '--read-only')
+
+ def diffparam_read_only_tmpfs(self):
+ return self._diff_generic('read_only_tmpfs', '--read-only-tmpfs')
+
+ def diffparam_requires(self):
+ return self._diff_generic('requires', '--requires')
def diffparam_restart_policy(self):
- before = self.info['hostconfig']['restartpolicy']['name']
- before_max_count = int(self.info['hostconfig']['restartpolicy'].get('maximumretrycount', 0))
- after = self.params['restart_policy'] or ""
- if ':' in after:
- after, after_max_count = after.rsplit(':', 1)
- after_max_count = int(after_max_count)
- else:
- after_max_count = 0
- before = "%s:%i" % (before, before_max_count)
- after = "%s:%i" % (after, after_max_count)
- return self._diff_update_and_compare('restart_policy', before, after)
+ return self._diff_generic('restart_policy', '--restart')
+
+ def diffparam_retry(self):
+ return self._diff_generic('retry', '--retry')
+
+ def diffparam_retry_delay(self):
+ return self._diff_generic('retry_delay', '--retry-delay')
+
+ def diffparam_rootfs(self):
+ return self._diff_generic('rootfs', '--rootfs')
+
+ # Disabling idemotency check for sdnotify as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/775
+ # def diffparam_sdnotify(self):
+ # return self._diff_generic('sdnotify', '--sdnotify')
def diffparam_rm(self):
before = self.info['hostconfig']['autoremove']
after = self.params['rm']
+ if after is None:
+ return self._diff_update_and_compare('rm', '', '')
return self._diff_update_and_compare('rm', before, after)
+ def diffparam_rmi(self):
+ return self._diff_generic('rmi', '--rmi', boolean_type=True)
+
+ def diffparam_seccomp_policy(self):
+ return self._diff_generic('seccomp_policy', '--seccomp-policy')
+
+ def diffparam_secrets(self):
+ return self._diff_generic('secrets', '--secret')
+
def diffparam_security_opt(self):
- unsorted_before = self.info['hostconfig']['securityopt']
- unsorted_after = self.params['security_opt']
- # In rootful containers with apparmor there is a profile, "container-default",
- # which is already added by default
- # Since SElinux labels are basically annotations, they are merged in a single list
- # element by podman so we need to split them in a (sorted) list if we want to compare it
- # to the list we provide to the module
- before = sorted(item for element in unsorted_before for item in element.split(',')
- if 'apparmor=container-default' not in item)
- after = sorted(list(set(unsorted_after)))
- return self._diff_update_and_compare('security_opt', before, after)
+ return self._diff_generic('security_opt', '--security-opt')
+
+ def diffparam_shm_size(self):
+ return self._diff_generic('shm_size', '--shm-size')
+
+ def diffparam_shm_size_systemd(self):
+ return self._diff_generic('shm_size_systemd', '--shm-size-systemd')
def diffparam_stop_signal(self):
- before = normalize_signal(self.info['config']['stopsignal'])
- after = normalize_signal(self.params['stop_signal'])
- return self._diff_update_and_compare('stop_signal', before, after)
+ return self._diff_generic('stop_signal', '--stop-signal')
+
+ def diffparam_stop_timeout(self):
+ return self._diff_generic('stop_timeout', '--stop-timeout')
+
+ def diffparam_subgidname(self):
+ return self._diff_generic('subgidname', '--subgidname')
+
+ def diffparam_subuidname(self):
+ return self._diff_generic('subuidname', '--subuidname')
+
+ def diffparam_sysctl(self):
+ return self._diff_generic('sysctl', '--sysctl')
+
+ def diffparam_systemd(self):
+ return self._diff_generic('systemd', '--systemd')
+
+ def diffparam_timeout(self):
+ return self._diff_generic('timeout', '--timeout')
def diffparam_timezone(self):
- before = self.info['config'].get('timezone')
- after = self.params['timezone']
- return self._diff_update_and_compare('timezone', before, after)
+ return self._diff_generic('timezone', '--tz')
+
+ def diffparam_tls_verify(self):
+ return self._diff_generic('tls_verify', '--tls-verify')
def diffparam_tty(self):
before = self.info['config']['tty']
after = self.params['tty']
return self._diff_update_and_compare('tty', before, after)
- def diffparam_user(self):
- before = self.info['config']['user']
- after = self.params['user']
- return self._diff_update_and_compare('user', before, after)
+ def diffparam_tmpfs(self):
+ return self._diff_generic('tmpfs', '--tmpfs')
+
+ def diffparam_uidmap(self):
+ return self._diff_generic('uidmap', '--uidmap')
def diffparam_ulimit(self):
- after = self.params['ulimit'] or []
- # In case of latest podman
- if 'createcommand' in self.info['config']:
- before = self._createcommand('--ulimit')
- before, after = sorted(before), sorted(after)
- return self._diff_update_and_compare('ulimit', before, after)
- if after:
- ulimits = self.info['hostconfig']['ulimits']
- before = {
- u['name'].replace('rlimit_', ''): "%s:%s" % (u['soft'], u['hard']) for u in ulimits}
- after = {i.split('=')[0]: i.split('=')[1]
- for i in self.params['ulimit']}
- new_before = []
- new_after = []
- for u in list(after.keys()):
- # We don't support unlimited ulimits because it depends on platform
- if u in before and "-1" not in after[u]:
- new_before.append([u, before[u]])
- new_after.append([u, after[u]])
- return self._diff_update_and_compare('ulimit', new_before, new_after)
- return self._diff_update_and_compare('ulimit', '', '')
+ return self._diff_generic('ulimit', '--ulimit')
+
+ def diffparam_umask(self):
+ return self._diff_generic('umask', '--umask')
+
+ def diffparam_unsetenv(self):
+ return self._diff_generic('unsetenv', '--unsetenv')
+
+ def diffparam_unsetenv_all(self):
+ return self._diff_generic('unsetenv_all', '--unsetenv-all', boolean_type=True)
+
+ def diffparam_user(self):
+ return self._diff_generic('user', '--user')
+
+ def diffparam_userns(self):
+ return self._diff_generic('userns', '--userns')
def diffparam_uts(self):
- before = self.info['hostconfig']['utsmode']
- after = self.params['uts']
- if self.params['pod'] and not self.module_params['uts']:
- after = before
- return self._diff_update_and_compare('uts', before, after)
+ return self._diff_generic('uts', '--uts')
+
+ def diffparam_variant(self):
+ return self._diff_generic('variant', '--variant')
def diffparam_volume(self):
def clean_volume(x):
@@ -1372,44 +1435,29 @@ class PodmanContainerDiff:
return "/"
return x.replace("//", "/").rstrip("/")
- before = self.info['mounts']
- before_local_vols = []
- if before:
- volumes = []
- local_vols = []
- for m in before:
- if m['type'] != 'volume':
- volumes.append(
- [
- clean_volume(m['source']),
- clean_volume(m['destination'])
- ])
- elif m['type'] == 'volume':
- local_vols.append(
- [m['name'], clean_volume(m['destination'])])
- before = [":".join(v) for v in volumes]
- before_local_vols = [":".join(v) for v in local_vols]
- if self.params['volume'] is not None:
+ before = createcommand('--volume', self.info['config'])
+ if before == []:
+ before = None
+ after = self.params['volume']
+ if after is not None:
after = [":".join(
- [clean_volume(i) for i in v.split(":")[:2]]
- ) for v in self.params['volume']]
- else:
- after = []
- if before_local_vols:
- after = list(set(after).difference(before_local_vols))
- before, after = sorted(list(set(before))), sorted(list(set(after)))
+ [clean_volume(i) for i in v.split(":")[:2]]) for v in self.params['volume']]
+ if before is not None:
+ before = [":".join([clean_volume(i) for i in v.split(":")[:2]]) for v in before]
+ self.module.log("PODMAN Before: %s and After: %s" % (before, after))
+ if before is None and after is None:
+ return self._diff_update_and_compare('volume', before, after)
+ if after is not None:
+ after = ",".join(sorted([str(i).lower() for i in after]))
+ if before:
+ before = ",".join(sorted([str(i).lower() for i in before]))
return self._diff_update_and_compare('volume', before, after)
def diffparam_volumes_from(self):
- # Possibly volumesfrom is not in config
- before = self.info['hostconfig'].get('volumesfrom', []) or []
- after = self.params['volumes_from'] or []
- return self._diff_update_and_compare('volumes_from', before, after)
+ return self._diff_generic('volumes_from', '--volumes-from')
def diffparam_workdir(self):
- before = self.info['config']['workingdir']
- after = self.params['workdir']
- return self._diff_update_and_compare('workdir', before, after)
+ return self._diff_generic('workdir', '--workdir')
def is_different(self):
diff_func_list = [func for func in dir(self)
diff --git a/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py b/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py
index e0031351f..8f315a5cc 100644
--- a/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py
+++ b/ansible_collections/containers/podman/plugins/module_utils/podman/podman_pod_lib.py
@@ -6,6 +6,8 @@ from ansible_collections.containers.podman.plugins.module_utils.podman.common im
from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
from ansible_collections.containers.podman.plugins.module_utils.podman.common import generate_systemd
from ansible_collections.containers.podman.plugins.module_utils.podman.common import delete_systemd
+from ansible_collections.containers.podman.plugins.module_utils.podman.common import diff_generic
+from ansible_collections.containers.podman.plugins.module_utils.podman.common import createcommand
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state, PodQuadlet
@@ -28,9 +30,9 @@ ARGUMENTS_SPEC_POD = dict(
]),
recreate=dict(type='bool', default=False),
add_host=dict(type='list', required=False, elements='str'),
- cgroup_parent=dict(type='str', required=False),
blkio_weight=dict(type='str', required=False),
blkio_weight_device=dict(type='list', elements='str', required=False),
+ cgroup_parent=dict(type='str', required=False),
cpus=dict(type='str', required=False),
cpuset_cpus=dict(type='str', required=False),
cpuset_mems=dict(type='str', required=False),
@@ -39,10 +41,12 @@ ARGUMENTS_SPEC_POD = dict(
device_read_bps=dict(type='list', elements='str', required=False),
device_write_bps=dict(type='list', elements='str', required=False),
dns=dict(type='list', elements='str', required=False),
- dns_opt=dict(type='list', elements='str', required=False),
+ dns_opt=dict(type='list', elements='str', aliases=['dns_option'], required=False),
dns_search=dict(type='list', elements='str', required=False),
+ exit_policy=dict(type='str', required=False, choices=['continue', 'stop']),
generate_systemd=dict(type='dict', default={}),
gidmap=dict(type='list', elements='str', required=False),
+ gpus=dict(type='str', required=False),
hostname=dict(type='str', required=False),
infra=dict(type='bool', required=False),
infra_conmon_pidfile=dict(type='str', required=False),
@@ -50,6 +54,7 @@ ARGUMENTS_SPEC_POD = dict(
infra_image=dict(type='str', required=False),
infra_name=dict(type='str', required=False),
ip=dict(type='str', required=False),
+ ip6=dict(type='str', required=False),
label=dict(type='dict', required=False),
label_file=dict(type='str', required=False),
mac_address=dict(type='str', required=False),
@@ -67,13 +72,21 @@ ARGUMENTS_SPEC_POD = dict(
quadlet_dir=dict(type='path'),
quadlet_filename=dict(type='str'),
quadlet_options=dict(type='list', elements='str'),
+ restart_policy=dict(type='str', required=False),
+ security_opt=dict(type='list', elements='str', required=False),
share=dict(type='str', required=False),
+ share_parent=dict(type='bool', required=False),
+ shm_size=dict(type='str', required=False),
+ shm_size_systemd=dict(type='str', required=False),
subgidname=dict(type='str', required=False),
subuidname=dict(type='str', required=False),
+ sysctl=dict(type='dict', required=False),
uidmap=dict(type='list', elements='str', required=False),
userns=dict(type='str', required=False),
+ uts=dict(type='str', required=False),
volume=dict(type='list', elements='str', aliases=['volumes'],
required=False),
+ volumes_from=dict(type='list', elements='str', required=False),
executable=dict(type='str', required=False, default='podman'),
debug=dict(type='bool', default=False),
)
@@ -200,7 +213,7 @@ class PodmanPodModuleParams:
def addparam_dns_opt(self, c):
for g in self.params['dns_opt']:
- c += ['--dns-opt', g]
+ c += ['--dns-option', g]
return c
def addparam_dns_search(self, c):
@@ -208,11 +221,17 @@ class PodmanPodModuleParams:
c += ['--dns-search', g]
return c
+ def addparam_exit_policy(self, c):
+ return c + ['--exit-policy=%s' % self.params['exit_policy']]
+
def addparam_gidmap(self, c):
for gidmap in self.params['gidmap']:
c += ['--gidmap', gidmap]
return c
+ def addparam_gpus(self, c):
+ return c + ['--gpus', self.params['gpus']]
+
def addparam_hostname(self, c):
return c + ['--hostname', self.params['hostname']]
@@ -236,6 +255,9 @@ class PodmanPodModuleParams:
def addparam_ip(self, c):
return c + ['--ip', self.params['ip']]
+ def addparam_ip6(self, c):
+ return c + ['--ip6', self.params['ip6']]
+
def addparam_label(self, c):
for label in self.params['label'].items():
c += ['--label', b'='.join(
@@ -285,15 +307,39 @@ class PodmanPodModuleParams:
c += ['--publish', g]
return c
+ def addparam_restart_policy(self, c):
+ return c + ['--restart=%s' % self.params['restart_policy']]
+
+ def addparam_security_opt(self, c):
+ for g in self.params['security_opt']:
+ c += ['--security-opt', g]
+ return c
+
def addparam_share(self, c):
return c + ['--share', self.params['share']]
+ def addparam_share_parent(self, c):
+ if self.params['share_parent'] is not None:
+ return c + ['--share-parent=%s' % self.params['share_parent']]
+ return c
+
+ def addparam_shm_size(self, c):
+ return c + ['--shm-size=%s' % self.params['shm_size']]
+
+ def addparam_shm_size_systemd(self, c):
+ return c + ['--shm-size-systemd=%s' % self.params['shm_size_systemd']]
+
def addparam_subgidname(self, c):
return c + ['--subgidname', self.params['subgidname']]
def addparam_subuidname(self, c):
return c + ['--subuidname', self.params['subuidname']]
+ def addparam_sysctl(self, c):
+ for k, v in self.params['sysctl'].items():
+ c += ['--sysctl', "%s=%s" % (k, v)]
+ return c
+
def addparam_uidmap(self, c):
for uidmap in self.params['uidmap']:
c += ['--uidmap', uidmap]
@@ -302,22 +348,26 @@ class PodmanPodModuleParams:
def addparam_userns(self, c):
return c + ['--userns', self.params['userns']]
+ def addparam_uts(self, c):
+ return c + ['--uts', self.params['uts']]
+
def addparam_volume(self, c):
for vol in self.params['volume']:
if vol:
c += ['--volume', vol]
return c
+ def addparam_volumes_from(self, c):
+ for vol in self.params['volumes_from']:
+ c += ['--volumes-from', vol]
+ return c
+
class PodmanPodDefaults:
def __init__(self, module, podman_version):
self.module = module
self.version = podman_version
self.defaults = {
- 'add_host': [],
- 'dns': [],
- 'dns_opt': [],
- 'dns_search': [],
'infra': True,
'label': {},
}
@@ -361,50 +411,79 @@ class PodmanPodDiff:
return True
return False
+ def _diff_generic(self, module_arg, cmd_arg, boolean_type=False):
+ """
+ Generic diff function for module arguments from CreateCommand
+ in Podman inspection output.
+
+ Args:
+ module_arg (str): module argument name
+ cmd_arg (str): command line argument name
+ boolean_type (bool): if True, then argument is boolean type
+
+ Returns:
+ bool: True if there is a difference, False otherwise
+
+ """
+ info_config = self.info
+ before, after = diff_generic(self.params, info_config, module_arg, cmd_arg, boolean_type)
+ return self._diff_update_and_compare(module_arg, before, after)
+
def diffparam_add_host(self):
- if not self.infra_info:
- return self._diff_update_and_compare('add_host', '', '')
- before = self.infra_info['hostconfig']['extrahosts'] or []
- after = self.params['add_host']
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('add_host', before, after)
+ return self._diff_generic('add_host', '--add-host')
+
+ def diffparam_blkio_weight(self):
+ return self._diff_generic('blkio_weight', '--blkio-weight')
+
+ def diffparam_blkio_weight_device(self):
+ return self._diff_generic('blkio_weight_device', '--blkio-weight-device')
def diffparam_cgroup_parent(self):
- before = (self.info.get('cgroupparent', '')
- or self.info.get('hostconfig', {}).get('cgroupparent', ''))
- after = self.params['cgroup_parent'] or before
- return self._diff_update_and_compare('cgroup_parent', before, after)
+ return self._diff_generic('cgroup_parent', '--cgroup-parent')
+
+ def diffparam_cpu_shares(self):
+ return self._diff_generic('cpu_shares', '--cpu-shares')
+
+ def diffparam_cpus(self):
+ return self._diff_generic('cpus', '--cpus')
+
+ def diffparam_cpuset_cpus(self):
+ return self._diff_generic('cpuset_cpus', '--cpuset-cpus')
+
+ def diffparam_cpuset_mems(self):
+ return self._diff_generic('cpuset_mems', '--cpuset-mems')
+
+ def diffparam_device(self):
+ return self._diff_generic('device', '--device')
+
+ def diffparam_device_read_bps(self):
+ return self._diff_generic('device_read_bps', '--device-read-bps')
+
+ def diffparam_device_write_bps(self):
+ return self._diff_generic('device_write_bps', '--device-write-bps')
def diffparam_dns(self):
- if not self.infra_info:
- return self._diff_update_and_compare('dns', '', '')
- before = self.infra_info['hostconfig']['dns'] or []
- after = self.params['dns']
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('dns', before, after)
+ return self._diff_generic('dns', '--dns')
def diffparam_dns_opt(self):
- if not self.infra_info:
- return self._diff_update_and_compare('dns_opt', '', '')
- before = self.infra_info['hostconfig']['dnsoptions'] or []
- after = self.params['dns_opt']
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('dns_opt', before, after)
+ return self._diff_generic('dns_opt', '--dns-option')
def diffparam_dns_search(self):
- if not self.infra_info:
- return self._diff_update_and_compare('dns_search', '', '')
- before = self.infra_info['hostconfig']['dnssearch'] or []
- after = self.params['dns_search']
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('dns_search', before, after)
+ return self._diff_generic('dns_search', '--dns-search')
+
+ # Disabling idemotency check for exit policy as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/774
+ # def diffparam_exit_policy(self):
+ # return self._diff_generic('exit_policy', '--exit-policy')
+
+ def diffparam_gidmap(self):
+ return self._diff_generic('gidmap', '--gidmap')
+
+ def diffparam_gpus(self):
+ return self._diff_generic('gpus', '--gpus')
def diffparam_hostname(self):
- if not self.infra_info:
- return self._diff_update_and_compare('hostname', '', '')
- before = self.infra_info['config']['hostname']
- after = self.params['hostname'] or before
- return self._diff_update_and_compare('hostname', before, after)
+ return self._diff_generic('hostname', '--hostname')
# TODO(sshnaidm): https://github.com/containers/podman/issues/6968
def diffparam_infra(self):
@@ -416,30 +495,25 @@ class PodmanPodDiff:
after = self.params['infra']
return self._diff_update_and_compare('infra', before, after)
- # TODO(sshnaidm): https://github.com/containers/podman/issues/6969
- # def diffparam_infra_command(self):
- # before = str(self.info['hostconfig']['infra_command'])
- # after = self.params['infra_command']
- # return self._diff_update_and_compare('infra_command', before, after)
+ def diffparam_infra_command(self):
+ return self._diff_generic('infra_command', '--infra-command')
+
+ # Disabling idemotency check for infra_conmon_pidfile as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/774
+ # def diffparam_infra_conmon_pidfile(self):
+ # return self._diff_generic('infra_conmon_pidfile', '--infra-conmon-pidfile')
def diffparam_infra_image(self):
- if not self.infra_info:
- return self._diff_update_and_compare('infra_image', '', '')
- before = str(self.infra_info['imagename'])
- after = before
- if self.module_params['infra_image']:
- after = self.params['infra_image']
- before = before.replace(":latest", "")
- after = after.replace(":latest", "")
- before = before.split("/")[-1] # pylint: disable=W,C,R
- after = after.split("/")[-1] # pylint: disable=W,C,R
- return self._diff_update_and_compare('infra_image', before, after)
-
- # TODO(sshnaidm): https://github.com/containers/podman/pull/6956
- # def diffparam_ip(self):
- # before = str(self.info['hostconfig']['ip'])
- # after = self.params['ip']
- # return self._diff_update_and_compare('ip', before, after)
+ return self._diff_generic('infra_image', '--infra-image')
+
+ def diffparam_infra_name(self):
+ return self._diff_generic('infra_name', '--infra-name')
+
+ def diffparam_ip(self):
+ return self._diff_generic('ip', '--ip')
+
+ def diffparam_ip6(self):
+ return self._diff_generic('ip6', '--ip6')
def diffparam_label(self):
if 'config' in self.info and 'labels' in self.info['config']:
@@ -454,129 +528,101 @@ class PodmanPodDiff:
before.pop('podman_systemd_unit', None)
return self._diff_update_and_compare('label', before, after)
- # TODO(sshnaidm): https://github.com/containers/podman/pull/6956
- # def diffparam_mac_address(self):
- # before = str(self.info['hostconfig']['mac_address'])
- # after = self.params['mac_address']
- # return self._diff_update_and_compare('mac_address', before, after)
+ def diffparam_label_file(self):
+ return self._diff_generic('label_file', '--label-file')
+
+ def diffparam_mac_address(self):
+ return self._diff_generic('mac_address', '--mac-address')
+
+ def diffparam_memory(self):
+ return self._diff_generic('memory', '--memory')
+
+ def diffparam_memory_swap(self):
+ return self._diff_generic('memory_swap', '--memory-swap')
def diffparam_network(self):
- if not self.infra_info:
- return self._diff_update_and_compare('network', [], [])
- net_mode_before = self.infra_info['hostconfig']['networkmode']
- net_mode_after = ''
- before = list(self.infra_info['networksettings'].get('networks', {}))
- # Remove default 'podman' network in v3 for comparison
- if before == ['podman']:
- before = []
- after = self.params['network'] or []
- after = [i.lower() for i in after]
- # Special case for options for slirp4netns rootless networking from v2
- if net_mode_before == 'slirp4netns' and 'createcommand' in self.info:
- cr_com = self.info['createcommand']
- if '--network' in cr_com:
- cr_net = cr_com[cr_com.index('--network') + 1].lower()
- if 'slirp4netns:' in cr_net:
- before = [cr_net]
- if net_mode_before == 'pasta' and 'createcommand' in self.info:
- cr_com = self.info['createcommand']
- if '--network' in cr_com:
- cr_net = cr_com[cr_com.index('--network') + 1].lower()
- if 'pasta:' in cr_net:
- before = [cr_net]
- # Currently supported only 'host' and 'none' network modes idempotency
- if after in [['bridge'], ['host'], ['slirp4netns'], ['pasta']]:
- net_mode_after = after[0]
-
- if net_mode_after and not before:
- # Remove differences between v1 and v2
- net_mode_after = net_mode_after.replace('bridge', 'default')
- net_mode_after = net_mode_after.replace('slirp4netns', 'default')
- net_mode_after = net_mode_after.replace('pasta', 'default')
- net_mode_before = net_mode_before.replace('bridge', 'default')
- net_mode_before = net_mode_before.replace('slirp4netns', 'default')
- net_mode_before = net_mode_before.replace('pasta', 'default')
- return self._diff_update_and_compare('network', net_mode_before, net_mode_after)
- # For 4.4.0+ podman versions with no network specified
- if not net_mode_after and net_mode_before == 'slirp4netns' and not after:
- net_mode_after = 'slirp4netns'
- if before == ['slirp4netns']:
- after = ['slirp4netns']
- if not net_mode_after and net_mode_before == 'bridge' and not after:
- net_mode_after = 'bridge'
- if before == ['bridge']:
- after = ['bridge']
- # For pasta networking for Podman v5
- if not net_mode_after and net_mode_before == 'pasta' and not after:
- net_mode_after = 'pasta'
- if before == ['pasta']:
- after = ['pasta']
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('network', before, after)
-
- # TODO(sshnaidm)
- # def diffparam_no_hosts(self):
- # before = str(self.info['hostconfig']['no_hosts'])
- # after = self.params['no_hosts']
- # return self._diff_update_and_compare('no_hosts', before, after)
-
- # TODO(sshnaidm) Need to add port ranges support
+ return self._diff_generic('network', '--network')
+
+ def diffparam_network_alias(self):
+ return self._diff_generic('network_alias', '--network-alias')
+
+ def diffparam_no_hosts(self):
+ return self._diff_generic('no_hosts', '--no-hosts', boolean_type=True)
+
+ def diffparam_pid(self):
+ return self._diff_generic('pid', '--pid')
+
+ # Disabling idemotency check for pod id file as it's added by systemd generator
+ # https://github.com/containers/ansible-podman-collections/issues/774
+ # def diffparam_pod_id_file(self):
+ # return self._diff_generic('pod_id_file', '--pod-id-file')
+
def diffparam_publish(self):
- def compose(p, h):
- s = ":".join(
- [str(h["hostport"]), p.replace('/tcp', '')]
- ).strip(":")
- if h['hostip'] == '0.0.0.0' and LooseVersion(self.version) >= LooseVersion('5.0.0'):
- return s
- if h['hostip']:
- return ":".join([h['hostip'], s])
- return s
-
- if not self.infra_info:
- return self._diff_update_and_compare('publish', '', '')
-
- ports = self.infra_info['hostconfig']['portbindings']
- before = []
- for port, hosts in ports.items():
- if hosts:
- for h in hosts:
- before.append(compose(port, h))
- after = self.params['publish'] or []
- after = [
- i.replace("/tcp", "").replace("[", "").replace("]", "")
- for i in after]
- # No support for port ranges yet
- for ports in after:
- if "-" in ports:
- return self._diff_update_and_compare('publish', '', '')
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('publish', before, after)
+ return self._diff_generic('publish', '--publish')
+
+ def diffparam_restart_policy(self):
+ return self._diff_generic('restart_policy', '--restart')
+
+ def diffparam_security_opt(self):
+ return self._diff_generic('security_opt', '--security-opt')
def diffparam_share(self):
- if not self.infra_info:
- return self._diff_update_and_compare('share', '', '')
- if 'sharednamespaces' in self.info:
- before = self.info['sharednamespaces']
- elif 'config' in self.info:
- before = [
- i.split('shares')[1].lower()
- for i in self.info['config'] if 'shares' in i]
- # TODO(sshnaidm): to discover why in podman v1 'cgroup' appears
- before.remove('cgroup')
- else:
- before = []
- if self.params['share'] is not None:
- after = self.params['share'].split(",")
- else:
- after = ['uts', 'ipc', 'net']
- # TODO: find out why on Ubuntu the 'net' is not present
- if 'net' not in before:
- after.remove('net')
- if self.params["uidmap"] or self.params["gidmap"] or self.params["userns"]:
- after.append('user')
-
- before, after = sorted(list(set(before))), sorted(list(set(after)))
- return self._diff_update_and_compare('share', before, after)
+ return self._diff_generic('share', '--share')
+
+ def diffparam_share_parent(self):
+ return self._diff_generic('share_parent', '--share-parent')
+
+ def diffparam_shm_size(self):
+ return self._diff_generic('shm_size', '--shm-size')
+
+ def diffparam_shm_size_systemd(self):
+ return self._diff_generic('shm_size_systemd', '--shm-size-systemd')
+
+ def diffparam_subgidname(self):
+ return self._diff_generic('subgidname', '--subgidname')
+
+ def diffparam_subuidname(self):
+ return self._diff_generic('subuidname', '--subuidname')
+
+ def diffparam_sysctl(self):
+ return self._diff_generic('sysctl', '--sysctl')
+
+ def diffparam_uidmap(self):
+ return self._diff_generic('uidmap', '--uidmap')
+
+ def diffparam_userns(self):
+ return self._diff_generic('userns', '--userns')
+
+ def diffparam_uts(self):
+ return self._diff_generic('uts', '--uts')
+
+ def diffparam_volume(self):
+ def clean_volume(x):
+ '''Remove trailing and double slashes from volumes.'''
+ if not x.rstrip("/"):
+ return "/"
+ return x.replace("//", "/").rstrip("/")
+
+ before = createcommand('--volume', self.info)
+ if before == []:
+ before = None
+ after = self.params['volume']
+ if after is not None:
+ after = [":".join(
+ [clean_volume(i) for i in v.split(":")[:2]]) for v in self.params['volume']]
+ if before is not None:
+ before = [":".join([clean_volume(i) for i in v.split(":")[:2]]) for v in before]
+ self.module.log("PODMAN Before: %s and After: %s" % (before, after))
+ if before is None and after is None:
+ return self._diff_update_and_compare('volume', before, after)
+ if after is not None:
+ after = ",".join(sorted([str(i).lower() for i in after]))
+ if before:
+ before = ",".join(sorted([str(i).lower() for i in before]))
+ return self._diff_update_and_compare('volume', before, after)
+
+ def diffparam_volumes_from(self):
+ return self._diff_generic('volumes_from', '--volumes-from')
def is_different(self):
diff_func_list = [func for func in dir(self)
diff --git a/ansible_collections/containers/podman/plugins/module_utils/podman/quadlet.py b/ansible_collections/containers/podman/plugins/module_utils/podman/quadlet.py
index 17764b60d..6296c7b81 100644
--- a/ansible_collections/containers/podman/plugins/module_utils/podman/quadlet.py
+++ b/ansible_collections/containers/podman/plugins/module_utils/podman/quadlet.py
@@ -88,25 +88,21 @@ class ContainerQuadlet(Quadlet):
'gidmap': 'GIDMap',
'global_args': 'GlobalArgs',
'group': 'Group', # Does not exist in module parameters
- 'healthcheck': 'HealthCheckCmd',
+ 'healthcheck': 'HealthCmd',
'healthcheck_interval': 'HealthInterval',
'healthcheck_failure_action': 'HealthOnFailure',
'healthcheck_retries': 'HealthRetries',
'healthcheck_start_period': 'HealthStartPeriod',
'healthcheck_timeout': 'HealthTimeout',
- # the following are not implemented yet in Podman module
- 'HealthStartupCmd': 'HealthStartupCmd',
- 'HealthStartupInterval': 'HealthStartupInterval',
- 'HealthStartupRetries': 'HealthStartupRetries',
- 'HealthStartupSuccess': 'HealthStartupSuccess',
- 'HealthStartupTimeout': 'HealthStartupTimeout',
- # end of not implemented yet
+ 'health_startup_cmd': 'HealthStartupCmd',
+ 'health_startup_interval': 'HealthStartupInterval',
+ 'health_startup_retries': 'HealthStartupRetries',
+ 'health_startup_success': 'HealthStartupSuccess',
+ 'health_startup_timeout': 'HealthStartupTimeout',
'hostname': 'HostName',
'image': 'Image',
'ip': 'IP',
- # the following are not implemented yet in Podman module
- 'IP6': 'IP6',
- # end of not implemented yet
+ 'ip6': 'IP6',
'label': 'Label',
'log_driver': 'LogDriver',
"Mask": "Mask", # add it in security_opt
@@ -117,9 +113,7 @@ class ContainerQuadlet(Quadlet):
'pids_limit': 'PidsLimit',
'pod': 'Pod',
'publish': 'PublishPort',
- # the following are not implemented yet in Podman module
- "Pull": "Pull",
- # end of not implemented yet
+ "pull": "Pull",
'read_only': 'ReadOnly',
'read_only_tmpfs': 'ReadOnlyTmpfs',
'rootfs': 'Rootfs',
@@ -194,6 +188,8 @@ class ContainerQuadlet(Quadlet):
# Work on params which are not in the param_map and add them to PodmanArgs
params["podman_args"] = []
+ if params["arch"]:
+ params["podman_args"].append(f"--arch {params['arch']}")
if params["authfile"]:
params["podman_args"].append(f"--authfile {params['authfile']}")
if params["attach"]:
@@ -206,8 +202,13 @@ class ContainerQuadlet(Quadlet):
f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()]))
if params["cgroupns"]:
params["podman_args"].append(f"--cgroupns {params['cgroupns']}")
+ if params["cgroup_conf"]:
+ for k, v in params["cgroup_conf"].items():
+ params["podman_args"].append(f"--cgroup-conf {k}={v}")
if params["cgroup_parent"]:
params["podman_args"].append(f"--cgroup-parent {params['cgroup_parent']}")
+ if params["chrootdirs"]:
+ params["podman_args"].append(f"--chrootdirs {params['chrootdirs']}")
if params["cidfile"]:
params["podman_args"].append(f"--cidfile {params['cidfile']}")
if params["conmon_pidfile"]:
@@ -226,6 +227,10 @@ class ContainerQuadlet(Quadlet):
params["podman_args"].append(f"--cpu-rt-runtime {params['cpu_rt_runtime']}")
if params["cpu_shares"]:
params["podman_args"].append(f"--cpu-shares {params['cpu_shares']}")
+ if params["decryption_key"]:
+ params["podman_args"].append(f"--decryption-key {params['decryption_key']}")
+ if params["device_cgroup_rule"]:
+ params["podman_args"].append(f"--device-cgroup-rule {params['device_cgroup_rule']}")
if params["device_read_bps"]:
for i in params["device_read_bps"]:
params["podman_args"].append(f"--device-read-bps {i}")
@@ -241,6 +246,15 @@ class ContainerQuadlet(Quadlet):
if params["etc_hosts"]:
for host_ip in params['etc_hosts'].items():
params["podman_args"].append(f"--add-host {':'.join(host_ip)}")
+ if params["env_merge"]:
+ for k, v in params["env_merge"].items():
+ params["podman_args"].append(f"--env {k}={v}")
+ if params["gpus"]:
+ params["podman_args"].append(f"--gpus {params['gpus']}")
+ if params["group_entry"]:
+ params["podman_args"].append(f"--group-entry {params['group_entry']}")
+ if params["hostuser"]:
+ params["podman_args"].append(f"--hostuser {params['hostuser']}")
if params["hooks_dir"]:
for hook in params["hooks_dir"]:
params["podman_args"].append(f"--hooks-dir {hook}")
@@ -248,6 +262,8 @@ class ContainerQuadlet(Quadlet):
params["podman_args"].append(f"--http-proxy {params['http_proxy']}")
if params["image_volume"]:
params["podman_args"].append(f"--image-volume {params['image_volume']}")
+ if params["init_ctr"]:
+ params["podman_args"].append(f"--init-ctr {params['init_ctr']}")
if params["init_path"]:
params["podman_args"].append(f"--init-path {params['init_path']}")
if params["interactive"]:
@@ -274,37 +290,79 @@ class ContainerQuadlet(Quadlet):
if params["network_aliases"]:
for alias in params["network_aliases"]:
params["podman_args"].append(f"--network-alias {alias}")
+ if params["no_healthcheck"]:
+ params["podman_args"].append("--no-healthcheck")
if params["no_hosts"] is not None:
params["podman_args"].append(f"--no-hosts={params['no_hosts']}")
if params["oom_kill_disable"]:
params["podman_args"].append(f"--oom-kill-disable={params['oom_kill_disable']}")
if params["oom_score_adj"]:
params["podman_args"].append(f"--oom-score-adj {params['oom_score_adj']}")
+ if params["os"]:
+ params["podman_args"].append(f"--os {params['os']}")
+ if params["passwd"]:
+ params["podman_args"].append("--passwd")
+ if params["passwd_entry"]:
+ params["podman_args"].append(f"--passwd-entry {params['passwd_entry']}")
+ if params["personality"]:
+ params["podman_args"].append(f"--personality {params['personality']}")
if params["pid"]:
params["podman_args"].append(f"--pid {params['pid']}")
+ if params["pid_file"]:
+ params["podman_args"].append(f"--pid-file {params['pid_file']}")
+ if params["preserve_fd"]:
+ for pres in params["preserve_fd"]:
+ params["podman_args"].append(f"--preserve-fd {pres}")
+ if params["preserve_fds"]:
+ params["podman_args"].append(f"--preserve-fds {params['preserve_fds']}")
if params["privileged"]:
params["podman_args"].append("--privileged")
if params["publish_all"]:
params["podman_args"].append("--publish-all")
+ if params["rdt_class"]:
+ params["podman_args"].append(f"--rdt-class {params['rdt_class']}")
if params["requires"]:
params["podman_args"].append(f"--requires {','.join(params['requires'])}")
if params["restart_policy"]:
params["podman_args"].append(f"--restart-policy {params['restart_policy']}")
+ if params["retry"]:
+ params["podman_args"].append(f"--retry {params['retry']}")
+ if params["retry_delay"]:
+ params["podman_args"].append(f"--retry-delay {params['retry_delay']}")
if params["rm"]:
params["podman_args"].append("--rm")
+ if params["rmi"]:
+ params["podman_args"].append("--rmi")
+ if params["seccomp_policy"]:
+ params["podman_args"].append(f"--seccomp-policy {params['seccomp_policy']}")
if params["security_opt"]:
for security_opt in params["security_opt"]:
params["podman_args"].append(f"--security-opt {security_opt}")
+ if params["shm_size_systemd"]:
+ params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
if params["sig_proxy"]:
params["podman_args"].append(f"--sig-proxy {params['sig_proxy']}")
if params["stop_signal"]:
params["podman_args"].append(f"--stop-signal {params['stop_signal']}")
if params["systemd"]:
params["podman_args"].append(f"--systemd={str(params['systemd']).lower()}")
+ if params["timeout"]:
+ params["podman_args"].append(f"--timeout {params['timeout']}")
+ if params["tls_verify"]:
+ params["podman_args"].append(f"--tls-verify={str(params['tls_verify']).lower()}")
if params["tty"]:
params["podman_args"].append("--tty")
+ if params["umask"]:
+ params["podman_args"].append(f"--umask {params['umask']}")
+ if params["unsetenv"]:
+ for unset in params["unsetenv"]:
+ params["podman_args"].append(f"--unsetenv {unset}")
+ if params["unsetenv_all"]:
+ params["podman_args"].append("--unsetenv-all")
if params["uts"]:
params["podman_args"].append(f"--uts {params['uts']}")
+ if params["variant"]:
+ params["podman_args"].append(f"--variant {params['variant']}")
if params["volumes_from"]:
for volume in params["volumes_from"]:
params["podman_args"].append(f"--volumes-from {volume}")
@@ -416,6 +474,10 @@ class PodQuadlet(Quadlet):
if params["gidmap"]:
for gidmap in params["gidmap"]:
params["podman_args"].append(f"--gidmap {gidmap}")
+ if params["exit_policy"]:
+ params["podman_args"].append(f"--exit-policy={params['gpus']}")
+ if params["gpus"]:
+ params["podman_args"].append(f"--gpus {params['gpus']}")
if params["hostname"]:
params["podman_args"].append(f"--hostname {params['hostname']}")
if params["infra"]:
@@ -430,6 +492,8 @@ class PodQuadlet(Quadlet):
params["podman_args"].append(f"--infra-name {params['infra_name']}")
if params["ip"]:
params["podman_args"].append(f"--ip {params['ip']}")
+ if params["ip6"]:
+ params["podman_args"].append(f"--ip6 {params['ip6']}")
if params["label"]:
for label, label_v in params["label"].items():
params["podman_args"].append(f"--label {label}={label_v}")
@@ -447,17 +511,36 @@ class PodQuadlet(Quadlet):
params["podman_args"].append(f"--pid {params['pid']}")
if params["pod_id_file"]:
params["podman_args"].append(f"--pod-id-file {params['pod_id_file']}")
+ if params["restart_policy"]:
+ params["podman_args"].append(f"--restart={params['restart_policy']}")
+ if params["security_opt"]:
+ for security_opt in params["security_opt"]:
+ params["podman_args"].append(f"--security-opt {security_opt}")
if params["share"]:
params["podman_args"].append(f"--share {params['share']}")
+ if params["share_parent"] is not None:
+ params["podman_args"].append(f"--share-parent={str(params['share_parent']).lower()}")
+ if params["shm_size"]:
+ params["podman_args"].append(f"--shm-size {params['shm_size']}")
+ if params["shm_size_systemd"]:
+ params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
if params["subgidname"]:
params["podman_args"].append(f"--subgidname {params['subgidname']}")
if params["subuidname"]:
params["podman_args"].append(f"--subuidname {params['subuidname']}")
+ if params["sysctl"]:
+ for k, v in params["sysctl"].items():
+ params["podman_args"].append(f"--sysctl {k}={v}")
if params["uidmap"]:
for uidmap in params["uidmap"]:
params["podman_args"].append(f"--uidmap {uidmap}")
if params["userns"]:
params["podman_args"].append(f"--userns {params['userns']}")
+ if params["uts"]:
+ params["podman_args"].append(f"--uts {params['uts']}")
+ if params["volumes_from"]:
+ for volume in params["volumes_from"]:
+ params["podman_args"].append(f"--volumes-from {volume}")
if params["debug"]:
params["global_args"].append("--log-level debug")
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_container.py b/ansible_collections/containers/podman/plugins/modules/podman_container.py
index 75349f14e..b06c9ae9e 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_container.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_container.py
@@ -79,6 +79,11 @@ options:
- Add an annotation to the container. The format is key value, multiple
times.
type: dict
+ arch:
+ description:
+ - Set the architecture for the container.
+ Override the architecture, defaults to hosts, of the image to be pulled. For example, arm.
+ type: str
attach:
description:
- Attach to STDIN, STDOUT or STDERR. The default in Podman is false.
@@ -125,6 +130,10 @@ options:
the cgroups path of the init process. Cgroups will be created if they
do not already exist.
type: path
+ cgroup_conf:
+ description:
+ - When running on cgroup v2, specify the cgroup file to write to and its value.
+ type: dict
cgroupns:
description:
- Path to cgroups under which the cgroup for the container will be
@@ -137,6 +146,10 @@ options:
The disabled option will force the container to not create CGroups,
and thus conflicts with CGroup options cgroupns and cgroup-parent.
type: str
+ chrootdirs:
+ description:
+ - Path to a directory inside the container that is treated as a chroot directory.
+ type: str
cidfile:
description:
- Write the container ID to the file
@@ -196,6 +209,10 @@ options:
- Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only
effective on NUMA systems.
type: str
+ decryption_key:
+ description:
+ - The "key-passphrase" to be used for decryption of images. Key can point to keys and/or certificates.
+ type: str
delete_depend:
description:
- Remove selected container and recursively remove all containers that depend on it.
@@ -234,6 +251,12 @@ options:
(e.g. device /dev/sdc:/dev/xvdc:rwm)
type: list
elements: str
+ device_cgroup_rule:
+ description:
+ - Add a rule to the cgroup allowed devices list.
+ The rule is expected to be in the format specified in the Linux kernel
+ documentation admin-guide/cgroup-v1/devices.
+ type: str
device_read_bps:
description:
- Limit read rate (bytes per second) from a device
@@ -307,6 +330,10 @@ options:
- Use all current host environment variables in container.
Defaults to false.
type: bool
+ env_merge:
+ description:
+ - Preprocess default environment variables for the containers
+ type: dict
etc_hosts:
description:
- Dict of host-to-IP mappings, where each host name is a key in the
@@ -436,6 +463,10 @@ options:
- Run the container in a new user namespace using the supplied mapping.
type: list
elements: str
+ gpus:
+ description:
+ - GPU devices to add to the container.
+ type: str
group_add:
description:
- Add additional groups to run as
@@ -443,33 +474,70 @@ options:
elements: str
aliases:
- groups
+ group_entry:
+ description:
+ - Customize the entry that is written to the /etc/group file within the container when --user is used.
+ type: str
healthcheck:
description:
- Set or alter a healthcheck command for a container.
type: str
+ aliases:
+ - health_cmd
healthcheck_interval:
description:
- Set an interval for the healthchecks
(a value of disable results in no automatic timer setup)
(default "30s")
type: str
+ aliases:
+ - health_interval
healthcheck_retries:
description:
- The number of retries allowed before a healthcheck is considered to be
unhealthy. The default value is 3.
type: int
+ aliases:
+ - health_retries
healthcheck_start_period:
description:
- The initialization time needed for a container to bootstrap.
The value can be expressed in time format like 2m3s. The default value
is 0s
type: str
+ aliases:
+ - health_start_period
+ health_startup_cmd:
+ description:
+ - Set a startup healthcheck command for a container.
+ type: str
+ health_startup_interval:
+ description:
+ - Set an interval for the startup healthcheck.
+ type: str
+ health_startup_retries:
+ description:
+ - The number of attempts allowed before the startup healthcheck restarts the container.
+ If set to 0, the container is never restarted. The default is 0.
+ type: int
+ health_startup_success:
+ description:
+ - The number of successful runs required before the startup healthcheck succeeds
+ and the regular healthcheck begins. A value of 0 means that any success begins the regular healthcheck.
+ The default is 0.
+ type: int
+ health_startup_timeout:
+ description:
+ - The maximum time a startup healthcheck command has to complete before it is marked as failed.
+ type: str
healthcheck_timeout:
description:
- The maximum time allowed to complete the healthcheck before an interval
is considered failed. Like start-period, the value can be expressed in
a time format such as 1m22s. The default value is 30s
type: str
+ aliases:
+ - health_timeout
healthcheck_failure_action:
description:
- The action to be taken when the container is considered unhealthy. The action must be one of
@@ -481,6 +549,8 @@ options:
- 'kill'
- 'restart'
- 'stop'
+ aliases:
+ - health_on_failure
hooks_dir:
description:
- Each .json file in the path configures a hook for Podman containers.
@@ -493,6 +563,11 @@ options:
- Container host name. Sets the container host name that is available
inside the container.
type: str
+ hostuser:
+ description:
+ - Add a user account to /etc/passwd from the host to the container.
+ The Username or UID must exist on the host system.
+ type: str
http_proxy:
description:
- By default proxy environment variables are passed into the container if
@@ -522,6 +597,14 @@ options:
- Run an init inside the container that forwards signals and reaps
processes. The default is false.
type: bool
+ init_ctr:
+ description:
+ - (Pods only). When using pods, create an init style container,
+ which is run after the infra container is started but before regular pod containers are started.
+ type: str
+ choices:
+ - 'once'
+ - 'always'
init_path:
description:
- Path to the container-init binary.
@@ -542,6 +625,10 @@ options:
The address must be within the default CNI network's pool
(default 10.88.0.0/16).
type: str
+ ip6:
+ description:
+ - Specify a static IPv6 address for the container
+ type: str
ipc:
description:
- Default is to create a private IPC namespace (POSIX SysV IPC) for the
@@ -671,6 +758,12 @@ options:
This is a limitation that will be removed in a later release.
type: list
elements: str
+ aliases:
+ - network_alias
+ no_healthcheck:
+ description:
+ - Disable any defined healthchecks for container.
+ type: bool
no_hosts:
description:
- Do not create /etc/hosts for the container
@@ -685,23 +778,64 @@ options:
description:
- Tune the host's OOM preferences for containers (accepts -1000 to 1000)
type: int
+ os:
+ description:
+ - Override the OS, defaults to hosts, of the image to be pulled. For example, windows.
+ type: str
+ passwd:
+ description:
+ - Allow Podman to add entries to /etc/passwd and /etc/group when used in conjunction with the --user option.
+ This is used to override the Podman provided user setup in favor of entrypoint configurations
+ such as libnss-extrausers.
+ type: bool
+ passwd_entry:
+ description:
+ - Customize the entry that is written to the /etc/passwd file within the container when --passwd is used.
+ type: str
+ personality:
+ description:
+ - Personality sets the execution domain via Linux personality(2).
+ type: str
pid:
description:
- Set the PID mode for the container
type: str
aliases:
- pid_mode
+ pid_file:
+ description:
+ - When the pidfile location is specified, the container process' PID is written to the pidfile.
+ type: path
pids_limit:
description:
- Tune the container's PIDs limit. Set -1 to have unlimited PIDs for the
container.
type: str
+ platform:
+ description:
+ - Specify the platform for selecting the image.
+ type: str
pod:
description:
- Run container in an existing pod.
If you want podman to make the pod for you, prefix the pod name
with "new:"
type: str
+ pod_id_file:
+ description:
+ - Run container in an existing pod and read the pod's ID from the specified file.
+ When a container is run within a pod which has an infra-container,
+ the infra-container starts first.
+ type: path
+ preserve_fd:
+ description:
+ - Pass down to the process the additional file descriptors specified in the comma separated list.
+ type: list
+ elements: str
+ preserve_fds:
+ description:
+ - Pass down to the process N additional file descriptors (in addition to 0, 1, 2). The total FDs are 3\+N.
+ type: str
privileged:
description:
- Give extended privileges to this container. The default is false.
@@ -724,6 +858,15 @@ options:
- Publish all exposed ports to random ports on the host interfaces. The
default is false.
type: bool
+ pull:
+ description:
+ - Pull image policy. The default is 'missing'.
+ type: str
+ choices:
+ - 'missing'
+ - 'always'
+ - 'never'
+ - 'newer'
quadlet_dir:
description:
- Path to the directory to write quadlet file in.
@@ -740,6 +883,10 @@ options:
options as a list of lines to add.
type: list
elements: str
+ rdt_class:
+ description:
+ - Rdt-class sets the class of service (CLOS or COS) for the container to run in. Requires root.
+ type: str
read_only:
description:
- Mount the container's root filesystem as read only. Default is false
@@ -779,6 +926,15 @@ options:
- Seconds to wait before forcibly stopping the container when restarting. Use -1 for infinite wait.
Applies to "restarted" status.
type: str
+ retry:
+ description:
+ - Number of times to retry pulling or pushing images between the registry and local storage in case of failure.
+ Default is 3.
+ type: int
+ retry_delay:
+ description:
+ - Duration of delay between retry attempts when pulling or pushing images between the registry and local storage in case of failure.
+ type: str
rm:
description:
- Automatically remove the container when it exits. The default is false.
@@ -786,6 +942,11 @@ options:
aliases:
- remove
- auto_remove
+ rmi:
+ description:
+ - After exit of the container, remove the image unless another container is using it.
+ Implies --rm on the new container. The default is false.
+ type: bool
rootfs:
description:
- If true, the first argument refers to an exploded container on the file
@@ -803,6 +964,10 @@ options:
L(documentation,https://docs.podman.io/en/latest/markdown/podman-run.1.html#secret-secret-opt-opt) for more details.
type: list
elements: str
+ seccomp_policy:
+ description:
+ - Specify the policy to select the seccomp profile.
+ type: str
security_opt:
description:
- Security Options. For example security_opt "seccomp=unconfined"
@@ -817,6 +982,10 @@ options:
If you omit the unit, the system uses bytes. If you omit the size
entirely, the system uses 64m
type: str
+ shm_size_systemd:
+ description:
+ - Size of systemd-specific tmpfs mounts such as /run, /run/lock, /var/log/journal and /tmp.
+ type: str
sig_proxy:
description:
- Proxy signals sent to the podman run command to the container process.
@@ -853,6 +1022,11 @@ options:
description:
- Run container in systemd mode. The default is true.
type: str
+ timeout:
+ description:
+ - Maximum time (in seconds) a container is allowed to run before conmon sends it the kill signal.
+ By default containers run until they exit or are stopped by "podman stop".
+ type: int
timezone:
description:
- Set timezone in container. This flag takes area-based timezones,
@@ -861,6 +1035,10 @@ options:
See /usr/share/zoneinfo/ for valid timezones.
Remote connections use local containers.conf for defaults.
type: str
+ tls_verify:
+ description:
+ - Require HTTPS and verify certificates when pulling images.
+ type: bool
tmpfs:
description:
- Create a tmpfs mount. For example tmpfs
@@ -882,6 +1060,20 @@ options:
elements: str
aliases:
- ulimits
+ umask:
+ description:
+ - Set the umask inside the container. Defaults to 0022.
+ Remote connections use local containers.conf for defaults.
+ type: str
+ unsetenv:
+ description:
+ - Unset default environment variables for the container.
+ type: list
+ elements: str
+ unsetenv_all:
+ description:
+ - Unset all default environment variables for the container.
+ type: bool
user:
description:
- Sets the username or UID used and optionally the groupname or GID for
@@ -899,6 +1091,10 @@ options:
description:
- Set the UTS mode for the container
type: str
+ variant:
+ description:
+ - Use VARIANT instead of the default architecture variant of the container image.
+ type: str
volume:
description:
- Create a bind mount. If you specify, volume /HOST-DIR:/CONTAINER-DIR,
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_image.py b/ansible_collections/containers/podman/plugins/modules/podman_image.py
index 7fcb0041a..a46a6c3c5 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_image.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_image.py
@@ -42,6 +42,10 @@ DOCUMENTATION = r'''
description: Whether or not to pull the image.
default: True
type: bool
+ pull_extra_args:
+ description:
+ - Extra arguments to pass to the pull command.
+ type: str
push:
description: Whether or not to push an image.
default: False
@@ -67,7 +71,8 @@ DOCUMENTATION = r'''
- quadlet
validate_certs:
description:
- - Require HTTPS and validate certificates when pulling or pushing. Also used during build if a pull or push is necessary.
+ - Require HTTPS and validate certificates when pulling or pushing.
+ Also used during build if a pull or push is necessary.
type: bool
aliases:
- tlsverify
@@ -94,9 +99,15 @@ DOCUMENTATION = r'''
- build_args
- buildargs
suboptions:
+ container_file:
+ description:
+ - Content of the Containerfile to use for building the image.
+ Mutually exclusive with the C(file) option which is path to the existing Containerfile.
+ type: str
file:
description:
- Path to the Containerfile if it is not in the build context directory.
+ Mutually exclusive with the C(container_file) option.
type: path
volume:
description:
@@ -105,7 +116,8 @@ DOCUMENTATION = r'''
elements: str
annotation:
description:
- - Dictionary of key=value pairs to add to the image. Only works with OCI images. Ignored for Docker containers.
+ - Dictionary of key=value pairs to add to the image. Only works with OCI images.
+ Ignored for Docker containers.
type: dict
force_rm:
description:
@@ -148,7 +160,7 @@ DOCUMENTATION = r'''
type: bool
format:
description:
- - Manifest type to use when pushing an image using the 'dir' transport (default is manifest type of source).
+ - Manifest type to use when pushing an image using the 'dir' transport (default is manifest type of source)
type: str
choices:
- oci
@@ -168,14 +180,19 @@ DOCUMENTATION = r'''
- destination
transport:
description:
- - Transport to use when pushing in image. If no transport is set, will attempt to push to a remote registry.
+ - Transport to use when pushing in image. If no transport is set, will attempt to push to a remote registry
type: str
choices:
- dir
+ - docker
- docker-archive
- docker-daemon
- oci-archive
- ostree
+ extra_args:
+ description:
+ - Extra args to pass to push, if executed. Does not idempotently check for new push args.
+ type: str
quadlet_dir:
description:
- Path to the directory to write quadlet file in.
@@ -300,6 +317,15 @@ EXAMPLES = r"""
name: nginx
arch: amd64
+- name: Build a container from file inline
+ containers.podman.podman_image:
+ name: mycustom_image
+ state: build
+ build:
+ container_file: |-
+ FROM alpine:latest
+ CMD echo "Hello, World!"
+
- name: Create a quadlet file for an image
containers.podman.podman_image:
name: docker.io/library/alpine:latest
@@ -333,7 +359,7 @@ RETURN = r"""
"/app-entrypoint.sh"
],
"Env": [
- "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:...",
"IMAGE_OS=debian-9",
"NAMI_VERSION=1.0.0-1",
"GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net",
@@ -373,10 +399,10 @@ RETURN = r"""
"Digest": "sha256:5a8ab28e314c2222de3feaf6dac94a0436a37fc08979d2722c99d2bef2619a9b",
"GraphDriver": {
"Data": {
- "LowerDir": "/var/lib/containers/storage/overlay/142c1beadf1bb09fbd929465ec98c9dca3256638220450efb4214727d0d0680e/diff:/var/lib/containers/s",
- "MergedDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/merged",
- "UpperDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/diff",
- "WorkDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/work"
+ "LowerDir": "/var/lib/containers/storage/overlay/142c1beadf1bb09fbd929465e..../diff:/var/lib/containers/s",
+ "MergedDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99/merged",
+ "UpperDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99/diff",
+ "WorkDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99/work"
},
"Name": "overlay"
},
@@ -434,9 +460,12 @@ RETURN = r"""
]
"""
-import json
-import re
-import shlex
+import json # noqa: E402
+import os # noqa: E402
+import re # noqa: E402
+import shlex # noqa: E402
+import tempfile # noqa: E402
+import time # noqa: E402
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import AnsibleModule
@@ -456,6 +485,7 @@ class PodmanImageManager(object):
self.executable = self.module.get_bin_path(module.params.get('executable'), required=True)
self.tag = self.module.params.get('tag')
self.pull = self.module.params.get('pull')
+ self.pull_extra_args = self.module.params.get('pull_extra_args')
self.push = self.module.params.get('push')
self.path = self.module.params.get('path')
self.force = self.module.params.get('force')
@@ -509,7 +539,7 @@ class PodmanImageManager(object):
if not layer_ids:
layer_ids = lines.splitlines()
- return (layer_ids[-1])
+ return layer_ids[-1]
def present(self):
image = self.find_image()
@@ -520,9 +550,18 @@ class PodmanImageManager(object):
digest_before = None
if not image or self.force:
- if self.path:
+ if self.state == 'build' or self.path:
# Build the image
- self.results['actions'].append('Built image {image_name} from {path}'.format(image_name=self.image_name, path=self.path))
+ build_file = self.build.get('file') if self.build else None
+ container_file_txt = self.build.get('container_file') if self.build else None
+ if build_file and container_file_txt:
+ self.module.fail_json(msg='Cannot specify both build file and container file content!')
+ if not self.path and build_file:
+ self.path = os.path.dirname(build_file)
+ elif not self.path and not build_file and not container_file_txt:
+ self.module.fail_json(msg='Path to build context or file is required when building an image')
+ self.results['actions'].append('Built image {image_name} from {path}'.format(
+ image_name=self.image_name, path=self.path or 'default context'))
if not self.module.check_mode:
self.results['image'], self.results['stdout'] = self.build_image()
image = self.results['image']
@@ -541,16 +580,8 @@ class PodmanImageManager(object):
self.results['changed'] = True
if self.push:
- # Push the image
- if '/' in self.image_name:
- push_format_string = 'Pushed image {image_name}'
- else:
- push_format_string = 'Pushed image {image_name} to {dest}'
- self.results['actions'].append(push_format_string.format(image_name=self.image_name, dest=self.push_args['dest']))
- self.results['changed'] = True
- if not self.module.check_mode:
- self.results['image'], output = self.push_image()
- self.results['stdout'] += "\n" + output
+ self.results['image'], output = self.push_image()
+ self.results['stdout'] += "\n" + output
if image and not self.results.get('image'):
self.results['image'] = image
@@ -654,13 +685,18 @@ class PodmanImageManager(object):
if self.ca_cert_dir:
args.extend(['--cert-dir', self.ca_cert_dir])
+ if self.pull_extra_args:
+ args.extend(shlex.split(self.pull_extra_args))
+
rc, out, err = self._run(args, ignore_errors=True)
if rc != 0:
if not self.pull:
- self.module.fail_json(msg='Failed to find image {image_name} locally, image pull set to {pull_bool}'.format(
- pull_bool=self.pull, image_name=image_name))
+ self.module.fail_json(
+ msg='Failed to find image {image_name} locally, image pull set to {pull_bool}'.format(
+ pull_bool=self.pull, image_name=image_name))
else:
- self.module.fail_json(msg='Failed to pull image {image_name}'.format(image_name=image_name))
+ self.module.fail_json(
+ msg='Failed to pull image {image_name}'.format(image_name=image_name))
return self.inspect_image(out.strip())
def build_image(self):
@@ -697,6 +733,17 @@ class PodmanImageManager(object):
containerfile = self.build.get('file')
if containerfile:
args.extend(['--file', containerfile])
+ container_file_txt = self.build.get('container_file')
+ if container_file_txt:
+ # create a temporarly file with the content of the Containerfile
+ if self.path:
+ container_file_path = os.path.join(self.path, 'Containerfile.generated_by_ansible_%s' % time.time())
+ else:
+ container_file_path = os.path.join(
+ tempfile.gettempdir(), 'Containerfile.generated_by_ansible_%s' % time.time())
+ with open(container_file_path, 'w') as f:
+ f.write(container_file_txt)
+ args.extend(['--file', container_file_path])
volume = self.build.get('volume')
if volume:
@@ -717,13 +764,16 @@ class PodmanImageManager(object):
target = self.build.get('target')
if target:
args.extend(['--target', target])
-
- args.append(self.path)
+ if self.path:
+ args.append(self.path)
rc, out, err = self._run(args, ignore_errors=True)
if rc != 0:
- self.module.fail_json(msg="Failed to build image {image}: {out} {err}".format(image=self.image_name, out=out, err=err))
-
+ self.module.fail_json(msg="Failed to build image {image}: {out} {err}".format(
+ image=self.image_name, out=out, err=err))
+ # remove the temporary file if it was created
+ if container_file_txt:
+ os.remove(container_file_path)
last_id = self._get_id_from_output(out, startswith='-->')
return self.inspect_image(last_id), out + err
@@ -760,49 +810,55 @@ class PodmanImageManager(object):
if sign_by_key:
args.extend(['--sign-by', sign_by_key])
+ push_extra_args = self.push_args.get('extra_args')
+ if push_extra_args:
+ args.extend(shlex.split(push_extra_args))
+
args.append(self.image_name)
# Build the destination argument
dest = self.push_args.get('dest')
- dest_format_string = '{dest}/{image_name}'
- regexp = re.compile(r'/{name}(:{tag})?'.format(name=self.name, tag=self.tag))
- if not dest:
- if '/' not in self.name:
- self.module.fail_json(msg="'push_args['dest']' is required when pushing images that do not have the remote registry in the image name")
-
- # If the push destination contains the image name and/or the tag
- # remove it and warn since it's not needed.
- elif regexp.search(dest):
- dest = regexp.sub('', dest)
- self.module.warn("Image name and tag are automatically added to push_args['dest']. Destination changed to {dest}".format(dest=dest))
+ transport = self.push_args.get('transport')
- if dest and dest.endswith('/'):
- dest = dest[:-1]
+ if dest is None:
+ dest = self.image_name
- transport = self.push_args.get('transport')
if transport:
- if not dest:
- self.module.fail_json("'push_args['transport'] requires 'push_args['dest'] but it was not provided.")
if transport == 'docker':
dest_format_string = '{transport}://{dest}'
elif transport == 'ostree':
dest_format_string = '{transport}:{name}@{dest}'
else:
dest_format_string = '{transport}:{dest}'
-
- dest_string = dest_format_string.format(transport=transport, name=self.name, dest=dest, image_name=self.image_name,)
-
- # Only append the destination argument if the image name is not a URL
- if '/' not in self.name:
- args.append(dest_string)
-
- rc, out, err = self._run(args, ignore_errors=True)
- if rc != 0:
- self.module.fail_json(msg="Failed to push image {image_name}: {err}".format(image_name=self.image_name, err=err))
- last_id = self._get_id_from_output(
- out + err, contains=':', split_on=':')
-
- return self.inspect_image(last_id), out + err
+ if transport == 'docker-daemon' and ":" not in dest:
+ dest_format_string = '{transport}:{dest}:latest'
+ dest_string = dest_format_string.format(transport=transport, name=self.name, dest=dest)
+ else:
+ dest_string = dest
+ # In case of dest as a repository with org name only, append image name to it
+ if ":" not in dest and "@" not in dest and len(dest.rstrip("/").split("/")) == 2:
+ dest_string = dest.rstrip("/") + "/" + self.image_name
+
+ if "/" not in dest_string and "@" not in dest_string and "docker-daemon" not in dest_string:
+ self.module.fail_json(msg="Destination must be a full URL or path to a directory.")
+
+ args.append(dest_string)
+ self.module.log("PODMAN-IMAGE-DEBUG: Pushing image {image_name} to {dest_string}".format(
+ image_name=self.image_name, dest_string=dest_string))
+ self.results['actions'].append(" ".join(args))
+ self.results['podman_actions'].append(" ".join([self.executable] + args))
+ self.results['changed'] = True
+ out, err = '', ''
+ if not self.module.check_mode:
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ self.module.fail_json(msg="Failed to push image {image_name}".format(
+ image_name=self.image_name),
+ stdout=out, stderr=err,
+ actions=self.results['actions'],
+ podman_actions=self.results['podman_actions'])
+
+ return self.inspect_image(self.image_name), out + err
def remove_image(self, image_name=None):
if image_name is None:
@@ -813,7 +869,8 @@ class PodmanImageManager(object):
args.append('--force')
rc, out, err = self._run(args, ignore_errors=True)
if rc != 0:
- self.module.fail_json(msg='Failed to remove image {image_name}. {err}'.format(image_name=image_name, err=err))
+ self.module.fail_json(msg='Failed to remove image {image_name}. {err}'.format(
+ image_name=image_name, err=err))
return out
def remove_image_id(self, image_id=None):
@@ -847,6 +904,7 @@ def main():
arch=dict(type='str'),
tag=dict(type='str', default='latest'),
pull=dict(type='bool', default=True),
+ pull_extra_args=dict(type='str'),
push=dict(type='bool', default=False),
path=dict(type='str'),
force=dict(type='bool', default=False),
@@ -868,6 +926,7 @@ def main():
annotation=dict(type='dict'),
force_rm=dict(type='bool', default=False),
file=dict(type='path'),
+ container_file=dict(type='str'),
format=dict(
type='str',
choices=['oci', 'docker'],
@@ -889,6 +948,7 @@ def main():
remove_signatures=dict(type='bool'),
sign_by=dict(type='str'),
dest=dict(type='str', aliases=['destination'],),
+ extra_args=dict(type='str'),
transport=dict(
type='str',
choices=[
@@ -897,6 +957,7 @@ def main():
'docker-daemon',
'oci-archive',
'ostree',
+ 'docker'
]
),
),
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_network.py b/ansible_collections/containers/podman/plugins/modules/podman_network.py
index 37bfefede..7623fffc1 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_network.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_network.py
@@ -33,6 +33,12 @@ options:
description:
- disable dns plugin (default "false")
type: bool
+ dns:
+ description:
+ - Set network-scoped DNS resolver/nameserver for containers in this network.
+ If not set, the host servers from /etc/resolv.conf is used.
+ type: list
+ elements: str
driver:
description:
- Driver to manage the network (default "bridge")
@@ -61,11 +67,27 @@ options:
description:
- Allocate container IP from range
type: str
+ ipam_driver:
+ description:
+ - Set the ipam driver (IP Address Management Driver) for the network.
+ When unset podman chooses an ipam driver automatically based on the network driver
+ type: str
+ choices:
+ - host-local
+ - dhcp
+ - none
ipv6:
description:
- Enable IPv6 (Dual Stack) networking. You must pass a IPv6 subnet.
The subnet option must be used with the ipv6 option.
+ Idempotency is not supported because it generates subnets randomly.
type: bool
+ route:
+ description:
+ - A static route in the format <destination in CIDR notation>,<gateway>,<route metric (optional)>.
+ This route will be added to every container in this network.
+ type: list
+ elements: str
subnet:
description:
- Subnet in CIDR format
@@ -74,6 +96,29 @@ options:
description:
- Create a Macvlan connection based on this device
type: str
+ net_config:
+ description:
+ - List of dictionaries with network configuration.
+ Each dictionary should contain 'subnet' and 'gateway' keys.
+ 'ip_range' is optional.
+ type: list
+ elements: dict
+ suboptions:
+ subnet:
+ description:
+ - Subnet in CIDR format
+ type: str
+ required: true
+ gateway:
+ description:
+ - Gateway for the subnet
+ type: str
+ required: true
+ ip_range:
+ description:
+ - Allocate container IP from range
+ type: str
+ required: false
opt:
description:
- Add network options. Currently 'vlan' and 'mtu' are supported.
@@ -297,6 +342,11 @@ class PodmanNetworkModuleParams:
def addparam_gateway(self, c):
return c + ['--gateway', self.params['gateway']]
+ def addparam_dns(self, c):
+ for dns in self.params['dns']:
+ c += ['--dns', dns]
+ return c
+
def addparam_driver(self, c):
return c + ['--driver', self.params['driver']]
@@ -312,6 +362,13 @@ class PodmanNetworkModuleParams:
def addparam_macvlan(self, c):
return c + ['--macvlan', self.params['macvlan']]
+ def addparam_net_config(self, c):
+ for net in self.params['net_config']:
+ for kw in ('subnet', 'gateway', 'ip_range'):
+ if kw in net and net[kw]:
+ c += ['--%s=%s' % (kw.replace('_', '-'), net[kw])]
+ return c
+
def addparam_interface_name(self, c):
return c + ['--interface-name', self.params['interface_name']]
@@ -326,6 +383,14 @@ class PodmanNetworkModuleParams:
for k in opt])]
return c
+ def addparam_route(self, c):
+ for route in self.params['route']:
+ c += ['--route', route]
+ return c
+
+ def addparam_ipam_driver(self, c):
+ return c + ['--ipam-driver=%s' % self.params['ipam_driver']]
+
def addparam_disable_dns(self, c):
return c + ['--disable-dns=%s' % self.params['disable_dns']]
@@ -337,7 +402,6 @@ class PodmanNetworkDefaults:
self.defaults = {
'driver': 'bridge',
'internal': False,
- 'ipv6': False
}
def default_dict(self):
@@ -385,32 +449,45 @@ class PodmanNetworkDiff:
before = after = self.params['disable_dns']
return self._diff_update_and_compare('disable_dns', before, after)
+ def diffparam_dns(self):
+ before = self.info.get('network_dns_servers', [])
+ after = self.params['dns'] or []
+ return self._diff_update_and_compare('dns', sorted(before), sorted(after))
+
def diffparam_driver(self):
# Currently only bridge is supported
before = after = 'bridge'
return self._diff_update_and_compare('driver', before, after)
def diffparam_ipv6(self):
- if LooseVersion(self.version) >= LooseVersion('4.0.0'):
- before = self.info.get('ipv6_enabled', False)
- after = self.params['ipv6']
- return self._diff_update_and_compare('ipv6', before, after)
- before = after = ''
- return self._diff_update_and_compare('ipv6', before, after)
+ # We don't support dual stack because it generates subnets randomly
+ return self._diff_update_and_compare('ipv6', '', '')
def diffparam_gateway(self):
# Disable idempotency of subnet for v4, subnets are added automatically
# TODO(sshnaidm): check if it's still the issue in v5
- if LooseVersion(self.version) >= LooseVersion('4.0.0'):
- return self._diff_update_and_compare('gateway', '', '')
- try:
- before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway']
- except (IndexError, KeyError):
- before = ''
- after = before
- if self.params['gateway'] is not None:
+ if LooseVersion(self.version) < LooseVersion('4.0.0'):
+ try:
+ before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway']
+ except (IndexError, KeyError):
+ before = ''
+ after = before
+ if self.params['gateway'] is not None:
+ after = self.params['gateway']
+ return self._diff_update_and_compare('gateway', before, after)
+ else:
+ before_subs = self.info.get('subnets')
after = self.params['gateway']
- return self._diff_update_and_compare('gateway', before, after)
+ if not before_subs:
+ before = None
+ if before_subs:
+ if len(before_subs) > 1 and after:
+ return self._diff_update_and_compare(
+ 'gateway', ",".join([i['gateway'] for i in before_subs]), after)
+ before = [i.get('gateway') for i in before_subs][0]
+ if not after:
+ after = before
+ return self._diff_update_and_compare('gateway', before, after)
def diffparam_internal(self):
if LooseVersion(self.version) >= LooseVersion('4.0.0'):
@@ -429,21 +506,62 @@ class PodmanNetworkDiff:
before = after = ''
return self._diff_update_and_compare('ip_range', before, after)
- def diffparam_subnet(self):
- # Disable idempotency of subnet for v4, subnets are added automatically
- # TODO(sshnaidm): check if it's still the issue in v5
- if LooseVersion(self.version) >= LooseVersion('4.0.0'):
- return self._diff_update_and_compare('subnet', '', '')
- try:
- before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet']
- except (IndexError, KeyError):
+ def diffparam_ipam_driver(self):
+ before = self.info.get("ipam_options", {}).get("driver", "")
+ after = self.params['ipam_driver']
+ if not after:
+ after = before
+ return self._diff_update_and_compare('ipam_driver', before, after)
+
+ def diffparam_net_config(self):
+ after = self.params['net_config']
+ if not after:
+ return self._diff_update_and_compare('net_config', '', '')
+ before_subs = self.info.get('subnets', [])
+ if before_subs:
+ before = ":".join(sorted([",".join([i['subnet'], i['gateway']]).rstrip(",") for i in before_subs]))
+ else:
before = ''
- after = before
- if self.params['subnet'] is not None:
+ after = ":".join(sorted([",".join([i['subnet'], i['gateway']]).rstrip(",") for i in after]))
+ return self._diff_update_and_compare('net_config', before, after)
+
+ def diffparam_route(self):
+ routes = self.info.get('routes', [])
+ if routes:
+ before = [",".join([
+ r['destination'], r['gateway'], str(r.get('metric', ''))]).rstrip(",") for r in routes]
+ else:
+ before = []
+ after = self.params['route'] or []
+ return self._diff_update_and_compare('route', sorted(before), sorted(after))
+
+ def diffparam_subnet(self):
+ # Disable idempotency of subnet for v3 and below
+ if LooseVersion(self.version) < LooseVersion('4.0.0'):
+ try:
+ before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet']
+ except (IndexError, KeyError):
+ before = ''
+ after = before
+ if self.params['subnet'] is not None:
+ after = self.params['subnet']
+ if HAS_IP_ADDRESS_MODULE:
+ after = ipaddress.ip_network(after).compressed
+ return self._diff_update_and_compare('subnet', before, after)
+ else:
+ if self.params['ipv6'] is not None:
+ # We can't support dual stack, it generates subnets randomly
+ return self._diff_update_and_compare('subnet', '', '')
after = self.params['subnet']
- if HAS_IP_ADDRESS_MODULE:
- after = ipaddress.ip_network(after).compressed
- return self._diff_update_and_compare('subnet', before, after)
+ if after is None:
+ # We can't guess what subnet was used before by default
+ return self._diff_update_and_compare('subnet', '', '')
+ before = self.info.get('subnets')
+ if before:
+ if len(before) > 1 and after:
+ return self._diff_update_and_compare('subnet', ",".join([i['subnet'] for i in before]), after)
+ before = [i['subnet'] for i in before][0]
+ return self._diff_update_and_compare('subnet', before, after)
def diffparam_macvlan(self):
before = after = ''
@@ -694,12 +812,15 @@ def main():
choices=['present', 'absent', 'quadlet']),
name=dict(type='str', required=True),
disable_dns=dict(type='bool', required=False),
+ dns=dict(type='list', elements='str', required=False),
driver=dict(type='str', required=False),
force=dict(type='bool', default=False),
gateway=dict(type='str', required=False),
interface_name=dict(type='str', required=False),
internal=dict(type='bool', required=False),
ip_range=dict(type='str', required=False),
+ ipam_driver=dict(type='str', required=False,
+ choices=['host-local', 'dhcp', 'none']),
ipv6=dict(type='bool', required=False),
subnet=dict(type='str', required=False),
macvlan=dict(type='str', required=False),
@@ -715,14 +836,23 @@ def main():
executable=dict(type='str', required=False, default='podman'),
debug=dict(type='bool', default=False),
recreate=dict(type='bool', default=False),
+ route=dict(type='list', elements='str', required=False),
quadlet_dir=dict(type='path', required=False),
quadlet_filename=dict(type='str', required=False),
quadlet_options=dict(type='list', elements='str', required=False),
+ net_config=dict(type='list', required=False, elements='dict',
+ options=dict(
+ subnet=dict(type='str', required=True),
+ gateway=dict(type='str', required=True),
+ ip_range=dict(type='str', required=False),
+ )),
),
required_by=dict( # for IP range and GW to set 'subnet' is required
ip_range=('subnet'),
gateway=('subnet'),
- ))
+ ),
+ # define or subnet or net config
+ mutually_exclusive=[['subnet', 'net_config']])
PodmanNetworkManager(module).execute()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_pod.py b/ansible_collections/containers/podman/plugins/modules/podman_pod.py
index a975921ea..cdf728243 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_pod.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_pod.py
@@ -117,6 +117,8 @@ options:
all containers in the pod.
type: list
elements: str
+ aliases:
+ - dns_option
required: false
dns_search:
description:
@@ -125,6 +127,14 @@ options:
type: list
elements: str
required: false
+ exit_policy:
+ description:
+ - Set the exit policy of the pod when the last container exits. Supported policies are stop and continue
+ choices:
+ - stop
+ - continue
+ type: str
+ required: false
generate_systemd:
description:
- Generate systemd unit file for container.
@@ -227,6 +237,11 @@ options:
elements: str
required: false
type: list
+ gpus:
+ description:
+ - GPU devices to add to the container ('all' to pass all GPUs).
+ type: str
+ required: false
hostname:
description:
- Set a hostname to the pod
@@ -266,6 +281,11 @@ options:
- Set a static IP for the pod's shared network.
type: str
required: false
+ ip6:
+ description:
+ - Set a static IPv6 for the pod's shared network.
+ type: str
+ required: false
label:
description:
- Add metadata to a pod, pass dictionary of label keys and values.
@@ -357,6 +377,16 @@ options:
options as a list of lines to add.
type: list
elements: str
+ restart_policy:
+ description:
+ - Restart policy to follow when containers exit.
+ type: str
+ security_opt:
+ description:
+ - Security options for the pod.
+ type: list
+ elements: str
+ required: false
share:
description:
- A comma delimited list of kernel namespaces to share. If none or "" is specified,
@@ -364,6 +394,30 @@ options:
user, uts.
type: str
required: false
+ share_parent:
+ description:
+ - This boolean determines whether or not all containers entering the pod use the pod as their cgroup parent.
+ The default value of this option in Podman is true.
+ type: bool
+ required: false
+ shm_size:
+ description:
+ - Set the size of the /dev/shm shared memory space.
+ A unit can be b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes).
+ If the unit is omitted, the system uses bytes.
+ If the size is omitted, the default is 64m.
+ When size is 0, there is no limit on the amount of memory used for IPC by the pod.
+ type: str
+ required: false
+ shm_size_systemd:
+ description:
+ - Size of systemd-specific tmpfs mounts such as /run, /run/lock, /var/log/journal and /tmp.
+ A unit can be b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes).
+ If the unit is omitted, the system uses bytes.
+ If the size is omitted, the default is 64m.
+ When size is 0, the usage is limited to 50 percents of the host's available memory.
+ type: str
+ required: false
subgidname:
description:
- Name for GID map from the /etc/subgid file. Using this flag will run the container
@@ -377,6 +431,11 @@ options:
This flag conflicts with `userns` and `uidmap`.
required: false
type: str
+ sysctl:
+ description:
+ - Set kernel parameters for the pod.
+ type: dict
+ required: false
uidmap:
description:
- Run the container in a new user namespace using the supplied mapping.
@@ -393,6 +452,11 @@ options:
An empty value ("") means user namespaces are disabled.
required: false
type: str
+ uts:
+ description:
+ - Set the UTS namespace mode for the pod.
+ required: false
+ type: str
volume:
description:
- Create a bind mount.
@@ -401,6 +465,12 @@ options:
elements: str
required: false
type: list
+ volumes_from:
+ description:
+ - Mount volumes from the specified container.
+ elements: str
+ required: false
+ type: list
executable:
description:
- Path to C(podman) executable if it is not in the C($PATH) on the
@@ -450,7 +520,7 @@ pod:
'''
-EXAMPLES = '''
+EXAMPLES = r'''
# What modules does for example
- containers.podman.podman_pod:
name: pod1
@@ -465,6 +535,62 @@ EXAMPLES = '''
state: started
publish: "127.0.0.1::80"
+# Full workflow example with pod and containers
+- name: Create a pod with parameters
+ containers.podman.podman_pod:
+ name: mypod
+ state: created
+ network: host
+ share: net
+ userns: auto
+ security_opt:
+ - seccomp=unconfined
+ - apparmor=unconfined
+ hostname: mypod
+ dns:
+ - 1.1.1.1
+ volumes:
+ - /tmp:/tmp/:ro
+ label:
+ key: cval
+ otherkey: kddkdk
+ somekey: someval
+ add_host:
+ - "google:5.5.5.5"
+
+- name: Create containers attached to the pod
+ containers.podman.podman_container:
+ name: "{{ item }}"
+ state: created
+ pod: mypod
+ image: alpine
+ command: sleep 1h
+ loop:
+ - "container1"
+ - "container2"
+
+- name: Start pod
+ containers.podman.podman_pod:
+ name: mypod
+ state: started
+ network: host
+ share: net
+ userns: auto
+ security_opt:
+ - seccomp=unconfined
+ - apparmor=unconfined
+ hostname: mypod
+ dns:
+ - 1.1.1.1
+ volumes:
+ - /tmp:/tmp/:ro
+ label:
+ key: cval
+ otherkey: kddkdk
+ somekey: someval
+ add_host:
+ - "google:5.5.5.5"
+
# Create a Quadlet file for a pod
- containers.podman.podman_pod:
name: qpod
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_search.py b/ansible_collections/containers/podman/plugins/modules/podman_search.py
new file mode 100644
index 000000000..128e3ce03
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_search.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python
+# Copyright (c) 2024 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+module: podman_search
+author:
+ - Derek Waters (@derekwaters)
+short_description: Search for remote images using podman
+notes:
+ - Podman may required elevated privileges in order to run properly.
+description:
+ - Search for remote images using C(podman)
+options:
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman)
+ default: 'podman'
+ type: str
+ term:
+ description:
+ - The search term to look for. Will search all default registries unless a registry is defined in the search term.
+ type: str
+ required: True
+ limit:
+ description:
+ - Limit the number of image results returned from the search (per image registry)
+ required: False
+ default: 25
+ type: int
+ list_tags:
+ description:
+ - Whether or not to return the list of tags associated with each image
+ required: False
+ default: False
+ type: bool
+
+'''
+
+EXAMPLES = r"""
+- name: Search for any rhel images
+ containers.podman.podman_search:
+ term: "rhel"
+ limit: 3
+
+- name: Gather info on a specific remote image
+ containers.podman.podman_search:
+ term: "myimageregistry.com/ansible-automation-platform/ee-minimal-rhel8"
+
+- name: Gather tag info on a known remote image
+ containers.podman.podman_search:
+ term: "myimageregistry.com/ansible-automation-platform/ee-minimal-rhel8"
+ list_tags: True
+"""
+
+RETURN = r"""
+images:
+ description: info from all or specified images
+ returned: always
+ type: list
+ sample: [
+ {
+ "Automated": "",
+ "Description": "Red Hat Enterprise Linux Atomic Image is a minimal, fully supported base image.",
+ "Index": "registry.access.redhat.com",
+ "Name": "registry.access.redhat.com/rhel7-atomic",
+ "Official": "",
+ "Stars": 0,
+ "Tags": ["1.0", "1.1", "1.1.1-devel"]
+ }
+ ]
+"""
+
+import json
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def search_images(module, executable, term, limit, list_tags):
+ command = [executable, 'search', term, '--format', 'json']
+ command.extend(['--limit', "{0}".format(limit)])
+ if list_tags:
+ command.extend(['--list-tags'])
+
+ rc, out, err = module.run_command(command)
+
+ if rc != 0:
+ module.fail_json(msg="Unable to gather info for '{0}': {1}".format(term, err))
+ return out
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ term=dict(type='str', required=True),
+ limit=dict(type='int', required=False, default=25),
+ list_tags=dict(type='bool', required=False, default=False)
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.params['executable']
+ term = module.params.get('term')
+ limit = module.params.get('limit')
+ list_tags = module.params.get('list_tags')
+ executable = module.get_bin_path(executable, required=True)
+
+ result_str = search_images(module, executable, term, limit, list_tags)
+ if result_str == "":
+ results = []
+ else:
+ try:
+ results = json.loads(result_str)
+ except json.decoder.JSONDecodeError:
+ module.fail_json(msg='Failed to parse JSON output from podman search: {out}'.format(out=result_str))
+
+ results = dict(
+ changed=False,
+ images=results
+ )
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_secret.py b/ansible_collections/containers/podman/plugins/modules/podman_secret.py
index a31aae9dc..76b10ad39 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_secret.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_secret.py
@@ -21,6 +21,7 @@ options:
data:
description:
- The value of the secret. Required when C(state) is C(present).
+ Mutually exclusive with C(env) and C(path).
type: str
driver:
description:
@@ -31,6 +32,11 @@ options:
description:
- Driver-specific key-value options.
type: dict
+ env:
+ description:
+ - The name of the environment variable that contains the secret.
+ Mutually exclusive with C(data) and C(path).
+ type: str
executable:
description:
- Path to C(podman) executable if it is not in the C($PATH) on the
@@ -53,6 +59,11 @@ options:
- The name of the secret.
required: True
type: str
+ path:
+ description:
+ - Path to the file that contains the secret.
+ Mutually exclusive with C(data) and C(env).
+ type: path
state:
description:
- Whether to create or remove the named secret.
@@ -67,7 +78,7 @@ options:
type: dict
debug:
description:
- - Enable debug mode for module.
+ - Enable debug mode for module. It prints secrets diff.
type: bool
default: False
'''
@@ -99,6 +110,8 @@ EXAMPLES = r"""
name: mysecret
"""
+import os
+
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion
from ansible_collections.containers.podman.plugins.module_utils.podman.common import get_podman_version
@@ -116,14 +129,15 @@ def podman_secret_exists(module, executable, name, version):
return rc == 0
-def need_update(module, executable, name, data, driver, driver_opts, debug, labels):
-
+def need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels):
cmd = [executable, 'secret', 'inspect', '--showsecret', name]
rc, out, err = module.run_command(cmd)
if rc != 0:
if debug:
module.log("PODMAN-SECRET-DEBUG: Unable to get secret info: %s" % err)
return True
+ if skip:
+ return False
try:
secret = module.from_json(out)[0]
# We support only file driver for now
@@ -131,10 +145,37 @@ def need_update(module, executable, name, data, driver, driver_opts, debug, labe
if debug:
module.log("PODMAN-SECRET-DEBUG: Idempotency of driver %s is not supported" % driver)
return True
- if secret['SecretData'] != data:
- diff['after'] = "<different-secret>"
- diff['before'] = "<secret>"
- return True
+ if data:
+ if secret['SecretData'] != data:
+ if debug:
+ diff['after'] = data
+ diff['before'] = secret['SecretData']
+ else:
+ diff['after'] = "<different-secret>"
+ diff['before'] = "<secret>"
+ return True
+ if path:
+ with open(path, 'rb') as f:
+ text = f.read().decode('utf-8')
+ if secret['SecretData'] != text:
+ if debug:
+ diff['after'] = text
+ diff['before'] = secret['SecretData']
+ else:
+ diff['after'] = "<different-secret>"
+ diff['before'] = "<secret>"
+ return True
+ if env:
+ env_data = os.environ.get(env)
+ if secret['SecretData'] != env_data:
+ if debug:
+ diff['after'] = env_data
+ diff['before'] = secret['SecretData']
+ else:
+ diff['after'] = "<different-secret>"
+ diff['before'] = "<secret>"
+ return True
+
if driver_opts:
for k, v in driver_opts.items():
if secret['Spec']['Driver']['Options'].get(k) != v:
@@ -154,13 +195,13 @@ def need_update(module, executable, name, data, driver, driver_opts, debug, labe
return False
-def podman_secret_create(module, executable, name, data, force, skip,
+def podman_secret_create(module, executable, name, data, path, env, force, skip,
driver, driver_opts, debug, labels):
podman_version = get_podman_version(module, fail=False)
if (podman_version is not None and
LooseVersion(podman_version) >= LooseVersion('4.7.0')
and (driver is None or driver == 'file')):
- if not skip and need_update(module, executable, name, data, driver, driver_opts, debug, labels):
+ if need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels):
podman_secret_remove(module, executable, name)
else:
return {"changed": False}
@@ -182,9 +223,20 @@ def podman_secret_create(module, executable, name, data, force, skip,
cmd.append('--label')
cmd.append("=".join([k, v]))
cmd.append(name)
- cmd.append('-')
+ if data:
+ cmd.append('-')
+ elif path:
+ cmd.append(path)
+ elif env:
+ if os.environ.get(env) is None:
+ module.fail_json(msg="Environment variable %s is not set" % env)
+ cmd.append("--env")
+ cmd.append(env)
- rc, out, err = module.run_command(cmd, data=data, binary_data=True)
+ if data:
+ rc, out, err = module.run_command(cmd, data=data, binary_data=True)
+ else:
+ rc, out, err = module.run_command(cmd)
if rc != 0:
module.fail_json(msg="Unable to create secret: %s" % err)
@@ -219,6 +271,8 @@ def main():
state=dict(type='str', default='present', choices=['absent', 'present']),
name=dict(type='str', required=True),
data=dict(type='str', no_log=True),
+ env=dict(type='str'),
+ path=dict(type='path'),
force=dict(type='bool', default=False),
skip_existing=dict(type='bool', default=False),
driver=dict(type='str'),
@@ -226,6 +280,8 @@ def main():
labels=dict(type='dict'),
debug=dict(type='bool', default=False),
),
+ required_if=[('state', 'present', ['path', 'env', 'data'], True)],
+ mutually_exclusive=[['path', 'env', 'data']],
)
state = module.params['state']
@@ -234,16 +290,16 @@ def main():
if state == 'present':
data = module.params['data']
- if data is None:
- raise Exception("'data' is required when 'state' is 'present'")
force = module.params['force']
skip = module.params['skip_existing']
driver = module.params['driver']
driver_opts = module.params['driver_opts']
debug = module.params['debug']
labels = module.params['labels']
+ path = module.params['path']
+ env = module.params['env']
results = podman_secret_create(module, executable,
- name, data, force, skip,
+ name, data, path, env, force, skip,
driver, driver_opts, debug, labels)
else:
results = podman_secret_remove(module, executable, name)
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_volume.py b/ansible_collections/containers/podman/plugins/modules/podman_volume.py
index 0b990354a..cb958cc50 100644
--- a/ansible_collections/containers/podman/plugins/modules/podman_volume.py
+++ b/ansible_collections/containers/podman/plugins/modules/podman_volume.py
@@ -24,6 +24,8 @@ options:
choices:
- present
- absent
+ - mounted
+ - unmounted
- quadlet
recreate:
description:
@@ -131,6 +133,7 @@ EXAMPLES = '''
'''
# noqa: F402
import json # noqa: F402
+import os # noqa: F402
from ansible.module_utils.basic import AnsibleModule # noqa: F402
from ansible.module_utils._text import to_bytes, to_native # noqa: F402
@@ -160,7 +163,7 @@ class PodmanVolumeModuleParams:
Returns:
list -- list of byte strings for Popen command
"""
- if self.action in ['delete']:
+ if self.action in ['delete', 'mount', 'unmount']:
return self._simple_action()
if self.action in ['create']:
return self._create_action()
@@ -169,6 +172,12 @@ class PodmanVolumeModuleParams:
if self.action == 'delete':
cmd = ['rm', '-f', self.params['name']]
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
+ if self.action == 'mount':
+ cmd = ['mount', self.params['name']]
+ return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
+ if self.action == 'unmount':
+ cmd = ['unmount', self.params['name']]
+ return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
def _create_action(self):
cmd = [self.action, self.params['name']]
@@ -326,6 +335,7 @@ class PodmanVolume:
self.module = module
self.name = name
self.stdout, self.stderr = '', ''
+ self.mount_point = None
self.info = self.get_info()
self.version = self._get_podman_version()
self.diff = {}
@@ -380,7 +390,7 @@ class PodmanVolume:
"""Perform action with volume.
Arguments:
- action {str} -- action to perform - create, stop, delete
+ action {str} -- action to perform - create, delete, mount, unmout
"""
b_command = PodmanVolumeModuleParams(action,
self.module.params,
@@ -389,11 +399,14 @@ class PodmanVolume:
).construct_command_from_params()
full_cmd = " ".join([self.module.params['executable'], 'volume']
+ [to_native(i) for i in b_command])
+ # check if running not from root
+ if os.getuid() != 0 and action == 'mount':
+ full_cmd = f"{self.module.params['executable']} unshare {full_cmd}"
self.module.log("PODMAN-VOLUME-DEBUG: %s" % full_cmd)
self.actions.append(full_cmd)
if not self.module.check_mode:
rc, out, err = self.module.run_command(
- [self.module.params['executable'], b'volume'] + b_command,
+ full_cmd,
expand_user_and_vars=False)
self.stdout = out
self.stderr = err
@@ -401,6 +414,9 @@ class PodmanVolume:
self.module.fail_json(
msg="Can't %s volume %s" % (action, self.name),
stdout=out, stderr=err)
+ # in case of mount/unmount, return path to the volume from stdout
+ if action in ['mount']:
+ self.mount_point = out.strip()
def delete(self):
"""Delete the volume."""
@@ -410,6 +426,14 @@ class PodmanVolume:
"""Create the volume."""
self._perform_action('create')
+ def mount(self):
+ """Delete the volume."""
+ self._perform_action('mount')
+
+ def unmount(self):
+ """Create the volume."""
+ self._perform_action('unmount')
+
def recreate(self):
"""Recreate the volume."""
self.delete()
@@ -468,6 +492,8 @@ class PodmanVolumeManager:
states_map = {
'present': self.make_present,
'absent': self.make_absent,
+ 'mounted': self.make_mount,
+ 'unmounted': self.make_unmount,
'quadlet': self.make_quadlet,
}
process_action = states_map[self.state]
@@ -501,6 +527,26 @@ class PodmanVolumeManager:
'podman_actions': self.volume.actions})
self.module.exit_json(**self.results)
+ def make_mount(self):
+ """Run actions if desired state is 'mounted'."""
+ if not self.volume.exists:
+ self.volume.create()
+ self.results['actions'].append('created %s' % self.volume.name)
+ self.volume.mount()
+ self.results['actions'].append('mounted %s' % self.volume.name)
+ if self.volume.mount_point:
+ self.results.update({'mount_point': self.volume.mount_point})
+ self.update_volume_result()
+
+ def make_unmount(self):
+ """Run actions if desired state is 'unmounted'."""
+ if self.volume.exists:
+ self.volume.unmount()
+ self.results['actions'].append('unmounted %s' % self.volume.name)
+ self.update_volume_result()
+ else:
+ self.module.fail_json(msg="Volume %s does not exist!" % self.name)
+
def make_quadlet(self):
results_update = create_quadlet_state(self.module, "volume")
self.results.update(results_update)
@@ -511,7 +557,7 @@ def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default="present",
- choices=['present', 'absent', 'quadlet']),
+ choices=['present', 'absent', 'mounted', 'unmounted', 'quadlet']),
name=dict(type='str', required=True),
label=dict(type='dict', required=False),
driver=dict(type='str', required=False),