diff --git a/contrib/build.py b/contrib/build.py index 87a5f47..7a1aaf8 100755 --- a/contrib/build.py +++ b/contrib/build.py @@ -9,6 +9,6 @@ if len(sys.argv) < 2: version = sys.argv[1] with open("galaxy.yml.in") as f: y = yaml.safe_load(f) -y['version'] = version +y["version"] = version with open("galaxy.yml", "w") as ff: yaml.safe_dump(y, ff) diff --git a/plugins/become/podman_unshare.py b/plugins/become/podman_unshare.py index 6453f23..02d6fd9 100644 --- a/plugins/become/podman_unshare.py +++ b/plugins/become/podman_unshare.py @@ -2,7 +2,8 @@ # Copyright (c) 2022 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Written by Janos Gerzson (grzs@backendo.com) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type DOCUMENTATION = """ @@ -119,7 +120,7 @@ from ansible.plugins.become import BecomeBase class BecomeModule(BecomeBase): - name = 'containers.podman.podman_unshare' + name = "containers.podman.podman_unshare" def build_become_command(self, cmd, shell): super(BecomeModule, self).build_become_command(cmd, shell) @@ -127,18 +128,20 @@ class BecomeModule(BecomeBase): if not cmd: return cmd - becomecmd = 'podman unshare' + becomecmd = "podman unshare" - user = self.get_option('become_user') or 'root' - if user != 'root': - cmdlist = [self.get_option('become_exe') or 'sudo'] + user = self.get_option("become_user") or "root" + if user != "root": + cmdlist = [self.get_option("become_exe") or "sudo"] # -i is required, because # podman unshare should be executed in a login shell to avoid chdir permission errors - cmdlist.append('-iu %s' % user) - if self.get_option('become_pass'): - self.prompt = '[sudo podman unshare via ansible, key=%s] password:' % self._id + cmdlist.append("-iu %s" % user) + if self.get_option("become_pass"): + self.prompt = ( + "[sudo podman unshare via ansible, key=%s] password:" % self._id + ) cmdlist.append('-p "%s"' % self.prompt) - cmdlist.append('-- %s' % becomecmd) - becomecmd = ' '.join(cmdlist) + cmdlist.append("-- %s" % becomecmd) + becomecmd = " ".join(cmdlist) - return ' '.join([becomecmd, self._build_success_command(cmd, shell)]) + return " ".join([becomecmd, self._build_success_command(cmd, shell)]) diff --git a/plugins/connection/buildah.py b/plugins/connection/buildah.py index 69fc63c..ad6ab54 100644 --- a/plugins/connection/buildah.py +++ b/plugins/connection/buildah.py @@ -7,11 +7,12 @@ # # Written by: Tomas Tomecek (https://github.com/TomasTomecek) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ short_description: Interact with an existing buildah container description: - Run commands or put/fetch files to an existing container using buildah tool. @@ -39,7 +40,7 @@ DOCUMENTATION = ''' - name: ansible_user # keyword: # - name: remote_user -''' +""" import os import shlex @@ -61,7 +62,7 @@ class Connection(ConnectionBase): """ # String used to identify this Connection class from other classes - transport = 'containers.podman.buildah' + transport = "containers.podman.buildah" has_pipelining = True def __init__(self, play_context, new_stdin, *args, **kwargs): @@ -77,7 +78,9 @@ class Connection(ConnectionBase): display.vvvv("Using buildah connection from collection") def _set_user(self): - self._buildah(b"config", [b"--user=" + to_bytes(self.user, errors='surrogate_or_strict')]) + self._buildah( + b"config", [b"--user=" + to_bytes(self.user, errors="surrogate_or_strict")] + ) def _buildah(self, cmd, cmd_args=None, in_data=None, outfile_stdout=None): """ @@ -89,17 +92,17 @@ class Connection(ConnectionBase): :param outfile_stdout: file for writing STDOUT to :return: return code, stdout, stderr """ - buildah_exec = 'buildah' + buildah_exec = "buildah" local_cmd = [buildah_exec] if isinstance(cmd, str): local_cmd.append(cmd) else: local_cmd.extend(cmd) - if self.user and self.user != 'root': - if cmd == 'run': + if self.user and self.user != "root": + if cmd == "run": local_cmd.extend(("--user", self.user)) - elif cmd == 'copy': + elif cmd == "copy": local_cmd.extend(("--chown", self.user)) local_cmd.append(self._container_id) @@ -109,23 +112,27 @@ class Connection(ConnectionBase): else: local_cmd.extend(cmd_args) - local_cmd = [to_bytes(i, errors='surrogate_or_strict') - for i in local_cmd] + local_cmd = [to_bytes(i, errors="surrogate_or_strict") for i in local_cmd] display.vvv("RUN %s" % (local_cmd,), host=self._container_id) if outfile_stdout: stdout_fd = open(outfile_stdout, "wb") else: stdout_fd = subprocess.PIPE - p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE, - stdout=stdout_fd, stderr=subprocess.PIPE) + p = subprocess.Popen( + local_cmd, + shell=False, + stdin=subprocess.PIPE, + stdout=stdout_fd, + stderr=subprocess.PIPE, + ) stdout, stderr = p.communicate(input=in_data) display.vvvv("STDOUT %s" % to_text(stdout)) display.vvvv("STDERR %s" % to_text(stderr)) display.vvvv("RC CODE %s" % p.returncode) - stdout = to_bytes(stdout, errors='surrogate_or_strict') - stderr = to_bytes(stderr, errors='surrogate_or_strict') + stdout = to_bytes(stdout, errors="surrogate_or_strict") + stderr = to_bytes(stderr, errors="surrogate_or_strict") return p.returncode, stdout, stderr def _connect(self): @@ -136,19 +143,26 @@ class Connection(ConnectionBase): super(Connection, self)._connect() rc, self._mount_point, stderr = self._buildah("mount") if rc != 0: - display.v("Failed to mount container %s: %s" % (self._container_id, stderr.strip())) + display.v( + "Failed to mount container %s: %s" + % (self._container_id, stderr.strip()) + ) else: - self._mount_point = self._mount_point.strip() + to_bytes(os.path.sep, errors='surrogate_or_strict') - display.vvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) + self._mount_point = self._mount_point.strip() + to_bytes( + os.path.sep, errors="surrogate_or_strict" + ) + display.vvvv( + "MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr) + ) self._connected = True @ensure_connect def exec_command(self, cmd, in_data=None, sudoable=False): - """ run specified command in a running OCI container using buildah """ + """run specified command in a running OCI container using buildah""" super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) # shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3 - cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict')) + cmd_args_list = shlex.split(to_native(cmd, errors="surrogate_or_strict")) rc, stdout, stderr = self._buildah("run", cmd_args_list, in_data) @@ -156,47 +170,51 @@ class Connection(ConnectionBase): return rc, stdout, stderr def put_file(self, in_path, out_path): - """ Place a local file located in 'in_path' inside container at 'out_path' """ + """Place a local file located in 'in_path' inside container at 'out_path'""" super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id) if not self._mount_point or self.user: - rc, stdout, stderr = self._buildah( - "copy", [in_path, out_path]) + rc, stdout, stderr = self._buildah("copy", [in_path, out_path]) if rc != 0: raise AnsibleError( - "Failed to copy file from %s to %s in container %s\n%s" % ( - in_path, out_path, self._container_id, stderr) + "Failed to copy file from %s to %s in container %s\n%s" + % (in_path, out_path, self._container_id, stderr) ) else: - real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') + real_out_path = self._mount_point + to_bytes( + out_path, errors="surrogate_or_strict" + ) shutil.copyfile( - to_bytes(in_path, errors='surrogate_or_strict'), - to_bytes(real_out_path, errors='surrogate_or_strict') + to_bytes(in_path, errors="surrogate_or_strict"), + to_bytes(real_out_path, errors="surrogate_or_strict"), ) def fetch_file(self, in_path, out_path): - """ obtain file specified via 'in_path' from the container and place it at 'out_path' """ + """obtain file specified via 'in_path' from the container and place it at 'out_path'""" super(Connection, self).fetch_file(in_path, out_path) - display.vvv("FETCH %s TO %s" % - (in_path, out_path), host=self._container_id) + display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id) if not self._mount_point: rc, stdout, stderr = self._buildah( "run", - ["cat", to_bytes(in_path, errors='surrogate_or_strict')], - outfile_stdout=out_path) + ["cat", to_bytes(in_path, errors="surrogate_or_strict")], + outfile_stdout=out_path, + ) if rc != 0: - raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % ( - in_path, out_path, self._container_id, stderr)) + raise AnsibleError( + "Failed to fetch file from %s to %s from container %s\n%s" + % (in_path, out_path, self._container_id, stderr) + ) else: - real_in_path = self._mount_point + \ - to_bytes(in_path, errors='surrogate_or_strict') + real_in_path = self._mount_point + to_bytes( + in_path, errors="surrogate_or_strict" + ) shutil.copyfile( - to_bytes(real_in_path, errors='surrogate_or_strict'), - to_bytes(out_path, errors='surrogate_or_strict') + to_bytes(real_in_path, errors="surrogate_or_strict"), + to_bytes(out_path, errors="surrogate_or_strict"), ) def close(self): - """ unmount container's filesystem """ + """unmount container's filesystem""" super(Connection, self).close() rc, stdout, stderr = self._buildah("umount") display.vvvv("RC %s STDOUT %r STDERR %r" % (rc, stdout, stderr)) diff --git a/plugins/connection/podman.py b/plugins/connection/podman.py index 2ade918..3f351d7 100644 --- a/plugins/connection/podman.py +++ b/plugins/connection/podman.py @@ -7,10 +7,11 @@ # # Written by: Tomas Tomecek (https://github.com/TomasTomecek) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ author: Tomas Tomecek (@TomasTomecek) name: podman short_description: Interact with an existing podman container @@ -56,7 +57,7 @@ DOCUMENTATION = ''' - name: ansible_podman_executable env: - name: ANSIBLE_PODMAN_EXECUTABLE -''' +""" import os import shlex @@ -79,7 +80,7 @@ class Connection(ConnectionBase): """ # String used to identify this Connection class from other classes - transport = 'containers.podman.podman' + transport = "containers.podman.podman" # We know that pipelining does not work with podman. Do not enable it, or # users will start containers and fail to connect to them. has_pipelining = False @@ -104,7 +105,7 @@ class Connection(ConnectionBase): :param use_container_id: whether to append the container ID to the command :return: return code, stdout, stderr """ - podman_exec = self.get_option('podman_executable') + podman_exec = self.get_option("podman_executable") try: podman_cmd = get_bin_path(podman_exec) except ValueError: @@ -112,11 +113,12 @@ class Connection(ConnectionBase): if not podman_cmd: raise AnsibleError("%s command not found in PATH" % podman_exec) local_cmd = [podman_cmd] - if self.get_option('podman_extra_args'): + if self.get_option("podman_extra_args"): local_cmd += shlex.split( to_native( - self.get_option('podman_extra_args'), - errors='surrogate_or_strict')) + self.get_option("podman_extra_args"), errors="surrogate_or_strict" + ) + ) if isinstance(cmd, str): local_cmd.append(cmd) else: @@ -126,18 +128,23 @@ class Connection(ConnectionBase): local_cmd.append(self._container_id) if cmd_args: local_cmd += cmd_args - local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] + local_cmd = [to_bytes(i, errors="surrogate_or_strict") for i in local_cmd] display.vvv("RUN %s" % (local_cmd,), host=self._container_id) - p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + local_cmd, + shell=False, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = p.communicate(input=in_data) display.vvvvv("STDOUT %s" % stdout) display.vvvvv("STDERR %s" % stderr) display.vvvvv("RC CODE %s" % p.returncode) - stdout = to_bytes(stdout, errors='surrogate_or_strict') - stderr = to_bytes(stderr, errors='surrogate_or_strict') + stdout = to_bytes(stdout, errors="surrogate_or_strict") + stderr = to_bytes(stderr, errors="surrogate_or_strict") return p.returncode, stdout, stderr def _connect(self): @@ -148,22 +155,30 @@ class Connection(ConnectionBase): super(Connection, self)._connect() rc, self._mount_point, stderr = self._podman("mount") if rc != 0: - display.vvvv("Failed to mount container %s: %s" % (self._container_id, stderr.strip())) + display.vvvv( + "Failed to mount container %s: %s" + % (self._container_id, stderr.strip()) + ) elif not os.listdir(self._mount_point.strip()): - display.vvvv("Failed to mount container with CGroups2: empty dir %s" % self._mount_point.strip()) + display.vvvv( + "Failed to mount container with CGroups2: empty dir %s" + % self._mount_point.strip() + ) self._mount_point = None else: self._mount_point = self._mount_point.strip() - display.vvvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)) + display.vvvvv( + "MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr) + ) self._connected = True @ensure_connect def exec_command(self, cmd, in_data=None, sudoable=False): - """ run specified command in a running OCI container using podman """ + """run specified command in a running OCI container using podman""" super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) # shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3 - cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict')) + cmd_args_list = shlex.split(to_native(cmd, errors="surrogate_or_strict")) exec_args_list = ["exec"] if self.user: exec_args_list.extend(("--user", self.user)) @@ -174,56 +189,70 @@ class Connection(ConnectionBase): return rc, stdout, stderr def put_file(self, in_path, out_path): - """ Place a local file located in 'in_path' inside container at 'out_path' """ + """Place a local file located in 'in_path' inside container at 'out_path'""" super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id) if not self._mount_point or self.user: rc, stdout, stderr = self._podman( - "cp", [in_path, self._container_id + ":" + out_path], use_container_id=False + "cp", + [in_path, self._container_id + ":" + out_path], + use_container_id=False, ) if rc != 0: rc, stdout, stderr = self._podman( - "cp", ["--pause=false", in_path, self._container_id + ":" + out_path], use_container_id=False + "cp", + ["--pause=false", in_path, self._container_id + ":" + out_path], + use_container_id=False, ) if rc != 0: raise AnsibleError( - "Failed to copy file from %s to %s in container %s\n%s" % ( - in_path, out_path, self._container_id, stderr) + "Failed to copy file from %s to %s in container %s\n%s" + % (in_path, out_path, self._container_id, stderr) ) if self.user: rc, stdout, stderr = self._podman( - "exec", ["chown", self.user, out_path]) + "exec", ["chown", self.user, out_path] + ) if rc != 0: raise AnsibleError( - "Failed to chown file %s for user %s in container %s\n%s" % ( - out_path, self.user, self._container_id, stderr) + "Failed to chown file %s for user %s in container %s\n%s" + % (out_path, self.user, self._container_id, stderr) ) else: - real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict') + real_out_path = self._mount_point + to_bytes( + out_path, errors="surrogate_or_strict" + ) shutil.copyfile( - to_bytes(in_path, errors='surrogate_or_strict'), - to_bytes(real_out_path, errors='surrogate_or_strict') + to_bytes(in_path, errors="surrogate_or_strict"), + to_bytes(real_out_path, errors="surrogate_or_strict"), ) def fetch_file(self, in_path, out_path): - """ obtain file specified via 'in_path' from the container and place it at 'out_path' """ + """obtain file specified via 'in_path' from the container and place it at 'out_path'""" super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id) if not self._mount_point: rc, stdout, stderr = self._podman( - "cp", [self._container_id + ":" + in_path, out_path], use_container_id=False) + "cp", + [self._container_id + ":" + in_path, out_path], + use_container_id=False, + ) if rc != 0: - raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % ( - in_path, out_path, self._container_id, stderr)) + raise AnsibleError( + "Failed to fetch file from %s to %s from container %s\n%s" + % (in_path, out_path, self._container_id, stderr) + ) else: - real_in_path = self._mount_point + to_bytes(in_path, errors='surrogate_or_strict') + real_in_path = self._mount_point + to_bytes( + in_path, errors="surrogate_or_strict" + ) shutil.copyfile( - to_bytes(real_in_path, errors='surrogate_or_strict'), - to_bytes(out_path, errors='surrogate_or_strict') + to_bytes(real_in_path, errors="surrogate_or_strict"), + to_bytes(out_path, errors="surrogate_or_strict"), ) def close(self): - """ unmount container's filesystem """ + """unmount container's filesystem""" super(Connection, self).close() # we actually don't need to unmount since the container is mounted anyway # rc, stdout, stderr = self._podman("umount") diff --git a/plugins/module_utils/podman/common.py b/plugins/module_utils/podman/common.py index 06c55e3..5747a62 100644 --- a/plugins/module_utils/podman/common.py +++ b/plugins/module_utils/podman/common.py @@ -2,6 +2,7 @@ # 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 import json @@ -9,37 +10,45 @@ import os import shutil from ansible.module_utils.six import raise_from + try: from ansible.module_utils.compat.version import LooseVersion # noqa: F401 except ImportError: try: from distutils.version import LooseVersion # noqa: F401 except ImportError as exc: - raise_from(ImportError('To use this plugin or module with ansible-core' - ' < 2.11, you need to use Python < 3.12 with ' - 'distutils.version present'), exc) + raise_from( + ImportError( + "To use this plugin or module with ansible-core" + " < 2.11, you need to use Python < 3.12 with " + "distutils.version present" + ), + exc, + ) ARGUMENTS_OPTS_DICT = { - '--attach': ['--attach', '-a'], - '--cpu-shares': ['--cpu-shares', '-c'], - '--detach': ['--detach', '-d'], - '--env': ['--env', '-e'], - '--hostname': ['--hostname', '-h'], - '--interactive': ['--interactive', '-i'], - '--label': ['--label', '-l'], - '--memory': ['--memory', '-m'], - '--network': ['--network', '--net'], - '--publish': ['--publish', '-p'], - '--publish-all': ['--publish-all', '-P'], - '--quiet': ['--quiet', '-q'], - '--tty': ['--tty', '-t'], - '--user': ['--user', '-u'], - '--volume': ['--volume', '-v'], - '--workdir': ['--workdir', '-w'], + "--attach": ["--attach", "-a"], + "--cpu-shares": ["--cpu-shares", "-c"], + "--detach": ["--detach", "-d"], + "--env": ["--env", "-e"], + "--hostname": ["--hostname", "-h"], + "--interactive": ["--interactive", "-i"], + "--label": ["--label", "-l"], + "--memory": ["--memory", "-m"], + "--network": ["--network", "--net"], + "--publish": ["--publish", "-p"], + "--publish-all": ["--publish-all", "-P"], + "--quiet": ["--quiet", "-q"], + "--tty": ["--tty", "-t"], + "--user": ["--user", "-u"], + "--volume": ["--volume", "-v"], + "--workdir": ["--workdir", "-w"], } -def run_podman_command(module, executable='podman', args=None, expected_rc=0, ignore_errors=False): +def run_podman_command( + module, executable="podman", args=None, expected_rc=0, ignore_errors=False +): if not isinstance(executable, list): command = [executable] if args is not None: @@ -47,77 +56,94 @@ def run_podman_command(module, executable='podman', args=None, expected_rc=0, ig rc, out, err = module.run_command(command) if not ignore_errors and rc != expected_rc: module.fail_json( - msg='Failed to run {command} {args}: {err}'.format( - command=command, args=args, err=err)) + msg="Failed to run {command} {args}: {err}".format( + command=command, args=args, err=err + ) + ) return rc, out, err def run_generate_systemd_command(module, module_params, name, version): """Generate systemd unit file.""" - command = [module_params['executable'], 'generate', 'systemd', - name, '--format', 'json'] - sysconf = module_params['generate_systemd'] - gt4ver = LooseVersion(version) >= LooseVersion('4.0.0') - if sysconf.get('restart_policy'): - if sysconf.get('restart_policy') not in [ - "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", - "on-abort", "always"]: + command = [ + module_params["executable"], + "generate", + "systemd", + name, + "--format", + "json", + ] + sysconf = module_params["generate_systemd"] + gt4ver = LooseVersion(version) >= LooseVersion("4.0.0") + if sysconf.get("restart_policy"): + if sysconf.get("restart_policy") not in [ + "no", + "on-success", + "on-failure", + "on-abnormal", + "on-watchdog", + "on-abort", + "always", + ]: module.fail_json( 'Restart policy for systemd unit file is "%s" and must be one of: ' - '"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always"' % - sysconf.get('restart_policy')) - command.extend([ - '--restart-policy', - sysconf['restart_policy']]) - if sysconf.get('restart_sec') is not None: - command.extend(['--restart-sec=%s' % sysconf['restart_sec']]) - if (sysconf.get('stop_timeout') is not None) or (sysconf.get('time') is not None): + '"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always"' + % sysconf.get("restart_policy") + ) + command.extend(["--restart-policy", sysconf["restart_policy"]]) + if sysconf.get("restart_sec") is not None: + command.extend(["--restart-sec=%s" % sysconf["restart_sec"]]) + if (sysconf.get("stop_timeout") is not None) or (sysconf.get("time") is not None): # Select correct parameter name based on version - arg_name = 'stop-timeout' if gt4ver else 'time' - arg_value = sysconf.get('stop_timeout') if sysconf.get('stop_timeout') is not None else sysconf.get('time') - command.extend(['--%s=%s' % (arg_name, arg_value)]) - if sysconf.get('start_timeout') is not None: - command.extend(['--start-timeout=%s' % sysconf['start_timeout']]) - if sysconf.get('no_header'): - command.extend(['--no-header']) - if sysconf.get('names', True): - command.extend(['--name']) + arg_name = "stop-timeout" if gt4ver else "time" + arg_value = ( + sysconf.get("stop_timeout") + if sysconf.get("stop_timeout") is not None + else sysconf.get("time") + ) + command.extend(["--%s=%s" % (arg_name, arg_value)]) + if sysconf.get("start_timeout") is not None: + command.extend(["--start-timeout=%s" % sysconf["start_timeout"]]) + if sysconf.get("no_header"): + command.extend(["--no-header"]) + if sysconf.get("names", True): + command.extend(["--name"]) if sysconf.get("new"): command.extend(["--new"]) - if sysconf.get('container_prefix') is not None: - command.extend(['--container-prefix=%s' % sysconf['container_prefix']]) - if sysconf.get('pod_prefix') is not None: - command.extend(['--pod-prefix=%s' % sysconf['pod_prefix']]) - if sysconf.get('separator') is not None: - command.extend(['--separator=%s' % sysconf['separator']]) - if sysconf.get('after') is not None: + if sysconf.get("container_prefix") is not None: + command.extend(["--container-prefix=%s" % sysconf["container_prefix"]]) + if sysconf.get("pod_prefix") is not None: + command.extend(["--pod-prefix=%s" % sysconf["pod_prefix"]]) + if sysconf.get("separator") is not None: + command.extend(["--separator=%s" % sysconf["separator"]]) + if sysconf.get("after") is not None: - sys_after = sysconf['after'] + sys_after = sysconf["after"] if isinstance(sys_after, str): sys_after = [sys_after] for after in sys_after: - command.extend(['--after=%s' % after]) - if sysconf.get('wants') is not None: - sys_wants = sysconf['wants'] + command.extend(["--after=%s" % after]) + if sysconf.get("wants") is not None: + sys_wants = sysconf["wants"] if isinstance(sys_wants, str): sys_wants = [sys_wants] for want in sys_wants: - command.extend(['--wants=%s' % want]) - if sysconf.get('requires') is not None: - sys_req = sysconf['requires'] + command.extend(["--wants=%s" % want]) + if sysconf.get("requires") is not None: + sys_req = sysconf["requires"] if isinstance(sys_req, str): sys_req = [sys_req] for require in sys_req: - command.extend(['--requires=%s' % require]) - for param in ['after', 'wants', 'requires']: + command.extend(["--requires=%s" % require]) + for param in ["after", "wants", "requires"]: if sysconf.get(param) is not None and not gt4ver: - module.fail_json(msg="Systemd parameter '%s' is supported from " - "podman version 4 only! Current version is %s" % ( - param, version)) + module.fail_json( + msg="Systemd parameter '%s' is supported from " + "podman version 4 only! Current version is %s" % (param, version) + ) - if module.params['debug'] or module_params['debug']: - module.log("PODMAN-CONTAINER-DEBUG: systemd command: %s" % - " ".join(command)) + if module.params["debug"] or module_params["debug"]: + module.log("PODMAN-CONTAINER-DEBUG: systemd command: %s" % " ".join(command)) rc, systemd, err = module.run_command(command) return rc, systemd, err @@ -125,14 +151,16 @@ def run_generate_systemd_command(module, module_params, name, version): def compare_systemd_file_content(file_path, file_content): if not os.path.exists(file_path): # File does not exist, so all lines in file_content are different - return '', file_content + return "", file_content # Read the file - with open(file_path, 'r') as unit_file: + with open(file_path, "r") as unit_file: current_unit_file_content = unit_file.read() # Function to remove comments from file content def remove_comments(content): - return "\n".join([line for line in content.splitlines() if not line.startswith('#')]) + return "\n".join( + [line for line in content.splitlines() if not line.startswith("#")] + ) # Remove comments from both file contents before comparison current_unit_file_content_nocmnt = remove_comments(current_unit_file_content) @@ -141,111 +169,145 @@ def compare_systemd_file_content(file_path, file_content): return None # Get the different lines between the two contents - diff_in_file = [line - for line in unit_content_nocmnt.splitlines() - if line not in current_unit_file_content_nocmnt.splitlines()] - diff_in_string = [line - for line in current_unit_file_content_nocmnt.splitlines() - if line not in unit_content_nocmnt.splitlines()] + diff_in_file = [ + line + for line in unit_content_nocmnt.splitlines() + if line not in current_unit_file_content_nocmnt.splitlines() + ] + diff_in_string = [ + line + for line in current_unit_file_content_nocmnt.splitlines() + if line not in unit_content_nocmnt.splitlines() + ] return diff_in_string, diff_in_file def generate_systemd(module, module_params, name, version): result = { - 'changed': False, - 'systemd': {}, - 'diff': {}, + "changed": False, + "systemd": {}, + "diff": {}, } - sysconf = module_params['generate_systemd'] - rc, systemd, err = run_generate_systemd_command(module, module_params, name, version) + sysconf = module_params["generate_systemd"] + rc, systemd, err = run_generate_systemd_command( + module, module_params, name, version + ) if rc != 0: - module.log( - "PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err) + module.log("PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err) if sysconf: module.fail_json(msg="Error generating systemd: %s" % err) return result else: try: data = json.loads(systemd) - result['systemd'] = data - if sysconf.get('path'): - full_path = os.path.expanduser(sysconf['path']) + result["systemd"] = data + if sysconf.get("path"): + full_path = os.path.expanduser(sysconf["path"]) if not os.path.exists(full_path): os.makedirs(full_path) - result['changed'] = True + result["changed"] = True if not os.path.isdir(full_path): - module.fail_json("Path %s is not a directory! " - "Can not save systemd unit files there!" - % full_path) + module.fail_json( + "Path %s is not a directory! " + "Can not save systemd unit files there!" % full_path + ) for file_name, file_content in data.items(): file_name += ".service" if not os.path.exists(os.path.join(full_path, file_name)): - result['changed'] = True - if result['diff'].get('before') is None: - result['diff'] = {'before': {}, 'after': {}} - result['diff']['before'].update( - {'systemd_{file_name}.service'.format(file_name=file_name): ''}) - result['diff']['after'].update( - {'systemd_{file_name}.service'.format(file_name=file_name): file_content}) + result["changed"] = True + if result["diff"].get("before") is None: + result["diff"] = {"before": {}, "after": {}} + result["diff"]["before"].update( + { + "systemd_{file_name}.service".format( + file_name=file_name + ): "" + } + ) + result["diff"]["after"].update( + { + "systemd_{file_name}.service".format( + file_name=file_name + ): file_content + } + ) else: - diff_ = compare_systemd_file_content(os.path.join(full_path, file_name), file_content) + diff_ = compare_systemd_file_content( + os.path.join(full_path, file_name), file_content + ) if diff_: - result['changed'] = True - if result['diff'].get('before') is None: - result['diff'] = {'before': {}, 'after': {}} - result['diff']['before'].update( - {'systemd_{file_name}.service'.format(file_name=file_name): "\n".join(diff_[0])}) - result['diff']['after'].update( - {'systemd_{file_name}.service'.format(file_name=file_name): "\n".join(diff_[1])}) - with open(os.path.join(full_path, file_name), 'w') as f: + result["changed"] = True + if result["diff"].get("before") is None: + result["diff"] = {"before": {}, "after": {}} + result["diff"]["before"].update( + { + "systemd_{file_name}.service".format( + file_name=file_name + ): "\n".join(diff_[0]) + } + ) + result["diff"]["after"].update( + { + "systemd_{file_name}.service".format( + file_name=file_name + ): "\n".join(diff_[1]) + } + ) + with open(os.path.join(full_path, file_name), "w") as f: f.write(file_content) diff_before = "\n".join( - ["{j} - {k}".format(j=j, k=k) - for j, k in result['diff'].get('before', {}).items() if 'PIDFile' not in k]).strip() + [ + "{j} - {k}".format(j=j, k=k) + for j, k in result["diff"].get("before", {}).items() + if "PIDFile" not in k + ] + ).strip() diff_after = "\n".join( - ["{j} - {k}".format(j=j, k=k) - for j, k in result['diff'].get('after', {}).items() if 'PIDFile' not in k]).strip() + [ + "{j} - {k}".format(j=j, k=k) + for j, k in result["diff"].get("after", {}).items() + if "PIDFile" not in k + ] + ).strip() if diff_before or diff_after: - result['diff']['before'] = diff_before + "\n" - result['diff']['after'] = diff_after + "\n" + result["diff"]["before"] = diff_before + "\n" + result["diff"]["after"] = diff_after + "\n" else: - result['diff'] = {} + result["diff"] = {} return result except Exception as e: - module.log( - "PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e) + module.log("PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e) if sysconf: module.fail_json(msg="Error writing systemd: %s" % e) return result def delete_systemd(module, module_params, name, version): - sysconf = module_params['generate_systemd'] - if not sysconf.get('path'): + sysconf = module_params["generate_systemd"] + if not sysconf.get("path"): # We don't know where systemd files are located, nothing to delete - module.log( - "PODMAN-CONTAINER-DEBUG: Not deleting systemd file - no path!") + module.log("PODMAN-CONTAINER-DEBUG: Not deleting systemd file - no path!") return - rc, systemd, err = run_generate_systemd_command(module, module_params, name, version) + rc, systemd, err = run_generate_systemd_command( + module, module_params, name, version + ) if rc != 0: - module.log( - "PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err) + module.log("PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err) return else: try: data = json.loads(systemd) for file_name in data.keys(): file_name += ".service" - full_dir_path = os.path.expanduser(sysconf['path']) + full_dir_path = os.path.expanduser(sysconf["path"]) file_path = os.path.join(full_dir_path, file_name) if os.path.exists(file_path): os.unlink(file_path) return except Exception as e: - module.log( - "PODMAN-CONTAINER-DEBUG: Error deleting systemd: %s" % e) + module.log("PODMAN-CONTAINER-DEBUG: Error deleting systemd: %s" % e) return @@ -306,12 +368,12 @@ _signal_map = { "VTALRM": 26, "WINCH": 28, "XCPU": 24, - "XFSZ": 25 + "XFSZ": 25, } -for i in range(1, _signal_map['RTMAX'] - _signal_map['RTMIN'] + 1): - _signal_map['RTMIN+{0}'.format(i)] = _signal_map['RTMIN'] + i - _signal_map['RTMAX-{0}'.format(i)] = _signal_map['RTMAX'] - i +for i in range(1, _signal_map["RTMAX"] - _signal_map["RTMIN"] + 1): + _signal_map["RTMIN+{0}".format(i)] = _signal_map["RTMIN"] + i + _signal_map["RTMAX-{0}".format(i)] = _signal_map["RTMAX"] - i def normalize_signal(signal_name_or_number): @@ -320,7 +382,7 @@ def normalize_signal(signal_name_or_number): return signal_name_or_number else: signal_name = signal_name_or_number.upper() - if signal_name.startswith('SIG'): + if signal_name.startswith("SIG"): signal_name = signal_name[3:] if signal_name not in _signal_map: raise RuntimeError("Unknown signal '{0}'".format(signal_name_or_number)) @@ -328,13 +390,15 @@ def normalize_signal(signal_name_or_number): def get_podman_version(module, fail=True): - executable = module.params['executable'] if module.params['executable'] else 'podman' - rc, out, err = module.run_command( - [executable, b'--version']) + executable = ( + module.params["executable"] if module.params["executable"] else "podman" + ) + rc, out, err = module.run_command([executable, b"--version"]) if rc != 0 or not out or "version" not in out: if fail: - module.fail_json(msg="'%s --version' run failed! Error: %s" % - (executable, err)) + module.fail_json( + msg="'%s --version' run failed! Error: %s" % (executable, err) + ) return None return out.split("version")[1].strip() @@ -409,16 +473,23 @@ def diff_generic(params, info_config, module_arg, cmd_arg, boolean_type=False): if before: before = ",".join(sorted([str(i).lower() for i in before])) else: - before = '' + before = "" elif isinstance(after, dict): if module_arg == "log_opt" and "max_size" in after: after["max-size"] = after.pop("max_size") - after = ",".join(sorted( - [str(k).lower() + "=" + str(v).lower() for k, v in after.items() if v is not None])) + 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])) else: - before = '' + before = "" elif isinstance(after, bool): after = str(after).capitalize() if before is not None: diff --git a/plugins/module_utils/podman/podman_container_lib.py b/plugins/module_utils/podman/podman_container_lib.py index 841edf8..2ecbea6 100644 --- a/plugins/module_utils/podman/podman_container_lib.py +++ b/plugins/module_utils/podman/podman_container_lib.py @@ -1,198 +1,220 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import json # noqa: F402 import os # noqa: F402 import shlex # noqa: F402 from ansible.module_utils._text import to_bytes, to_native # noqa: F402 -from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion -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 -from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ContainerQuadlet +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + LooseVersion, +) +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, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ( + ContainerQuadlet, +) __metaclass__ = type ARGUMENTS_SPEC_CONTAINER = dict( - name=dict(required=True, type='str'), - executable=dict(default='podman', type='str'), - state=dict(type='str', default='started', choices=[ - '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'), - command=dict(type='raw'), - cpu_period=dict(type='int'), - cpu_quota=dict(type='int'), - cpu_rt_period=dict(type='int'), - cpu_rt_runtime=dict(type='int'), - cpu_shares=dict(type='int'), - 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'), - detach=dict(type='bool', default=True), - 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'), - device_write_iops=dict(type='list', elements='str'), - dns=dict(type='list', elements='str', aliases=['dns_servers']), - dns_option=dict(type='str', aliases=['dns_opts']), - dns_search=dict(type='list', elements='str', aliases=['dns_search_domains']), - entrypoint=dict(type='str'), - 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']), - force_restart=dict(type='bool', default=False, - aliases=['restart']), - 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']), - 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'], 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']), - label_file=dict(type='str'), - log_driver=dict(type='str', choices=[ - 'k8s-file', 'journald', 'json-file']), + name=dict(required=True, type="str"), + executable=dict(default="podman", type="str"), + state=dict( + type="str", + default="started", + choices=["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"), + command=dict(type="raw"), + cpu_period=dict(type="int"), + cpu_quota=dict(type="int"), + cpu_rt_period=dict(type="int"), + cpu_rt_runtime=dict(type="int"), + cpu_shares=dict(type="int"), + 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"), + detach=dict(type="bool", default=True), + 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"), + device_write_iops=dict(type="list", elements="str"), + dns=dict(type="list", elements="str", aliases=["dns_servers"]), + dns_option=dict(type="str", aliases=["dns_opts"]), + dns_search=dict(type="list", elements="str", aliases=["dns_search_domains"]), + entrypoint=dict(type="str"), + 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"]), + force_restart=dict(type="bool", default=False, aliases=["restart"]), + 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"]), + 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"], + 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"]), + label_file=dict(type="str"), + log_driver=dict(type="str", choices=["k8s-file", "journald", "json-file"]), log_level=dict( - type='str', - choices=["debug", "info", "warn", "error", "fatal", "panic"]), - log_opt=dict(type='dict', aliases=['log_options'], - options=dict( - max_size=dict(type='str'), - path=dict(type='str'), - tag=dict(type='str'))), - mac_address=dict(type='str'), - memory=dict(type='str'), - memory_reservation=dict(type='str'), - memory_swap=dict(type='str'), - 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', 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_file_mode=dict(type='raw'), - 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'), - stop_time=dict(type='str'), - subgidname=dict(type='str'), - 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']) + type="str", choices=["debug", "info", "warn", "error", "fatal", "panic"] + ), + log_opt=dict( + type="dict", + aliases=["log_options"], + options=dict( + max_size=dict(type="str"), path=dict(type="str"), tag=dict(type="str") + ), + ), + mac_address=dict(type="str"), + memory=dict(type="str"), + memory_reservation=dict(type="str"), + memory_swap=dict(type="str"), + 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", 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_file_mode=dict(type="raw"), + 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"), + stop_time=dict(type="str"), + subgidname=dict(type="str"), + 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"]), ) @@ -200,8 +222,8 @@ def init_options(): default = {} opts = ARGUMENTS_SPEC_CONTAINER for k, v in opts.items(): - if 'default' in v: - default[k] = v['default'] + if "default" in v: + default[k] = v["default"] else: default[k] = None return default @@ -209,12 +231,12 @@ def init_options(): def update_options(opts_dict, container): def to_bool(x): - return str(x).lower() not in ['no', 'false'] + return str(x).lower() not in ["no", "false"] aliases = {} for k, v in ARGUMENTS_SPEC_CONTAINER.items(): - if 'aliases' in v: - for alias in v['aliases']: + if "aliases" in v: + for alias in v["aliases"]: aliases[alias] = k for k in list(container): if k in aliases: @@ -222,11 +244,17 @@ def update_options(opts_dict, container): container[key] = container.pop(k) else: key = k - if ARGUMENTS_SPEC_CONTAINER[key]['type'] == 'list' and not isinstance(container[key], list): + if ARGUMENTS_SPEC_CONTAINER[key]["type"] == "list" and not isinstance( + container[key], list + ): opts_dict[key] = [container[key]] - elif ARGUMENTS_SPEC_CONTAINER[key]['type'] == 'bool' and not isinstance(container[key], bool): + elif ARGUMENTS_SPEC_CONTAINER[key]["type"] == "bool" and not isinstance( + container[key], bool + ): opts_dict[key] = to_bool(container[key]) - elif ARGUMENTS_SPEC_CONTAINER[key]['type'] == 'int' and not isinstance(container[key], int): + elif ARGUMENTS_SPEC_CONTAINER[key]["type"] == "int" and not isinstance( + container[key], int + ): opts_dict[key] = int(container[key]) else: opts_dict[key] = container[key] @@ -243,12 +271,12 @@ def set_container_opts(input_vars): class PodmanModuleParams: """Creates list of arguments for podman CLI command. - Arguments: - action {str} -- action type from 'run', 'stop', 'create', 'delete', - 'start', 'restart' - params {dict} -- dictionary of module parameters + Arguments: + action {str} -- action type from 'run', 'stop', 'create', 'delete', + 'start', 'restart' + params {dict} -- dictionary of module parameters - """ + """ def __init__(self, action, params, podman_version, module): self.params = params @@ -262,620 +290,642 @@ class PodmanModuleParams: Returns: list -- list of byte strings for Popen command """ - if self.action in ['start', 'stop', 'delete', 'restart']: + if self.action in ["start", "stop", "delete", "restart"]: return self.start_stop_delete() - if self.action in ['create', 'run']: - cmd = [self.action, '--name', self.params['name']] - all_param_methods = [func for func in dir(self) - if callable(getattr(self, func)) - and func.startswith("addparam")] + if self.action in ["create", "run"]: + cmd = [self.action, "--name", self.params["name"]] + all_param_methods = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("addparam") + ] params_set = (i for i in self.params if self.params[i] is not None) for param in params_set: func_name = "_".join(["addparam", param]) if func_name in all_param_methods: cmd = getattr(self, func_name)(cmd) - cmd.append(self.params['image']) - if self.params['command']: - if isinstance(self.params['command'], list): - cmd += self.params['command'] + cmd.append(self.params["image"]) + if self.params["command"]: + if isinstance(self.params["command"], list): + cmd += self.params["command"] else: - cmd += self.params['command'].split() - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + cmd += self.params["command"].split() + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def start_stop_delete(self): def complete_params(cmd): - if self.params['attach'] and self.action == 'start': - cmd.append('--attach') - if self.params['detach'] is False and self.action == 'start' and '--attach' not in cmd: - cmd.append('--attach') - if self.params['detach_keys'] and self.action == 'start': - cmd += ['--detach-keys', self.params['detach_keys']] - if self.params['sig_proxy'] and self.action == 'start': - cmd.append('--sig-proxy') - if self.params['stop_time'] and self.action == 'stop': - cmd += ['--time', self.params['stop_time']] - if self.params['restart_time'] and self.action == 'restart': - cmd += ['--time', self.params['restart_time']] - if self.params['delete_depend'] and self.action == 'delete': - cmd.append('--depend') - if self.params['delete_time'] and self.action == 'delete': - cmd += ['--time', self.params['delete_time']] - if self.params['delete_volumes'] and self.action == 'delete': - cmd.append('--volumes') - if self.params['force_delete'] and self.action == 'delete': - cmd.append('--force') + if self.params["attach"] and self.action == "start": + cmd.append("--attach") + if ( + self.params["detach"] is False + and self.action == "start" + and "--attach" not in cmd + ): + cmd.append("--attach") + if self.params["detach_keys"] and self.action == "start": + cmd += ["--detach-keys", self.params["detach_keys"]] + if self.params["sig_proxy"] and self.action == "start": + cmd.append("--sig-proxy") + if self.params["stop_time"] and self.action == "stop": + cmd += ["--time", self.params["stop_time"]] + if self.params["restart_time"] and self.action == "restart": + cmd += ["--time", self.params["restart_time"]] + if self.params["delete_depend"] and self.action == "delete": + cmd.append("--depend") + if self.params["delete_time"] and self.action == "delete": + cmd += ["--time", self.params["delete_time"]] + if self.params["delete_volumes"] and self.action == "delete": + cmd.append("--volumes") + if self.params["force_delete"] and self.action == "delete": + cmd.append("--force") return cmd - if self.action in ['stop', 'start', 'restart']: - cmd = complete_params([self.action]) + [self.params['name']] - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + if self.action in ["stop", "start", "restart"]: + cmd = complete_params([self.action]) + [self.params["name"]] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] - if self.action == 'delete': - cmd = complete_params(['rm']) + [self.params['name']] - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + if self.action == "delete": + cmd = complete_params(["rm"]) + [self.params["name"]] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def check_version(self, param, minv=None, maxv=None): - if minv and LooseVersion(minv) > LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported from podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) - if maxv and LooseVersion(maxv) < LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported till podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) + if minv and LooseVersion(minv) > LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) + if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) def addparam_annotation(self, c): - for annotate in self.params['annotation'].items(): - c += ['--annotation', '='.join(annotate)] + for annotate in self.params["annotation"].items(): + c += ["--annotation", "=".join(annotate)] return c def addparam_arch(self, c): - return c + ['--arch=%s' % self.params['arch']] + return c + ["--arch=%s" % self.params["arch"]] def addparam_attach(self, c): - for attach in self.params['attach']: - c += ['--attach=%s' % attach] + for attach in self.params["attach"]: + c += ["--attach=%s" % attach] return c def addparam_authfile(self, c): - return c + ['--authfile', self.params['authfile']] + return c + ["--authfile", self.params["authfile"]] def addparam_blkio_weight(self, c): - return c + ['--blkio-weight', self.params['blkio_weight']] + return c + ["--blkio-weight", self.params["blkio_weight"]] def addparam_blkio_weight_device(self, c): - for blkio in self.params['blkio_weight_device'].items(): - c += ['--blkio-weight-device', ':'.join(blkio)] + for blkio in self.params["blkio_weight_device"].items(): + c += ["--blkio-weight-device", ":".join(blkio)] return c def addparam_cap_add(self, c): - for cap_add in self.params['cap_add']: - c += ['--cap-add', cap_add] + for cap_add in self.params["cap_add"]: + c += ["--cap-add", cap_add] return c def addparam_cap_drop(self, c): - for cap_drop in self.params['cap_drop']: - c += ['--cap-drop', cap_drop] + for cap_drop in self.params["cap_drop"]: + c += ["--cap-drop", cap_drop] return c def addparam_cgroups(self, c): - self.check_version('--cgroups', minv='1.6.0') - return c + ['--cgroups=%s' % self.params['cgroups']] + self.check_version("--cgroups", minv="1.6.0") + return c + ["--cgroups=%s" % self.params["cgroups"]] def addparam_cgroupns(self, c): - self.check_version('--cgroupns', minv='1.6.2') - return c + ['--cgroupns=%s' % self.params['cgroupns']] + self.check_version("--cgroupns", minv="1.6.2") + return c + ["--cgroupns=%s" % self.params["cgroupns"]] def addparam_cgroup_parent(self, c): - return c + ['--cgroup-parent', self.params['cgroup_parent']] + 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])] + 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']] + return c + ["--chrootdirs", self.params["chrootdirs"]] def addparam_cidfile(self, c): - return c + ['--cidfile', self.params['cidfile']] + return c + ["--cidfile", self.params["cidfile"]] def addparam_conmon_pidfile(self, c): - return c + ['--conmon-pidfile', self.params['conmon_pidfile']] + return c + ["--conmon-pidfile", self.params["conmon_pidfile"]] def addparam_cpu_period(self, c): - return c + ['--cpu-period', self.params['cpu_period']] + return c + ["--cpu-period", self.params["cpu_period"]] def addparam_cpu_quota(self, c): - return c + ['--cpu-quota', self.params['cpu_quota']] + return c + ["--cpu-quota", self.params["cpu_quota"]] def addparam_cpu_rt_period(self, c): - return c + ['--cpu-rt-period', self.params['cpu_rt_period']] + return c + ["--cpu-rt-period", self.params["cpu_rt_period"]] def addparam_cpu_rt_runtime(self, c): - return c + ['--cpu-rt-runtime', self.params['cpu_rt_runtime']] + return c + ["--cpu-rt-runtime", self.params["cpu_rt_runtime"]] def addparam_cpu_shares(self, c): - return c + ['--cpu-shares', self.params['cpu_shares']] + return c + ["--cpu-shares", self.params["cpu_shares"]] def addparam_cpus(self, c): - return c + ['--cpus', self.params['cpus']] + return c + ["--cpus", self.params["cpus"]] def addparam_cpuset_cpus(self, c): - return c + ['--cpuset-cpus', self.params['cpuset_cpus']] + return c + ["--cpuset-cpus", self.params["cpuset_cpus"]] def addparam_cpuset_mems(self, c): - return c + ['--cpuset-mems', self.params['cpuset_mems']] + return c + ["--cpuset-mems", self.params["cpuset_mems"]] def addparam_decryption_key(self, c): - return c + ['--decryption-key=%s' % self.params['decryption_key']] + 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']: + if self.action == "create" or self.params["attach"]: return c - return c + ['--detach=%s' % self.params['detach']] + return c + ["--detach=%s" % self.params["detach"]] def addparam_detach_keys(self, c): - return c + ['--detach-keys', self.params['detach_keys']] + return c + ["--detach-keys", self.params["detach_keys"]] def addparam_device(self, c): - for dev in self.params['device']: - c += ['--device', dev] + for dev in self.params["device"]: + c += ["--device", dev] return c def addparam_device_cgroup_rule(self, c): - return c + ['--device-cgroup-rule=%s' % self.params['device_cgroup_rule']] + 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] + for dev in self.params["device_read_bps"]: + c += ["--device-read-bps", dev] return c def addparam_device_read_iops(self, c): - for dev in self.params['device_read_iops']: - c += ['--device-read-iops', dev] + for dev in self.params["device_read_iops"]: + c += ["--device-read-iops", dev] return c def addparam_device_write_bps(self, c): - for dev in self.params['device_write_bps']: - c += ['--device-write-bps', dev] + for dev in self.params["device_write_bps"]: + c += ["--device-write-bps", dev] return c def addparam_device_write_iops(self, c): - for dev in self.params['device_write_iops']: - c += ['--device-write-iops', dev] + for dev in self.params["device_write_iops"]: + c += ["--device-write-iops", dev] return c def addparam_dns(self, c): - return c + ['--dns', ','.join(self.params['dns'])] + return c + ["--dns", ",".join(self.params["dns"])] def addparam_dns_option(self, c): - return c + ['--dns-option', self.params['dns_option']] + return c + ["--dns-option", self.params["dns_option"]] def addparam_dns_search(self, c): - for search in self.params['dns_search']: - c += ['--dns-search', search] + for search in self.params["dns_search"]: + c += ["--dns-search", search] return c def addparam_entrypoint(self, c): - return c + ['--entrypoint=%s' % self.params['entrypoint']] + return c + ["--entrypoint=%s" % self.params["entrypoint"]] def addparam_env(self, c): - for env_value in self.params['env'].items(): - c += ['--env', - b"=".join([to_bytes(k, errors='surrogate_or_strict') - for k in env_value])] + for env_value in self.params["env"].items(): + c += [ + "--env", + b"=".join( + [to_bytes(k, errors="surrogate_or_strict") for k in env_value] + ), + ] return c def addparam_env_file(self, c): - for env_file in self.params['env_file']: - c += ['--env-file', env_file] + for env_file in self.params["env_file"]: + c += ["--env-file", env_file] return c def addparam_env_host(self, c): - self.check_version('--env-host', minv='1.5.0') - return c + ['--env-host=%s' % self.params['env_host']] + self.check_version("--env-host", minv="1.5.0") + return c + ["--env-host=%s" % self.params["env_host"]] # Exception for etc_hosts and add-host def addparam_etc_hosts(self, c): - for host_ip in self.params['etc_hosts'].items(): - c += ['--add-host', ':'.join(host_ip)] + for host_ip in self.params["etc_hosts"].items(): + 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])] + 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] + for exp in self.params["expose"]: + c += ["--expose", exp] return c def addparam_gidmap(self, c): - for gidmap in self.params['gidmap']: - c += ['--gidmap', gidmap] + for gidmap in self.params["gidmap"]: + c += ["--gidmap", gidmap] return c def addparam_gpus(self, c): - return c + ['--gpus', self.params['gpus']] + return c + ["--gpus", self.params["gpus"]] def addparam_group_add(self, c): - for g in self.params['group_add']: - c += ['--group-add', g] + 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']] + return c + ["--group-entry", self.params["group_entry"]] # Exception for healthcheck and healthcheck-command def addparam_healthcheck(self, c): - return c + ['--healthcheck-command', self.params['healthcheck']] + return c + ["--healthcheck-command", self.params["healthcheck"]] def addparam_healthcheck_interval(self, c): - return c + ['--healthcheck-interval', - self.params['healthcheck_interval']] + return c + ["--healthcheck-interval", self.params["healthcheck_interval"]] def addparam_healthcheck_retries(self, c): - return c + ['--healthcheck-retries', - self.params['healthcheck_retries']] + return c + ["--healthcheck-retries", self.params["healthcheck_retries"]] def addparam_healthcheck_start_period(self, c): - return c + ['--healthcheck-start-period', - self.params['healthcheck_start_period']] + return c + [ + "--healthcheck-start-period", + self.params["healthcheck_start_period"], + ] def addparam_health_startup_cmd(self, c): - return c + ['--health-startup-cmd', self.params['health_startup_cmd']] + return c + ["--health-startup-cmd", self.params["health_startup_cmd"]] def addparam_health_startup_interval(self, c): - return c + ['--health-startup-interval', self.params['health_startup_interval']] + return c + ["--health-startup-interval", self.params["health_startup_interval"]] def addparam_healthcheck_timeout(self, c): - return c + ['--healthcheck-timeout', - self.params['healthcheck_timeout']] + 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']] + 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']] + 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']] + 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']] + return c + ["--health-on-failure", self.params["healthcheck_failure_action"]] def addparam_hooks_dir(self, c): - for hook_dir in self.params['hooks_dir']: - c += ['--hooks-dir=%s' % hook_dir] + for hook_dir in self.params["hooks_dir"]: + c += ["--hooks-dir=%s" % hook_dir] return c def addparam_hostname(self, c): - return c + ['--hostname', self.params['hostname']] + return c + ["--hostname", self.params["hostname"]] def addparam_hostuser(self, c): - return c + ['--hostuser', self.params['hostuser']] + return c + ["--hostuser", self.params["hostuser"]] def addparam_http_proxy(self, c): - return c + ['--http-proxy=%s' % self.params['http_proxy']] + return c + ["--http-proxy=%s" % self.params["http_proxy"]] def addparam_image_volume(self, c): - return c + ['--image-volume', self.params['image_volume']] + return c + ["--image-volume", self.params["image_volume"]] def addparam_init(self, c): - if self.params['init']: - c += ['--init'] + if self.params["init"]: + c += ["--init"] return c def addparam_init_path(self, c): - return c + ['--init-path', self.params['init_path']] + return c + ["--init-path", self.params["init_path"]] def addparam_init_ctr(self, c): - return c + ['--init-ctr', self.params['init_ctr']] + return c + ["--init-ctr", self.params["init_ctr"]] def addparam_interactive(self, c): - return c + ['--interactive=%s' % self.params['interactive']] + return c + ["--interactive=%s" % self.params["interactive"]] def addparam_ip(self, c): - return c + ['--ip', self.params['ip']] + return c + ["--ip", self.params["ip"]] def addparam_ip6(self, c): - return c + ['--ip6', self.params['ip6']] + return c + ["--ip6", self.params["ip6"]] def addparam_ipc(self, c): - return c + ['--ipc', self.params['ipc']] + return c + ["--ipc", self.params["ipc"]] def addparam_kernel_memory(self, c): - return c + ['--kernel-memory', self.params['kernel_memory']] + return c + ["--kernel-memory", self.params["kernel_memory"]] def addparam_label(self, c): - for label in self.params['label'].items(): - c += ['--label', b'='.join([to_bytes(la, errors='surrogate_or_strict') - for la in label])] + for label in self.params["label"].items(): + c += [ + "--label", + b"=".join([to_bytes(la, errors="surrogate_or_strict") for la in label]), + ] return c def addparam_label_file(self, c): - return c + ['--label-file', self.params['label_file']] + return c + ["--label-file", self.params["label_file"]] def addparam_log_driver(self, c): - return c + ['--log-driver', self.params['log_driver']] + return c + ["--log-driver", self.params["log_driver"]] def addparam_log_opt(self, c): - for k, v in self.params['log_opt'].items(): + for k, v in self.params["log_opt"].items(): if v is not None: - c += ['--log-opt', - b"=".join([to_bytes(k.replace('max_size', 'max-size'), - errors='surrogate_or_strict'), - to_bytes(v, - errors='surrogate_or_strict')])] + c += [ + "--log-opt", + b"=".join( + [ + to_bytes( + k.replace("max_size", "max-size"), + errors="surrogate_or_strict", + ), + to_bytes(v, errors="surrogate_or_strict"), + ] + ), + ] return c def addparam_log_level(self, c): - return c + ['--log-level', self.params['log_level']] + return c + ["--log-level", self.params["log_level"]] def addparam_mac_address(self, c): - return c + ['--mac-address', self.params['mac_address']] + return c + ["--mac-address", self.params["mac_address"]] def addparam_memory(self, c): - return c + ['--memory', self.params['memory']] + return c + ["--memory", self.params["memory"]] def addparam_memory_reservation(self, c): - return c + ['--memory-reservation', self.params['memory_reservation']] + return c + ["--memory-reservation", self.params["memory_reservation"]] def addparam_memory_swap(self, c): - return c + ['--memory-swap', self.params['memory_swap']] + return c + ["--memory-swap", self.params["memory_swap"]] def addparam_memory_swappiness(self, c): - return c + ['--memory-swappiness', self.params['memory_swappiness']] + return c + ["--memory-swappiness", self.params["memory_swappiness"]] def addparam_mount(self, c): - for mnt in self.params['mount']: + for mnt in self.params["mount"]: if mnt: - c += ['--mount', mnt] + c += ["--mount", mnt] return c def addparam_network(self, c): - if LooseVersion(self.podman_version) >= LooseVersion('4.0.0'): - for net in self.params['network']: - c += ['--network', net] + if LooseVersion(self.podman_version) >= LooseVersion("4.0.0"): + for net in self.params["network"]: + c += ["--network", net] return c - return c + ['--network', ",".join(self.params['network'])] + return c + ["--network", ",".join(self.params["network"])] # Exception for network_aliases and network-alias def addparam_network_aliases(self, c): - for alias in self.params['network_aliases']: - c += ['--network-alias', alias] + for alias in self.params["network_aliases"]: + c += ["--network-alias", alias] return c def addparam_no_hosts(self, c): - return c + ['--no-hosts=%s' % self.params['no_hosts']] + return c + ["--no-hosts=%s" % self.params["no_hosts"]] def addparam_no_healthcheck(self, c): - if self.params['no_healthcheck']: - c += ['--no-healthcheck'] + 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']] + 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']] + return c + ["--oom-score-adj", self.params["oom_score_adj"]] def addparam_os(self, c): - return c + ['--os', self.params['os']] + return c + ["--os", self.params["os"]] def addparam_passwd(self, c): - if self.params['passwd']: - c += ['--passwd'] + if self.params["passwd"]: + c += ["--passwd"] return c def addparam_passwd_entry(self, c): - return c + ['--passwd-entry', self.params['passwd_entry']] + return c + ["--passwd-entry", self.params["passwd_entry"]] def addparam_personality(self, c): - return c + ['--personality', self.params['personality']] + return c + ["--personality", self.params["personality"]] def addparam_pid(self, c): - return c + ['--pid', self.params['pid']] + return c + ["--pid", self.params["pid"]] def addparam_pid_file(self, c): - return c + ['--pid-file', self.params['pid_file']] + return c + ["--pid-file", self.params["pid_file"]] def addparam_pids_limit(self, c): - return c + ['--pids-limit', self.params['pids_limit']] + return c + ["--pids-limit", self.params["pids_limit"]] def addparam_platform(self, c): - return c + ['--platform', self.params['platform']] + return c + ["--platform", self.params["platform"]] def addparam_pod(self, c): - return c + ['--pod', self.params['pod']] + return c + ["--pod", self.params["pod"]] def addparam_pod_id_file(self, c): - return c + ['--pod-id-file', self.params['pod_id_file']] + 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] + 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']] + return c + ["--preserve-fds", self.params["preserve_fds"]] def addparam_privileged(self, c): - return c + ['--privileged=%s' % self.params['privileged']] + return c + ["--privileged=%s" % self.params["privileged"]] def addparam_publish(self, c): - for pub in self.params['publish']: - c += ['--publish', pub] + for pub in self.params["publish"]: + c += ["--publish", pub] return c def addparam_publish_all(self, c): - return c + ['--publish-all=%s' % self.params['publish_all']] + return c + ["--publish-all=%s" % self.params["publish_all"]] def addparam_pull(self, c): - return c + ['--pull=%s' % self.params['pull']] + return c + ["--pull=%s" % self.params["pull"]] def addparam_rdt_class(self, c): - return c + ['--rdt-class', self.params['rdt_class']] + return c + ["--rdt-class", self.params["rdt_class"]] def addparam_read_only(self, c): - return c + ['--read-only=%s' % self.params['read_only']] + return c + ["--read-only=%s" % self.params["read_only"]] def addparam_read_only_tmpfs(self, c): - return c + ['--read-only-tmpfs=%s' % self.params['read_only_tmpfs']] + return c + ["--read-only-tmpfs=%s" % self.params["read_only_tmpfs"]] def addparam_requires(self, c): - return c + ['--requires', ",".join(self.params['requires'])] + return c + ["--requires", ",".join(self.params["requires"])] # Exception for restart_policy and restart def addparam_restart_policy(self, c): - return c + ['--restart=%s' % self.params['restart_policy']] + return c + ["--restart=%s" % self.params["restart_policy"]] def addparam_retry(self, c): - return c + ['--retry', self.params['retry']] + return c + ["--retry", self.params["retry"]] def addparam_retry_delay(self, c): - return c + ['--retry-delay', self.params['retry_delay']] + return c + ["--retry-delay", self.params["retry_delay"]] def addparam_rm(self, c): - if self.params['rm']: - c += ['--rm'] + if self.params["rm"]: + c += ["--rm"] return c def addparam_rmi(self, c): - if self.params['rmi']: - c += ['--rmi'] + if self.params["rmi"]: + c += ["--rmi"] return c def addparam_rootfs(self, c): - return c + ['--rootfs=%s' % self.params['rootfs']] + return c + ["--rootfs=%s" % self.params["rootfs"]] def addparam_sdnotify(self, c): - return c + ['--sdnotify=%s' % self.params['sdnotify']] + return c + ["--sdnotify=%s" % self.params["sdnotify"]] def addparam_seccomp_policy(self, c): - return c + ['--seccomp-policy', self.params['seccomp_policy']] + return c + ["--seccomp-policy", self.params["seccomp_policy"]] # Exception for secrets and secret def addparam_secrets(self, c): - for secret in self.params['secrets']: - c += ['--secret', secret] + for secret in self.params["secrets"]: + c += ["--secret", secret] return c def addparam_security_opt(self, c): - for secopt in self.params['security_opt']: - c += ['--security-opt', secopt] + for secopt in self.params["security_opt"]: + c += ["--security-opt", secopt] return c def addparam_shm_size(self, c): - return c + ['--shm-size', self.params['shm_size']] + 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']] + 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']] + return c + ["--sig-proxy=%s" % self.params["sig_proxy"]] def addparam_stop_signal(self, c): - return c + ['--stop-signal', self.params['stop_signal']] + return c + ["--stop-signal", self.params["stop_signal"]] def addparam_stop_timeout(self, c): - return c + ['--stop-timeout', self.params['stop_timeout']] + return c + ["--stop-timeout", self.params["stop_timeout"]] def addparam_subgidname(self, c): - return c + ['--subgidname', self.params['subgidname']] + return c + ["--subgidname", self.params["subgidname"]] def addparam_subuidname(self, c): - return c + ['--subuidname', self.params['subuidname']] + return c + ["--subuidname", self.params["subuidname"]] def addparam_sysctl(self, c): - for sysctl in self.params['sysctl'].items(): - c += ['--sysctl', - b"=".join([to_bytes(k, errors='surrogate_or_strict') - for k in sysctl])] + for sysctl in self.params["sysctl"].items(): + c += [ + "--sysctl", + b"=".join([to_bytes(k, errors="surrogate_or_strict") for k in sysctl]), + ] return c def addparam_systemd(self, c): - return c + ['--systemd=%s' % str(self.params['systemd']).lower()] + return c + ["--systemd=%s" % str(self.params["systemd"]).lower()] def addparam_timeout(self, c): - return c + ['--timeout', self.params['timeout']] + return c + ["--timeout", self.params["timeout"]] # Exception for timezone and tz def addparam_timezone(self, c): - return c + ['--tz=%s' % self.params['timezone']] + return c + ["--tz=%s" % self.params["timezone"]] def addparam_tls_verify(self, c): - return c + ['--tls-verify=%s' % self.params['tls_verify']] + 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)] + for tmpfs in self.params["tmpfs"].items(): + c += ["--tmpfs", ":".join(tmpfs)] return c def addparam_tty(self, c): - return c + ['--tty=%s' % self.params['tty']] + return c + ["--tty=%s" % self.params["tty"]] def addparam_uidmap(self, c): - for uidmap in self.params['uidmap']: - c += ['--uidmap', uidmap] + for uidmap in self.params["uidmap"]: + c += ["--uidmap", uidmap] return c def addparam_ulimit(self, c): - for u in self.params['ulimit']: - c += ['--ulimit', u] + for u in self.params["ulimit"]: + c += ["--ulimit", u] return c def addparam_umask(self, c): - return c + ['--umask', self.params['umask']] + return c + ["--umask", self.params["umask"]] def addparam_unsetenv(self, c): - for unsetenv in self.params['unsetenv']: - c += ['--unsetenv', unsetenv] + 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'] + if self.params["unsetenv_all"]: + c += ["--unsetenv-all"] return c def addparam_user(self, c): - return c + ['--user', self.params['user']] + return c + ["--user", self.params["user"]] def addparam_userns(self, c): - return c + ['--userns', self.params['userns']] + return c + ["--userns", self.params["userns"]] def addparam_uts(self, c): - return c + ['--uts', self.params['uts']] + return c + ["--uts", self.params["uts"]] def addparam_variant(self, c): - return c + ['--variant', self.params['variant']] + return c + ["--variant", self.params["variant"]] def addparam_volume(self, c): - for vol in self.params['volume']: + for vol in self.params["volume"]: if vol: - c += ['--volume', vol] + c += ["--volume", vol] return c def addparam_volumes_from(self, c): - for vol in self.params['volumes_from']: - c += ['--volumes-from', vol] + for vol in self.params["volumes_from"]: + c += ["--volumes-from", vol] return c def addparam_workdir(self, c): - return c + ['--workdir', self.params['workdir']] + return c + ["--workdir", self.params["workdir"]] # Add your own args for podman command def addparam_cmd_args(self, c): - return c + self.params['cmd_args'] + return c + self.params["cmd_args"] class PodmanDefaults: @@ -891,11 +941,12 @@ class PodmanDefaults: def default_dict(self): # make here any changes to self.defaults related to podman version # https://github.com/containers/libpod/pull/5669 - 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('3.0.0')): - self.defaults['log_level'] = "warning" + 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("3.0.0"): + self.defaults["log_level"] = "warning" return self.defaults @@ -908,13 +959,12 @@ class PodmanContainerDiff: self.info = lower_keys(info) self.image_info = lower_keys(image_info) self.params = self.defaultize() - self.diff = {'before': {}, 'after': {}} + self.diff = {"before": {}, "after": {}} self.non_idempotent = {} def defaultize(self): params_with_defaults = {} - self.default_dict = PodmanDefaults( - self.image_info, self.version).default_dict() + self.default_dict = PodmanDefaults(self.image_info, self.version).default_dict() for p in self.module_params: if self.module_params[p] is None and p in self.default_dict: params_with_defaults[p] = self.default_dict[p] @@ -924,8 +974,8 @@ class PodmanContainerDiff: def _diff_update_and_compare(self, param_name, before, after): if before != after: - self.diff['before'].update({param_name: before}) - self.diff['after'].update({param_name: after}) + self.diff["before"].update({param_name: before}) + self.diff["after"].update({param_name: after}) return True return False @@ -944,62 +994,64 @@ class PodmanContainerDiff: """ info_config = self.info["config"] - before, after = diff_generic(self.params, info_config, module_arg, cmd_arg, boolean_type) + 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 {} + before = self.info["config"]["annotations"] or {} after = before.copy() - if self.module_params['annotation'] is not None: - after.update(self.params['annotation']) - return self._diff_update_and_compare('annotation', before, after) + if self.module_params["annotation"] is not None: + after.update(self.params["annotation"]) + return self._diff_update_and_compare("annotation", before, after) def diffparam_arch(self): - return self._diff_generic('arch', '--arch') + return self._diff_generic("arch", "--arch") def diffparam_authfile(self): - return self._diff_generic('authfile', '--authfile') + return self._diff_generic("authfile", "--authfile") def diffparam_blkio_weight(self): - return self._diff_generic('blkio_weight', '--blkio-weight') + return self._diff_generic("blkio_weight", "--blkio-weight") def diffparam_blkio_weight_device(self): - return self._diff_generic('blkio_weight_device', '--blkio-weight-device') + return self._diff_generic("blkio_weight_device", "--blkio-weight-device") def diffparam_cap_add(self): - before = self.info['effectivecaps'] or [] + before = self.info["effectivecaps"] or [] before = [i.lower() for i in before] after = [] - if self.module_params['cap_add'] is not None: - for cap in self.module_params['cap_add']: + if self.module_params["cap_add"] is not None: + for cap in self.module_params["cap_add"]: cap = cap.lower() - cap = cap if cap.startswith('cap_') else 'cap_' + cap + cap = cap if cap.startswith("cap_") else "cap_" + cap after.append(cap) after += before before, after = sorted(list(set(before))), sorted(list(set(after))) - return self._diff_update_and_compare('cap_add', before, after) + return self._diff_update_and_compare("cap_add", before, after) def diffparam_cap_drop(self): - before = self.info['effectivecaps'] or [] + before = self.info["effectivecaps"] or [] before = [i.lower() for i in before] after = before[:] - if self.module_params['cap_drop'] is not None: - for cap in self.module_params['cap_drop']: + if self.module_params["cap_drop"] is not None: + for cap in self.module_params["cap_drop"]: cap = cap.lower() - cap = cap if cap.startswith('cap_') else 'cap_' + cap + cap = cap if cap.startswith("cap_") else "cap_" + cap if cap in after: after.remove(cap) before, after = sorted(list(set(before))), sorted(list(set(after))) - return self._diff_update_and_compare('cap_drop', before, after) + return self._diff_update_and_compare("cap_drop", before, after) def diffparam_cgroup_conf(self): - return self._diff_generic('cgroup_conf', '--cgroup-conf') + return self._diff_generic("cgroup_conf", "--cgroup-conf") def diffparam_cgroup_parent(self): - return self._diff_generic('cgroup_parent', '--cgroup-parent') + return self._diff_generic("cgroup_parent", "--cgroup-parent") def diffparam_cgroupns(self): - return self._diff_generic('cgroupns', '--cgroupns') + 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 @@ -1007,7 +1059,7 @@ class PodmanContainerDiff: # return self._diff_generic('cgroups', '--cgroups') def diffparam_chrootdirs(self): - return self._diff_generic('chrootdirs', '--chrootdirs') + 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 @@ -1026,7 +1078,7 @@ class PodmanContainerDiff: in_quotes = True elif item.endswith('"') and in_quotes: buffer.append(item) - result.append(' '.join(buffer).strip('"')) + result.append(" ".join(buffer).strip('"')) buffer = [] in_quotes = False elif in_quotes: @@ -1039,179 +1091,186 @@ class PodmanContainerDiff: return result # TODO(sshnaidm): to inspect image to get the default command - if self.module_params['command'] is not None: - before = self.info['config']['cmd'] - after = self.params['command'] + if self.module_params["command"] is not None: + before = self.info["config"]["cmd"] + after = self.params["command"] if before: before = _join_quotes(before) if isinstance(after, list): after = [str(i) for i in after] if isinstance(after, str): after = shlex.split(after) - return self._diff_update_and_compare('command', before, after) + return self._diff_update_and_compare("command", before, after) return False def diffparam_conmon_pidfile(self): - return self._diff_generic('conmon_pidfile', '--conmon-pidfile') + return self._diff_generic("conmon_pidfile", "--conmon-pidfile") def diffparam_cpu_period(self): - return self._diff_generic('cpu_period', '--cpu-period') + return self._diff_generic("cpu_period", "--cpu-period") def diffparam_cpu_quota(self): - return self._diff_generic('cpu_quota', '--cpu-quota') + return self._diff_generic("cpu_quota", "--cpu-quota") def diffparam_cpu_rt_period(self): - return self._diff_generic('cpu_rt_period', '--cpu-rt-period') + return self._diff_generic("cpu_rt_period", "--cpu-rt-period") def diffparam_cpu_rt_runtime(self): - return self._diff_generic('cpu_rt_runtime', '--cpu-rt-runtime') + return self._diff_generic("cpu_rt_runtime", "--cpu-rt-runtime") def diffparam_cpu_shares(self): - return self._diff_generic('cpu_shares', '--cpu-shares') + return self._diff_generic("cpu_shares", "--cpu-shares") def diffparam_cpus(self): - return self._diff_generic('cpus', '--cpus') + return self._diff_generic("cpus", "--cpus") def diffparam_cpuset_cpus(self): - return self._diff_generic('cpuset_cpus', '--cpuset-cpus') + return self._diff_generic("cpuset_cpus", "--cpuset-cpus") def diffparam_cpuset_mems(self): - return self._diff_generic('cpuset_mems', '--cpuset-mems') + return self._diff_generic("cpuset_mems", "--cpuset-mems") def diffparam_decryption_key(self): - return self._diff_generic('decryption_key', '--decryption-key') + return self._diff_generic("decryption_key", "--decryption-key") def diffparam_device(self): - return self._diff_generic('device', '--device') + return self._diff_generic("device", "--device") def diffparam_device_cgroup_rule(self): - return self._diff_generic('device_cgroup_rule', '--device-cgroup-rule') + return self._diff_generic("device_cgroup_rule", "--device-cgroup-rule") def diffparam_device_read_bps(self): - return self._diff_generic('device_read_bps', '--device-read-bps') + return self._diff_generic("device_read_bps", "--device-read-bps") def diffparam_device_read_iops(self): - return self._diff_generic('device_read_iops', '--device-read-iops') + return self._diff_generic("device_read_iops", "--device-read-iops") def diffparam_device_write_bps(self): - return self._diff_generic('device_write_bps', '--device-write-bps') + return self._diff_generic("device_write_bps", "--device-write-bps") def diffparam_device_write_iops(self): - return self._diff_generic('device_write_iops', '--device-write-iops') + return self._diff_generic("device_write_iops", "--device-write-iops") def diffparam_dns(self): - return self._diff_generic('dns', '--dns') + return self._diff_generic("dns", "--dns") def diffparam_dns_option(self): - return self._diff_generic('dns_option', '--dns-option') + return self._diff_generic("dns_option", "--dns-option") def diffparam_dns_search(self): - return self._diff_generic('dns_search', '--dns-search') + return self._diff_generic("dns_search", "--dns-search") def diffparam_env(self): - return self._diff_generic('env', '--env') + return self._diff_generic("env", "--env") def diffparam_env_file(self): - return self._diff_generic('env_file', '--env-file') + return self._diff_generic("env_file", "--env-file") def diffparam_env_merge(self): - return self._diff_generic('env_merge', '--env-merge') + return self._diff_generic("env_merge", "--env-merge") def diffparam_env_host(self): - return self._diff_generic('env_host', '--env-host') + return self._diff_generic("env_host", "--env-host") def diffparam_etc_hosts(self): - if self.info['hostconfig']['extrahosts']: - before = dict([i.split(":", 1) - for i in self.info['hostconfig']['extrahosts']]) + if self.info["hostconfig"]["extrahosts"]: + before = dict( + [i.split(":", 1) for i in self.info["hostconfig"]["extrahosts"]] + ) else: before = {} - after = self.params['etc_hosts'] or {} - return self._diff_update_and_compare('etc_hosts', before, after) + 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') + return self._diff_generic("expose", "--expose") def diffparam_gidmap(self): - return self._diff_generic('gidmap', '--gidmap') + return self._diff_generic("gidmap", "--gidmap") def diffparam_gpus(self): - return self._diff_generic('gpus', '--gpus') + return self._diff_generic("gpus", "--gpus") def diffparam_group_add(self): - return self._diff_generic('group_add', '--group-add') + return self._diff_generic("group_add", "--group-add") def diffparam_group_entry(self): - return self._diff_generic('group_entry', '--group-entry') + 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. def diffparam_healthcheck(self): - before = '' - if 'healthcheck' in self.info['config']: + before = "" + if "healthcheck" in self.info["config"]: # the "test" key is a list of 2 items where the first one is # "CMD-SHELL" and the second one is the actual healthcheck command. - if len(self.info['config']['healthcheck']['test']) > 1: - before = self.info['config']['healthcheck']['test'][1] - after = self.params['healthcheck'] or before - return self._diff_update_and_compare('healthcheck', before, after) + if len(self.info["config"]["healthcheck"]["test"]) > 1: + before = self.info["config"]["healthcheck"]["test"][1] + after = self.params["healthcheck"] or before + return self._diff_update_and_compare("healthcheck", before, after) def diffparam_healthcheck_failure_action(self): - if 'healthcheckonfailureaction' in self.info['config']: - before = self.info['config']['healthcheckonfailureaction'] + if "healthcheckonfailureaction" in self.info["config"]: + before = self.info["config"]["healthcheckonfailureaction"] else: - before = '' - after = self.params['healthcheck_failure_action'] or before - return self._diff_update_and_compare('healthcheckonfailureaction', before, after) + before = "" + after = self.params["healthcheck_failure_action"] or before + return self._diff_update_and_compare( + "healthcheckonfailureaction", before, after + ) def diffparam_healthcheck_interval(self): - return self._diff_generic('healthcheck_interval', '--healthcheck-interval') + return self._diff_generic("healthcheck_interval", "--healthcheck-interval") def diffparam_healthcheck_retries(self): - return self._diff_generic('healthcheck_retries', '--healthcheck-retries') + return self._diff_generic("healthcheck_retries", "--healthcheck-retries") def diffparam_healthcheck_start_period(self): - return self._diff_generic('healthcheck_start_period', '--healthcheck-start-period') + 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') + 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') + 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') + 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') + 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') + return self._diff_generic("health_startup_timeout", "--health-startup-timeout") def diffparam_healthcheck_timeout(self): - return self._diff_generic('healthcheck_timeout', '--healthcheck-timeout') + return self._diff_generic("healthcheck_timeout", "--healthcheck-timeout") def diffparam_hooks_dir(self): - return self._diff_generic('hooks_dir', '--hooks-dir') + return self._diff_generic("hooks_dir", "--hooks-dir") def diffparam_hostname(self): - return self._diff_generic('hostname', '--hostname') + return self._diff_generic("hostname", "--hostname") def diffparam_hostuser(self): - return self._diff_generic('hostuser', '--hostuser') + return self._diff_generic("hostuser", "--hostuser") def diffparam_http_proxy(self): - return self._diff_generic('http_proxy', '--http-proxy') + return self._diff_generic("http_proxy", "--http-proxy") def diffparam_image(self): - before_id = self.info['image'] or self.info['rootfs'] - after_id = self.image_info['id'] + before_id = self.info["image"] or self.info["rootfs"] + after_id = self.image_info["id"] if before_id == after_id: - return self._diff_update_and_compare('image', before_id, after_id) - is_rootfs = self.info['rootfs'] != '' or self.params['rootfs'] - before = self.info['config']['image'] or before_id - after = self.params['image'] - mode = self.params['image_strict'] or is_rootfs + return self._diff_update_and_compare("image", before_id, after_id) + is_rootfs = self.info["rootfs"] != "" or self.params["rootfs"] + before = self.info["config"]["image"] or before_id + after = self.params["image"] + mode = self.params["image_strict"] or is_rootfs if mode is None or not mode: # In a idempotency 'lite mode' assume all images from different registries are the same before = before.replace(":latest", "") @@ -1219,116 +1278,117 @@ class PodmanContainerDiff: before = before.split("/")[-1] after = after.split("/")[-1] else: - return self._diff_update_and_compare('image', before_id, after_id) - return self._diff_update_and_compare('image', before, after) + 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') + return self._diff_generic("image_volume", "--image-volume") def diffparam_init(self): - return self._diff_generic('init', '--init', boolean_type=True) + return self._diff_generic("init", "--init", boolean_type=True) def diffparam_init_ctr(self): - return self._diff_generic('init_ctr', '--init-ctr') + return self._diff_generic("init_ctr", "--init-ctr") def diffparam_init_path(self): - return self._diff_generic('init_path', '--init-path') + return self._diff_generic("init_path", "--init-path") def diffparam_interactive(self): - return self._diff_generic('interactive', '--interactive') + return self._diff_generic("interactive", "--interactive") def diffparam_ip(self): - return self._diff_generic('ip', '--ip') + return self._diff_generic("ip", "--ip") def diffparam_ip6(self): - return self._diff_generic('ip6', '--ip6') + return self._diff_generic("ip6", "--ip6") def diffparam_ipc(self): - return self._diff_generic('ipc', '--ipc') + return self._diff_generic("ipc", "--ipc") def diffparam_label(self): - before = self.info['config']['labels'] or {} - after = self.image_info.get('labels') or {} - if self.params['label']: - after.update({ - str(k).lower(): str(v) - for k, v in self.params['label'].items() - }) + before = self.info["config"]["labels"] or {} + after = self.image_info.get("labels") or {} + if self.params["label"]: + after.update( + {str(k).lower(): str(v) for k, v in self.params["label"].items()} + ) # Strip out labels that are coming from systemd files # https://github.com/containers/ansible-podman-collections/issues/276 - if 'podman_systemd_unit' in before: - after.pop('podman_systemd_unit', None) - before.pop('podman_systemd_unit', None) - return self._diff_update_and_compare('label', before, after) + if "podman_systemd_unit" in before: + after.pop("podman_systemd_unit", None) + 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') + return self._diff_generic("label_file", "--label-file") def diffparam_log_driver(self): - return self._diff_generic('log_driver', '--log-driver') + return self._diff_generic("log_driver", "--log-driver") def diffparam_log_opt(self): - return self._diff_generic('log_opt', '--log-opt') + return self._diff_generic("log_opt", "--log-opt") def diffparam_mac_address(self): - return self._diff_generic('mac_address', '--mac-address') + return self._diff_generic("mac_address", "--mac-address") def diffparam_memory(self): - return self._diff_generic('memory', '--memory') + return self._diff_generic("memory", "--memory") def diffparam_memory_reservation(self): - return self._diff_generic('memory_reservation', '--memory-reservation') + return self._diff_generic("memory_reservation", "--memory-reservation") def diffparam_memory_swap(self): - return self._diff_generic('memory_swap', '--memory-swap') + return self._diff_generic("memory_swap", "--memory-swap") def diffparam_memory_swappiness(self): - return self._diff_generic('memory_swappiness', '--memory-swappiness') + return self._diff_generic("memory_swappiness", "--memory-swappiness") def diffparam_mount(self): - return self._diff_generic('mount', '--mount') + return self._diff_generic("mount", "--mount") def diffparam_network(self): - return self._diff_generic('network', '--network') + return self._diff_generic("network", "--network") def diffparam_network_aliases(self): - return self._diff_generic('network_aliases', '--network-alias') + return self._diff_generic("network_aliases", "--network-alias") def diffparam_no_healthcheck(self): - return self._diff_generic('no_healthcheck', '--no-healthcheck', boolean_type=True) + return self._diff_generic( + "no_healthcheck", "--no-healthcheck", boolean_type=True + ) def diffparam_no_hosts(self): - return self._diff_generic('no_hosts', '--no-hosts') + return self._diff_generic("no_hosts", "--no-hosts") def diffparam_oom_kill_disable(self): - return self._diff_generic('oom_kill_disable', '--oom-kill-disable') + return self._diff_generic("oom_kill_disable", "--oom-kill-disable") def diffparam_oom_score_adj(self): - return self._diff_generic('oom_score_adj', '--oom-score-adj') + return self._diff_generic("oom_score_adj", "--oom-score-adj") def diffparam_os(self): - return self._diff_generic('os', '--os') + return self._diff_generic("os", "--os") def diffparam_passwd(self): - return self._diff_generic('passwd', '--passwd', boolean_type=True) + return self._diff_generic("passwd", "--passwd", boolean_type=True) def diffparam_passwd_entry(self): - return self._diff_generic('passwd_entry', '--passwd-entry') + return self._diff_generic("passwd_entry", "--passwd-entry") def diffparam_personality(self): - return self._diff_generic('personality', '--personality') + return self._diff_generic("personality", "--personality") def diffparam_pid(self): - return self._diff_generic('pid', '--pid') + return self._diff_generic("pid", "--pid") def diffparam_pid_file(self): - return self._diff_generic('pid_file', '--pid-file') + return self._diff_generic("pid_file", "--pid-file") def diffparam_pids_limit(self): - return self._diff_generic('pids_limit', '--pids-limit') + return self._diff_generic("pids_limit", "--pids-limit") def diffparam_platform(self): - return self._diff_generic('platform', '--platform') + return self._diff_generic("platform", "--platform") # def diffparam_pod(self): # return self._diff_generic('pod', '--pod') @@ -1338,40 +1398,40 @@ class PodmanContainerDiff: # return self._diff_generic('pod_id_file', '--pod-id-file') def diffparam_privileged(self): - return self._diff_generic('privileged', '--privileged') + return self._diff_generic("privileged", "--privileged") def diffparam_publish(self): - return self._diff_generic('publish', '--publish') + return self._diff_generic("publish", "--publish") def diffparam_publish_all(self): - return self._diff_generic('publish_all', '--publish-all') + return self._diff_generic("publish_all", "--publish-all") def diffparam_pull(self): - return self._diff_generic('pull', '--pull') + return self._diff_generic("pull", "--pull") def diffparam_rdt_class(self): - return self._diff_generic('rdt_class', '--rdt-class') + return self._diff_generic("rdt_class", "--rdt-class") def diffparam_read_only(self): - return self._diff_generic('read_only', '--read-only') + return self._diff_generic("read_only", "--read-only") def diffparam_read_only_tmpfs(self): - return self._diff_generic('read_only_tmpfs', '--read-only-tmpfs') + return self._diff_generic("read_only_tmpfs", "--read-only-tmpfs") def diffparam_requires(self): - return self._diff_generic('requires', '--requires') + return self._diff_generic("requires", "--requires") def diffparam_restart_policy(self): - return self._diff_generic('restart_policy', '--restart') + return self._diff_generic("restart_policy", "--restart") def diffparam_retry(self): - return self._diff_generic('retry', '--retry') + return self._diff_generic("retry", "--retry") def diffparam_retry_delay(self): - return self._diff_generic('retry_delay', '--retry-delay') + return self._diff_generic("retry_delay", "--retry-delay") def diffparam_rootfs(self): - return self._diff_generic('rootfs', '--rootfs') + 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 @@ -1379,141 +1439,154 @@ class PodmanContainerDiff: # return self._diff_generic('sdnotify', '--sdnotify') def diffparam_rm(self): - before = self.info['hostconfig']['autoremove'] - after = self.params['rm'] + 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) + 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) + return self._diff_generic("rmi", "--rmi", boolean_type=True) def diffparam_seccomp_policy(self): - return self._diff_generic('seccomp_policy', '--seccomp-policy') + return self._diff_generic("seccomp_policy", "--seccomp-policy") def diffparam_secrets(self): - return self._diff_generic('secrets', '--secret') + return self._diff_generic("secrets", "--secret") def diffparam_security_opt(self): - return self._diff_generic('security_opt', '--security-opt') + return self._diff_generic("security_opt", "--security-opt") def diffparam_shm_size(self): - return self._diff_generic('shm_size', '--shm-size') + return self._diff_generic("shm_size", "--shm-size") def diffparam_shm_size_systemd(self): - return self._diff_generic('shm_size_systemd', '--shm-size-systemd') + return self._diff_generic("shm_size_systemd", "--shm-size-systemd") def diffparam_stop_signal(self): - return self._diff_generic('stop_signal', '--stop-signal') + return self._diff_generic("stop_signal", "--stop-signal") def diffparam_stop_timeout(self): - return self._diff_generic('stop_timeout', '--stop-timeout') + return self._diff_generic("stop_timeout", "--stop-timeout") def diffparam_subgidname(self): - return self._diff_generic('subgidname', '--subgidname') + return self._diff_generic("subgidname", "--subgidname") def diffparam_subuidname(self): - return self._diff_generic('subuidname', '--subuidname') + return self._diff_generic("subuidname", "--subuidname") def diffparam_sysctl(self): - return self._diff_generic('sysctl', '--sysctl') + return self._diff_generic("sysctl", "--sysctl") def diffparam_systemd(self): - if self.params['systemd'] is not None: - self.params['systemd'] = str(self.params['systemd']).lower() - return self._diff_generic('systemd', '--systemd') + if self.params["systemd"] is not None: + self.params["systemd"] = str(self.params["systemd"]).lower() + return self._diff_generic("systemd", "--systemd") def diffparam_timeout(self): - return self._diff_generic('timeout', '--timeout') + return self._diff_generic("timeout", "--timeout") def diffparam_timezone(self): - return self._diff_generic('timezone', '--tz') + return self._diff_generic("timezone", "--tz") def diffparam_tls_verify(self): - return self._diff_generic('tls_verify', '--tls-verify') + 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) + before = self.info["config"]["tty"] + after = self.params["tty"] + return self._diff_update_and_compare("tty", before, after) def diffparam_tmpfs(self): before = createcommand("--tmpfs", self.info["config"]) if before == []: before = None - after = self.params['tmpfs'] + after = self.params["tmpfs"] if before is None and after is None: - return self._diff_update_and_compare('tmpfs', before, after) + return self._diff_update_and_compare("tmpfs", before, after) if after is not None: - after = ",".join(sorted( - [str(k).lower() + ":" + str(v).lower() for k, v in after.items() if v is not None])) + 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])) else: - before = '' - return self._diff_update_and_compare('tmpfs', before, after) + before = "" + return self._diff_update_and_compare("tmpfs", before, after) def diffparam_uidmap(self): - return self._diff_generic('uidmap', '--uidmap') + return self._diff_generic("uidmap", "--uidmap") def diffparam_ulimit(self): - return self._diff_generic('ulimit', '--ulimit') + return self._diff_generic("ulimit", "--ulimit") def diffparam_umask(self): - return self._diff_generic('umask', '--umask') + return self._diff_generic("umask", "--umask") def diffparam_unsetenv(self): - return self._diff_generic('unsetenv', '--unsetenv') + return self._diff_generic("unsetenv", "--unsetenv") def diffparam_unsetenv_all(self): - return self._diff_generic('unsetenv_all', '--unsetenv-all', boolean_type=True) + return self._diff_generic("unsetenv_all", "--unsetenv-all", boolean_type=True) def diffparam_user(self): - return self._diff_generic('user', '--user') + return self._diff_generic("user", "--user") def diffparam_userns(self): - return self._diff_generic('userns', '--userns') + return self._diff_generic("userns", "--userns") def diffparam_uts(self): - return self._diff_generic('uts', '--uts') + return self._diff_generic("uts", "--uts") def diffparam_variant(self): - return self._diff_generic('variant', '--variant') + return self._diff_generic("variant", "--variant") def diffparam_volume(self): def clean_volume(x): - '''Remove trailing and double slashes from volumes.''' + """Remove trailing and double slashes from volumes.""" if not x.rstrip("/"): return "/" return x.replace("//", "/").rstrip("/") - before = createcommand('--volume', self.info['config']) + before = createcommand("--volume", self.info["config"]) if before == []: before = None - after = self.params['volume'] + 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']] + 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] + before = [ + ":".join([clean_volume(i) for i in v.split(":")[:2]]) for v in before + ] if before is None and after is None: - return self._diff_update_and_compare('volume', before, after) + 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) + return self._diff_update_and_compare("volume", before, after) def diffparam_volumes_from(self): - return self._diff_generic('volumes_from', '--volumes-from') + return self._diff_generic("volumes_from", "--volumes-from") def diffparam_workdir(self): - return self._diff_generic('workdir', '--workdir') + return self._diff_generic("workdir", "--workdir") def is_different(self): - diff_func_list = [func for func in dir(self) - if callable(getattr(self, func)) and func.startswith( - "diffparam")] + diff_func_list = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("diffparam") + ] fail_fast = not bool(self.module._diff) different = False for func_name in diff_func_list: @@ -1524,7 +1597,11 @@ class PodmanContainerDiff: different = True # Check non idempotent parameters for p in self.non_idempotent: - if self.module_params[p] is not None and self.module_params[p] not in [{}, [], '']: + if self.module_params[p] is not None and self.module_params[p] not in [ + {}, + [], + "", + ]: different = True return different @@ -1540,8 +1617,8 @@ def ensure_image_exists(module, image, module_params): list -- list of image actions - if it pulled or nothing was done """ image_actions = [] - module_exec = module_params['executable'] - is_rootfs = module_params['rootfs'] + module_exec = module_params["executable"] + is_rootfs = module_params["rootfs"] if is_rootfs: if not os.path.exists(image) or not os.path.isdir(image): @@ -1549,31 +1626,30 @@ def ensure_image_exists(module, image, module_params): return image_actions if not image: return image_actions - image_exists_cmd = [module_exec, 'image', 'exists', image] + image_exists_cmd = [module_exec, "image", "exists", image] rc, out, err = module.run_command(image_exists_cmd) if rc == 0: return image_actions - image_pull_cmd = [module_exec, 'image', 'pull', image] - if module_params['tls_verify'] is False: - image_pull_cmd.append('--tls-verify=false') - if module_params['authfile']: - image_pull_cmd.extend(['--authfile', module_params['authfile']]) - if module_params['arch']: - image_pull_cmd.append('--arch=%s' % module_params['arch']) - if module_params['decryption_key']: - image_pull_cmd.append('--decryption-key=%s' % module_params['decryption_key']) - if module_params['platform']: - image_pull_cmd.append('--platform=%s' % module_params['platform']) - if module_params['os']: - image_pull_cmd.append('--os=%s' % module_params['os']) - if module_params['variant']: - image_pull_cmd.append('--variant=%s' % module_params['variant']) - if module_params.get('debug'): + image_pull_cmd = [module_exec, "image", "pull", image] + if module_params["tls_verify"] is False: + image_pull_cmd.append("--tls-verify=false") + if module_params["authfile"]: + image_pull_cmd.extend(["--authfile", module_params["authfile"]]) + if module_params["arch"]: + image_pull_cmd.append("--arch=%s" % module_params["arch"]) + if module_params["decryption_key"]: + image_pull_cmd.append("--decryption-key=%s" % module_params["decryption_key"]) + if module_params["platform"]: + image_pull_cmd.append("--platform=%s" % module_params["platform"]) + if module_params["os"]: + image_pull_cmd.append("--os=%s" % module_params["os"]) + if module_params["variant"]: + image_pull_cmd.append("--variant=%s" % module_params["variant"]) + if module_params.get("debug"): module.log("PODMAN-CONTAINER-DEBUG: %s" % " ".join(image_pull_cmd)) rc, out, err = module.run_command(image_pull_cmd) if rc != 0: - module.fail_json(msg="Can't pull image %s" % image, stdout=out, - stderr=err) + module.fail_json(msg="Can't pull image %s" % image, stdout=out, stderr=err) image_actions.append("pulled image %s" % image) return image_actions @@ -1595,7 +1671,7 @@ class PodmanContainer: self.module = module self.module_params = module_params self.name = name - self.stdout, self.stderr = '', '' + self.stdout, self.stderr = "", "" self.info = self.get_info() self.version = self._get_podman_version() self.diff = {} @@ -1614,56 +1690,71 @@ class PodmanContainer: self.module_params, self.info, self.get_image_info(), - self.version) + self.version, + ) is_different = diffcheck.is_different() diffs = diffcheck.diff - if self.module._diff and is_different and diffs['before'] and diffs['after']: - self.diff['before'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['before'].items())]) + "\n" - self.diff['after'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['after'].items())]) + "\n" + if self.module._diff and is_different and diffs["before"] and diffs["after"]: + self.diff["before"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())] + ) + + "\n" + ) + self.diff["after"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())] + ) + + "\n" + ) return is_different @property def running(self): """Return True if container is running now.""" - return self.exists and self.info['State']['Running'] + return self.exists and self.info["State"]["Running"] @property def stopped(self): """Return True if container exists and is not running now.""" - return self.exists and not self.info['State']['Running'] + return self.exists and not self.info["State"]["Running"] def get_info(self): """Inspect container and gather info about it.""" # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'container', b'inspect', self.name]) + [self.module_params["executable"], b"container", b"inspect", self.name] + ) return json.loads(out)[0] if rc == 0 else {} def get_image_info(self): """Inspect container image and gather info about it.""" # pylint: disable=unused-variable - is_rootfs = self.module_params['rootfs'] + is_rootfs = self.module_params["rootfs"] if is_rootfs: - return {'Id': self.module_params['image']} + return {"Id": self.module_params["image"]} rc, out, err = self.module.run_command( - [self.module_params['executable'], - b'image', - b'inspect', - self.module_params['image'].replace('docker://', '')]) - self.module.log("PODMAN-CONTAINER-DEBUG: %s: %s" % (out, self.module_params['image'])) + [ + self.module_params["executable"], + b"image", + b"inspect", + self.module_params["image"].replace("docker://", ""), + ] + ) + self.module.log( + "PODMAN-CONTAINER-DEBUG: %s: %s" % (out, self.module_params["image"]) + ) return json.loads(out)[0] if rc == 0 else {} def _get_podman_version(self): # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'--version']) + [self.module_params["executable"], b"--version"] + ) if rc != 0 or not out or "version" not in out: - self.module.fail_json(msg="%s run failed!" % - self.module_params['executable']) + self.module.fail_json( + msg="%s run failed!" % self.module_params["executable"] + ) return out.split("version")[1].strip() def _perform_action(self, action): @@ -1673,23 +1764,25 @@ class PodmanContainer: action {str} -- action to perform - start, create, stop, run, delete, restart """ - b_command = PodmanModuleParams(action, - self.module_params, - self.version, - self.module, - ).construct_command_from_params() - full_cmd = " ".join([self.module_params['executable']] - + [to_native(i) for i in b_command]) + b_command = PodmanModuleParams( + action, + self.module_params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join( + [self.module_params["executable"]] + [to_native(i) for i in b_command] + ) self.actions.append(full_cmd) if self.module.check_mode: - self.module.log( - "PODMAN-CONTAINER-DEBUG (check_mode): %s" % full_cmd) + self.module.log("PODMAN-CONTAINER-DEBUG (check_mode): %s" % full_cmd) else: rc, out, err = self.module.run_command( - [self.module_params['executable'], b'container'] + b_command, - expand_user_and_vars=False) + [self.module_params["executable"], b"container"] + b_command, + expand_user_and_vars=False, + ) self.module.log("PODMAN-CONTAINER-DEBUG: %s" % full_cmd) - if self.module_params['debug']: + if self.module_params["debug"]: self.module.log("PODMAN-CONTAINER-DEBUG STDOUT: %s" % out) self.module.log("PODMAN-CONTAINER-DEBUG STDERR: %s" % err) self.module.log("PODMAN-CONTAINER-DEBUG RC: %s" % rc) @@ -1697,38 +1790,41 @@ class PodmanContainer: self.stderr = err if rc != 0: self.module.fail_json( - msg="Container %s exited with code %s when %sed" % (self.name, rc, action), - stdout=out, stderr=err) + msg="Container %s exited with code %s when %sed" + % (self.name, rc, action), + stdout=out, + stderr=err, + ) def run(self): """Run the container.""" - self._perform_action('run') + self._perform_action("run") def delete(self): """Delete the container.""" - self._perform_action('delete') + self._perform_action("delete") def stop(self): """Stop the container.""" - self._perform_action('stop') + self._perform_action("stop") def start(self): """Start the container.""" - self._perform_action('start') + self._perform_action("start") def restart(self): """Restart the container.""" - self._perform_action('restart') + self._perform_action("restart") def create(self): """Create the container.""" - self._perform_action('create') + self._perform_action("create") def recreate(self): """Recreate the container.""" if self.running: self.stop() - if not self.info['HostConfig']['AutoRemove']: + if not self.info["HostConfig"]["AutoRemove"]: self.delete() self.create() @@ -1736,7 +1832,7 @@ class PodmanContainer: """Recreate and run the container.""" if self.running: self.stop() - if not self.info['HostConfig']['AutoRemove']: + if not self.info["HostConfig"]["AutoRemove"]: self.delete() self.run() @@ -1756,30 +1852,34 @@ class PodmanManager: self.module = module self.results = { - 'changed': False, - 'actions': [], - 'container': {}, + "changed": False, + "actions": [], + "container": {}, } self.module_params = params - self.name = self.module_params['name'] - self.executable = \ - self.module.get_bin_path(self.module_params['executable'], - required=True) - self.image = self.module_params['image'] - self.state = self.module_params['state'] - disable_image_pull = self.state in ('quadlet', 'absent') or self.module_params['pull'] == 'never' - image_actions = ensure_image_exists( - self.module, self.image, self.module_params) if not disable_image_pull else [] - self.results['actions'] += image_actions + self.name = self.module_params["name"] + self.executable = self.module.get_bin_path( + self.module_params["executable"], required=True + ) + self.image = self.module_params["image"] + self.state = self.module_params["state"] + disable_image_pull = ( + self.state in ("quadlet", "absent") or self.module_params["pull"] == "never" + ) + image_actions = ( + ensure_image_exists(self.module, self.image, self.module_params) + if not disable_image_pull + else [] + ) + self.results["actions"] += image_actions - self.restart = self.module_params['force_restart'] - self.recreate = self.module_params['recreate'] + self.restart = self.module_params["force_restart"] + self.recreate = self.module_params["recreate"] - if self.module_params['generate_systemd'].get('new'): - self.module_params['rm'] = True + if self.module_params["generate_systemd"].get("new"): + self.module_params["rm"] = True - self.container = PodmanContainer( - self.module, self.name, self.module_params) + self.container = PodmanContainer(self.module, self.name, self.module_params) def update_container_result(self, changed=True): """Inspect the current container, update results with last info, exit. @@ -1790,121 +1890,114 @@ class PodmanManager: """ facts = self.container.get_info() if changed else self.container.info out, err = self.container.stdout, self.container.stderr - self.results.update({'changed': changed, 'container': facts, - 'podman_actions': self.container.actions}, - stdout=out, stderr=err) - if self.container.diff: - self.results.update({'diff': self.container.diff}) - if self.module.params['debug'] or self.module_params['debug']: - self.results.update({'podman_version': self.container.version}) - sysd = generate_systemd(self.module, - self.module_params, - self.name, - self.container.version) - self.results['changed'] = changed or sysd['changed'] self.results.update( - {'podman_systemd': sysd['systemd']}) - if sysd['diff']: - if 'diff' not in self.results: - self.results.update({'diff': sysd['diff']}) + { + "changed": changed, + "container": facts, + "podman_actions": self.container.actions, + }, + stdout=out, + stderr=err, + ) + if self.container.diff: + self.results.update({"diff": self.container.diff}) + if self.module.params["debug"] or self.module_params["debug"]: + self.results.update({"podman_version": self.container.version}) + sysd = generate_systemd( + self.module, self.module_params, self.name, self.container.version + ) + self.results["changed"] = changed or sysd["changed"] + self.results.update({"podman_systemd": sysd["systemd"]}) + if sysd["diff"]: + if "diff" not in self.results: + self.results.update({"diff": sysd["diff"]}) else: - self.results['diff']['before'] += sysd['diff']['before'] - self.results['diff']['after'] += sysd['diff']['after'] + self.results["diff"]["before"] += sysd["diff"]["before"] + self.results["diff"]["after"] += sysd["diff"]["after"] quadlet = ContainerQuadlet(self.module_params) quadlet_content = quadlet.create_quadlet_content() - self.results.update({'podman_quadlet': quadlet_content}) + self.results.update({"podman_quadlet": quadlet_content}) def make_started(self): """Run actions if desired state is 'started'.""" if not self.image: if not self.container.exists: - self.module.fail_json(msg='Cannot start container when image' - ' is not specified!') + self.module.fail_json( + msg="Cannot start container when image" " is not specified!" + ) if self.restart: self.container.restart() - self.results['actions'].append('restarted %s' % - self.container.name) + self.results["actions"].append("restarted %s" % self.container.name) else: self.container.start() - self.results['actions'].append('started %s' % - self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return if self.container.exists and self.restart: if self.container.running: self.container.restart() - self.results['actions'].append('restarted %s' % - self.container.name) + self.results["actions"].append("restarted %s" % self.container.name) else: self.container.start() - self.results['actions'].append('started %s' % - self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return - if self.container.running and \ - (self.container.different or self.recreate): + if self.container.running and (self.container.different or self.recreate): self.container.recreate_run() - self.results['actions'].append('recreated %s' % - self.container.name) + self.results["actions"].append("recreated %s" % self.container.name) self.update_container_result() return elif self.container.running and not self.container.different: if self.restart: self.container.restart() - self.results['actions'].append('restarted %s' % - self.container.name) + self.results["actions"].append("restarted %s" % self.container.name) self.update_container_result() return self.update_container_result(changed=False) return elif not self.container.exists: self.container.run() - self.results['actions'].append('started %s' % self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return - elif self.container.stopped and \ - (self.container.different or self.recreate): + elif self.container.stopped and (self.container.different or self.recreate): self.container.recreate_run() - self.results['actions'].append('recreated %s' % - self.container.name) + self.results["actions"].append("recreated %s" % self.container.name) self.update_container_result() return elif self.container.stopped and not self.container.different: self.container.start() - self.results['actions'].append('started %s' % self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return def make_created(self): """Run actions if desired state is 'created'.""" if not self.container.exists and not self.image: - self.module.fail_json(msg='Cannot create container when image' - ' is not specified!') + self.module.fail_json( + msg="Cannot create container when image" " is not specified!" + ) if not self.container.exists: self.container.create() - self.results['actions'].append('created %s' % self.container.name) + self.results["actions"].append("created %s" % self.container.name) self.update_container_result() return else: - if (self.container.different or self.recreate): + if self.container.different or self.recreate: self.container.recreate() - self.results['actions'].append('recreated %s' % - self.container.name) + self.results["actions"].append("recreated %s" % self.container.name) if self.container.running: self.container.start() - self.results['actions'].append('started %s' % - self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return elif self.restart: if self.container.running: self.container.restart() - self.results['actions'].append('restarted %s' % - self.container.name) + self.results["actions"].append("restarted %s" % self.container.name) else: self.container.start() - self.results['actions'].append('started %s' % - self.container.name) + self.results["actions"].append("started %s" % self.container.name) self.update_container_result() return self.update_container_result(changed=False) @@ -1913,11 +2006,12 @@ class PodmanManager: def make_stopped(self): """Run actions if desired state is 'stopped'.""" if not self.container.exists and not self.image: - self.module.fail_json(msg='Cannot create container when image' - ' is not specified!') + self.module.fail_json( + msg="Cannot create container when image" " is not specified!" + ) if not self.container.exists: self.container.create() - self.results['actions'].append('created %s' % self.container.name) + self.results["actions"].append("created %s" % self.container.name) self.update_container_result() return if self.container.stopped: @@ -1925,24 +2019,22 @@ class PodmanManager: return elif self.container.running: self.container.stop() - self.results['actions'].append('stopped %s' % self.container.name) + self.results["actions"].append("stopped %s" % self.container.name) self.update_container_result() return def make_absent(self): """Run actions if desired state is 'absent'.""" if not self.container.exists: - self.results.update({'changed': False}) + self.results.update({"changed": False}) elif self.container.exists: - delete_systemd(self.module, - self.module_params, - self.name, - self.container.version) + delete_systemd( + self.module, self.module_params, self.name, self.container.version + ) self.container.delete() - self.results['actions'].append('deleted %s' % self.container.name) - self.results.update({'changed': True}) - self.results.update({'container': {}, - 'podman_actions': self.container.actions}) + self.results["actions"].append("deleted %s" % self.container.name) + self.results.update({"changed": True}) + self.results.update({"container": {}, "podman_actions": self.container.actions}) def make_quadlet(self): results_update = create_quadlet_state(self.module, "container") @@ -1951,12 +2043,12 @@ class PodmanManager: def execute(self): """Execute the desired action according to map of actions & states.""" states_map = { - 'present': self.make_created, - 'started': self.make_started, - 'absent': self.make_absent, - 'stopped': self.make_stopped, - 'created': self.make_created, - 'quadlet': self.make_quadlet, + "present": self.make_created, + "started": self.make_started, + "absent": self.make_absent, + "stopped": self.make_stopped, + "created": self.make_created, + "quadlet": self.make_quadlet, } process_action = states_map[self.state] process_action() diff --git a/plugins/module_utils/podman/podman_pod_lib.py b/plugins/module_utils/podman/podman_pod_lib.py index 17c4254..d9afff5 100644 --- a/plugins/module_utils/podman/podman_pod_lib.py +++ b/plugins/module_utils/podman/podman_pod_lib.py @@ -1,107 +1,122 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import json # noqa: F402 from ansible.module_utils._text import to_bytes, to_native -from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion -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 +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + LooseVersion, +) +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, +) __metaclass__ = type ARGUMENTS_SPEC_POD = dict( state=dict( - type='str', + type="str", default="created", choices=[ - 'created', - 'killed', - 'restarted', - 'absent', - 'started', - 'stopped', - 'paused', - 'unpaused', - 'quadlet' - ]), - recreate=dict(type='bool', default=False), - add_host=dict(type='list', required=False, elements='str'), - 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), - cpu_shares=dict(type='str', required=False), - device=dict(type='list', elements='str', required=False), - 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', 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), - infra_command=dict(type='str', required=False), - 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), - memory=dict(type='str', required=False), - memory_swap=dict(type='str', required=False), - name=dict(type='str', required=True), - network=dict(type='list', elements='str', required=False), - network_alias=dict(type='list', elements='str', required=False, - aliases=['network_aliases']), - no_hosts=dict(type='bool', required=False), - pid=dict(type='str', required=False), - pod_id_file=dict(type='str', required=False), - publish=dict(type='list', required=False, - elements='str', aliases=['ports']), - quadlet_dir=dict(type='path'), - quadlet_filename=dict(type='str'), - quadlet_file_mode=dict(type='raw', required=False), - 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), + "created", + "killed", + "restarted", + "absent", + "started", + "stopped", + "paused", + "unpaused", + "quadlet", + ], + ), + recreate=dict(type="bool", default=False), + add_host=dict(type="list", required=False, elements="str"), + 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), + cpu_shares=dict(type="str", required=False), + device=dict(type="list", elements="str", required=False), + 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", 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), + infra_command=dict(type="str", required=False), + 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), + memory=dict(type="str", required=False), + memory_swap=dict(type="str", required=False), + name=dict(type="str", required=True), + network=dict(type="list", elements="str", required=False), + network_alias=dict( + type="list", elements="str", required=False, aliases=["network_aliases"] + ), + no_hosts=dict(type="bool", required=False), + pid=dict(type="str", required=False), + pod_id_file=dict(type="str", required=False), + publish=dict(type="list", required=False, elements="str", aliases=["ports"]), + quadlet_dir=dict(type="path"), + quadlet_filename=dict(type="str"), + quadlet_file_mode=dict(type="raw", required=False), + 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), ) class PodmanPodModuleParams: """Creates list of arguments for podman CLI command. - Arguments: - action {str} -- action type from 'run', 'stop', 'create', 'delete', - 'start' - params {dict} -- dictionary of module parameters + Arguments: + action {str} -- action type from 'run', 'stop', 'create', 'delete', + 'start' + params {dict} -- dictionary of module parameters - """ + """ def __init__(self, action, params, podman_version, module): self.params = params @@ -115,252 +130,270 @@ class PodmanPodModuleParams: Returns: list -- list of byte strings for Popen command """ - if self.action in ['start', 'restart', 'stop', 'delete', 'pause', - 'unpause', 'kill']: + if self.action in [ + "start", + "restart", + "stop", + "delete", + "pause", + "unpause", + "kill", + ]: return self._simple_action() - if self.action in ['create']: + if self.action in ["create"]: return self._create_action() self.module.fail_json(msg="Unknown action %s" % self.action) def _simple_action(self): - if self.action in ['start', 'restart', 'stop', 'pause', 'unpause', 'kill']: - cmd = [self.action, self.params['name']] - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + if self.action in ["start", "restart", "stop", "pause", "unpause", "kill"]: + cmd = [self.action, self.params["name"]] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] - 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 == "delete": + cmd = ["rm", "-f", self.params["name"]] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] self.module.fail_json(msg="Unknown action %s" % self.action) def _create_action(self): cmd = [self.action] - all_param_methods = [func for func in dir(self) - if callable(getattr(self, func)) - and func.startswith("addparam")] + all_param_methods = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("addparam") + ] params_set = (i for i in self.params if self.params[i] is not None) for param in params_set: func_name = "_".join(["addparam", param]) if func_name in all_param_methods: cmd = getattr(self, func_name)(cmd) - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def check_version(self, param, minv=None, maxv=None): - if minv and LooseVersion(minv) > LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported from podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) - if maxv and LooseVersion(maxv) < LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported till podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) + if minv and LooseVersion(minv) > LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) + if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) def addparam_add_host(self, c): - for g in self.params['add_host']: - c += ['--add-host', g] + for g in self.params["add_host"]: + c += ["--add-host", g] return c def addparam_blkio_weight(self, c): - self.check_version('--blkio-weight', minv='4.3.0') - return c + ['--blkio-weight', self.params['blkio_weight']] + self.check_version("--blkio-weight", minv="4.3.0") + return c + ["--blkio-weight", self.params["blkio_weight"]] def addparam_blkio_weight_device(self, c): - self.check_version('--blkio-weight-device', minv='4.3.0') - for dev in self.params['blkio_weight_device']: - c += ['--blkio-weight-device', dev] + self.check_version("--blkio-weight-device", minv="4.3.0") + for dev in self.params["blkio_weight_device"]: + c += ["--blkio-weight-device", dev] return c def addparam_cgroup_parent(self, c): - return c + ['--cgroup-parent', self.params['cgroup_parent']] + return c + ["--cgroup-parent", self.params["cgroup_parent"]] def addparam_cpus(self, c): - self.check_version('--cpus', minv='4.2.0') - return c + ['--cpus', self.params['cpus']] + self.check_version("--cpus", minv="4.2.0") + return c + ["--cpus", self.params["cpus"]] def addparam_cpuset_cpus(self, c): - self.check_version('--cpus', minv='4.2.0') - return c + ['--cpuset-cpus', self.params['cpuset_cpus']] + self.check_version("--cpus", minv="4.2.0") + return c + ["--cpuset-cpus", self.params["cpuset_cpus"]] def addparam_cpuset_mems(self, c): - self.check_version('--cpuset-mems', minv='4.3.0') - return c + ['--cpuset-mems', self.params['cpuset_mems']] + self.check_version("--cpuset-mems", minv="4.3.0") + return c + ["--cpuset-mems", self.params["cpuset_mems"]] def addparam_cpu_shares(self, c): - self.check_version('--cpu-shares', minv='4.3.0') - return c + ['--cpu-shares', self.params['cpu_shares']] + self.check_version("--cpu-shares", minv="4.3.0") + return c + ["--cpu-shares", self.params["cpu_shares"]] def addparam_device(self, c): - for dev in self.params['device']: - c += ['--device', dev] + for dev in self.params["device"]: + c += ["--device", dev] return c def addparam_device_read_bps(self, c): - self.check_version('--device-read-bps', minv='4.3.0') - for dev in self.params['device_read_bps']: - c += ['--device-read-bps', dev] + self.check_version("--device-read-bps", minv="4.3.0") + for dev in self.params["device_read_bps"]: + c += ["--device-read-bps", dev] return c def addparam_device_write_bps(self, c): - self.check_version('--device-write-bps', minv='4.3.0') - for dev in self.params['device_write_bps']: - c += ['--device-write-bps', dev] + self.check_version("--device-write-bps", minv="4.3.0") + for dev in self.params["device_write_bps"]: + c += ["--device-write-bps", dev] return c def addparam_dns(self, c): - for g in self.params['dns']: - c += ['--dns', g] + for g in self.params["dns"]: + c += ["--dns", g] return c def addparam_dns_opt(self, c): - for g in self.params['dns_opt']: - c += ['--dns-option', g] + for g in self.params["dns_opt"]: + c += ["--dns-option", g] return c def addparam_dns_search(self, c): - for g in self.params['dns_search']: - c += ['--dns-search', g] + for g in self.params["dns_search"]: + c += ["--dns-search", g] return c def addparam_exit_policy(self, c): - return c + ['--exit-policy=%s' % self.params['exit_policy']] + return c + ["--exit-policy=%s" % self.params["exit_policy"]] def addparam_gidmap(self, c): - for gidmap in self.params['gidmap']: - c += ['--gidmap', gidmap] + for gidmap in self.params["gidmap"]: + c += ["--gidmap", gidmap] return c def addparam_gpus(self, c): - return c + ['--gpus', self.params['gpus']] + return c + ["--gpus", self.params["gpus"]] def addparam_hostname(self, c): - return c + ['--hostname', self.params['hostname']] + return c + ["--hostname", self.params["hostname"]] def addparam_infra(self, c): - return c + [b'='.join([b'--infra', - to_bytes(self.params['infra'], - errors='surrogate_or_strict')])] + return c + [ + b"=".join( + [ + b"--infra", + to_bytes(self.params["infra"], errors="surrogate_or_strict"), + ] + ) + ] def addparam_infra_conmon_pidfile(self, c): - return c + ['--infra-conmon-pidfile', self.params['infra_conmon_pidfile']] + return c + ["--infra-conmon-pidfile", self.params["infra_conmon_pidfile"]] def addparam_infra_command(self, c): - return c + ['--infra-command', self.params['infra_command']] + return c + ["--infra-command", self.params["infra_command"]] def addparam_infra_image(self, c): - return c + ['--infra-image', self.params['infra_image']] + return c + ["--infra-image", self.params["infra_image"]] def addparam_infra_name(self, c): - return c + ['--infra-name', self.params['infra_name']] + return c + ["--infra-name", self.params["infra_name"]] def addparam_ip(self, c): - return c + ['--ip', self.params['ip']] + return c + ["--ip", self.params["ip"]] def addparam_ip6(self, c): - return c + ['--ip6', self.params['ip6']] + return c + ["--ip6", self.params["ip6"]] def addparam_label(self, c): - for label in self.params['label'].items(): - c += ['--label', b'='.join( - [to_bytes(i, errors='surrogate_or_strict') for i in label])] + for label in self.params["label"].items(): + c += [ + "--label", + b"=".join([to_bytes(i, errors="surrogate_or_strict") for i in label]), + ] return c def addparam_label_file(self, c): - return c + ['--label-file', self.params['label_file']] + return c + ["--label-file", self.params["label_file"]] def addparam_mac_address(self, c): - return c + ['--mac-address', self.params['mac_address']] + return c + ["--mac-address", self.params["mac_address"]] def addparam_memory(self, c): - self.check_version('--memory', minv='4.2.0') - return c + ['--memory', self.params['memory']] + self.check_version("--memory", minv="4.2.0") + return c + ["--memory", self.params["memory"]] def addparam_memory_swap(self, c): - self.check_version('--memory-swap', minv='4.3.0') - return c + ['--memory-swap', self.params['memory_swap']] + self.check_version("--memory-swap", minv="4.3.0") + return c + ["--memory-swap", self.params["memory_swap"]] def addparam_name(self, c): - return c + ['--name', self.params['name']] + return c + ["--name", self.params["name"]] def addparam_network(self, c): - if LooseVersion(self.podman_version) >= LooseVersion('4.0.0'): - for net in self.params['network']: - c += ['--network', net] + if LooseVersion(self.podman_version) >= LooseVersion("4.0.0"): + for net in self.params["network"]: + c += ["--network", net] return c - return c + ['--network', ",".join(self.params['network'])] + return c + ["--network", ",".join(self.params["network"])] def addparam_network_aliases(self, c): - for alias in self.params['network_aliases']: - c += ['--network-alias', alias] + for alias in self.params["network_aliases"]: + c += ["--network-alias", alias] return c def addparam_no_hosts(self, c): - return c + ["=".join(['--no-hosts', self.params['no_hosts']])] + return c + ["=".join(["--no-hosts", self.params["no_hosts"]])] def addparam_pid(self, c): - return c + ['--pid', self.params['pid']] + return c + ["--pid", self.params["pid"]] def addparam_pod_id_file(self, c): - return c + ['--pod-id-file', self.params['pod_id_file']] + return c + ["--pod-id-file", self.params["pod_id_file"]] def addparam_publish(self, c): - for g in self.params['publish']: - c += ['--publish', g] + for g in self.params["publish"]: + c += ["--publish", g] return c def addparam_restart_policy(self, c): - return c + ['--restart=%s' % self.params['restart_policy']] + 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] + for g in self.params["security_opt"]: + c += ["--security-opt", g] return c def addparam_share(self, c): - return c + ['--share', self.params['share']] + 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']] + 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']] + 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']] + return c + ["--shm-size-systemd=%s" % self.params["shm_size_systemd"]] def addparam_subgidname(self, c): - return c + ['--subgidname', self.params['subgidname']] + return c + ["--subgidname", self.params["subgidname"]] def addparam_subuidname(self, c): - return c + ['--subuidname', self.params['subuidname']] + 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)] + 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] + for uidmap in self.params["uidmap"]: + c += ["--uidmap", uidmap] return c def addparam_userns(self, c): - return c + ['--userns', self.params['userns']] + return c + ["--userns", self.params["userns"]] def addparam_uts(self, c): - return c + ['--uts', self.params['uts']] + return c + ["--uts", self.params["uts"]] def addparam_volume(self, c): - for vol in self.params['volume']: + for vol in self.params["volume"]: if vol: - c += ['--volume', vol] + c += ["--volume", vol] return c def addparam_volumes_from(self, c): - for vol in self.params['volumes_from']: - c += ['--volumes-from', vol] + for vol in self.params["volumes_from"]: + c += ["--volumes-from", vol] return c @@ -369,8 +402,8 @@ class PodmanPodDefaults: self.module = module self.version = podman_version self.defaults = { - 'infra': True, - 'label': {}, + "infra": True, + "label": {}, } def default_dict(self): @@ -391,13 +424,12 @@ class PodmanPodDiff: self.info = lower_keys(info) self.infra_info = lower_keys(infra_info) self.params = self.defaultize() - self.diff = {'before': {}, 'after': {}} + self.diff = {"before": {}, "after": {}} self.non_idempotent = {} def defaultize(self): params_with_defaults = {} - self.default_dict = PodmanPodDefaults( - self.module, self.version).default_dict() + self.default_dict = PodmanPodDefaults(self.module, self.version).default_dict() for p in self.module_params: if self.module_params[p] is None and p in self.default_dict: params_with_defaults[p] = self.default_dict[p] @@ -407,8 +439,8 @@ class PodmanPodDiff: def _diff_update_and_compare(self, param_name, before, after): if before != after: - self.diff['before'].update({param_name: before}) - self.diff['after'].update({param_name: after}) + self.diff["before"].update({param_name: before}) + self.diff["after"].update({param_name: after}) return True return False @@ -427,50 +459,52 @@ class PodmanPodDiff: """ info_config = self.info - before, after = diff_generic(self.params, info_config, module_arg, cmd_arg, boolean_type) + 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): - return self._diff_generic('add_host', '--add-host') + return self._diff_generic("add_host", "--add-host") def diffparam_blkio_weight(self): - return self._diff_generic('blkio_weight', '--blkio-weight') + return self._diff_generic("blkio_weight", "--blkio-weight") def diffparam_blkio_weight_device(self): - return self._diff_generic('blkio_weight_device', '--blkio-weight-device') + return self._diff_generic("blkio_weight_device", "--blkio-weight-device") def diffparam_cgroup_parent(self): - return self._diff_generic('cgroup_parent', '--cgroup-parent') + return self._diff_generic("cgroup_parent", "--cgroup-parent") def diffparam_cpu_shares(self): - return self._diff_generic('cpu_shares', '--cpu-shares') + return self._diff_generic("cpu_shares", "--cpu-shares") def diffparam_cpus(self): - return self._diff_generic('cpus', '--cpus') + return self._diff_generic("cpus", "--cpus") def diffparam_cpuset_cpus(self): - return self._diff_generic('cpuset_cpus', '--cpuset-cpus') + return self._diff_generic("cpuset_cpus", "--cpuset-cpus") def diffparam_cpuset_mems(self): - return self._diff_generic('cpuset_mems', '--cpuset-mems') + return self._diff_generic("cpuset_mems", "--cpuset-mems") def diffparam_device(self): - return self._diff_generic('device', '--device') + return self._diff_generic("device", "--device") def diffparam_device_read_bps(self): - return self._diff_generic('device_read_bps', '--device-read-bps') + 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') + return self._diff_generic("device_write_bps", "--device-write-bps") def diffparam_dns(self): - return self._diff_generic('dns', '--dns') + return self._diff_generic("dns", "--dns") def diffparam_dns_opt(self): - return self._diff_generic('dns_opt', '--dns-option') + return self._diff_generic("dns_opt", "--dns-option") def diffparam_dns_search(self): - return self._diff_generic('dns_search', '--dns-search') + 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 @@ -478,26 +512,26 @@ class PodmanPodDiff: # return self._diff_generic('exit_policy', '--exit-policy') def diffparam_gidmap(self): - return self._diff_generic('gidmap', '--gidmap') + return self._diff_generic("gidmap", "--gidmap") def diffparam_gpus(self): - return self._diff_generic('gpus', '--gpus') + return self._diff_generic("gpus", "--gpus") def diffparam_hostname(self): - return self._diff_generic('hostname', '--hostname') + return self._diff_generic("hostname", "--hostname") # TODO(sshnaidm): https://github.com/containers/podman/issues/6968 def diffparam_infra(self): - if 'state' in self.info and 'infracontainerid' in self.info['state']: - before = self.info['state']['infracontainerid'] != "" + if "state" in self.info and "infracontainerid" in self.info["state"]: + before = self.info["state"]["infracontainerid"] != "" else: # TODO(sshnaidm): https://github.com/containers/podman/issues/6968 - before = 'infracontainerid' in self.info - after = self.params['infra'] - return self._diff_update_and_compare('infra', before, after) + before = "infracontainerid" in self.info + after = self.params["infra"] + return self._diff_update_and_compare("infra", before, after) def diffparam_infra_command(self): - return self._diff_generic('infra_command', '--infra-command') + 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 @@ -505,53 +539,53 @@ class PodmanPodDiff: # return self._diff_generic('infra_conmon_pidfile', '--infra-conmon-pidfile') def diffparam_infra_image(self): - return self._diff_generic('infra_image', '--infra-image') + return self._diff_generic("infra_image", "--infra-image") def diffparam_infra_name(self): - return self._diff_generic('infra_name', '--infra-name') + return self._diff_generic("infra_name", "--infra-name") def diffparam_ip(self): - return self._diff_generic('ip', '--ip') + return self._diff_generic("ip", "--ip") def diffparam_ip6(self): - return self._diff_generic('ip6', '--ip6') + return self._diff_generic("ip6", "--ip6") def diffparam_label(self): - if 'config' in self.info and 'labels' in self.info['config']: - before = self.info['config'].get('labels') or {} + if "config" in self.info and "labels" in self.info["config"]: + before = self.info["config"].get("labels") or {} else: - before = self.info['labels'] if 'labels' in self.info else {} - after = self.params['label'] + before = self.info["labels"] if "labels" in self.info else {} + after = self.params["label"] # Strip out labels that are coming from systemd files # https://github.com/containers/ansible-podman-collections/issues/276 - if 'podman_systemd_unit' in before: - after.pop('podman_systemd_unit', None) - before.pop('podman_systemd_unit', None) - return self._diff_update_and_compare('label', before, after) + if "podman_systemd_unit" in before: + after.pop("podman_systemd_unit", None) + 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') + return self._diff_generic("label_file", "--label-file") def diffparam_mac_address(self): - return self._diff_generic('mac_address', '--mac-address') + return self._diff_generic("mac_address", "--mac-address") def diffparam_memory(self): - return self._diff_generic('memory', '--memory') + return self._diff_generic("memory", "--memory") def diffparam_memory_swap(self): - return self._diff_generic('memory_swap', '--memory-swap') + return self._diff_generic("memory_swap", "--memory-swap") def diffparam_network(self): - return self._diff_generic('network', '--network') + return self._diff_generic("network", "--network") def diffparam_network_alias(self): - return self._diff_generic('network_alias', '--network-alias') + return self._diff_generic("network_alias", "--network-alias") def diffparam_no_hosts(self): - return self._diff_generic('no_hosts', '--no-hosts', boolean_type=True) + return self._diff_generic("no_hosts", "--no-hosts", boolean_type=True) def diffparam_pid(self): - return self._diff_generic('pid', '--pid') + 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 @@ -559,76 +593,82 @@ class PodmanPodDiff: # return self._diff_generic('pod_id_file', '--pod-id-file') def diffparam_publish(self): - return self._diff_generic('publish', '--publish') + return self._diff_generic("publish", "--publish") def diffparam_restart_policy(self): - return self._diff_generic('restart_policy', '--restart') + return self._diff_generic("restart_policy", "--restart") def diffparam_security_opt(self): - return self._diff_generic('security_opt', '--security-opt') + return self._diff_generic("security_opt", "--security-opt") def diffparam_share(self): - return self._diff_generic('share', '--share') + return self._diff_generic("share", "--share") def diffparam_share_parent(self): - return self._diff_generic('share_parent', '--share-parent') + return self._diff_generic("share_parent", "--share-parent") def diffparam_shm_size(self): - return self._diff_generic('shm_size', '--shm-size') + return self._diff_generic("shm_size", "--shm-size") def diffparam_shm_size_systemd(self): - return self._diff_generic('shm_size_systemd', '--shm-size-systemd') + return self._diff_generic("shm_size_systemd", "--shm-size-systemd") def diffparam_subgidname(self): - return self._diff_generic('subgidname', '--subgidname') + return self._diff_generic("subgidname", "--subgidname") def diffparam_subuidname(self): - return self._diff_generic('subuidname', '--subuidname') + return self._diff_generic("subuidname", "--subuidname") def diffparam_sysctl(self): - return self._diff_generic('sysctl', '--sysctl') + return self._diff_generic("sysctl", "--sysctl") def diffparam_uidmap(self): - return self._diff_generic('uidmap', '--uidmap') + return self._diff_generic("uidmap", "--uidmap") def diffparam_userns(self): - return self._diff_generic('userns', '--userns') + return self._diff_generic("userns", "--userns") def diffparam_uts(self): - return self._diff_generic('uts', '--uts') + return self._diff_generic("uts", "--uts") def diffparam_volume(self): def clean_volume(x): - '''Remove trailing and double slashes from volumes.''' + """Remove trailing and double slashes from volumes.""" if not x.rstrip("/"): return "/" return x.replace("//", "/").rstrip("/") - before = createcommand('--volume', self.info) + before = createcommand("--volume", self.info) if before == []: before = None - after = self.params['volume'] + 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']] + 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] + 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) + 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) + return self._diff_update_and_compare("volume", before, after) def diffparam_volumes_from(self): - return self._diff_generic('volumes_from', '--volumes-from') + return self._diff_generic("volumes_from", "--volumes-from") def is_different(self): - diff_func_list = [func for func in dir(self) - if callable(getattr(self, func)) and func.startswith( - "diffparam")] + diff_func_list = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("diffparam") + ] fail_fast = not bool(self.module._diff) different = False for func_name in diff_func_list: @@ -639,7 +679,11 @@ class PodmanPodDiff: different = True # Check non idempotent parameters for p in self.non_idempotent: - if self.module_params[p] is not None and self.module_params[p] not in [{}, [], '']: + if self.module_params[p] is not None and self.module_params[p] not in [ + {}, + [], + "", + ]: different = True return different @@ -661,7 +705,7 @@ class PodmanPod: self.module = module self.module_params = module_params self.name = name - self.stdout, self.stderr = '', '' + self.stdout, self.stderr = "", "" self.info = self.get_info() self.infra_info = self.get_infra_info() self.version = self._get_podman_version() @@ -677,55 +721,59 @@ class PodmanPod: def different(self): """Check if pod is different.""" diffcheck = PodmanPodDiff( - self.module, - self.module_params, - self.info, - self.infra_info, - self.version) + self.module, self.module_params, self.info, self.infra_info, self.version + ) is_different = diffcheck.is_different() diffs = diffcheck.diff - if self.module._diff and is_different and diffs['before'] and diffs['after']: - self.diff['before'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['before'].items())]) + "\n" - self.diff['after'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['after'].items())]) + "\n" + if self.module._diff and is_different and diffs["before"] and diffs["after"]: + self.diff["before"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())] + ) + + "\n" + ) + self.diff["after"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())] + ) + + "\n" + ) return is_different @property def running(self): """Return True if pod is running now.""" - if 'status' in self.info['State']: - return self.info['State']['status'] == 'Running' + if "status" in self.info["State"]: + return self.info["State"]["status"] == "Running" # older podman versions (1.6.x) don't have status in 'podman pod inspect' # if other methods fail, use 'podman pod ps' ps_info = self.get_ps() - if 'status' in ps_info: - return ps_info['status'] == 'Running' - return self.info['State'] == 'Running' + if "status" in ps_info: + return ps_info["status"] == "Running" + return self.info["State"] == "Running" @property def paused(self): """Return True if pod is paused now.""" - if 'status' in self.info['State']: - return self.info['State']['status'] == 'Paused' - return self.info['State'] == 'Paused' + if "status" in self.info["State"]: + return self.info["State"]["status"] == "Paused" + return self.info["State"] == "Paused" @property def stopped(self): """Return True if pod exists and is not running now.""" if not self.exists: return False - if 'status' in self.info['State']: - return not (self.info['State']['status'] == 'Running') - return not (self.info['State'] == 'Running') + if "status" in self.info["State"]: + return not (self.info["State"]["status"] == "Running") + return not (self.info["State"] == "Running") def get_info(self): """Inspect pod and gather info about it.""" # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'pod', b'inspect', self.name]) + [self.module_params["executable"], b"pod", b"inspect", self.name] + ) if rc == 0: info = json.loads(out) # from podman 5 onwards, this is a list of dicts, @@ -741,30 +789,43 @@ class PodmanPod: """Inspect pod process and gather info about it.""" # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'pod', b'ps', b'--format', b'json', b'--filter', 'name=' + self.name]) + [ + self.module_params["executable"], + b"pod", + b"ps", + b"--format", + b"json", + b"--filter", + "name=" + self.name, + ] + ) return json.loads(out)[0] if rc == 0 else {} def get_infra_info(self): """Inspect pod and gather info about it.""" if not self.info: return {} - if 'InfraContainerID' in self.info: - infra_container_id = self.info['InfraContainerID'] - elif 'State' in self.info and 'infraContainerID' in self.info['State']: - infra_container_id = self.info['State']['infraContainerID'] + if "InfraContainerID" in self.info: + infra_container_id = self.info["InfraContainerID"] + elif "State" in self.info and "infraContainerID" in self.info["State"]: + infra_container_id = self.info["State"]["infraContainerID"] else: return {} # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'inspect', infra_container_id]) + [self.module_params["executable"], b"inspect", infra_container_id] + ) return json.loads(out)[0] if rc == 0 else {} def _get_podman_version(self): # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module_params['executable'], b'--version']) + [self.module_params["executable"], b"--version"] + ) if rc != 0 or not out or "version" not in out: - self.module.fail_json(msg="%s run failed!" % self.module_params['executable']) + self.module.fail_json( + msg="%s run failed!" % self.module_params["executable"] + ) return out.split("version")[1].strip() def _perform_action(self, action): @@ -774,41 +835,45 @@ class PodmanPod: action {str} -- action to perform - start, create, stop, pause unpause, delete, restart, kill """ - b_command = PodmanPodModuleParams(action, - self.module_params, - self.version, - self.module, - ).construct_command_from_params() - full_cmd = " ".join([self.module_params['executable'], 'pod'] - + [to_native(i) for i in b_command]) + b_command = PodmanPodModuleParams( + action, + self.module_params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join( + [self.module_params["executable"], "pod"] + + [to_native(i) for i in b_command] + ) self.module.log("PODMAN-POD-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'pod'] + b_command, - expand_user_and_vars=False) + [self.module_params["executable"], b"pod"] + b_command, + expand_user_and_vars=False, + ) self.stdout = out self.stderr = err if rc != 0: self.module.fail_json( - msg="Can't %s pod %s" % (action, self.name), - stdout=out, stderr=err) + msg="Can't %s pod %s" % (action, self.name), stdout=out, stderr=err + ) def delete(self): """Delete the pod.""" - self._perform_action('delete') + self._perform_action("delete") def stop(self): """Stop the pod.""" - self._perform_action('stop') + self._perform_action("stop") def start(self): """Start the pod.""" - self._perform_action('start') + self._perform_action("start") def create(self): """Create the pod.""" - self._perform_action('create') + self._perform_action("create") def recreate(self): """Recreate the pod.""" @@ -817,19 +882,19 @@ class PodmanPod: def restart(self): """Restart the pod.""" - self._perform_action('restart') + self._perform_action("restart") def kill(self): """Kill the pod.""" - self._perform_action('kill') + self._perform_action("kill") def pause(self): """Pause the pod.""" - self._perform_action('pause') + self._perform_action("pause") def unpause(self): """Unpause the pod.""" - self._perform_action('unpause') + self._perform_action("unpause") class PodmanPodManager: @@ -848,16 +913,16 @@ class PodmanPodManager: self.module = module self.module_params = params self.results = { - 'changed': False, - 'actions': [], - 'pod': {}, + "changed": False, + "actions": [], + "pod": {}, } - self.name = self.module_params['name'] - self.executable = \ - self.module.get_bin_path(self.module_params['executable'], - required=True) - self.state = self.module_params['state'] - self.recreate = self.module_params['recreate'] + self.name = self.module_params["name"] + self.executable = self.module.get_bin_path( + self.module_params["executable"], required=True + ) + self.state = self.module_params["state"] + self.recreate = self.module_params["recreate"] self.pod = PodmanPod(self.module, self.name, self.module_params) def update_pod_result(self, changed=True): @@ -871,42 +936,42 @@ class PodmanPodManager: if isinstance(facts, list): facts = facts[0] out, err = self.pod.stdout, self.pod.stderr - self.results.update({'changed': changed, 'pod': facts, - 'podman_actions': self.pod.actions}, - stdout=out, stderr=err) - if self.pod.diff: - self.results.update({'diff': self.pod.diff}) - if self.module.params['debug'] or self.module_params['debug']: - self.results.update({'podman_version': self.pod.version}) - sysd = generate_systemd(self.module, - self.module_params, - self.name, - self.pod.version) - self.results['changed'] = changed or sysd['changed'] self.results.update( - {'podman_systemd': sysd['systemd']}) - if sysd['diff']: - if 'diff' not in self.results: - self.results.update({'diff': sysd['diff']}) + {"changed": changed, "pod": facts, "podman_actions": self.pod.actions}, + stdout=out, + stderr=err, + ) + if self.pod.diff: + self.results.update({"diff": self.pod.diff}) + if self.module.params["debug"] or self.module_params["debug"]: + self.results.update({"podman_version": self.pod.version}) + sysd = generate_systemd( + self.module, self.module_params, self.name, self.pod.version + ) + self.results["changed"] = changed or sysd["changed"] + self.results.update({"podman_systemd": sysd["systemd"]}) + if sysd["diff"]: + if "diff" not in self.results: + self.results.update({"diff": sysd["diff"]}) else: - self.results['diff']['before'] += sysd['diff']['before'] - self.results['diff']['after'] += sysd['diff']['after'] + self.results["diff"]["before"] += sysd["diff"]["before"] + self.results["diff"]["after"] += sysd["diff"]["after"] quadlet = PodQuadlet(self.module_params) quadlet_content = quadlet.create_quadlet_content() - self.results.update({'podman_quadlet': quadlet_content}) + self.results.update({"podman_quadlet": quadlet_content}) def execute(self): """Execute the desired action according to map of actions & states.""" states_map = { - 'created': self.make_created, - 'started': self.make_started, - 'stopped': self.make_stopped, - 'restarted': self.make_restarted, - 'absent': self.make_absent, - 'killed': self.make_killed, - 'paused': self.make_paused, - 'unpaused': self.make_unpaused, - 'quadlet': self.make_quadlet, + "created": self.make_created, + "started": self.make_started, + "stopped": self.make_stopped, + "restarted": self.make_restarted, + "absent": self.make_absent, + "killed": self.make_killed, + "paused": self.make_paused, + "unpaused": self.make_unpaused, + "quadlet": self.make_quadlet, } process_action = states_map[self.state] process_action() @@ -918,11 +983,11 @@ class PodmanPodManager: if self.pod.exists: if self.pod.different or self.recreate: self.pod.recreate() - self.results['actions'].append('recreated %s' % self.pod.name) + self.results["actions"].append("recreated %s" % self.pod.name) changed = True elif not self.pod.exists: self.pod.create() - self.results['actions'].append('created %s' % self.pod.name) + self.results["actions"].append("created %s" % self.pod.name) changed = True return changed @@ -938,7 +1003,7 @@ class PodmanPodManager: """Run actions if desired state is 'killed'.""" self._create_or_recreate_pod() self.pod.kill() - self.results['actions'].append('killed %s' % self.pod.name) + self.results["actions"].append("killed %s" % self.pod.name) self.update_pod_result() def make_paused(self): @@ -948,7 +1013,7 @@ class PodmanPodManager: self.update_pod_result(changed=changed) return self.pod.pause() - self.results['actions'].append('paused %s' % self.pod.name) + self.results["actions"].append("paused %s" % self.pod.name) self.update_pod_result() def make_unpaused(self): @@ -958,7 +1023,7 @@ class PodmanPodManager: self.update_pod_result(changed=changed) return self.pod.unpause() - self.results['actions'].append('unpaused %s' % self.pod.name) + self.results["actions"].append("unpaused %s" % self.pod.name) self.update_pod_result() def make_started(self): @@ -970,7 +1035,7 @@ class PodmanPodManager: # self.pod.unpause() TODO(sshnaidm): to unpause if state == started? self.pod.start() - self.results['actions'].append('started %s' % self.pod.name) + self.results["actions"].append("started %s" % self.pod.name) self.update_pod_result() def make_stopped(self): @@ -979,7 +1044,7 @@ class PodmanPodManager: self.module.fail_json("Pod %s doesn't exist!" % self.pod.name) if self.pod.running: self.pod.stop() - self.results['actions'].append('stopped %s' % self.pod.name) + self.results["actions"].append("stopped %s" % self.pod.name) self.update_pod_result() elif self.pod.stopped: self.update_pod_result(changed=False) @@ -988,8 +1053,8 @@ class PodmanPodManager: """Run actions if desired state is 'restarted'.""" if self.pod.exists: self.pod.restart() - self.results['actions'].append('restarted %s' % self.pod.name) - self.results.update({'changed': True}) + self.results["actions"].append("restarted %s" % self.pod.name) + self.results.update({"changed": True}) self.update_pod_result() else: self.module.fail_json("Pod %s doesn't exist!" % self.pod.name) @@ -997,17 +1062,13 @@ class PodmanPodManager: def make_absent(self): """Run actions if desired state is 'absent'.""" if not self.pod.exists: - self.results.update({'changed': False}) + self.results.update({"changed": False}) elif self.pod.exists: - delete_systemd(self.module, - self.module_params, - self.name, - self.pod.version) + delete_systemd(self.module, self.module_params, self.name, self.pod.version) self.pod.delete() - self.results['actions'].append('deleted %s' % self.pod.name) - self.results.update({'changed': True}) - self.results.update({'pod': {}, - 'podman_actions': self.pod.actions}) + self.results["actions"].append("deleted %s" % self.pod.name) + self.results.update({"changed": True}) + self.results.update({"pod": {}, "podman_actions": self.pod.actions}) def make_quadlet(self): results_update = create_quadlet_state(self.module, "pod") diff --git a/plugins/module_utils/podman/quadlet.py b/plugins/module_utils/podman/quadlet.py index 1607e29..dd4285a 100644 --- a/plugins/module_utils/podman/quadlet.py +++ b/plugins/module_utils/podman/quadlet.py @@ -2,12 +2,15 @@ # 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 import os import shlex -from ansible_collections.containers.podman.plugins.module_utils.podman.common import compare_systemd_file_content +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + compare_systemd_file_content, +) QUADLET_ROOT_PATH = "/etc/containers/systemd/" QUADLET_NON_ROOT_PATH = "~/.config/containers/systemd/" @@ -52,102 +55,107 @@ class Quadlet: Construct the quadlet content as a string. """ custom_user_options = self.custom_params.get("quadlet_options") - custom_text = "\n" + "\n".join(custom_user_options) if custom_user_options else "" - return f"[{self.section}]\n" + "\n".join( - f"{key}={value}" for key, value in self.dict_params - ) + custom_text + "\n" + custom_text = ( + "\n" + "\n".join(custom_user_options) if custom_user_options else "" + ) + return ( + f"[{self.section}]\n" + + "\n".join(f"{key}={value}" for key, value in self.dict_params) + + custom_text + + "\n" + ) def write_to_file(self, path: str): """ Write the quadlet content to a file at the specified path. """ content = self.create_quadlet_content() - with open(path, 'w') as file: + with open(path, "w") as file: file.write(content) class ContainerQuadlet(Quadlet): param_map = { - 'cap_add': 'AddCapability', - 'device': 'AddDevice', - 'annotation': 'Annotation', - 'name': 'ContainerName', + "cap_add": "AddCapability", + "device": "AddDevice", + "annotation": "Annotation", + "name": "ContainerName", # the following are not implemented yet in Podman module - 'AutoUpdate': 'AutoUpdate', - 'ContainersConfModule': 'ContainersConfModule', + "AutoUpdate": "AutoUpdate", + "ContainersConfModule": "ContainersConfModule", # end of not implemented yet - 'dns': 'DNS', - 'dns_option': 'DNSOption', - 'dns_search': 'DNSSearch', - 'cap_drop': 'DropCapability', - 'cgroups': 'CgroupsMode', - 'entrypoint': 'Entrypoint', - 'env': 'Environment', - 'env_file': 'EnvironmentFile', - 'env_host': 'EnvironmentHost', - 'etc_hosts': 'AddHost', - 'command': 'Exec', - 'expose': 'ExposeHostPort', - 'gidmap': 'GIDMap', - 'global_args': 'GlobalArgs', - 'group': 'Group', # Does not exist in module parameters - 'group_add': 'GroupAdd', - 'healthcheck': 'HealthCmd', - 'healthcheck_interval': 'HealthInterval', - 'healthcheck_failure_action': 'HealthOnFailure', - 'healthcheck_retries': 'HealthRetries', - 'healthcheck_start_period': 'HealthStartPeriod', - 'healthcheck_timeout': 'HealthTimeout', - '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', - 'ip6': 'IP6', - 'label': 'Label', - 'log_driver': 'LogDriver', - 'log_opt': 'LogOpt', + "dns": "DNS", + "dns_option": "DNSOption", + "dns_search": "DNSSearch", + "cap_drop": "DropCapability", + "cgroups": "CgroupsMode", + "entrypoint": "Entrypoint", + "env": "Environment", + "env_file": "EnvironmentFile", + "env_host": "EnvironmentHost", + "etc_hosts": "AddHost", + "command": "Exec", + "expose": "ExposeHostPort", + "gidmap": "GIDMap", + "global_args": "GlobalArgs", + "group": "Group", # Does not exist in module parameters + "group_add": "GroupAdd", + "healthcheck": "HealthCmd", + "healthcheck_interval": "HealthInterval", + "healthcheck_failure_action": "HealthOnFailure", + "healthcheck_retries": "HealthRetries", + "healthcheck_start_period": "HealthStartPeriod", + "healthcheck_timeout": "HealthTimeout", + "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", + "ip6": "IP6", + "label": "Label", + "log_driver": "LogDriver", + "log_opt": "LogOpt", "Mask": "Mask", # add it in security_opt - 'mount': 'Mount', - 'network': 'Network', - 'network_aliases': 'NetworkAlias', - 'no_new_privileges': 'NoNewPrivileges', - 'sdnotify': 'Notify', - 'pids_limit': 'PidsLimit', - 'pod': 'Pod', - 'publish': 'PublishPort', + "mount": "Mount", + "network": "Network", + "network_aliases": "NetworkAlias", + "no_new_privileges": "NoNewPrivileges", + "sdnotify": "Notify", + "pids_limit": "PidsLimit", + "pod": "Pod", + "publish": "PublishPort", "pull": "Pull", - 'read_only': 'ReadOnly', - 'read_only_tmpfs': 'ReadOnlyTmpfs', - 'rootfs': 'Rootfs', - 'init': 'RunInit', - 'SeccompProfile': 'SeccompProfile', - 'secrets': 'Secret', + "read_only": "ReadOnly", + "read_only_tmpfs": "ReadOnlyTmpfs", + "rootfs": "Rootfs", + "init": "RunInit", + "SeccompProfile": "SeccompProfile", + "secrets": "Secret", # All these are in security_opt - 'SecurityLabelDisable': 'SecurityLabelDisable', - 'SecurityLabelFileType': 'SecurityLabelFileType', - 'SecurityLabelLevel': 'SecurityLabelLevel', - 'SecurityLabelNested': 'SecurityLabelNested', - 'SecurityLabelType': 'SecurityLabelType', - 'shm_size': 'ShmSize', - 'stop_signal': 'StopSignal', - 'stop_timeout': 'StopTimeout', - 'subgidname': 'SubGIDMap', - 'subuidname': 'SubUIDMap', - 'sysctl': 'Sysctl', - 'timezone': 'Timezone', - 'tmpfs': 'Tmpfs', - 'uidmap': 'UIDMap', - 'ulimit': 'Ulimit', - 'Unmask': 'Unmask', # --security-opt unmask=ALL - 'user': 'User', - 'userns': 'UserNS', - 'volume': 'Volume', - 'workdir': 'WorkingDir', - 'podman_args': 'PodmanArgs', + "SecurityLabelDisable": "SecurityLabelDisable", + "SecurityLabelFileType": "SecurityLabelFileType", + "SecurityLabelLevel": "SecurityLabelLevel", + "SecurityLabelNested": "SecurityLabelNested", + "SecurityLabelType": "SecurityLabelType", + "shm_size": "ShmSize", + "stop_signal": "StopSignal", + "stop_timeout": "StopTimeout", + "subgidname": "SubGIDMap", + "subuidname": "SubUIDMap", + "sysctl": "Sysctl", + "timezone": "Timezone", + "tmpfs": "Tmpfs", + "uidmap": "UIDMap", + "ulimit": "Ulimit", + "Unmask": "Unmask", # --security-opt unmask=ALL + "user": "User", + "userns": "UserNS", + "volume": "Volume", + "workdir": "WorkingDir", + "podman_args": "PodmanArgs", } def __init__(self, params: dict): @@ -159,27 +167,36 @@ class ContainerQuadlet(Quadlet): """ # Work on params in params_map and convert them to a right form if params["annotation"]: - params['annotation'] = ["%s=%s" % - (k, v) for k, v in params['annotation'].items()] + params["annotation"] = [ + "%s=%s" % (k, v) for k, v in params["annotation"].items() + ] if params["cap_add"]: params["cap_add"] = " ".join(params["cap_add"]) if params["cap_drop"]: params["cap_drop"] = " ".join(params["cap_drop"]) if params["command"]: - params["command"] = (" ".join([str(j) for j in params["command"]]) - if isinstance(params["command"], list) - else params["command"]) + params["command"] = ( + " ".join([str(j) for j in params["command"]]) + if isinstance(params["command"], list) + else params["command"] + ) if params["label"]: - params["label"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["label"].items()] + params["label"] = [ + shlex.quote("%s=%s" % (k, v)) for k, v in params["label"].items() + ] if params["env"]: - params["env"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["env"].items()] + params["env"] = [ + shlex.quote("%s=%s" % (k, v)) for k, v in params["env"].items() + ] if params["rootfs"]: params["rootfs"] = params["image"] params["image"] = None if params["sysctl"]: params["sysctl"] = ["%s=%s" % (k, v) for k, v in params["sysctl"].items()] if params["tmpfs"]: - params["tmpfs"] = ["%s:%s" % (k, v) if v else k for k, v in params["tmpfs"].items()] + params["tmpfs"] = [ + "%s:%s" % (k, v) if v else k for k, v in params["tmpfs"].items() + ] # Work on params which are not in the param_map but can be calculated params["global_args"] = [] @@ -208,8 +225,14 @@ class ContainerQuadlet(Quadlet): if params["blkio_weight"]: params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}") if params["blkio_weight_device"]: - params["podman_args"].append(" ".join([ - f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()])) + params["podman_args"].append( + " ".join( + [ + 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"]: @@ -242,7 +265,9 @@ class ContainerQuadlet(Quadlet): 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']}") + 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}") @@ -256,7 +281,9 @@ class ContainerQuadlet(Quadlet): for i in params["device_write_iops"]: params["podman_args"].append(f"--device-write-iops {i}") if params["etc_hosts"]: - params['etc_hosts'] = ["%s:%s" % (k, v) for k, v in params['etc_hosts'].items()] + params["etc_hosts"] = [ + "%s:%s" % (k, v) for k, v in params["etc_hosts"].items() + ] if params["env_merge"]: for k, v in params["env_merge"].items(): params["podman_args"].append(f"--env {k}={v}") @@ -287,24 +314,32 @@ class ContainerQuadlet(Quadlet): params["podman_args"].append(f"--label-file {params['label_file']}") if params["log_opt"]: params["log_opt"] = [ - "%s=%s" % (k.replace('max_size', 'max-size'), v) - for k, v in params['log_opt'].items() if v is not None] + "%s=%s" % (k.replace("max_size", "max-size"), v) + for k, v in params["log_opt"].items() + if v is not None + ] if params["mac_address"]: params["podman_args"].append(f"--mac-address {params['mac_address']}") if params["memory"]: params["podman_args"].append(f"--memory {params['memory']}") if params["memory_reservation"]: - params["podman_args"].append(f"--memory-reservation {params['memory_reservation']}") + params["podman_args"].append( + f"--memory-reservation {params['memory_reservation']}" + ) if params["memory_swap"]: params["podman_args"].append(f"--memory-swap {params['memory_swap']}") if params["memory_swappiness"]: - params["podman_args"].append(f"--memory-swappiness {params['memory_swappiness']}") + params["podman_args"].append( + f"--memory-swappiness {params['memory_swappiness']}" + ) 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']}") + 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"]: @@ -350,7 +385,9 @@ class ContainerQuadlet(Quadlet): 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']}") + 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["systemd"]: @@ -358,7 +395,9 @@ class ContainerQuadlet(Quadlet): 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()}") + params["podman_args"].append( + f"--tls-verify={str(params['tls_verify']).lower()}" + ) if params["tty"]: params["podman_args"].append("--tty") if params["umask"]: @@ -384,17 +423,17 @@ class ContainerQuadlet(Quadlet): class NetworkQuadlet(Quadlet): param_map = { - 'name': 'NetworkName', - 'internal': 'Internal', - 'driver': 'Driver', - 'gateway': 'Gateway', - 'disable_dns': 'DisableDNS', - 'subnet': 'Subnet', - 'ip_range': 'IPRange', - 'ipv6': 'IPv6', + "name": "NetworkName", + "internal": "Internal", + "driver": "Driver", + "gateway": "Gateway", + "disable_dns": "DisableDNS", + "subnet": "Subnet", + "ip_range": "IPRange", + "ipv6": "IPv6", "opt": "Options", # Add more parameter mappings specific to networks - 'ContainersConfModule': 'ContainersConfModule', + "ContainersConfModule": "ContainersConfModule", "dns": "DNS", "ipam_driver": "IPAMDriver", "Label": "Label", @@ -424,11 +463,11 @@ class NetworkQuadlet(Quadlet): # This is a inherited class that represents a Quadlet file for the Podman pod class PodQuadlet(Quadlet): param_map = { - 'name': 'PodName', + "name": "PodName", "network": "Network", "publish": "PublishPort", "volume": "Volume", - 'ContainersConfModule': 'ContainersConfModule', + "ContainersConfModule": "ContainersConfModule", "global_args": "GlobalArgs", "podman_args": "PodmanArgs", } @@ -445,15 +484,21 @@ class PodQuadlet(Quadlet): params["podman_args"] = [] if params["add_host"]: - for host in params['add_host']: + for host in params["add_host"]: params["podman_args"].append(f"--add-host {host}") if params["cgroup_parent"]: params["podman_args"].append(f"--cgroup-parent {params['cgroup_parent']}") if params["blkio_weight"]: params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}") if params["blkio_weight_device"]: - params["podman_args"].append(" ".join([ - f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()])) + params["podman_args"].append( + " ".join( + [ + f"--blkio-weight-device {':'.join(blkio)}" + for blkio in params["blkio_weight_device"].items() + ] + ) + ) if params["cpuset_cpus"]: params["podman_args"].append(f"--cpuset-cpus {params['cpuset_cpus']}") if params["cpuset_mems"]: @@ -494,7 +539,9 @@ class PodQuadlet(Quadlet): if params["infra_command"]: params["podman_args"].append(f"--infra-command {params['infra_command']}") if params["infra_conmon_pidfile"]: - params["podman_args"].append(f"--infra-conmon-pidfile {params['infra_conmon_pidfile']}") + params["podman_args"].append( + f"--infra-conmon-pidfile {params['infra_conmon_pidfile']}" + ) if params["infra_image"]: params["podman_args"].append(f"--infra-image {params['infra_image']}") if params["infra_name"]: @@ -528,11 +575,15 @@ class PodQuadlet(Quadlet): 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()}") + 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']}") + 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"]: @@ -559,13 +610,13 @@ class PodQuadlet(Quadlet): # This is a inherited class that represents a Quadlet file for the Podman volume class VolumeQuadlet(Quadlet): param_map = { - 'name': 'VolumeName', - 'driver': 'Driver', - 'label': 'Label', + "name": "VolumeName", + "driver": "Driver", + "label": "Label", # 'opt': 'Options', - 'ContainersConfModule': 'ContainersConfModule', - 'global_args': 'GlobalArgs', - 'podman_args': 'PodmanArgs', + "ContainersConfModule": "ContainersConfModule", + "global_args": "GlobalArgs", + "podman_args": "PodmanArgs", } def __init__(self, params: dict): @@ -593,19 +644,19 @@ class VolumeQuadlet(Quadlet): # This is a inherited class that represents a Quadlet file for the Podman kube class KubeQuadlet(Quadlet): param_map = { - 'configmap': 'ConfigMap', - 'log_driver': 'LogDriver', - 'network': 'Network', - 'kube_file': 'Yaml', - 'userns': 'UserNS', - 'AutoUpdate': 'AutoUpdate', - 'ExitCodePropagation': 'ExitCodePropagation', - 'KubeDownForce': 'KubeDownForce', - 'PublishPort': 'PublishPort', - 'SetWorkingDirectory': 'SetWorkingDirectory', - 'ContainersConfModule': 'ContainersConfModule', - 'global_args': 'GlobalArgs', - 'podman_args': 'PodmanArgs', + "configmap": "ConfigMap", + "log_driver": "LogDriver", + "network": "Network", + "kube_file": "Yaml", + "userns": "UserNS", + "AutoUpdate": "AutoUpdate", + "ExitCodePropagation": "ExitCodePropagation", + "KubeDownForce": "KubeDownForce", + "PublishPort": "PublishPort", + "SetWorkingDirectory": "SetWorkingDirectory", + "ContainersConfModule": "ContainersConfModule", + "global_args": "GlobalArgs", + "podman_args": "PodmanArgs", } def __init__(self, params: dict): @@ -628,20 +679,20 @@ class KubeQuadlet(Quadlet): # This is a inherited class that represents a Quadlet file for the Podman image class ImageQuadlet(Quadlet): param_map = { - 'AllTags': 'AllTags', - 'arch': 'Arch', - 'authfile': 'AuthFile', - 'ca_cert_dir': 'CertDir', - 'creds': 'Creds', - 'DecryptionKey': 'DecryptionKey', - 'name': 'Image', - 'ImageTag': 'ImageTag', - 'OS': 'OS', - 'validate_certs': 'TLSVerify', - 'Variant': 'Variant', - 'ContainersConfModule': 'ContainersConfModule', - 'global_args': 'GlobalArgs', - 'podman_args': 'PodmanArgs', + "AllTags": "AllTags", + "arch": "Arch", + "authfile": "AuthFile", + "ca_cert_dir": "CertDir", + "creds": "Creds", + "DecryptionKey": "DecryptionKey", + "name": "Image", + "ImageTag": "ImageTag", + "OS": "OS", + "validate_certs": "TLSVerify", + "Variant": "Variant", + "ContainersConfModule": "ContainersConfModule", + "global_args": "GlobalArgs", + "podman_args": "PodmanArgs", } def __init__(self, params: dict): @@ -664,20 +715,20 @@ class ImageQuadlet(Quadlet): def check_quadlet_directory(module, quadlet_dir): - '''Check if the directory exists and is writable. If not, fail the module.''' + """Check if the directory exists and is writable. If not, fail the module.""" if not os.path.exists(quadlet_dir): try: os.makedirs(quadlet_dir) except Exception as e: - module.fail_json( - msg="Directory for quadlet_file can't be created: %s" % e) + module.fail_json(msg="Directory for quadlet_file can't be created: %s" % e) if not os.access(quadlet_dir, os.W_OK): module.fail_json( - msg="Directory for quadlet_file is not writable: %s" % quadlet_dir) + msg="Directory for quadlet_file is not writable: %s" % quadlet_dir + ) def create_quadlet_state(module, issuer): - '''Create a quadlet file for the specified issuer.''' + """Create a quadlet file for the specified issuer.""" class_map = { "container": ContainerQuadlet, "network": NetworkQuadlet, @@ -688,20 +739,22 @@ def create_quadlet_state(module, issuer): } # Let's detect which user is running user = "root" if os.geteuid() == 0 else "user" - quadlet_dir = module.params.get('quadlet_dir') + quadlet_dir = module.params.get("quadlet_dir") if not quadlet_dir: if user == "root": quadlet_dir = QUADLET_ROOT_PATH else: quadlet_dir = os.path.expanduser(QUADLET_NON_ROOT_PATH) # Create a filename based on the issuer - if not module.params.get('name') and not module.params.get('quadlet_filename'): - module.fail_json(msg=f"Filename for {issuer} is required for creating a quadlet file.") + if not module.params.get("name") and not module.params.get("quadlet_filename"): + module.fail_json( + msg=f"Filename for {issuer} is required for creating a quadlet file." + ) if issuer == "image": - name = module.params['name'].split("/")[-1].split(":")[0] + name = module.params["name"].split("/")[-1].split(":")[0] else: - name = module.params.get('name') - quad_file_name = module.params['quadlet_filename'] + name = module.params.get("name") + quad_file_name = module.params["quadlet_filename"] if quad_file_name and not quad_file_name.endswith(f".{issuer}"): quad_file_name = f"{quad_file_name}.{issuer}" filename = quad_file_name or f"{name}.{issuer}" @@ -710,10 +763,10 @@ def create_quadlet_state(module, issuer): if not module.check_mode: check_quadlet_directory(module, quadlet_dir) # Specify file permissions - mode = module.params.get('quadlet_file_mode', None) + mode = module.params.get("quadlet_file_mode", None) if mode is None and not os.path.exists(quadlet_file_path): # default mode for new quadlet file only - mode = '0640' + mode = "0640" # Check if file already exists and if it's different quadlet = class_map[issuer](module.params) quadlet_content = quadlet.create_quadlet_content() @@ -724,22 +777,31 @@ def create_quadlet_state(module, issuer): if mode is not None: module.set_mode_if_different(quadlet_file_path, mode, False) results_update = { - 'changed': True, + "changed": True, "diff": { - "before": "\n".join(file_diff[0]) if isinstance(file_diff[0], list) else file_diff[0] + "\n", - "after": "\n".join(file_diff[1]) if isinstance(file_diff[1], list) else file_diff[1] + "\n", - }} + "before": ( + "\n".join(file_diff[0]) + if isinstance(file_diff[0], list) + else file_diff[0] + "\n" + ), + "after": ( + "\n".join(file_diff[1]) + if isinstance(file_diff[1], list) + else file_diff[1] + "\n" + ), + }, + } else: # adjust file permissions diff = {} - if mode is not None and module.set_mode_if_different(quadlet_file_path, mode, False, diff): - results_update = { - 'changed': True, - 'diff': diff - } + if mode is not None and module.set_mode_if_different( + quadlet_file_path, mode, False, diff + ): + results_update = {"changed": True, "diff": diff} else: results_update = {} return results_update + # Check with following command: # QUADLET_UNIT_DIRS= /usr/lib/systemd/system-generators/podman-system-generator {--user} --dryrun diff --git a/plugins/modules/podman_container.py b/plugins/modules/podman_container.py index aebf3e9..4029952 100644 --- a/plugins/modules/podman_container.py +++ b/plugins/modules/podman_container.py @@ -1522,28 +1522,31 @@ container: from ansible.module_utils.basic import AnsibleModule # noqa: F402 from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402 -from ..module_utils.podman.podman_container_lib import ARGUMENTS_SPEC_CONTAINER # noqa: F402 +from ..module_utils.podman.podman_container_lib import ( + ARGUMENTS_SPEC_CONTAINER, +) # noqa: F402 def main(): module = AnsibleModule( argument_spec=ARGUMENTS_SPEC_CONTAINER, - mutually_exclusive=( - ['no_hosts', 'etc_hosts'], - ), + mutually_exclusive=(["no_hosts", "etc_hosts"],), supports_check_mode=True, ) # work on input vars - if (module.params['state'] in ['present', 'created'] - and not module.params['force_restart'] - and not module.params['image']): - module.fail_json(msg="State '%s' required image to be configured!" % - module.params['state']) + if ( + module.params["state"] in ["present", "created"] + and not module.params["force_restart"] + and not module.params["image"] + ): + module.fail_json( + msg="State '%s' required image to be configured!" % module.params["state"] + ) results = PodmanManager(module, module.params).execute() module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_container_copy.py b/plugins/modules/podman_container_copy.py index 9163644..6dc7eb7 100644 --- a/plugins/modules/podman_container_copy.py +++ b/plugins/modules/podman_container_copy.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_container_copy author: - Alessandro Rossi (@kubealex) @@ -54,7 +55,7 @@ options: required: False default: False type: bool -''' +""" EXAMPLES = r""" - name: Copy file "test.yml" on the host to the "apache" container's root folder @@ -73,22 +74,26 @@ EXAMPLES = r""" from ansible.module_utils.basic import AnsibleModule -def copy_file(module, executable, src, dest, container, from_container, archive, overwrite): +def copy_file( + module, executable, src, dest, container, from_container, archive, overwrite +): if from_container: - command = [executable, 'cp', '{0}:{1}'.format(container, src), dest] + command = [executable, "cp", "{0}:{1}".format(container, src), dest] else: - command = [executable, 'cp', src, '{0}:{1}'.format(container, dest)] + command = [executable, "cp", src, "{0}:{1}".format(container, dest)] if not archive: - command.append('--archive=False') + command.append("--archive=False") if overwrite: - command.append('--overwrite') + command.append("--overwrite") rc, out, err = module.run_command(command) if rc != 0: - module.fail_json(msg='Unable to copy file to/from container - {out}'.format(out=err)) + module.fail_json( + msg="Unable to copy file to/from container - {out}".format(out=err) + ) else: changed = True return changed, out, err @@ -97,35 +102,35 @@ def copy_file(module, executable, src, dest, container, from_container, archive, def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - src=dict(type='str', required=True), - dest=dict(type='str', required=True), - container=dict(type='str', required=True), - from_container=dict(type='bool', required=False, default=False), - archive=dict(type='bool', required=False, default=True), - overwrite=dict(type='bool', required=False, default=False) + executable=dict(type="str", default="podman"), + src=dict(type="str", required=True), + dest=dict(type="str", required=True), + container=dict(type="str", required=True), + from_container=dict(type="bool", required=False, default=False), + archive=dict(type="bool", required=False, default=True), + overwrite=dict(type="bool", required=False, default=False), ), supports_check_mode=False, ) - executable = module.params['executable'] - src = module.params['src'] - dest = module.params['dest'] - container = module.params['container'] - from_container = module.params['from_container'] - archive = module.params['archive'] - overwrite = module.params['overwrite'] + executable = module.params["executable"] + src = module.params["src"] + dest = module.params["dest"] + container = module.params["container"] + from_container = module.params["from_container"] + archive = module.params["archive"] + overwrite = module.params["overwrite"] executable = module.get_bin_path(executable, required=True) - changed, out, err = copy_file(module, executable, src, dest, container, from_container, archive, overwrite) - - results = dict( - changed=changed + changed, out, err = copy_file( + module, executable, src, dest, container, from_container, archive, overwrite ) + results = dict(changed=changed) + module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_container_exec.py b/plugins/modules/podman_container_exec.py index 1827b0c..a00d655 100644 --- a/plugins/modules/podman_container_exec.py +++ b/plugins/modules/podman_container_exec.py @@ -4,9 +4,10 @@ # Copyright (c) 2023, Takuya Nishimura <@nishipy> # 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''' +DOCUMENTATION = r""" module: podman_container_exec author: - Takuya Nishimura (@nishipy) @@ -68,9 +69,9 @@ requirements: - podman notes: - See L(the Podman documentation,https://docs.podman.io/en/latest/markdown/podman-exec.1.html) for details of podman-exec(1). -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" - name: Execute a command with workdir containers.podman.podman_container_exec: name: ubi8 @@ -93,9 +94,9 @@ EXAMPLES = r''' name: detach_container command: "cat redhat-release" detach: true -''' +""" -RETURN = r''' +RETURN = r""" stdout: type: str returned: success @@ -118,65 +119,65 @@ exec_id: sample: f99002e34c1087fd1aa08d5027e455bf7c2d6b74f019069acf6462a96ddf2a47 description: - The ID of the exec session. -''' +""" import shlex from ansible.module_utils.six import string_types from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule -from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + run_podman_command, +) def run_container_exec(module: AnsibleModule) -> dict: - ''' + """ Execute podman-container-exec for the given options - ''' - exec_with_args = ['container', 'exec'] + """ + exec_with_args = ["container", "exec"] # podman_container_exec always returns changed=true changed = True exec_options = [] - name = module.params['name'] - argv = module.params['argv'] - command = module.params['command'] - detach = module.params['detach'] - env = module.params['env'] - privileged = module.params['privileged'] - tty = module.params['tty'] - user = module.params['user'] - workdir = module.params['workdir'] - executable = module.params['executable'] + name = module.params["name"] + argv = module.params["argv"] + command = module.params["command"] + detach = module.params["detach"] + env = module.params["env"] + privileged = module.params["privileged"] + tty = module.params["tty"] + user = module.params["user"] + workdir = module.params["workdir"] + executable = module.params["executable"] if command is not None: argv = shlex.split(command) if detach: - exec_options.append('--detach') + exec_options.append("--detach") if env is not None: for key, value in env.items(): if not isinstance(value, string_types): module.fail_json( - msg="Specify string value %s on the env field" % (value)) + msg="Specify string value %s on the env field" % (value) + ) - to_text(value, errors='surrogate_or_strict') - exec_options += ['--env', - '%s=%s' % (key, value)] + to_text(value, errors="surrogate_or_strict") + exec_options += ["--env", "%s=%s" % (key, value)] if privileged: - exec_options.append('--privileged') + exec_options.append("--privileged") if tty: - exec_options.append('--tty') + exec_options.append("--tty") if user is not None: - exec_options += ['--user', - to_text(user, errors='surrogate_or_strict')] + exec_options += ["--user", to_text(user, errors="surrogate_or_strict")] if workdir is not None: - exec_options += ['--workdir', - to_text(workdir, errors='surrogate_or_strict')] + exec_options += ["--workdir", to_text(workdir, errors="surrogate_or_strict")] exec_options.append(name) exec_options.extend(argv) @@ -184,71 +185,72 @@ def run_container_exec(module: AnsibleModule) -> dict: exec_with_args.extend(exec_options) rc, stdout, stderr = run_podman_command( - module=module, executable=executable, args=exec_with_args, ignore_errors=True) + module=module, executable=executable, args=exec_with_args, ignore_errors=True + ) result = { - 'changed': changed, - 'podman_command': exec_options, - 'rc': rc, - 'stdout': stdout, - 'stderr': stderr, + "changed": changed, + "podman_command": exec_options, + "rc": rc, + "stdout": stdout, + "stderr": stderr, } if detach: - result['exec_id'] = stdout.replace('\n', '') + result["exec_id"] = stdout.replace("\n", "") return result def main(): argument_spec = { - 'name': { - 'type': 'str', - 'required': True, + "name": { + "type": "str", + "required": True, }, - 'command': { - 'type': 'str', + "command": { + "type": "str", }, - 'argv': { - 'type': 'list', - 'elements': 'str', + "argv": { + "type": "list", + "elements": "str", }, - 'detach': { - 'type': 'bool', - 'default': False, + "detach": { + "type": "bool", + "default": False, }, - 'executable': { - 'type': 'str', - 'default': 'podman', + "executable": { + "type": "str", + "default": "podman", }, - 'env': { - 'type': 'dict', + "env": { + "type": "dict", }, - 'privileged': { - 'type': 'bool', - 'default': False, + "privileged": { + "type": "bool", + "default": False, }, - 'tty': { - 'type': 'bool', - 'default': False, + "tty": { + "type": "bool", + "default": False, }, - 'user': { - 'type': 'str', + "user": { + "type": "str", }, - 'workdir': { - 'type': 'str', + "workdir": { + "type": "str", }, } module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, - required_one_of=[('argv', 'command')], + required_one_of=[("argv", "command")], ) result = run_container_exec(module) module.exit_json(**result) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_container_info.py b/plugins/modules/podman_container_info.py index 1f498fb..bb2ea40 100644 --- a/plugins/modules/podman_container_info.py +++ b/plugins/modules/podman_container_info.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_container_info author: - Sagi Shnaidman (@podman) @@ -31,7 +32,7 @@ options: machine running C(podman) default: 'podman' type: str -''' +""" EXAMPLES = r""" - name: Gather facts for all containers @@ -338,7 +339,7 @@ def get_containers_facts(module, executable, name): retry = 0 retry_limit = 4 if not name: - all_names = [executable, 'container', 'ls', '-q', '-a'] + all_names = [executable, "container", "ls", "-q", "-a"] rc, out, err = module.run_command(all_names) # This should not fail in regular circumstances, so retry again # https://github.com/containers/podman/issues/10225 @@ -348,12 +349,14 @@ def get_containers_facts(module, executable, name): retry += 1 rc, out, err = module.run_command(all_names) if rc != 0: - module.fail_json(msg="Unable to get list of containers during" - " %s retries" % retry_limit) + module.fail_json( + msg="Unable to get list of containers during" + " %s retries" % retry_limit + ) name = out.split() if not name: return [], out, err - command = [executable, 'container', 'inspect'] + command = [executable, "container", "inspect"] command.extend(name) rc, out, err = module.run_command(command) if rc == 0: @@ -361,7 +364,7 @@ def get_containers_facts(module, executable, name): if json_out is None: return [], out, err return json_out, out, err - if rc != 0 and 'no such ' in err: + if rc != 0 and "no such " in err: if len(name) < 2: return [], out, err return cycle_over(module, executable, name) @@ -382,9 +385,9 @@ def cycle_over(module, executable, name): inspection = [] stderrs = [] for container in name: - command = [executable, 'container', 'inspect', container] + command = [executable, "container", "inspect", container] rc, out, err = module.run_command(command) - if rc != 0 and 'no such ' not in err: + if rc != 0 and "no such " not in err: module.fail_json(msg="Unable to gather info for %s: %s" % (container, err)) if rc == 0 and out: json_out = json.loads(out) @@ -397,25 +400,21 @@ def cycle_over(module, executable, name): def main(): module = AnsibleModule( argument_spec={ - 'executable': {'type': 'str', 'default': 'podman'}, - 'name': {'type': 'list', 'elements': 'str'}, + "executable": {"type": "str", "default": "podman"}, + "name": {"type": "list", "elements": "str"}, }, supports_check_mode=True, ) - name = module.params['name'] - executable = module.get_bin_path(module.params['executable'], required=True) + name = module.params["name"] + executable = module.get_bin_path(module.params["executable"], required=True) # pylint: disable=unused-variable inspect_results, out, err = get_containers_facts(module, executable, name) - results = { - "changed": False, - "containers": inspect_results, - "stderr": err - } + results = {"changed": False, "containers": inspect_results, "stderr": err} module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_containers.py b/plugins/modules/podman_containers.py index 7f418a6..1b7bc8a 100644 --- a/plugins/modules/podman_containers.py +++ b/plugins/modules/podman_containers.py @@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: podman_containers author: @@ -29,9 +29,9 @@ options: - Return additional information which can be helpful for investigations. type: bool default: False -''' +""" -EXAMPLES = ''' +EXAMPLES = """ - name: Run three containers at once podman_containers: containers: @@ -43,7 +43,7 @@ EXAMPLES = ''' - name: test image: python:3.10-alpine command: python -V -''' +""" from ansible.module_utils.basic import AnsibleModule # noqa: F402 from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402 @@ -51,82 +51,81 @@ from ..module_utils.podman.podman_container_lib import set_container_opts # noq def combine(results): - changed = any(i.get('changed', False) for i in results) - failed = any(i.get('failed', False) for i in results) + changed = any(i.get("changed", False) for i in results) + failed = any(i.get("failed", False) for i in results) actions = [] podman_actions = [] containers = [] - podman_version = '' + podman_version = "" diffs = {} - stderr = '' - stdout = '' + stderr = "" + stdout = "" for i in results: - if 'actions' in i and i['actions']: - actions += i['actions'] - if 'podman_actions' in i and i['podman_actions']: - podman_actions += i['podman_actions'] - if 'container' in i and i['container']: - containers.append(i['container']) - if 'podman_version' in i: - podman_version = i['podman_version'] - if 'diff' in i: - diffs[i['container']['Name']] = i['diff'] - if 'stderr' in i: - stderr += i['stderr'] - if 'stdout' in i: - stdout += i['stdout'] + if "actions" in i and i["actions"]: + actions += i["actions"] + if "podman_actions" in i and i["podman_actions"]: + podman_actions += i["podman_actions"] + if "container" in i and i["container"]: + containers.append(i["container"]) + if "podman_version" in i: + podman_version = i["podman_version"] + if "diff" in i: + diffs[i["container"]["Name"]] = i["diff"] + if "stderr" in i: + stderr += i["stderr"] + if "stdout" in i: + stdout += i["stdout"] total = { - 'changed': changed, - 'failed': failed, - 'actions': actions, - 'podman_actions': podman_actions, - 'containers': containers, - 'stdout': stdout, - 'stderr': stderr, + "changed": changed, + "failed": failed, + "actions": actions, + "podman_actions": podman_actions, + "containers": containers, + "stdout": stdout, + "stderr": stderr, } if podman_version: - total['podman_version'] = podman_version + total["podman_version"] = podman_version if diffs: - before = after = '' + before = after = "" for k, v in diffs.items(): - before += "".join([str(k), ": ", str(v['before']), "\n"]) - after += "".join([str(k), ": ", str(v['after']), "\n"]) - total['diff'] = { - 'before': before, - 'after': after - } + before += "".join([str(k), ": ", str(v["before"]), "\n"]) + after += "".join([str(k), ": ", str(v["after"]), "\n"]) + total["diff"] = {"before": before, "after": after} return total def check_input_strict(container): - if container['state'] in ['started', 'present'] and not container['image']: - return "State '%s' required image to be configured!" % container['state'] + if container["state"] in ["started", "present"] and not container["image"]: + return "State '%s' required image to be configured!" % container["state"] def main(): module = AnsibleModule( argument_spec=dict( - containers=dict(type='list', elements='dict', required=True), - debug=dict(type='bool', default=False), + containers=dict(type="list", elements="dict", required=True), + debug=dict(type="bool", default=False), ), supports_check_mode=True, ) # work on input vars results = [] - for container in module.params['containers']: + for container in module.params["containers"]: options_dict = set_container_opts(container) - options_dict['debug'] = module.params['debug'] or options_dict['debug'] + options_dict["debug"] = module.params["debug"] or options_dict["debug"] test_input = check_input_strict(options_dict) if test_input: module.fail_json( - msg="Failed to run container %s because: %s" % (options_dict['name'], test_input)) + msg="Failed to run container %s because: %s" + % (options_dict["name"], test_input) + ) res = PodmanManager(module, options_dict).execute() results.append(res) total_results = combine(results) module.exit_json(**total_results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_export.py b/plugins/modules/podman_export.py index dda0099..11e04d4 100644 --- a/plugins/modules/podman_export.py +++ b/plugins/modules/podman_export.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_export short_description: Export a podman container author: Sagi Shnaidman (@sshnaidm) @@ -41,12 +41,12 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' -''' +RETURN = """ +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_export: dest: /path/to/tar/file @@ -54,7 +54,7 @@ EXAMPLES = ''' - containers.podman.podman_export: dest: /path/to/tar/file volume: volume-name -''' +""" import os # noqa: E402 from ansible.module_utils.basic import AnsibleModule # noqa: E402 @@ -63,56 +63,58 @@ from ..module_utils.podman.common import remove_file_or_dir # noqa: E402 def export(module, executable): changed = False - export_type = '' + export_type = "" command = [] - if module.params['container']: - export_type = 'container' - command = [executable, 'export'] + if module.params["container"]: + export_type = "container" + command = [executable, "export"] else: - export_type = 'volume' - command = [executable, 'volume', 'export'] + export_type = "volume" + command = [executable, "volume", "export"] - command += ['-o=%s' % module.params['dest'], module.params[export_type]] - if module.params['force']: - dest = module.params['dest'] + command += ["-o=%s" % module.params["dest"], module.params[export_type]] + if module.params["force"]: + dest = module.params["dest"] if os.path.exists(dest): changed = True if module.check_mode: - return changed, '', '' + return changed, "", "" try: remove_file_or_dir(dest) except Exception as e: module.fail_json(msg="Error deleting %s path: %s" % (dest, e)) else: - changed = not os.path.exists(module.params['dest']) + changed = not os.path.exists(module.params["dest"]) if module.check_mode: - return changed, '', '' + return changed, "", "" rc, out, err = module.run_command(command) if rc != 0: - module.fail_json(msg="Error exporting %s %s: %s" % (export_type, - module.params['container'], err)) + module.fail_json( + msg="Error exporting %s %s: %s" + % (export_type, module.params["container"], err) + ) return changed, out, err def main(): module = AnsibleModule( argument_spec=dict( - dest=dict(type='str', required=True), - container=dict(type='str'), - volume=dict(type='str'), - force=dict(type='bool', default=True), - executable=dict(type='str', default='podman') + dest=dict(type="str", required=True), + container=dict(type="str"), + volume=dict(type="str"), + force=dict(type="bool", default=True), + executable=dict(type="str", default="podman"), ), supports_check_mode=True, mutually_exclusive=[ - ('container', 'volume'), + ("container", "volume"), ], required_one_of=[ - ('container', 'volume'), + ("container", "volume"), ], ) - executable = module.get_bin_path(module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) changed, out, err = export(module, executable) results = { @@ -123,5 +125,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_generate_systemd.py b/plugins/modules/podman_generate_systemd.py index b27c16c..7894a2d 100644 --- a/plugins/modules/podman_generate_systemd.py +++ b/plugins/modules/podman_generate_systemd.py @@ -3,11 +3,12 @@ # 2022, Sébastien Gendre # 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) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: podman_generate_systemd author: - Sébastien Gendre (@CyberFox001) @@ -140,9 +141,9 @@ notes: systemd will not see the container or pod as started - Stop your container or pod before you do a C(systemctl daemon-reload), then you can start them with C(systemctl start my_container.service) -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # Example of creating a container and systemd unit file. # When using podman_generate_systemd with new:true then # the container needs rm:true for idempotence. @@ -204,9 +205,9 @@ EXAMPLES = ''' POSTGRES_USER: my_app POSTGRES_PASSWORD: example register: postgres_local_systemd_unit -''' +""" -RETURN = ''' +RETURN = """ systemd_units: description: A copy of the generated systemd .service unit(s) returned: always @@ -220,27 +221,29 @@ podman_command: returned: always type: str sample: "podman generate systemd my_webapp" -''' +""" import os from ansible.module_utils.basic import AnsibleModule import json -from ansible_collections.containers.podman.plugins.module_utils.podman.common import compare_systemd_file_content +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + compare_systemd_file_content, +) RESTART_POLICY_CHOICES = [ - 'no-restart', - 'on-success', - 'on-failure', - 'on-abnormal', - 'on-watchdog', - 'on-abort', - 'always', + "no-restart", + "on-success", + "on-failure", + "on-abnormal", + "on-watchdog", + "on-abort", + "always", ] def generate_systemd(module): - '''Generate systemd .service unit file from a pod or container. + """Generate systemd .service unit file from a pod or container. Parameter: - module (AnsibleModule): An AnsibleModule object @@ -249,7 +252,7 @@ def generate_systemd(module): - A boolean which indicate whether the targeted systemd state is modified - A copy of the generated systemd .service units content - A copy of the command, as a string - ''' + """ # Flag which indicate whether the targeted system state is modified changed = False @@ -257,115 +260,115 @@ def generate_systemd(module): command_options = [] # New option - if module.params['new']: - command_options.append('--new') + if module.params["new"]: + command_options.append("--new") # Restart policy option - restart_policy = module.params['restart_policy'] + restart_policy = module.params["restart_policy"] if restart_policy: # add the restart policy to options - if restart_policy == 'no-restart': - restart_policy = 'no' + if restart_policy == "no-restart": + restart_policy = "no" command_options.append( - '--restart-policy={restart_policy}'.format( + "--restart-policy={restart_policy}".format( restart_policy=restart_policy, ), ) # Restart-sec option (only for Podman 4.0.0 and above) - restart_sec = module.params['restart_sec'] + restart_sec = module.params["restart_sec"] if restart_sec: command_options.append( - '--restart-sec={restart_sec}'.format( + "--restart-sec={restart_sec}".format( restart_sec=restart_sec, ), ) # Start-timeout option (only for Podman 4.0.0 and above) - start_timeout = module.params['start_timeout'] + start_timeout = module.params["start_timeout"] if start_timeout: command_options.append( - '--start-timeout={start_timeout}'.format( + "--start-timeout={start_timeout}".format( start_timeout=start_timeout, ), ) # Stop-timeout option - stop_timeout = module.params['stop_timeout'] + stop_timeout = module.params["stop_timeout"] if stop_timeout: command_options.append( - '--stop-timeout={stop_timeout}'.format( + "--stop-timeout={stop_timeout}".format( stop_timeout=stop_timeout, ), ) # Use container name(s) option - if module.params['use_names']: - command_options.append('--name') + if module.params["use_names"]: + command_options.append("--name") # Container-prefix option - container_prefix = module.params['container_prefix'] + container_prefix = module.params["container_prefix"] if container_prefix is not None: command_options.append( - '--container-prefix={container_prefix}'.format( + "--container-prefix={container_prefix}".format( container_prefix=container_prefix, ), ) # Pod-prefix option - pod_prefix = module.params['pod_prefix'] + pod_prefix = module.params["pod_prefix"] if pod_prefix is not None: command_options.append( - '--pod-prefix={pod_prefix}'.format( + "--pod-prefix={pod_prefix}".format( pod_prefix=pod_prefix, ), ) # Separator option - separator = module.params['separator'] + separator = module.params["separator"] if separator is not None: command_options.append( - '--separator={separator}'.format( + "--separator={separator}".format( separator=separator, ), ) # No-header option - if module.params['no_header']: - command_options.append('--no-header') + if module.params["no_header"]: + command_options.append("--no-header") # After option (only for Podman 4.0.0 and above) - after = module.params['after'] + after = module.params["after"] if after: for item in after: command_options.append( - '--after={item}'.format( + "--after={item}".format( item=item, ), ) # Wants option (only for Podman 4.0.0 and above) - wants = module.params['wants'] + wants = module.params["wants"] if wants: for item in wants: command_options.append( - '--wants={item}'.format( + "--wants={item}".format( item=item, ) ) # Requires option (only for Podman 4.0.0 and above) - requires = module.params['requires'] + requires = module.params["requires"] if requires: for item in requires: command_options.append( - '--requires={item}'.format( + "--requires={item}".format( item=item, ), ) # Environment variables (only for Podman 4.3.0 and above) - environment_variables = module.params['env'] + environment_variables = module.params["env"] if environment_variables: for env_var_name, env_var_value in environment_variables.items(): command_options.append( @@ -376,19 +379,21 @@ def generate_systemd(module): ) # Set output format, of podman command, to json - command_options.extend(['--format', 'json']) + command_options.extend(["--format", "json"]) # Full command build, with option included # Base of the command command = [ - module.params['executable'], 'generate', 'systemd', + module.params["executable"], + "generate", + "systemd", ] # Add the options to the commande command.extend(command_options) # Add pod or container name to the command - command.append(module.params['name']) + command.append(module.params["name"]) # Build the string version of the command, only for module return - command_str = ' '.join(command) + command_str = " ".join(command) # Run the podman command to generated systemd .service unit(s) content return_code, stdout, stderr = module.run_command(command) @@ -396,10 +401,10 @@ def generate_systemd(module): # In case of error in running the command if return_code != 0: # Print information about the error and return and empty dictionary - message = 'Error generating systemd .service unit(s).' - message += ' Command executed: {command_str}' - message += ' Command returned with code: {return_code}.' - message += ' Error message: {stderr}.' + message = "Error generating systemd .service unit(s)." + message += " Command executed: {command_str}" + message += " Command returned with code: {return_code}." + message += " Error message: {stderr}." module.fail_json( msg=message.format( command_str=command_str, @@ -421,9 +426,9 @@ def generate_systemd(module): # Write the systemd .service unit(s) content to file(s), if # requested - if module.params['dest']: + if module.params["dest"]: try: - systemd_units_dest = module.params['dest'] + systemd_units_dest = module.params["dest"] # If destination don't exist if not os.path.exists(systemd_units_dest): # If not in check mode, make it @@ -447,23 +452,24 @@ def generate_systemd(module): # Write each systemd unit, if needed for unit_name, unit_content in systemd_units.items(): # Build full path to unit file - unit_file_name = unit_name + '.service' + unit_file_name = unit_name + ".service" unit_file_full_path = os.path.join( systemd_units_dest, unit_file_name, ) - if module.params['force']: + if module.params["force"]: # Force to replace the existing unit file need_to_write_file = True else: # See if we need to write the unit file, default yes - need_to_write_file = bool(compare_systemd_file_content( - unit_file_full_path, unit_content)) + need_to_write_file = bool( + compare_systemd_file_content(unit_file_full_path, unit_content) + ) # Write the file, if needed if need_to_write_file: - with open(unit_file_full_path, 'w') as unit_file: + with open(unit_file_full_path, "w") as unit_file: # If not in check mode, write the file if not module.check_mode: unit_file.write(unit_content) @@ -471,133 +477,128 @@ def generate_systemd(module): except Exception as exception: # When exception occurs while trying to write units file - message = 'PODMAN-GENERATE-SYSTEMD-DEBUG: ' - message += 'Error writing systemd units files: ' - message += '{exception}' + message = "PODMAN-GENERATE-SYSTEMD-DEBUG: " + message += "Error writing systemd units files: " + message += "{exception}" module.log( - message.format( - exception=exception - ), + message.format(exception=exception), ) # Return the systemd .service unit(s) content return changed, systemd_units, command_str def run_module(): - '''Run the module on the target''' + """Run the module on the target""" # Build the list of parameters user can use module_parameters = { - 'name': { - 'type': 'str', - 'required': True, + "name": { + "type": "str", + "required": True, }, - 'dest': { - 'type': 'path', - 'required': False, + "dest": { + "type": "path", + "required": False, }, - 'new': { - 'type': 'bool', - 'required': False, - 'default': False, + "new": { + "type": "bool", + "required": False, + "default": False, }, - 'force': { - 'type': 'bool', - 'required': False, - 'default': False, + "force": { + "type": "bool", + "required": False, + "default": False, }, - 'restart_policy': { - 'type': 'str', - 'required': False, - 'choices': RESTART_POLICY_CHOICES, + "restart_policy": { + "type": "str", + "required": False, + "choices": RESTART_POLICY_CHOICES, }, - 'restart_sec': { - 'type': 'int', - 'required': False, + "restart_sec": { + "type": "int", + "required": False, }, - 'start_timeout': { - 'type': 'int', - 'required': False, + "start_timeout": { + "type": "int", + "required": False, }, - 'stop_timeout': { - 'type': 'int', - 'required': False, + "stop_timeout": { + "type": "int", + "required": False, }, - 'env': { - 'type': 'dict', - 'required': False, + "env": { + "type": "dict", + "required": False, }, - 'use_names': { - 'type': 'bool', - 'required': False, - 'default': True, + "use_names": { + "type": "bool", + "required": False, + "default": True, }, - 'container_prefix': { - 'type': 'str', - 'required': False, + "container_prefix": { + "type": "str", + "required": False, }, - 'pod_prefix': { - 'type': 'str', - 'required': False, + "pod_prefix": { + "type": "str", + "required": False, }, - 'separator': { - 'type': 'str', - 'required': False, + "separator": { + "type": "str", + "required": False, }, - 'no_header': { - 'type': 'bool', - 'required': False, - 'default': False, + "no_header": { + "type": "bool", + "required": False, + "default": False, }, - 'after': { - 'type': 'list', - 'elements': 'str', - 'required': False, + "after": { + "type": "list", + "elements": "str", + "required": False, }, - 'wants': { - 'type': 'list', - 'elements': 'str', - 'required': False, + "wants": { + "type": "list", + "elements": "str", + "required": False, }, - 'requires': { - 'type': 'list', - 'elements': 'str', - 'required': False, + "requires": { + "type": "list", + "elements": "str", + "required": False, }, - 'executable': { - 'type': 'str', - 'required': False, - 'default': 'podman', + "executable": { + "type": "str", + "required": False, + "default": "podman", }, } # Build result dictionary result = { - 'changed': False, - 'systemd_units': {}, - 'podman_command': '', + "changed": False, + "systemd_units": {}, + "podman_command": "", } # Build the Ansible Module - module = AnsibleModule( - argument_spec=module_parameters, - supports_check_mode=True - ) + module = AnsibleModule(argument_spec=module_parameters, supports_check_mode=True) # Generate the systemd units state_changed, systemd_units, podman_command = generate_systemd(module) - result['changed'] = state_changed - result['systemd_units'] = systemd_units - result['podman_command'] = podman_command + result["changed"] = state_changed + result["systemd_units"] = systemd_units + result["podman_command"] = podman_command # Return the result module.exit_json(**result) def main(): - '''Main function of this script.''' + """Main function of this script.""" run_module() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_image.py b/plugins/modules/podman_image.py index f81abbe..106e64e 100644 --- a/plugins/modules/podman_image.py +++ b/plugins/modules/podman_image.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_image author: - Sam Doran (@samdoran) @@ -225,7 +226,7 @@ DOCUMENTATION = r''' type: list elements: str required: false -''' +""" EXAMPLES = r""" - name: Pull an image @@ -436,8 +437,12 @@ import sys # noqa: E402 from ansible.module_utils._text import to_native from ansible.module_utils.basic import AnsibleModule -from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command -from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + run_podman_command, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ( + create_quadlet_state, +) class PodmanImageManager(object): @@ -448,57 +453,68 @@ class PodmanImageManager(object): self.module = module self.results = results - self.name = self.module.params.get('name') - 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') - self.state = self.module.params.get('state') - self.validate_certs = self.module.params.get('validate_certs') - self.auth_file = self.module.params.get('auth_file') - self.username = self.module.params.get('username') - self.password = self.module.params.get('password') - self.ca_cert_dir = self.module.params.get('ca_cert_dir') - self.build = self.module.params.get('build') - self.push_args = self.module.params.get('push_args') - self.arch = self.module.params.get('arch') + self.name = self.module.params.get("name") + 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") + self.state = self.module.params.get("state") + self.validate_certs = self.module.params.get("validate_certs") + self.auth_file = self.module.params.get("auth_file") + self.username = self.module.params.get("username") + self.password = self.module.params.get("password") + self.ca_cert_dir = self.module.params.get("ca_cert_dir") + self.build = self.module.params.get("build") + self.push_args = self.module.params.get("push_args") + self.arch = self.module.params.get("arch") repo, repo_tag = parse_repository_tag(self.name) if repo_tag: self.name = repo self.tag = repo_tag - delimiter = ':' if "sha256" not in self.tag else '@' - self.image_name = '{name}{d}{tag}'.format(name=self.name, d=delimiter, tag=self.tag) + delimiter = ":" if "sha256" not in self.tag else "@" + self.image_name = "{name}{d}{tag}".format( + name=self.name, d=delimiter, tag=self.tag + ) - if self.state in ['present', 'build']: + if self.state in ["present", "build"]: self.present() - if self.state in ['absent']: + if self.state in ["absent"]: self.absent() - if self.state == 'quadlet': + if self.state == "quadlet": self.make_quadlet() def _run(self, args, expected_rc=0, ignore_errors=False): - cmd = " ".join([self.executable] - + [to_native(i) for i in args]) + cmd = " ".join([self.executable] + [to_native(i) for i in args]) self.module.log("PODMAN-IMAGE-DEBUG: %s" % cmd) - self.results['podman_actions'].append(cmd) + self.results["podman_actions"].append(cmd) return run_podman_command( module=self.module, executable=self.executable, args=args, expected_rc=expected_rc, - ignore_errors=ignore_errors) + ignore_errors=ignore_errors, + ) - def _get_id_from_output(self, lines, startswith=None, contains=None, split_on=' ', maxsplit=1): + def _get_id_from_output( + self, lines, startswith=None, contains=None, split_on=" ", maxsplit=1 + ): layer_ids = [] for line in lines.splitlines(): - if startswith and line.startswith(startswith) or contains and contains in line: + if ( + startswith + and line.startswith(startswith) + or contains + and contains in line + ): splitline = line.rsplit(split_on, maxsplit) layer_ids.append(splitline[1]) @@ -515,7 +531,9 @@ class PodmanImageManager(object): """ containerfile_path = None - for filename in [os.path.join(self.path, fname) for fname in ["Containerfile", "Dockerfile"]]: + for filename in [ + os.path.join(self.path, fname) for fname in ["Containerfile", "Dockerfile"] + ]: if os.path.exists(filename): containerfile_path = filename break @@ -534,8 +552,10 @@ class PodmanImageManager(object): locations, return 'None'. """ - build_file_arg = self.build.get('file') if self.build else None - containerfile_contents = self.build.get('container_file') if self.build else None + build_file_arg = self.build.get("file") if self.build else None + containerfile_contents = ( + self.build.get("container_file") if self.build else None + ) container_filename = None if build_file_arg: @@ -564,8 +584,7 @@ class PodmanImageManager(object): ).hexdigest() else: return hashlib.sha256( - containerfile_contents.encode(), - usedforsecurity=False + containerfile_contents.encode(), usedforsecurity=False ).hexdigest() def _get_args_containerfile_hash(self): @@ -578,12 +597,14 @@ class PodmanImageManager(object): args_containerfile_hash = None - context_has_containerfile = self.path and self._find_containerfile_from_context() + context_has_containerfile = ( + self.path and self._find_containerfile_from_context() + ) should_hash_args_containerfile = ( - context_has_containerfile or - self.build.get('file') is not None or - self.build.get('container_file') is not None + context_has_containerfile + or self.build.get("file") is not None + or self.build.get("container_file") is not None ) if should_hash_args_containerfile: @@ -599,68 +620,86 @@ class PodmanImageManager(object): args_containerfile_hash = self._get_args_containerfile_hash() if image: - digest_before = image[0].get('Digest', image[0].get('digest')) - labels = image[0].get('Labels') or {} + digest_before = image[0].get("Digest", image[0].get("digest")) + labels = image[0].get("Labels") or {} if "containerfile.hash" in labels: existing_image_containerfile_hash = labels["containerfile.hash"] else: digest_before = None - both_hashes_exist_and_differ = (args_containerfile_hash and existing_image_containerfile_hash and - args_containerfile_hash != existing_image_containerfile_hash - ) + both_hashes_exist_and_differ = ( + args_containerfile_hash + and existing_image_containerfile_hash + and args_containerfile_hash != existing_image_containerfile_hash + ) if not image or self.force or both_hashes_exist_and_differ: - if self.state == 'build' or self.path: + if self.state == "build" or self.path: # Build the image - build_file = self.build.get('file') if self.build else None - container_file_txt = self.build.get('container_file') if self.build else None + 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!') + 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')) + 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(args_containerfile_hash) - image = self.results['image'] + self.results["image"], self.results["stdout"] = self.build_image( + args_containerfile_hash + ) + image = self.results["image"] else: # Pull the image - self.results['actions'].append('Pulled image {image_name}'.format(image_name=self.image_name)) + self.results["actions"].append( + "Pulled image {image_name}".format(image_name=self.image_name) + ) if not self.module.check_mode: - image = self.results['image'] = self.pull_image() + image = self.results["image"] = self.pull_image() if not image: image = self.find_image() if not self.module.check_mode: - digest_after = image[0].get('Digest', image[0].get('digest')) - self.results['changed'] = digest_before != digest_after + digest_after = image[0].get("Digest", image[0].get("digest")) + self.results["changed"] = digest_before != digest_after else: - self.results['changed'] = True + self.results["changed"] = True if self.push: - self.results['image'], output = self.push_image() - self.results['stdout'] += "\n" + output - if image and not self.results.get('image'): - self.results['image'] = image + self.results["image"], output = self.push_image() + self.results["stdout"] += "\n" + output + if image and not self.results.get("image"): + self.results["image"] = image def absent(self): image = self.find_image() image_id = self.find_image_id() if image: - self.results['actions'].append('Removed image {name}'.format(name=self.name)) - self.results['changed'] = True - self.results['image']['state'] = 'Deleted' + self.results["actions"].append( + "Removed image {name}".format(name=self.name) + ) + self.results["changed"] = True + self.results["image"]["state"] = "Deleted" if not self.module.check_mode: self.remove_image() elif image_id: - self.results['actions'].append( - 'Removed image with id {id}'.format(id=self.image_name)) - self.results['changed'] = True - self.results['image']['state'] = 'Deleted' + self.results["actions"].append( + "Removed image with id {id}".format(id=self.image_name) + ) + self.results["changed"] = True + self.results["image"]["state"] = "Deleted" if not self.module.check_mode: self.remove_image_id() @@ -673,17 +712,21 @@ class PodmanImageManager(object): if image_name is None: image_name = self.image_name # Let's find out if image exists - rc, out, err = self._run(['image', 'exists', image_name], ignore_errors=True) + rc, out, err = self._run(["image", "exists", image_name], ignore_errors=True) if rc == 0: inspect_json = self.inspect_image(image_name) else: return None - args = ['image', 'ls', image_name, '--format', 'json'] + args = ["image", "ls", image_name, "--format", "json"] rc, images, err = self._run(args, ignore_errors=True) try: images = json.loads(images) except json.decoder.JSONDecodeError: - self.module.fail_json(msg='Failed to parse JSON output from podman image ls: {out}'.format(out=images)) + self.module.fail_json( + msg="Failed to parse JSON output from podman image ls: {out}".format( + out=images + ) + ) if len(images) == 0: return None inspect_json = self.inspect_image(image_name) @@ -692,16 +735,15 @@ class PodmanImageManager(object): return None def _is_target_arch(self, inspect_json=None, arch=None): - return arch and inspect_json[0]['Architecture'] == arch + return arch and inspect_json[0]["Architecture"] == arch def find_image_id(self, image_id=None): if image_id is None: # If image id is set as image_name, remove tag - image_id = re.sub(':.*$', '', self.image_name) - args = ['image', 'ls', '--quiet', '--no-trunc'] + image_id = re.sub(":.*$", "", self.image_name) + args = ["image", "ls", "--quiet", "--no-trunc"] rc, candidates, err = self._run(args, ignore_errors=True) - candidates = [re.sub('^sha256:', '', c) - for c in str.splitlines(candidates)] + candidates = [re.sub("^sha256:", "", c) for c in str.splitlines(candidates)] for c in candidates: if c.startswith(image_id): return image_id @@ -710,12 +752,16 @@ class PodmanImageManager(object): def inspect_image(self, image_name=None): if image_name is None: image_name = self.image_name - args = ['inspect', image_name, '--format', 'json'] + args = ["inspect", image_name, "--format", "json"] rc, image_data, err = self._run(args) try: image_data = json.loads(image_data) except json.decoder.JSONDecodeError: - self.module.fail_json(msg='Failed to parse JSON output from podman inspect: {out}'.format(out=image_data)) + self.module.fail_json( + msg="Failed to parse JSON output from podman inspect: {out}".format( + out=image_data + ) + ) if len(image_data) > 0: return image_data else: @@ -725,26 +771,28 @@ class PodmanImageManager(object): if image_name is None: image_name = self.image_name - args = ['pull', image_name, '-q'] + args = ["pull", image_name, "-q"] if self.arch: - args.extend(['--arch', self.arch]) + args.extend(["--arch", self.arch]) if self.auth_file: - args.extend(['--authfile', self.auth_file]) + args.extend(["--authfile", self.auth_file]) if self.username and self.password: - cred_string = '{user}:{password}'.format(user=self.username, password=self.password) - args.extend(['--creds', cred_string]) + cred_string = "{user}:{password}".format( + user=self.username, password=self.password + ) + args.extend(["--creds", cred_string]) if self.validate_certs is not None: if self.validate_certs: - args.append('--tls-verify') + args.append("--tls-verify") else: - args.append('--tls-verify=false') + args.append("--tls-verify=false") if self.ca_cert_dir: - args.extend(['--cert-dir', 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)) @@ -753,177 +801,212 @@ class PodmanImageManager(object): 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)) + 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)) + msg="Failed to pull image {image_name}".format( + image_name=image_name + ) + ) return self.inspect_image(out.strip()) def build_image(self, containerfile_hash): - args = ['build'] - args.extend(['-t', self.image_name]) + args = ["build"] + args.extend(["-t", self.image_name]) if self.validate_certs is not None: if self.validate_certs: - args.append('--tls-verify') + args.append("--tls-verify") else: - args.append('--tls-verify=false') + args.append("--tls-verify=false") - annotation = self.build.get('annotation') + annotation = self.build.get("annotation") if annotation: for k, v in annotation.items(): - args.extend(['--annotation', '{k}={v}'.format(k=k, v=v)]) + args.extend(["--annotation", "{k}={v}".format(k=k, v=v)]) if self.ca_cert_dir: - args.extend(['--cert-dir', self.ca_cert_dir]) + args.extend(["--cert-dir", self.ca_cert_dir]) - if self.build.get('force_rm'): - args.append('--force-rm') + if self.build.get("force_rm"): + args.append("--force-rm") - image_format = self.build.get('format') + image_format = self.build.get("format") if image_format: - args.extend(['--format', image_format]) + args.extend(["--format", image_format]) if self.arch: - args.extend(['--arch', self.arch]) + args.extend(["--arch", self.arch]) - if not self.build.get('cache'): - args.append('--no-cache') + if not self.build.get("cache"): + args.append("--no-cache") - if self.build.get('rm'): - args.append('--rm') + if self.build.get("rm"): + args.append("--rm") - containerfile = self.build.get('file') + containerfile = self.build.get("file") if containerfile: - args.extend(['--file', containerfile]) - container_file_txt = self.build.get('container_file') + 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()) + 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: + 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]) + args.extend(["--file", container_file_path]) if containerfile_hash: - args.extend(['--label', f"containerfile.hash={containerfile_hash}"]) + args.extend(["--label", f"containerfile.hash={containerfile_hash}"]) - volume = self.build.get('volume') + volume = self.build.get("volume") if volume: for v in volume: if v: - args.extend(['--volume', v]) + args.extend(["--volume", v]) if self.auth_file: - args.extend(['--authfile', self.auth_file]) + args.extend(["--authfile", self.auth_file]) if self.username and self.password: - cred_string = '{user}:{password}'.format(user=self.username, password=self.password) - args.extend(['--creds', cred_string]) + cred_string = "{user}:{password}".format( + user=self.username, password=self.password + ) + args.extend(["--creds", cred_string]) - extra_args = self.build.get('extra_args') + extra_args = self.build.get("extra_args") if extra_args: args.extend(shlex.split(extra_args)) - target = self.build.get('target') + target = self.build.get("target") if target: - args.extend(['--target', target]) + args.extend(["--target", target]) 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='-->') + last_id = self._get_id_from_output(out, startswith="-->") return self.inspect_image(last_id), out + err def push_image(self): - args = ['push'] + args = ["push"] if self.validate_certs is not None: if self.validate_certs: - args.append('--tls-verify') + args.append("--tls-verify") else: - args.append('--tls-verify=false') + args.append("--tls-verify=false") if self.ca_cert_dir: - args.extend(['--cert-dir', self.ca_cert_dir]) + args.extend(["--cert-dir", self.ca_cert_dir]) if self.username and self.password: - cred_string = '{user}:{password}'.format(user=self.username, password=self.password) - args.extend(['--creds', cred_string]) + cred_string = "{user}:{password}".format( + user=self.username, password=self.password + ) + args.extend(["--creds", cred_string]) if self.auth_file: - args.extend(['--authfile', self.auth_file]) + args.extend(["--authfile", self.auth_file]) - if self.push_args.get('compress'): - args.append('--compress') + if self.push_args.get("compress"): + args.append("--compress") - push_format = self.push_args.get('format') + push_format = self.push_args.get("format") if push_format: - args.extend(['--format', push_format]) + args.extend(["--format", push_format]) - if self.push_args.get('remove_signatures'): - args.append('--remove-signatures') + if self.push_args.get("remove_signatures"): + args.append("--remove-signatures") - sign_by_key = self.push_args.get('sign_by') + sign_by_key = self.push_args.get("sign_by") if sign_by_key: - args.extend(['--sign-by', sign_by_key]) + args.extend(["--sign-by", sign_by_key]) - push_extra_args = self.push_args.get('extra_args') + 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') - transport = self.push_args.get('transport') + dest = self.push_args.get("dest") + transport = self.push_args.get("transport") if dest is None: dest = self.image_name if transport: - if transport == 'docker': - dest_format_string = '{transport}://{dest}' - elif transport == 'ostree': - dest_format_string = '{transport}:{name}@{dest}' + if transport == "docker": + dest_format_string = "{transport}://{dest}" + elif transport == "ostree": + dest_format_string = "{transport}:{name}@{dest}" else: - dest_format_string = '{transport}:{dest}' - 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) + dest_format_string = "{transport}:{dest}" + 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: + 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 with image name and tag.") + 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 with image name and tag." + ) 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['changed'] = True - out, err = '', '' + 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["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']) + 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 @@ -931,35 +1014,41 @@ class PodmanImageManager(object): if image_name is None: image_name = self.image_name - args = ['rmi', image_name] + args = ["rmi", image_name] if self.force: - args.append('--force') + 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): if image_id is None: - image_id = re.sub(':.*$', '', self.image_name) + image_id = re.sub(":.*$", "", self.image_name) - args = ['rmi', image_id] + args = ["rmi", image_id] if self.force: - args.append('--force') + args.append("--force") rc, out, err = self._run(args, ignore_errors=True) if rc != 0: - self.module.fail_json(msg='Failed to remove image with id {image_id}. {err}'.format( - image_id=image_id, err=err)) + self.module.fail_json( + msg="Failed to remove image with id {image_id}. {err}".format( + image_id=image_id, err=err + ) + ) return out def parse_repository_tag(repo_name): - parts = repo_name.rsplit('@', 1) + parts = repo_name.rsplit("@", 1) if len(parts) == 2: return tuple(parts) - parts = repo_name.rsplit(':', 1) - if len(parts) == 2 and '/' not in parts[1]: + parts = repo_name.rsplit(":", 1) + if len(parts) == 2 and "/" not in parts[1]: return tuple(parts) return repo_name, None @@ -967,77 +1056,78 @@ def parse_repository_tag(repo_name): def main(): module = AnsibleModule( argument_spec=dict( - name=dict(type='str', required=True), - 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), - state=dict(type='str', default='present', choices=['absent', 'present', 'build', 'quadlet']), - validate_certs=dict(type='bool', aliases=['tlsverify', 'tls_verify']), - executable=dict(type='str', default='podman'), - auth_file=dict(type='path', aliases=['authfile']), - username=dict(type='str'), - password=dict(type='str', no_log=True), - ca_cert_dir=dict(type='path'), - quadlet_dir=dict(type='path', required=False), - quadlet_filename=dict(type='str'), - quadlet_file_mode=dict(type='raw', required=False), - quadlet_options=dict(type='list', elements='str', required=False), + name=dict(type="str", required=True), + 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), + state=dict( + type="str", + default="present", + choices=["absent", "present", "build", "quadlet"], + ), + validate_certs=dict(type="bool", aliases=["tlsverify", "tls_verify"]), + executable=dict(type="str", default="podman"), + auth_file=dict(type="path", aliases=["authfile"]), + username=dict(type="str"), + password=dict(type="str", no_log=True), + ca_cert_dir=dict(type="path"), + quadlet_dir=dict(type="path", required=False), + quadlet_filename=dict(type="str"), + quadlet_file_mode=dict(type="raw", required=False), + quadlet_options=dict(type="list", elements="str", required=False), build=dict( - type='dict', - aliases=['build_args', 'buildargs'], + type="dict", + aliases=["build_args", "buildargs"], default={}, options=dict( - 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'], - default='oci' - ), - cache=dict(type='bool', default=True), - rm=dict(type='bool', default=True), - volume=dict(type='list', elements='str'), - extra_args=dict(type='str'), - target=dict(type='str'), + 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"], default="oci"), + cache=dict(type="bool", default=True), + rm=dict(type="bool", default=True), + volume=dict(type="list", elements="str"), + extra_args=dict(type="str"), + target=dict(type="str"), ), ), push_args=dict( - type='dict', + type="dict", default={}, options=dict( - compress=dict(type='bool'), - format=dict(type='str', choices=['oci', 'v2s1', 'v2s2']), - remove_signatures=dict(type='bool'), - sign_by=dict(type='str'), - dest=dict(type='str', aliases=['destination'],), - extra_args=dict(type='str'), + compress=dict(type="bool"), + format=dict(type="str", choices=["oci", "v2s1", "v2s2"]), + 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', + type="str", choices=[ - 'dir', - 'docker-archive', - 'docker-daemon', - 'oci-archive', - 'ostree', - 'docker' - ] + "dir", + "docker-archive", + "docker-daemon", + "oci-archive", + "ostree", + "docker", + ], ), ), ), ), supports_check_mode=True, - required_together=( - ['username', 'password'], - ), + required_together=(["username", "password"],), mutually_exclusive=( - ['auth_file', 'username'], - ['auth_file', 'password'], + ["auth_file", "username"], + ["auth_file", "password"], ), ) @@ -1046,12 +1136,12 @@ def main(): actions=[], podman_actions=[], image={}, - stdout='', + stdout="", ) PodmanImageManager(module, results) module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_image_info.py b/plugins/modules/podman_image_info.py index ed7412f..f269a4c 100644 --- a/plugins/modules/podman_image_info.py +++ b/plugins/modules/podman_image_info.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_image_info author: - Sam Doran (@samdoran) @@ -27,7 +28,7 @@ options: type: list elements: str -''' +""" EXAMPLES = r""" - name: Gather info for all images @@ -126,13 +127,13 @@ from ansible.module_utils.basic import AnsibleModule def image_exists(module, executable, name): - command = [executable, 'image', 'exists', name] + command = [executable, "image", "exists", name] rc, out, err = module.run_command(command) if rc == 1: return False elif 'Command "exists" not found' in err: # The 'exists' test is available in podman >= 0.12.1 - command = [executable, 'image', 'ls', '-q', name] + command = [executable, "image", "ls", "-q", name] rc2, out2, err2 = module.run_command(command) if rc2 != 0: return False @@ -158,12 +159,14 @@ def get_image_info(module, executable, name): names = [name] if len(names) > 0: - command = [executable, 'image', 'inspect'] + command = [executable, "image", "inspect"] command.extend(names) rc, out, err = module.run_command(command) if rc != 0: - module.fail_json(msg="Unable to gather info for '{0}': {1}".format(', '.join(names), err)) + module.fail_json( + msg="Unable to gather info for '{0}': {1}".format(", ".join(names), err) + ) return out else: @@ -171,11 +174,11 @@ def get_image_info(module, executable, name): def get_all_image_info(module, executable): - command = [executable, 'image', 'ls', '-q'] + command = [executable, "image", "ls", "-q"] rc, out, err = module.run_command(command) out = out.strip() if out: - name = out.split('\n') + name = out.split("\n") res = get_image_info(module, executable, name) return res return json.dumps([]) @@ -184,14 +187,14 @@ def get_all_image_info(module, executable): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - name=dict(type='list', elements='str') + executable=dict(type="str", default="podman"), + name=dict(type="list", elements="str"), ), supports_check_mode=True, ) - executable = module.params['executable'] - name = module.params.get('name') + executable = module.params["executable"] + name = module.params.get("name") executable = module.get_bin_path(executable, required=True) if name: @@ -200,13 +203,10 @@ def main(): else: results = json.loads(get_all_image_info(module, executable)) - results = dict( - changed=False, - images=results - ) + results = dict(changed=False, images=results) module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_import.py b/plugins/modules/podman_import.py index 6a408c0..ccfd76d 100644 --- a/plugins/modules/podman_import.py +++ b/plugins/modules/podman_import.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_import short_description: Import Podman container from a tar file. author: Sagi Shnaidman (@sshnaidm) @@ -41,9 +41,9 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' +RETURN = """ image: description: info from loaded image returned: always @@ -89,9 +89,9 @@ image: ], "NamesHistory": null } -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_import: src: /path/to/tar/file @@ -102,7 +102,7 @@ EXAMPLES = ''' - containers.podman.podman_import: src: /path/to/tar/file volume: myvolume -''' +""" import json # noqa: E402 from ansible.module_utils.basic import AnsibleModule # noqa: E402 @@ -110,22 +110,22 @@ from ansible.module_utils.basic import AnsibleModule # noqa: E402 def load(module, executable): changed = False - command = [executable, 'import'] - if module.params['commit_message']: - command.extend(['--message', module.params['commit_message']]) - if module.params['change']: - for change in module.params['change']: - command += ['--change', "=".join(list(change.items())[0])] - command += [module.params['src']] + command = [executable, "import"] + if module.params["commit_message"]: + command.extend(["--message", module.params["commit_message"]]) + if module.params["change"]: + for change in module.params["change"]: + command += ["--change", "=".join(list(change.items())[0])] + command += [module.params["src"]] changed = True if module.check_mode: - return changed, '', '', '', command + return changed, "", "", "", command rc, out, err = module.run_command(command) if rc != 0: module.fail_json(msg="Image loading failed: %s" % (err)) - image_name_line = [i for i in out.splitlines() if 'sha256' in i][0] + image_name_line = [i for i in out.splitlines() if "sha256" in i][0] image_name = image_name_line.split(":", maxsplit=1)[1].strip() - rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name]) + rc, out2, err2 = module.run_command([executable, "image", "inspect", image_name]) if rc != 0: module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2)) try: @@ -137,43 +137,55 @@ def load(module, executable): def volume_load(module, executable): changed = True - command = [executable, 'volume', 'import', module.params['volume'], module.params['src']] - src = module.params['src'] + command = [ + executable, + "volume", + "import", + module.params["volume"], + module.params["src"], + ] + src = module.params["src"] if module.check_mode: - return changed, '', '', '', command + return changed, "", "", "", command rc, out, err = module.run_command(command) if rc != 0: module.fail_json(msg="Error importing volume %s: %s" % (src, err)) - rc, out2, err2 = module.run_command([executable, 'volume', 'inspect', module.params['volume']]) + rc, out2, err2 = module.run_command( + [executable, "volume", "inspect", module.params["volume"]] + ) if rc != 0: - module.fail_json(msg="Volume %s inspection failed: %s" % (module.params['volume'], err2)) + module.fail_json( + msg="Volume %s inspection failed: %s" % (module.params["volume"], err2) + ) try: info = json.loads(out2)[0] except Exception as e: - module.fail_json(msg="Could not parse JSON from volume %s: %s" % (module.params['volume'], e)) + module.fail_json( + msg="Could not parse JSON from volume %s: %s" % (module.params["volume"], e) + ) return changed, out, err, info, command def main(): module = AnsibleModule( argument_spec=dict( - src=dict(type='str', required=True), - commit_message=dict(type='str'), - change=dict(type='list', elements='dict'), - executable=dict(type='str', default='podman'), - volume=dict(type='str', required=False), + src=dict(type="str", required=True), + commit_message=dict(type="str"), + change=dict(type="list", elements="dict"), + executable=dict(type="str", default="podman"), + volume=dict(type="str", required=False), ), mutually_exclusive=[ - ('volume', 'commit_message'), - ('volume', 'change'), + ("volume", "commit_message"), + ("volume", "change"), ], supports_check_mode=True, ) - executable = module.get_bin_path(module.params['executable'], required=True) - volume_info = '' - image_info = '' - if module.params['volume']: + executable = module.get_bin_path(module.params["executable"], required=True) + volume_info = "" + image_info = "" + if module.params["volume"]: changed, out, err, volume_info, command = volume_load(module, executable) else: changed, out, err, image_info, command = load(module, executable) @@ -184,11 +196,11 @@ def main(): "stderr": err, "image": image_info, "volume": volume_info, - "podman_command": " ".join(command) + "podman_command": " ".join(command), } module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_load.py b/plugins/modules/podman_load.py index 4fa7bde..df74204 100644 --- a/plugins/modules/podman_load.py +++ b/plugins/modules/podman_load.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_load short_description: Load image from a tar file. author: Sagi Shnaidman (@sshnaidm) @@ -32,9 +32,9 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' +RETURN = """ image: description: info from loaded image returned: always @@ -132,13 +132,13 @@ c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/merged", "VirtualSize": 569919342 } ] -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_load: input: /path/to/tar/file -''' +""" import json # noqa: E402 from ansible.module_utils.basic import AnsibleModule # noqa: E402 @@ -146,24 +146,24 @@ from ansible.module_utils.basic import AnsibleModule # noqa: E402 def load(module, executable): changed = False - command = [executable, 'load', '--input'] - command.append(module.params['input']) + command = [executable, "load", "--input"] + command.append(module.params["input"]) changed = True if module.check_mode: - return changed, '', '', '' + return changed, "", "", "" rc, out, err = module.run_command(command) if rc != 0: module.fail_json(msg="Image loading failed: %s" % (err)) - image_name_line = [i for i in out.splitlines() if 'Loaded image' in i][0] + image_name_line = [i for i in out.splitlines() if "Loaded image" in i][0] # For Podman < 4.x - if 'Loaded image(s):' in image_name_line: - image_name = image_name_line.split("Loaded image(s): ")[1].split(',')[0].strip() + if "Loaded image(s):" in image_name_line: + image_name = image_name_line.split("Loaded image(s): ")[1].split(",")[0].strip() # For Podman > 4.x - elif 'Loaded image:' in image_name_line: + elif "Loaded image:" in image_name_line: image_name = image_name_line.split("Loaded image: ")[1].strip() else: module.fail_json(msg="Not found images in %s" % image_name_line) - rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name]) + rc, out2, err2 = module.run_command([executable, "image", "inspect", image_name]) if rc != 0: module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2)) try: @@ -176,13 +176,13 @@ def load(module, executable): def main(): module = AnsibleModule( argument_spec=dict( - input=dict(type='str', required=True, aliases=['path']), - executable=dict(type='str', default='podman') + input=dict(type="str", required=True, aliases=["path"]), + executable=dict(type="str", default="podman"), ), supports_check_mode=True, ) - executable = module.get_bin_path(module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) changed, out, err, image_info = load(module, executable) results = { @@ -195,5 +195,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_login.py b/plugins/modules/podman_login.py index 27f12c8..d075323 100644 --- a/plugins/modules/podman_login.py +++ b/plugins/modules/podman_login.py @@ -2,9 +2,10 @@ # 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''' +DOCUMENTATION = r""" module: podman_login author: - "Jason Hiatt (@jthiatt)" @@ -70,7 +71,7 @@ options: - Name of an existing C(podman) secret to use for authentication to target registry type: str -''' +""" EXAMPLES = r""" - name: Login to default registry and create ${XDG_RUNTIME_DIR}/containers/auth.json @@ -96,57 +97,70 @@ EXAMPLES = r""" import hashlib 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 +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, +) -def login(module, executable, registry, authfile, - certdir, tlsverify, username, password, secret): +def login( + module, + executable, + registry, + authfile, + certdir, + tlsverify, + username, + password, + secret, +): - command = [executable, 'login'] + command = [executable, "login"] changed = False if username: - command.extend(['--username', username]) + command.extend(["--username", username]) if password: - command.extend(['--password', password]) + command.extend(["--password", password]) if secret: - command.extend(['--secret', secret]) + command.extend(["--secret", secret]) if authfile: - command.extend(['--authfile', authfile]) + command.extend(["--authfile", authfile]) authfile = os.path.expandvars(authfile) else: - authfile = os.getenv('XDG_RUNTIME_DIR', '') + '/containers/auth.json' + authfile = os.getenv("XDG_RUNTIME_DIR", "") + "/containers/auth.json" if registry: command.append(registry) if certdir: - command.extend(['--cert-dir', certdir]) + command.extend(["--cert-dir", certdir]) if tlsverify is not None: if tlsverify: - command.append('--tls-verify') + command.append("--tls-verify") else: - command.append('--tls-verify=False') + command.append("--tls-verify=False") # Use a checksum to check if the auth JSON has changed checksum = None - docker_authfile = os.path.expandvars('$HOME/.docker/config.json') + docker_authfile = os.path.expandvars("$HOME/.docker/config.json") # podman falls back to ~/.docker/config.json if the default authfile doesn't exist check_file = authfile if os.path.exists(authfile) else docker_authfile if os.path.exists(check_file): - content = open(check_file, 'rb').read() + content = open(check_file, "rb").read() checksum = hashlib.sha256(content).hexdigest() rc, out, err = module.run_command(command) if rc != 0: - if 'Error: Not logged into' not in err: + if "Error: Not logged into" not in err: module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err)) else: # If the command is successful, we managed to login changed = True - if 'Existing credentials are valid' in out: + if "Existing credentials are valid" in out: changed = False # If we have managed to calculate a checksum before, check if it has changed # due to the login if checksum: - content = open(check_file, 'rb').read() + content = open(check_file, "rb").read() new_checksum = hashlib.sha256(content).hexdigest() if new_checksum == checksum: changed = False @@ -156,47 +170,56 @@ def login(module, executable, registry, authfile, def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - registry=dict(type='str'), - authfile=dict(type='path'), - username=dict(type='str'), - password=dict(type='str', no_log=True), - certdir=dict(type='path'), - tlsverify=dict(type='bool'), - secret=dict(type='str', no_log=False), + executable=dict(type="str", default="podman"), + registry=dict(type="str"), + authfile=dict(type="path"), + username=dict(type="str"), + password=dict(type="str", no_log=True), + certdir=dict(type="path"), + tlsverify=dict(type="bool"), + secret=dict(type="str", no_log=False), ), supports_check_mode=False, required_by={ - 'password': 'username', + "password": "username", }, mutually_exclusive=[ - ['password', 'secret'], + ["password", "secret"], ], ) - registry = module.params['registry'] - authfile = module.params['authfile'] - username = module.params['username'] - password = module.params['password'] - certdir = module.params['certdir'] - tlsverify = module.params['tlsverify'] - secret = module.params['secret'] - executable = module.get_bin_path(module.params['executable'], required=True) + registry = module.params["registry"] + authfile = module.params["authfile"] + username = module.params["username"] + password = module.params["password"] + certdir = module.params["certdir"] + tlsverify = module.params["tlsverify"] + secret = module.params["secret"] + executable = module.get_bin_path(module.params["executable"], required=True) podman_version = get_podman_version(module, fail=False) if ( - (podman_version is not None) and - (LooseVersion(podman_version) < LooseVersion('4.7.0')) and - secret + (podman_version is not None) + and (LooseVersion(podman_version) < LooseVersion("4.7.0")) + and secret ): module.fail_json(msg="secret option may not be used with podman < 4.7.0") if username and ((not password) and (not secret)): module.fail_json(msg="Must pass either password or secret with username") - changed, out, err = login(module, executable, registry, authfile, - certdir, tlsverify, username, password, secret) + changed, out, err = login( + module, + executable, + registry, + authfile, + certdir, + tlsverify, + username, + password, + secret, + ) results = { "changed": changed, @@ -207,5 +230,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_login_info.py b/plugins/modules/podman_login_info.py index 739adb1..da4347d 100644 --- a/plugins/modules/podman_login_info.py +++ b/plugins/modules/podman_login_info.py @@ -2,6 +2,7 @@ # 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 @@ -64,23 +65,23 @@ from ansible.module_utils.basic import AnsibleModule def get_login_info(module, executable, authfile, registry): - command = [executable, 'login', '--get-login'] + command = [executable, "login", "--get-login"] result = dict( registry=registry, - username='', + username="", logged_in=False, ) if authfile: - command.extend(['--authfile', authfile]) + command.extend(["--authfile", authfile]) if registry: command.append(registry) rc, out, err = module.run_command(command) if rc != 0: - if 'Error: not logged into' in err: + if "Error: not logged into" in err: # The error message is e.g. 'Error: not logged into docker.io' # Therefore get last word to extract registry name result["registry"] = err.split()[-1] - err = '' + err = "" return result module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err)) result["username"] = out.strip() @@ -91,16 +92,16 @@ def get_login_info(module, executable, authfile, registry): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - authfile=dict(type='path'), - registry=dict(type='str', required=True) + executable=dict(type="str", default="podman"), + authfile=dict(type="path"), + registry=dict(type="str", required=True), ), supports_check_mode=True, ) - registry = module.params['registry'] - authfile = module.params['authfile'] - executable = module.get_bin_path(module.params['executable'], required=True) + registry = module.params["registry"] + authfile = module.params["authfile"] + executable = module.get_bin_path(module.params["executable"], required=True) inspect_results = get_login_info(module, executable, authfile, registry) @@ -112,5 +113,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_logout.py b/plugins/modules/podman_logout.py index d5816a9..81eb947 100644 --- a/plugins/modules/podman_logout.py +++ b/plugins/modules/podman_logout.py @@ -2,9 +2,10 @@ # 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''' +DOCUMENTATION = r""" module: podman_logout author: - "Clemens Lange (@clelange)" @@ -63,7 +64,7 @@ options: machine running C(podman) default: 'podman' type: str -''' +""" EXAMPLES = r""" - name: Log out of default registry @@ -87,29 +88,31 @@ EXAMPLES = r""" from ansible.module_utils.basic import AnsibleModule -def logout(module, executable, registry, authfile, all_registries, ignore_docker_credentials): - command = [executable, 'logout'] +def logout( + module, executable, registry, authfile, all_registries, ignore_docker_credentials +): + command = [executable, "logout"] changed = False if authfile: - command.extend(['--authfile', authfile]) + command.extend(["--authfile", authfile]) if registry: command.append(registry) if all_registries: command.append("--all") rc, out, err = module.run_command(command) if rc != 0: - if 'Error: Not logged into' not in err: + if "Error: Not logged into" not in err: module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err)) else: # If the command is successful, we managed to log out # Mind: This also applied if --all flag is used, while in this case # there is no check whether one has been logged into any registry changed = True - if 'Existing credentials were established via' in out: + if "Existing credentials were established via" in out: # The command will return successfully but not log out the user if the # credentials were initially created using docker. Catch this behaviour: if not ignore_docker_credentials: - module.fail_json(msg="Unable to log out %s: %s" % (registry or '', out)) + module.fail_json(msg="Unable to log out %s: %s" % (registry or "", out)) else: changed = False return changed, out, err @@ -118,27 +121,33 @@ def logout(module, executable, registry, authfile, all_registries, ignore_docker def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - registry=dict(type='str'), - authfile=dict(type='path'), - all=dict(type='bool'), - ignore_docker_credentials=dict(type='bool'), + executable=dict(type="str", default="podman"), + registry=dict(type="str"), + authfile=dict(type="path"), + all=dict(type="bool"), + ignore_docker_credentials=dict(type="bool"), ), supports_check_mode=True, mutually_exclusive=( - ['registry', 'all'], - ['ignore_docker_credentials', 'all'], + ["registry", "all"], + ["ignore_docker_credentials", "all"], ), ) - registry = module.params['registry'] - authfile = module.params['authfile'] - all_registries = module.params['all'] - ignore_docker_credentials = module.params['ignore_docker_credentials'] - executable = module.get_bin_path(module.params['executable'], required=True) + registry = module.params["registry"] + authfile = module.params["authfile"] + all_registries = module.params["all"] + ignore_docker_credentials = module.params["ignore_docker_credentials"] + executable = module.get_bin_path(module.params["executable"], required=True) - changed, out, err = logout(module, executable, registry, authfile, - all_registries, ignore_docker_credentials) + changed, out, err = logout( + module, + executable, + registry, + authfile, + all_registries, + ignore_docker_credentials, + ) results = { "changed": changed, @@ -149,5 +158,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_network.py b/plugins/modules/podman_network.py index c5853bd..4a79901 100644 --- a/plugins/modules/podman_network.py +++ b/plugins/modules/podman_network.py @@ -3,6 +3,7 @@ # 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 @@ -295,27 +296,35 @@ network: """ import json + try: import ipaddress + HAS_IP_ADDRESS_MODULE = True except ImportError: HAS_IP_ADDRESS_MODULE = False from ansible.module_utils.basic import AnsibleModule # noqa: F402 from ansible.module_utils._text import to_bytes, to_native # noqa: F402 -from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion -from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys -from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + LooseVersion, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + lower_keys, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ( + create_quadlet_state, +) class PodmanNetworkModuleParams: """Creates list of arguments for podman CLI command. - Arguments: - action {str} -- action type from 'create', 'delete' - params {dict} -- dictionary of module parameters + Arguments: + action {str} -- action type from 'create', 'delete' + params {dict} -- dictionary of module parameters - """ + """ def __init__(self, action, params, podman_version, module): self.params = params @@ -329,99 +338,104 @@ class PodmanNetworkModuleParams: Returns: list -- list of byte strings for Popen command """ - if self.action in ['delete']: + if self.action in ["delete"]: return self._delete_action() - if self.action in ['create']: + if self.action in ["create"]: return self._create_action() def _delete_action(self): - cmd = ['rm', self.params['name']] - if self.params['force']: - cmd += ['--force'] - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + cmd = ["rm", self.params["name"]] + if self.params["force"]: + cmd += ["--force"] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def _create_action(self): - cmd = [self.action, self.params['name']] - all_param_methods = [func for func in dir(self) - if callable(getattr(self, func)) - and func.startswith("addparam")] + cmd = [self.action, self.params["name"]] + all_param_methods = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("addparam") + ] params_set = (i for i in self.params if self.params[i] is not None) for param in params_set: func_name = "_".join(["addparam", param]) if func_name in all_param_methods: cmd = getattr(self, func_name)(cmd) - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def check_version(self, param, minv=None, maxv=None): - if minv and LooseVersion(minv) > LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported from podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) - if maxv and LooseVersion(maxv) < LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported till podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) + if minv and LooseVersion(minv) > LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) + if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) def addparam_gateway(self, c): - return c + ['--gateway', self.params['gateway']] + return c + ["--gateway", self.params["gateway"]] def addparam_dns(self, c): - for dns in self.params['dns']: - c += ['--dns', dns] + for dns in self.params["dns"]: + c += ["--dns", dns] return c def addparam_driver(self, c): - return c + ['--driver', self.params['driver']] + return c + ["--driver", self.params["driver"]] def addparam_subnet(self, c): - return c + ['--subnet', self.params['subnet']] + return c + ["--subnet", self.params["subnet"]] def addparam_ip_range(self, c): - return c + ['--ip-range', self.params['ip_range']] + return c + ["--ip-range", self.params["ip_range"]] def addparam_ipv6(self, c): - return c + ['--ipv6=%s' % self.params['ipv6']] + return c + ["--ipv6=%s" % self.params["ipv6"]] def addparam_macvlan(self, c): - return c + ['--macvlan', self.params['macvlan']] + 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'): + 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])] + c += ["--%s=%s" % (kw.replace("_", "-"), net[kw])] return c def addparam_interface_name(self, c): - return c + ['--interface-name', self.params['interface_name']] + return c + ["--interface-name", self.params["interface_name"]] def addparam_internal(self, c): - return c + ['--internal=%s' % self.params['internal']] + return c + ["--internal=%s" % self.params["internal"]] def addparam_opt(self, c): - for opt in self.params['opt'].items(): + for opt in self.params["opt"].items(): if opt[1] is not None: - if opt[0] == 'bridge_name': - opt = ('com.docker.network.bridge.name', opt[1]) - if opt[0] == 'driver_mtu': - opt = ('com.docker.network.driver.mtu', opt[1]) - c += ['--opt', - b"=".join([to_bytes(k, errors='surrogate_or_strict') - for k in opt])] + if opt[0] == "bridge_name": + opt = ("com.docker.network.bridge.name", opt[1]) + if opt[0] == "driver_mtu": + opt = ("com.docker.network.driver.mtu", opt[1]) + c += [ + "--opt", + b"=".join([to_bytes(k, errors="surrogate_or_strict") for k in opt]), + ] return c def addparam_route(self, c): - for route in self.params['route']: - c += ['--route', route] + 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']] + return c + ["--ipam-driver=%s" % self.params["ipam_driver"]] def addparam_disable_dns(self, c): - return c + ['--disable-dns=%s' % self.params['disable_dns']] + return c + ["--disable-dns=%s" % self.params["disable_dns"]] class PodmanNetworkDefaults: @@ -429,8 +443,8 @@ class PodmanNetworkDefaults: self.module = module self.version = podman_version self.defaults = { - 'driver': 'bridge', - 'internal': False, + "driver": "bridge", + "internal": False, } def default_dict(self): @@ -445,13 +459,14 @@ class PodmanNetworkDiff: self.default_dict = None self.info = lower_keys(info) self.params = self.defaultize() - self.diff = {'before': {}, 'after': {}} + self.diff = {"before": {}, "after": {}} self.non_idempotent = {} def defaultize(self): params_with_defaults = {} self.default_dict = PodmanNetworkDefaults( - self.module, self.version).default_dict() + self.module, self.version + ).default_dict() for p in self.module.params: if self.module.params[p] is None and p in self.default_dict: params_with_defaults[p] = self.default_dict[p] @@ -461,171 +476,189 @@ class PodmanNetworkDiff: def _diff_update_and_compare(self, param_name, before, after): if before != after: - self.diff['before'].update({param_name: before}) - self.diff['after'].update({param_name: after}) + self.diff["before"].update({param_name: before}) + self.diff["after"].update({param_name: after}) return True return False def diffparam_disable_dns(self): # For v3 it's impossible to find out DNS settings. - if LooseVersion(self.version) >= LooseVersion('4.0.0'): - before = not self.info.get('dns_enabled', True) - after = self.params['disable_dns'] + if LooseVersion(self.version) >= LooseVersion("4.0.0"): + before = not self.info.get("dns_enabled", True) + after = self.params["disable_dns"] # compare only if set explicitly - if self.params['disable_dns'] is None: + if self.params["disable_dns"] is None: after = before - return self._diff_update_and_compare('disable_dns', before, after) - before = after = self.params['disable_dns'] - return self._diff_update_and_compare('disable_dns', before, after) + return self._diff_update_and_compare("disable_dns", before, after) + 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)) + 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) + before = after = "bridge" + return self._diff_update_and_compare("driver", before, after) def diffparam_ipv6(self): # We don't support dual stack because it generates subnets randomly - return self._diff_update_and_compare('ipv6', '', '') + 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'): + if LooseVersion(self.version) < LooseVersion("4.0.0"): try: - before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway'] + before = self.info["plugins"][0]["ipam"]["ranges"][0][0]["gateway"] except (IndexError, KeyError): - before = '' + before = "" after = before - if self.params['gateway'] is not None: - after = self.params['gateway'] - return self._diff_update_and_compare('gateway', before, after) + 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'] + before_subs = self.info.get("subnets") + after = self.params["gateway"] 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] + "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) + return self._diff_update_and_compare("gateway", before, after) def diffparam_internal(self): - if LooseVersion(self.version) >= LooseVersion('4.0.0'): - before = self.info.get('internal', False) - after = self.params['internal'] - return self._diff_update_and_compare('internal', before, after) + if LooseVersion(self.version) >= LooseVersion("4.0.0"): + before = self.info.get("internal", False) + after = self.params["internal"] + return self._diff_update_and_compare("internal", before, after) try: - before = not self.info['plugins'][0]['isgateway'] + before = not self.info["plugins"][0]["isgateway"] except (IndexError, KeyError): before = False - after = self.params['internal'] - return self._diff_update_and_compare('internal', before, after) + after = self.params["internal"] + return self._diff_update_and_compare("internal", before, after) def diffparam_ip_range(self): # TODO(sshnaidm): implement IP to CIDR convert and vice versa - before = after = '' - return self._diff_update_and_compare('ip_range', before, after) + before = after = "" + return self._diff_update_and_compare("ip_range", before, after) def diffparam_ipam_driver(self): before = self.info.get("ipam_options", {}).get("driver", "") - after = self.params['ipam_driver'] + after = self.params["ipam_driver"] if not after: after = before - return self._diff_update_and_compare('ipam_driver', before, after) + return self._diff_update_and_compare("ipam_driver", before, after) def diffparam_net_config(self): - after = self.params['net_config'] + after = self.params["net_config"] if not after: - return self._diff_update_and_compare('net_config', '', '') - before_subs = self.info.get('subnets', []) + 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])) + before = ":".join( + sorted( + [ + ",".join([i["subnet"], i["gateway"]]).rstrip(",") + for i in before_subs + ] + ) + ) else: - before = '' - after = ":".join(sorted([",".join([i['subnet'], i['gateway']]).rstrip(",") for i in after])) - return self._diff_update_and_compare('net_config', before, after) + before = "" + 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', []) + routes = self.info.get("routes", []) if routes: - before = [",".join([ - r['destination'], r['gateway'], str(r.get('metric', ''))]).rstrip(",") for r in 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)) + 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'): + if LooseVersion(self.version) < LooseVersion("4.0.0"): try: - before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet'] + before = self.info["plugins"][0]["ipam"]["ranges"][0][0]["subnet"] except (IndexError, KeyError): - before = '' + before = "" after = before - if self.params['subnet'] is not None: - after = self.params['subnet'] + 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) + return self._diff_update_and_compare("subnet", before, after) else: - if self.params['ipv6'] is not None: + 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'] + return self._diff_update_and_compare("subnet", "", "") + after = self.params["subnet"] 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') + 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) + 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 = '' - return self._diff_update_and_compare('macvlan', before, after) + before = after = "" + return self._diff_update_and_compare("macvlan", before, after) def diffparam_opt(self): - if LooseVersion(self.version) >= LooseVersion('4.0.0'): - vlan_before = self.info.get('options', {}).get('vlan') + if LooseVersion(self.version) >= LooseVersion("4.0.0"): + vlan_before = self.info.get("options", {}).get("vlan") else: try: - vlan_before = self.info['plugins'][0].get('vlan') + vlan_before = self.info["plugins"][0].get("vlan") except (IndexError, KeyError): vlan_before = None - vlan_after = self.params['opt'].get('vlan') if self.params['opt'] else None + vlan_after = self.params["opt"].get("vlan") if self.params["opt"] else None if vlan_before or vlan_after: - before, after = {'vlan': str(vlan_before)}, {'vlan': str(vlan_after)} + before, after = {"vlan": str(vlan_before)}, {"vlan": str(vlan_after)} else: before, after = {}, {} - if LooseVersion(self.version) >= LooseVersion('4.0.0'): - mtu_before = self.info.get('options', {}).get('mtu') + if LooseVersion(self.version) >= LooseVersion("4.0.0"): + mtu_before = self.info.get("options", {}).get("mtu") else: try: - mtu_before = self.info['plugins'][0].get('mtu') + mtu_before = self.info["plugins"][0].get("mtu") except (IndexError, KeyError): mtu_before = None - mtu_after = self.params['opt'].get('mtu') if self.params['opt'] else None + mtu_after = self.params["opt"].get("mtu") if self.params["opt"] else None if mtu_before or mtu_after: - before.update({'mtu': str(mtu_before)}) - after.update({'mtu': str(mtu_after)}) - return self._diff_update_and_compare('opt', before, after) + before.update({"mtu": str(mtu_before)}) + after.update({"mtu": str(mtu_after)}) + return self._diff_update_and_compare("opt", before, after) def is_different(self): - diff_func_list = [func for func in dir(self) - if callable(getattr(self, func)) and func.startswith( - "diffparam")] + diff_func_list = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("diffparam") + ] fail_fast = not bool(self.module._diff) different = False for func_name in diff_func_list: @@ -636,7 +669,11 @@ class PodmanNetworkDiff: different = True # Check non idempotent parameters for p in self.non_idempotent: - if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']: + if self.module.params[p] is not None and self.module.params[p] not in [ + {}, + [], + "", + ]: different = True return different @@ -658,7 +695,7 @@ class PodmanNetwork: super(PodmanNetwork, self).__init__() self.module = module self.name = name - self.stdout, self.stderr = '', '' + self.stdout, self.stderr = "", "" self.info = self.get_info() self.version = self._get_podman_version() self.diff = {} @@ -672,35 +709,41 @@ class PodmanNetwork: @property def different(self): """Check if network is different.""" - diffcheck = PodmanNetworkDiff( - self.module, - self.info, - self.version) + diffcheck = PodmanNetworkDiff(self.module, self.info, self.version) is_different = diffcheck.is_different() diffs = diffcheck.diff - if self.module._diff and is_different and diffs['before'] and diffs['after']: - self.diff['before'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['before'].items())]) + "\n" - self.diff['after'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['after'].items())]) + "\n" + if self.module._diff and is_different and diffs["before"] and diffs["after"]: + self.diff["before"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())] + ) + + "\n" + ) + self.diff["after"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())] + ) + + "\n" + ) return is_different def get_info(self): """Inspect network and gather info about it.""" # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module.params['executable'], b'network', b'inspect', self.name]) + [self.module.params["executable"], b"network", b"inspect", self.name] + ) return json.loads(out)[0] if rc == 0 else {} def _get_podman_version(self): # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module.params['executable'], b'--version']) + [self.module.params["executable"], b"--version"] + ) if rc != 0 or not out or "version" not in out: - self.module.fail_json(msg="%s run failed!" % - self.module.params['executable']) + self.module.fail_json( + msg="%s run failed!" % self.module.params["executable"] + ) return out.split("version")[1].strip() def _perform_action(self, action): @@ -709,33 +752,39 @@ class PodmanNetwork: Arguments: action {str} -- action to perform - create, stop, delete """ - b_command = PodmanNetworkModuleParams(action, - self.module.params, - self.version, - self.module, - ).construct_command_from_params() - full_cmd = " ".join([self.module.params['executable'], 'network'] - + [to_native(i) for i in b_command]) + b_command = PodmanNetworkModuleParams( + action, + self.module.params, + self.version, + self.module, + ).construct_command_from_params() + full_cmd = " ".join( + [self.module.params["executable"], "network"] + + [to_native(i) for i in b_command] + ) self.module.log("PODMAN-NETWORK-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'network'] + b_command, - expand_user_and_vars=False) + [self.module.params["executable"], b"network"] + b_command, + expand_user_and_vars=False, + ) self.stdout = out self.stderr = err if rc != 0: self.module.fail_json( msg="Can't %s network %s" % (action, self.name), - stdout=out, stderr=err) + stdout=out, + stderr=err, + ) def delete(self): """Delete the network.""" - self._perform_action('delete') + self._perform_action("delete") def create(self): """Create the network.""" - self._perform_action('create') + self._perform_action("create") def recreate(self): """Recreate the network.""" @@ -760,16 +809,16 @@ class PodmanNetworkManager: self.module = module self.results = { - 'changed': False, - 'actions': [], - 'network': {}, + "changed": False, + "actions": [], + "network": {}, } - self.name = self.module.params['name'] - self.executable = \ - self.module.get_bin_path(self.module.params['executable'], - required=True) - self.state = self.module.params['state'] - self.recreate = self.module.params['recreate'] + self.name = self.module.params["name"] + self.executable = self.module.get_bin_path( + self.module.params["executable"], required=True + ) + self.state = self.module.params["state"] + self.recreate = self.module.params["recreate"] self.network = PodmanNetwork(self.module, self.name) def update_network_result(self, changed=True): @@ -781,37 +830,43 @@ class PodmanNetworkManager: """ facts = self.network.get_info() if changed else self.network.info out, err = self.network.stdout, self.network.stderr - self.results.update({'changed': changed, 'network': facts, - 'podman_actions': self.network.actions}, - stdout=out, stderr=err) + self.results.update( + { + "changed": changed, + "network": facts, + "podman_actions": self.network.actions, + }, + stdout=out, + stderr=err, + ) if self.network.diff: - self.results.update({'diff': self.network.diff}) - if self.module.params['debug']: - self.results.update({'podman_version': self.network.version}) + self.results.update({"diff": self.network.diff}) + if self.module.params["debug"]: + self.results.update({"podman_version": self.network.version}) self.module.exit_json(**self.results) def execute(self): """Execute the desired action according to map of actions & states.""" states_map = { - 'present': self.make_present, - 'absent': self.make_absent, - 'quadlet': self.make_quadlet, + "present": self.make_present, + "absent": self.make_absent, + "quadlet": self.make_quadlet, } process_action = states_map[self.state] process_action() - self.module.fail_json(msg="Unexpected logic error happened, " - "please contact maintainers ASAP!") + self.module.fail_json( + msg="Unexpected logic error happened, " "please contact maintainers ASAP!" + ) def make_present(self): """Run actions if desired state is 'started'.""" if not self.network.exists: self.network.create() - self.results['actions'].append('created %s' % self.network.name) + self.results["actions"].append("created %s" % self.network.name) self.update_network_result() elif self.recreate or self.network.different: self.network.recreate() - self.results['actions'].append('recreated %s' % - self.network.name) + self.results["actions"].append("recreated %s" % self.network.name) self.update_network_result() else: self.update_network_result(changed=False) @@ -819,13 +874,12 @@ class PodmanNetworkManager: def make_absent(self): """Run actions if desired state is 'absent'.""" if not self.network.exists: - self.results.update({'changed': False}) + self.results.update({"changed": False}) elif self.network.exists: self.network.delete() - self.results['actions'].append('deleted %s' % self.network.name) - self.results.update({'changed': True}) - self.results.update({'network': {}, - 'podman_actions': self.network.actions}) + self.results["actions"].append("deleted %s" % self.network.name) + self.results.update({"changed": True}) + self.results.update({"network": {}, "podman_actions": self.network.actions}) self.module.exit_json(**self.results) def make_quadlet(self): @@ -837,60 +891,70 @@ class PodmanNetworkManager: def main(): module = AnsibleModule( argument_spec=dict( - state=dict(type='str', default="present", - 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), - opt=dict(type='dict', required=False, - options=dict( - isolate=dict(type='bool', required=False), - mtu=dict(type='int', required=False), - metric=dict(type='int', required=False), - mode=dict(type='str', required=False), - parent=dict(type='str', required=False), - vlan=dict(type='int', required=False), - bclim=dict(type='int', required=False), - no_default_route=dict(type='str', required=False), - vrf=dict(type='str', required=False), - bridge_name=dict(type='str', required=False), - driver_mtu=dict(type='str', required=False), - )), - 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_file_mode=dict(type='raw', 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), - )), + state=dict( + type="str", default="present", 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), + opt=dict( + type="dict", + required=False, + options=dict( + isolate=dict(type="bool", required=False), + mtu=dict(type="int", required=False), + metric=dict(type="int", required=False), + mode=dict(type="str", required=False), + parent=dict(type="str", required=False), + vlan=dict(type="int", required=False), + bclim=dict(type="int", required=False), + no_default_route=dict(type="str", required=False), + vrf=dict(type="str", required=False), + bridge_name=dict(type="str", required=False), + driver_mtu=dict(type="str", required=False), + ), + ), + 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_file_mode=dict(type="raw", 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'), + ip_range=("subnet"), + gateway=("subnet"), ), # define or subnet or net config - mutually_exclusive=[['subnet', 'net_config']]) + mutually_exclusive=[["subnet", "net_config"]], + ) PodmanNetworkManager(module).execute() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_network_info.py b/plugins/modules/podman_network_info.py index e5ab7f8..7695869 100644 --- a/plugins/modules/podman_network_info.py +++ b/plugins/modules/podman_network_info.py @@ -3,6 +3,7 @@ # 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 @@ -82,9 +83,9 @@ from ansible.module_utils.basic import AnsibleModule def get_network_info(module, executable, name): - command = [executable, 'network', 'inspect'] + command = [executable, "network", "inspect"] if not name: - all_names = [executable, 'network', 'ls', '-q'] + all_names = [executable, "network", "ls", "-q"] rc, out, err = module.run_command(all_names) if rc != 0: module.fail_json(msg="Unable to get list of networks: %s" % err) @@ -95,7 +96,7 @@ def get_network_info(module, executable, name): else: command.append(name) rc, out, err = module.run_command(command) - if rc != 0 or 'unable to find network configuration' in err: + if rc != 0 or "unable to find network configuration" in err: module.fail_json(msg="Unable to gather info for %s: %s" % (name, err)) if not out or json.loads(out) is None: return [], out, err @@ -105,25 +106,20 @@ def get_network_info(module, executable, name): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - name=dict(type='str') + executable=dict(type="str", default="podman"), name=dict(type="str") ), supports_check_mode=True, ) - name = module.params['name'] - executable = module.get_bin_path(module.params['executable'], required=True) + name = module.params["name"] + executable = module.get_bin_path(module.params["executable"], required=True) inspect_results, out, err = get_network_info(module, executable, name) - results = { - "changed": False, - "networks": inspect_results, - "stderr": err - } + results = {"changed": False, "networks": inspect_results, "stderr": err} module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_play.py b/plugins/modules/podman_play.py index f9ae3a6..0dc8288 100644 --- a/plugins/modules/podman_play.py +++ b/plugins/modules/podman_play.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_play author: - "Sagi Shnaidman (@sshnaidm)" @@ -195,9 +195,9 @@ options: type: list elements: str required: false -''' +""" -EXAMPLES = ''' +EXAMPLES = """ - name: Play kube file containers.podman.podman_play: kube_file: ~/kube.yaml @@ -229,17 +229,24 @@ EXAMPLES = ''' quadlet_options: - "SetWorkingDirectory=yaml" - "ExitCodePropagation=any" -''' +""" import re # noqa: F402 + try: import yaml + HAS_YAML = True except ImportError: HAS_YAML = False from ansible.module_utils.basic import AnsibleModule # noqa: F402 -from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion, get_podman_version -from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state # noqa: F402 +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + LooseVersion, + get_podman_version, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ( + create_quadlet_state, +) # noqa: F402 class PodmanKubeManagement: @@ -248,115 +255,127 @@ class PodmanKubeManagement: self.module = module self.actions = [] self.executable = executable - self.command = [self.executable, 'play', 'kube'] + self.command = [self.executable, "play", "kube"] self.version = get_podman_version(module) creds = [] # pod_name = extract_pod_name(module.params['kube_file']) - if self.module.params['annotation']: - for k, v in self.module.params['annotation'].items(): - self.command.extend(['--annotation', '{k}={v}'.format(k=k, v=v)]) - if self.module.params['username']: - creds += [self.module.params['username']] - if self.module.params['password']: - creds += [self.module.params['password']] + if self.module.params["annotation"]: + for k, v in self.module.params["annotation"].items(): + self.command.extend(["--annotation", "{k}={v}".format(k=k, v=v)]) + if self.module.params["username"]: + creds += [self.module.params["username"]] + if self.module.params["password"]: + creds += [self.module.params["password"]] creds = ":".join(creds) - self.command.extend(['--creds=%s' % creds]) - if self.module.params['network']: - networks = ",".join(self.module.params['network']) - self.command.extend(['--network=%s' % networks]) - if self.module.params['configmap']: - configmaps = ",".join(self.module.params['configmap']) - self.command.extend(['--configmap=%s' % configmaps]) - if self.module.params['log_opt']: - for k, v in self.module.params['log_opt'].items(): - self.command.extend(['--log-opt', '{k}={v}'.format(k=k.replace('_', '-'), v=v)]) - start = self.module.params['state'] == 'started' - self.command.extend(['--start=%s' % str(start).lower()]) + self.command.extend(["--creds=%s" % creds]) + if self.module.params["network"]: + networks = ",".join(self.module.params["network"]) + self.command.extend(["--network=%s" % networks]) + if self.module.params["configmap"]: + configmaps = ",".join(self.module.params["configmap"]) + self.command.extend(["--configmap=%s" % configmaps]) + if self.module.params["log_opt"]: + for k, v in self.module.params["log_opt"].items(): + self.command.extend( + ["--log-opt", "{k}={v}".format(k=k.replace("_", "-"), v=v)] + ) + start = self.module.params["state"] == "started" + self.command.extend(["--start=%s" % str(start).lower()]) for arg, param in { - '--authfile': 'authfile', - '--build': 'build', - '--cert-dir': 'cert_dir', - '--context-dir': 'context_dir', - '--log-driver': 'log_driver', - '--seccomp-profile-root': 'seccomp_profile_root', - '--tls-verify': 'tls_verify', - '--log-level': 'log_level', - '--userns': 'userns', - '--quiet': 'quiet', + "--authfile": "authfile", + "--build": "build", + "--cert-dir": "cert_dir", + "--context-dir": "context_dir", + "--log-driver": "log_driver", + "--seccomp-profile-root": "seccomp_profile_root", + "--tls-verify": "tls_verify", + "--log-level": "log_level", + "--userns": "userns", + "--quiet": "quiet", }.items(): if self.module.params[param] is not None: self.command += ["%s=%s" % (arg, self.module.params[param])] - if self.module.params['kube_file']: - self.command += [self.module.params['kube_file']] - elif self.module.params['kube_file_content']: - self.command += ['-'] + if self.module.params["kube_file"]: + self.command += [self.module.params["kube_file"]] + elif self.module.params["kube_file_content"]: + self.command += ["-"] def _command_run(self, cmd): - if self.module.params['kube_file_content']: - rc, out, err = self.module.run_command(cmd, data=self.module.params['kube_file_content']) + if self.module.params["kube_file_content"]: + rc, out, err = self.module.run_command( + cmd, data=self.module.params["kube_file_content"] + ) else: rc, out, err = self.module.run_command(cmd) self.actions.append(" ".join(cmd)) - if self.module.params['debug']: - self.module.log('PODMAN-PLAY-KUBE command: %s' % " ".join(cmd)) - self.module.log('PODMAN-PLAY-KUBE stdout: %s' % out) - self.module.log('PODMAN-PLAY-KUBE stderr: %s' % err) - self.module.log('PODMAN-PLAY-KUBE rc: %s' % rc) + if self.module.params["debug"]: + self.module.log("PODMAN-PLAY-KUBE command: %s" % " ".join(cmd)) + self.module.log("PODMAN-PLAY-KUBE stdout: %s" % out) + self.module.log("PODMAN-PLAY-KUBE stderr: %s" % err) + self.module.log("PODMAN-PLAY-KUBE rc: %s" % rc) return rc, out, err def tear_down_pods(self): - ''' + """ Tear down the pod and contaiers by using --down option in kube play which is supported since Podman 3.4.0 - ''' + """ changed = False - kube_file = self.module.params['kube_file'] - kube_file_content = self.module.params['kube_file_content'] + kube_file = self.module.params["kube_file"] + kube_file_content = self.module.params["kube_file_content"] if kube_file: - rc, out, err = self._command_run([self.executable, "kube", "play", "--down", kube_file]) + rc, out, err = self._command_run( + [self.executable, "kube", "play", "--down", kube_file] + ) elif kube_file_content: - rc, out, err = self._command_run([self.executable, "kube", "play", "--down", "-"]) + rc, out, err = self._command_run( + [self.executable, "kube", "play", "--down", "-"] + ) if rc != 0 and "no such pod" in err: changed = False return changed, out, err if rc != 0: - self.module.fail_json(msg="Failed to delete Pod with %s: %s %s" % ( - kube_file if kube_file else "YAML content", out, err)) + self.module.fail_json( + msg="Failed to delete Pod with %s: %s %s" + % (kube_file if kube_file else "YAML content", out, err) + ) # hack to check if no resources are deleted for line in out.splitlines(): - if line and not line.endswith(':'): + if line and not line.endswith(":"): changed = True break return changed, out, err def discover_pods(self): - pod_name = '' - if self.module.params['kube_file']: + pod_name = "" + if self.module.params["kube_file"]: if HAS_YAML: - with open(self.module.params['kube_file']) as f: + with open(self.module.params["kube_file"]) as f: pods = list(yaml.safe_load_all(f)) for pod in pods: - if 'metadata' in pod and pod['kind'] in ['Deployment', 'Pod']: - pod_name = pod['metadata'].get('name') + if "metadata" in pod and pod["kind"] in ["Deployment", "Pod"]: + pod_name = pod["metadata"].get("name") else: - with open(self.module.params['kube_file']) as text: + with open(self.module.params["kube_file"]) as text: # the following formats are matched for a kube name: # should match name field within metadata (2 or 4 spaces in front of name) # the name can be written without quotes, in single or double quotes # the name can contain -_ - re_pod_name = re.compile(r'^\s{2,4}name: ["|\']?(?P[\w|\-|\_]+)["|\']?', re.MULTILINE) + re_pod_name = re.compile( + r'^\s{2,4}name: ["|\']?(?P[\w|\-|\_]+)["|\']?', + re.MULTILINE, + ) re_pod = re_pod_name.search(text.read()) if re_pod: pod_name = re_pod.group(1) if not pod_name: self.module.fail_json("This Kube file doesn't have Pod or Deployment!") # Find all pods - all_pods = '' + all_pods = "" # In case of one pod or replicasets for name in ("name=%s$", "name=%s-pod-*"): - cmd = [self.executable, - "pod", "ps", "-q", "--filter", name % pod_name] + cmd = [self.executable, "pod", "ps", "-q", "--filter", name % pod_name] rc, out, err = self._command_run(cmd) all_pods += out ids = list(set([i for i in all_pods.splitlines() if i])) @@ -364,11 +383,12 @@ class PodmanKubeManagement: def remove_associated_pods(self, pods): changed = False - out_all, err_all = '', '' + out_all, err_all = "", "" # Delete all pods for pod_id in pods: rc, out, err = self._command_run( - [self.executable, "pod", "rm", "-f", pod_id]) + [self.executable, "pod", "rm", "-f", pod_id] + ) if rc != 0: self.module.fail_json("Can NOT delete Pod %s" % pod_id) else: @@ -378,7 +398,9 @@ class PodmanKubeManagement: return changed, out_all, err_all def pod_recreate(self): - if self.version is not None and LooseVersion(self.version) >= LooseVersion('3.4.0'): + if self.version is not None and LooseVersion(self.version) >= LooseVersion( + "3.4.0" + ): self.tear_down_pods() else: pods = self.discover_pods() @@ -392,14 +414,15 @@ class PodmanKubeManagement: def play(self): rc, out, err = self._command_run(self.command) - if rc != 0 and 'pod already exists' in err: - if self.module.params['recreate']: + if rc != 0 and "pod already exists" in err: + if self.module.params["recreate"]: out, err = self.pod_recreate() changed = True else: changed = False - err = "\n".join([ - i for i in err.splitlines() if 'pod already exists' not in i]) + err = "\n".join( + [i for i in err.splitlines() if "pod already exists" not in i] + ) elif rc != 0: self.module.fail_json(msg="Output: %s\nError=%s" % (out, err)) else: @@ -416,63 +439,73 @@ class PodmanKubeManagement: def main(): module = AnsibleModule( argument_spec=dict( - annotation=dict(type='dict', aliases=['annotations']), - executable=dict(type='str', default='podman'), - kube_file=dict(type='path'), - kube_file_content=dict(type='str'), - authfile=dict(type='path'), - build=dict(type='bool'), - cert_dir=dict(type='path'), - configmap=dict(type='list', elements='path'), - context_dir=dict(type='path'), - seccomp_profile_root=dict(type='path'), - username=dict(type='str'), - password=dict(type='str', no_log=True), - log_driver=dict(type='str'), - log_opt=dict(type='dict', aliases=['log_options'], options=dict( - path=dict(type='str'), - max_size=dict(type='str'), - tag=dict(type='str'))), - network=dict(type='list', elements='str'), + annotation=dict(type="dict", aliases=["annotations"]), + executable=dict(type="str", default="podman"), + kube_file=dict(type="path"), + kube_file_content=dict(type="str"), + authfile=dict(type="path"), + build=dict(type="bool"), + cert_dir=dict(type="path"), + configmap=dict(type="list", elements="path"), + context_dir=dict(type="path"), + seccomp_profile_root=dict(type="path"), + username=dict(type="str"), + password=dict(type="str", no_log=True), + log_driver=dict(type="str"), + log_opt=dict( + type="dict", + aliases=["log_options"], + options=dict( + path=dict(type="str"), + max_size=dict(type="str"), + tag=dict(type="str"), + ), + ), + network=dict(type="list", elements="str"), state=dict( - type='str', - choices=['started', 'created', 'absent', 'quadlet'], - required=True), - tls_verify=dict(type='bool'), - debug=dict(type='bool'), - quiet=dict(type='bool'), - recreate=dict(type='bool'), - userns=dict(type='str'), + type="str", + choices=["started", "created", "absent", "quadlet"], + required=True, + ), + tls_verify=dict(type="bool"), + debug=dict(type="bool"), + quiet=dict(type="bool"), + recreate=dict(type="bool"), + userns=dict(type="str"), log_level=dict( - type='str', - choices=["debug", "info", "warn", "error", "fatal", "panic"]), - quadlet_dir=dict(type='path', required=False), - quadlet_filename=dict(type='str', required=False), - quadlet_file_mode=dict(type='raw', required=False), - quadlet_options=dict(type='list', elements='str', required=False), + type="str", choices=["debug", "info", "warn", "error", "fatal", "panic"] + ), + quadlet_dir=dict(type="path", required=False), + quadlet_filename=dict(type="str", required=False), + quadlet_file_mode=dict(type="raw", required=False), + quadlet_options=dict(type="list", elements="str", required=False), ), supports_check_mode=True, required_if=[ - ('state', 'quadlet', ['quadlet_filename']), + ("state", "quadlet", ["quadlet_filename"]), ], required_one_of=[ - ('kube_file', 'kube_file_content'), + ("kube_file", "kube_file_content"), ], ) - executable = module.get_bin_path( - module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) manage = PodmanKubeManagement(module, executable) changed = False - out = err = '' - if module.params['state'] == 'absent': - if manage.version is not None and LooseVersion(manage.version) > LooseVersion('3.4.0'): - manage.module.log(msg="version: %s, kube file %s" % (manage.version, manage.module.params['kube_file'])) + out = err = "" + if module.params["state"] == "absent": + if manage.version is not None and LooseVersion(manage.version) > LooseVersion( + "3.4.0" + ): + manage.module.log( + msg="version: %s, kube file %s" + % (manage.version, manage.module.params["kube_file"]) + ) changed, out, err = manage.tear_down_pods() else: pods = manage.discover_pods() changed, out, err = manage.remove_associated_pods(pods) - elif module.params['state'] == 'quadlet': + elif module.params["state"] == "quadlet": manage.make_quadlet() else: changed, out, err = manage.play() @@ -480,10 +513,10 @@ def main(): "changed": changed, "stdout": out, "stderr": err, - "actions": manage.actions + "actions": manage.actions, } module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_pod.py b/plugins/modules/podman_pod.py index 04fc81f..ccec9a4 100644 --- a/plugins/modules/podman_pod.py +++ b/plugins/modules/podman_pod.py @@ -4,9 +4,10 @@ # flake8: noqa: E501 from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: podman_pod short_description: Manage Podman pods @@ -500,9 +501,9 @@ options: requirements: - "podman" -''' +""" -RETURN = r''' +RETURN = r""" pod: description: Pod inspection results for the given pod built. @@ -611,9 +612,9 @@ pod: ], "LockNumber": 1 } -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # What modules does for example - containers.podman.podman_pod: name: pod1 @@ -693,7 +694,7 @@ EXAMPLES = r''' volume: - /var/run/docker.sock:/var/run/docker.sock quadlet_dir: /custom/dir -''' +""" from ansible.module_utils.basic import AnsibleModule # noqa: F402 from ..module_utils.podman.podman_pod_lib import PodmanPodManager # noqa: F402 from ..module_utils.podman.podman_pod_lib import ARGUMENTS_SPEC_POD # noqa: F402 @@ -705,5 +706,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_pod_info.py b/plugins/modules/podman_pod_info.py index 3201c16..ab1495c 100644 --- a/plugins/modules/podman_pod_info.py +++ b/plugins/modules/podman_pod_info.py @@ -3,6 +3,7 @@ # 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 @@ -155,13 +156,13 @@ from ansible.module_utils.basic import AnsibleModule def get_pod_info(module, executable, name): - command = [executable, 'pod', 'inspect'] + command = [executable, "pod", "inspect"] pods = [name] result = [] errs = [] rcs = [] if not name: - all_names = [executable, 'pod', 'ls', '-q'] + all_names = [executable, "pod", "ls", "-q"] rc, out, err = module.run_command(all_names) if rc != 0: module.fail_json(msg="Unable to get list of pods: %s" % err) @@ -185,14 +186,13 @@ def get_pod_info(module, executable, name): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - name=dict(type='str') + executable=dict(type="str", default="podman"), name=dict(type="str") ), supports_check_mode=True, ) - name = module.params['name'] - executable = module.get_bin_path(module.params['executable'], required=True) + name = module.params["name"] + executable = module.get_bin_path(module.params["executable"], required=True) inspect_results, errs, rcs = get_pod_info(module, executable, name) @@ -208,5 +208,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_prune.py b/plugins/modules/podman_prune.py index 3fe3b75..ab936a3 100644 --- a/plugins/modules/podman_prune.py +++ b/plugins/modules/podman_prune.py @@ -5,9 +5,10 @@ # Copyright (c) 2023, Roberto Alfieri from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_prune author: - 'Roberto Alfieri (@rebtoor)' @@ -91,9 +92,9 @@ options: https://docs.podman.io/en/latest/markdown/podman-volume-prune.1.html#filter) for more information on possible filters. type: dict -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" - name: Prune containers older than 24h containers.podman.podman_prune: containers: true @@ -110,9 +111,9 @@ EXAMPLES = r''' system: true system_all: true system_volumes: true -''' +""" -RETURN = r''' +RETURN = r""" # containers containers: description: @@ -157,7 +158,7 @@ system: type: list elements: str sample: [] -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -165,7 +166,7 @@ from ansible.module_utils.basic import AnsibleModule def filtersPrepare(target, filters): filter_out = [] - if target == 'system': + if target == "system": for system_filter in filters: filter_out.append(filters[system_filter]) else: @@ -173,23 +174,36 @@ def filtersPrepare(target, filters): if isinstance(filters[common_filter], dict): dict_filters = filters[common_filter] for single_filter in dict_filters: - filter_out.append('--filter={label}={key}={value}'.format(label=common_filter, key=single_filter, - value=dict_filters[single_filter])) + filter_out.append( + "--filter={label}={key}={value}".format( + label=common_filter, + key=single_filter, + value=dict_filters[single_filter], + ) + ) else: - if target == 'image' and (common_filter in ('dangling_only', 'external')): - if common_filter == 'dangling_only' and not filters['dangling_only']: - filter_out.append('-a') - if common_filter == 'external' and filters['external']: - filter_out.append('--external') + if target == "image" and ( + common_filter in ("dangling_only", "external") + ): + if ( + common_filter == "dangling_only" + and not filters["dangling_only"] + ): + filter_out.append("-a") + if common_filter == "external" and filters["external"]: + filter_out.append("--external") else: - filter_out.append('--filter={label}={value}'.format(label=common_filter, - value=filters[common_filter])) + filter_out.append( + "--filter={label}={value}".format( + label=common_filter, value=filters[common_filter] + ) + ) return filter_out def podmanExec(module, target, filters, executable): - command = [executable, target, 'prune', '--force'] + command = [executable, target, "prune", "--force"] if filters is not None: command.extend(filtersPrepare(target, filters)) rc, out, err = module.run_command(command) @@ -197,56 +211,61 @@ def podmanExec(module, target, filters, executable): if rc != 0: module.fail_json( - msg='Error executing prune on {target}: {err}'.format(target=target, err=err)) + msg="Error executing prune on {target}: {err}".format( + target=target, err=err + ) + ) return { "changed": changed, - target: list(filter(None, out.split('\n'))), - "errors": err + target: list(filter(None, out.split("\n"))), + "errors": err, } def main(): results = dict() module_args = dict( - container=dict(type='bool', default=False), - container_filters=dict(type='dict'), - image=dict(type='bool', default=False), - image_filters=dict(type='dict'), - network=dict(type='bool', default=False), - network_filters=dict(type='dict'), - volume=dict(type='bool', default=False), - volume_filters=dict(type='dict'), - system=dict(type='bool', default=False), - system_all=dict(type='bool', default=False), - system_volumes=dict(type='bool', default=False), - executable=dict(type='str', default='podman') + container=dict(type="bool", default=False), + container_filters=dict(type="dict"), + image=dict(type="bool", default=False), + image_filters=dict(type="dict"), + network=dict(type="bool", default=False), + network_filters=dict(type="dict"), + volume=dict(type="bool", default=False), + volume_filters=dict(type="dict"), + system=dict(type="bool", default=False), + system_all=dict(type="bool", default=False), + system_volumes=dict(type="bool", default=False), + executable=dict(type="str", default="podman"), ) - module = AnsibleModule( - argument_spec=module_args - ) + module = AnsibleModule(argument_spec=module_args) - executable = module.get_bin_path( - module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) for target, filters in ( - ('container', 'container_filters'), ('image', 'image_filters'), ('network', 'network_filters'), - ('volume', 'volume_filters')): + ("container", "container_filters"), + ("image", "image_filters"), + ("network", "network_filters"), + ("volume", "volume_filters"), + ): if module.params[target]: - results[target] = podmanExec(module, target, module.params[filters], executable) + results[target] = podmanExec( + module, target, module.params[filters], executable + ) - if module.params['system']: - target = 'system' + if module.params["system"]: + target = "system" system_filters = {} - if module.params['system_all']: - system_filters['system_all'] = '--all' - if module.params['system_volumes']: - system_filters['system_volumes'] = '--volumes' + if module.params["system_all"]: + system_filters["system_all"] = "--all" + if module.params["system_volumes"]: + system_filters["system_volumes"] = "--volumes" results[target] = podmanExec(module, target, system_filters, executable) module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_runlabel.py b/plugins/modules/podman_runlabel.py index e5b6cf3..0e76ab0 100644 --- a/plugins/modules/podman_runlabel.py +++ b/plugins/modules/podman_runlabel.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_runlabel short_description: Run given label from given image author: Pavel Dostal (@pdostal) @@ -32,55 +32,53 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' -''' +RETURN = """ +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_runlabel: image: docker.io/continuumio/miniconda3 label: INSTALL -''' +""" from ansible.module_utils.basic import AnsibleModule # noqa: E402 def runlabel(module, executable): changed = False - command = [executable, 'container', 'runlabel'] - command.append(module.params['label']) - command.append(module.params['image']) + command = [executable, "container", "runlabel"] + command.append(module.params["label"]) + command.append(module.params["image"]) rc, out, err = module.run_command(command) if rc == 0: changed = True else: - module.fail_json(msg="Error running the runlabel from image %s: %s" % ( - module.params['image'], err)) + module.fail_json( + msg="Error running the runlabel from image %s: %s" + % (module.params["image"], err) + ) return changed, out, err def main(): module = AnsibleModule( argument_spec=dict( - image=dict(type='str', required=True), - label=dict(type='str', required=True), - executable=dict(type='str', default='podman') + image=dict(type="str", required=True), + label=dict(type="str", required=True), + executable=dict(type="str", default="podman"), ), supports_check_mode=False, ) - executable = module.get_bin_path(module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) changed, out, err = runlabel(module, executable) - results = { - "changed": changed, - "stdout": out, - "stderr": err - } + results = {"changed": changed, "stdout": out, "stderr": err} module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_save.py b/plugins/modules/podman_save.py index 69cf983..32a1eaf 100644 --- a/plugins/modules/podman_save.py +++ b/plugins/modules/podman_save.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_save short_description: Saves podman image to tar file author: Sagi Shnaidman (@sshnaidm) @@ -63,12 +63,12 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' -''' +RETURN = """ +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_save: image: nginx @@ -79,7 +79,7 @@ EXAMPLES = ''' - fedora dest: /tmp/file456.tar multi_image_archive: true -''' +""" import os # noqa: E402 from ansible.module_utils.basic import AnsibleModule # noqa: E402 @@ -88,32 +88,32 @@ from ..module_utils.podman.common import remove_file_or_dir # noqa: E402 def save(module, executable): changed = False - command = [executable, 'save'] + command = [executable, "save"] cmd_args = { - 'compress': ['--compress'], - 'dest': ['-o=%s' % module.params['dest']], - 'format': ['--format=%s' % module.params['format']], - 'multi_image_archive': ['--multi-image-archive'], + "compress": ["--compress"], + "dest": ["-o=%s" % module.params["dest"]], + "format": ["--format=%s" % module.params["format"]], + "multi_image_archive": ["--multi-image-archive"], } for param in module.params: if module.params[param] is not None and param in cmd_args: command += cmd_args[param] - for img in module.params['image']: + for img in module.params["image"]: command.append(img) - if module.params['force']: + if module.params["force"]: changed = True - dest = module.params['dest'] + dest = module.params["dest"] if os.path.exists(dest): if module.check_mode: - return changed, '', '' + return changed, "", "" try: remove_file_or_dir(dest) except Exception as e: module.fail_json(msg="Error deleting %s path: %s" % (dest, e)) else: - changed = not os.path.exists(module.params['dest']) + changed = not os.path.exists(module.params["dest"]) if module.check_mode: - return changed, '', '' + return changed, "", "" rc, out, err = module.run_command(command) if rc != 0: module.fail_json(msg="Error: %s" % (err)) @@ -123,20 +123,28 @@ def save(module, executable): def main(): module = AnsibleModule( argument_spec=dict( - image=dict(type='list', elements='str', required=True), - compress=dict(type='bool'), - dest=dict(type='str', required=True, aliases=['path']), - format=dict(type='str', choices=['docker-archive', 'oci-archive', 'oci-dir', 'docker-dir']), - multi_image_archive=dict(type='bool'), - force=dict(type='bool', default=True), - executable=dict(type='str', default='podman') + image=dict(type="list", elements="str", required=True), + compress=dict(type="bool"), + dest=dict(type="str", required=True, aliases=["path"]), + format=dict( + type="str", + choices=["docker-archive", "oci-archive", "oci-dir", "docker-dir"], + ), + multi_image_archive=dict(type="bool"), + force=dict(type="bool", default=True), + executable=dict(type="str", default="podman"), ), supports_check_mode=True, ) - if module.params['compress'] and module.params['format'] not in ['oci-dir', 'docker-dir']: - module.fail_json(msg="Compression is only supported for oci-dir and docker-dir format") + if module.params["compress"] and module.params["format"] not in [ + "oci-dir", + "docker-dir", + ]: + module.fail_json( + msg="Compression is only supported for oci-dir and docker-dir format" + ) - executable = module.get_bin_path(module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) changed, out, err = save(module, executable) results = { @@ -148,5 +156,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_search.py b/plugins/modules/podman_search.py index c65e4ce..0fdd22b 100644 --- a/plugins/modules/podman_search.py +++ b/plugins/modules/podman_search.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_search author: - Derek Waters (@derekwaters) @@ -39,7 +40,7 @@ options: default: False type: bool -''' +""" EXAMPLES = r""" - name: Search for any rhel images @@ -81,10 +82,10 @@ 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)]) + command = [executable, "search", term, "--format", "json"] + command.extend(["--limit", "{0}".format(limit)]) if list_tags: - command.extend(['--list-tags']) + command.extend(["--list-tags"]) rc, out, err = module.run_command(command) if rc != 0 and list_tags and out == "" and "fetching tags list" in err: @@ -97,18 +98,18 @@ def search_images(module, executable, term, limit, list_tags): 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) + 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.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, errors = search_images(module, executable, term, limit, list_tags) @@ -118,7 +119,11 @@ def main(): 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)) + module.fail_json( + msg="Failed to parse JSON output from podman search: {out}".format( + out=result_str + ) + ) results = dict( changed=False, @@ -129,5 +134,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_secret.py b/plugins/modules/podman_secret.py index baa94a1..0a5d8a6 100644 --- a/plugins/modules/podman_secret.py +++ b/plugins/modules/podman_secret.py @@ -2,10 +2,11 @@ # 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''' +DOCUMENTATION = r""" --- module: podman_secret author: @@ -81,7 +82,7 @@ options: - Enable debug mode for module. It prints secrets diff. type: bool default: False -''' +""" EXAMPLES = r""" - name: Create secret @@ -113,24 +114,30 @@ EXAMPLES = r""" 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 +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, +) -diff = {"before": '', "after": ''} +diff = {"before": "", "after": ""} def podman_secret_exists(module, executable, name, version): - if version is None or LooseVersion(version) < LooseVersion('4.5.0'): + if version is None or LooseVersion(version) < LooseVersion("4.5.0"): rc, out, err = module.run_command( - [executable, 'secret', 'ls', "--format", "{{.Name}}"]) + [executable, "secret", "ls", "--format", "{{.Name}}"] + ) return name in [i.strip() for i in out.splitlines()] - rc, out, err = module.run_command( - [executable, 'secret', 'exists', name]) + rc, out, err = module.run_command([executable, "secret", "exists", name]) return rc == 0 -def need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels): - cmd = [executable, 'secret', 'inspect', '--showsecret', name] +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: @@ -141,64 +148,90 @@ def need_update(module, executable, name, data, path, env, skip, driver, driver_ try: secret = module.from_json(out)[0] if data: - if secret['SecretData'] != data: + if secret["SecretData"] != data: if debug: - diff['after'] = data - diff['before'] = secret['SecretData'] + diff["after"] = data + diff["before"] = secret["SecretData"] else: - diff['after'] = "" - diff['before'] = "" + diff["after"] = "" + diff["before"] = "" return True if path: - with open(path, 'rb') as f: - text = f.read().decode('utf-8') - if secret['SecretData'] != text: + 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'] + diff["after"] = text + diff["before"] = secret["SecretData"] else: - diff['after'] = "" - diff['before'] = "" + diff["after"] = "" + diff["before"] = "" return True if env: env_data = os.environ.get(env) - if secret['SecretData'] != env_data: + if secret["SecretData"] != env_data: if debug: - diff['after'] = env_data - diff['before'] = secret['SecretData'] + diff["after"] = env_data + diff["before"] = secret["SecretData"] else: - diff['after'] = "" - diff['before'] = "" + diff["after"] = "" + diff["before"] = "" return True if driver: - if secret['Spec']['Driver']['Name'] != driver: - diff['after'] = driver - diff['before'] = secret['Spec']['Driver']['Name'] + if secret["Spec"]["Driver"]["Name"] != driver: + diff["after"] = driver + diff["before"] = secret["Spec"]["Driver"]["Name"] return True if driver_opts: for k, v in driver_opts.items(): - if secret['Spec']['Driver']['Options'].get(k) != v: - diff['after'] = "=".join([k, v]) - diff['before'] = "=".join( - [k, secret['Spec']['Driver']['Options'].get(k)]) + if secret["Spec"]["Driver"]["Options"].get(k) != v: + diff["after"] = "=".join([k, v]) + diff["before"] = "=".join( + [k, secret["Spec"]["Driver"]["Options"].get(k)] + ) return True if labels: for k, v in labels.items(): - if secret['Spec']['Labels'].get(k) != v: - diff['after'] = "=".join([k, v]) - diff['before'] = "=".join( - [k, secret['Spec']['Labels'].get(k)]) + if secret["Spec"]["Labels"].get(k) != v: + diff["after"] = "=".join([k, v]) + diff["before"] = "=".join([k, secret["Spec"]["Labels"].get(k)]) return True except Exception: return True return False -def podman_secret_create(module, executable, name, data, path, env, force, skip, - driver, driver_opts, debug, labels): +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'): - if need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels): + if podman_version is not None and LooseVersion(podman_version) >= LooseVersion( + "4.7.0" + ): + 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} @@ -208,20 +241,20 @@ def podman_secret_create(module, executable, name, data, path, env, force, skip, if skip and podman_secret_exists(module, executable, name, podman_version): return {"changed": False} - cmd = [executable, 'secret', 'create'] + cmd = [executable, "secret", "create"] if driver: - cmd.append('--driver') + cmd.append("--driver") cmd.append(driver) if driver_opts: - cmd.append('--driver-opts') + cmd.append("--driver-opts") cmd.append(",".join("=".join(i) for i in driver_opts.items())) if labels: for k, v in labels.items(): - cmd.append('--label') + cmd.append("--label") cmd.append("=".join([k, v])) cmd.append(name) if data: - cmd.append('-') + cmd.append("-") elif path: cmd.append(path) elif env: @@ -240,18 +273,18 @@ def podman_secret_create(module, executable, name, data, path, env, force, skip, return { "changed": True, "diff": { - "before": diff['before'] + "\n", - "after": diff['after'] + "\n", + "before": diff["before"] + "\n", + "after": diff["after"] + "\n", }, } def podman_secret_remove(module, executable, name): changed = False - rc, out, err = module.run_command([executable, 'secret', 'rm', name]) + rc, out, err = module.run_command([executable, "secret", "rm", name]) if rc == 0: changed = True - elif 'no such secret' in err: + elif "no such secret" in err: pass else: module.fail_json(msg="Unable to remove secret: %s" % err) @@ -264,45 +297,56 @@ def podman_secret_remove(module, executable, name): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - 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'), - driver_opts=dict(type='dict'), - labels=dict(type='dict'), - debug=dict(type='bool', default=False), + executable=dict(type="str", default="podman"), + 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"), + driver_opts=dict(type="dict"), + labels=dict(type="dict"), + debug=dict(type="bool", default=False), ), - required_if=[('state', 'present', ['path', 'env', 'data'], True)], - mutually_exclusive=[['path', 'env', 'data']], + required_if=[("state", "present", ["path", "env", "data"], True)], + mutually_exclusive=[["path", "env", "data"]], ) - state = module.params['state'] - name = module.params['name'] - executable = module.get_bin_path(module.params['executable'], required=True) + state = module.params["state"] + name = module.params["name"] + executable = module.get_bin_path(module.params["executable"], required=True) - if state == 'present': - data = module.params['data'] - 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, path, env, force, skip, - driver, driver_opts, debug, labels) + if state == "present": + data = module.params["data"] + 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, + path, + env, + force, + skip, + driver, + driver_opts, + debug, + labels, + ) else: results = podman_secret_remove(module, executable, name) module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_secret_info.py b/plugins/modules/podman_secret_info.py index ebe8542..646b6a3 100644 --- a/plugins/modules/podman_secret_info.py +++ b/plugins/modules/podman_secret_info.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_secret_info author: - "Sagi Shnaidman (@sshnaidm)" @@ -32,7 +33,7 @@ options: machine running C(podman) default: 'podman' type: str -''' +""" EXAMPLES = r""" - name: Gather info about all present secrets @@ -72,21 +73,23 @@ from ansible.module_utils.basic import AnsibleModule def get_secret_info(module, executable, show, name): - command = [executable, 'secret', 'inspect'] + command = [executable, "secret", "inspect"] if show: - command.append('--showsecret') + command.append("--showsecret") if name: command.append(name) else: - all_names = [executable, 'secret', 'ls', '-q'] + all_names = [executable, "secret", "ls", "-q"] rc, out, err = module.run_command(all_names) name = out.split() if not name: return [], out, err command.extend(name) rc, out, err = module.run_command(command) - if rc != 0 or 'no secret with name or id' in err: - module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all secrets', err)) + if rc != 0 or "no secret with name or id" in err: + module.fail_json( + msg="Unable to gather info for %s: %s" % (name or "all secrets", err) + ) if not out or json.loads(out) is None: return [], out, err return json.loads(out), out, err @@ -95,16 +98,16 @@ def get_secret_info(module, executable, show, name): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - name=dict(type='str'), - showsecret=dict(type='bool', default=False), + executable=dict(type="str", default="podman"), + name=dict(type="str"), + showsecret=dict(type="bool", default=False), ), supports_check_mode=True, ) - name = module.params['name'] - showsecret = module.params['showsecret'] - executable = module.get_bin_path(module.params['executable'], required=True) + name = module.params["name"] + showsecret = module.params["showsecret"] + executable = module.get_bin_path(module.params["executable"], required=True) inspect_results, out, err = get_secret_info(module, executable, showsecret, name) @@ -117,5 +120,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_tag.py b/plugins/modules/podman_tag.py index 39e799f..92fd042 100644 --- a/plugins/modules/podman_tag.py +++ b/plugins/modules/podman_tag.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: podman_tag short_description: Add an additional name to a local image author: Christian Bourque (@ocafebabe) @@ -33,50 +33,51 @@ options: type: str requirements: - "Podman installed on host" -''' +""" -RETURN = ''' -''' +RETURN = """ +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - containers.podman.podman_tag: image: docker.io/continuumio/miniconda3 target_names: - miniconda3 - miniconda -''' +""" from ansible.module_utils.basic import AnsibleModule # noqa: E402 def tag(module, executable): changed = False - command = [executable, 'tag'] - command.append(module.params['image']) - command.extend(module.params['target_names']) + command = [executable, "tag"] + command.append(module.params["image"]) + command.extend(module.params["target_names"]) if module.check_mode: - return changed, '', '' + return changed, "", "" rc, out, err = module.run_command(command) if rc == 0: changed = True else: - module.fail_json(msg="Error tagging local image %s: %s" % ( - module.params['image'], err)) + module.fail_json( + msg="Error tagging local image %s: %s" % (module.params["image"], err) + ) return changed, out, err def main(): module = AnsibleModule( argument_spec=dict( - image=dict(type='str', required=True), - target_names=dict(type='list', elements='str', required=True), - executable=dict(type='str', default='podman') + image=dict(type="str", required=True), + target_names=dict(type="list", elements="str", required=True), + executable=dict(type="str", default="podman"), ), supports_check_mode=True, ) - executable = module.get_bin_path(module.params['executable'], required=True) + executable = module.get_bin_path(module.params["executable"], required=True) changed, out, err = tag(module, executable) results = { @@ -87,5 +88,5 @@ def main(): module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_volume.py b/plugins/modules/podman_volume.py index e808103..85bd1ea 100644 --- a/plugins/modules/podman_volume.py +++ b/plugins/modules/podman_volume.py @@ -4,9 +4,10 @@ # flake8: noqa: E501 from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: podman_volume short_description: Manage Podman volumes @@ -101,9 +102,9 @@ options: requirements: - "podman" -''' +""" -RETURN = ''' +RETURN = """ volume: description: Volume inspection results if exists. returned: always @@ -121,9 +122,9 @@ volume: Options: {} Scope: local -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # What modules does for example - name: Create a volume containers.podman.podman_volume: @@ -147,26 +148,32 @@ EXAMPLES = ''' - Copy=true - Image=quay.io/centos/centos:latest -''' +""" # 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 -from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion -from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys -from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + LooseVersion, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.common import ( + lower_keys, +) +from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import ( + create_quadlet_state, +) class PodmanVolumeModuleParams: """Creates list of arguments for podman CLI command. - Arguments: - action {str} -- action type from 'create', 'delete' - params {dict} -- dictionary of module parameters + Arguments: + action {str} -- action type from 'create', 'delete' + params {dict} -- dictionary of module parameters - """ + """ def __init__(self, action, params, podman_version, module): self.params = params @@ -180,58 +187,64 @@ class PodmanVolumeModuleParams: Returns: list -- list of byte strings for Popen command """ - if self.action in ['delete', 'mount', 'unmount']: + if self.action in ["delete", "mount", "unmount"]: return self._simple_action() - if self.action in ['create']: + if self.action in ["create"]: return self._create_action() def _simple_action(self): - 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] + 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']] - all_param_methods = [func for func in dir(self) - if callable(getattr(self, func)) - and func.startswith("addparam")] + cmd = [self.action, self.params["name"]] + all_param_methods = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("addparam") + ] params_set = (i for i in self.params if self.params[i] is not None) for param in params_set: func_name = "_".join(["addparam", param]) if func_name in all_param_methods: cmd = getattr(self, func_name)(cmd) - return [to_bytes(i, errors='surrogate_or_strict') for i in cmd] + return [to_bytes(i, errors="surrogate_or_strict") for i in cmd] def check_version(self, param, minv=None, maxv=None): - if minv and LooseVersion(minv) > LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported from podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) - if maxv and LooseVersion(maxv) < LooseVersion( - self.podman_version): - self.module.fail_json(msg="Parameter %s is supported till podman " - "version %s only! Current version is %s" % ( - param, minv, self.podman_version)) + if minv and LooseVersion(minv) > LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported from podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) + if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version): + self.module.fail_json( + msg="Parameter %s is supported till podman " + "version %s only! Current version is %s" + % (param, minv, self.podman_version) + ) def addparam_label(self, c): - for label in self.params['label'].items(): - c += ['--label', b'='.join( - [to_bytes(l, errors='surrogate_or_strict') for l in label])] + for label in self.params["label"].items(): + c += [ + "--label", + b"=".join([to_bytes(l, errors="surrogate_or_strict") for l in label]), + ] return c def addparam_driver(self, c): - return c + ['--driver', self.params['driver']] + return c + ["--driver", self.params["driver"]] def addparam_options(self, c): - for opt in self.params['options']: - c += ['--opt', opt] + for opt in self.params["options"]: + c += ["--opt", opt] return c @@ -239,11 +252,7 @@ class PodmanVolumeDefaults: def __init__(self, module, podman_version): self.module = module self.version = podman_version - self.defaults = { - 'driver': 'local', - 'label': {}, - 'options': {} - } + self.defaults = {"driver": "local", "label": {}, "options": {}} def default_dict(self): # make here any changes to self.defaults related to podman version @@ -257,13 +266,14 @@ class PodmanVolumeDiff: self.default_dict = None self.info = lower_keys(info) self.params = self.defaultize() - self.diff = {'before': {}, 'after': {}} + self.diff = {"before": {}, "after": {}} self.non_idempotent = {} def defaultize(self): params_with_defaults = {} self.default_dict = PodmanVolumeDefaults( - self.module, self.version).default_dict() + self.module, self.version + ).default_dict() for p in self.module.params: if self.module.params[p] is None and p in self.default_dict: params_with_defaults[p] = self.default_dict[p] @@ -273,29 +283,29 @@ class PodmanVolumeDiff: def _diff_update_and_compare(self, param_name, before, after): if before != after: - self.diff['before'].update({param_name: before}) - self.diff['after'].update({param_name: after}) + self.diff["before"].update({param_name: before}) + self.diff["after"].update({param_name: after}) return True return False def diffparam_label(self): - before = self.info['labels'] if 'labels' in self.info else {} - after = self.params['label'] - return self._diff_update_and_compare('label', before, after) + before = self.info["labels"] if "labels" in self.info else {} + after = self.params["label"] + return self._diff_update_and_compare("label", before, after) def diffparam_driver(self): - before = self.info['driver'] - after = self.params['driver'] - return self._diff_update_and_compare('driver', before, after) + before = self.info["driver"] + after = self.params["driver"] + return self._diff_update_and_compare("driver", before, after) def diffparam_options(self): - before = self.info['options'] if 'options' in self.info else {} + before = self.info["options"] if "options" in self.info else {} # Removing GID and UID from options list - before.pop('uid', None) - before.pop('gid', None) + before.pop("uid", None) + before.pop("gid", None) # Collecting all other options in the list before = ["=".join((k, v)) for k, v in before.items()] - after = self.params['options'] + after = self.params["options"] # # For UID, GID # if 'uid' in self.info or 'gid' in self.info: # ids = [] @@ -312,12 +322,14 @@ class PodmanVolumeDiff: # after = [i for i in after if 'gid' not in i and 'uid' not in i] # after += ids before, after = sorted(list(set(before))), sorted(list(set(after))) - return self._diff_update_and_compare('options', before, after) + return self._diff_update_and_compare("options", before, after) def is_different(self): - diff_func_list = [func for func in dir(self) - if callable(getattr(self, func)) and func.startswith( - "diffparam")] + diff_func_list = [ + func + for func in dir(self) + if callable(getattr(self, func)) and func.startswith("diffparam") + ] fail_fast = not bool(self.module._diff) different = False for func_name in diff_func_list: @@ -329,7 +341,11 @@ class PodmanVolumeDiff: different = True # Check non idempotent parameters for p in self.non_idempotent: - if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']: + if self.module.params[p] is not None and self.module.params[p] not in [ + {}, + [], + "", + ]: different = True return different @@ -351,7 +367,7 @@ class PodmanVolume: super(PodmanVolume, self).__init__() self.module = module self.name = name - self.stdout, self.stderr = '', '' + self.stdout, self.stderr = "", "" self.mount_point = None self.info = self.get_info() self.version = self._get_podman_version() @@ -366,26 +382,30 @@ class PodmanVolume: @property def different(self): """Check if volume is different.""" - diffcheck = PodmanVolumeDiff( - self.module, - self.info, - self.version) + diffcheck = PodmanVolumeDiff(self.module, self.info, self.version) is_different = diffcheck.is_different() diffs = diffcheck.diff - if self.module._diff and is_different and diffs['before'] and diffs['after']: - self.diff['before'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['before'].items())]) + "\n" - self.diff['after'] = "\n".join( - ["%s - %s" % (k, v) for k, v in sorted( - diffs['after'].items())]) + "\n" + if self.module._diff and is_different and diffs["before"] and diffs["after"]: + self.diff["before"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())] + ) + + "\n" + ) + self.diff["after"] = ( + "\n".join( + ["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())] + ) + + "\n" + ) return is_different def get_info(self): """Inspect volume and gather info about it.""" # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module.params['executable'], b'volume', b'inspect', self.name]) + [self.module.params["executable"], b"volume", b"inspect", self.name] + ) if rc == 0: data = json.loads(out) if data: @@ -397,10 +417,12 @@ class PodmanVolume: def _get_podman_version(self): # pylint: disable=unused-variable rc, out, err = self.module.run_command( - [self.module.params['executable'], b'--version']) + [self.module.params["executable"], b"--version"] + ) if rc != 0 or not out or "version" not in out: - self.module.fail_json(msg="%s run failed!" % - self.module.params['executable']) + self.module.fail_json( + msg="%s run failed!" % self.module.params["executable"] + ) return out.split("version")[1].strip() def _perform_action(self, action): @@ -409,47 +431,50 @@ class PodmanVolume: Arguments: action {str} -- action to perform - create, delete, mount, unmout """ - b_command = PodmanVolumeModuleParams(action, - self.module.params, - self.version, - self.module, - ).construct_command_from_params() - full_cmd = " ".join([self.module.params['executable'], 'volume'] - + [to_native(i) for i in b_command]) + b_command = PodmanVolumeModuleParams( + action, + self.module.params, + self.version, + self.module, + ).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': + 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( - full_cmd, - expand_user_and_vars=False) + rc, out, err = self.module.run_command(full_cmd, expand_user_and_vars=False) self.stdout = out self.stderr = err if rc != 0: self.module.fail_json( msg="Can't %s volume %s" % (action, self.name), - stdout=out, stderr=err) + stdout=out, + stderr=err, + ) # in case of mount/unmount, return path to the volume from stdout - if action in ['mount']: + if action in ["mount"]: self.mount_point = out.strip() def delete(self): """Delete the volume.""" - self._perform_action('delete') + self._perform_action("delete") def create(self): """Create the volume.""" - self._perform_action('create') + self._perform_action("create") def mount(self): """Delete the volume.""" - self._perform_action('mount') + self._perform_action("mount") def unmount(self): """Create the volume.""" - self._perform_action('unmount') + self._perform_action("unmount") def recreate(self): """Recreate the volume.""" @@ -474,16 +499,16 @@ class PodmanVolumeManager: self.module = module self.results = { - 'changed': False, - 'actions': [], - 'volume': {}, + "changed": False, + "actions": [], + "volume": {}, } - self.name = self.module.params['name'] - self.executable = \ - self.module.get_bin_path(self.module.params['executable'], - required=True) - self.state = self.module.params['state'] - self.recreate = self.module.params['recreate'] + self.name = self.module.params["name"] + self.executable = self.module.get_bin_path( + self.module.params["executable"], required=True + ) + self.state = self.module.params["state"] + self.recreate = self.module.params["recreate"] self.volume = PodmanVolume(self.module, self.name) def update_volume_result(self, changed=True): @@ -495,39 +520,45 @@ class PodmanVolumeManager: """ facts = self.volume.get_info() if changed else self.volume.info out, err = self.volume.stdout, self.volume.stderr - self.results.update({'changed': changed, 'volume': facts, - 'podman_actions': self.volume.actions}, - stdout=out, stderr=err) + self.results.update( + { + "changed": changed, + "volume": facts, + "podman_actions": self.volume.actions, + }, + stdout=out, + stderr=err, + ) if self.volume.diff: - self.results.update({'diff': self.volume.diff}) - if self.module.params['debug']: - self.results.update({'podman_version': self.volume.version}) + self.results.update({"diff": self.volume.diff}) + if self.module.params["debug"]: + self.results.update({"podman_version": self.volume.version}) self.module.exit_json(**self.results) def execute(self): """Execute the desired action according to map of actions & states.""" states_map = { - 'present': self.make_present, - 'absent': self.make_absent, - 'mounted': self.make_mount, - 'unmounted': self.make_unmount, - 'quadlet': self.make_quadlet, + "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] process_action() - self.module.fail_json(msg="Unexpected logic error happened, " - "please contact maintainers ASAP!") + self.module.fail_json( + msg="Unexpected logic error happened, " "please contact maintainers ASAP!" + ) def make_present(self): """Run actions if desired state is 'started'.""" if not self.volume.exists: self.volume.create() - self.results['actions'].append('created %s' % self.volume.name) + self.results["actions"].append("created %s" % self.volume.name) self.update_volume_result() elif self.recreate or self.volume.different: self.volume.recreate() - self.results['actions'].append('recreated %s' % - self.volume.name) + self.results["actions"].append("recreated %s" % self.volume.name) self.update_volume_result() else: self.update_volume_result(changed=False) @@ -535,31 +566,30 @@ class PodmanVolumeManager: def make_absent(self): """Run actions if desired state is 'absent'.""" if not self.volume.exists: - self.results.update({'changed': False}) + self.results.update({"changed": False}) elif self.volume.exists: self.volume.delete() - self.results['actions'].append('deleted %s' % self.volume.name) - self.results.update({'changed': True}) - self.results.update({'volume': {}, - 'podman_actions': self.volume.actions}) + self.results["actions"].append("deleted %s" % self.volume.name) + self.results.update({"changed": True}) + self.results.update({"volume": {}, "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.results["actions"].append("created %s" % self.volume.name) self.volume.mount() - self.results['actions'].append('mounted %s' % self.volume.name) + self.results["actions"].append("mounted %s" % self.volume.name) if self.volume.mount_point: - self.results.update({'mount_point': 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.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) @@ -573,23 +603,27 @@ class PodmanVolumeManager: def main(): module = AnsibleModule( argument_spec=dict( - state=dict(type='str', default="present", - choices=['present', 'absent', 'mounted', 'unmounted', 'quadlet']), - name=dict(type='str', required=True), - label=dict(type='dict', required=False), - driver=dict(type='str', required=False), - options=dict(type='list', elements='str', required=False), - recreate=dict(type='bool', default=False), - executable=dict(type='str', required=False, default='podman'), - debug=dict(type='bool', default=False), - quadlet_dir=dict(type='path', required=False), - quadlet_filename=dict(type='str', required=False), - quadlet_file_mode=dict(type='raw', required=False), - quadlet_options=dict(type='list', elements='str', required=False), - )) + state=dict( + type="str", + default="present", + choices=["present", "absent", "mounted", "unmounted", "quadlet"], + ), + name=dict(type="str", required=True), + label=dict(type="dict", required=False), + driver=dict(type="str", required=False), + options=dict(type="list", elements="str", required=False), + recreate=dict(type="bool", default=False), + executable=dict(type="str", required=False, default="podman"), + debug=dict(type="bool", default=False), + quadlet_dir=dict(type="path", required=False), + quadlet_filename=dict(type="str", required=False), + quadlet_file_mode=dict(type="raw", required=False), + quadlet_options=dict(type="list", elements="str", required=False), + ) + ) PodmanVolumeManager(module).execute() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/podman_volume_info.py b/plugins/modules/podman_volume_info.py index 911d364..6b72e06 100644 --- a/plugins/modules/podman_volume_info.py +++ b/plugins/modules/podman_volume_info.py @@ -3,10 +3,11 @@ # 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''' +DOCUMENTATION = r""" module: podman_volume_info author: - "Sagi Shnaidman (@sshnaidm)" @@ -27,7 +28,7 @@ options: machine running C(podman) default: 'podman' type: str -''' +""" EXAMPLES = r""" - name: Gather info about all present volumes @@ -65,14 +66,16 @@ from ansible.module_utils.basic import AnsibleModule def get_volume_info(module, executable, name): - command = [executable, 'volume', 'inspect'] + command = [executable, "volume", "inspect"] if name: command.append(name) else: command.append("--all") rc, out, err = module.run_command(command) - if rc != 0 or 'no such volume' in err: - module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all volumes', err)) + if rc != 0 or "no such volume" in err: + module.fail_json( + msg="Unable to gather info for %s: %s" % (name or "all volumes", err) + ) if not out or json.loads(out) is None: return [], out, err return json.loads(out), out, err @@ -81,25 +84,20 @@ def get_volume_info(module, executable, name): def main(): module = AnsibleModule( argument_spec=dict( - executable=dict(type='str', default='podman'), - name=dict(type='str') + executable=dict(type="str", default="podman"), name=dict(type="str") ), supports_check_mode=True, ) - name = module.params['name'] - executable = module.get_bin_path(module.params['executable'], required=True) + name = module.params["name"] + executable = module.get_bin_path(module.params["executable"], required=True) inspect_results, out, err = get_volume_info(module, executable, name) - results = { - "changed": False, - "volumes": inspect_results, - "stderr": err - } + results = {"changed": False, "volumes": inspect_results, "stderr": err} module.exit_json(**results) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/setup.py b/setup.py index c453a91..7b3a105 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,4 @@ import setuptools -setuptools.setup( - setup_requires=['pbr'], - pbr=True, - py_modules=[]) +setuptools.setup(setup_requires=["pbr"], pbr=True, py_modules=[]) diff --git a/tests/unit/plugins/modules/test_common.py b/tests/unit/plugins/modules/test_common.py index 583e26d..8705860 100644 --- a/tests/unit/plugins/modules/test_common.py +++ b/tests/unit/plugins/modules/test_common.py @@ -1,4 +1,5 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type import pytest @@ -8,12 +9,17 @@ from ansible_collections.containers.podman.plugins.module_utils.podman.common im ) -@pytest.mark.parametrize('test_input, expected', [ - (["AAA", "BBB"], ["AAA", "BBB"]), - ("AAQQ", "AAQQ"), - ({"AAA": "AaaAa", "11": 22, "AbCdEf": None, "bbb": "aaaAA"}, - {"aaa": "AaaAa", "11": 22, "abcdef": None, "bbb": "aaaAA"}) -]) +@pytest.mark.parametrize( + "test_input, expected", + [ + (["AAA", "BBB"], ["AAA", "BBB"]), + ("AAQQ", "AAQQ"), + ( + {"AAA": "AaaAa", "11": 22, "AbCdEf": None, "bbb": "aaaAA"}, + {"aaa": "AaaAa", "11": 22, "abcdef": None, "bbb": "aaaAA"}, + ), + ], +) def test_lower_keys(test_input, expected): print(lower_keys.__code__.co_filename) assert lower_keys(test_input) == expected diff --git a/tests/unit/plugins/modules/test_container_lib.py b/tests/unit/plugins/modules/test_container_lib.py index ea33641..38ec3c8 100644 --- a/tests/unit/plugins/modules/test_container_lib.py +++ b/tests/unit/plugins/modules/test_container_lib.py @@ -66,15 +66,17 @@ def test_container_add_params(test_input, expected): [ None, # module {"conmon_pidfile": "bbb"}, # module params - {"conmonpidfile": "ccc", + { + "conmonpidfile": "ccc", "config": { "createcommand": [ "podman", "create", "--conmon-pidfile=ccc", "testcont", - ]} - }, # container info + ] + }, + }, # container info {}, # image info "4.1.1", # podman version ], @@ -84,15 +86,17 @@ def test_container_add_params(test_input, expected): [ None, # module {"conmon_pidfile": None}, # module params - {"conmonpidfile": "ccc", + { + "conmonpidfile": "ccc", "config": { "createcommand": [ "podman", "create", "--conmon-pidfile=ccc", "testcont", - ]} - }, # container info + ] + }, + }, # container info {}, # image info "4.1.1", # podman version ], @@ -102,14 +106,16 @@ def test_container_add_params(test_input, expected): [ None, # module {"conmon_pidfile": None}, # module params - {"conmonpidfile": None, + { + "conmonpidfile": None, "config": { "createcommand": [ "podman", "create", "testcont", - ]} - }, # container info + ] + }, + }, # container info {}, # image info "4.1.1", # podman version ], @@ -118,15 +124,17 @@ def test_container_add_params(test_input, expected): ( [ None, # module - {"conmon_pidfile": 'aaa'}, # module params - {"conmonpidfile": None, + {"conmon_pidfile": "aaa"}, # module params + { + "conmonpidfile": None, "config": { "createcommand": [ "podman", "create", "testcont", - ]} - }, # container info + ] + }, + }, # container info {}, # image info "4.1.1", # podman version ], @@ -135,16 +143,18 @@ def test_container_add_params(test_input, expected): ( [ None, # module - {"conmon_pidfile": 'aaa'}, # module params - {"conmonpidfile": 'aaa', + {"conmon_pidfile": "aaa"}, # module params + { + "conmonpidfile": "aaa", "config": { "createcommand": [ "podman", "create", "--conmon-pidfile=aaa", "testcont", - ]} - }, # container info + ] + }, + }, # container info {}, # image info "4.1.1", # podman version ],