From d65d37e9e9a78e03a35643704b413121515ee39c Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Mon, 6 Oct 2025 18:34:59 +0200 Subject: [PATCH] Reformat code with black and isort. --- plugins/action/docker_container_copy_into.py | 20 +- plugins/connection/docker.py | 322 +- plugins/connection/docker_api.py | 238 +- plugins/connection/nsenter.py | 79 +- plugins/doc_fragments/attributes.py | 12 +- plugins/doc_fragments/compose_v2.py | 4 +- plugins/doc_fragments/docker.py | 20 +- plugins/inventory/docker_containers.py | 169 +- plugins/inventory/docker_machine.py | 150 +- plugins/inventory/docker_swarm.py | 193 +- plugins/module_utils/_api/_import_helper.py | 25 +- plugins/module_utils/_api/api/client.py | 362 +- plugins/module_utils/_api/api/daemon.py | 34 +- plugins/module_utils/_api/auth.py | 170 +- plugins/module_utils/_api/constants.py | 20 +- plugins/module_utils/_api/context/api.py | 36 +- plugins/module_utils/_api/context/config.py | 16 +- plugins/module_utils/_api/context/context.py | 95 +- .../_api/credentials/constants.py | 9 +- .../module_utils/_api/credentials/errors.py | 8 +- .../module_utils/_api/credentials/store.py | 63 +- .../module_utils/_api/credentials/utils.py | 1 - plugins/module_utils/_api/errors.py | 41 +- plugins/module_utils/_api/tls.py | 36 +- .../_api/transport/basehttpadapter.py | 4 +- .../module_utils/_api/transport/npipeconn.py | 40 +- .../_api/transport/npipesocket.py | 40 +- .../module_utils/_api/transport/sshconn.py | 103 +- .../module_utils/_api/transport/ssladapter.py | 30 +- .../module_utils/_api/transport/unixconn.py | 44 +- plugins/module_utils/_api/types/daemon.py | 16 +- plugins/module_utils/_api/utils/build.py | 88 +- plugins/module_utils/_api/utils/config.py | 28 +- plugins/module_utils/_api/utils/decorators.py | 21 +- plugins/module_utils/_api/utils/fnmatch.py | 44 +- .../module_utils/_api/utils/json_stream.py | 10 +- plugins/module_utils/_api/utils/ports.py | 30 +- plugins/module_utils/_api/utils/proxy.py | 43 +- plugins/module_utils/_api/utils/socket.py | 24 +- plugins/module_utils/_api/utils/utils.py | 297 +- plugins/module_utils/_logfmt.py | 90 +- plugins/module_utils/_platform.py | 124 +- plugins/module_utils/_scramble.py | 18 +- plugins/module_utils/common.py | 435 +- plugins/module_utils/common_api.py | 377 +- plugins/module_utils/common_cli.py | 210 +- plugins/module_utils/compose_v2.py | 786 +- plugins/module_utils/copy.py | 258 +- plugins/module_utils/image_archive.py | 49 +- plugins/module_utils/module_container/base.py | 1164 +- .../module_container/docker_api.py | 1581 ++- .../module_utils/module_container/module.py | 895 +- plugins/module_utils/selectors.py | 1 - plugins/module_utils/socket_handler.py | 52 +- plugins/module_utils/socket_helper.py | 31 +- plugins/module_utils/swarm.py | 92 +- plugins/module_utils/util.py | 227 +- plugins/module_utils/version.py | 6 +- plugins/modules/current_container_facts.py | 52 +- plugins/modules/docker_compose_v2.py | 226 +- plugins/modules/docker_compose_v2_exec.py | 111 +- plugins/modules/docker_compose_v2_pull.py | 65 +- plugins/modules/docker_compose_v2_run.py | 191 +- plugins/modules/docker_config.py | 153 +- plugins/modules/docker_container.py | 3 +- plugins/modules/docker_container_copy_into.py | 617 +- plugins/modules/docker_container_exec.py | 160 +- plugins/modules/docker_container_info.py | 20 +- plugins/modules/docker_context_info.py | 109 +- plugins/modules/docker_host_info.py | 139 +- plugins/modules/docker_image.py | 642 +- plugins/modules/docker_image_build.py | 338 +- plugins/modules/docker_image_export.py | 103 +- plugins/modules/docker_image_info.py | 50 +- plugins/modules/docker_image_load.py | 78 +- plugins/modules/docker_image_pull.py | 87 +- plugins/modules/docker_image_push.py | 71 +- plugins/modules/docker_image_remove.py | 121 +- plugins/modules/docker_image_tag.py | 85 +- plugins/modules/docker_login.py | 210 +- plugins/modules/docker_network.py | 454 +- plugins/modules/docker_network_info.py | 20 +- plugins/modules/docker_node.py | 98 +- plugins/modules/docker_node_info.py | 33 +- plugins/modules/docker_plugin.py | 171 +- plugins/modules/docker_prune.py | 159 +- plugins/modules/docker_secret.py | 143 +- plugins/modules/docker_stack.py | 83 +- plugins/modules/docker_stack_info.py | 21 +- plugins/modules/docker_stack_task_info.py | 24 +- plugins/modules/docker_swarm.py | 328 +- plugins/modules/docker_swarm_info.py | 163 +- plugins/modules/docker_swarm_service.py | 1905 ++-- plugins/modules/docker_swarm_service_info.py | 35 +- plugins/modules/docker_volume.py | 155 +- plugins/modules/docker_volume_info.py | 23 +- plugins/plugin_utils/common.py | 24 +- plugins/plugin_utils/common_api.py | 21 +- plugins/plugin_utils/socket_handler.py | 8 +- plugins/plugin_utils/unsafe.py | 9 +- tests/unit/plugins/connection/test_docker.py | 86 +- .../inventory/test_docker_containers.py | 348 +- .../module_utils/_api/api/test_client.py | 324 +- .../plugins/module_utils/_api/constants.py | 6 +- .../plugins/module_utils/_api/fake_api.py | 607 +- .../plugins/module_utils/_api/fake_stat.py | 95 +- .../plugins/module_utils/_api/test_auth.py | 959 +- .../plugins/module_utils/_api/test_context.py | 21 +- .../plugins/module_utils/_api/test_errors.py | 40 +- .../_api/transport/test_sshconn.py | 12 +- .../_api/transport/test_ssladapter.py | 78 +- .../module_utils/_api/utils/test_build.py | 493 +- .../module_utils/_api/utils/test_config.py | 78 +- .../_api/utils/test_decorators.py | 32 +- .../_api/utils/test_json_stream.py | 40 +- .../module_utils/_api/utils/test_ports.py | 51 +- .../module_utils/_api/utils/test_proxy.py | 71 +- .../module_utils/_api/utils/test_utils.py | 412 +- .../module_utils/compose_v2_test_cases.py | 9774 +++++++++-------- .../unit/plugins/module_utils/test__logfmt.py | 57 +- .../plugins/module_utils/test__scramble.py | 22 +- .../plugins/module_utils/test_compose_v2.py | 319 +- tests/unit/plugins/module_utils/test_copy.py | 98 +- .../module_utils/test_image_archive.py | 31 +- tests/unit/plugins/module_utils/test_util.py | 649 +- .../test_docker_container_copy_into.py | 88 +- .../unit/plugins/modules/test_docker_image.py | 77 +- .../modules/test_docker_image_build.py | 26 +- .../plugins/modules/test_docker_network.py | 41 +- .../modules/test_docker_swarm_service.py | 358 +- .../unit/plugins/plugin_utils/test_unsafe.py | 97 +- .../docker_image_archive_stubbing.py | 31 +- 132 files changed, 17581 insertions(+), 14729 deletions(-) diff --git a/plugins/action/docker_container_copy_into.py b/plugins/action/docker_container_copy_into.py index 19b2ed0d..1090da43 100644 --- a/plugins/action/docker_container_copy_into.py +++ b/plugins/action/docker_container_copy_into.py @@ -9,8 +9,9 @@ import base64 from ansible import constants as C from ansible.plugins.action import ActionBase from ansible.utils.vars import merge_hash - -from ansible_collections.community.docker.plugins.module_utils._scramble import unscramble +from ansible_collections.community.docker.plugins.module_utils._scramble import ( + unscramble, +) class ActionModule(ActionBase): @@ -24,15 +25,18 @@ class ActionModule(ActionBase): result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect - self._task.args['_max_file_size_for_diff'] = C.MAX_FILE_SIZE_FOR_DIFF + self._task.args["_max_file_size_for_diff"] = C.MAX_FILE_SIZE_FOR_DIFF - result = merge_hash(result, self._execute_module(task_vars=task_vars, wrap_async=self._task.async_val)) + result = merge_hash( + result, + self._execute_module(task_vars=task_vars, wrap_async=self._task.async_val), + ) - if 'diff' in result and result['diff'].get('scrambled_diff'): + if "diff" in result and result["diff"].get("scrambled_diff"): # Scrambling is not done for security, but to avoid no_log screwing up the diff - diff = result['diff'] - key = base64.b64decode(diff.pop('scrambled_diff')) - for k in ('before', 'after'): + diff = result["diff"] + key = base64.b64decode(diff.pop("scrambled_diff")) + for k in ("before", "after"): if k in diff: diff[k] = unscramble(diff[k], key) diff --git a/plugins/connection/docker.py b/plugins/connection/docker.py index bf455fb2..cd516bbc 100644 --- a/plugins/connection/docker.py +++ b/plugins/connection/docker.py @@ -9,6 +9,7 @@ from __future__ import annotations + DOCUMENTATION = r""" author: - Lorin Hochestein (!UNKNOWN) @@ -114,26 +115,28 @@ options: import fcntl import os import os.path +import re import selectors import subprocess -import re from shlex import quote -from ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleConnectionFailure +from ansible.errors import AnsibleConnectionFailure, AnsibleError, AnsibleFileNotFound from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text -from ansible.plugins.connection import ConnectionBase, BUFSIZE +from ansible.plugins.connection import BUFSIZE, ConnectionBase from ansible.utils.display import Display +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion display = Display() class Connection(ConnectionBase): - ''' Local docker based connections ''' + """Local docker based connections""" - transport = 'community.docker.docker' + transport = "community.docker.docker" has_pipelining = True def __init__(self, play_context, new_stdin, *args, **kwargs): @@ -154,29 +157,31 @@ class Connection(ConnectionBase): # Windows uses Powershell modules if getattr(self._shell, "_IS_WINDOWS", False): - self.module_implementation_preferences = ('.ps1', '.exe', '') + self.module_implementation_preferences = (".ps1", ".exe", "") - if 'docker_command' in kwargs: - self.docker_cmd = kwargs['docker_command'] + if "docker_command" in kwargs: + self.docker_cmd = kwargs["docker_command"] else: try: - self.docker_cmd = get_bin_path('docker') + self.docker_cmd = get_bin_path("docker") except ValueError: raise AnsibleError("docker command not found in PATH") @staticmethod def _sanitize_version(version): - version = re.sub('[^0-9a-zA-Z.]', '', version) - version = re.sub('^v', '', version) + version = re.sub("[^0-9a-zA-Z.]", "", version) + version = re.sub("^v", "", version) return version def _old_docker_version(self): cmd_args = self._docker_args - old_version_subcommand = ['version'] + old_version_subcommand = ["version"] old_docker_cmd = [self.docker_cmd] + cmd_args + old_version_subcommand - p = subprocess.Popen(old_docker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + old_docker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) cmd_output, err = p.communicate() return old_docker_cmd, to_native(cmd_output), err, p.returncode @@ -185,10 +190,12 @@ class Connection(ConnectionBase): # no result yet, must be newer Docker version cmd_args = self._docker_args - new_version_subcommand = ['version', '--format', "'{{.Server.Version}}'"] + new_version_subcommand = ["version", "--format", "'{{.Server.Version}}'"] new_docker_cmd = [self.docker_cmd] + cmd_args + new_version_subcommand - p = subprocess.Popen(new_docker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + new_docker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) cmd_output, err = p.communicate() return new_docker_cmd, to_native(cmd_output), err, p.returncode @@ -196,42 +203,49 @@ class Connection(ConnectionBase): cmd, cmd_output, err, returncode = self._old_docker_version() if returncode == 0: - for line in to_text(cmd_output, errors='surrogate_or_strict').split('\n'): - if line.startswith('Server version:'): # old docker versions + for line in to_text(cmd_output, errors="surrogate_or_strict").split("\n"): + if line.startswith("Server version:"): # old docker versions return self._sanitize_version(line.split()[2]) cmd, cmd_output, err, returncode = self._new_docker_version() if returncode: - raise AnsibleError(f'Docker version check ({to_native(cmd)}) failed: {to_native(err)}') + raise AnsibleError( + f"Docker version check ({to_native(cmd)}) failed: {to_native(err)}" + ) - return self._sanitize_version(to_text(cmd_output, errors='surrogate_or_strict')) + return self._sanitize_version(to_text(cmd_output, errors="surrogate_or_strict")) def _get_docker_remote_user(self): - """ Get the default user configured in the docker container """ - container = self.get_option('remote_addr') + """Get the default user configured in the docker container""" + container = self.get_option("remote_addr") if container in self._container_user_cache: return self._container_user_cache[container] - p = subprocess.Popen([self.docker_cmd, 'inspect', '--format', '{{.Config.User}}', container], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + [self.docker_cmd, "inspect", "--format", "{{.Config.User}}", container], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = p.communicate() - out = to_text(out, errors='surrogate_or_strict') + out = to_text(out, errors="surrogate_or_strict") if p.returncode != 0: - display.warning(f'unable to retrieve default user from docker container: {out} {to_text(err)}') + display.warning( + f"unable to retrieve default user from docker container: {out} {to_text(err)}" + ) self._container_user_cache[container] = None return None # The default exec user is root, unless it was changed in the Dockerfile with USER - user = out.strip() or 'root' + user = out.strip() or "root" self._container_user_cache[container] = user return user def _build_exec_cmd(self, cmd): - """ Build the local docker exec command to run cmd on remote_host + """Build the local docker exec command to run cmd on remote_host - If remote_user is available and is supported by the docker - version we are using, it will be provided to docker exec. + If remote_user is available and is supported by the docker + version we are using, it will be provided to docker exec. """ local_cmd = [self.docker_cmd] @@ -239,34 +253,46 @@ class Connection(ConnectionBase): if self._docker_args: local_cmd += self._docker_args - local_cmd += [b'exec'] + local_cmd += [b"exec"] if self.remote_user is not None: - local_cmd += [b'-u', self.remote_user] + local_cmd += [b"-u", self.remote_user] - if self.get_option('extra_env'): - for k, v in self.get_option('extra_env').items(): - for val, what in ((k, 'Key'), (v, 'Value')): + if self.get_option("extra_env"): + for k, v in self.get_option("extra_env").items(): + for val, what in ((k, "Key"), (v, "Value")): if not isinstance(val, str): raise AnsibleConnectionFailure( - f'Non-string {what.lower()} found for extra_env option. Ambiguous env options must be ' - f'wrapped in quotes to avoid them being interpreted. {what}: {val!r}' + f"Non-string {what.lower()} found for extra_env option. Ambiguous env options must be " + f"wrapped in quotes to avoid them being interpreted. {what}: {val!r}" ) - local_cmd += [b'-e', b"%s=%s" % (to_bytes(k, errors='surrogate_or_strict'), to_bytes(v, errors='surrogate_or_strict'))] + local_cmd += [ + b"-e", + b"%s=%s" + % ( + to_bytes(k, errors="surrogate_or_strict"), + to_bytes(v, errors="surrogate_or_strict"), + ), + ] - if self.get_option('working_dir') is not None: - local_cmd += [b'-w', to_bytes(self.get_option('working_dir'), errors='surrogate_or_strict')] - if self.docker_version != 'dev' and LooseVersion(self.docker_version) < LooseVersion('18.06'): + if self.get_option("working_dir") is not None: + local_cmd += [ + b"-w", + to_bytes(self.get_option("working_dir"), errors="surrogate_or_strict"), + ] + if self.docker_version != "dev" and LooseVersion( + self.docker_version + ) < LooseVersion("18.06"): # https://github.com/docker/cli/pull/732, first appeared in release 18.06.0 raise AnsibleConnectionFailure( - f'Providing the working directory requires Docker CLI version 18.06 or newer. You have Docker CLI version {self.docker_version}.' + f"Providing the working directory requires Docker CLI version 18.06 or newer. You have Docker CLI version {self.docker_version}." ) - if self.get_option('privileged'): - local_cmd += [b'--privileged'] + if self.get_option("privileged"): + local_cmd += [b"--privileged"] # -i is needed to keep stdin open which allows pipelining to work - local_cmd += [b'-i', self.get_option('remote_addr')] + cmd + local_cmd += [b"-i", self.get_option("remote_addr")] + cmd return local_cmd @@ -274,22 +300,23 @@ class Connection(ConnectionBase): # TODO: this is mostly for backwards compatibility, play_context is used as fallback for older versions # docker arguments del self._docker_args[:] - extra_args = self.get_option('docker_extra_args') or getattr(self._play_context, 'docker_extra_args', '') + extra_args = self.get_option("docker_extra_args") or getattr( + self._play_context, "docker_extra_args", "" + ) if extra_args: - self._docker_args += extra_args.split(' ') + self._docker_args += extra_args.split(" ") def _set_conn_data(self): - - ''' initialize for the connection, cannot do only in init since all data is not ready at that point ''' + """initialize for the connection, cannot do only in init since all data is not ready at that point""" self._set_docker_args() - self.remote_user = self.get_option('remote_user') + self.remote_user = self.get_option("remote_user") if self.remote_user is None and self._play_context.remote_user is not None: self.remote_user = self._play_context.remote_user # timeout, use unless default and pc is different, backwards compat - self.timeout = self.get_option('container_timeout') + self.timeout = self.get_option("container_timeout") if self.timeout == 10 and self.timeout != self._play_context.timeout: self.timeout = self._play_context.timeout @@ -300,23 +327,33 @@ class Connection(ConnectionBase): self._set_docker_args() self._version = self._get_docker_version() - if self._version == 'dev': - display.warning('Docker version number is "dev". Will assume latest version.') - if self._version != 'dev' and LooseVersion(self._version) < LooseVersion('1.3'): - raise AnsibleError('docker connection type requires docker 1.3 or higher') + if self._version == "dev": + display.warning( + 'Docker version number is "dev". Will assume latest version.' + ) + if self._version != "dev" and LooseVersion(self._version) < LooseVersion( + "1.3" + ): + raise AnsibleError( + "docker connection type requires docker 1.3 or higher" + ) return self._version def _get_actual_user(self): if self.remote_user is not None: # An explicit user is provided - if self.docker_version == 'dev' or LooseVersion(self.docker_version) >= LooseVersion('1.7'): + if self.docker_version == "dev" or LooseVersion( + self.docker_version + ) >= LooseVersion("1.7"): # Support for specifying the exec user was added in docker 1.7 return self.remote_user else: self.remote_user = None actual_user = self._get_docker_remote_user() - if actual_user != self.get_option('remote_user'): - display.warning(f'docker {self.docker_version} does not support remote_user, using container default: {self.actual_user or "?"}') + if actual_user != self.get_option("remote_user"): + display.warning( + f'docker {self.docker_version} does not support remote_user, using container default: {self.actual_user or "?"}' + ) return actual_user elif self._display.verbosity > 2: # Since we are not setting the actual_user, look it up so we have it for logging later @@ -327,27 +364,30 @@ class Connection(ConnectionBase): return None def _connect(self, port=None): - """ Connect to the container. Nothing to do """ + """Connect to the container. Nothing to do""" super(Connection, self)._connect() if not self._connected: self._set_conn_data() actual_user = self._get_actual_user() - display.vvv(f"ESTABLISH DOCKER CONNECTION FOR USER: {actual_user or '?'}", host=self.get_option('remote_addr')) + display.vvv( + f"ESTABLISH DOCKER CONNECTION FOR USER: {actual_user or '?'}", + host=self.get_option("remote_addr"), + ) self._connected = True def exec_command(self, cmd, in_data=None, sudoable=False): - """ Run a command on the docker host """ + """Run a command on the docker host""" self._set_conn_data() super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) - local_cmd = self._build_exec_cmd([self._play_context.executable, '-c', cmd]) + local_cmd = self._build_exec_cmd([self._play_context.executable, "-c", cmd]) - display.vvv(f"EXEC {to_text(local_cmd)}", host=self.get_option('remote_addr')) + display.vvv(f"EXEC {to_text(local_cmd)}", host=self.get_option("remote_addr")) display.debug("opening command with Popen()") - 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] p = subprocess.Popen( local_cmd, @@ -358,19 +398,32 @@ class Connection(ConnectionBase): display.debug("done running command with Popen()") if self.become and self.become.expect_prompt() and sudoable: - fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) - fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK) + fcntl.fcntl( + p.stdout, + fcntl.F_SETFL, + fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK, + ) + fcntl.fcntl( + p.stderr, + fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK, + ) selector = selectors.DefaultSelector() selector.register(p.stdout, selectors.EVENT_READ) selector.register(p.stderr, selectors.EVENT_READ) - become_output = b'' + become_output = b"" try: - while not self.become.check_success(become_output) and not self.become.check_password_prompt(become_output): + while not self.become.check_success( + become_output + ) and not self.become.check_password_prompt(become_output): events = selector.select(self.timeout) if not events: stdout, stderr = p.communicate() - raise AnsibleError('timeout waiting for privilege escalation password prompt:\n' + to_native(become_output)) + raise AnsibleError( + "timeout waiting for privilege escalation password prompt:\n" + + to_native(become_output) + ) for key, event in events: if key.fileobj == p.stdout: @@ -380,16 +433,31 @@ class Connection(ConnectionBase): if not chunk: stdout, stderr = p.communicate() - raise AnsibleError('privilege output closed while waiting for password prompt:\n' + to_native(become_output)) + raise AnsibleError( + "privilege output closed while waiting for password prompt:\n" + + to_native(become_output) + ) become_output += chunk finally: selector.close() if not self.become.check_success(become_output): - become_pass = self.become.get_option('become_pass', playcontext=self._play_context) - p.stdin.write(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n') - fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) - fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK) + become_pass = self.become.get_option( + "become_pass", playcontext=self._play_context + ) + p.stdin.write( + to_bytes(become_pass, errors="surrogate_or_strict") + b"\n" + ) + fcntl.fcntl( + p.stdout, + fcntl.F_SETFL, + fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK, + ) + fcntl.fcntl( + p.stderr, + fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK, + ) display.debug("getting output with communicate()") stdout, stderr = p.communicate(in_data) @@ -399,17 +467,18 @@ class Connection(ConnectionBase): return (p.returncode, stdout, stderr) def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path + """Make sure that we put files into a standard path - If a path is relative, then we need to choose where to put it. - ssh chooses $HOME but we are not guaranteed that a home dir will - exist in any given chroot. So for now we are choosing "/" instead. - This also happens to be the former default. + If a path is relative, then we need to choose where to put it. + ssh chooses $HOME but we are not guaranteed that a home dir will + exist in any given chroot. So for now we are choosing "/" instead. + This also happens to be the former default. - Can revisit using $HOME instead if it is a problem - ''' + Can revisit using $HOME instead if it is a problem + """ if getattr(self._shell, "_IS_WINDOWS", False): import ntpath + return ntpath.normpath(remote_path) else: if not remote_path.startswith(os.path.sep): @@ -417,57 +486,79 @@ class Connection(ConnectionBase): return os.path.normpath(remote_path) def put_file(self, in_path, out_path): - """ Transfer a file from local to docker container """ + """Transfer a file from local to docker container""" self._set_conn_data() super(Connection, self).put_file(in_path, out_path) - display.vvv(f"PUT {in_path} TO {out_path}", host=self.get_option('remote_addr')) + display.vvv(f"PUT {in_path} TO {out_path}", host=self.get_option("remote_addr")) out_path = self._prefix_login_path(out_path) - if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')): + if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")): raise AnsibleFileNotFound( - f"file or module does not exist: {to_native(in_path)}") + f"file or module does not exist: {to_native(in_path)}" + ) out_path = quote(out_path) # Older docker does not have native support for copying files into # running containers, so we use docker exec to implement this # Although docker version 1.8 and later provide support, the # owner and group of the files are always set to root - with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file: + with open(to_bytes(in_path, errors="surrogate_or_strict"), "rb") as in_file: if not os.fstat(in_file.fileno()).st_size: - count = ' count=0' + count = " count=0" else: - count = '' - args = self._build_exec_cmd([self._play_context.executable, "-c", f"dd of={out_path} bs={BUFSIZE}{count}"]) - args = [to_bytes(i, errors='surrogate_or_strict') for i in args] + count = "" + args = self._build_exec_cmd( + [ + self._play_context.executable, + "-c", + f"dd of={out_path} bs={BUFSIZE}{count}", + ] + ) + args = [to_bytes(i, errors="surrogate_or_strict") for i in args] try: - p = subprocess.Popen(args, stdin=in_file, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + args, stdin=in_file, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) except OSError: - raise AnsibleError("docker connection requires dd command in the container to put files") + raise AnsibleError( + "docker connection requires dd command in the container to put files" + ) stdout, stderr = p.communicate() if p.returncode != 0: - raise AnsibleError(f"failed to transfer file {to_native(in_path)} to {to_native(out_path)}:\n{to_native(stdout)}\n{to_native(stderr)}") + raise AnsibleError( + f"failed to transfer file {to_native(in_path)} to {to_native(out_path)}:\n{to_native(stdout)}\n{to_native(stderr)}" + ) def fetch_file(self, in_path, out_path): - """ Fetch a file from container to local. """ + """Fetch a file from container to local.""" self._set_conn_data() super(Connection, self).fetch_file(in_path, out_path) - display.vvv(f"FETCH {in_path} TO {out_path}", host=self.get_option('remote_addr')) + display.vvv( + f"FETCH {in_path} TO {out_path}", host=self.get_option("remote_addr") + ) in_path = self._prefix_login_path(in_path) # out_path is the final file path, but docker takes a directory, not a # file path out_dir = os.path.dirname(out_path) - args = [self.docker_cmd, "cp", f"{self.get_option('remote_addr')}:{in_path}", out_dir] - args = [to_bytes(i, errors='surrogate_or_strict') for i in args] + args = [ + self.docker_cmd, + "cp", + f"{self.get_option('remote_addr')}:{in_path}", + out_dir, + ] + args = [to_bytes(i, errors="surrogate_or_strict") for i in args] - p = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) p.communicate() if getattr(self._shell, "_IS_WINDOWS", False): import ntpath + actual_out_path = ntpath.join(out_dir, ntpath.basename(in_path)) else: actual_out_path = os.path.join(out_dir, os.path.basename(in_path)) @@ -475,25 +566,40 @@ class Connection(ConnectionBase): if p.returncode != 0: # Older docker does not have native support for fetching files command `cp` # If `cp` fails, try to use `dd` instead - args = self._build_exec_cmd([self._play_context.executable, "-c", f"dd if={in_path} bs={BUFSIZE}"]) - args = [to_bytes(i, errors='surrogate_or_strict') for i in args] - with open(to_bytes(actual_out_path, errors='surrogate_or_strict'), 'wb') as out_file: + args = self._build_exec_cmd( + [self._play_context.executable, "-c", f"dd if={in_path} bs={BUFSIZE}"] + ) + args = [to_bytes(i, errors="surrogate_or_strict") for i in args] + with open( + to_bytes(actual_out_path, errors="surrogate_or_strict"), "wb" + ) as out_file: try: - p = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=out_file, stderr=subprocess.PIPE) + p = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=out_file, + stderr=subprocess.PIPE, + ) except OSError: - raise AnsibleError("docker connection requires dd command in the container to put files") + raise AnsibleError( + "docker connection requires dd command in the container to put files" + ) stdout, stderr = p.communicate() if p.returncode != 0: - raise AnsibleError(f"failed to fetch file {in_path} to {out_path}:\n{stdout}\n{stderr}") + raise AnsibleError( + f"failed to fetch file {in_path} to {out_path}:\n{stdout}\n{stderr}" + ) # Rename if needed if actual_out_path != out_path: - os.rename(to_bytes(actual_out_path, errors='strict'), to_bytes(out_path, errors='strict')) + os.rename( + to_bytes(actual_out_path, errors="strict"), + to_bytes(out_path, errors="strict"), + ) def close(self): - """ Terminate the connection. Nothing to do for Docker""" + """Terminate the connection. Nothing to do for Docker""" super(Connection, self).close() self._connected = False diff --git a/plugins/connection/docker_api.py b/plugins/connection/docker_api.py index df7f8158..b0d06bca 100644 --- a/plugins/connection/docker_api.py +++ b/plugins/connection/docker_api.py @@ -4,6 +4,7 @@ from __future__ import annotations + DOCUMENTATION = r""" author: - Felix Fontein (@felixfontein) @@ -107,11 +108,15 @@ options: import os import os.path -from ansible.errors import AnsibleFileNotFound, AnsibleConnectionFailure +from ansible.errors import AnsibleConnectionFailure, AnsibleFileNotFound from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.plugins.connection import ConnectionBase from ansible.utils.display import Display - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, + NotFound, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( RequestException, ) @@ -121,17 +126,16 @@ from ansible_collections.community.docker.plugins.module_utils.copy import ( fetch_file, put_file, ) - -from ansible_collections.community.docker.plugins.plugin_utils.socket_handler import ( - DockerSocketHandler, +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, ) from ansible_collections.community.docker.plugins.plugin_utils.common_api import ( AnsibleDockerClient, ) +from ansible_collections.community.docker.plugins.plugin_utils.socket_handler import ( + DockerSocketHandler, +) -from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException, NotFound - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion MIN_DOCKER_API = None @@ -140,23 +144,29 @@ display = Display() class Connection(ConnectionBase): - ''' Local docker based connections ''' + """Local docker based connections""" - transport = 'community.docker.docker_api' + transport = "community.docker.docker_api" has_pipelining = True def _call_client(self, callable, not_found_can_be_resource=False): - remote_addr = self.get_option('remote_addr') + remote_addr = self.get_option("remote_addr") try: return callable() except NotFound as e: if not_found_can_be_resource: - raise AnsibleConnectionFailure(f'Could not find container "{remote_addr}" or resource in it ({e})') + raise AnsibleConnectionFailure( + f'Could not find container "{remote_addr}" or resource in it ({e})' + ) else: - raise AnsibleConnectionFailure(f'Could not find container "{remote_addr}" ({e})') + raise AnsibleConnectionFailure( + f'Could not find container "{remote_addr}" ({e})' + ) except APIError as e: if e.response is not None and e.response.status_code == 409: - raise AnsibleConnectionFailure(f'The container "{remote_addr}" has been paused ({e})') + raise AnsibleConnectionFailure( + f'The container "{remote_addr}" has been paused ({e})' + ) self.client.fail( f'An unexpected Docker error occurred for container "{remote_addr}": {e}' ) @@ -177,18 +187,23 @@ class Connection(ConnectionBase): # Windows uses Powershell modules if getattr(self._shell, "_IS_WINDOWS", False): - self.module_implementation_preferences = ('.ps1', '.exe', '') + self.module_implementation_preferences = (".ps1", ".exe", "") self.actual_user = None def _connect(self, port=None): - """ Connect to the container. Nothing to do """ + """Connect to the container. Nothing to do""" super(Connection, self)._connect() if not self._connected: - self.actual_user = self.get_option('remote_user') - display.vvv(f"ESTABLISH DOCKER CONNECTION FOR USER: {self.actual_user or '?'}", host=self.get_option('remote_addr')) + self.actual_user = self.get_option("remote_user") + display.vvv( + f"ESTABLISH DOCKER CONNECTION FOR USER: {self.actual_user or '?'}", + host=self.get_option("remote_addr"), + ) if self.client is None: - self.client = AnsibleDockerClient(self, min_docker_api_version=MIN_DOCKER_API) + self.client = AnsibleDockerClient( + self, min_docker_api_version=MIN_DOCKER_API + ) self._connected = True if self.actual_user is None and display.verbosity > 2: @@ -196,95 +211,125 @@ class Connection(ConnectionBase): # Only do this if display verbosity is high enough that we'll need the value # This saves overhead from calling into docker when we do not need to display.vvv("Trying to determine actual user") - result = self._call_client(lambda: self.client.get_json('/containers/{0}/json', self.get_option('remote_addr'))) - if result.get('Config'): - self.actual_user = result['Config'].get('User') + result = self._call_client( + lambda: self.client.get_json( + "/containers/{0}/json", self.get_option("remote_addr") + ) + ) + if result.get("Config"): + self.actual_user = result["Config"].get("User") if self.actual_user is not None: display.vvv(f"Actual user is '{self.actual_user}'") def exec_command(self, cmd, in_data=None, sudoable=False): - """ Run a command on the docker host """ + """Run a command on the docker host""" super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) - command = [self._play_context.executable, '-c', to_text(cmd)] + command = [self._play_context.executable, "-c", to_text(cmd)] do_become = self.become and self.become.expect_prompt() and sudoable - stdin_part = f', with stdin ({len(in_data)} bytes)' if in_data is not None else '' - become_part = ', with become prompt' if do_become else '' + stdin_part = ( + f", with stdin ({len(in_data)} bytes)" if in_data is not None else "" + ) + become_part = ", with become prompt" if do_become else "" display.vvv( f"EXEC {to_text(command)}{stdin_part}{become_part}", - host=self.get_option('remote_addr') + host=self.get_option("remote_addr"), ) need_stdin = True if (in_data is not None) or do_become else False data = { - 'Container': self.get_option('remote_addr'), - 'User': self.get_option('remote_user') or '', - 'Privileged': self.get_option('privileged'), - 'Tty': False, - 'AttachStdin': need_stdin, - 'AttachStdout': True, - 'AttachStderr': True, - 'Cmd': command, + "Container": self.get_option("remote_addr"), + "User": self.get_option("remote_user") or "", + "Privileged": self.get_option("privileged"), + "Tty": False, + "AttachStdin": need_stdin, + "AttachStdout": True, + "AttachStderr": True, + "Cmd": command, } - if 'detachKeys' in self.client._general_configs: - data['detachKeys'] = self.client._general_configs['detachKeys'] + if "detachKeys" in self.client._general_configs: + data["detachKeys"] = self.client._general_configs["detachKeys"] - if self.get_option('extra_env'): - data['Env'] = [] - for k, v in self.get_option('extra_env').items(): - for val, what in ((k, 'Key'), (v, 'Value')): + if self.get_option("extra_env"): + data["Env"] = [] + for k, v in self.get_option("extra_env").items(): + for val, what in ((k, "Key"), (v, "Value")): if not isinstance(val, str): raise AnsibleConnectionFailure( - f'Non-string {what.lower()} found for extra_env option. Ambiguous env options must be ' - f'wrapped in quotes to avoid them being interpreted. {what}: {val!r}' + f"Non-string {what.lower()} found for extra_env option. Ambiguous env options must be " + f"wrapped in quotes to avoid them being interpreted. {what}: {val!r}" ) - kk = to_text(k, errors='surrogate_or_strict') - vv = to_text(v, errors='surrogate_or_strict') - data['Env'].append(f'{kk}={vv}') + kk = to_text(k, errors="surrogate_or_strict") + vv = to_text(v, errors="surrogate_or_strict") + data["Env"].append(f"{kk}={vv}") - if self.get_option('working_dir') is not None: - data['WorkingDir'] = self.get_option('working_dir') - if self.client.docker_api_version < LooseVersion('1.35'): + if self.get_option("working_dir") is not None: + data["WorkingDir"] = self.get_option("working_dir") + if self.client.docker_api_version < LooseVersion("1.35"): raise AnsibleConnectionFailure( - 'Providing the working directory requires Docker API version 1.35 or newer.' - f' The Docker daemon the connection is using has API version {self.client.docker_api_version_str}.' + "Providing the working directory requires Docker API version 1.35 or newer." + f" The Docker daemon the connection is using has API version {self.client.docker_api_version_str}." ) - exec_data = self._call_client(lambda: self.client.post_json_to_json('/containers/{0}/exec', self.get_option('remote_addr'), data=data)) - exec_id = exec_data['Id'] + exec_data = self._call_client( + lambda: self.client.post_json_to_json( + "/containers/{0}/exec", self.get_option("remote_addr"), data=data + ) + ) + exec_id = exec_data["Id"] - data = { - 'Tty': False, - 'Detach': False - } + data = {"Tty": False, "Detach": False} if need_stdin: - exec_socket = self._call_client(lambda: self.client.post_json_to_stream_socket('/exec/{0}/start', exec_id, data=data)) + exec_socket = self._call_client( + lambda: self.client.post_json_to_stream_socket( + "/exec/{0}/start", exec_id, data=data + ) + ) try: - with DockerSocketHandler(display, exec_socket, container=self.get_option('remote_addr')) as exec_socket_handler: + with DockerSocketHandler( + display, exec_socket, container=self.get_option("remote_addr") + ) as exec_socket_handler: if do_become: - become_output = [b''] + become_output = [b""] def append_become_output(stream_id, data): become_output[0] += data - exec_socket_handler.set_block_done_callback(append_become_output) + exec_socket_handler.set_block_done_callback( + append_become_output + ) - while not self.become.check_success(become_output[0]) and not self.become.check_password_prompt(become_output[0]): - if not exec_socket_handler.select(self.get_option('container_timeout')): + while not self.become.check_success( + become_output[0] + ) and not self.become.check_password_prompt(become_output[0]): + if not exec_socket_handler.select( + self.get_option("container_timeout") + ): stdout, stderr = exec_socket_handler.consume() - raise AnsibleConnectionFailure('timeout waiting for privilege escalation password prompt:\n' + to_native(become_output[0])) + raise AnsibleConnectionFailure( + "timeout waiting for privilege escalation password prompt:\n" + + to_native(become_output[0]) + ) if exec_socket_handler.is_eof(): - raise AnsibleConnectionFailure('privilege output closed while waiting for password prompt:\n' + to_native(become_output[0])) + raise AnsibleConnectionFailure( + "privilege output closed while waiting for password prompt:\n" + + to_native(become_output[0]) + ) if not self.become.check_success(become_output[0]): - become_pass = self.become.get_option('become_pass', playcontext=self._play_context) - exec_socket_handler.write(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n') + become_pass = self.become.get_option( + "become_pass", playcontext=self._play_context + ) + exec_socket_handler.write( + to_bytes(become_pass, errors="surrogate_or_strict") + + b"\n" + ) if in_data is not None: exec_socket_handler.write(in_data) @@ -293,25 +338,36 @@ class Connection(ConnectionBase): finally: exec_socket.close() else: - stdout, stderr = self._call_client(lambda: self.client.post_json_to_stream( - '/exec/{0}/start', exec_id, stream=False, demux=True, tty=False, data=data)) + stdout, stderr = self._call_client( + lambda: self.client.post_json_to_stream( + "/exec/{0}/start", + exec_id, + stream=False, + demux=True, + tty=False, + data=data, + ) + ) - result = self._call_client(lambda: self.client.get_json('/exec/{0}/json', exec_id)) + result = self._call_client( + lambda: self.client.get_json("/exec/{0}/json", exec_id) + ) - return result.get('ExitCode') or 0, stdout or b'', stderr or b'' + return result.get("ExitCode") or 0, stdout or b"", stderr or b"" def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path + """Make sure that we put files into a standard path - If a path is relative, then we need to choose where to put it. - ssh chooses $HOME but we are not guaranteed that a home dir will - exist in any given chroot. So for now we are choosing "/" instead. - This also happens to be the former default. + If a path is relative, then we need to choose where to put it. + ssh chooses $HOME but we are not guaranteed that a home dir will + exist in any given chroot. So for now we are choosing "/" instead. + This also happens to be the former default. - Can revisit using $HOME instead if it is a problem - ''' + Can revisit using $HOME instead if it is a problem + """ if getattr(self._shell, "_IS_WINDOWS", False): import ntpath + return ntpath.normpath(remote_path) else: if not remote_path.startswith(os.path.sep): @@ -319,21 +375,21 @@ class Connection(ConnectionBase): return os.path.normpath(remote_path) def put_file(self, in_path, out_path): - """ Transfer a file from local to docker container """ + """Transfer a file from local to docker container""" super(Connection, self).put_file(in_path, out_path) - display.vvv(f"PUT {in_path} TO {out_path}", host=self.get_option('remote_addr')) + display.vvv(f"PUT {in_path} TO {out_path}", host=self.get_option("remote_addr")) out_path = self._prefix_login_path(out_path) if self.actual_user not in self.ids: - dummy, ids, dummy = self.exec_command(b'id -u && id -g') - remote_addr = self.get_option('remote_addr') + dummy, ids, dummy = self.exec_command(b"id -u && id -g") + remote_addr = self.get_option("remote_addr") try: user_id, group_id = ids.splitlines() self.ids[self.actual_user] = int(user_id), int(group_id) display.vvvv( f'PUT: Determined uid={user_id} and gid={group_id} for user "{self.actual_user}"', - host=remote_addr + host=remote_addr, ) except Exception as e: raise AnsibleConnectionFailure( @@ -345,7 +401,7 @@ class Connection(ConnectionBase): self._call_client( lambda: put_file( self.client, - container=self.get_option('remote_addr'), + container=self.get_option("remote_addr"), in_path=in_path, out_path=out_path, user_id=user_id, @@ -361,9 +417,11 @@ class Connection(ConnectionBase): raise AnsibleConnectionFailure(to_native(exc)) def fetch_file(self, in_path, out_path): - """ Fetch a file from container to local. """ + """Fetch a file from container to local.""" super(Connection, self).fetch_file(in_path, out_path) - display.vvv(f"FETCH {in_path} TO {out_path}", host=self.get_option('remote_addr')) + display.vvv( + f"FETCH {in_path} TO {out_path}", host=self.get_option("remote_addr") + ) in_path = self._prefix_login_path(in_path) @@ -371,11 +429,13 @@ class Connection(ConnectionBase): self._call_client( lambda: fetch_file( self.client, - container=self.get_option('remote_addr'), + container=self.get_option("remote_addr"), in_path=in_path, out_path=out_path, follow_links=True, - log=lambda msg: display.vvvv(msg, host=self.get_option('remote_addr')), + log=lambda msg: display.vvvv( + msg, host=self.get_option("remote_addr") + ), ), not_found_can_be_resource=True, ) @@ -385,7 +445,7 @@ class Connection(ConnectionBase): raise AnsibleConnectionFailure(to_native(exc)) def close(self): - """ Terminate the connection. Nothing to do for Docker""" + """Terminate the connection. Nothing to do for Docker""" super(Connection, self).close() self._connected = False diff --git a/plugins/connection/nsenter.py b/plugins/connection/nsenter.py index a18f4dfd..a3e86022 100644 --- a/plugins/connection/nsenter.py +++ b/plugins/connection/nsenter.py @@ -7,6 +7,7 @@ from __future__ import annotations + DOCUMENTATION = r""" name: nsenter short_description: execute on host running controller container @@ -39,11 +40,11 @@ notes: PID namespace (C(--pid host))." """ +import fcntl import os import pty import selectors import subprocess -import fcntl import ansible.constants as C from ansible.errors import AnsibleError @@ -57,10 +58,9 @@ display = Display() class Connection(ConnectionBase): - '''Connections to a container host using nsenter - ''' + """Connections to a container host using nsenter""" - transport = 'community.docker.nsenter' + transport = "community.docker.nsenter" has_pipelining = False def __init__(self, *args, **kwargs): @@ -89,9 +89,11 @@ class Connection(ConnectionBase): executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else None - if not os.path.exists(to_bytes(executable, errors='surrogate_or_strict')): - raise AnsibleError(f"failed to find the executable specified {executable}." - " Please verify if the executable exists and re-try.") + if not os.path.exists(to_bytes(executable, errors="surrogate_or_strict")): + raise AnsibleError( + f"failed to find the executable specified {executable}." + " Please verify if the executable exists and re-try." + ) # Rewrite the provided command to prefix it with nsenter nsenter_cmd_parts = [ @@ -148,19 +150,32 @@ class Connection(ConnectionBase): display.debug("done running command with Popen()") if self.become and self.become.expect_prompt() and sudoable: - fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) - fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK) + fcntl.fcntl( + p.stdout, + fcntl.F_SETFL, + fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK, + ) + fcntl.fcntl( + p.stderr, + fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK, + ) selector = selectors.DefaultSelector() selector.register(p.stdout, selectors.EVENT_READ) selector.register(p.stderr, selectors.EVENT_READ) - become_output = b'' + become_output = b"" try: - while not self.become.check_success(become_output) and not self.become.check_password_prompt(become_output): + while not self.become.check_success( + become_output + ) and not self.become.check_password_prompt(become_output): events = selector.select(self._play_context.timeout) if not events: stdout, stderr = p.communicate() - raise AnsibleError('timeout waiting for privilege escalation password prompt:\n' + to_native(become_output)) + raise AnsibleError( + "timeout waiting for privilege escalation password prompt:\n" + + to_native(become_output) + ) for key, event in events: if key.fileobj == p.stdout: @@ -170,20 +185,38 @@ class Connection(ConnectionBase): if not chunk: stdout, stderr = p.communicate() - raise AnsibleError('privilege output closed while waiting for password prompt:\n' + to_native(become_output)) + raise AnsibleError( + "privilege output closed while waiting for password prompt:\n" + + to_native(become_output) + ) become_output += chunk finally: selector.close() if not self.become.check_success(become_output): - become_pass = self.become.get_option('become_pass', playcontext=self._play_context) + become_pass = self.become.get_option( + "become_pass", playcontext=self._play_context + ) if master is None: - p.stdin.write(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n') + p.stdin.write( + to_bytes(become_pass, errors="surrogate_or_strict") + b"\n" + ) else: - os.write(master, to_bytes(become_pass, errors='surrogate_or_strict') + b'\n') + os.write( + master, + to_bytes(become_pass, errors="surrogate_or_strict") + b"\n", + ) - fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) - fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK) + fcntl.fcntl( + p.stdout, + fcntl.F_SETFL, + fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK, + ) + fcntl.fcntl( + p.stderr, + fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK, + ) display.debug("getting output with communicate()") stdout, stderr = p.communicate(in_data) @@ -220,14 +253,18 @@ class Connection(ConnectionBase): try: rc, out, err = self.exec_command(cmd=["cat", in_path]) - display.vvv(f"FETCH {in_path} TO {out_path}", host=self._play_context.remote_addr) + display.vvv( + f"FETCH {in_path} TO {out_path}", host=self._play_context.remote_addr + ) if rc != 0: raise AnsibleError(f"failed to transfer file to {in_path}: {err}") - with open(to_bytes(out_path, errors='surrogate_or_strict'), 'wb') as out_file: + with open( + to_bytes(out_path, errors="surrogate_or_strict"), "wb" + ) as out_file: out_file.write(out) except IOError as e: raise AnsibleError(f"failed to transfer file to {to_native(out_path)}: {e}") def close(self): - ''' terminate the connection; nothing to do here ''' + """terminate the connection; nothing to do here""" self._connected = False diff --git a/plugins/doc_fragments/attributes.py b/plugins/doc_fragments/attributes.py index 614d5a26..709a28e8 100644 --- a/plugins/doc_fragments/attributes.py +++ b/plugins/doc_fragments/attributes.py @@ -34,7 +34,7 @@ attributes: """ # Should be used together with the standard fragment - INFO_MODULE = r''' + INFO_MODULE = r""" options: {} attributes: check_mode: @@ -45,9 +45,9 @@ attributes: support: N/A details: - This action does not modify state. -''' +""" - ACTIONGROUP_DOCKER = r''' + ACTIONGROUP_DOCKER = r""" options: {} attributes: action_group: @@ -56,7 +56,7 @@ attributes: membership: - community.docker.docker - docker -''' +""" CONN = r""" options: {} @@ -77,7 +77,7 @@ attributes: """ # Should be used together with the standard fragment and the FACTS fragment - FACTS_MODULE = r''' + FACTS_MODULE = r""" options: {} attributes: check_mode: @@ -90,7 +90,7 @@ attributes: - This action does not modify state. facts: support: full -''' +""" FILES = r""" options: {} diff --git a/plugins/doc_fragments/compose_v2.py b/plugins/doc_fragments/compose_v2.py index 6aeae8d2..eca77e32 100644 --- a/plugins/doc_fragments/compose_v2.py +++ b/plugins/doc_fragments/compose_v2.py @@ -74,8 +74,8 @@ notes: """ # The following needs to be kept in sync with the compose_v2 module utils - MINIMUM_VERSION = r''' + MINIMUM_VERSION = r""" options: {} requirements: - "Docker CLI with Docker compose plugin 2.18.0 or later" -''' +""" diff --git a/plugins/doc_fragments/docker.py b/plugins/doc_fragments/docker.py index 59fd084a..c94b97ef 100644 --- a/plugins/doc_fragments/docker.py +++ b/plugins/doc_fragments/docker.py @@ -123,7 +123,7 @@ notes: # For plugins: allow to define common options with Ansible variables - VAR_NAMES = r''' + VAR_NAMES = r""" options: docker_host: vars: @@ -154,11 +154,11 @@ options: validate_certs: vars: - name: ansible_docker_validate_certs -''' +""" # Additional, more specific stuff for minimal Docker SDK for Python version < 2.0 - DOCKER_PY_1_DOCUMENTATION = r''' + DOCKER_PY_1_DOCUMENTATION = r""" options: {} notes: - This module uses the L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) to @@ -170,12 +170,12 @@ requirements: modules should *not* be installed at the same time. Also note that when both modules are installed and one of them is uninstalled, the other might no longer function and a reinstall of it is required." -''' +""" # Additional, more specific stuff for minimal Docker SDK for Python version >= 2.0. # Note that Docker SDK for Python >= 2.0 requires Python 2.7 or newer. - DOCKER_PY_2_DOCUMENTATION = r''' + DOCKER_PY_2_DOCUMENTATION = r""" options: {} notes: - This module uses the L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) to @@ -186,10 +186,10 @@ requirements: Python module has been superseded by L(docker,https://pypi.org/project/docker/) (see L(here,https://github.com/docker/docker-py/issues/1310) for details). This module does *not* work with docker-py." -''' +""" # Docker doc fragment when using the vendored API access code - API_DOCUMENTATION = r''' + API_DOCUMENTATION = r""" options: docker_host: description: @@ -302,10 +302,10 @@ requirements: - pywin32 (when using named pipes on Windows 32) - paramiko (when using SSH with O(use_ssh_client=false)) - pyOpenSSL (when using TLS) -''' +""" # Docker doc fragment when using the Docker CLI - CLI_DOCUMENTATION = r''' + CLI_DOCUMENTATION = r""" options: docker_cli: description: @@ -403,4 +403,4 @@ notes: U(https://docs.docker.com/machine/reference/env/) for more details. - This module does B(not) use the L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) to communicate with the Docker daemon. It directly calls the Docker CLI program. -''' +""" diff --git a/plugins/inventory/docker_containers.py b/plugins/inventory/docker_containers.py index df2c6c40..1d5a2388 100644 --- a/plugins/inventory/docker_containers.py +++ b/plugins/inventory/docker_containers.py @@ -107,7 +107,7 @@ options: version_added: 3.5.0 """ -EXAMPLES = ''' +EXAMPLES = """ --- # Minimal example using local Docker daemon plugin: community.docker.docker_containers @@ -167,13 +167,16 @@ filters: inventory_hostname.startswith("a") # Exclude all containers that did not match any of the above filters - exclude: true -''' +""" import re from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( RequestException, ) @@ -183,64 +186,66 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( from ansible_collections.community.docker.plugins.plugin_utils.common_api import ( AnsibleDockerClient, ) - -from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe -from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host +from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import ( + filter_host, + parse_filters, +) + MIN_DOCKER_API = None class InventoryModule(BaseInventoryPlugin, Constructable): - ''' Host inventory parser for ansible using Docker daemon as source. ''' + """Host inventory parser for ansible using Docker daemon as source.""" - NAME = 'community.docker.docker_containers' + NAME = "community.docker.docker_containers" def _slugify(self, value): - slug = re.sub(r'[^\w-]', '_', value).lower().lstrip('_') - return f'docker_{slug}' + slug = re.sub(r"[^\w-]", "_", value).lower().lstrip("_") + return f"docker_{slug}" def _populate(self, client): - strict = self.get_option('strict') + strict = self.get_option("strict") - ssh_port = self.get_option('private_ssh_port') - default_ip = self.get_option('default_ip') - hostname = self.get_option('docker_host') - verbose_output = self.get_option('verbose_output') - connection_type = self.get_option('connection_type') - add_legacy_groups = self.get_option('add_legacy_groups') + ssh_port = self.get_option("private_ssh_port") + default_ip = self.get_option("default_ip") + hostname = self.get_option("docker_host") + verbose_output = self.get_option("verbose_output") + connection_type = self.get_option("connection_type") + add_legacy_groups = self.get_option("add_legacy_groups") try: params = { - 'limit': -1, - 'all': 1, - 'size': 0, - 'trunc_cmd': 0, - 'since': None, - 'before': None, + "limit": -1, + "all": 1, + "size": 0, + "trunc_cmd": 0, + "since": None, + "before": None, } - containers = client.get_json('/containers/json', params=params) + containers = client.get_json("/containers/json", params=params) except APIError as exc: raise AnsibleError(f"Error listing containers: {exc}") if add_legacy_groups: - self.inventory.add_group('running') - self.inventory.add_group('stopped') + self.inventory.add_group("running") + self.inventory.add_group("stopped") extra_facts = {} - if self.get_option('configure_docker_daemon'): + if self.get_option("configure_docker_daemon"): for option_name, var_name in DOCKER_COMMON_ARGS_VARS.items(): value = self.get_option(option_name) if value is not None: extra_facts[var_name] = value - filters = parse_filters(self.get_option('filters')) + filters = parse_filters(self.get_option("filters")) for container in containers: - id = container.get('Id') + id = container.get("Id") short_id = id[:13] try: - name = container.get('Names', list())[0].lstrip('/') + name = container.get("Names", list())[0].lstrip("/") full_name = name except IndexError: name = short_id @@ -253,66 +258,72 @@ class InventoryModule(BaseInventoryPlugin, Constructable): full_facts = dict() try: - inspect = client.get_json('/containers/{0}/json', id) + inspect = client.get_json("/containers/{0}/json", id) except APIError as exc: raise AnsibleError(f"Error inspecting container {name} - {exc}") - state = inspect.get('State') or dict() - config = inspect.get('Config') or dict() - labels = config.get('Labels') or dict() + state = inspect.get("State") or dict() + config = inspect.get("Config") or dict() + labels = config.get("Labels") or dict() - running = state.get('Running') + running = state.get("Running") groups = [] # Add container to groups - image_name = config.get('Image') + image_name = config.get("Image") if image_name and add_legacy_groups: - groups.append(f'image_{image_name}') + groups.append(f"image_{image_name}") - stack_name = labels.get('com.docker.stack.namespace') + stack_name = labels.get("com.docker.stack.namespace") if stack_name: - full_facts['docker_stack'] = stack_name + full_facts["docker_stack"] = stack_name if add_legacy_groups: - groups.append(f'stack_{stack_name}') + groups.append(f"stack_{stack_name}") - service_name = labels.get('com.docker.swarm.service.name') + service_name = labels.get("com.docker.swarm.service.name") if service_name: - full_facts['docker_service'] = service_name + full_facts["docker_service"] = service_name if add_legacy_groups: - groups.append(f'service_{service_name}') + groups.append(f"service_{service_name}") ansible_connection = None - if connection_type == 'ssh': + if connection_type == "ssh": # Figure out ssh IP and Port try: # Lookup the public facing port Nat'ed to ssh port. - network_settings = inspect.get('NetworkSettings') or {} - port_settings = network_settings.get('Ports') or {} - port = port_settings.get(f'{ssh_port}/tcp')[0] + network_settings = inspect.get("NetworkSettings") or {} + port_settings = network_settings.get("Ports") or {} + port = port_settings.get(f"{ssh_port}/tcp")[0] except (IndexError, AttributeError, TypeError): port = dict() try: - ip = default_ip if port['HostIp'] == '0.0.0.0' else port['HostIp'] + ip = default_ip if port["HostIp"] == "0.0.0.0" else port["HostIp"] except KeyError: - ip = '' + ip = "" - facts.update(dict( - ansible_ssh_host=ip, - ansible_ssh_port=port.get('HostPort', 0), - )) - elif connection_type == 'docker-cli': - facts.update(dict( - ansible_host=full_name, - )) - ansible_connection = 'community.docker.docker' - elif connection_type == 'docker-api': - facts.update(dict( - ansible_host=full_name, - )) + facts.update( + dict( + ansible_ssh_host=ip, + ansible_ssh_port=port.get("HostPort", 0), + ) + ) + elif connection_type == "docker-cli": + facts.update( + dict( + ansible_host=full_name, + ) + ) + ansible_connection = "community.docker.docker" + elif connection_type == "docker-api": + facts.update( + dict( + ansible_host=full_name, + ) + ) facts.update(extra_facts) - ansible_connection = 'community.docker.docker_api' + ansible_connection = "community.docker.docker_api" full_facts.update(facts) for key, value in inspect.items(): @@ -323,8 +334,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable): if ansible_connection: for d in (facts, full_facts): - if 'ansible_connection' not in d: - d['ansible_connection'] = ansible_connection + if "ansible_connection" not in d: + d["ansible_connection"] = ansible_connection if not filter_host(self, name, full_facts, filters): continue @@ -342,11 +353,17 @@ class InventoryModule(BaseInventoryPlugin, Constructable): # Use constructed if applicable # Composed variables - self._set_composite_vars(self.get_option('compose'), full_facts, name, strict=strict) + self._set_composite_vars( + self.get_option("compose"), full_facts, name, strict=strict + ) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group - self._add_host_to_composed_groups(self.get_option('groups'), full_facts, name, strict=strict) + self._add_host_to_composed_groups( + self.get_option("groups"), full_facts, name, strict=strict + ) # Create groups based on variable values and add the corresponding hosts to it - self._add_host_to_keyed_groups(self.get_option('keyed_groups'), full_facts, name, strict=strict) + self._add_host_to_keyed_groups( + self.get_option("keyed_groups"), full_facts, name, strict=strict + ) # We need to do this last since we also add a group called `name`. # When we do this before a set_variable() call, the variables are assigned @@ -362,15 +379,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable): self.inventory.add_host(name, group=hostname) if running is True: - self.inventory.add_host(name, group='running') + self.inventory.add_host(name, group="running") else: - self.inventory.add_host(name, group='stopped') + self.inventory.add_host(name, group="stopped") def verify_file(self, path): """Return the possibly of a file being consumable by this plugin.""" - return ( - super(InventoryModule, self).verify_file(path) and - path.endswith(('docker.yaml', 'docker.yml'))) + return super(InventoryModule, self).verify_file(path) and path.endswith( + ("docker.yaml", "docker.yml") + ) def _create_client(self): return AnsibleDockerClient(self, min_docker_api_version=MIN_DOCKER_API) @@ -382,10 +399,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable): try: self._populate(client) except DockerException as e: - raise AnsibleError( - f'An unexpected Docker error occurred: {e}' - ) + raise AnsibleError(f"An unexpected Docker error occurred: {e}") except RequestException as e: raise AnsibleError( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}' + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}" ) diff --git a/plugins/inventory/docker_machine.py b/plugins/inventory/docker_machine.py index f1b1cb5d..c704b1d8 100644 --- a/plugins/inventory/docker_machine.py +++ b/plugins/inventory/docker_machine.py @@ -5,6 +5,7 @@ from __future__ import annotations + DOCUMENTATION = r""" name: docker_machine author: Ximon Eighteen (@ximon18) @@ -59,7 +60,7 @@ options: version_added: 3.5.0 """ -EXAMPLES = ''' +EXAMPLES = """ --- # Minimal example plugin: community.docker.docker_machine @@ -96,57 +97,63 @@ keyed_groups: plugin: community.docker.docker_machine compose: ansible_ssh_common_args: '"-o StrictHostKeyChecking=accept-new"' -''' - -from ansible.errors import AnsibleError -from ansible.module_utils.common.text.converters import to_native -from ansible.module_utils.common.text.converters import to_text -from ansible.module_utils.common.process import get_bin_path -from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable -from ansible.utils.display import Display - -from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe -from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host +""" import json import re import subprocess +from ansible.errors import AnsibleError +from ansible.module_utils.common.process import get_bin_path +from ansible.module_utils.common.text.converters import to_native, to_text +from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable +from ansible.utils.display import Display +from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe +from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import ( + filter_host, + parse_filters, +) + + display = Display() class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): - ''' Host inventory parser for ansible using Docker machine as source. ''' + """Host inventory parser for ansible using Docker machine as source.""" - NAME = 'community.docker.docker_machine' + NAME = "community.docker.docker_machine" DOCKER_MACHINE_PATH = None def _run_command(self, args): if not self.DOCKER_MACHINE_PATH: try: - self.DOCKER_MACHINE_PATH = get_bin_path('docker-machine') + self.DOCKER_MACHINE_PATH = get_bin_path("docker-machine") except ValueError as e: raise AnsibleError(to_native(e)) command = [self.DOCKER_MACHINE_PATH] command.extend(args) - display.debug(f'Executing command {command}') + display.debug(f"Executing command {command}") try: result = subprocess.check_output(command) except subprocess.CalledProcessError as e: - display.warning(f'Exception {type(e).__name__} caught while executing command {command}, this was the original exception: {e}') + display.warning( + f"Exception {type(e).__name__} caught while executing command {command}, this was the original exception: {e}" + ) raise e return to_text(result).strip() def _get_docker_daemon_variables(self, machine_name): - ''' + """ Capture settings from Docker Machine that would be needed to connect to the remote Docker daemon installed on the Docker Machine remote host. Note: passing '--shell=sh' is a workaround for 'Error: Unknown shell'. - ''' + """ try: - env_lines = self._run_command(['env', '--shell=sh', machine_name]).splitlines() + env_lines = self._run_command( + ["env", "--shell=sh", machine_name] + ).splitlines() except subprocess.CalledProcessError: # This can happen when the machine is created but provisioning is incomplete return [] @@ -174,9 +181,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _get_machine_names(self): # Filter out machines that are not in the Running state as we probably cannot do anything useful actions # with them. - ls_command = ['ls', '-q'] - if self.get_option('running_required'): - ls_command.extend(['--filter', 'state=Running']) + ls_command = ["ls", "-q"] + if self.get_option("running_required"): + ls_command.extend(["--filter", "state=Running"]) try: ls_lines = self._run_command(ls_command) @@ -187,7 +194,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _inspect_docker_machine_host(self, node): try: - inspect_lines = self._run_command(['inspect', node]) + inspect_lines = self._run_command(["inspect", node]) except subprocess.CalledProcessError: return None @@ -195,7 +202,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _ip_addr_docker_machine_host(self, node): try: - ip_addr = self._run_command(['ip', node]) + ip_addr = self._run_command(["ip", node]) except subprocess.CalledProcessError: return None @@ -203,19 +210,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _should_skip_host(self, machine_name, env_var_tuples, daemon_env): if not env_var_tuples: - warning_prefix = f'Unable to fetch Docker daemon env vars from Docker Machine for host {machine_name}' - if daemon_env in ('require', 'require-silently'): - if daemon_env == 'require': - display.warning(f'{warning_prefix}: host will be skipped') + warning_prefix = f"Unable to fetch Docker daemon env vars from Docker Machine for host {machine_name}" + if daemon_env in ("require", "require-silently"): + if daemon_env == "require": + display.warning(f"{warning_prefix}: host will be skipped") return True else: # 'optional', 'optional-silently' - if daemon_env == 'optional': - display.warning(f'{warning_prefix}: host will lack dm_DOCKER_xxx variables') + if daemon_env == "optional": + display.warning( + f"{warning_prefix}: host will lack dm_DOCKER_xxx variables" + ) return False def _populate(self): - daemon_env = self.get_option('daemon_env') - filters = parse_filters(self.get_option('filters')) + daemon_env = self.get_option("daemon_env") + filters = parse_filters(self.get_option("filters")) try: for node in self._get_machine_names(): node_attrs = self._inspect_docker_machine_host(node) @@ -224,13 +233,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): unsafe_node_attrs = make_unsafe(node_attrs) - machine_name = unsafe_node_attrs['Driver']['MachineName'] + machine_name = unsafe_node_attrs["Driver"]["MachineName"] if not filter_host(self, machine_name, unsafe_node_attrs, filters): continue # query `docker-machine env` to obtain remote Docker daemon connection settings in the form of commands # that could be used to set environment variables to influence a local Docker client: - if daemon_env == 'skip': + if daemon_env == "skip": env_var_tuples = [] else: env_var_tuples = self._get_docker_daemon_variables(machine_name) @@ -243,49 +252,82 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): # check for valid ip address from inspect output, else explicitly use ip command to find host ip address # this works around an issue seen with Google Compute Platform where the IP address was not available # via the 'inspect' subcommand but was via the 'ip' subcomannd. - if unsafe_node_attrs['Driver']['IPAddress']: - ip_addr = unsafe_node_attrs['Driver']['IPAddress'] + if unsafe_node_attrs["Driver"]["IPAddress"]: + ip_addr = unsafe_node_attrs["Driver"]["IPAddress"] else: ip_addr = self._ip_addr_docker_machine_host(node) # set standard Ansible remote host connection settings to details captured from `docker-machine` # see: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html - self.inventory.set_variable(machine_name, 'ansible_host', make_unsafe(ip_addr)) - self.inventory.set_variable(machine_name, 'ansible_port', unsafe_node_attrs['Driver']['SSHPort']) - self.inventory.set_variable(machine_name, 'ansible_user', unsafe_node_attrs['Driver']['SSHUser']) - self.inventory.set_variable(machine_name, 'ansible_ssh_private_key_file', unsafe_node_attrs['Driver']['SSHKeyPath']) + self.inventory.set_variable( + machine_name, "ansible_host", make_unsafe(ip_addr) + ) + self.inventory.set_variable( + machine_name, "ansible_port", unsafe_node_attrs["Driver"]["SSHPort"] + ) + self.inventory.set_variable( + machine_name, "ansible_user", unsafe_node_attrs["Driver"]["SSHUser"] + ) + self.inventory.set_variable( + machine_name, + "ansible_ssh_private_key_file", + unsafe_node_attrs["Driver"]["SSHKeyPath"], + ) # set variables based on Docker Machine tags - tags = unsafe_node_attrs['Driver'].get('Tags') or '' - self.inventory.set_variable(machine_name, 'dm_tags', make_unsafe(tags)) + tags = unsafe_node_attrs["Driver"].get("Tags") or "" + self.inventory.set_variable(machine_name, "dm_tags", make_unsafe(tags)) # set variables based on Docker Machine env variables for kv in env_var_tuples: - self.inventory.set_variable(machine_name, f'dm_{kv[0]}', make_unsafe(kv[1])) + self.inventory.set_variable( + machine_name, f"dm_{kv[0]}", make_unsafe(kv[1]) + ) - if self.get_option('verbose_output'): - self.inventory.set_variable(machine_name, 'docker_machine_node_attributes', unsafe_node_attrs) + if self.get_option("verbose_output"): + self.inventory.set_variable( + machine_name, + "docker_machine_node_attributes", + unsafe_node_attrs, + ) # Use constructed if applicable - strict = self.get_option('strict') + strict = self.get_option("strict") # Composed variables - self._set_composite_vars(self.get_option('compose'), unsafe_node_attrs, machine_name, strict=strict) + self._set_composite_vars( + self.get_option("compose"), + unsafe_node_attrs, + machine_name, + strict=strict, + ) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group - self._add_host_to_composed_groups(self.get_option('groups'), unsafe_node_attrs, machine_name, strict=strict) + self._add_host_to_composed_groups( + self.get_option("groups"), + unsafe_node_attrs, + machine_name, + strict=strict, + ) # Create groups based on variable values and add the corresponding hosts to it - self._add_host_to_keyed_groups(self.get_option('keyed_groups'), unsafe_node_attrs, machine_name, strict=strict) + self._add_host_to_keyed_groups( + self.get_option("keyed_groups"), + unsafe_node_attrs, + machine_name, + strict=strict, + ) except Exception as e: - raise AnsibleError(f'Unable to fetch hosts from Docker Machine, this was the original exception: {e}') from e + raise AnsibleError( + f"Unable to fetch hosts from Docker Machine, this was the original exception: {e}" + ) from e def verify_file(self, path): """Return the possibility of a file being consumable by this plugin.""" - return ( - super(InventoryModule, self).verify_file(path) and - path.endswith(('docker_machine.yaml', 'docker_machine.yml'))) + return super(InventoryModule, self).verify_file(path) and path.endswith( + ("docker_machine.yaml", "docker_machine.yml") + ) def parse(self, inventory, loader, path, cache=True): super(InventoryModule, self).parse(inventory, loader, path, cache) diff --git a/plugins/inventory/docker_swarm.py b/plugins/inventory/docker_swarm.py index 012a5bc3..47716839 100644 --- a/plugins/inventory/docker_swarm.py +++ b/plugins/inventory/docker_swarm.py @@ -6,6 +6,7 @@ from __future__ import annotations + DOCUMENTATION = r""" name: docker_swarm author: @@ -103,7 +104,7 @@ options: version_added: 3.5.0 """ -EXAMPLES = ''' +EXAMPLES = """ --- # Minimal example using local docker plugin: community.docker.docker_swarm @@ -146,126 +147,176 @@ keyed_groups: # hint: labels containing special characters will be converted to safe names - key: 'Spec.Labels' prefix: label -''' +""" from ansible.errors import AnsibleError -from ansible_collections.community.docker.plugins.module_utils.common import get_connect_params -from ansible_collections.community.docker.plugins.module_utils.util import update_tls_hostname -from ansible.plugins.inventory import BaseInventoryPlugin, Constructable from ansible.parsing.utils.addresses import parse_address - +from ansible.plugins.inventory import BaseInventoryPlugin, Constructable +from ansible_collections.community.docker.plugins.module_utils.common import ( + get_connect_params, +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + update_tls_hostname, +) from ansible_collections.community.docker.plugins.plugin_utils.unsafe import make_unsafe -from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import parse_filters, filter_host +from ansible_collections.community.library_inventory_filtering_v1.plugins.plugin_utils.inventory_filter import ( + filter_host, + parse_filters, +) + try: import docker + HAS_DOCKER = True except ImportError: HAS_DOCKER = False class InventoryModule(BaseInventoryPlugin, Constructable): - ''' Host inventory parser for ansible using Docker swarm as source. ''' + """Host inventory parser for ansible using Docker swarm as source.""" - NAME = 'community.docker.docker_swarm' + NAME = "community.docker.docker_swarm" def _fail(self, msg): raise AnsibleError(msg) def _populate(self): raw_params = dict( - docker_host=self.get_option('docker_host'), - tls=self.get_option('tls'), - tls_verify=self.get_option('validate_certs'), - key_path=self.get_option('client_key'), - cacert_path=self.get_option('ca_path'), - cert_path=self.get_option('client_cert'), - tls_hostname=self.get_option('tls_hostname'), - api_version=self.get_option('api_version'), - timeout=self.get_option('timeout'), - use_ssh_client=self.get_option('use_ssh_client'), + docker_host=self.get_option("docker_host"), + tls=self.get_option("tls"), + tls_verify=self.get_option("validate_certs"), + key_path=self.get_option("client_key"), + cacert_path=self.get_option("ca_path"), + cert_path=self.get_option("client_cert"), + tls_hostname=self.get_option("tls_hostname"), + api_version=self.get_option("api_version"), + timeout=self.get_option("timeout"), + use_ssh_client=self.get_option("use_ssh_client"), debug=None, ) update_tls_hostname(raw_params) connect_params = get_connect_params(raw_params, fail_function=self._fail) self.client = docker.DockerClient(**connect_params) - self.inventory.add_group('all') - self.inventory.add_group('manager') - self.inventory.add_group('worker') - self.inventory.add_group('leader') - self.inventory.add_group('nonleaders') + self.inventory.add_group("all") + self.inventory.add_group("manager") + self.inventory.add_group("worker") + self.inventory.add_group("leader") + self.inventory.add_group("nonleaders") - filters = parse_filters(self.get_option('filters')) + filters = parse_filters(self.get_option("filters")) - if self.get_option('include_host_uri'): - if self.get_option('include_host_uri_port'): - host_uri_port = str(self.get_option('include_host_uri_port')) - elif self.get_option('tls') or self.get_option('validate_certs'): - host_uri_port = '2376' + if self.get_option("include_host_uri"): + if self.get_option("include_host_uri_port"): + host_uri_port = str(self.get_option("include_host_uri_port")) + elif self.get_option("tls") or self.get_option("validate_certs"): + host_uri_port = "2376" else: - host_uri_port = '2375' + host_uri_port = "2375" try: self.nodes = self.client.nodes.list() for node in self.nodes: node_attrs = self.client.nodes.get(node.id).attrs unsafe_node_attrs = make_unsafe(node_attrs) - if not filter_host(self, unsafe_node_attrs['ID'], unsafe_node_attrs, filters): + if not filter_host( + self, unsafe_node_attrs["ID"], unsafe_node_attrs, filters + ): continue - self.inventory.add_host(unsafe_node_attrs['ID']) - self.inventory.add_host(unsafe_node_attrs['ID'], group=unsafe_node_attrs['Spec']['Role']) - self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host', - unsafe_node_attrs['Status']['Addr']) - if self.get_option('include_host_uri'): - self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host_uri', - make_unsafe('tcp://' + unsafe_node_attrs['Status']['Addr'] + ':' + host_uri_port)) - if self.get_option('verbose_output'): - self.inventory.set_variable(unsafe_node_attrs['ID'], 'docker_swarm_node_attributes', unsafe_node_attrs) - if 'ManagerStatus' in unsafe_node_attrs: - if unsafe_node_attrs['ManagerStatus'].get('Leader'): + self.inventory.add_host(unsafe_node_attrs["ID"]) + self.inventory.add_host( + unsafe_node_attrs["ID"], group=unsafe_node_attrs["Spec"]["Role"] + ) + self.inventory.set_variable( + unsafe_node_attrs["ID"], + "ansible_host", + unsafe_node_attrs["Status"]["Addr"], + ) + if self.get_option("include_host_uri"): + self.inventory.set_variable( + unsafe_node_attrs["ID"], + "ansible_host_uri", + make_unsafe( + "tcp://" + + unsafe_node_attrs["Status"]["Addr"] + + ":" + + host_uri_port + ), + ) + if self.get_option("verbose_output"): + self.inventory.set_variable( + unsafe_node_attrs["ID"], + "docker_swarm_node_attributes", + unsafe_node_attrs, + ) + if "ManagerStatus" in unsafe_node_attrs: + if unsafe_node_attrs["ManagerStatus"].get("Leader"): # This is workaround of bug in Docker when in some cases the Leader IP is 0.0.0.0 # Check moby/moby#35437 for details - swarm_leader_ip = parse_address(node_attrs['ManagerStatus']['Addr'])[0] or \ - unsafe_node_attrs['Status']['Addr'] - if self.get_option('include_host_uri'): - self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host_uri', - make_unsafe('tcp://' + swarm_leader_ip + ':' + host_uri_port)) - self.inventory.set_variable(unsafe_node_attrs['ID'], 'ansible_host', make_unsafe(swarm_leader_ip)) - self.inventory.add_host(unsafe_node_attrs['ID'], group='leader') + swarm_leader_ip = ( + parse_address(node_attrs["ManagerStatus"]["Addr"])[0] + or unsafe_node_attrs["Status"]["Addr"] + ) + if self.get_option("include_host_uri"): + self.inventory.set_variable( + unsafe_node_attrs["ID"], + "ansible_host_uri", + make_unsafe( + "tcp://" + swarm_leader_ip + ":" + host_uri_port + ), + ) + self.inventory.set_variable( + unsafe_node_attrs["ID"], + "ansible_host", + make_unsafe(swarm_leader_ip), + ) + self.inventory.add_host(unsafe_node_attrs["ID"], group="leader") else: - self.inventory.add_host(unsafe_node_attrs['ID'], group='nonleaders') + self.inventory.add_host( + unsafe_node_attrs["ID"], group="nonleaders" + ) else: - self.inventory.add_host(unsafe_node_attrs['ID'], group='nonleaders') + self.inventory.add_host(unsafe_node_attrs["ID"], group="nonleaders") # Use constructed if applicable - strict = self.get_option('strict') + strict = self.get_option("strict") # Composed variables - self._set_composite_vars(self.get_option('compose'), - unsafe_node_attrs, - unsafe_node_attrs['ID'], - strict=strict) + self._set_composite_vars( + self.get_option("compose"), + unsafe_node_attrs, + unsafe_node_attrs["ID"], + strict=strict, + ) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group - self._add_host_to_composed_groups(self.get_option('groups'), - unsafe_node_attrs, - unsafe_node_attrs['ID'], - strict=strict) + self._add_host_to_composed_groups( + self.get_option("groups"), + unsafe_node_attrs, + unsafe_node_attrs["ID"], + strict=strict, + ) # Create groups based on variable values and add the corresponding hosts to it - self._add_host_to_keyed_groups(self.get_option('keyed_groups'), - unsafe_node_attrs, - unsafe_node_attrs['ID'], - strict=strict) + self._add_host_to_keyed_groups( + self.get_option("keyed_groups"), + unsafe_node_attrs, + unsafe_node_attrs["ID"], + strict=strict, + ) except Exception as e: - raise AnsibleError(f'Unable to fetch hosts from Docker swarm API, this was the original exception: {e}') + raise AnsibleError( + f"Unable to fetch hosts from Docker swarm API, this was the original exception: {e}" + ) def verify_file(self, path): """Return the possibly of a file being consumable by this plugin.""" - return ( - super(InventoryModule, self).verify_file(path) and - path.endswith(('docker_swarm.yaml', 'docker_swarm.yml'))) + return super(InventoryModule, self).verify_file(path) and path.endswith( + ("docker_swarm.yaml", "docker_swarm.yml") + ) def parse(self, inventory, loader, path, cache=True): if not HAS_DOCKER: - raise AnsibleError('The Docker swarm dynamic inventory plugin requires the Docker SDK for Python: ' - 'https://github.com/docker/docker-py.') + raise AnsibleError( + "The Docker swarm dynamic inventory plugin requires the Docker SDK for Python: " + "https://github.com/docker/docker-py." + ) super(InventoryModule, self).parse(inventory, loader, path, cache) self._read_config_data(path) self._populate() diff --git a/plugins/module_utils/_api/_import_helper.py b/plugins/module_utils/_api/_import_helper.py index f464f776..058f3fa8 100644 --- a/plugins/module_utils/_api/_import_helper.py +++ b/plugins/module_utils/_api/_import_helper.py @@ -18,8 +18,13 @@ URLLIB3_IMPORT_ERROR = None try: from requests import Session # noqa: F401, pylint: disable=unused-import - from requests.adapters import HTTPAdapter # noqa: F401, pylint: disable=unused-import - from requests.exceptions import HTTPError, InvalidSchema # noqa: F401, pylint: disable=unused-import + from requests.adapters import ( # noqa: F401, pylint: disable=unused-import + HTTPAdapter, + ) + from requests.exceptions import ( # noqa: F401, pylint: disable=unused-import + HTTPError, + InvalidSchema, + ) except ImportError: REQUESTS_IMPORT_ERROR = traceback.format_exc() @@ -38,11 +43,15 @@ except ImportError: try: from requests.packages import urllib3 # pylint: disable=unused-import - from requests.packages.urllib3 import connection as urllib3_connection # pylint: disable=unused-import + from requests.packages.urllib3 import ( + connection as urllib3_connection, # pylint: disable=unused-import + ) except ImportError: try: import urllib3 # pylint: disable=unused-import - from urllib3 import connection as urllib3_connection # pylint: disable=unused-import + from urllib3 import ( + connection as urllib3_connection, # pylint: disable=unused-import + ) except ImportError: URLLIB3_IMPORT_ERROR = traceback.format_exc() @@ -77,11 +86,11 @@ def fail_on_missing_imports(): from .errors import MissingRequirementException raise MissingRequirementException( - 'You have to install requests', - 'requests', REQUESTS_IMPORT_ERROR) + "You have to install requests", "requests", REQUESTS_IMPORT_ERROR + ) if URLLIB3_IMPORT_ERROR is not None: from .errors import MissingRequirementException raise MissingRequirementException( - 'You have to install urllib3', - 'urllib3', URLLIB3_IMPORT_ERROR) + "You have to install urllib3", "urllib3", URLLIB3_IMPORT_ERROR + ) diff --git a/plugins/module_utils/_api/api/client.py b/plugins/module_utils/_api/api/client.py index 4d959239..32d35312 100644 --- a/plugins/module_utils/_api/api/client.py +++ b/plugins/module_utils/_api/api/client.py @@ -16,37 +16,45 @@ from functools import partial from urllib.parse import quote from .. import auth -from .._import_helper import fail_on_missing_imports from .._import_helper import HTTPError as _HTTPError from .._import_helper import InvalidSchema as _InvalidSchema from .._import_helper import Session as _Session -from ..constants import (DEFAULT_NUM_POOLS, DEFAULT_NUM_POOLS_SSH, - DEFAULT_MAX_POOL_SIZE, DEFAULT_TIMEOUT_SECONDS, - DEFAULT_USER_AGENT, IS_WINDOWS_PLATFORM, - MINIMUM_DOCKER_API_VERSION, STREAM_HEADER_SIZE_BYTES, - DEFAULT_DATA_CHUNK_SIZE) -from ..errors import (DockerException, InvalidVersion, TLSParameterError, MissingRequirementException, - create_api_error_from_http_exception) +from .._import_helper import fail_on_missing_imports +from ..constants import ( + DEFAULT_DATA_CHUNK_SIZE, + DEFAULT_MAX_POOL_SIZE, + DEFAULT_NUM_POOLS, + DEFAULT_NUM_POOLS_SSH, + DEFAULT_TIMEOUT_SECONDS, + DEFAULT_USER_AGENT, + IS_WINDOWS_PLATFORM, + MINIMUM_DOCKER_API_VERSION, + STREAM_HEADER_SIZE_BYTES, +) +from ..errors import ( + DockerException, + InvalidVersion, + MissingRequirementException, + TLSParameterError, + create_api_error_from_http_exception, +) from ..tls import TLSConfig from ..transport.npipeconn import NpipeHTTPAdapter from ..transport.npipesocket import PYWIN32_IMPORT_ERROR -from ..transport.unixconn import UnixHTTPAdapter -from ..transport.sshconn import SSHHTTPAdapter, PARAMIKO_IMPORT_ERROR +from ..transport.sshconn import PARAMIKO_IMPORT_ERROR, SSHHTTPAdapter from ..transport.ssladapter import SSLHTTPAdapter -from ..utils import config, utils, json_stream +from ..transport.unixconn import UnixHTTPAdapter +from ..utils import config, json_stream, utils from ..utils.decorators import check_resource, update_headers from ..utils.proxy import ProxyConfig from ..utils.socket import consume_socket_output, demux_adaptor, frames_iter - from .daemon import DaemonApiMixin log = logging.getLogger(__name__) -class APIClient( - _Session, - DaemonApiMixin): +class APIClient(_Session, DaemonApiMixin): """ A low-level client for the Docker Engine API. @@ -85,139 +93,155 @@ class APIClient( to save in the pool. """ - __attrs__ = _Session.__attrs__ + ['_auth_configs', - '_general_configs', - '_version', - 'base_url', - 'timeout'] + __attrs__ = _Session.__attrs__ + [ + "_auth_configs", + "_general_configs", + "_version", + "base_url", + "timeout", + ] - def __init__(self, base_url=None, version=None, - timeout=DEFAULT_TIMEOUT_SECONDS, tls=False, - user_agent=DEFAULT_USER_AGENT, num_pools=None, - credstore_env=None, use_ssh_client=False, - max_pool_size=DEFAULT_MAX_POOL_SIZE): + def __init__( + self, + base_url=None, + version=None, + timeout=DEFAULT_TIMEOUT_SECONDS, + tls=False, + user_agent=DEFAULT_USER_AGENT, + num_pools=None, + credstore_env=None, + use_ssh_client=False, + max_pool_size=DEFAULT_MAX_POOL_SIZE, + ): super(APIClient, self).__init__() fail_on_missing_imports() if tls and not base_url: raise TLSParameterError( - 'If using TLS, the base_url argument must be provided.' + "If using TLS, the base_url argument must be provided." ) self.base_url = base_url self.timeout = timeout - self.headers['User-Agent'] = user_agent + self.headers["User-Agent"] = user_agent self._general_configs = config.load_general_config() - proxy_config = self._general_configs.get('proxies', {}) + proxy_config = self._general_configs.get("proxies", {}) try: proxies = proxy_config[base_url] except KeyError: - proxies = proxy_config.get('default', {}) + proxies = proxy_config.get("default", {}) self._proxy_configs = ProxyConfig.from_dict(proxies) self._auth_configs = auth.load_config( - config_dict=self._general_configs, credstore_env=credstore_env, + config_dict=self._general_configs, + credstore_env=credstore_env, ) self.credstore_env = credstore_env - base_url = utils.parse_host( - base_url, IS_WINDOWS_PLATFORM, tls=bool(tls) - ) + base_url = utils.parse_host(base_url, IS_WINDOWS_PLATFORM, tls=bool(tls)) # SSH has a different default for num_pools to all other adapters - num_pools = num_pools or DEFAULT_NUM_POOLS_SSH if \ - base_url.startswith('ssh://') else DEFAULT_NUM_POOLS + num_pools = ( + num_pools or DEFAULT_NUM_POOLS_SSH + if base_url.startswith("ssh://") + else DEFAULT_NUM_POOLS + ) - if base_url.startswith('http+unix://'): + if base_url.startswith("http+unix://"): self._custom_adapter = UnixHTTPAdapter( - base_url, timeout, pool_connections=num_pools, - max_pool_size=max_pool_size + base_url, + timeout, + pool_connections=num_pools, + max_pool_size=max_pool_size, ) - self.mount('http+docker://', self._custom_adapter) - self._unmount('http://', 'https://') + self.mount("http+docker://", self._custom_adapter) + self._unmount("http://", "https://") # host part of URL should be unused, but is resolved by requests # module in proxy_bypass_macosx_sysconf() - self.base_url = 'http+docker://localhost' - elif base_url.startswith('npipe://'): + self.base_url = "http+docker://localhost" + elif base_url.startswith("npipe://"): if not IS_WINDOWS_PLATFORM: raise DockerException( - 'The npipe:// protocol is only supported on Windows' + "The npipe:// protocol is only supported on Windows" ) if PYWIN32_IMPORT_ERROR is not None: raise MissingRequirementException( - 'Install pypiwin32 package to enable npipe:// support', - 'pywin32', - PYWIN32_IMPORT_ERROR) + "Install pypiwin32 package to enable npipe:// support", + "pywin32", + PYWIN32_IMPORT_ERROR, + ) self._custom_adapter = NpipeHTTPAdapter( - base_url, timeout, pool_connections=num_pools, - max_pool_size=max_pool_size + base_url, + timeout, + pool_connections=num_pools, + max_pool_size=max_pool_size, ) - self.mount('http+docker://', self._custom_adapter) - self.base_url = 'http+docker://localnpipe' - elif base_url.startswith('ssh://'): + self.mount("http+docker://", self._custom_adapter) + self.base_url = "http+docker://localnpipe" + elif base_url.startswith("ssh://"): if PARAMIKO_IMPORT_ERROR is not None and not use_ssh_client: raise MissingRequirementException( - 'Install paramiko package to enable ssh:// support', - 'paramiko', - PARAMIKO_IMPORT_ERROR) + "Install paramiko package to enable ssh:// support", + "paramiko", + PARAMIKO_IMPORT_ERROR, + ) self._custom_adapter = SSHHTTPAdapter( - base_url, timeout, pool_connections=num_pools, - max_pool_size=max_pool_size, shell_out=use_ssh_client + base_url, + timeout, + pool_connections=num_pools, + max_pool_size=max_pool_size, + shell_out=use_ssh_client, ) - self.mount('http+docker://ssh', self._custom_adapter) - self._unmount('http://', 'https://') - self.base_url = 'http+docker://ssh' + self.mount("http+docker://ssh", self._custom_adapter) + self._unmount("http://", "https://") + self.base_url = "http+docker://ssh" else: # Use SSLAdapter for the ability to specify SSL version if isinstance(tls, TLSConfig): tls.configure_client(self) elif tls: - self._custom_adapter = SSLHTTPAdapter( - pool_connections=num_pools) - self.mount('https://', self._custom_adapter) + self._custom_adapter = SSLHTTPAdapter(pool_connections=num_pools) + self.mount("https://", self._custom_adapter) self.base_url = base_url # version detection needs to be after unix adapter mounting - if version is None or (isinstance(version, str) and version.lower() == 'auto'): + if version is None or (isinstance(version, str) and version.lower() == "auto"): self._version = self._retrieve_server_version() else: self._version = version if not isinstance(self._version, str): raise DockerException( - f'Version parameter must be a string or None. Found {type(version).__name__}' + f"Version parameter must be a string or None. Found {type(version).__name__}" ) if utils.version_lt(self._version, MINIMUM_DOCKER_API_VERSION): raise InvalidVersion( - f'API versions below {MINIMUM_DOCKER_API_VERSION} are no longer supported by this library.' + f"API versions below {MINIMUM_DOCKER_API_VERSION} are no longer supported by this library." ) def _retrieve_server_version(self): try: version_result = self.version(api_version=False) except Exception as e: - raise DockerException( - f'Error while fetching server API version: {e}' - ) + raise DockerException(f"Error while fetching server API version: {e}") try: return version_result["ApiVersion"] except KeyError: raise DockerException( - 'Invalid response from docker daemon: key "ApiVersion"' - ' is missing.' + 'Invalid response from docker daemon: key "ApiVersion"' " is missing." ) except Exception as e: raise DockerException( - f'Error while fetching server API version: {e}. Response seems to be broken.' + f"Error while fetching server API version: {e}. Response seems to be broken." ) def _set_request_timeout(self, kwargs): """Prepare the kwargs for an HTTP request by inserting the timeout parameter, if not already present.""" - kwargs.setdefault('timeout', self.timeout) + kwargs.setdefault("timeout", self.timeout) return kwargs @update_headers @@ -244,16 +268,16 @@ class APIClient( for arg in args: if not isinstance(arg, str): raise ValueError( - f'Expected a string but found {arg} ({type(arg)}) instead' + f"Expected a string but found {arg} ({type(arg)}) instead" ) quote_f = partial(quote, safe="/:") args = map(quote_f, args) - if kwargs.get('versioned_api', True): - return f'{self.base_url}/v{self._version}{pathfmt.format(*args)}' + if kwargs.get("versioned_api", True): + return f"{self.base_url}/v{self._version}{pathfmt.format(*args)}" else: - return f'{self.base_url}{pathfmt.format(*args)}' + return f"{self.base_url}{pathfmt.format(*args)}" def _raise_for_status(self, response): """Raises stored :class:`APIError`, if one occurred.""" @@ -264,7 +288,7 @@ class APIClient( def _result(self, response, json=False, binary=False): if json and binary: - raise AssertionError('json and binary must not be both True') + raise AssertionError("json and binary must not be both True") self._raise_for_status(response) if json: @@ -284,23 +308,19 @@ class APIClient( elif data is not None: data2 = data - if 'headers' not in kwargs: - kwargs['headers'] = {} - kwargs['headers']['Content-Type'] = 'application/json' + if "headers" not in kwargs: + kwargs["headers"] = {} + kwargs["headers"]["Content-Type"] = "application/json" return self._post(url, data=json.dumps(data2), **kwargs) def _attach_params(self, override=None): - return override or { - 'stdout': 1, - 'stderr': 1, - 'stream': 1 - } + return override or {"stdout": 1, "stderr": 1, "stream": 1} def _get_raw_response_socket(self, response): self._raise_for_status(response) if self.base_url == "http+docker://localnpipe": sock = response.raw._fp.fp.raw.sock - elif self.base_url.startswith('http+docker://ssh'): + elif self.base_url.startswith("http+docker://ssh"): sock = response.raw._fp.fp.channel else: sock = response.raw._fp.fp.raw @@ -348,8 +368,8 @@ class APIClient( while True: if buf_length - walker < STREAM_HEADER_SIZE_BYTES: break - header = buf[walker:walker + STREAM_HEADER_SIZE_BYTES] - dummy, length = struct.unpack_from('>BxxxL', header) + header = buf[walker : walker + STREAM_HEADER_SIZE_BYTES] + dummy, length = struct.unpack_from(">BxxxL", header) start = walker + STREAM_HEADER_SIZE_BYTES end = start + length walker = end @@ -368,7 +388,7 @@ class APIClient( header = response.raw.read(STREAM_HEADER_SIZE_BYTES) if not header: break - dummy, length = struct.unpack('>BxxxL', header) + dummy, length = struct.unpack(">BxxxL", header) if not length: continue data = response.raw.read(length) @@ -377,7 +397,7 @@ class APIClient( yield data def _stream_raw_result(self, response, chunk_size=1, decode=True): - ''' Stream result for TTY-enabled container and raw binary data''' + """Stream result for TTY-enabled container and raw binary data""" self._raise_for_status(response) # Disable timeout on the underlying socket to prevent @@ -413,7 +433,7 @@ class APIClient( response.close() def _disable_socket_timeout(self, socket): - """ Depending on the combination of python version and whether we are + """Depending on the combination of python version and whether we are connecting over http or https, we might need to access _sock, which may or may not exist; or we may need to just settimeout on socket itself, which also may or may not have settimeout on it. To avoid @@ -423,15 +443,15 @@ class APIClient( you run the risk of changing a socket that was non-blocking to blocking, for example when using gevent. """ - sockets = [socket, getattr(socket, '_sock', None)] + sockets = [socket, getattr(socket, "_sock", None)] for s in sockets: - if not hasattr(s, 'settimeout'): + if not hasattr(s, "settimeout"): continue timeout = -1 - if hasattr(s, 'gettimeout'): + if hasattr(s, "gettimeout"): timeout = s.gettimeout() # Do not change the timeout if it is already disabled. @@ -440,10 +460,10 @@ class APIClient( s.settimeout(None) - @check_resource('container') + @check_resource("container") def _check_is_tty(self, container): cont = self.inspect_container(container) - return cont['Config']['Tty'] + return cont["Config"]["Tty"] def _get_result(self, container, stream, res): return self._get_result_tty(stream, res, self._check_is_tty(container)) @@ -452,17 +472,18 @@ class APIClient( # We should also use raw streaming (without keep-alive) # if we are dealing with a tty-enabled container. if is_tty: - return self._stream_raw_result(res) if stream else \ - self._result(res, binary=True) + return ( + self._stream_raw_result(res) + if stream + else self._result(res, binary=True) + ) self._raise_for_status(res) - sep = b'' + sep = b"" if stream: return self._multiplexed_response_stream_helper(res) else: - return sep.join( - list(self._multiplexed_buffer_helper(res)) - ) + return sep.join(list(self._multiplexed_buffer_helper(res))) def _unmount(self, *args): for proto in args: @@ -498,15 +519,13 @@ class APIClient( ) def _set_auth_headers(self, headers): - log.debug('Looking for auth config') + log.debug("Looking for auth config") # If we do not have any auth data so far, try reloading the config # file one more time in case anything showed up in there. if not self._auth_configs or self._auth_configs.is_empty: log.debug("No auth config in memory - loading from filesystem") - self._auth_configs = auth.load_config( - credstore_env=self.credstore_env - ) + self._auth_configs = auth.load_config(credstore_env=self.credstore_env) # Send the full auth configuration (if any exists), since the build # could use any (or all) of the registries. @@ -514,87 +533,134 @@ class APIClient( auth_data = self._auth_configs.get_all_credentials() # See https://github.com/docker/docker-py/issues/1683 - if (auth.INDEX_URL not in auth_data and - auth.INDEX_NAME in auth_data): + if auth.INDEX_URL not in auth_data and auth.INDEX_NAME in auth_data: auth_data[auth.INDEX_URL] = auth_data.get(auth.INDEX_NAME, {}) log.debug( - 'Sending auth config (%s)', - ', '.join(repr(k) for k in auth_data.keys()) + "Sending auth config (%s)", ", ".join(repr(k) for k in auth_data.keys()) ) if auth_data: - headers['X-Registry-Config'] = auth.encode_header( - auth_data - ) + headers["X-Registry-Config"] = auth.encode_header(auth_data) else: - log.debug('No auth config found') + log.debug("No auth config found") def get_binary(self, pathfmt, *args, **kwargs): - return self._result(self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs), binary=True) + return self._result( + self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs), + binary=True, + ) def get_json(self, pathfmt, *args, **kwargs): - return self._result(self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs), json=True) + return self._result( + self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs), + json=True, + ) def get_text(self, pathfmt, *args, **kwargs): - return self._result(self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs)) + return self._result( + self._get(self._url(pathfmt, *args, versioned_api=True), **kwargs) + ) def get_raw_stream(self, pathfmt, *args, **kwargs): - chunk_size = kwargs.pop('chunk_size', DEFAULT_DATA_CHUNK_SIZE) - res = self._get(self._url(pathfmt, *args, versioned_api=True), stream=True, **kwargs) + chunk_size = kwargs.pop("chunk_size", DEFAULT_DATA_CHUNK_SIZE) + res = self._get( + self._url(pathfmt, *args, versioned_api=True), stream=True, **kwargs + ) self._raise_for_status(res) return self._stream_raw_result(res, chunk_size, False) def delete_call(self, pathfmt, *args, **kwargs): - self._raise_for_status(self._delete(self._url(pathfmt, *args, versioned_api=True), **kwargs)) + self._raise_for_status( + self._delete(self._url(pathfmt, *args, versioned_api=True), **kwargs) + ) def delete_json(self, pathfmt, *args, **kwargs): - return self._result(self._delete(self._url(pathfmt, *args, versioned_api=True), **kwargs), json=True) + return self._result( + self._delete(self._url(pathfmt, *args, versioned_api=True), **kwargs), + json=True, + ) def post_call(self, pathfmt, *args, **kwargs): - self._raise_for_status(self._post(self._url(pathfmt, *args, versioned_api=True), **kwargs)) + self._raise_for_status( + self._post(self._url(pathfmt, *args, versioned_api=True), **kwargs) + ) def post_json(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) - self._raise_for_status(self._post_json(self._url(pathfmt, *args, versioned_api=True), data, **kwargs)) + data = kwargs.pop("data", None) + self._raise_for_status( + self._post_json( + self._url(pathfmt, *args, versioned_api=True), data, **kwargs + ) + ) def post_json_to_binary(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) - return self._result(self._post_json(self._url(pathfmt, *args, versioned_api=True), data, **kwargs), binary=True) + data = kwargs.pop("data", None) + return self._result( + self._post_json( + self._url(pathfmt, *args, versioned_api=True), data, **kwargs + ), + binary=True, + ) def post_json_to_json(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) - return self._result(self._post_json(self._url(pathfmt, *args, versioned_api=True), data, **kwargs), json=True) + data = kwargs.pop("data", None) + return self._result( + self._post_json( + self._url(pathfmt, *args, versioned_api=True), data, **kwargs + ), + json=True, + ) def post_json_to_text(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) + data = kwargs.pop("data", None) def post_json_to_stream_socket(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) - headers = (kwargs.pop('headers', None) or {}).copy() - headers.update({ - 'Connection': 'Upgrade', - 'Upgrade': 'tcp', - }) + data = kwargs.pop("data", None) + headers = (kwargs.pop("headers", None) or {}).copy() + headers.update( + { + "Connection": "Upgrade", + "Upgrade": "tcp", + } + ) return self._get_raw_response_socket( - self._post_json(self._url(pathfmt, *args, versioned_api=True), data, headers=headers, stream=True, **kwargs)) + self._post_json( + self._url(pathfmt, *args, versioned_api=True), + data, + headers=headers, + stream=True, + **kwargs, + ) + ) def post_json_to_stream(self, pathfmt, *args, **kwargs): - data = kwargs.pop('data', None) - headers = (kwargs.pop('headers', None) or {}).copy() - headers.update({ - 'Connection': 'Upgrade', - 'Upgrade': 'tcp', - }) - stream = kwargs.pop('stream', False) - demux = kwargs.pop('demux', False) - tty = kwargs.pop('tty', False) + data = kwargs.pop("data", None) + headers = (kwargs.pop("headers", None) or {}).copy() + headers.update( + { + "Connection": "Upgrade", + "Upgrade": "tcp", + } + ) + stream = kwargs.pop("stream", False) + demux = kwargs.pop("demux", False) + tty = kwargs.pop("tty", False) return self._read_from_socket( - self._post_json(self._url(pathfmt, *args, versioned_api=True), data, headers=headers, stream=True, **kwargs), + self._post_json( + self._url(pathfmt, *args, versioned_api=True), + data, + headers=headers, + stream=True, + **kwargs, + ), stream, tty=tty, - demux=demux + demux=demux, ) def post_to_json(self, pathfmt, *args, **kwargs): - return self._result(self._post(self._url(pathfmt, *args, versioned_api=True), **kwargs), json=True) + return self._result( + self._post(self._url(pathfmt, *args, versioned_api=True), **kwargs), + json=True, + ) diff --git a/plugins/module_utils/_api/api/daemon.py b/plugins/module_utils/_api/api/daemon.py index f69af9cc..035955da 100644 --- a/plugins/module_utils/_api/api/daemon.py +++ b/plugins/module_utils/_api/api/daemon.py @@ -16,7 +16,7 @@ from ..utils.decorators import minimum_version class DaemonApiMixin(object): - @minimum_version('1.25') + @minimum_version("1.25") def df(self): """ Get data usage information. @@ -29,7 +29,7 @@ class DaemonApiMixin(object): :py:class:`docker.errors.APIError` If the server returns an error. """ - url = self._url('/system/df') + url = self._url("/system/df") return self._result(self._get(url), True) def info(self): @@ -46,8 +46,15 @@ class DaemonApiMixin(object): """ return self._result(self._get(self._url("/info")), True) - def login(self, username, password=None, email=None, registry=None, - reauth=False, dockercfg_path=None): + def login( + self, + username, + password=None, + email=None, + registry=None, + reauth=False, + dockercfg_path=None, + ): """ Authenticate with a registry. Similar to the ``docker login`` command. @@ -80,25 +87,22 @@ class DaemonApiMixin(object): dockercfg_path, credstore_env=self.credstore_env ) elif not self._auth_configs or self._auth_configs.is_empty: - self._auth_configs = auth.load_config( - credstore_env=self.credstore_env - ) + self._auth_configs = auth.load_config(credstore_env=self.credstore_env) authcfg = self._auth_configs.resolve_authconfig(registry) # If we found an existing auth config for this registry and username # combination, we can return it immediately unless reauth is requested. - if authcfg and authcfg.get('username', None) == username \ - and not reauth: + if authcfg and authcfg.get("username", None) == username and not reauth: return authcfg req_data = { - 'username': username, - 'password': password, - 'email': email, - 'serveraddress': registry, + "username": username, + "password": password, + "email": email, + "serveraddress": registry, } - response = self._post_json(self._url('/auth'), data=req_data) + response = self._post_json(self._url("/auth"), data=req_data) if response.status_code == 200: self._auth_configs.add_auth(registry or auth.INDEX_NAME, req_data) return self._result(response, json=True) @@ -115,7 +119,7 @@ class DaemonApiMixin(object): :py:class:`docker.errors.APIError` If the server returns an error. """ - return self._result(self._get(self._url('/_ping'))) == 'OK' + return self._result(self._get(self._url("/_ping"))) == "OK" def version(self, api_version=True): """ diff --git a/plugins/module_utils/_api/auth.py b/plugins/module_utils/_api/auth.py index abbaafc4..e8826d45 100644 --- a/plugins/module_utils/_api/auth.py +++ b/plugins/module_utils/_api/auth.py @@ -14,44 +14,43 @@ import json import logging from . import errors +from .credentials.errors import CredentialsNotFound, StoreError from .credentials.store import Store -from .credentials.errors import StoreError, CredentialsNotFound from .utils import config -INDEX_NAME = 'docker.io' -INDEX_URL = f'https://index.{INDEX_NAME}/v1/' -TOKEN_USERNAME = '' + +INDEX_NAME = "docker.io" +INDEX_URL = f"https://index.{INDEX_NAME}/v1/" +TOKEN_USERNAME = "" log = logging.getLogger(__name__) def resolve_repository_name(repo_name): - if '://' in repo_name: + if "://" in repo_name: raise errors.InvalidRepository( - f'Repository name cannot contain a scheme ({repo_name})' + f"Repository name cannot contain a scheme ({repo_name})" ) index_name, remote_name = split_repo_name(repo_name) - if index_name[0] == '-' or index_name[-1] == '-': + if index_name[0] == "-" or index_name[-1] == "-": raise errors.InvalidRepository( - f'Invalid index name ({index_name}). Cannot begin or end with a hyphen.' + f"Invalid index name ({index_name}). Cannot begin or end with a hyphen." ) return resolve_index_name(index_name), remote_name def resolve_index_name(index_name): index_name = convert_to_hostname(index_name) - if index_name == 'index.' + INDEX_NAME: + if index_name == "index." + INDEX_NAME: index_name = INDEX_NAME return index_name def get_config_header(client, registry): - log.debug('Looking for auth config') + log.debug("Looking for auth config") if not client._auth_configs or client._auth_configs.is_empty: - log.debug( - "No auth config in memory - loading from filesystem" - ) + log.debug("No auth config in memory - loading from filesystem") client._auth_configs = load_config(credstore_env=client.credstore_env) authcfg = resolve_authconfig( client._auth_configs, registry, credstore_env=client.credstore_env @@ -60,18 +59,18 @@ def get_config_header(client, registry): # specific registry as we can have a readonly pull. Just # put the header if we can. if authcfg: - log.debug('Found auth config') + log.debug("Found auth config") # auth_config needs to be a dict in the format used by # auth.py username , password, serveraddress, email return encode_header(authcfg) - log.debug('No auth config found') + log.debug("No auth config found") return None def split_repo_name(repo_name): - parts = repo_name.split('/', 1) + parts = repo_name.split("/", 1) if len(parts) == 1 or ( - '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost' + "." not in parts[0] and ":" not in parts[0] and parts[0] != "localhost" ): # This is a docker index repo (ex: username/foobar or ubuntu) return INDEX_NAME, repo_name @@ -86,8 +85,8 @@ def get_credential_store(authconfig, registry): class AuthConfig(dict): def __init__(self, dct, credstore_env=None): - if 'auths' not in dct: - dct['auths'] = {} + if "auths" not in dct: + dct["auths"] = {} self.update(dct) self._credstore_env = credstore_env self._stores = {} @@ -109,39 +108,42 @@ class AuthConfig(dict): conf = {} for registry, entry in entries.items(): if not isinstance(entry, dict): - log.debug('Config entry for key %s is not auth config', registry) + log.debug("Config entry for key %s is not auth config", registry) # We sometimes fall back to parsing the whole config as if it # was the auth config by itself, for legacy purposes. In that # case, we fail silently and return an empty conf if any of the # keys is not formatted properly. if raise_on_error: raise errors.InvalidConfigFile( - f'Invalid configuration for registry {registry}' + f"Invalid configuration for registry {registry}" ) return {} - if 'identitytoken' in entry: - log.debug('Found an IdentityToken entry for registry %s', registry) - conf[registry] = { - 'IdentityToken': entry['identitytoken'] - } + if "identitytoken" in entry: + log.debug("Found an IdentityToken entry for registry %s", registry) + conf[registry] = {"IdentityToken": entry["identitytoken"]} continue # Other values are irrelevant if we have a token - if 'auth' not in entry: + if "auth" not in entry: # Starting with engine v1.11 (API 1.23), an empty dictionary is # a valid value in the auths config. # https://github.com/docker/compose/issues/3265 - log.debug('Auth data for %s is absent. Client might be using a credentials store instead.', registry) + log.debug( + "Auth data for %s is absent. Client might be using a credentials store instead.", + registry, + ) conf[registry] = {} continue - username, password = decode_auth(entry['auth']) - log.debug('Found entry (registry=%s, username=%s)', repr(registry), repr(username)) + username, password = decode_auth(entry["auth"]) + log.debug( + "Found entry (registry=%s, username=%s)", repr(registry), repr(username) + ) conf[registry] = { - 'username': username, - 'password': password, - 'email': entry.get('email'), - 'serveraddress': registry, + "username": username, + "password": password, + "email": entry.get("email"), + "serveraddress": registry, } return conf @@ -171,19 +173,17 @@ class AuthConfig(dict): return cls(_load_legacy_config(config_file), credstore_env) res = {} - if config_dict.get('auths'): + if config_dict.get("auths"): log.debug("Found 'auths' section") - res.update({ - 'auths': cls.parse_auth( - config_dict.pop('auths'), raise_on_error=True - ) - }) - if config_dict.get('credsStore'): + res.update( + {"auths": cls.parse_auth(config_dict.pop("auths"), raise_on_error=True)} + ) + if config_dict.get("credsStore"): log.debug("Found 'credsStore' section") - res.update({'credsStore': config_dict.pop('credsStore')}) - if config_dict.get('credHelpers'): + res.update({"credsStore": config_dict.pop("credsStore")}) + if config_dict.get("credHelpers"): log.debug("Found 'credHelpers' section") - res.update({'credHelpers': config_dict.pop('credHelpers')}) + res.update({"credHelpers": config_dict.pop("credHelpers")}) if res: return cls(res, credstore_env) @@ -191,25 +191,23 @@ class AuthConfig(dict): "Could not find auth-related section ; attempting to interpret " "as auth-only file" ) - return cls({'auths': cls.parse_auth(config_dict)}, credstore_env) + return cls({"auths": cls.parse_auth(config_dict)}, credstore_env) @property def auths(self): - return self.get('auths', {}) + return self.get("auths", {}) @property def creds_store(self): - return self.get('credsStore', None) + return self.get("credsStore", None) @property def cred_helpers(self): - return self.get('credHelpers', {}) + return self.get("credHelpers", {}) @property def is_empty(self): - return ( - not self.auths and not self.creds_store and not self.cred_helpers - ) + return not self.auths and not self.creds_store and not self.cred_helpers def resolve_authconfig(self, registry=None): """ @@ -226,7 +224,7 @@ class AuthConfig(dict): cfg = self._resolve_authconfig_credstore(registry, store_name) if cfg is not None: return cfg - log.debug('No entry in credstore - fetching from auth dict') + log.debug("No entry in credstore - fetching from auth dict") # Default to the public index server registry = resolve_index_name(registry) if registry else INDEX_NAME @@ -254,29 +252,27 @@ class AuthConfig(dict): try: data = store.get(registry) res = { - 'ServerAddress': registry, + "ServerAddress": registry, } - if data['Username'] == TOKEN_USERNAME: - res['IdentityToken'] = data['Secret'] + if data["Username"] == TOKEN_USERNAME: + res["IdentityToken"] = data["Secret"] else: - res.update({ - 'Username': data['Username'], - 'Password': data['Secret'], - }) + res.update( + { + "Username": data["Username"], + "Password": data["Secret"], + } + ) return res except CredentialsNotFound: - log.debug('No entry found') + log.debug("No entry found") return None except StoreError as e: - raise errors.DockerException( - f'Credentials store error: {e}' - ) + raise errors.DockerException(f"Credentials store error: {e}") def _get_store_instance(self, name): if name not in self._stores: - self._stores[name] = Store( - name, environment=self._credstore_env - ) + self._stores[name] = Store(name, environment=self._credstore_env) return self._stores[name] def get_credential_store(self, registry): @@ -291,22 +287,18 @@ class AuthConfig(dict): # Retrieve all credentials from the default store store = self._get_store_instance(self.creds_store) for k in store.list().keys(): - auth_data[k] = self._resolve_authconfig_credstore( - k, self.creds_store - ) + auth_data[k] = self._resolve_authconfig_credstore(k, self.creds_store) auth_data[convert_to_hostname(k)] = auth_data[k] # credHelpers entries take priority over all others for reg, store_name in self.cred_helpers.items(): - auth_data[reg] = self._resolve_authconfig_credstore( - reg, store_name - ) + auth_data[reg] = self._resolve_authconfig_credstore(reg, store_name) auth_data[convert_to_hostname(reg)] = auth_data[reg] return auth_data def add_auth(self, reg, data): - self['auths'][reg] = data + self["auths"][reg] = data def resolve_authconfig(authconfig, registry=None, credstore_env=None): @@ -316,19 +308,19 @@ def resolve_authconfig(authconfig, registry=None, credstore_env=None): def convert_to_hostname(url): - return url.replace('http://', '').replace('https://', '').split('/', 1)[0] + return url.replace("http://", "").replace("https://", "").split("/", 1)[0] def decode_auth(auth): if isinstance(auth, str): - auth = auth.encode('ascii') + auth = auth.encode("ascii") s = base64.b64decode(auth) - login, pwd = s.split(b':', 1) - return login.decode('utf8'), pwd.decode('utf8') + login, pwd = s.split(b":", 1) + return login.decode("utf8"), pwd.decode("utf8") def encode_header(auth): - auth_json = json.dumps(auth).encode('ascii') + auth_json = json.dumps(auth).encode("ascii") return base64.urlsafe_b64encode(auth_json) @@ -358,22 +350,22 @@ def _load_legacy_config(config_file): data = [] with open(config_file) as f: for line in f.readlines(): - data.append(line.strip().split(' = ')[1]) + data.append(line.strip().split(" = ")[1]) if len(data) < 2: # Not enough data - raise errors.InvalidConfigFile( - 'Invalid or empty configuration file!' - ) + raise errors.InvalidConfigFile("Invalid or empty configuration file!") username, password = decode_auth(data[0]) - return {'auths': { - INDEX_NAME: { - 'username': username, - 'password': password, - 'email': data[1], - 'serveraddress': INDEX_URL, + return { + "auths": { + INDEX_NAME: { + "username": username, + "password": password, + "email": data[1], + "serveraddress": INDEX_URL, + } } - }} + } except Exception as e: log.debug(e) pass diff --git a/plugins/module_utils/_api/constants.py b/plugins/module_utils/_api/constants.py index b0e48501..67afa16d 100644 --- a/plugins/module_utils/_api/constants.py +++ b/plugins/module_utils/_api/constants.py @@ -11,26 +11,20 @@ from __future__ import annotations import sys -MINIMUM_DOCKER_API_VERSION = '1.21' + +MINIMUM_DOCKER_API_VERSION = "1.21" DEFAULT_TIMEOUT_SECONDS = 60 STREAM_HEADER_SIZE_BYTES = 8 -CONTAINER_LIMITS_KEYS = [ - 'memory', 'memswap', 'cpushares', 'cpusetcpus' -] +CONTAINER_LIMITS_KEYS = ["memory", "memswap", "cpushares", "cpusetcpus"] DEFAULT_HTTP_HOST = "127.0.0.1" DEFAULT_UNIX_SOCKET = "http+unix:///var/run/docker.sock" -DEFAULT_NPIPE = 'npipe:////./pipe/docker_engine' +DEFAULT_NPIPE = "npipe:////./pipe/docker_engine" -BYTE_UNITS = { - 'b': 1, - 'k': 1024, - 'm': 1024 * 1024, - 'g': 1024 * 1024 * 1024 -} +BYTE_UNITS = {"b": 1, "k": 1024, "m": 1024 * 1024, "g": 1024 * 1024 * 1024} -IS_WINDOWS_PLATFORM = (sys.platform == 'win32') -WINDOWS_LONGPATH_PREFIX = '\\\\?\\' +IS_WINDOWS_PLATFORM = sys.platform == "win32" +WINDOWS_LONGPATH_PREFIX = "\\\\?\\" DEFAULT_USER_AGENT = "ansible-community.docker" DEFAULT_NUM_POOLS = 25 diff --git a/plugins/module_utils/_api/context/api.py b/plugins/module_utils/_api/context/api.py index 0dc45042..743672c5 100644 --- a/plugins/module_utils/_api/context/api.py +++ b/plugins/module_utils/_api/context/api.py @@ -13,7 +13,6 @@ import json import os from .. import errors - from .config import ( METAFILE, get_current_context_name, @@ -25,9 +24,11 @@ from .context import Context def create_default_context(): host = None - if os.environ.get('DOCKER_HOST'): - host = os.environ.get('DOCKER_HOST') - return Context("default", "swarm", host, description="Current DOCKER_HOST based configuration") + if os.environ.get("DOCKER_HOST"): + host = os.environ.get("DOCKER_HOST") + return Context( + "default", "swarm", host, description="Current DOCKER_HOST based configuration" + ) class ContextAPI(object): @@ -35,6 +36,7 @@ class ContextAPI(object): Contains methods for context management: create, list, remove, get, inspect. """ + DEFAULT_CONTEXT = None @classmethod @@ -47,8 +49,14 @@ class ContextAPI(object): @classmethod def create_context( - cls, name, orchestrator=None, host=None, tls_cfg=None, - default_namespace=None, skip_tls_verify=False): + cls, + name, + orchestrator=None, + host=None, + tls_cfg=None, + default_namespace=None, + skip_tls_verify=False, + ): """Creates a new context. Returns: (Context): a Context object. @@ -79,8 +87,7 @@ class ContextAPI(object): if not name: raise errors.MissingContextParameter("name") if name == "default": - raise errors.ContextException( - '"default" is a reserved context name') + raise errors.ContextException('"default" is a reserved context name') ctx = Context.load_context(name) if ctx: raise errors.ContextAlreadyExists(name) @@ -89,9 +96,12 @@ class ContextAPI(object): endpoint = orchestrator ctx = Context(name, orchestrator) ctx.set_endpoint( - endpoint, host, tls_cfg, + endpoint, + host, + tls_cfg, skip_tls_verify=skip_tls_verify, - def_namespace=default_namespace) + def_namespace=default_namespace, + ) ctx.save() return ctx @@ -173,8 +183,7 @@ class ContextAPI(object): err = write_context_name_to_docker_config(name) if err: - raise errors.ContextException( - f'Failed to set current context: {err}') + raise errors.ContextException(f"Failed to set current context: {err}") @classmethod def remove_context(cls, name): @@ -200,8 +209,7 @@ class ContextAPI(object): if not name: raise errors.MissingContextParameter("name") if name == "default": - raise errors.ContextException( - 'context "default" cannot be removed') + raise errors.ContextException('context "default" cannot be removed') ctx = Context.load_context(name) if not ctx: raise errors.ContextNotFound(name) diff --git a/plugins/module_utils/_api/context/config.py b/plugins/module_utils/_api/context/config.py index 9bcd020d..2c965a74 100644 --- a/plugins/module_utils/_api/context/config.py +++ b/plugins/module_utils/_api/context/config.py @@ -17,19 +17,23 @@ from ..constants import DEFAULT_UNIX_SOCKET, IS_WINDOWS_PLATFORM from ..utils.config import find_config_file, get_default_config_file from ..utils.utils import parse_host + METAFILE = "meta.json" def get_current_context_name_with_source(): - if os.environ.get('DOCKER_HOST'): + if os.environ.get("DOCKER_HOST"): return "default", "DOCKER_HOST environment variable set" - if os.environ.get('DOCKER_CONTEXT'): - return os.environ['DOCKER_CONTEXT'], "DOCKER_CONTEXT environment variable set" + if os.environ.get("DOCKER_CONTEXT"): + return os.environ["DOCKER_CONTEXT"], "DOCKER_CONTEXT environment variable set" docker_cfg_path = find_config_file() if docker_cfg_path: try: with open(docker_cfg_path) as f: - return json.load(f).get("currentContext", "default"), f"configuration file {docker_cfg_path}" + return ( + json.load(f).get("currentContext", "default"), + f"configuration file {docker_cfg_path}", + ) except Exception: pass return "default", "fallback value" @@ -40,7 +44,7 @@ def get_current_context_name(): def write_context_name_to_docker_config(name=None): - if name == 'default': + if name == "default": name = None docker_cfg_path = find_config_file() config = {} @@ -67,7 +71,7 @@ def write_context_name_to_docker_config(name=None): def get_context_id(name): - return hashlib.sha256(name.encode('utf-8')).hexdigest() + return hashlib.sha256(name.encode("utf-8")).hexdigest() def get_context_dir(): diff --git a/plugins/module_utils/_api/context/context.py b/plugins/module_utils/_api/context/context.py index 798d36e6..8a8d05e7 100644 --- a/plugins/module_utils/_api/context/context.py +++ b/plugins/module_utils/_api/context/context.py @@ -15,7 +15,6 @@ from shutil import copyfile, rmtree from ..errors import ContextException from ..tls import TLSConfig - from .config import ( get_context_host, get_meta_dir, @@ -30,8 +29,16 @@ IN_MEMORY = "IN MEMORY" class Context(object): """A context.""" - def __init__(self, name, orchestrator=None, host=None, endpoints=None, - skip_tls_verify=False, tls=False, description=None): + def __init__( + self, + name, + orchestrator=None, + host=None, + endpoints=None, + skip_tls_verify=False, + tls=False, + description=None, + ): if not name: raise Exception("Name not provided") self.name = name @@ -45,9 +52,11 @@ class Context(object): if not endpoints: # set default docker endpoint if no endpoint is set - default_endpoint = "docker" if ( - not orchestrator or orchestrator == "swarm" - ) else orchestrator + default_endpoint = ( + "docker" + if (not orchestrator or orchestrator == "swarm") + else orchestrator + ) self.endpoints = { default_endpoint: { @@ -69,17 +78,24 @@ class Context(object): if k != "docker": continue - self.endpoints[k]["Host"] = v.get("Host", get_context_host( - host, skip_tls_verify or tls)) - self.endpoints[k]["SkipTLSVerify"] = bool(v.get( - "SkipTLSVerify", skip_tls_verify)) + self.endpoints[k]["Host"] = v.get( + "Host", get_context_host(host, skip_tls_verify or tls) + ) + self.endpoints[k]["SkipTLSVerify"] = bool( + v.get("SkipTLSVerify", skip_tls_verify) + ) def set_endpoint( - self, name="docker", host=None, tls_cfg=None, - skip_tls_verify=False, def_namespace=None): + self, + name="docker", + host=None, + tls_cfg=None, + skip_tls_verify=False, + def_namespace=None, + ): self.endpoints[name] = { "Host": get_context_host(host, not skip_tls_verify or tls_cfg is not None), - "SkipTLSVerify": skip_tls_verify + "SkipTLSVerify": skip_tls_verify, } if def_namespace: self.endpoints[name]["DefaultNamespace"] = def_namespace @@ -98,7 +114,8 @@ class Context(object): meta["Name"], orchestrator=meta["Metadata"].get("StackOrchestrator", None), endpoints=meta.get("Endpoints", None), - description=meta["Metadata"].get('Description')) + description=meta["Metadata"].get("Description"), + ) instance.context_type = meta["Metadata"].get("Type", None) instance._load_certs() instance.meta_path = get_meta_dir(name) @@ -127,9 +144,11 @@ class Context(object): if k != "docker": continue metadata["Endpoints"][k]["Host"] = v.get( - "Host", get_context_host(None, False)) + "Host", get_context_host(None, False) + ) metadata["Endpoints"][k]["SkipTLSVerify"] = bool( - v.get("SkipTLSVerify", True)) + v.get("SkipTLSVerify", True) + ) return metadata @@ -152,10 +171,14 @@ class Context(object): if all([cert, key]) or ca_cert: verify = None if endpoint == "docker" and not self.endpoints["docker"].get( - "SkipTLSVerify", False): + "SkipTLSVerify", False + ): verify = True certs[endpoint] = TLSConfig( - client_cert=(cert, key) if cert and key else None, ca_cert=ca_cert, verify=verify) + client_cert=(cert, key) if cert and key else None, + ca_cert=ca_cert, + verify=verify, + ) self.tls_cfg = certs self.tls_path = tls_dir @@ -173,15 +196,20 @@ class Context(object): ca_file = tls.ca_cert if ca_file: - copyfile(ca_file, os.path.join( - tls_dir, endpoint, os.path.basename(ca_file))) + copyfile( + ca_file, os.path.join(tls_dir, endpoint, os.path.basename(ca_file)) + ) if tls.cert: cert_file, key_file = tls.cert - copyfile(cert_file, os.path.join( - tls_dir, endpoint, os.path.basename(cert_file))) - copyfile(key_file, os.path.join( - tls_dir, endpoint, os.path.basename(key_file))) + copyfile( + cert_file, + os.path.join(tls_dir, endpoint, os.path.basename(cert_file)), + ) + copyfile( + key_file, + os.path.join(tls_dir, endpoint, os.path.basename(key_file)), + ) self.meta_path = get_meta_dir(self.name) self.tls_path = get_tls_dir(self.name) @@ -230,11 +258,7 @@ class Context(object): meta = {} if self.orchestrator: meta = {"StackOrchestrator": self.orchestrator} - return { - "Name": self.name, - "Metadata": meta, - "Endpoints": self.endpoints - } + return {"Name": self.name, "Metadata": meta, "Endpoints": self.endpoints} @property def TLSConfig(self): @@ -250,16 +274,9 @@ class Context(object): certs = {} for endpoint, tls in self.tls_cfg.items(): cert, key = tls.cert - certs[endpoint] = list( - map(os.path.basename, [tls.ca_cert, cert, key])) - return { - "TLSMaterial": certs - } + certs[endpoint] = list(map(os.path.basename, [tls.ca_cert, cert, key])) + return {"TLSMaterial": certs} @property def Storage(self): - return { - "Storage": { - "MetadataPath": self.meta_path, - "TLSPath": self.tls_path - }} + return {"Storage": {"MetadataPath": self.meta_path, "TLSPath": self.tls_path}} diff --git a/plugins/module_utils/_api/credentials/constants.py b/plugins/module_utils/_api/credentials/constants.py index 710d7573..dac2a513 100644 --- a/plugins/module_utils/_api/credentials/constants.py +++ b/plugins/module_utils/_api/credentials/constants.py @@ -9,7 +9,8 @@ from __future__ import annotations -PROGRAM_PREFIX = 'docker-credential-' -DEFAULT_LINUX_STORE = 'secretservice' -DEFAULT_OSX_STORE = 'osxkeychain' -DEFAULT_WIN32_STORE = 'wincred' + +PROGRAM_PREFIX = "docker-credential-" +DEFAULT_LINUX_STORE = "secretservice" +DEFAULT_OSX_STORE = "osxkeychain" +DEFAULT_WIN32_STORE = "wincred" diff --git a/plugins/module_utils/_api/credentials/errors.py b/plugins/module_utils/_api/credentials/errors.py index 3d74324a..b5fda8d9 100644 --- a/plugins/module_utils/_api/credentials/errors.py +++ b/plugins/module_utils/_api/credentials/errors.py @@ -23,11 +23,9 @@ class InitializationError(StoreError): def process_store_error(cpe, program): - message = cpe.output.decode('utf-8') - if 'credentials not found in native keychain' in message: - return CredentialsNotFound( - f'No matching credentials in {program}' - ) + message = cpe.output.decode("utf-8") + if "credentials not found in native keychain" in message: + return CredentialsNotFound(f"No matching credentials in {program}") return StoreError( f'Credentials store {program} exited with "{cpe.output.decode("utf-8").strip()}".' ) diff --git a/plugins/module_utils/_api/credentials/store.py b/plugins/module_utils/_api/credentials/store.py index f152f736..e07084aa 100644 --- a/plugins/module_utils/_api/credentials/store.py +++ b/plugins/module_utils/_api/credentials/store.py @@ -13,84 +13,81 @@ import errno import json import subprocess -from . import constants -from . import errors -from .utils import create_environment_dict -from .utils import find_executable +from . import constants, errors +from .utils import create_environment_dict, find_executable class Store(object): def __init__(self, program, environment=None): - """ Create a store object that acts as an interface to - perform the basic operations for storing, retrieving - and erasing credentials using `program`. + """Create a store object that acts as an interface to + perform the basic operations for storing, retrieving + and erasing credentials using `program`. """ self.program = constants.PROGRAM_PREFIX + program self.exe = find_executable(self.program) self.environment = environment if self.exe is None: raise errors.InitializationError( - f'{self.program} not installed or not available in PATH' + f"{self.program} not installed or not available in PATH" ) def get(self, server): - """ Retrieve credentials for `server`. If no credentials are found, - a `StoreError` will be raised. + """Retrieve credentials for `server`. If no credentials are found, + a `StoreError` will be raised. """ if not isinstance(server, bytes): - server = server.encode('utf-8') - data = self._execute('get', server) - result = json.loads(data.decode('utf-8')) + server = server.encode("utf-8") + data = self._execute("get", server) + result = json.loads(data.decode("utf-8")) # docker-credential-pass will return an object for inexistent servers # whereas other helpers will exit with returncode != 0. For # consistency, if no significant data is returned, # raise CredentialsNotFound - if result['Username'] == '' and result['Secret'] == '': + if result["Username"] == "" and result["Secret"] == "": raise errors.CredentialsNotFound( - f'No matching credentials in {self.program}' + f"No matching credentials in {self.program}" ) return result def store(self, server, username, secret): - """ Store credentials for `server`. Raises a `StoreError` if an error - occurs. + """Store credentials for `server`. Raises a `StoreError` if an error + occurs. """ - data_input = json.dumps({ - 'ServerURL': server, - 'Username': username, - 'Secret': secret - }).encode('utf-8') - return self._execute('store', data_input) + data_input = json.dumps( + {"ServerURL": server, "Username": username, "Secret": secret} + ).encode("utf-8") + return self._execute("store", data_input) def erase(self, server): - """ Erase credentials for `server`. Raises a `StoreError` if an error - occurs. + """Erase credentials for `server`. Raises a `StoreError` if an error + occurs. """ if not isinstance(server, bytes): - server = server.encode('utf-8') - self._execute('erase', server) + server = server.encode("utf-8") + self._execute("erase", server) def list(self): - """ List stored credentials. Requires v0.4.0+ of the helper. - """ - data = self._execute('list', None) - return json.loads(data.decode('utf-8')) + """List stored credentials. Requires v0.4.0+ of the helper.""" + data = self._execute("list", None) + return json.loads(data.decode("utf-8")) def _execute(self, subcmd, data_input): output = None env = create_environment_dict(self.environment) try: output = subprocess.check_output( - [self.exe, subcmd], input=data_input, env=env, + [self.exe, subcmd], + input=data_input, + env=env, ) except subprocess.CalledProcessError as e: raise errors.process_store_error(e, self.program) except OSError as e: if e.errno == errno.ENOENT: raise errors.StoreError( - f'{self.program} not installed or not available in PATH' + f"{self.program} not installed or not available in PATH" ) else: raise errors.StoreError( diff --git a/plugins/module_utils/_api/credentials/utils.py b/plugins/module_utils/_api/credentials/utils.py index f8810381..24792311 100644 --- a/plugins/module_utils/_api/credentials/utils.py +++ b/plugins/module_utils/_api/credentials/utils.py @@ -10,7 +10,6 @@ from __future__ import annotations import os - from shutil import which diff --git a/plugins/module_utils/_api/errors.py b/plugins/module_utils/_api/errors.py index 8854f612..1097a494 100644 --- a/plugins/module_utils/_api/errors.py +++ b/plugins/module_utils/_api/errors.py @@ -9,10 +9,10 @@ from __future__ import annotations -from ._import_helper import HTTPError as _HTTPError - from ansible.module_utils.common.text.converters import to_native +from ._import_helper import HTTPError as _HTTPError + class DockerException(Exception): """ @@ -29,15 +29,16 @@ def create_api_error_from_http_exception(e): """ response = e.response try: - explanation = response.json()['message'] + explanation = response.json()["message"] except ValueError: - explanation = to_native((response.content or '').strip()) + explanation = to_native((response.content or "").strip()) cls = APIError if response.status_code == 404: - if explanation and ('No such image' in str(explanation) or - 'not found: does not exist or no pull access' - in str(explanation) or - 'repository does not exist' in str(explanation)): + if explanation and ( + "No such image" in str(explanation) + or "not found: does not exist or no pull access" in str(explanation) + or "repository does not exist" in str(explanation) + ): cls = ImageNotFound else: cls = NotFound @@ -48,6 +49,7 @@ class APIError(_HTTPError, DockerException): """ An HTTP error from the API. """ + def __init__(self, message, response=None, explanation=None): # requests 1.2 supports response as a keyword argument, but # requests 1.1 does not @@ -59,10 +61,10 @@ class APIError(_HTTPError, DockerException): message = super(APIError, self).__str__() if self.is_client_error(): - message = f'{self.response.status_code} Client Error for {self.response.url}: {self.response.reason}' + message = f"{self.response.status_code} Client Error for {self.response.url}: {self.response.reason}" elif self.is_server_error(): - message = f'{self.response.status_code} Server Error for {self.response.url}: {self.response.reason}' + message = f"{self.response.status_code} Server Error for {self.response.url}: {self.response.reason}" if self.explanation: message = f'{message} ("{self.explanation}")' @@ -121,10 +123,12 @@ class TLSParameterError(DockerException): self.msg = msg def __str__(self): - return self.msg + (". TLS configurations should map the Docker CLI " - "client configurations. See " - "https://docs.docker.com/engine/articles/https/ " - "for API details.") + return self.msg + ( + ". TLS configurations should map the Docker CLI " + "client configurations. See " + "https://docs.docker.com/engine/articles/https/ " + "for API details." + ) class NullResource(DockerException, ValueError): @@ -135,6 +139,7 @@ class ContainerError(DockerException): """ Represents a container that has exited with a non-zero exit code. """ + def __init__(self, container, exit_status, command, image, stderr): self.container = container self.exit_status = exit_status @@ -171,8 +176,8 @@ def create_unexpected_kwargs_error(name, kwargs): text.append("got an unexpected keyword argument ") else: text.append("got unexpected keyword arguments ") - text.append(', '.join(quoted_kwargs)) - return TypeError(''.join(text)) + text.append(", ".join(quoted_kwargs)) + return TypeError("".join(text)) class MissingContextParameter(DockerException): @@ -196,7 +201,7 @@ class ContextException(DockerException): self.msg = msg def __str__(self): - return (self.msg) + return self.msg class ContextNotFound(DockerException): @@ -214,4 +219,4 @@ class MissingRequirementException(DockerException): self.import_exception = import_exception def __str__(self): - return (self.msg) + return self.msg diff --git a/plugins/module_utils/_api/tls.py b/plugins/module_utils/_api/tls.py index b8c96352..e77f5c36 100644 --- a/plugins/module_utils/_api/tls.py +++ b/plugins/module_utils/_api/tls.py @@ -31,13 +31,20 @@ class TLSConfig(object): .. _`SSL version`: https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_TLSv1 """ + cert = None ca_cert = None verify = None ssl_version = None - def __init__(self, client_cert=None, ca_cert=None, verify=None, - ssl_version=None, assert_hostname=None): + def __init__( + self, + client_cert=None, + ca_cert=None, + verify=None, + ssl_version=None, + assert_hostname=None, + ): # Argument compatibility/mapping with # https://docs.docker.com/engine/articles/https/ # This diverges from the Docker CLI in that users can specify 'tls' @@ -61,15 +68,15 @@ class TLSConfig(object): tls_cert, tls_key = client_cert except ValueError: raise errors.TLSParameterError( - 'client_cert must be a tuple of' - ' (client certificate, key file)' + "client_cert must be a tuple of" " (client certificate, key file)" ) - if not (tls_cert and tls_key) or (not os.path.isfile(tls_cert) or - not os.path.isfile(tls_key)): + if not (tls_cert and tls_key) or ( + not os.path.isfile(tls_cert) or not os.path.isfile(tls_key) + ): raise errors.TLSParameterError( - 'Path to a certificate and key files must be provided' - ' through the client_cert param' + "Path to a certificate and key files must be provided" + " through the client_cert param" ) self.cert = (tls_cert, tls_key) @@ -78,7 +85,7 @@ class TLSConfig(object): self.ca_cert = ca_cert if self.verify and self.ca_cert and not os.path.isfile(self.ca_cert): raise errors.TLSParameterError( - 'Invalid CA certificate provided for `ca_cert`.' + "Invalid CA certificate provided for `ca_cert`." ) def configure_client(self, client): @@ -95,7 +102,10 @@ class TLSConfig(object): if self.cert: client.cert = self.cert - client.mount('https://', SSLHTTPAdapter( - ssl_version=self.ssl_version, - assert_hostname=self.assert_hostname, - )) + client.mount( + "https://", + SSLHTTPAdapter( + ssl_version=self.ssl_version, + assert_hostname=self.assert_hostname, + ), + ) diff --git a/plugins/module_utils/_api/transport/basehttpadapter.py b/plugins/module_utils/_api/transport/basehttpadapter.py index bc27e397..f9943d7f 100644 --- a/plugins/module_utils/_api/transport/basehttpadapter.py +++ b/plugins/module_utils/_api/transport/basehttpadapter.py @@ -15,7 +15,7 @@ from .._import_helper import HTTPAdapter as _HTTPAdapter class BaseHTTPAdapter(_HTTPAdapter): def close(self): super(BaseHTTPAdapter, self).close() - if hasattr(self, 'pools'): + if hasattr(self, "pools"): self.pools.clear() # Hotfix for requests 2.32.0 and 2.32.1: its commit @@ -23,7 +23,7 @@ class BaseHTTPAdapter(_HTTPAdapter): # changes requests.adapters.HTTPAdapter to no longer call get_connection() from # send(), but instead call _get_connection(). def _get_connection(self, request, *args, **kwargs): - return self.get_connection(request.url, kwargs.get('proxies')) + return self.get_connection(request.url, kwargs.get("proxies")) # Fix for requests 2.32.2+: # https://github.com/psf/requests/commit/c98e4d133ef29c46a9b68cd783087218a8075e05 diff --git a/plugins/module_utils/_api/transport/npipeconn.py b/plugins/module_utils/_api/transport/npipeconn.py index 8b919594..588e7b07 100644 --- a/plugins/module_utils/_api/transport/npipeconn.py +++ b/plugins/module_utils/_api/transport/npipeconn.py @@ -13,18 +13,16 @@ from queue import Empty from .. import constants from .._import_helper import HTTPAdapter, urllib3, urllib3_connection - from .basehttpadapter import BaseHTTPAdapter from .npipesocket import NpipeSocket + RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer class NpipeHTTPConnection(urllib3_connection.HTTPConnection, object): def __init__(self, npipe_path, timeout=60): - super(NpipeHTTPConnection, self).__init__( - 'localhost', timeout=timeout - ) + super(NpipeHTTPConnection, self).__init__("localhost", timeout=timeout) self.npipe_path = npipe_path self.timeout = timeout @@ -38,15 +36,13 @@ class NpipeHTTPConnection(urllib3_connection.HTTPConnection, object): class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): def __init__(self, npipe_path, timeout=60, maxsize=10): super(NpipeHTTPConnectionPool, self).__init__( - 'localhost', timeout=timeout, maxsize=maxsize + "localhost", timeout=timeout, maxsize=maxsize ) self.npipe_path = npipe_path self.timeout = timeout def _new_conn(self): - return NpipeHTTPConnection( - self.npipe_path, self.timeout - ) + return NpipeHTTPConnection(self.npipe_path, self.timeout) # When re-using connections, urllib3 tries to call select() on our # NpipeSocket instance, causing a crash. To circumvent this, we override @@ -63,8 +59,7 @@ class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): if self.block: raise urllib3.exceptions.EmptyPoolError( self, - "Pool reached maximum size and no more " - "connections are allowed." + "Pool reached maximum size and no more " "connections are allowed.", ) pass # Oh well, we'll create a new connection then @@ -73,15 +68,21 @@ class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): class NpipeHTTPAdapter(BaseHTTPAdapter): - __attrs__ = HTTPAdapter.__attrs__ + ['npipe_path', - 'pools', - 'timeout', - 'max_pool_size'] + __attrs__ = HTTPAdapter.__attrs__ + [ + "npipe_path", + "pools", + "timeout", + "max_pool_size", + ] - def __init__(self, base_url, timeout=60, - pool_connections=constants.DEFAULT_NUM_POOLS, - max_pool_size=constants.DEFAULT_MAX_POOL_SIZE): - self.npipe_path = base_url.replace('npipe://', '') + def __init__( + self, + base_url, + timeout=60, + pool_connections=constants.DEFAULT_NUM_POOLS, + max_pool_size=constants.DEFAULT_MAX_POOL_SIZE, + ): + self.npipe_path = base_url.replace("npipe://", "") self.timeout = timeout self.max_pool_size = max_pool_size self.pools = RecentlyUsedContainer( @@ -96,8 +97,7 @@ class NpipeHTTPAdapter(BaseHTTPAdapter): return pool pool = NpipeHTTPConnectionPool( - self.npipe_path, self.timeout, - maxsize=self.max_pool_size + self.npipe_path, self.timeout, maxsize=self.max_pool_size ) self.pools[url] = pool diff --git a/plugins/module_utils/_api/transport/npipesocket.py b/plugins/module_utils/_api/transport/npipesocket.py index 7cf86df1..9d18a9d6 100644 --- a/plugins/module_utils/_api/transport/npipesocket.py +++ b/plugins/module_utils/_api/transport/npipesocket.py @@ -14,18 +14,19 @@ import io import time import traceback + PYWIN32_IMPORT_ERROR = None try: + import pywintypes + import win32api + import win32event import win32file import win32pipe - import pywintypes - import win32event - import win32api except ImportError: PYWIN32_IMPORT_ERROR = traceback.format_exc() -cERROR_PIPE_BUSY = 0xe7 +cERROR_PIPE_BUSY = 0xE7 cSECURITY_SQOS_PRESENT = 0x100000 cSECURITY_ANONYMOUS = 0 @@ -36,18 +37,17 @@ def check_closed(f): @functools.wraps(f) def wrapped(self, *args, **kwargs): if self._closed: - raise RuntimeError( - 'Can not reuse socket after connection was closed.' - ) + raise RuntimeError("Can not reuse socket after connection was closed.") return f(self, *args, **kwargs) + return wrapped class NpipeSocket(object): - """ Partial implementation of the socket API over windows named pipes. - This implementation is only designed to be used as a client socket, - and server-specific methods (bind, listen, accept...) are not - implemented. + """Partial implementation of the socket API over windows named pipes. + This implementation is only designed to be used as a client socket, + and server-specific methods (bind, listen, accept...) are not + implemented. """ def __init__(self, handle=None): @@ -74,10 +74,12 @@ class NpipeSocket(object): 0, None, win32file.OPEN_EXISTING, - (cSECURITY_ANONYMOUS + ( + cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT - | win32file.FILE_FLAG_OVERLAPPED), - 0 + | win32file.FILE_FLAG_OVERLAPPED + ), + 0, ) except win32pipe.error as e: # See Remarks: @@ -87,7 +89,7 @@ class NpipeSocket(object): # before we got to it. Wait for availability and attempt to # connect again. retry_count = retry_count + 1 - if (retry_count < MAXIMUM_RETRY_COUNT): + if retry_count < MAXIMUM_RETRY_COUNT: time.sleep(1) return self.connect(address, retry_count) raise e @@ -126,7 +128,7 @@ class NpipeSocket(object): raise NotImplementedError() def makefile(self, mode=None, bufsize=None): - if mode.strip('b') != 'r': + if mode.strip("b") != "r": raise NotImplementedError() rawio = NpipeFileIOBase(self) if bufsize is None or bufsize <= 0: @@ -158,9 +160,7 @@ class NpipeSocket(object): overlapped = pywintypes.OVERLAPPED() overlapped.hEvent = event err, data = win32file.ReadFile( - self._handle, - readbuf[:nbytes] if nbytes else readbuf, - overlapped + self._handle, readbuf[:nbytes] if nbytes else readbuf, overlapped ) wait_result = win32event.WaitForSingleObject(event, self._timeout) if wait_result == win32event.WAIT_TIMEOUT: @@ -204,7 +204,7 @@ class NpipeSocket(object): # Blocking mode self._timeout = win32event.INFINITE elif not isinstance(value, (float, int)) or value < 0: - raise ValueError('Timeout value out of range') + raise ValueError("Timeout value out of range") else: # Timeout mode - Value converted to milliseconds self._timeout = int(value * 1000) diff --git a/plugins/module_utils/_api/transport/sshconn.py b/plugins/module_utils/_api/transport/sshconn.py index 92ca9d3d..c52b8cb0 100644 --- a/plugins/module_utils/_api/transport/sshconn.py +++ b/plugins/module_utils/_api/transport/sshconn.py @@ -18,10 +18,10 @@ import traceback from queue import Empty from urllib.parse import urlparse -from .basehttpadapter import BaseHTTPAdapter from .. import constants - from .._import_helper import HTTPAdapter, urllib3, urllib3_connection +from .basehttpadapter import BaseHTTPAdapter + PARAMIKO_IMPORT_ERROR = None try: @@ -35,51 +35,54 @@ RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer class SSHSocket(socket.socket): def __init__(self, host): - super(SSHSocket, self).__init__( - socket.AF_INET, socket.SOCK_STREAM) + super(SSHSocket, self).__init__(socket.AF_INET, socket.SOCK_STREAM) self.host = host self.port = None self.user = None - if ':' in self.host: - self.host, self.port = self.host.split(':') - if '@' in self.host: - self.user, self.host = self.host.split('@') + if ":" in self.host: + self.host, self.port = self.host.split(":") + if "@" in self.host: + self.user, self.host = self.host.split("@") self.proc = None def connect(self, **kwargs): - args = ['ssh'] + args = ["ssh"] if self.user: - args = args + ['-l', self.user] + args = args + ["-l", self.user] if self.port: - args = args + ['-p', self.port] + args = args + ["-p", self.port] - args = args + ['--', self.host, 'docker system dial-stdio'] + args = args + ["--", self.host, "docker system dial-stdio"] preexec_func = None if not constants.IS_WINDOWS_PLATFORM: + def f(): signal.signal(signal.SIGINT, signal.SIG_IGN) + preexec_func = f env = dict(os.environ) # drop LD_LIBRARY_PATH and SSL_CERT_FILE - env.pop('LD_LIBRARY_PATH', None) - env.pop('SSL_CERT_FILE', None) + env.pop("LD_LIBRARY_PATH", None) + env.pop("SSL_CERT_FILE", None) self.proc = subprocess.Popen( args, env=env, stdout=subprocess.PIPE, stdin=subprocess.PIPE, - preexec_fn=preexec_func) + preexec_fn=preexec_func, + ) def _write(self, data): if not self.proc or self.proc.stdin.closed: - raise Exception('SSH subprocess not initiated.' - 'connect() must be called first.') + raise Exception( + "SSH subprocess not initiated." "connect() must be called first." + ) written = self.proc.stdin.write(data) self.proc.stdin.flush() return written @@ -92,8 +95,9 @@ class SSHSocket(socket.socket): def recv(self, n): if not self.proc: - raise Exception('SSH subprocess not initiated.' - 'connect() must be called first.') + raise Exception( + "SSH subprocess not initiated." "connect() must be called first." + ) return self.proc.stdout.read(n) def makefile(self, mode): @@ -106,16 +110,14 @@ class SSHSocket(socket.socket): def close(self): if not self.proc or self.proc.stdin.closed: return - self.proc.stdin.write(b'\n\n') + self.proc.stdin.write(b"\n\n") self.proc.stdin.flush() self.proc.terminate() class SSHConnection(urllib3_connection.HTTPConnection, object): def __init__(self, ssh_transport=None, timeout=60, host=None): - super(SSHConnection, self).__init__( - 'localhost', timeout=timeout - ) + super(SSHConnection, self).__init__("localhost", timeout=timeout) self.ssh_transport = ssh_transport self.timeout = timeout self.ssh_host = host @@ -124,7 +126,7 @@ class SSHConnection(urllib3_connection.HTTPConnection, object): if self.ssh_transport: sock = self.ssh_transport.open_session() sock.settimeout(self.timeout) - sock.exec_command('docker system dial-stdio') + sock.exec_command("docker system dial-stdio") else: sock = SSHSocket(self.ssh_host) sock.settimeout(self.timeout) @@ -134,11 +136,11 @@ class SSHConnection(urllib3_connection.HTTPConnection, object): class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool): - scheme = 'ssh' + scheme = "ssh" def __init__(self, ssh_client=None, timeout=60, maxsize=10, host=None): super(SSHConnectionPool, self).__init__( - 'localhost', timeout=timeout, maxsize=maxsize + "localhost", timeout=timeout, maxsize=maxsize ) self.ssh_transport = None self.timeout = timeout @@ -164,8 +166,7 @@ class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool): if self.block: raise urllib3.exceptions.EmptyPoolError( self, - "Pool reached maximum size and no more " - "connections are allowed." + "Pool reached maximum size and no more " "connections are allowed.", ) pass # Oh well, we'll create a new connection then @@ -175,21 +176,29 @@ class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool): class SSHHTTPAdapter(BaseHTTPAdapter): __attrs__ = HTTPAdapter.__attrs__ + [ - 'pools', 'timeout', 'ssh_client', 'ssh_params', 'max_pool_size' + "pools", + "timeout", + "ssh_client", + "ssh_params", + "max_pool_size", ] - def __init__(self, base_url, timeout=60, - pool_connections=constants.DEFAULT_NUM_POOLS, - max_pool_size=constants.DEFAULT_MAX_POOL_SIZE, - shell_out=False): + def __init__( + self, + base_url, + timeout=60, + pool_connections=constants.DEFAULT_NUM_POOLS, + max_pool_size=constants.DEFAULT_MAX_POOL_SIZE, + shell_out=False, + ): self.ssh_client = None if not shell_out: self._create_paramiko_client(base_url) self._connect() self.ssh_host = base_url - if base_url.startswith('ssh://'): - self.ssh_host = base_url[len('ssh://'):] + if base_url.startswith("ssh://"): + self.ssh_host = base_url[len("ssh://") :] self.timeout = timeout self.max_pool_size = max_pool_size @@ -213,18 +222,18 @@ class SSHHTTPAdapter(BaseHTTPAdapter): with open(ssh_config_file) as f: conf.parse(f) host_config = conf.lookup(base_url.hostname) - if 'proxycommand' in host_config: + if "proxycommand" in host_config: self.ssh_params["sock"] = paramiko.ProxyCommand( - host_config['proxycommand'] + host_config["proxycommand"] ) - if 'hostname' in host_config: - self.ssh_params['hostname'] = host_config['hostname'] - if base_url.port is None and 'port' in host_config: - self.ssh_params['port'] = host_config['port'] - if base_url.username is None and 'user' in host_config: - self.ssh_params['username'] = host_config['user'] - if 'identityfile' in host_config: - self.ssh_params['key_filename'] = host_config['identityfile'] + if "hostname" in host_config: + self.ssh_params["hostname"] = host_config["hostname"] + if base_url.port is None and "port" in host_config: + self.ssh_params["port"] = host_config["port"] + if base_url.username is None and "user" in host_config: + self.ssh_params["username"] = host_config["user"] + if "identityfile" in host_config: + self.ssh_params["key_filename"] = host_config["identityfile"] self.ssh_client.load_system_host_keys() self.ssh_client.set_missing_host_key_policy(paramiko.RejectPolicy()) @@ -239,7 +248,7 @@ class SSHHTTPAdapter(BaseHTTPAdapter): ssh_client=self.ssh_client, timeout=self.timeout, maxsize=self.max_pool_size, - host=self.ssh_host + host=self.ssh_host, ) with self.pools.lock: pool = self.pools.get(url) @@ -254,7 +263,7 @@ class SSHHTTPAdapter(BaseHTTPAdapter): ssh_client=self.ssh_client, timeout=self.timeout, maxsize=self.max_pool_size, - host=self.ssh_host + host=self.ssh_host, ) self.pools[url] = pool diff --git a/plugins/module_utils/_api/transport/ssladapter.py b/plugins/module_utils/_api/transport/ssladapter.py index 5876be36..3ee9cdb5 100644 --- a/plugins/module_utils/_api/transport/ssladapter.py +++ b/plugins/module_utils/_api/transport/ssladapter.py @@ -9,12 +9,15 @@ from __future__ import annotations + """ Resolves OpenSSL issues in some servers: https://lukasa.co.uk/2013/01/Choosing_SSL_Version_In_Requests/ https://github.com/kennethreitz/requests/pull/799 """ -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) from .._import_helper import HTTPAdapter, urllib3 from .basehttpadapter import BaseHTTPAdapter @@ -24,9 +27,9 @@ PoolManager = urllib3.poolmanager.PoolManager class SSLHTTPAdapter(BaseHTTPAdapter): - '''An HTTPS Transport Adapter that uses an arbitrary SSL version.''' + """An HTTPS Transport Adapter that uses an arbitrary SSL version.""" - __attrs__ = HTTPAdapter.__attrs__ + ['assert_hostname', 'ssl_version'] + __attrs__ = HTTPAdapter.__attrs__ + ["assert_hostname", "ssl_version"] def __init__(self, ssl_version=None, assert_hostname=None, **kwargs): self.ssl_version = ssl_version @@ -35,14 +38,14 @@ class SSLHTTPAdapter(BaseHTTPAdapter): def init_poolmanager(self, connections, maxsize, block=False): kwargs = { - 'num_pools': connections, - 'maxsize': maxsize, - 'block': block, + "num_pools": connections, + "maxsize": maxsize, + "block": block, } if self.assert_hostname is not None: - kwargs['assert_hostname'] = self.assert_hostname + kwargs["assert_hostname"] = self.assert_hostname if self.ssl_version and self.can_override_ssl_version(): - kwargs['ssl_version'] = self.ssl_version + kwargs["ssl_version"] = self.ssl_version self.poolmanager = PoolManager(**kwargs) @@ -55,14 +58,17 @@ class SSLHTTPAdapter(BaseHTTPAdapter): But we still need to take care of when there is a proxy poolmanager """ conn = super(SSLHTTPAdapter, self).get_connection(*args, **kwargs) - if self.assert_hostname is not None and conn.assert_hostname != self.assert_hostname: + if ( + self.assert_hostname is not None + and conn.assert_hostname != self.assert_hostname + ): conn.assert_hostname = self.assert_hostname return conn def can_override_ssl_version(self): - urllib_ver = urllib3.__version__.split('-')[0] + urllib_ver = urllib3.__version__.split("-")[0] if urllib_ver is None: return False - if urllib_ver == 'dev': + if urllib_ver == "dev": return True - return LooseVersion(urllib_ver) > LooseVersion('1.5') + return LooseVersion(urllib_ver) > LooseVersion("1.5") diff --git a/plugins/module_utils/_api/transport/unixconn.py b/plugins/module_utils/_api/transport/unixconn.py index 2a380a2a..97335992 100644 --- a/plugins/module_utils/_api/transport/unixconn.py +++ b/plugins/module_utils/_api/transport/unixconn.py @@ -11,10 +11,9 @@ from __future__ import annotations import socket -from .basehttpadapter import BaseHTTPAdapter from .. import constants - from .._import_helper import HTTPAdapter, urllib3, urllib3_connection +from .basehttpadapter import BaseHTTPAdapter RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer @@ -23,9 +22,7 @@ RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer class UnixHTTPConnection(urllib3_connection.HTTPConnection, object): def __init__(self, base_url, unix_socket, timeout=60): - super(UnixHTTPConnection, self).__init__( - 'localhost', timeout=timeout - ) + super(UnixHTTPConnection, self).__init__("localhost", timeout=timeout) self.base_url = base_url self.unix_socket = unix_socket self.timeout = timeout @@ -39,7 +36,7 @@ class UnixHTTPConnection(urllib3_connection.HTTPConnection, object): def putheader(self, header, *values): super(UnixHTTPConnection, self).putheader(header, *values) - if header == 'Connection' and 'Upgrade' in values: + if header == "Connection" and "Upgrade" in values: self.disable_buffering = True def response_class(self, sock, *args, **kwargs): @@ -52,31 +49,35 @@ class UnixHTTPConnection(urllib3_connection.HTTPConnection, object): class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): def __init__(self, base_url, socket_path, timeout=60, maxsize=10): super(UnixHTTPConnectionPool, self).__init__( - 'localhost', timeout=timeout, maxsize=maxsize + "localhost", timeout=timeout, maxsize=maxsize ) self.base_url = base_url self.socket_path = socket_path self.timeout = timeout def _new_conn(self): - return UnixHTTPConnection( - self.base_url, self.socket_path, self.timeout - ) + return UnixHTTPConnection(self.base_url, self.socket_path, self.timeout) class UnixHTTPAdapter(BaseHTTPAdapter): - __attrs__ = HTTPAdapter.__attrs__ + ['pools', - 'socket_path', - 'timeout', - 'max_pool_size'] + __attrs__ = HTTPAdapter.__attrs__ + [ + "pools", + "socket_path", + "timeout", + "max_pool_size", + ] - def __init__(self, socket_url, timeout=60, - pool_connections=constants.DEFAULT_NUM_POOLS, - max_pool_size=constants.DEFAULT_MAX_POOL_SIZE): - socket_path = socket_url.replace('http+unix://', '') - if not socket_path.startswith('/'): - socket_path = '/' + socket_path + def __init__( + self, + socket_url, + timeout=60, + pool_connections=constants.DEFAULT_NUM_POOLS, + max_pool_size=constants.DEFAULT_MAX_POOL_SIZE, + ): + socket_path = socket_url.replace("http+unix://", "") + if not socket_path.startswith("/"): + socket_path = "/" + socket_path self.socket_path = socket_path self.timeout = timeout self.max_pool_size = max_pool_size @@ -92,8 +93,7 @@ class UnixHTTPAdapter(BaseHTTPAdapter): return pool pool = UnixHTTPConnectionPool( - url, self.socket_path, self.timeout, - maxsize=self.max_pool_size + url, self.socket_path, self.timeout, maxsize=self.max_pool_size ) self.pools[url] = pool diff --git a/plugins/module_utils/_api/types/daemon.py b/plugins/module_utils/_api/types/daemon.py index ec8c44a0..c8f7c2d8 100644 --- a/plugins/module_utils/_api/types/daemon.py +++ b/plugins/module_utils/_api/types/daemon.py @@ -12,7 +12,6 @@ from __future__ import annotations import socket from .._import_helper import urllib3 - from ..errors import DockerException @@ -56,26 +55,27 @@ class CancellableStream(object): sock_fp = self._response.raw._fp.fp - if hasattr(sock_fp, 'raw'): + if hasattr(sock_fp, "raw"): sock_raw = sock_fp.raw - if hasattr(sock_raw, 'sock'): + if hasattr(sock_raw, "sock"): sock = sock_raw.sock - elif hasattr(sock_raw, '_sock'): + elif hasattr(sock_raw, "_sock"): sock = sock_raw._sock - elif hasattr(sock_fp, 'channel'): + elif hasattr(sock_fp, "channel"): # We are working with a paramiko (SSH) channel, which does not # support cancelable streams with the current implementation raise DockerException( - 'Cancellable streams not supported for the SSH protocol' + "Cancellable streams not supported for the SSH protocol" ) else: sock = sock_fp._sock - if hasattr(urllib3.contrib, 'pyopenssl') and isinstance( - sock, urllib3.contrib.pyopenssl.WrappedSocket): + if hasattr(urllib3.contrib, "pyopenssl") and isinstance( + sock, urllib3.contrib.pyopenssl.WrappedSocket + ): sock = sock.socket sock.shutdown(socket.SHUT_RDWR) diff --git a/plugins/module_utils/_api/utils/build.py b/plugins/module_utils/_api/utils/build.py index c75119da..425232db 100644 --- a/plugins/module_utils/_api/utils/build.py +++ b/plugins/module_utils/_api/utils/build.py @@ -16,11 +16,11 @@ import re import tarfile import tempfile -from . import fnmatch from ..constants import IS_WINDOWS_PLATFORM, WINDOWS_LONGPATH_PREFIX +from . import fnmatch -_SEP = re.compile('/|\\\\') if IS_WINDOWS_PLATFORM else re.compile('/') +_SEP = re.compile("/|\\\\") if IS_WINDOWS_PLATFORM else re.compile("/") def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False): @@ -29,16 +29,19 @@ def tar(path, exclude=None, dockerfile=None, fileobj=None, gzip=False): dockerfile = dockerfile or (None, None) extra_files = [] if dockerfile[1] is not None: - dockerignore_contents = '\n'.join( - (exclude or ['.dockerignore']) + [dockerfile[0]] + dockerignore_contents = "\n".join( + (exclude or [".dockerignore"]) + [dockerfile[0]] ) extra_files = [ - ('.dockerignore', dockerignore_contents), + (".dockerignore", dockerignore_contents), dockerfile, ] return create_archive( files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile[0])), - root=root, fileobj=fileobj, gzip=gzip, extra_files=extra_files + root=root, + fileobj=fileobj, + gzip=gzip, + extra_files=extra_files, ) @@ -52,9 +55,9 @@ def exclude_paths(root, patterns, dockerfile=None): """ if dockerfile is None: - dockerfile = 'Dockerfile' + dockerfile = "Dockerfile" - patterns.append('!' + dockerfile) + patterns.append("!" + dockerfile) pm = PatternMatcher(patterns) return set(pm.walk(root)) @@ -64,19 +67,16 @@ def build_file_list(root): for dirname, dirnames, fnames in os.walk(root): for filename in fnames + dirnames: longpath = os.path.join(dirname, filename) - files.append( - longpath.replace(root, '', 1).lstrip('/') - ) + files.append(longpath.replace(root, "", 1).lstrip("/")) return files -def create_archive(root, files=None, fileobj=None, gzip=False, - extra_files=None): +def create_archive(root, files=None, fileobj=None, gzip=False, extra_files=None): extra_files = extra_files or [] if not fileobj: fileobj = tempfile.NamedTemporaryFile() - t = tarfile.open(mode='w:gz' if gzip else 'w', fileobj=fileobj) + t = tarfile.open(mode="w:gz" if gzip else "w", fileobj=fileobj) if files is None: files = build_file_list(root) extra_names = set(e[0] for e in extra_files) @@ -103,19 +103,17 @@ def create_archive(root, files=None, fileobj=None, gzip=False, if i.isfile(): try: - with open(full_path, 'rb') as f: + with open(full_path, "rb") as f: t.addfile(i, f) except IOError: - raise IOError( - f'Can not read file in context: {full_path}' - ) + raise IOError(f"Can not read file in context: {full_path}") else: # Directories, FIFOs, symlinks... do not need to be read. t.addfile(i, None) for name, contents in extra_files: info = tarfile.TarInfo(name) - contents_encoded = contents.encode('utf-8') + contents_encoded = contents.encode("utf-8") info.size = len(contents_encoded) t.addfile(info, io.BytesIO(contents_encoded)) @@ -126,15 +124,15 @@ def create_archive(root, files=None, fileobj=None, gzip=False, def mkbuildcontext(dockerfile): f = tempfile.NamedTemporaryFile() - t = tarfile.open(mode='w', fileobj=f) + t = tarfile.open(mode="w", fileobj=f) if isinstance(dockerfile, io.StringIO): - raise TypeError('Please use io.BytesIO to create in-memory Dockerfiles') + raise TypeError("Please use io.BytesIO to create in-memory Dockerfiles") elif isinstance(dockerfile, io.BytesIO): - dfinfo = tarfile.TarInfo('Dockerfile') + dfinfo = tarfile.TarInfo("Dockerfile") dfinfo.size = len(dockerfile.getvalue()) dockerfile.seek(0) else: - dfinfo = t.gettarinfo(fileobj=dockerfile, arcname='Dockerfile') + dfinfo = t.gettarinfo(fileobj=dockerfile, arcname="Dockerfile") t.addfile(dfinfo, dockerfile) t.close() f.seek(0) @@ -142,12 +140,12 @@ def mkbuildcontext(dockerfile): def split_path(p): - return [pt for pt in re.split(_SEP, p) if pt and pt != '.'] + return [pt for pt in re.split(_SEP, p) if pt and pt != "."] def normalize_slashes(p): if IS_WINDOWS_PLATFORM: - return '/'.join(split_path(p)) + return "/".join(split_path(p)) return p @@ -160,10 +158,8 @@ def walk(root, patterns, default=True): # https://github.com/moby/moby/blob/master/pkg/fileutils/fileutils.go class PatternMatcher(object): def __init__(self, patterns): - self.patterns = list(filter( - lambda p: p.dirs, [Pattern(p) for p in patterns] - )) - self.patterns.append(Pattern('!.dockerignore')) + self.patterns = list(filter(lambda p: p.dirs, [Pattern(p) for p in patterns])) + self.patterns.append(Pattern("!.dockerignore")) def matches(self, filepath): matched = False @@ -173,10 +169,10 @@ class PatternMatcher(object): for pattern in self.patterns: negative = pattern.exclusion match = pattern.match(filepath) - if not match and parent_path != '': + if not match and parent_path != "": if len(pattern.dirs) <= len(parent_path_dirs): match = pattern.match( - os.path.sep.join(parent_path_dirs[:len(pattern.dirs)]) + os.path.sep.join(parent_path_dirs[: len(pattern.dirs)]) ) if match: @@ -187,10 +183,8 @@ class PatternMatcher(object): def walk(self, root): def rec_walk(current_dir): for f in os.listdir(current_dir): - fpath = os.path.join( - os.path.relpath(current_dir, root), f - ) - if fpath.startswith('.' + os.path.sep): + fpath = os.path.join(os.path.relpath(current_dir, root), f) + if fpath.startswith("." + os.path.sep): fpath = fpath[2:] match = self.matches(fpath) if not match: @@ -210,8 +204,7 @@ class PatternMatcher(object): for pat in self.patterns: if not pat.exclusion: continue - if pat.cleaned_pattern.startswith( - normalize_slashes(fpath)): + if pat.cleaned_pattern.startswith(normalize_slashes(fpath)): skip = False break if skip: @@ -224,12 +217,12 @@ class PatternMatcher(object): class Pattern(object): def __init__(self, pattern_str): self.exclusion = False - if pattern_str.startswith('!'): + if pattern_str.startswith("!"): self.exclusion = True pattern_str = pattern_str[1:] self.dirs = self.normalize(pattern_str) - self.cleaned_pattern = '/'.join(self.dirs) + self.cleaned_pattern = "/".join(self.dirs) @classmethod def normalize(cls, p): @@ -249,7 +242,7 @@ class Pattern(object): i = 0 split = split_path(p) while i < len(split): - if split[i] == '..': + if split[i] == "..": del split[i] if i > 0: del split[i - 1] @@ -269,17 +262,14 @@ def process_dockerfile(dockerfile, path): abs_dockerfile = dockerfile if not os.path.isabs(dockerfile): abs_dockerfile = os.path.join(path, dockerfile) - if IS_WINDOWS_PLATFORM and path.startswith( - WINDOWS_LONGPATH_PREFIX): - abs_dockerfile = f'{WINDOWS_LONGPATH_PREFIX}{os.path.normpath(abs_dockerfile[len(WINDOWS_LONGPATH_PREFIX):])}' - if (os.path.splitdrive(path)[0] != os.path.splitdrive(abs_dockerfile)[0] or - os.path.relpath(abs_dockerfile, path).startswith('..')): + if IS_WINDOWS_PLATFORM and path.startswith(WINDOWS_LONGPATH_PREFIX): + abs_dockerfile = f"{WINDOWS_LONGPATH_PREFIX}{os.path.normpath(abs_dockerfile[len(WINDOWS_LONGPATH_PREFIX):])}" + if os.path.splitdrive(path)[0] != os.path.splitdrive(abs_dockerfile)[ + 0 + ] or os.path.relpath(abs_dockerfile, path).startswith(".."): # Dockerfile not in context - read data to insert into tar later with open(abs_dockerfile) as df: - return ( - f'.dockerfile.{random.getrandbits(160):x}', - df.read() - ) + return (f".dockerfile.{random.getrandbits(160):x}", df.read()) # Dockerfile is inside the context - return path relative to context root if dockerfile == abs_dockerfile: diff --git a/plugins/module_utils/_api/utils/config.py b/plugins/module_utils/_api/utils/config.py index e4ecb55b..d0ccf82b 100644 --- a/plugins/module_utils/_api/utils/config.py +++ b/plugins/module_utils/_api/utils/config.py @@ -15,8 +15,9 @@ import os from ..constants import IS_WINDOWS_PLATFORM -DOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json') -LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg' + +DOCKER_CONFIG_FILENAME = os.path.join(".docker", "config.json") +LEGACY_DOCKER_CONFIG_FILENAME = ".dockercfg" log = logging.getLogger(__name__) @@ -27,12 +28,17 @@ def get_default_config_file(): def find_config_file(config_path=None): homedir = home_dir() - paths = list(filter(None, [ - config_path, # 1 - config_path_from_environment(), # 2 - os.path.join(homedir, DOCKER_CONFIG_FILENAME), # 3 - os.path.join(homedir, LEGACY_DOCKER_CONFIG_FILENAME), # 4 - ])) + paths = list( + filter( + None, + [ + config_path, # 1 + config_path_from_environment(), # 2 + os.path.join(homedir, DOCKER_CONFIG_FILENAME), # 3 + os.path.join(homedir, LEGACY_DOCKER_CONFIG_FILENAME), # 4 + ], + ) + ) log.debug("Trying paths: %s", repr(paths)) @@ -47,7 +53,7 @@ def find_config_file(config_path=None): def config_path_from_environment(): - config_dir = os.environ.get('DOCKER_CONFIG') + config_dir = os.environ.get("DOCKER_CONFIG") if not config_dir: return None return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME)) @@ -59,9 +65,9 @@ def home_dir(): client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX. """ if IS_WINDOWS_PLATFORM: - return os.environ.get('USERPROFILE', '') + return os.environ.get("USERPROFILE", "") else: - return os.path.expanduser('~') + return os.path.expanduser("~") def load_general_config(config_path=None): diff --git a/plugins/module_utils/_api/utils/decorators.py b/plugins/module_utils/_api/utils/decorators.py index 5a0cd091..11d7b64d 100644 --- a/plugins/module_utils/_api/utils/decorators.py +++ b/plugins/module_utils/_api/utils/decorators.py @@ -22,13 +22,13 @@ def check_resource(resource_name): if resource_id is None and kwargs.get(resource_name): resource_id = kwargs.pop(resource_name) if isinstance(resource_id, dict): - resource_id = resource_id.get('Id', resource_id.get('ID')) + resource_id = resource_id.get("Id", resource_id.get("ID")) if not resource_id: - raise errors.NullResource( - 'Resource ID was not provided' - ) + raise errors.NullResource("Resource ID was not provided") return f(self, resource_id, *args, **kwargs) + return wrapped + return decorator @@ -38,19 +38,22 @@ def minimum_version(version): def wrapper(self, *args, **kwargs): if utils.version_lt(self._version, version): raise errors.InvalidVersion( - f'{f.__name__} is not available for version < {version}' + f"{f.__name__} is not available for version < {version}" ) return f(self, *args, **kwargs) + return wrapper + return decorator def update_headers(f): def inner(self, *args, **kwargs): - if 'HttpHeaders' in self._general_configs: - if not kwargs.get('headers'): - kwargs['headers'] = self._general_configs['HttpHeaders'] + if "HttpHeaders" in self._general_configs: + if not kwargs.get("headers"): + kwargs["headers"] = self._general_configs["HttpHeaders"] else: - kwargs['headers'].update(self._general_configs['HttpHeaders']) + kwargs["headers"].update(self._general_configs["HttpHeaders"]) return f(self, *args, **kwargs) + return inner diff --git a/plugins/module_utils/_api/utils/fnmatch.py b/plugins/module_utils/_api/utils/fnmatch.py index 84677e1e..379f1b4e 100644 --- a/plugins/module_utils/_api/utils/fnmatch.py +++ b/plugins/module_utils/_api/utils/fnmatch.py @@ -9,6 +9,7 @@ from __future__ import annotations + """Filename matching with shell patterns. fnmatch(FILENAME, PATTERN) matches according to the local convention. @@ -23,6 +24,7 @@ corresponding to PATTERN. (It does not compile it.) import re + __all__ = ["fnmatch", "fnmatchcase", "translate"] _cache = {} @@ -77,50 +79,50 @@ def translate(pat): There is no way to quote meta-characters. """ i, n = 0, len(pat) - res = '^' + res = "^" while i < n: c = pat[i] i = i + 1 - if c == '*': - if i < n and pat[i] == '*': + if c == "*": + if i < n and pat[i] == "*": # is some flavor of "**" i = i + 1 # Treat **/ as ** so eat the "/" - if i < n and pat[i] == '/': + if i < n and pat[i] == "/": i = i + 1 if i >= n: # is "**EOF" - to align with .gitignore just accept all - res = res + '.*' + res = res + ".*" else: # is "**" # Note that this allows for any # of /'s (even 0) because # the .* will eat everything, even /'s - res = res + '(.*/)?' + res = res + "(.*/)?" else: # is "*" so map it to anything but "/" - res = res + '[^/]*' - elif c == '?': + res = res + "[^/]*" + elif c == "?": # "?" is any char except "/" - res = res + '[^/]' - elif c == '[': + res = res + "[^/]" + elif c == "[": j = i - if j < n and pat[j] == '!': + if j < n and pat[j] == "!": j = j + 1 - if j < n and pat[j] == ']': + if j < n and pat[j] == "]": j = j + 1 - while j < n and pat[j] != ']': + while j < n and pat[j] != "]": j = j + 1 if j >= n: - res = res + '\\[' + res = res + "\\[" else: - stuff = pat[i:j].replace('\\', '\\\\') + stuff = pat[i:j].replace("\\", "\\\\") i = j + 1 - if stuff[0] == '!': - stuff = '^' + stuff[1:] - elif stuff[0] == '^': - stuff = '\\' + stuff - res = f'{res}[{stuff}]' + if stuff[0] == "!": + stuff = "^" + stuff[1:] + elif stuff[0] == "^": + stuff = "\\" + stuff + res = f"{res}[{stuff}]" else: res = res + re.escape(c) - return res + '$' + return res + "$" diff --git a/plugins/module_utils/_api/utils/json_stream.py b/plugins/module_utils/_api/utils/json_stream.py index caf400b1..1271d48d 100644 --- a/plugins/module_utils/_api/utils/json_stream.py +++ b/plugins/module_utils/_api/utils/json_stream.py @@ -27,7 +27,7 @@ def stream_as_text(stream): """ for data in stream: if not isinstance(data, str): - data = data.decode('utf-8', 'replace') + data = data.decode("utf-8", "replace") yield data @@ -38,7 +38,7 @@ def json_splitter(buffer): buffer = buffer.strip() try: obj, index = json_decoder.raw_decode(buffer) - rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end():] + rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end() :] return obj, rest except ValueError: return None @@ -52,11 +52,11 @@ def json_stream(stream): return split_buffer(stream, json_splitter, json_decoder.decode) -def line_splitter(buffer, separator='\n'): +def line_splitter(buffer, separator="\n"): index = buffer.find(str(separator)) if index == -1: return None - return buffer[:index + 1], buffer[index + 1:] + return buffer[: index + 1], buffer[index + 1 :] def split_buffer(stream, splitter=None, decoder=lambda a: a): @@ -67,7 +67,7 @@ def split_buffer(stream, splitter=None, decoder=lambda a: a): of the input. """ splitter = splitter or line_splitter - buffered = '' + buffered = "" for data in stream_as_text(stream): buffered += data diff --git a/plugins/module_utils/_api/utils/ports.py b/plugins/module_utils/_api/utils/ports.py index a0e3a04c..857466d4 100644 --- a/plugins/module_utils/_api/utils/ports.py +++ b/plugins/module_utils/_api/utils/ports.py @@ -11,6 +11,7 @@ from __future__ import annotations import re + PORT_SPEC = re.compile( "^" # Match full string "(" # External part @@ -49,23 +50,25 @@ def build_port_bindings(ports): def _raise_invalid_port(port): - raise ValueError(f'Invalid port "{port}", should be ' - '[[remote_ip:]remote_port[-remote_port]:]' - 'port[/protocol]') + raise ValueError( + f'Invalid port "{port}", should be ' + "[[remote_ip:]remote_port[-remote_port]:]" + "port[/protocol]" + ) def port_range(start, end, proto, randomly_available_port=False): if not start: return start if not end: - return [f'{start}{proto}'] + return [f"{start}{proto}"] if randomly_available_port: - return [f'{start}-{end}{proto}'] - return [f'{port}{proto}' for port in range(int(start), int(end) + 1)] + return [f"{start}-{end}{proto}"] + return [f"{port}{proto}" for port in range(int(start), int(end) + 1)] def split_port(port): - if hasattr(port, 'legacy_repr'): + if hasattr(port, "legacy_repr"): # This is the worst hack, but it prevents a bug in Compose 1.14.0 # https://github.com/docker/docker-py/issues/1668 # TODO: remove once fixed in Compose stable @@ -76,19 +79,18 @@ def split_port(port): _raise_invalid_port(port) parts = match.groupdict() - host = parts['host'] - proto = parts['proto'] or '' - internal = port_range(parts['int'], parts['int_end'], proto) - external = port_range( - parts['ext'], parts['ext_end'], '', len(internal) == 1) + host = parts["host"] + proto = parts["proto"] or "" + internal = port_range(parts["int"], parts["int_end"], proto) + external = port_range(parts["ext"], parts["ext_end"], "", len(internal) == 1) if host is None: if external is not None and len(internal) != len(external): - raise ValueError('Port ranges don\'t match in length') + raise ValueError("Port ranges don't match in length") return internal, external else: if not external: external = [None] * len(internal) elif len(internal) != len(external): - raise ValueError('Port ranges don\'t match in length') + raise ValueError("Port ranges don't match in length") return internal, [(host, ext_port) for ext_port in external] diff --git a/plugins/module_utils/_api/utils/proxy.py b/plugins/module_utils/_api/utils/proxy.py index 9389eae9..01b0454e 100644 --- a/plugins/module_utils/_api/utils/proxy.py +++ b/plugins/module_utils/_api/utils/proxy.py @@ -13,62 +13,63 @@ from .utils import format_environment class ProxyConfig(dict): - ''' + """ Hold the client's proxy configuration - ''' + """ + @property def http(self): - return self.get('http') + return self.get("http") @property def https(self): - return self.get('https') + return self.get("https") @property def ftp(self): - return self.get('ftp') + return self.get("ftp") @property def no_proxy(self): - return self.get('no_proxy') + return self.get("no_proxy") @staticmethod def from_dict(config): - ''' + """ Instantiate a new ProxyConfig from a dictionary that represents a client configuration, as described in `the documentation`_. .. _the documentation: https://docs.docker.com/network/proxy/#configure-the-docker-client - ''' + """ return ProxyConfig( - http=config.get('httpProxy'), - https=config.get('httpsProxy'), - ftp=config.get('ftpProxy'), - no_proxy=config.get('noProxy'), + http=config.get("httpProxy"), + https=config.get("httpsProxy"), + ftp=config.get("ftpProxy"), + no_proxy=config.get("noProxy"), ) def get_environment(self): - ''' + """ Return a dictionary representing the environment variables used to set the proxy settings. - ''' + """ env = {} if self.http: - env['http_proxy'] = env['HTTP_PROXY'] = self.http + env["http_proxy"] = env["HTTP_PROXY"] = self.http if self.https: - env['https_proxy'] = env['HTTPS_PROXY'] = self.https + env["https_proxy"] = env["HTTPS_PROXY"] = self.https if self.ftp: - env['ftp_proxy'] = env['FTP_PROXY'] = self.ftp + env["ftp_proxy"] = env["FTP_PROXY"] = self.ftp if self.no_proxy: - env['no_proxy'] = env['NO_PROXY'] = self.no_proxy + env["no_proxy"] = env["NO_PROXY"] = self.no_proxy return env def inject_proxy_environment(self, environment): - ''' + """ Given a list of strings representing environment variables, prepend the environment variables corresponding to the proxy settings. - ''' + """ if not self: return environment @@ -80,4 +81,4 @@ class ProxyConfig(dict): return proxy_env + environment def __str__(self): - return f'ProxyConfig(http={self.http}, https={self.https}, ftp={self.ftp}, no_proxy={self.no_proxy})' + return f"ProxyConfig(http={self.http}, https={self.https}, ftp={self.ftp}, no_proxy={self.no_proxy})" diff --git a/plugins/module_utils/_api/utils/socket.py b/plugins/module_utils/_api/utils/socket.py index 44b81f2e..a373dba8 100644 --- a/plugins/module_utils/_api/utils/socket.py +++ b/plugins/module_utils/_api/utils/socket.py @@ -48,22 +48,24 @@ def read(socket, n=4096): poll.poll() try: - if hasattr(socket, 'recv'): + if hasattr(socket, "recv"): return socket.recv(n) - if isinstance(socket, getattr(pysocket, 'SocketIO')): + if isinstance(socket, getattr(pysocket, "SocketIO")): return socket.read(n) return os.read(socket.fileno(), n) except EnvironmentError as e: if e.errno not in recoverable_errors: raise except Exception as e: - is_pipe_ended = (isinstance(socket, NpipeSocket) and - len(e.args) > 0 and - e.args[0] == NPIPE_ENDED) + is_pipe_ended = ( + isinstance(socket, NpipeSocket) + and len(e.args) > 0 + and e.args[0] == NPIPE_ENDED + ) if is_pipe_ended: # npipes do not support duplex sockets, so we interpret # a PIPE_ENDED error as a close operation (0-length read). - return '' + return "" raise @@ -72,7 +74,7 @@ def read_exactly(socket, n): Reads exactly n bytes from socket Raises SocketError if there is not enough data """ - data = b'' + data = b"" while len(data) < n: next_data = read(socket, n - len(data)) if not next_data: @@ -93,7 +95,7 @@ def next_frame_header(socket): except SocketError: return (-1, -1) - stream, actual = struct.unpack('>BxxxL', data) + stream, actual = struct.unpack(">BxxxL", data) return (stream, actual) @@ -160,7 +162,7 @@ def consume_socket_output(frames, demux=False): if demux is False: # If the streams are multiplexed, the generator returns strings, that # we just need to concatenate. - return b''.join(frames) + return b"".join(frames) # If the streams are demultiplexed, the generator yields tuples # (stdout, stderr) @@ -169,7 +171,7 @@ def consume_socket_output(frames, demux=False): # It is guaranteed that for each frame, one and only one stream # is not None. if frame == (None, None): - raise AssertionError(f'frame must be (None, None), but got {frame}') + raise AssertionError(f"frame must be (None, None), but got {frame}") if frame[0] is not None: if out[0] is None: out[0] = frame[0] @@ -193,4 +195,4 @@ def demux_adaptor(stream_id, data): elif stream_id == STDERR: return (None, data) else: - raise ValueError(f'{stream_id} is not a valid stream') + raise ValueError(f"{stream_id} is not a valid stream") diff --git a/plugins/module_utils/_api/utils/utils.py b/plugins/module_utils/_api/utils/utils.py index 8bcf8546..386e3f81 100644 --- a/plugins/module_utils/_api/utils/utils.py +++ b/plugins/module_utils/_api/utils/utils.py @@ -16,40 +16,44 @@ import os import os.path import shlex import string -from ansible_collections.community.docker.plugins.module_utils.version import StrictVersion +from urllib.parse import urlparse, urlunparse + +from ansible_collections.community.docker.plugins.module_utils.version import ( + StrictVersion, +) from .. import errors -from ..constants import DEFAULT_HTTP_HOST -from ..constants import DEFAULT_UNIX_SOCKET -from ..constants import DEFAULT_NPIPE -from ..constants import BYTE_UNITS +from ..constants import ( + BYTE_UNITS, + DEFAULT_HTTP_HOST, + DEFAULT_NPIPE, + DEFAULT_UNIX_SOCKET, +) from ..tls import TLSConfig -from urllib.parse import urlparse, urlunparse - URLComponents = collections.namedtuple( - 'URLComponents', - 'scheme netloc url params query fragment', + "URLComponents", + "scheme netloc url params query fragment", ) def create_ipam_pool(*args, **kwargs): raise errors.DeprecatedMethod( - 'utils.create_ipam_pool has been removed. Please use a ' - 'docker.types.IPAMPool object instead.' + "utils.create_ipam_pool has been removed. Please use a " + "docker.types.IPAMPool object instead." ) def create_ipam_config(*args, **kwargs): raise errors.DeprecatedMethod( - 'utils.create_ipam_config has been removed. Please use a ' - 'docker.types.IPAMConfig object instead.' + "utils.create_ipam_config has been removed. Please use a " + "docker.types.IPAMConfig object instead." ) def decode_json_header(header): - data = base64.b64decode(header).decode('utf-8') + data = base64.b64decode(header).decode("utf-8") return json.loads(data) @@ -84,29 +88,29 @@ def version_gte(v1, v2): def _convert_port_binding(binding): - result = {'HostIp': '', 'HostPort': ''} + result = {"HostIp": "", "HostPort": ""} if isinstance(binding, tuple): if len(binding) == 2: - result['HostPort'] = binding[1] - result['HostIp'] = binding[0] + result["HostPort"] = binding[1] + result["HostIp"] = binding[0] elif isinstance(binding[0], str): - result['HostIp'] = binding[0] + result["HostIp"] = binding[0] else: - result['HostPort'] = binding[0] + result["HostPort"] = binding[0] elif isinstance(binding, dict): - if 'HostPort' in binding: - result['HostPort'] = binding['HostPort'] - if 'HostIp' in binding: - result['HostIp'] = binding['HostIp'] + if "HostPort" in binding: + result["HostPort"] = binding["HostPort"] + if "HostIp" in binding: + result["HostIp"] = binding["HostIp"] else: raise ValueError(binding) else: - result['HostPort'] = binding + result["HostPort"] = binding - if result['HostPort'] is None: - result['HostPort'] = '' + if result["HostPort"] is None: + result["HostPort"] = "" else: - result['HostPort'] = str(result['HostPort']) + result["HostPort"] = str(result["HostPort"]) return result @@ -115,8 +119,8 @@ def convert_port_bindings(port_bindings): result = {} for k, v in port_bindings.items(): key = str(k) - if '/' not in key: - key += '/tcp' + if "/" not in key: + key += "/tcp" if isinstance(v, list): result[key] = [_convert_port_binding(binding) for binding in v] else: @@ -131,46 +135,44 @@ def convert_volume_binds(binds): result = [] for k, v in binds.items(): if isinstance(k, bytes): - k = k.decode('utf-8') + k = k.decode("utf-8") if isinstance(v, dict): - if 'ro' in v and 'mode' in v: - raise ValueError( - f'Binding cannot contain both "ro" and "mode": {v!r}' - ) + if "ro" in v and "mode" in v: + raise ValueError(f'Binding cannot contain both "ro" and "mode": {v!r}') - bind = v['bind'] + bind = v["bind"] if isinstance(bind, bytes): - bind = bind.decode('utf-8') + bind = bind.decode("utf-8") - if 'ro' in v: - mode = 'ro' if v['ro'] else 'rw' - elif 'mode' in v: - mode = v['mode'] + if "ro" in v: + mode = "ro" if v["ro"] else "rw" + elif "mode" in v: + mode = v["mode"] else: - mode = 'rw' + mode = "rw" # NOTE: this is only relevant for Linux hosts # (does not apply in Docker Desktop) propagation_modes = [ - 'rshared', - 'shared', - 'rslave', - 'slave', - 'rprivate', - 'private', + "rshared", + "shared", + "rslave", + "slave", + "rprivate", + "private", ] - if 'propagation' in v and v['propagation'] in propagation_modes: + if "propagation" in v and v["propagation"] in propagation_modes: if mode: - mode = ','.join([mode, v['propagation']]) + mode = ",".join([mode, v["propagation"]]) else: - mode = v['propagation'] + mode = v["propagation"] - result.append(f'{k}:{bind}:{mode}') + result.append(f"{k}:{bind}:{mode}") else: if isinstance(v, bytes): - v = v.decode('utf-8') - result.append(f'{k}:{v}:rw') + v = v.decode("utf-8") + result.append(f"{k}:{v}:rw") return result @@ -180,7 +182,7 @@ def convert_tmpfs_mounts(tmpfs): if not isinstance(tmpfs, list): raise ValueError( - f'Expected tmpfs value to be either a list or a dict, found: {type(tmpfs).__name__}' + f"Expected tmpfs value to be either a list or a dict, found: {type(tmpfs).__name__}" ) result = {} @@ -205,22 +207,22 @@ def convert_service_networks(networks): if not networks: return networks if not isinstance(networks, list): - raise TypeError('networks parameter must be a list.') + raise TypeError("networks parameter must be a list.") result = [] for n in networks: if isinstance(n, str): - n = {'Target': n} + n = {"Target": n} result.append(n) return result 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 @@ -229,86 +231,81 @@ def parse_host(addr, is_win32=False, tls=False): # Sensible defaults if not addr and is_win32: return DEFAULT_NPIPE - if not addr or addr.strip() == 'unix://': + if not addr or addr.strip() == "unix://": return DEFAULT_UNIX_SOCKET addr = addr.strip() parsed_url = urlparse(addr) proto = parsed_url.scheme - if not proto or any(x not in string.ascii_letters + '+' for x in proto): + if not proto or any(x not in string.ascii_letters + "+" for x in proto): # https://bugs.python.org/issue754016 - parsed_url = urlparse('//' + addr, 'tcp') - proto = 'tcp' + parsed_url = urlparse("//" + addr, "tcp") + proto = "tcp" - if proto == 'fd': - raise errors.DockerException('fd protocol is not implemented') + if proto == "fd": + raise errors.DockerException("fd protocol is not implemented") # These protos are valid aliases for our library but not for the # official spec - if proto == 'http' or proto == 'https': - tls = proto == 'https' - proto = 'tcp' - elif proto == 'http+unix': - proto = 'unix' + if proto == "http" or proto == "https": + tls = proto == "https" + proto = "tcp" + elif proto == "http+unix": + proto = "unix" - if proto not in ('tcp', 'unix', 'npipe', 'ssh'): - raise errors.DockerException( - f"Invalid bind address protocol: {addr}" - ) + if proto not in ("tcp", "unix", "npipe", "ssh"): + raise errors.DockerException(f"Invalid bind address protocol: {addr}") - if proto == 'tcp' and not parsed_url.netloc: + if proto == "tcp" and not parsed_url.netloc: # "tcp://" is exceptionally disallowed by convention; # omitting a hostname for other protocols is fine - raise errors.DockerException( - f'Invalid bind address format: {addr}' - ) + raise errors.DockerException(f"Invalid bind address format: {addr}") - if any([ - parsed_url.params, parsed_url.query, parsed_url.fragment, - parsed_url.password - ]): - raise errors.DockerException( - f'Invalid bind address format: {addr}' - ) + if any( + [parsed_url.params, parsed_url.query, parsed_url.fragment, parsed_url.password] + ): + raise errors.DockerException(f"Invalid bind address format: {addr}") - if parsed_url.path and proto == 'ssh': + if parsed_url.path and proto == "ssh": raise errors.DockerException( - f'Invalid bind address format: no path allowed for this protocol: {addr}' + f"Invalid bind address format: no path allowed for this protocol: {addr}" ) else: path = parsed_url.path - if proto == 'unix' and parsed_url.hostname is not None: + if proto == "unix" and parsed_url.hostname is not None: # For legacy reasons, we consider unix://path # to be valid and equivalent to unix:///path - path = '/'.join((parsed_url.hostname, path)) + path = "/".join((parsed_url.hostname, path)) netloc = parsed_url.netloc - if proto in ('tcp', 'ssh'): + if proto in ("tcp", "ssh"): port = parsed_url.port or 0 if port <= 0: - port = 22 if proto == 'ssh' else (2375 if tls else 2376) - netloc = f'{parsed_url.netloc}:{port}' + port = 22 if proto == "ssh" else (2375 if tls else 2376) + netloc = f"{parsed_url.netloc}:{port}" if not parsed_url.hostname: - netloc = f'{DEFAULT_HTTP_HOST}:{port}' + netloc = f"{DEFAULT_HTTP_HOST}:{port}" # Rewrite schemes to fit library internals (requests adapters) - if proto == 'tcp': + if proto == "tcp": proto = f"http{'s' if tls else ''}" - elif proto == 'unix': - proto = 'http+unix' + elif proto == "unix": + proto = "http+unix" - if proto in ('http+unix', 'npipe'): - return f"{proto}://{path}".rstrip('/') - return urlunparse(URLComponents( - scheme=proto, - netloc=netloc, - url=path, - params='', - query='', - fragment='', - )).rstrip('/') + if proto in ("http+unix", "npipe"): + return f"{proto}://{path}".rstrip("/") + return urlunparse( + URLComponents( + scheme=proto, + netloc=netloc, + url=path, + params="", + query="", + fragment="", + ) + ).rstrip("/") def parse_devices(devices): @@ -318,10 +315,8 @@ def parse_devices(devices): device_list.append(device) continue if not isinstance(device, str): - raise errors.DockerException( - f'Invalid device type {type(device)}' - ) - device_mapping = device.split(':') + raise errors.DockerException(f"Invalid device type {type(device)}") + device_mapping = device.split(":") if device_mapping: path_on_host = device_mapping[0] if len(device_mapping) > 1: @@ -331,27 +326,29 @@ def parse_devices(devices): if len(device_mapping) > 2: permissions = device_mapping[2] else: - permissions = 'rwm' - device_list.append({ - 'PathOnHost': path_on_host, - 'PathInContainer': path_in_container, - 'CgroupPermissions': permissions - }) + permissions = "rwm" + device_list.append( + { + "PathOnHost": path_on_host, + "PathInContainer": path_in_container, + "CgroupPermissions": permissions, + } + ) return device_list def kwargs_from_env(ssl_version=None, assert_hostname=None, environment=None): if not environment: environment = os.environ - host = environment.get('DOCKER_HOST') + host = environment.get("DOCKER_HOST") # empty string for cert path is the same as unset. - cert_path = environment.get('DOCKER_CERT_PATH') or None + cert_path = environment.get("DOCKER_CERT_PATH") or None # empty string for tls verify counts as "false". # Any value or 'unset' counts as true. - tls_verify = environment.get('DOCKER_TLS_VERIFY') - if tls_verify == '': + tls_verify = environment.get("DOCKER_TLS_VERIFY") + if tls_verify == "": tls_verify = False else: tls_verify = tls_verify is not None @@ -360,23 +357,25 @@ def kwargs_from_env(ssl_version=None, assert_hostname=None, environment=None): params = {} if host: - params['base_url'] = host + params["base_url"] = host if not enable_tls: return params if not cert_path: - cert_path = os.path.join(os.path.expanduser('~'), '.docker') + cert_path = os.path.join(os.path.expanduser("~"), ".docker") if not tls_verify and assert_hostname is None: # assert_hostname is a subset of TLS verification, # so if it is not set already then set it to false. assert_hostname = False - params['tls'] = TLSConfig( - client_cert=(os.path.join(cert_path, 'cert.pem'), - os.path.join(cert_path, 'key.pem')), - ca_cert=os.path.join(cert_path, 'ca.pem'), + params["tls"] = TLSConfig( + client_cert=( + os.path.join(cert_path, "cert.pem"), + os.path.join(cert_path, "key.pem"), + ), + ca_cert=os.path.join(cert_path, "ca.pem"), verify=tls_verify, ssl_version=ssl_version, assert_hostname=assert_hostname, @@ -389,13 +388,12 @@ def convert_filters(filters): result = {} for k, v in filters.items(): if isinstance(v, bool): - v = 'true' if v else 'false' + v = "true" if v else "false" if not isinstance(v, list): - v = [v, ] - result[k] = [ - str(item) if not isinstance(item, str) else item - for item in v - ] + v = [ + v, + ] + result[k] = [str(item) if not isinstance(item, str) else item for item in v] return json.dumps(result) @@ -415,7 +413,7 @@ def parse_bytes(s): # without a units part. Assuming that the units are bytes. if suffix.isdigit(): digits_part = s - suffix = 'b' + suffix = "b" else: digits_part = s[:-1] @@ -424,14 +422,14 @@ def parse_bytes(s): digits = float(digits_part) except ValueError: raise errors.DockerException( - f'Failed converting the string value for memory ({digits_part}) to an integer.' + f"Failed converting the string value for memory ({digits_part}) to an integer." ) # Reconvert to long for the final result s = int(digits * units[suffix]) else: raise errors.DockerException( - f'The specified value for memory ({s}) should specify the units. The postfix should be one of the `b` `k` `m` `g` characters' + f"The specified value for memory ({s}) should specify the units. The postfix should be one of the `b` `k` `m` `g` characters" ) return s @@ -441,7 +439,7 @@ def normalize_links(links): if isinstance(links, dict): links = links.items() - return [f'{k}:{v}' if v else k for k, v in sorted(links)] + return [f"{k}:{v}" if v else k for k, v in sorted(links)] def parse_env_file(env_file): @@ -451,22 +449,24 @@ def parse_env_file(env_file): """ environment = {} - with open(env_file, 'r') as f: + with open(env_file, "r") as f: for line in f: - if line[0] == '#': + if line[0] == "#": continue line = line.strip() if not line: continue - parse_line = line.split('=', 1) + parse_line = line.split("=", 1) if len(parse_line) == 2: k, v = parse_line environment[k] = v else: - raise errors.DockerException(f'Invalid line in environment file {env_file}:\n{line}') + raise errors.DockerException( + f"Invalid line in environment file {env_file}:\n{line}" + ) return environment @@ -480,26 +480,23 @@ def format_environment(environment): if value is None: return key if isinstance(value, bytes): - value = value.decode('utf-8') + value = value.decode("utf-8") + + return f"{key}={value}" - return f'{key}={value}' return [format_env(*var) for var in environment.items()] def format_extra_hosts(extra_hosts, task=False): # Use format dictated by Swarm API if container is part of a task if task: - return [ - f'{v} {k}' for k, v in sorted(extra_hosts.items()) - ] + return [f"{v} {k}" for k, v in sorted(extra_hosts.items())] - return [ - f'{k}:{v}' for k, v in sorted(extra_hosts.items()) - ] + return [f"{k}:{v}" for k, v in sorted(extra_hosts.items())] def create_host_config(self, *args, **kwargs): raise errors.DeprecatedMethod( - 'utils.create_host_config has been removed. Please use a ' - 'docker.types.HostConfig object instead.' + "utils.create_host_config has been removed. Please use a " + "docker.types.HostConfig object instead." ) diff --git a/plugins/module_utils/_logfmt.py b/plugins/module_utils/_logfmt.py index 1c306742..ce3c0689 100644 --- a/plugins/module_utils/_logfmt.py +++ b/plugins/module_utils/_logfmt.py @@ -29,44 +29,44 @@ class _Mode(object): _ESCAPE_DICT = { '"': '"', - '\\': '\\', + "\\": "\\", "'": "'", - '/': '/', - 'b': '\b', - 'f': '\f', - 'n': '\n', - 'r': '\r', - 't': '\t', + "/": "/", + "b": "\b", + "f": "\f", + "n": "\n", + "r": "\r", + "t": "\t", } _HEX_DICT = { - '0': 0, - '1': 1, - '2': 2, - '3': 3, - '4': 4, - '5': 5, - '6': 6, - '7': 7, - '8': 8, - '9': 9, - 'a': 0xA, - 'b': 0xB, - 'c': 0xC, - 'd': 0xD, - 'e': 0xE, - 'f': 0xF, - 'A': 0xA, - 'B': 0xB, - 'C': 0xC, - 'D': 0xD, - 'E': 0xE, - 'F': 0xF, + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + "a": 0xA, + "b": 0xB, + "c": 0xC, + "d": 0xD, + "e": 0xE, + "f": 0xF, + "A": 0xA, + "B": 0xB, + "C": 0xC, + "D": 0xD, + "E": 0xE, + "F": 0xF, } def _is_ident(cur): - return cur > ' ' and cur not in ('"', '=') + return cur > " " and cur not in ('"', "=") class _Parser(object): @@ -89,16 +89,18 @@ class _Parser(object): def parse_unicode_sequence(self): if self.index + 6 > self.length: - raise InvalidLogFmt('Not enough space for unicode escape') - if self.line[self.index:self.index + 2] != '\\u': - raise InvalidLogFmt('Invalid unicode escape start') + raise InvalidLogFmt("Not enough space for unicode escape") + if self.line[self.index : self.index + 2] != "\\u": + raise InvalidLogFmt("Invalid unicode escape start") v = 0 for i in range(self.index + 2, self.index + 6): v <<= 4 try: v += _HEX_DICT[self.line[self.index]] except KeyError: - raise InvalidLogFmt(f'Invalid unicode escape digit {self.line[self.index]!r}') + raise InvalidLogFmt( + f"Invalid unicode escape digit {self.line[self.index]!r}" + ) self.index += 6 return chr(v) @@ -111,8 +113,8 @@ def parse_line(line, logrus_mode=False): mode = _Mode.GARBAGE def handle_kv(has_no_value=False): - k = ''.join(key) - v = None if has_no_value else ''.join(value) + k = "".join(key) + v = None if has_no_value else "".join(value) result[k] = v del key[:] del value[:] @@ -128,7 +130,7 @@ def parse_line(line, logrus_mode=False): key.append(cur) parser.next() return _Mode.KEY - elif cur == '=': + elif cur == "=": parser.next() return _Mode.EQUAL else: @@ -162,16 +164,16 @@ def parse_line(line, logrus_mode=False): return _Mode.GARBAGE def parse_quoted_value(cur): - if cur == '\\': + if cur == "\\": parser.next() if parser.done(): - raise InvalidLogFmt('Unterminated escape sequence in quoted string') + raise InvalidLogFmt("Unterminated escape sequence in quoted string") cur = parser.cur() if cur in _ESCAPE_DICT: value.append(_ESCAPE_DICT[cur]) - elif cur != 'u': + elif cur != "u": es = f"\\{cur}" - raise InvalidLogFmt(f'Unknown escape sequence {es!r}') + raise InvalidLogFmt(f"Unknown escape sequence {es!r}") else: parser.prev() value.append(parser.parse_unicode_sequence()) @@ -181,8 +183,8 @@ def parse_line(line, logrus_mode=False): handle_kv() parser.next() return _Mode.GARBAGE - elif cur < ' ': - raise InvalidLogFmt('Control characters in quoted string are not allowed') + elif cur < " ": + raise InvalidLogFmt("Control characters in quoted string are not allowed") else: value.append(cur) parser.next() @@ -204,5 +206,5 @@ def parse_line(line, logrus_mode=False): elif mode == _Mode.IDENT_VALUE: handle_kv() elif mode == _Mode.QUOTED_VALUE: - raise InvalidLogFmt('Unterminated quoted string') + raise InvalidLogFmt("Unterminated quoted string") return result diff --git a/plugins/module_utils/_platform.py b/plugins/module_utils/_platform.py index 5d993ff2..d1990d70 100644 --- a/plugins/module_utils/_platform.py +++ b/plugins/module_utils/_platform.py @@ -13,36 +13,74 @@ from __future__ import annotations import re -_VALID_STR = re.compile('^[A-Za-z0-9_-]+$') +_VALID_STR = re.compile("^[A-Za-z0-9_-]+$") def _validate_part(string, part, part_name): if not part: raise ValueError(f'Invalid platform string "{string}": {part_name} is empty') if not _VALID_STR.match(part): - raise ValueError(f'Invalid platform string "{string}": {part_name} has invalid characters') + raise ValueError( + f'Invalid platform string "{string}": {part_name} has invalid characters' + ) return part # See https://github.com/containerd/containerd/blob/main/platforms/database.go#L32-L38 _KNOWN_OS = ( - "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", - "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos", + "aix", + "android", + "darwin", + "dragonfly", + "freebsd", + "hurd", + "illumos", + "ios", + "js", + "linux", + "nacl", + "netbsd", + "openbsd", + "plan9", + "solaris", + "windows", + "zos", ) # See https://github.com/containerd/containerd/blob/main/platforms/database.go#L54-L60 _KNOWN_ARCH = ( - "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", - "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", - "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm", + "386", + "amd64", + "amd64p32", + "arm", + "armbe", + "arm64", + "arm64be", + "ppc64", + "ppc64le", + "loong64", + "mips", + "mipsle", + "mips64", + "mips64le", + "mips64p32", + "mips64p32le", + "ppc", + "riscv", + "riscv64", + "s390", + "s390x", + "sparc", + "sparc64", + "wasm", ) def _normalize_os(os_str): # See normalizeOS() in https://github.com/containerd/containerd/blob/main/platforms/database.go os_str = os_str.lower() - if os_str == 'macos': - os_str = 'darwin' + if os_str == "macos": + os_str = "darwin" return os_str @@ -94,9 +132,9 @@ class _Platform(object): self.variant = variant if variant is not None: if arch is None: - raise ValueError('If variant is given, architecture must be given too') + raise ValueError("If variant is given, architecture must be given too") if os is None: - raise ValueError('If variant is given, os must be given too') + raise ValueError("If variant is given, os must be given too") @classmethod def parse_platform_string(cls, string, daemon_os=None, daemon_arch=None): @@ -104,40 +142,48 @@ class _Platform(object): if string is None: return cls() if not string: - raise ValueError('Platform string must be non-empty') - parts = string.split('/', 2) + raise ValueError("Platform string must be non-empty") + parts = string.split("/", 2) arch = None variant = None if len(parts) == 1: - _validate_part(string, string, 'OS/architecture') + _validate_part(string, string, "OS/architecture") # The part is either OS or architecture os = _normalize_os(string) if os in _KNOWN_OS: if daemon_arch is not None: - arch, variant = _normalize_arch(daemon_arch, '') + arch, variant = _normalize_arch(daemon_arch, "") return cls(os=os, arch=arch, variant=variant) - arch, variant = _normalize_arch(os, '') + arch, variant = _normalize_arch(os, "") if arch in _KNOWN_ARCH: return cls( os=_normalize_os(daemon_os) if daemon_os else None, arch=arch or None, variant=variant or None, ) - raise ValueError(f'Invalid platform string "{string}": unknown OS or architecture') - os = _validate_part(string, parts[0], 'OS') + raise ValueError( + f'Invalid platform string "{string}": unknown OS or architecture' + ) + os = _validate_part(string, parts[0], "OS") if not os: raise ValueError(f'Invalid platform string "{string}": OS is empty') - arch = _validate_part(string, parts[1], 'architecture') if len(parts) > 1 else None + arch = ( + _validate_part(string, parts[1], "architecture") if len(parts) > 1 else None + ) if arch is not None and not arch: - raise ValueError(f'Invalid platform string "{string}": architecture is empty') - variant = _validate_part(string, parts[2], 'variant') if len(parts) > 2 else None + raise ValueError( + f'Invalid platform string "{string}": architecture is empty' + ) + variant = ( + _validate_part(string, parts[2], "variant") if len(parts) > 2 else None + ) if variant is not None and not variant: raise ValueError(f'Invalid platform string "{string}": variant is empty') - arch, variant = _normalize_arch(arch, variant or '') - if len(parts) == 2 and arch == 'arm' and variant == 'v7': + arch, variant = _normalize_arch(arch, variant or "") + if len(parts) == 2 and arch == "arm" and variant == "v7": variant = None - if len(parts) == 3 and arch == 'arm64' and variant == '': - variant = 'v8' + if len(parts) == 3 and arch == "arm64" and variant == "": + variant = "v8" return cls(os=_normalize_os(os), arch=arch, variant=variant or None) def __str__(self): @@ -152,27 +198,41 @@ class _Platform(object): parts = [self.arch] else: parts = [] - return '/'.join(parts) + return "/".join(parts) def __repr__(self): - return f'_Platform(os={self.os!r}, arch={self.arch!r}, variant={self.variant!r})' + return ( + f"_Platform(os={self.os!r}, arch={self.arch!r}, variant={self.variant!r})" + ) def __eq__(self, other): - return self.os == other.os and self.arch == other.arch and self.variant == other.variant + return ( + self.os == other.os + and self.arch == other.arch + and self.variant == other.variant + ) def normalize_platform_string(string, daemon_os=None, daemon_arch=None): - return str(_Platform.parse_platform_string(string, daemon_os=daemon_os, daemon_arch=daemon_arch)) + return str( + _Platform.parse_platform_string( + string, daemon_os=daemon_os, daemon_arch=daemon_arch + ) + ) -def compose_platform_string(os=None, arch=None, variant=None, daemon_os=None, daemon_arch=None): +def compose_platform_string( + os=None, arch=None, variant=None, daemon_os=None, daemon_arch=None +): if os is None and daemon_os is not None: os = _normalize_os(daemon_os) if arch is None and daemon_arch is not None: - arch, variant = _normalize_arch(daemon_arch, variant or '') + arch, variant = _normalize_arch(daemon_arch, variant or "") variant = variant or None return str(_Platform(os=os, arch=arch, variant=variant or None)) def compare_platform_strings(string1, string2): - return _Platform.parse_platform_string(string1) == _Platform.parse_platform_string(string2) + return _Platform.parse_platform_string(string1) == _Platform.parse_platform_string( + string2 + ) diff --git a/plugins/module_utils/_scramble.py b/plugins/module_utils/_scramble.py index 17cfe28a..f90cea63 100644 --- a/plugins/module_utils/_scramble.py +++ b/plugins/module_utils/_scramble.py @@ -11,32 +11,32 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_ def generate_insecure_key(): - '''Do NOT use this for cryptographic purposes!''' + """Do NOT use this for cryptographic purposes!""" while True: # Generate a one-byte key. Right now the functions below do not use more # than one byte, so this is sufficient. key = bytes([random.randint(0, 255)]) # Return anything that is not zero - if key != b'\x00': + if key != b"\x00": return key def scramble(value, key): - '''Do NOT use this for cryptographic purposes!''' + """Do NOT use this for cryptographic purposes!""" if len(key) < 1: - raise ValueError('Key must be at least one byte') + raise ValueError("Key must be at least one byte") value = to_bytes(value) k = key[0] value = bytes([k ^ b for b in value]) - return '=S=' + to_native(base64.b64encode(value)) + return "=S=" + to_native(base64.b64encode(value)) def unscramble(value, key): - '''Do NOT use this for cryptographic purposes!''' + """Do NOT use this for cryptographic purposes!""" if len(key) < 1: - raise ValueError('Key must be at least one byte') - if not value.startswith('=S='): - raise ValueError('Value does not start with indicator') + raise ValueError("Key must be at least one byte") + if not value.startswith("=S="): + raise ValueError("Value does not start with indicator") value = base64.b64decode(value[3:]) k = key[0] value = bytes([k ^ b for b in value]) diff --git a/plugins/module_utils/common.py b/plugins/module_utils/common.py index 2954d0d2..1c658e70 100644 --- a/plugins/module_utils/common.py +++ b/plugins/module_utils/common.py @@ -4,7 +4,6 @@ from __future__ import annotations - import abc import os import platform @@ -14,9 +13,11 @@ import traceback from collections.abc import Mapping, Sequence from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE, BOOLEANS_FALSE +from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE, BOOLEANS_TRUE +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion HAS_DOCKER_PY = True HAS_DOCKER_PY_2 = False @@ -26,15 +27,16 @@ HAS_DOCKER_TRACEBACK = None try: from requests.exceptions import SSLError + from docker import __version__ as docker_version + from docker import auth from docker.errors import APIError, NotFound, TLSParameterError from docker.tls import TLSConfig - from docker import auth - if LooseVersion(docker_version) >= LooseVersion('3.0.0'): + if LooseVersion(docker_version) >= LooseVersion("3.0.0"): HAS_DOCKER_PY_3 = True from docker import APIClient as Client - elif LooseVersion(docker_version) >= LooseVersion('2.0.0'): + elif LooseVersion(docker_version) >= LooseVersion("2.0.0"): HAS_DOCKER_PY_2 = True from docker import APIClient as Client else: @@ -52,6 +54,7 @@ except ImportError as exc: try: # docker (Docker SDK for Python >= 2.0.0) import docker.models # noqa: F401, pylint: disable=unused-import + HAS_DOCKER_MODELS = True except ImportError: HAS_DOCKER_MODELS = False @@ -59,13 +62,16 @@ except ImportError: try: # docker-py (Docker SDK for Python < 2.0.0) import docker.ssladapter # noqa: F401, pylint: disable=unused-import + HAS_DOCKER_SSLADAPTER = True except ImportError: HAS_DOCKER_SSLADAPTER = False try: - from requests.exceptions import RequestException # noqa: F401, pylint: disable=unused-import + from requests.exceptions import ( # noqa: F401, pylint: disable=unused-import + RequestException, + ) except ImportError: # Either Docker SDK for Python is no longer using requests, or Docker SDK for Python is not around either, # or Docker SDK for Python's dependency requests is missing. In any case, define an exception @@ -73,30 +79,59 @@ except ImportError: class RequestException(Exception): pass + +from ansible_collections.community.docker.plugins.module_utils.util import ( + BYTE_SUFFIXES, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DEFAULT_DOCKER_REGISTRY, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DEFAULT_TLS_HOSTNAME, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DOCKER_COMMON_ARGS_VARS, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DifferenceTracker, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DockerBaseClass, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + clean_dict_booleans_for_docker_api, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + compare_dict_allow_more_present, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + compare_generic, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + convert_duration_to_nanosecond, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + is_image_name_id, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + is_valid_tag, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + omit_none_from_dict, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + parse_healthcheck, # TODO: remove +) from ansible_collections.community.docker.plugins.module_utils.util import ( # noqa: F401, pylint: disable=unused-import DEFAULT_DOCKER_HOST, + DEFAULT_TIMEOUT_SECONDS, DEFAULT_TLS, DEFAULT_TLS_VERIFY, - DEFAULT_TLS_HOSTNAME, # TODO: remove - DEFAULT_TIMEOUT_SECONDS, DOCKER_COMMON_ARGS, - DOCKER_COMMON_ARGS_VARS, # TODO: remove DOCKER_MUTUALLY_EXCLUSIVE, DOCKER_REQUIRED_TOGETHER, - DEFAULT_DOCKER_REGISTRY, # TODO: remove - BYTE_SUFFIXES, # TODO: remove - is_image_name_id, # TODO: remove - is_valid_tag, # TODO: remove sanitize_result, - DockerBaseClass, # TODO: remove update_tls_hostname, - compare_dict_allow_more_present, # TODO: remove - compare_generic, # TODO: remove - DifferenceTracker, # TODO: remove - clean_dict_booleans_for_docker_api, # TODO: remove - convert_duration_to_nanosecond, # TODO: remove - parse_healthcheck, # TODO: remove - omit_none_from_dict, # TODO: remove ) @@ -120,8 +155,10 @@ if not HAS_DOCKER_PY: def _get_tls_config(fail_function, **kwargs): - if 'assert_hostname' in kwargs and LooseVersion(docker_version) >= LooseVersion('7.0.0b1'): - assert_hostname = kwargs.pop('assert_hostname') + if "assert_hostname" in kwargs and LooseVersion(docker_version) >= LooseVersion( + "7.0.0b1" + ): + assert_hostname = kwargs.pop("assert_hostname") if assert_hostname is not None: fail_function( "tls_hostname is not compatible with Docker SDK for Python 7.0.0+. You are using" @@ -139,51 +176,55 @@ def _get_tls_config(fail_function, **kwargs): def is_using_tls(auth): - return auth['tls_verify'] or auth['tls'] + return auth["tls_verify"] or auth["tls"] def get_connect_params(auth, fail_function): if is_using_tls(auth): - auth['docker_host'] = auth['docker_host'].replace('tcp://', 'https://') + auth["docker_host"] = auth["docker_host"].replace("tcp://", "https://") result = dict( - base_url=auth['docker_host'], - version=auth['api_version'], - timeout=auth['timeout'], + base_url=auth["docker_host"], + version=auth["api_version"], + timeout=auth["timeout"], ) - if auth['tls_verify']: + if auth["tls_verify"]: # TLS with verification tls_config = dict( verify=True, - assert_hostname=auth['tls_hostname'], + assert_hostname=auth["tls_hostname"], fail_function=fail_function, ) - if auth['cert_path'] and auth['key_path']: - tls_config['client_cert'] = (auth['cert_path'], auth['key_path']) - if auth['cacert_path']: - tls_config['ca_cert'] = auth['cacert_path'] - result['tls'] = _get_tls_config(**tls_config) - elif auth['tls']: + if auth["cert_path"] and auth["key_path"]: + tls_config["client_cert"] = (auth["cert_path"], auth["key_path"]) + if auth["cacert_path"]: + tls_config["ca_cert"] = auth["cacert_path"] + result["tls"] = _get_tls_config(**tls_config) + elif auth["tls"]: # TLS without verification tls_config = dict( verify=False, fail_function=fail_function, ) - if auth['cert_path'] and auth['key_path']: - tls_config['client_cert'] = (auth['cert_path'], auth['key_path']) - result['tls'] = _get_tls_config(**tls_config) + if auth["cert_path"] and auth["key_path"]: + tls_config["client_cert"] = (auth["cert_path"], auth["key_path"]) + result["tls"] = _get_tls_config(**tls_config) - if auth.get('use_ssh_client'): - if LooseVersion(docker_version) < LooseVersion('4.4.0'): - fail_function("use_ssh_client=True requires Docker SDK for Python 4.4.0 or newer") - result['use_ssh_client'] = True + if auth.get("use_ssh_client"): + if LooseVersion(docker_version) < LooseVersion("4.4.0"): + fail_function( + "use_ssh_client=True requires Docker SDK for Python 4.4.0 or newer" + ) + result["use_ssh_client"] = True # No TLS return result -DOCKERPYUPGRADE_SWITCH_TO_DOCKER = "Try `pip uninstall docker-py` followed by `pip install docker`." +DOCKERPYUPGRADE_SWITCH_TO_DOCKER = ( + "Try `pip uninstall docker-py` followed by `pip install docker`." +) DOCKERPYUPGRADE_UPGRADE_DOCKER = "Use `pip install --upgrade docker` to upgrade." DOCKERPYUPGRADE_RECOMMEND_DOCKER = "Use `pip install --upgrade docker-py` to upgrade." @@ -192,17 +233,19 @@ class AnsibleDockerClientBase(Client): def __init__(self, min_docker_version=None, min_docker_api_version=None): if min_docker_version is None: min_docker_version = MIN_DOCKER_VERSION - NEEDS_DOCKER_PY2 = (LooseVersion(min_docker_version) >= LooseVersion('2.0.0')) + NEEDS_DOCKER_PY2 = LooseVersion(min_docker_version) >= LooseVersion("2.0.0") self.docker_py_version = LooseVersion(docker_version) if HAS_DOCKER_MODELS and HAS_DOCKER_SSLADAPTER: - self.fail("Cannot have both the docker-py and docker python modules (old and new version of Docker " - "SDK for Python) installed together as they use the same namespace and cause a corrupt " - "installation. Please uninstall both packages, and re-install only the docker-py or docker " - f"python module (for {platform.node()}'s Python {sys.executable}). It is recommended to install the docker module. Please " - "note that simply uninstalling one of the modules can leave the other module in a broken " - "state.") + self.fail( + "Cannot have both the docker-py and docker python modules (old and new version of Docker " + "SDK for Python) installed together as they use the same namespace and cause a corrupt " + "installation. Please uninstall both packages, and re-install only the docker-py or docker " + f"python module (for {platform.node()}'s Python {sys.executable}). It is recommended to install the docker module. Please " + "note that simply uninstalling one of the modules can leave the other module in a broken " + "state." + ) if not HAS_DOCKER_PY: msg = missing_required_lib("Docker SDK for Python: docker>=5.0.0") @@ -218,13 +261,15 @@ class AnsibleDockerClientBase(Client): # The minimal required version is < 2.0 (and the current version as well). # Advertise docker (instead of docker-py). msg += DOCKERPYUPGRADE_RECOMMEND_DOCKER - elif docker_version < LooseVersion('2.0'): + elif docker_version < LooseVersion("2.0"): msg += DOCKERPYUPGRADE_SWITCH_TO_DOCKER else: msg += DOCKERPYUPGRADE_UPGRADE_DOCKER self.fail(msg) - self._connect_params = get_connect_params(self.auth_params, fail_function=self.fail) + self._connect_params = get_connect_params( + self.auth_params, fail_function=self.fail + ) try: super(AnsibleDockerClientBase, self).__init__(**self._connect_params) @@ -235,9 +280,11 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error connecting: {exc}") self.docker_api_version = LooseVersion(self.docker_api_version_str) - min_docker_api_version = min_docker_api_version or '1.25' + min_docker_api_version = min_docker_api_version or "1.25" if self.docker_api_version < LooseVersion(min_docker_api_version): - self.fail(f'Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}.') + self.fail( + f"Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}." + ) def log(self, msg, pretty_print=False): pass @@ -253,16 +300,16 @@ class AnsibleDockerClientBase(Client): pass @staticmethod - def _get_value(param_name, param_value, env_variable, default_value, type='str'): + def _get_value(param_name, param_value, env_variable, default_value, type="str"): if param_value is not None: # take module parameter value - if type == 'bool': + if type == "bool": if param_value in BOOLEANS_TRUE: return True if param_value in BOOLEANS_FALSE: return False return bool(param_value) - if type == 'int': + if type == "int": return int(param_value) return param_value @@ -270,19 +317,19 @@ class AnsibleDockerClientBase(Client): env_value = os.environ.get(env_variable) if env_value is not None: # take the env variable value - if param_name == 'cert_path': - return os.path.join(env_value, 'cert.pem') - if param_name == 'cacert_path': - return os.path.join(env_value, 'ca.pem') - if param_name == 'key_path': - return os.path.join(env_value, 'key.pem') - if type == 'bool': + if param_name == "cert_path": + return os.path.join(env_value, "cert.pem") + if param_name == "cacert_path": + return os.path.join(env_value, "ca.pem") + if param_name == "key_path": + return os.path.join(env_value, "key.pem") + if type == "bool": if env_value in BOOLEANS_TRUE: return True if env_value in BOOLEANS_FALSE: return False return bool(env_value) - if type == 'int': + if type == "int": return int(env_value) return env_value @@ -298,7 +345,7 @@ class AnsibleDockerClientBase(Client): # Get authentication credentials. # Precedence: module parameters-> environment variables-> defaults. - self.log('Getting credentials') + self.log("Getting credentials") client_params = self._get_params() @@ -307,21 +354,56 @@ class AnsibleDockerClientBase(Client): params[key] = client_params.get(key) result = dict( - docker_host=self._get_value('docker_host', params['docker_host'], 'DOCKER_HOST', - DEFAULT_DOCKER_HOST, type='str'), - tls_hostname=self._get_value('tls_hostname', params['tls_hostname'], - 'DOCKER_TLS_HOSTNAME', None, type='str'), - api_version=self._get_value('api_version', params['api_version'], 'DOCKER_API_VERSION', - 'auto', type='str'), - cacert_path=self._get_value('cacert_path', params['ca_path'], 'DOCKER_CERT_PATH', None, type='str'), - cert_path=self._get_value('cert_path', params['client_cert'], 'DOCKER_CERT_PATH', None, type='str'), - key_path=self._get_value('key_path', params['client_key'], 'DOCKER_CERT_PATH', None, type='str'), - tls=self._get_value('tls', params['tls'], 'DOCKER_TLS', DEFAULT_TLS, type='bool'), - tls_verify=self._get_value('validate_certs', params['validate_certs'], 'DOCKER_TLS_VERIFY', - DEFAULT_TLS_VERIFY, type='bool'), - timeout=self._get_value('timeout', params['timeout'], 'DOCKER_TIMEOUT', - DEFAULT_TIMEOUT_SECONDS, type='int'), - use_ssh_client=self._get_value('use_ssh_client', params['use_ssh_client'], None, False, type='bool'), + docker_host=self._get_value( + "docker_host", + params["docker_host"], + "DOCKER_HOST", + DEFAULT_DOCKER_HOST, + type="str", + ), + tls_hostname=self._get_value( + "tls_hostname", + params["tls_hostname"], + "DOCKER_TLS_HOSTNAME", + None, + type="str", + ), + api_version=self._get_value( + "api_version", + params["api_version"], + "DOCKER_API_VERSION", + "auto", + type="str", + ), + cacert_path=self._get_value( + "cacert_path", params["ca_path"], "DOCKER_CERT_PATH", None, type="str" + ), + cert_path=self._get_value( + "cert_path", params["client_cert"], "DOCKER_CERT_PATH", None, type="str" + ), + key_path=self._get_value( + "key_path", params["client_key"], "DOCKER_CERT_PATH", None, type="str" + ), + tls=self._get_value( + "tls", params["tls"], "DOCKER_TLS", DEFAULT_TLS, type="bool" + ), + tls_verify=self._get_value( + "validate_certs", + params["validate_certs"], + "DOCKER_TLS_VERIFY", + DEFAULT_TLS_VERIFY, + type="bool", + ), + timeout=self._get_value( + "timeout", + params["timeout"], + "DOCKER_TIMEOUT", + DEFAULT_TIMEOUT_SECONDS, + type="int", + ), + use_ssh_client=self._get_value( + "use_ssh_client", params["use_ssh_client"], None, False, type="bool" + ), ) update_tls_hostname(result) @@ -331,11 +413,13 @@ class AnsibleDockerClientBase(Client): def _handle_ssl_error(self, error): match = re.match(r"hostname.*doesn\'t match (\'.*\')", str(error)) if match: - hostname = self.auth_params['tls_hostname'] - self.fail(f"You asked for verification that Docker daemons certificate's hostname matches {hostname}. " - f"The actual certificate's hostname is {match.group(1)}. Most likely you need to set DOCKER_TLS_HOSTNAME " - f"or pass `tls_hostname` with a value of {match.group(1)}. You may also use TLS without verification by " - "setting the `tls` parameter to true.") + hostname = self.auth_params["tls_hostname"] + self.fail( + f"You asked for verification that Docker daemons certificate's hostname matches {hostname}. " + f"The actual certificate's hostname is {match.group(1)}. Most likely you need to set DOCKER_TLS_HOSTNAME " + f"or pass `tls_hostname` with a value of {match.group(1)}. You may also use TLS without verification by " + "setting the `tls` parameter to true." + ) self.fail(f"SSL Exception: {error}") def get_container_by_id(self, container_id): @@ -350,27 +434,30 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error inspecting container: {exc}") def get_container(self, name=None): - ''' + """ Lookup a container and return the inspection results. - ''' + """ if name is None: return None search_name = name - if not name.startswith('/'): - search_name = '/' + name + if not name.startswith("/"): + search_name = "/" + name result = None try: for container in self.containers(all=True): self.log(f"testing container: {container['Names']}") - if isinstance(container['Names'], list) and search_name in container['Names']: + if ( + isinstance(container["Names"], list) + and search_name in container["Names"] + ): result = container break - if container['Id'].startswith(name): + if container["Id"].startswith(name): result = container break - if container['Id'] == name: + if container["Id"] == name: result = container break except SSLError as exc: @@ -381,12 +468,12 @@ class AnsibleDockerClientBase(Client): if result is None: return None - return self.get_container_by_id(result['Id']) + return self.get_container_by_id(result["Id"]) def get_network(self, name=None, network_id=None): - ''' + """ Lookup a network and return the inspection results. - ''' + """ if name is None and network_id is None: return None @@ -396,10 +483,10 @@ class AnsibleDockerClientBase(Client): try: for network in self.networks(): self.log(f"testing network: {network['Name']}") - if name == network['Name']: + if name == network["Name"]: result = network break - if network['Id'].startswith(name): + if network["Id"].startswith(name): result = network break except SSLError as exc: @@ -408,7 +495,7 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error retrieving network list: {exc}") if result is not None: - network_id = result['Id'] + network_id = result["Id"] if network_id is not None: try: @@ -423,9 +510,9 @@ class AnsibleDockerClientBase(Client): return result def find_image(self, name, tag): - ''' + """ Lookup an image (by name and tag) and return the inspection results. - ''' + """ if not name: return None @@ -434,14 +521,14 @@ class AnsibleDockerClientBase(Client): if not images: # In API <= 1.20 seeing 'docker.io/' as the name of images pulled from docker hub registry, repo_name = auth.resolve_repository_name(name) - if registry == 'docker.io': + if registry == "docker.io": # If docker.io is explicitly there in name, the image # is not found in some cases (#41509) self.log(f"Check for docker.io image: {repo_name}") images = self._image_lookup(repo_name, tag) - if not images and repo_name.startswith('library/'): + if not images and repo_name.startswith("library/"): # Sometimes library/xxx images are not found - lookup = repo_name[len('library/'):] + lookup = repo_name[len("library/") :] self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) if not images: @@ -451,7 +538,7 @@ class AnsibleDockerClientBase(Client): lookup = f"{registry}/{repo_name}" self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) - if not images and '/' not in repo_name: + if not images and "/" not in repo_name: # This seems to be happening with podman-docker # (https://github.com/ansible-collections/community.docker/issues/291) lookup = f"{registry}/library/{repo_name}" @@ -463,7 +550,7 @@ class AnsibleDockerClientBase(Client): if len(images) == 1: try: - inspection = self.inspect_image(images[0]['Id']) + inspection = self.inspect_image(images[0]["Id"]) except NotFound: self.log(f"Image {name}:{tag} not found.") return None @@ -475,9 +562,9 @@ class AnsibleDockerClientBase(Client): return None def find_image_by_id(self, image_id, accept_missing_image=False): - ''' + """ Lookup an image (by ID) and return the inspection results. - ''' + """ if not image_id: return None @@ -494,11 +581,11 @@ class AnsibleDockerClientBase(Client): return inspection def _image_lookup(self, name, tag): - ''' + """ Including a tag in the name parameter sent to the Docker SDK for Python images method does not work consistently. Instead, get the result set for name and manually check if the tag exists. - ''' + """ try: response = self.images(name=name) except Exception as exc: @@ -509,33 +596,35 @@ class AnsibleDockerClientBase(Client): lookup_digest = f"{name}@{tag}" images = [] for image in response: - tags = image.get('RepoTags') - digests = image.get('RepoDigests') + tags = image.get("RepoTags") + digests = image.get("RepoDigests") if (tags and lookup in tags) or (digests and lookup_digest in digests): images = [image] break return images def pull_image(self, name, tag="latest", platform=None): - ''' + """ Pull an image - ''' + """ kwargs = dict( tag=tag, stream=True, decode=True, ) if platform is not None: - kwargs['platform'] = platform + kwargs["platform"] = platform self.log(f"Pulling image {name}:{tag}") old_tag = self.find_image(name, tag) try: for line in self.pull(name, **kwargs): self.log(line, pretty_print=True) - if line.get('error'): - if line.get('errorDetail'): - error_detail = line.get('errorDetail') - self.fail(f"Error pulling {name} - code: {error_detail.get('code')} message: {error_detail.get('message')}") + if line.get("error"): + if line.get("errorDetail"): + error_detail = line.get("errorDetail") + self.fail( + f"Error pulling {name} - code: {error_detail.get('code')} message: {error_detail.get('message')}" + ) else: self.fail(f"Error pulling {name} - {line.get('error')}") except Exception as exc: @@ -546,27 +635,43 @@ class AnsibleDockerClientBase(Client): return new_tag, old_tag == new_tag def inspect_distribution(self, image, **kwargs): - ''' + """ Get image digest by directly calling the Docker API when running Docker SDK < 4.0.0 since prior versions did not support accessing private repositories. - ''' - if self.docker_py_version < LooseVersion('4.0.0'): + """ + if self.docker_py_version < LooseVersion("4.0.0"): registry = auth.resolve_repository_name(image)[0] header = auth.get_config_header(self, registry) if header: - return self._result(self._get( - self._url('/distribution/{0}/json', image), - headers={'X-Registry-Auth': header} - ), json=True) - return super(AnsibleDockerClientBase, self).inspect_distribution(image, **kwargs) + return self._result( + self._get( + self._url("/distribution/{0}/json", image), + headers={"X-Registry-Auth": header}, + ), + json=True, + ) + return super(AnsibleDockerClientBase, self).inspect_distribution( + image, **kwargs + ) class AnsibleDockerClient(AnsibleDockerClientBase): - def __init__(self, argument_spec=None, supports_check_mode=False, mutually_exclusive=None, - required_together=None, required_if=None, required_one_of=None, required_by=None, - min_docker_version=None, min_docker_api_version=None, option_minimal_versions=None, - option_minimal_versions_ignore_params=None, fail_results=None): + def __init__( + self, + argument_spec=None, + supports_check_mode=False, + mutually_exclusive=None, + required_together=None, + required_if=None, + required_one_of=None, + required_by=None, + min_docker_version=None, + min_docker_api_version=None, + option_minimal_versions=None, + option_minimal_versions_ignore_params=None, + fail_results=None, + ): # Modules can put information in here which will always be returned # in case client.fail() is called. @@ -598,22 +703,27 @@ class AnsibleDockerClient(AnsibleDockerClientBase): required_by=required_by or {}, ) - self.debug = self.module.params.get('debug') + self.debug = self.module.params.get("debug") self.check_mode = self.module.check_mode super(AnsibleDockerClient, self).__init__( min_docker_version=min_docker_version, - min_docker_api_version=min_docker_api_version) + min_docker_api_version=min_docker_api_version, + ) if option_minimal_versions is not None: - self._get_minimal_versions(option_minimal_versions, option_minimal_versions_ignore_params) + self._get_minimal_versions( + option_minimal_versions, option_minimal_versions_ignore_params + ) def fail(self, msg, **kwargs): self.fail_results.update(kwargs) self.module.fail_json(msg=msg, **sanitize_result(self.fail_results)) def deprecate(self, msg, version=None, date=None, collection_name=None): - self.module.deprecate(msg, version=version, date=date, collection_name=collection_name) + self.module.deprecate( + msg, version=version, date=date, collection_name=collection_name + ) def _get_params(self): return self.module.params @@ -631,26 +741,33 @@ class AnsibleDockerClient(AnsibleDockerClientBase): # Test whether option is supported, and store result support_docker_py = True support_docker_api = True - if 'docker_py_version' in data: - support_docker_py = self.docker_py_version >= LooseVersion(data['docker_py_version']) - if 'docker_api_version' in data: - support_docker_api = self.docker_api_version >= LooseVersion(data['docker_api_version']) - data['supported'] = support_docker_py and support_docker_api + if "docker_py_version" in data: + support_docker_py = self.docker_py_version >= LooseVersion( + data["docker_py_version"] + ) + if "docker_api_version" in data: + support_docker_api = self.docker_api_version >= LooseVersion( + data["docker_api_version"] + ) + data["supported"] = support_docker_py and support_docker_api # Fail if option is not supported but used - if not data['supported']: + if not data["supported"]: # Test whether option is specified - if 'detect_usage' in data: - used = data['detect_usage'](self) + if "detect_usage" in data: + used = data["detect_usage"](self) else: used = self.module.params.get(option) is not None - if used and 'default' in self.module.argument_spec[option]: - used = self.module.params[option] != self.module.argument_spec[option]['default'] + if used and "default" in self.module.argument_spec[option]: + used = ( + self.module.params[option] + != self.module.argument_spec[option]["default"] + ) if used: # If the option is used, compose error message. - if 'usage_msg' in data: - usg = data['usage_msg'] + if "usage_msg" in data: + usg = data["usage_msg"] else: - usg = f'set {option} option' + usg = f"set {option} option" if not support_docker_api: msg = f"Docker API version is {self.docker_api_version_str}. Minimum version required is {data['docker_api_version']} to {usg}." elif not support_docker_py: @@ -658,19 +775,21 @@ class AnsibleDockerClient(AnsibleDockerClientBase): f"Docker SDK for Python version is {docker_version} ({platform.node()}'s Python {sys.executable})." f" Minimum version required is {data['docker_py_version']} to {usg}. " ) - if LooseVersion(data['docker_py_version']) < LooseVersion('2.0.0'): + if LooseVersion(data["docker_py_version"]) < LooseVersion( + "2.0.0" + ): msg += DOCKERPYUPGRADE_RECOMMEND_DOCKER - elif self.docker_py_version < LooseVersion('2.0.0'): + elif self.docker_py_version < LooseVersion("2.0.0"): msg += DOCKERPYUPGRADE_SWITCH_TO_DOCKER else: msg += DOCKERPYUPGRADE_UPGRADE_DOCKER else: # should not happen - msg = f'Cannot {usg} with your configuration.' + msg = f"Cannot {usg} with your configuration." self.fail(msg) def report_warnings(self, result, warnings_key=None): - ''' + """ Checks result of client operation for warnings, and if present, outputs them. warnings_key should be a list of keys used to crawl the result dictionary. @@ -681,15 +800,15 @@ class AnsibleDockerClient(AnsibleDockerClientBase): In most cases (if warnings are returned at all), warnings_key should be ['Warnings'] or ['Warning']. The default value (if not specified) is ['Warnings']. - ''' + """ if warnings_key is None: - warnings_key = ['Warnings'] + warnings_key = ["Warnings"] for key in warnings_key: if not isinstance(result, Mapping): return result = result.get(key) if isinstance(result, Sequence): for warning in result: - self.module.warn(f'Docker warning: {warning}') + self.module.warn(f"Docker warning: {warning}") elif isinstance(result, str) and result: - self.module.warn(f'Docker warning: {result}') + self.module.warn(f"Docker warning: {result}") diff --git a/plugins/module_utils/common_api.py b/plugins/module_utils/common_api.py index 19421255..d0c1dd44 100644 --- a/plugins/module_utils/common_api.py +++ b/plugins/module_utils/common_api.py @@ -5,30 +5,37 @@ from __future__ import annotations - import abc import os import re from collections.abc import Mapping, Sequence from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE, BOOLEANS_FALSE +from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE, BOOLEANS_TRUE +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion try: - from requests.exceptions import RequestException, SSLError # noqa: F401, pylint: disable=unused-import + from requests.exceptions import ( # noqa: F401, pylint: disable=unused-import + RequestException, + SSLError, + ) except ImportError: # Define an exception class RequestException so that our code does not break. class RequestException(Exception): pass + from ansible_collections.community.docker.plugins.module_utils._api import auth -from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient as Client +from ansible_collections.community.docker.plugins.module_utils._api.api.client import ( + APIClient as Client, +) from ansible_collections.community.docker.plugins.module_utils._api.errors import ( APIError, - NotFound, MissingRequirementException, + NotFound, TLSParameterError, ) from ansible_collections.community.docker.plugins.module_utils._api.tls import TLSConfig @@ -36,19 +43,26 @@ from ansible_collections.community.docker.plugins.module_utils._api.utils.utils convert_filters, parse_repository_tag, ) - +from ansible_collections.community.docker.plugins.module_utils.util import ( + DEFAULT_DOCKER_REGISTRY, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DEFAULT_TLS_HOSTNAME, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + is_image_name_id, # TODO: remove +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + is_valid_tag, # TODO: remove +) from ansible_collections.community.docker.plugins.module_utils.util import ( # noqa: F401, pylint: disable=unused-import DEFAULT_DOCKER_HOST, + DEFAULT_TIMEOUT_SECONDS, DEFAULT_TLS, DEFAULT_TLS_VERIFY, - DEFAULT_TLS_HOSTNAME, # TODO: remove - DEFAULT_TIMEOUT_SECONDS, DOCKER_COMMON_ARGS, DOCKER_MUTUALLY_EXCLUSIVE, DOCKER_REQUIRED_TOGETHER, - DEFAULT_DOCKER_REGISTRY, # TODO: remove - is_image_name_id, # TODO: remove - is_valid_tag, # TODO: remove sanitize_result, update_tls_hostname, ) @@ -63,43 +77,45 @@ def _get_tls_config(fail_function, **kwargs): def is_using_tls(auth_data): - return auth_data['tls_verify'] or auth_data['tls'] + return auth_data["tls_verify"] or auth_data["tls"] def get_connect_params(auth_data, fail_function): if is_using_tls(auth_data): - auth_data['docker_host'] = auth_data['docker_host'].replace('tcp://', 'https://') + auth_data["docker_host"] = auth_data["docker_host"].replace( + "tcp://", "https://" + ) result = dict( - base_url=auth_data['docker_host'], - version=auth_data['api_version'], - timeout=auth_data['timeout'], + base_url=auth_data["docker_host"], + version=auth_data["api_version"], + timeout=auth_data["timeout"], ) - if auth_data['tls_verify']: + if auth_data["tls_verify"]: # TLS with verification tls_config = dict( verify=True, - assert_hostname=auth_data['tls_hostname'], + assert_hostname=auth_data["tls_hostname"], fail_function=fail_function, ) - if auth_data['cert_path'] and auth_data['key_path']: - tls_config['client_cert'] = (auth_data['cert_path'], auth_data['key_path']) - if auth_data['cacert_path']: - tls_config['ca_cert'] = auth_data['cacert_path'] - result['tls'] = _get_tls_config(**tls_config) - elif auth_data['tls']: + if auth_data["cert_path"] and auth_data["key_path"]: + tls_config["client_cert"] = (auth_data["cert_path"], auth_data["key_path"]) + if auth_data["cacert_path"]: + tls_config["ca_cert"] = auth_data["cacert_path"] + result["tls"] = _get_tls_config(**tls_config) + elif auth_data["tls"]: # TLS without verification tls_config = dict( verify=False, fail_function=fail_function, ) - if auth_data['cert_path'] and auth_data['key_path']: - tls_config['client_cert'] = (auth_data['cert_path'], auth_data['key_path']) - result['tls'] = _get_tls_config(**tls_config) + if auth_data["cert_path"] and auth_data["key_path"]: + tls_config["client_cert"] = (auth_data["cert_path"], auth_data["key_path"]) + result["tls"] = _get_tls_config(**tls_config) - if auth_data.get('use_ssh_client'): - result['use_ssh_client'] = True + if auth_data.get("use_ssh_client"): + result["use_ssh_client"] = True # No TLS return result @@ -107,22 +123,28 @@ def get_connect_params(auth_data, fail_function): class AnsibleDockerClientBase(Client): def __init__(self, min_docker_api_version=None): - self._connect_params = get_connect_params(self.auth_params, fail_function=self.fail) + self._connect_params = get_connect_params( + self.auth_params, fail_function=self.fail + ) try: super(AnsibleDockerClientBase, self).__init__(**self._connect_params) self.docker_api_version_str = self.api_version except MissingRequirementException as exc: - self.fail(missing_required_lib(exc.requirement), exception=exc.import_exception) + self.fail( + missing_required_lib(exc.requirement), exception=exc.import_exception + ) except APIError as exc: self.fail(f"Docker API error: {exc}") except Exception as exc: self.fail(f"Error connecting: {exc}") self.docker_api_version = LooseVersion(self.docker_api_version_str) - min_docker_api_version = min_docker_api_version or '1.25' + min_docker_api_version = min_docker_api_version or "1.25" if self.docker_api_version < LooseVersion(min_docker_api_version): - self.fail(f'Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}.') + self.fail( + f"Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}." + ) def log(self, msg, pretty_print=False): pass @@ -138,16 +160,16 @@ class AnsibleDockerClientBase(Client): pass @staticmethod - def _get_value(param_name, param_value, env_variable, default_value, type='str'): + def _get_value(param_name, param_value, env_variable, default_value, type="str"): if param_value is not None: # take module parameter value - if type == 'bool': + if type == "bool": if param_value in BOOLEANS_TRUE: return True if param_value in BOOLEANS_FALSE: return False return bool(param_value) - if type == 'int': + if type == "int": return int(param_value) return param_value @@ -155,19 +177,19 @@ class AnsibleDockerClientBase(Client): env_value = os.environ.get(env_variable) if env_value is not None: # take the env variable value - if param_name == 'cert_path': - return os.path.join(env_value, 'cert.pem') - if param_name == 'cacert_path': - return os.path.join(env_value, 'ca.pem') - if param_name == 'key_path': - return os.path.join(env_value, 'key.pem') - if type == 'bool': + if param_name == "cert_path": + return os.path.join(env_value, "cert.pem") + if param_name == "cacert_path": + return os.path.join(env_value, "ca.pem") + if param_name == "key_path": + return os.path.join(env_value, "key.pem") + if type == "bool": if env_value in BOOLEANS_TRUE: return True if env_value in BOOLEANS_FALSE: return False return bool(env_value) - if type == 'int': + if type == "int": return int(env_value) return env_value @@ -183,7 +205,7 @@ class AnsibleDockerClientBase(Client): # Get authentication credentials. # Precedence: module parameters-> environment variables-> defaults. - self.log('Getting credentials') + self.log("Getting credentials") client_params = self._get_params() @@ -192,44 +214,86 @@ class AnsibleDockerClientBase(Client): params[key] = client_params.get(key) result = dict( - docker_host=self._get_value('docker_host', params['docker_host'], 'DOCKER_HOST', - DEFAULT_DOCKER_HOST, type='str'), - tls_hostname=self._get_value('tls_hostname', params['tls_hostname'], - 'DOCKER_TLS_HOSTNAME', None, type='str'), - api_version=self._get_value('api_version', params['api_version'], 'DOCKER_API_VERSION', - 'auto', type='str'), - cacert_path=self._get_value('cacert_path', params['ca_path'], 'DOCKER_CERT_PATH', None, type='str'), - cert_path=self._get_value('cert_path', params['client_cert'], 'DOCKER_CERT_PATH', None, type='str'), - key_path=self._get_value('key_path', params['client_key'], 'DOCKER_CERT_PATH', None, type='str'), - tls=self._get_value('tls', params['tls'], 'DOCKER_TLS', DEFAULT_TLS, type='bool'), - tls_verify=self._get_value('validate_certs', params['validate_certs'], 'DOCKER_TLS_VERIFY', - DEFAULT_TLS_VERIFY, type='bool'), - timeout=self._get_value('timeout', params['timeout'], 'DOCKER_TIMEOUT', - DEFAULT_TIMEOUT_SECONDS, type='int'), - use_ssh_client=self._get_value('use_ssh_client', params['use_ssh_client'], None, False, type='bool'), + docker_host=self._get_value( + "docker_host", + params["docker_host"], + "DOCKER_HOST", + DEFAULT_DOCKER_HOST, + type="str", + ), + tls_hostname=self._get_value( + "tls_hostname", + params["tls_hostname"], + "DOCKER_TLS_HOSTNAME", + None, + type="str", + ), + api_version=self._get_value( + "api_version", + params["api_version"], + "DOCKER_API_VERSION", + "auto", + type="str", + ), + cacert_path=self._get_value( + "cacert_path", params["ca_path"], "DOCKER_CERT_PATH", None, type="str" + ), + cert_path=self._get_value( + "cert_path", params["client_cert"], "DOCKER_CERT_PATH", None, type="str" + ), + key_path=self._get_value( + "key_path", params["client_key"], "DOCKER_CERT_PATH", None, type="str" + ), + tls=self._get_value( + "tls", params["tls"], "DOCKER_TLS", DEFAULT_TLS, type="bool" + ), + tls_verify=self._get_value( + "validate_certs", + params["validate_certs"], + "DOCKER_TLS_VERIFY", + DEFAULT_TLS_VERIFY, + type="bool", + ), + timeout=self._get_value( + "timeout", + params["timeout"], + "DOCKER_TIMEOUT", + DEFAULT_TIMEOUT_SECONDS, + type="int", + ), + use_ssh_client=self._get_value( + "use_ssh_client", params["use_ssh_client"], None, False, type="bool" + ), ) def depr(*args, **kwargs): self.deprecate(*args, **kwargs) - update_tls_hostname(result, old_behavior=True, deprecate_function=depr, uses_tls=is_using_tls(result)) + update_tls_hostname( + result, + old_behavior=True, + deprecate_function=depr, + uses_tls=is_using_tls(result), + ) return result def _handle_ssl_error(self, error): match = re.match(r"hostname.*doesn\'t match (\'.*\')", str(error)) if match: - hostname = self.auth_params['tls_hostname'] - self.fail(f"You asked for verification that Docker daemons certificate's hostname matches {hostname}. " - f"The actual certificate's hostname is {match.group(1)}. Most likely you need to set DOCKER_TLS_HOSTNAME " - f"or pass `tls_hostname` with a value of {match.group(1)}. You may also use TLS without verification by " - "setting the `tls` parameter to true.") + hostname = self.auth_params["tls_hostname"] + self.fail( + f"You asked for verification that Docker daemons certificate's hostname matches {hostname}. " + f"The actual certificate's hostname is {match.group(1)}. Most likely you need to set DOCKER_TLS_HOSTNAME " + f"or pass `tls_hostname` with a value of {match.group(1)}. You may also use TLS without verification by " + "setting the `tls` parameter to true." + ) self.fail(f"SSL Exception: {error}") def get_container_by_id(self, container_id): try: self.log(f"Inspecting container Id {container_id}") - result = self.get_json('/containers/{0}/json', container_id) + result = self.get_json("/containers/{0}/json", container_id) self.log("Completed container inspection") return result except NotFound as dummy: @@ -238,34 +302,37 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error inspecting container: {exc}") def get_container(self, name=None): - ''' + """ Lookup a container and return the inspection results. - ''' + """ if name is None: return None search_name = name - if not name.startswith('/'): - search_name = '/' + name + if not name.startswith("/"): + search_name = "/" + name result = None try: params = { - 'limit': -1, - 'all': 1, - 'size': 0, - 'trunc_cmd': 0, + "limit": -1, + "all": 1, + "size": 0, + "trunc_cmd": 0, } containers = self.get_json("/containers/json", params=params) for container in containers: self.log(f"testing container: {container['Names']}") - if isinstance(container['Names'], list) and search_name in container['Names']: + if ( + isinstance(container["Names"], list) + and search_name in container["Names"] + ): result = container break - if container['Id'].startswith(name): + if container["Id"].startswith(name): result = container break - if container['Id'] == name: + if container["Id"] == name: result = container break except SSLError as exc: @@ -276,12 +343,12 @@ class AnsibleDockerClientBase(Client): if result is None: return None - return self.get_container_by_id(result['Id']) + return self.get_container_by_id(result["Id"]) def get_network(self, name=None, network_id=None): - ''' + """ Lookup a network and return the inspection results. - ''' + """ if name is None and network_id is None: return None @@ -292,10 +359,10 @@ class AnsibleDockerClientBase(Client): networks = self.get_json("/networks") for network in networks: self.log(f"testing network: {network['Name']}") - if name == network['Name']: + if name == network["Name"]: result = network break - if network['Id'].startswith(name): + if network["Id"].startswith(name): result = network break except SSLError as exc: @@ -304,12 +371,12 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error retrieving network list: {exc}") if result is not None: - network_id = result['Id'] + network_id = result["Id"] if network_id is not None: try: self.log(f"Inspecting network Id {network_id}") - result = self.get_json('/networks/{0}', network_id) + result = self.get_json("/networks/{0}", network_id) self.log("Completed network inspection") except NotFound as dummy: return None @@ -319,21 +386,21 @@ class AnsibleDockerClientBase(Client): return result def _image_lookup(self, name, tag): - ''' + """ Including a tag in the name parameter sent to the Docker SDK for Python images method does not work consistently. Instead, get the result set for name and manually check if the tag exists. - ''' + """ try: params = { - 'only_ids': 0, - 'all': 0, + "only_ids": 0, + "all": 0, } - if LooseVersion(self.api_version) < LooseVersion('1.25'): + if LooseVersion(self.api_version) < LooseVersion("1.25"): # only use "filter" on API 1.24 and under, as it is deprecated - params['filter'] = name + params["filter"] = name else: - params['filters'] = convert_filters({'reference': name}) + params["filters"] = convert_filters({"reference": name}) images = self.get_json("/images/json", params=params) except Exception as exc: self.fail(f"Error searching for image {name} - {exc}") @@ -343,17 +410,17 @@ class AnsibleDockerClientBase(Client): response = images images = [] for image in response: - tags = image.get('RepoTags') - digests = image.get('RepoDigests') + tags = image.get("RepoTags") + digests = image.get("RepoDigests") if (tags and lookup in tags) or (digests and lookup_digest in digests): images = [image] break return images def find_image(self, name, tag): - ''' + """ Lookup an image (by name and tag) and return the inspection results. - ''' + """ if not name: return None @@ -362,14 +429,14 @@ class AnsibleDockerClientBase(Client): if not images: # In API <= 1.20 seeing 'docker.io/' as the name of images pulled from docker hub registry, repo_name = auth.resolve_repository_name(name) - if registry == 'docker.io': + if registry == "docker.io": # If docker.io is explicitly there in name, the image # is not found in some cases (#41509) self.log(f"Check for docker.io image: {repo_name}") images = self._image_lookup(repo_name, tag) - if not images and repo_name.startswith('library/'): + if not images and repo_name.startswith("library/"): # Sometimes library/xxx images are not found - lookup = repo_name[len('library/'):] + lookup = repo_name[len("library/") :] self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) if not images: @@ -379,7 +446,7 @@ class AnsibleDockerClientBase(Client): lookup = f"{registry}/{repo_name}" self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) - if not images and '/' not in repo_name: + if not images and "/" not in repo_name: # This seems to be happening with podman-docker # (https://github.com/ansible-collections/community.docker/issues/291) lookup = f"{registry}/library/{repo_name}" @@ -391,7 +458,7 @@ class AnsibleDockerClientBase(Client): if len(images) == 1: try: - return self.get_json('/images/{0}/json', images[0]['Id']) + return self.get_json("/images/{0}/json", images[0]["Id"]) except NotFound: self.log(f"Image {name}:{tag} not found.") return None @@ -402,15 +469,15 @@ class AnsibleDockerClientBase(Client): return None def find_image_by_id(self, image_id, accept_missing_image=False): - ''' + """ Lookup an image (by ID) and return the inspection results. - ''' + """ if not image_id: return None self.log(f"Find image {image_id} (by ID)") try: - return self.get_json('/images/{0}/json', image_id) + return self.get_json("/images/{0}/json", image_id) except NotFound as exc: if not accept_missing_image: self.fail(f"Error inspecting image ID {image_id} - {exc}") @@ -420,37 +487,42 @@ class AnsibleDockerClientBase(Client): self.fail(f"Error inspecting image ID {image_id} - {exc}") def pull_image(self, name, tag="latest", platform=None): - ''' + """ Pull an image - ''' + """ self.log(f"Pulling image {name}:{tag}") old_tag = self.find_image(name, tag) try: repository, image_tag = parse_repository_tag(name) registry, repo_name = auth.resolve_repository_name(repository) params = { - 'tag': tag or image_tag or 'latest', - 'fromImage': repository, + "tag": tag or image_tag or "latest", + "fromImage": repository, } if platform is not None: - params['platform'] = platform + params["platform"] = platform headers = {} header = auth.get_config_header(self, registry) if header: - headers['X-Registry-Auth'] = header + headers["X-Registry-Auth"] = header response = self._post( - self._url('/images/create'), params=params, headers=headers, - stream=True, timeout=None + self._url("/images/create"), + params=params, + headers=headers, + stream=True, + timeout=None, ) self._raise_for_status(response) for line in self._stream_helper(response, decode=True): self.log(line, pretty_print=True) - if line.get('error'): - if line.get('errorDetail'): - error_detail = line.get('errorDetail') - self.fail(f"Error pulling {name} - code: {error_detail.get('code')} message: {error_detail.get('message')}") + if line.get("error"): + if line.get("errorDetail"): + error_detail = line.get("errorDetail") + self.fail( + f"Error pulling {name} - code: {error_detail.get('code')} message: {error_detail.get('message')}" + ) else: self.fail(f"Error pulling {name} - {line.get('error')}") except Exception as exc: @@ -463,10 +535,20 @@ class AnsibleDockerClientBase(Client): class AnsibleDockerClient(AnsibleDockerClientBase): - def __init__(self, argument_spec=None, supports_check_mode=False, mutually_exclusive=None, - required_together=None, required_if=None, required_one_of=None, required_by=None, - min_docker_api_version=None, option_minimal_versions=None, - option_minimal_versions_ignore_params=None, fail_results=None): + def __init__( + self, + argument_spec=None, + supports_check_mode=False, + mutually_exclusive=None, + required_together=None, + required_if=None, + required_one_of=None, + required_by=None, + min_docker_api_version=None, + option_minimal_versions=None, + option_minimal_versions_ignore_params=None, + fail_results=None, + ): # Modules can put information in here which will always be returned # in case client.fail() is called. @@ -498,20 +580,26 @@ class AnsibleDockerClient(AnsibleDockerClientBase): required_by=required_by or {}, ) - self.debug = self.module.params.get('debug') + self.debug = self.module.params.get("debug") self.check_mode = self.module.check_mode - super(AnsibleDockerClient, self).__init__(min_docker_api_version=min_docker_api_version) + super(AnsibleDockerClient, self).__init__( + min_docker_api_version=min_docker_api_version + ) if option_minimal_versions is not None: - self._get_minimal_versions(option_minimal_versions, option_minimal_versions_ignore_params) + self._get_minimal_versions( + option_minimal_versions, option_minimal_versions_ignore_params + ) def fail(self, msg, **kwargs): self.fail_results.update(kwargs) self.module.fail_json(msg=msg, **sanitize_result(self.fail_results)) def deprecate(self, msg, version=None, date=None, collection_name=None): - self.module.deprecate(msg, version=version, date=date, collection_name=collection_name) + self.module.deprecate( + msg, version=version, date=date, collection_name=collection_name + ) def _get_params(self): return self.module.params @@ -528,33 +616,38 @@ class AnsibleDockerClient(AnsibleDockerClientBase): for option, data in self.option_minimal_versions.items(): # Test whether option is supported, and store result support_docker_api = True - if 'docker_api_version' in data: - support_docker_api = self.docker_api_version >= LooseVersion(data['docker_api_version']) - data['supported'] = support_docker_api + if "docker_api_version" in data: + support_docker_api = self.docker_api_version >= LooseVersion( + data["docker_api_version"] + ) + data["supported"] = support_docker_api # Fail if option is not supported but used - if not data['supported']: + if not data["supported"]: # Test whether option is specified - if 'detect_usage' in data: - used = data['detect_usage'](self) + if "detect_usage" in data: + used = data["detect_usage"](self) else: used = self.module.params.get(option) is not None - if used and 'default' in self.module.argument_spec[option]: - used = self.module.params[option] != self.module.argument_spec[option]['default'] + if used and "default" in self.module.argument_spec[option]: + used = ( + self.module.params[option] + != self.module.argument_spec[option]["default"] + ) if used: # If the option is used, compose error message. - if 'usage_msg' in data: - usg = data['usage_msg'] + if "usage_msg" in data: + usg = data["usage_msg"] else: - usg = f'set {option} option' + usg = f"set {option} option" if not support_docker_api: msg = f"Docker API version is {self.docker_api_version_str}. Minimum version required is {data['docker_api_version']} to {usg}." else: # should not happen - msg = f'Cannot {usg} with your configuration.' + msg = f"Cannot {usg} with your configuration." self.fail(msg) def report_warnings(self, result, warnings_key=None): - ''' + """ Checks result of client operation for warnings, and if present, outputs them. warnings_key should be a list of keys used to crawl the result dictionary. @@ -565,15 +658,15 @@ class AnsibleDockerClient(AnsibleDockerClientBase): In most cases (if warnings are returned at all), warnings_key should be ['Warnings'] or ['Warning']. The default value (if not specified) is ['Warnings']. - ''' + """ if warnings_key is None: - warnings_key = ['Warnings'] + warnings_key = ["Warnings"] for key in warnings_key: if not isinstance(result, Mapping): return result = result.get(key) if isinstance(result, Sequence): for warning in result: - self.module.warn(f'Docker warning: {warning}') + self.module.warn(f"Docker warning: {warning}") elif isinstance(result, str) and result: - self.module.warn(f'Docker warning: {result}') + self.module.warn(f"Docker warning: {result}") diff --git a/plugins/module_utils/common_cli.py b/plugins/module_utils/common_cli.py index 7ea5d124..f4d91b5c 100644 --- a/plugins/module_utils/common_cli.py +++ b/plugins/module_utils/common_cli.py @@ -4,7 +4,6 @@ from __future__ import annotations - import abc import json import shlex @@ -12,11 +11,9 @@ import shlex from ansible.module_utils.basic import AnsibleModule, env_fallback from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.common.text.converters import to_native - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - -from ansible_collections.community.docker.plugins.module_utils._api.auth import resolve_repository_name - +from ansible_collections.community.docker.plugins.module_utils._api.auth import ( + resolve_repository_name, +) from ansible_collections.community.docker.plugins.module_utils.util import ( # noqa: F401, pylint: disable=unused-import DEFAULT_DOCKER_HOST, DEFAULT_TLS, @@ -25,20 +22,35 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( # DOCKER_REQUIRED_TOGETHER, sanitize_result, ) +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) DOCKER_COMMON_ARGS = dict( - docker_cli=dict(type='path'), - docker_host=dict(type='str', fallback=(env_fallback, ['DOCKER_HOST']), aliases=['docker_url']), - tls_hostname=dict(type='str', fallback=(env_fallback, ['DOCKER_TLS_HOSTNAME'])), - api_version=dict(type='str', default='auto', fallback=(env_fallback, ['DOCKER_API_VERSION']), aliases=['docker_api_version']), - ca_path=dict(type='path', aliases=['ca_cert', 'tls_ca_cert', 'cacert_path']), - client_cert=dict(type='path', aliases=['tls_client_cert', 'cert_path']), - client_key=dict(type='path', aliases=['tls_client_key', 'key_path']), - tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])), - validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']), + docker_cli=dict(type="path"), + docker_host=dict( + type="str", fallback=(env_fallback, ["DOCKER_HOST"]), aliases=["docker_url"] + ), + tls_hostname=dict(type="str", fallback=(env_fallback, ["DOCKER_TLS_HOSTNAME"])), + api_version=dict( + type="str", + default="auto", + fallback=(env_fallback, ["DOCKER_API_VERSION"]), + aliases=["docker_api_version"], + ), + ca_path=dict(type="path", aliases=["ca_cert", "tls_ca_cert", "cacert_path"]), + client_cert=dict(type="path", aliases=["tls_client_cert", "cert_path"]), + client_key=dict(type="path", aliases=["tls_client_key", "key_path"]), + tls=dict(type="bool", default=DEFAULT_TLS, fallback=(env_fallback, ["DOCKER_TLS"])), + validate_certs=dict( + type="bool", + default=DEFAULT_TLS_VERIFY, + fallback=(env_fallback, ["DOCKER_TLS_VERIFY"]), + aliases=["tls_verify"], + ), # debug=dict(type='bool', default=False), - cli_context=dict(type='str'), + cli_context=dict(type="str"), ) @@ -47,55 +59,71 @@ class DockerException(Exception): class AnsibleDockerClientBase(object): - def __init__(self, common_args, min_docker_api_version=None, needs_api_version=True): + def __init__( + self, common_args, min_docker_api_version=None, needs_api_version=True + ): self._environment = {} - if common_args['tls_hostname']: - self._environment['DOCKER_TLS_HOSTNAME'] = common_args['tls_hostname'] - if common_args['api_version'] and common_args['api_version'] != 'auto': - self._environment['DOCKER_API_VERSION'] = common_args['api_version'] - self._cli = common_args.get('docker_cli') + if common_args["tls_hostname"]: + self._environment["DOCKER_TLS_HOSTNAME"] = common_args["tls_hostname"] + if common_args["api_version"] and common_args["api_version"] != "auto": + self._environment["DOCKER_API_VERSION"] = common_args["api_version"] + self._cli = common_args.get("docker_cli") if self._cli is None: try: - self._cli = get_bin_path('docker') + self._cli = get_bin_path("docker") except ValueError: - self.fail('Cannot find docker CLI in path. Please provide it explicitly with the docker_cli parameter') + self.fail( + "Cannot find docker CLI in path. Please provide it explicitly with the docker_cli parameter" + ) self._cli_base = [self._cli] - docker_host = common_args['docker_host'] - if not docker_host and not common_args['cli_context']: + docker_host = common_args["docker_host"] + if not docker_host and not common_args["cli_context"]: docker_host = DEFAULT_DOCKER_HOST if docker_host: - self._cli_base.extend(['--host', docker_host]) - if common_args['validate_certs']: - self._cli_base.append('--tlsverify') - elif common_args['tls']: - self._cli_base.append('--tls') - if common_args['ca_path']: - self._cli_base.extend(['--tlscacert', common_args['ca_path']]) - if common_args['client_cert']: - self._cli_base.extend(['--tlscert', common_args['client_cert']]) - if common_args['client_key']: - self._cli_base.extend(['--tlskey', common_args['client_key']]) - if common_args['cli_context']: - self._cli_base.extend(['--context', common_args['cli_context']]) + self._cli_base.extend(["--host", docker_host]) + if common_args["validate_certs"]: + self._cli_base.append("--tlsverify") + elif common_args["tls"]: + self._cli_base.append("--tls") + if common_args["ca_path"]: + self._cli_base.extend(["--tlscacert", common_args["ca_path"]]) + if common_args["client_cert"]: + self._cli_base.extend(["--tlscert", common_args["client_cert"]]) + if common_args["client_key"]: + self._cli_base.extend(["--tlskey", common_args["client_key"]]) + if common_args["cli_context"]: + self._cli_base.extend(["--context", common_args["cli_context"]]) # `--format json` was only added as a shorthand for `--format {{ json . }}` in Docker 23.0 - dummy, self._version, dummy = self.call_cli_json('version', '--format', '{{ json . }}', check_rc=True) + dummy, self._version, dummy = self.call_cli_json( + "version", "--format", "{{ json . }}", check_rc=True + ) self._info = None if needs_api_version: - if not isinstance(self._version.get('Server'), dict) or not isinstance(self._version['Server'].get('ApiVersion'), str): - self.fail('Cannot determine Docker Daemon information. Are you maybe using podman instead of docker?') - self.docker_api_version_str = to_native(self._version['Server']['ApiVersion']) + if not isinstance(self._version.get("Server"), dict) or not isinstance( + self._version["Server"].get("ApiVersion"), str + ): + self.fail( + "Cannot determine Docker Daemon information. Are you maybe using podman instead of docker?" + ) + self.docker_api_version_str = to_native( + self._version["Server"]["ApiVersion"] + ) self.docker_api_version = LooseVersion(self.docker_api_version_str) - min_docker_api_version = min_docker_api_version or '1.25' + min_docker_api_version = min_docker_api_version or "1.25" if self.docker_api_version < LooseVersion(min_docker_api_version): - self.fail(f'Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}.') + self.fail( + f"Docker API version is {self.docker_api_version_str}. Minimum version required is {min_docker_api_version}." + ) else: self.docker_api_version_str = None self.docker_api_version = None if min_docker_api_version is not None: - self.fail('Internal error: cannot have needs_api_version=False with min_docker_api_version not None') + self.fail( + "Internal error: cannot have needs_api_version=False with min_docker_api_version not None" + ) def log(self, msg, pretty_print=False): pass @@ -113,7 +141,7 @@ class AnsibleDockerClientBase(object): return self._cli_base + list(args) def _compose_cmd_str(self, args): - return ' '.join(shlex.quote(a) for a in self._compose_cmd(args)) + return " ".join(shlex.quote(a) for a in self._compose_cmd(args)) @abc.abstractmethod def call_cli(self, *args, check_rc=False, data=None, cwd=None, environ_update=None): @@ -121,19 +149,21 @@ class AnsibleDockerClientBase(object): # def call_cli_json(self, *args, check_rc=False, data=None, cwd=None, environ_update=None, warn_on_stderr=False): def call_cli_json(self, *args, **kwargs): - warn_on_stderr = kwargs.pop('warn_on_stderr', False) + warn_on_stderr = kwargs.pop("warn_on_stderr", False) rc, stdout, stderr = self.call_cli(*args, **kwargs) if warn_on_stderr and stderr: self.warn(to_native(stderr)) try: data = json.loads(stdout) except Exception as exc: - self.fail(f'Error while parsing JSON output of {self._compose_cmd_str(args)}: {exc}\nJSON output: {to_native(stdout)}') + self.fail( + f"Error while parsing JSON output of {self._compose_cmd_str(args)}: {exc}\nJSON output: {to_native(stdout)}" + ) return rc, data, stderr # def call_cli_json_stream(self, *args, check_rc=False, data=None, cwd=None, environ_update=None, warn_on_stderr=False): def call_cli_json_stream(self, *args, **kwargs): - warn_on_stderr = kwargs.pop('warn_on_stderr', False) + warn_on_stderr = kwargs.pop("warn_on_stderr", False) rc, stdout, stderr = self.call_cli(*args, **kwargs) if warn_on_stderr and stderr: self.warn(to_native(stderr)) @@ -141,10 +171,12 @@ class AnsibleDockerClientBase(object): try: for line in stdout.splitlines(): line = line.strip() - if line.startswith(b'{'): + if line.startswith(b"{"): result.append(json.loads(line)) except Exception as exc: - self.fail(f'Error while parsing JSON output of {self._compose_cmd_str(args)}: {exc}\nJSON output: {to_native(stdout)}') + self.fail( + f"Error while parsing JSON output of {self._compose_cmd_str(args)}: {exc}\nJSON output: {to_native(stdout)}" + ) return rc, result, stderr @abc.abstractmethod @@ -161,26 +193,36 @@ class AnsibleDockerClientBase(object): def get_cli_info(self): if self._info is None: - dummy, self._info, dummy = self.call_cli_json('info', '--format', '{{ json . }}', check_rc=True) + dummy, self._info, dummy = self.call_cli_json( + "info", "--format", "{{ json . }}", check_rc=True + ) return self._info def get_client_plugin_info(self, component): cli_info = self.get_cli_info() - if not isinstance(cli_info.get('ClientInfo'), dict): - self.fail('Cannot determine Docker client information. Are you maybe using podman instead of docker?') - for plugin in cli_info['ClientInfo'].get('Plugins') or []: - if plugin.get('Name') == component: + if not isinstance(cli_info.get("ClientInfo"), dict): + self.fail( + "Cannot determine Docker client information. Are you maybe using podman instead of docker?" + ) + for plugin in cli_info["ClientInfo"].get("Plugins") or []: + if plugin.get("Name") == component: return plugin return None def _image_lookup(self, name, tag): - ''' + """ Including a tag in the name parameter sent to the Docker SDK for Python images method does not work consistently. Instead, get the result set for name and manually check if the tag exists. - ''' + """ dummy, images, dummy = self.call_cli_json_stream( - 'image', 'ls', '--format', '{{ json . }}', '--no-trunc', '--filter', f'reference={name}', + "image", + "ls", + "--format", + "{{ json . }}", + "--no-trunc", + "--filter", + f"reference={name}", check_rc=True, ) if tag: @@ -189,15 +231,15 @@ class AnsibleDockerClientBase(object): response = images images = [] for image in response: - if image.get('Tag') == tag or image.get('Digest') == tag: + if image.get("Tag") == tag or image.get("Digest") == tag: images = [image] break return images def find_image(self, name, tag): - ''' + """ Lookup an image (by name and tag) and return the inspection results. - ''' + """ if not name: return None @@ -206,14 +248,14 @@ class AnsibleDockerClientBase(object): if not images: # In API <= 1.20 seeing 'docker.io/' as the name of images pulled from docker hub registry, repo_name = resolve_repository_name(name) - if registry == 'docker.io': + if registry == "docker.io": # If docker.io is explicitly there in name, the image # is not found in some cases (#41509) self.log(f"Check for docker.io image: {repo_name}") images = self._image_lookup(repo_name, tag) - if not images and repo_name.startswith('library/'): + if not images and repo_name.startswith("library/"): # Sometimes library/xxx images are not found - lookup = repo_name[len('library/'):] + lookup = repo_name[len("library/") :] self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) if not images: @@ -223,7 +265,7 @@ class AnsibleDockerClientBase(object): lookup = f"{registry}/{repo_name}" self.log(f"Check for docker.io image: {lookup}") images = self._image_lookup(lookup, tag) - if not images and '/' not in repo_name: + if not images and "/" not in repo_name: # This seems to be happening with podman-docker # (https://github.com/ansible-collections/community.docker/issues/291) lookup = f"{registry}/library/{repo_name}" @@ -234,7 +276,7 @@ class AnsibleDockerClientBase(object): self.fail(f"Daemon returned more than one result for {name}:{tag}") if len(images) == 1: - rc, image, stderr = self.call_cli_json('image', 'inspect', images[0]['ID']) + rc, image, stderr = self.call_cli_json("image", "inspect", images[0]["ID"]) if not image: self.log(f"Image {name}:{tag} not found.") return None @@ -246,14 +288,14 @@ class AnsibleDockerClientBase(object): return None def find_image_by_id(self, image_id, accept_missing_image=False): - ''' + """ Lookup an image (by ID) and return the inspection results. - ''' + """ if not image_id: return None self.log(f"Find image {image_id} (by ID)") - rc, image, stderr = self.call_cli_json('image', 'inspect', image_id) + rc, image, stderr = self.call_cli_json("image", "inspect", image_id) if not image: if not accept_missing_image: self.fail(f"Error inspecting image ID {image_id} - {to_native(stderr)}") @@ -265,9 +307,19 @@ class AnsibleDockerClientBase(object): class AnsibleModuleDockerClient(AnsibleDockerClientBase): - def __init__(self, argument_spec=None, supports_check_mode=False, mutually_exclusive=None, - required_together=None, required_if=None, required_one_of=None, required_by=None, - min_docker_api_version=None, fail_results=None, needs_api_version=True): + def __init__( + self, + argument_spec=None, + supports_check_mode=False, + mutually_exclusive=None, + required_together=None, + required_if=None, + required_one_of=None, + required_by=None, + min_docker_api_version=None, + fail_results=None, + needs_api_version=True, + ): # Modules can put information in here which will always be returned # in case client.fail() is called. @@ -279,7 +331,7 @@ class AnsibleModuleDockerClient(AnsibleDockerClientBase): merged_arg_spec.update(argument_spec) self.arg_spec = merged_arg_spec - mutually_exclusive_params = [('docker_host', 'cli_context')] + mutually_exclusive_params = [("docker_host", "cli_context")] mutually_exclusive_params += DOCKER_MUTUALLY_EXCLUSIVE if mutually_exclusive: mutually_exclusive_params += mutually_exclusive @@ -305,7 +357,9 @@ class AnsibleModuleDockerClient(AnsibleDockerClientBase): common_args = dict((k, self.module.params[k]) for k in DOCKER_COMMON_ARGS) super(AnsibleModuleDockerClient, self).__init__( - common_args, min_docker_api_version=min_docker_api_version, needs_api_version=needs_api_version, + common_args, + min_docker_api_version=min_docker_api_version, + needs_api_version=needs_api_version, ) def call_cli(self, *args, check_rc=False, data=None, cwd=None, environ_update=None): @@ -333,4 +387,6 @@ class AnsibleModuleDockerClient(AnsibleDockerClientBase): self.module.warn(msg) def deprecate(self, msg, version=None, date=None, collection_name=None): - self.module.deprecate(msg, version=version, date=date, collection_name=collection_name) + self.module.deprecate( + msg, version=version, date=date, collection_name=collection_name + ) diff --git a/plugins/module_utils/compose_v2.py b/plugins/module_utils/compose_v2.py index 26295f3b..1d447da7 100644 --- a/plugins/module_utils/compose_v2.py +++ b/plugins/module_utils/compose_v2.py @@ -5,7 +5,6 @@ from __future__ import annotations - import json import os import re @@ -17,16 +16,23 @@ from shlex import quote from ansible.module_utils.basic import missing_required_lib from ansible.module_utils.common.text.converters import to_native - -from ansible_collections.community.docker.plugins.module_utils.util import DockerBaseClass -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion from ansible_collections.community.docker.plugins.module_utils._logfmt import ( InvalidLogFmt as _InvalidLogFmt, +) +from ansible_collections.community.docker.plugins.module_utils._logfmt import ( parse_line as _parse_logfmt_line, ) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DockerBaseClass, +) +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) + try: import yaml + try: # use C version if possible for speedup from yaml import CSafeDumper as _SafeDumper @@ -39,71 +45,88 @@ except ImportError: PYYAML_IMPORT_ERROR = traceback.format_exc() -DOCKER_COMPOSE_FILES = ('compose.yaml', 'compose.yml', 'docker-compose.yaml', 'docker-compose.yml') +DOCKER_COMPOSE_FILES = ( + "compose.yaml", + "compose.yml", + "docker-compose.yaml", + "docker-compose.yml", +) -DOCKER_STATUS_DONE = frozenset(( - 'Started', - 'Healthy', - 'Exited', - 'Restarted', - 'Running', - 'Created', - 'Stopped', - 'Killed', - 'Removed', - # An extra, specific to containers - 'Recreated', - # Extras for pull events - 'Pulled', - # Extras for built events - 'Built', -)) -DOCKER_STATUS_WORKING = frozenset(( - 'Creating', - 'Starting', - 'Restarting', - 'Stopping', - 'Killing', - 'Removing', - # An extra, specific to containers - 'Recreate', - # Extras for pull events - 'Pulling', - # Extras for build start events - 'Building', -)) -DOCKER_STATUS_PULL = frozenset(( - 'Pulled', - 'Pulling', -)) -DOCKER_STATUS_BUILD = frozenset(( - 'Built', - 'Building', -)) -DOCKER_STATUS_ERROR = frozenset(( - 'Error', -)) -DOCKER_STATUS_WARNING = frozenset(( - 'Warning', -)) -DOCKER_STATUS_WAITING = frozenset(( - 'Waiting', -)) -DOCKER_STATUS = frozenset(DOCKER_STATUS_DONE | DOCKER_STATUS_WORKING | DOCKER_STATUS_PULL | DOCKER_STATUS_ERROR | DOCKER_STATUS_WAITING) +DOCKER_STATUS_DONE = frozenset( + ( + "Started", + "Healthy", + "Exited", + "Restarted", + "Running", + "Created", + "Stopped", + "Killed", + "Removed", + # An extra, specific to containers + "Recreated", + # Extras for pull events + "Pulled", + # Extras for built events + "Built", + ) +) +DOCKER_STATUS_WORKING = frozenset( + ( + "Creating", + "Starting", + "Restarting", + "Stopping", + "Killing", + "Removing", + # An extra, specific to containers + "Recreate", + # Extras for pull events + "Pulling", + # Extras for build start events + "Building", + ) +) +DOCKER_STATUS_PULL = frozenset( + ( + "Pulled", + "Pulling", + ) +) +DOCKER_STATUS_BUILD = frozenset( + ( + "Built", + "Building", + ) +) +DOCKER_STATUS_ERROR = frozenset(("Error",)) +DOCKER_STATUS_WARNING = frozenset(("Warning",)) +DOCKER_STATUS_WAITING = frozenset(("Waiting",)) +DOCKER_STATUS = frozenset( + DOCKER_STATUS_DONE + | DOCKER_STATUS_WORKING + | DOCKER_STATUS_PULL + | DOCKER_STATUS_ERROR + | DOCKER_STATUS_WAITING +) DOCKER_STATUS_AND_WARNING = frozenset(DOCKER_STATUS | DOCKER_STATUS_WARNING) -DOCKER_PULL_PROGRESS_DONE = frozenset(( - 'Already exists', - 'Download complete', - 'Pull complete', -)) -DOCKER_PULL_PROGRESS_WORKING = frozenset(( - 'Pulling fs layer', - 'Waiting', - 'Downloading', - 'Verifying Checksum', - 'Extracting', -)) +DOCKER_PULL_PROGRESS_DONE = frozenset( + ( + "Already exists", + "Download complete", + "Pull complete", + ) +) +DOCKER_PULL_PROGRESS_WORKING = frozenset( + ( + "Pulling fs layer", + "Waiting", + "Downloading", + "Verifying Checksum", + "Extracting", + ) +) class ResourceType(object): @@ -127,150 +150,137 @@ class ResourceType(object): }[resource_type] -Event = namedtuple( - 'Event', - ['resource_type', 'resource_id', 'status', 'msg'] -) +Event = namedtuple("Event", ["resource_type", "resource_id", "status", "msg"]) -_DRY_RUN_MARKER = 'DRY-RUN MODE -' +_DRY_RUN_MARKER = "DRY-RUN MODE -" _RE_RESOURCE_EVENT = re.compile( - r'^' - r'\s*' - r'(?PNetwork|Image|Volume|Container)' - r'\s+' - r'(?P\S+)' - r'\s+' - r'(?P\S(?:|.*\S))' - r'\s*' - r'$' + r"^" + r"\s*" + r"(?PNetwork|Image|Volume|Container)" + r"\s+" + r"(?P\S+)" + r"\s+" + r"(?P\S(?:|.*\S))" + r"\s*" + r"$" ) _RE_PULL_EVENT = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'(?P%s)' - r'\s*' - r'$' - % '|'.join(re.escape(status) for status in DOCKER_STATUS_PULL) + r"^" + r"\s*" + r"(?P\S+)" + r"\s+" + r"(?P%s)" + r"\s*" + r"$" % "|".join(re.escape(status) for status in DOCKER_STATUS_PULL) ) _RE_PULL_PROGRESS = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'(?P%s)' - r'\s*' - r'(?:|\s\[[^]]+\]\s+\S+\s*|\s+[0-9.kKmMgGbB]+/[0-9.kKmMgGbB]+\s*)' - r'$' - % '|'.join(re.escape(status) for status in sorted(DOCKER_PULL_PROGRESS_DONE | DOCKER_PULL_PROGRESS_WORKING)) + r"^" + r"\s*" + r"(?P\S+)" + r"\s+" + r"(?P%s)" + r"\s*" + r"(?:|\s\[[^]]+\]\s+\S+\s*|\s+[0-9.kKmMgGbB]+/[0-9.kKmMgGbB]+\s*)" + r"$" + % "|".join( + re.escape(status) + for status in sorted(DOCKER_PULL_PROGRESS_DONE | DOCKER_PULL_PROGRESS_WORKING) + ) ) _RE_ERROR_EVENT = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'(?P%s)' - r'\s*' - r'(?P\S.*\S)?' - r'$' - % '|'.join(re.escape(status) for status in DOCKER_STATUS_ERROR) + r"^" + r"\s*" + r"(?P\S+)" + r"\s+" + r"(?P%s)" + r"\s*" + r"(?P\S.*\S)?" + r"$" % "|".join(re.escape(status) for status in DOCKER_STATUS_ERROR) ) _RE_WARNING_EVENT = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'(?P%s)' - r'\s*' - r'(?P\S.*\S)?' - r'$' - % '|'.join(re.escape(status) for status in DOCKER_STATUS_WARNING) + r"^" + r"\s*" + r"(?P\S+)" + r"\s+" + r"(?P%s)" + r"\s*" + r"(?P\S.*\S)?" + r"$" % "|".join(re.escape(status) for status in DOCKER_STATUS_WARNING) ) _RE_CONTINUE_EVENT = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'-' - r'\s*' - r'(?P\S(?:|.*\S))' - r'$' + r"^" r"\s*" r"(?P\S+)" r"\s+" r"-" r"\s*" r"(?P\S(?:|.*\S))" r"$" ) _RE_SKIPPED_EVENT = re.compile( - r'^' - r'\s*' - r'(?P\S+)' - r'\s+' - r'Skipped(?: -' - r'\s*' - r'(?P\S(?:|.*\S))|\s*)' - r'$' + r"^" + r"\s*" + r"(?P\S+)" + r"\s+" + r"Skipped(?: -" + r"\s*" + r"(?P\S(?:|.*\S))|\s*)" + r"$" ) _RE_BUILD_START_EVENT = re.compile( - r'^' - r'\s*' - r'build service' - r'\s+' - r'(?P\S+)' - r'$' + r"^" r"\s*" r"build service" r"\s+" r"(?P\S+)" r"$" ) -_RE_BUILD_PROGRESS_EVENT = re.compile( - r'^' - r'\s*' - r'==>' - r'\s+' - r'(?P.*)' - r'$' -) +_RE_BUILD_PROGRESS_EVENT = re.compile(r"^" r"\s*" r"==>" r"\s+" r"(?P.*)" r"$") # The following needs to be kept in sync with the MINIMUM_VERSION compose_v2 docs fragment -MINIMUM_COMPOSE_VERSION = '2.18.0' +MINIMUM_COMPOSE_VERSION = "2.18.0" def _extract_event(line, warn_function=None): match = _RE_RESOURCE_EVENT.match(line) if match is not None: - status = match.group('status') + status = match.group("status") msg = None if status not in DOCKER_STATUS: status, msg = msg, status - return Event( - ResourceType.from_docker_compose_event(match.group('resource_type')), - match.group('resource_id'), - status, - msg, - ), True + return ( + Event( + ResourceType.from_docker_compose_event(match.group("resource_type")), + match.group("resource_id"), + status, + msg, + ), + True, + ) match = _RE_PULL_EVENT.match(line) if match: - return Event( - ResourceType.SERVICE, - match.group('service'), - match.group('status'), - None, - ), True + return ( + Event( + ResourceType.SERVICE, + match.group("service"), + match.group("status"), + None, + ), + True, + ) match = _RE_ERROR_EVENT.match(line) if match: - return Event( - ResourceType.UNKNOWN, - match.group('resource_id'), - match.group('status'), - match.group('msg') or None, - ), True + return ( + Event( + ResourceType.UNKNOWN, + match.group("resource_id"), + match.group("status"), + match.group("msg") or None, + ), + True, + ) match = _RE_WARNING_EVENT.match(line) if match: if warn_function: - if match.group('msg'): + if match.group("msg"): msg = f"{match.group('resource_id')}: {match.group('msg')}" else: msg = f"Unspecified warning for {match.group('resource_id')}" @@ -278,28 +288,37 @@ def _extract_event(line, warn_function=None): return None, True match = _RE_PULL_PROGRESS.match(line) if match: - return Event( - ResourceType.IMAGE_LAYER, - match.group('layer'), - match.group('status'), - None, - ), True + return ( + Event( + ResourceType.IMAGE_LAYER, + match.group("layer"), + match.group("status"), + None, + ), + True, + ) match = _RE_SKIPPED_EVENT.match(line) if match: - return Event( - ResourceType.UNKNOWN, - match.group('resource_id'), - 'Skipped', - match.group('msg'), - ), True + return ( + Event( + ResourceType.UNKNOWN, + match.group("resource_id"), + "Skipped", + match.group("msg"), + ), + True, + ) match = _RE_BUILD_START_EVENT.match(line) if match: - return Event( - ResourceType.SERVICE, - match.group('resource_id'), - 'Building', - None, - ), True + return ( + Event( + ResourceType.SERVICE, + match.group("resource_id"), + "Building", + None, + ), + True, + ) return None, False @@ -308,11 +327,11 @@ def _extract_logfmt_event(line, warn_function=None): result = _parse_logfmt_line(line, logrus_mode=True) except _InvalidLogFmt: return None, False - if 'time' not in result or 'level' not in result or 'msg' not in result: + if "time" not in result or "level" not in result or "msg" not in result: return None, False - if result['level'] == 'warning': + if result["level"] == "warning": if warn_function: - warn_function(result['msg']) + warn_function(result["msg"]) return None, True # TODO: no idea what to do with this return None, False @@ -323,8 +342,8 @@ def _warn_missing_dry_run_prefix(line, warn_missing_dry_run_prefix, warn_functio # This could be a bug, a change of docker compose's output format, ... # Tell the user to report it to us :-) warn_function( - f'Event line is missing dry-run mode marker: {line!r}. Please report this at ' - 'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md' + f"Event line is missing dry-run mode marker: {line!r}. Please report this at " + "https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md" ) @@ -333,8 +352,8 @@ def _warn_unparsable_line(line, warn_function): # Tell the user to report it to us :-) if warn_function: warn_function( - f'Cannot parse event from line: {line!r}. Please report this at ' - 'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md' + f"Cannot parse event from line: {line!r}. Please report this at " + "https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md" ) @@ -350,38 +369,38 @@ def _concat_event_msg(event, append_msg): event.resource_type, event.resource_id, event.status, - '\n'.join(msg for msg in [event.msg, append_msg] if msg is not None), + "\n".join(msg for msg in [event.msg, append_msg] if msg is not None), ) _JSON_LEVEL_TO_STATUS_MAP = { - 'warning': 'Warning', - 'error': 'Error', + "warning": "Warning", + "error": "Error", } def parse_json_events(stderr, warn_function=None): events = [] stderr_lines = stderr.splitlines() - if stderr_lines and stderr_lines[-1] == b'': + if stderr_lines and stderr_lines[-1] == b"": del stderr_lines[-1] for line in stderr_lines: line = line.strip() - if not line.startswith(b'{') or not line.endswith(b'}'): - if line.startswith(b'Warning: '): + if not line.startswith(b"{") or not line.endswith(b"}"): + if line.startswith(b"Warning: "): # This is a bug in Compose that will get fixed by https://github.com/docker/compose/pull/11996 event = Event( ResourceType.UNKNOWN, None, - 'Warning', - to_native(line[len(b'Warning: '):]), + "Warning", + to_native(line[len(b"Warning: ") :]), ) events.append(event) continue if warn_function: warn_function( - f'Cannot parse event from non-JSON line: {line!r}. Please report this at ' - 'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md' + f"Cannot parse event from non-JSON line: {line!r}. Please report this at " + "https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md" ) continue try: @@ -389,78 +408,103 @@ def parse_json_events(stderr, warn_function=None): except Exception as exc: if warn_function: warn_function( - f'Cannot parse event from line: {line!r}: {exc}. Please report this at ' - 'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md' + f"Cannot parse event from line: {line!r}: {exc}. Please report this at " + "https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md" ) continue - if line_data.get('tail'): + if line_data.get("tail"): resource_type = ResourceType.UNKNOWN - msg = line_data.get('text') - status = 'Error' - if isinstance(msg, str) and msg.lower().startswith('warning:'): + msg = line_data.get("text") + status = "Error" + if isinstance(msg, str) and msg.lower().startswith("warning:"): # For some reason, Writer.TailMsgf() is always used for errors *except* in one place, # where its message is prepended with 'WARNING: ' (in pkg/compose/pull.go). - status = 'Warning' - msg = msg[len('warning:'):].lstrip() + status = "Warning" + msg = msg[len("warning:") :].lstrip() event = Event( resource_type, None, status, msg, ) - elif line_data.get('error'): + elif line_data.get("error"): resource_type = ResourceType.UNKNOWN event = Event( resource_type, - line_data.get('id'), - 'Error', - line_data.get('message'), + line_data.get("id"), + "Error", + line_data.get("message"), ) else: resource_type = ResourceType.UNKNOWN - resource_id = line_data.get('id') - status = line_data.get('status') - text = line_data.get('text') + resource_id = line_data.get("id") + status = line_data.get("status") + text = line_data.get("text") if resource_id == " " and text and text.startswith("build service "): # Example: # {"dry-run":true,"id":" ","text":"build service app"} - resource_id = "S" + text[len("build s"):] + resource_id = "S" + text[len("build s") :] text = "Building" - if isinstance(resource_id, str) and resource_id.endswith("==>") and text and text.startswith("==> writing image "): + if ( + isinstance(resource_id, str) + and resource_id.endswith("==>") + and text + and text.startswith("==> writing image ") + ): # Example: # {"dry-run":true,"id":"==>","text":"==> writing image dryRun-7d1043473d55bfa90e8530d35801d4e381bc69f0"} # {"dry-run":true,"id":"ansible-docker-test-dc713f1f-container ==>","text":"==> writing image dryRun-5d9204268db1a73d57bbd24a25afbeacebe2bc02"} # (The longer form happens since Docker Compose 2.39.0) continue - if isinstance(resource_id, str) and resource_id.endswith("==> ==>") and text and text.startswith("naming to "): + if ( + isinstance(resource_id, str) + and resource_id.endswith("==> ==>") + and text + and text.startswith("naming to ") + ): # Example: # {"dry-run":true,"id":"==> ==>","text":"naming to display-app"} # {"dry-run":true,"id":"ansible-docker-test-dc713f1f-container ==> ==>","text":"naming to ansible-docker-test-dc713f1f-image"} # (The longer form happens since Docker Compose 2.39.0) continue - if isinstance(resource_id, str) and ' ' in resource_id: - resource_type_str, resource_id = resource_id.split(' ', 1) + if isinstance(resource_id, str) and " " in resource_id: + resource_type_str, resource_id = resource_id.split(" ", 1) try: - resource_type = ResourceType.from_docker_compose_event(resource_type_str) + resource_type = ResourceType.from_docker_compose_event( + resource_type_str + ) except KeyError: if warn_function: warn_function( - f'Unknown resource type {resource_type_str!r} in line {line!r}. Please report this at ' - 'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md' + f"Unknown resource type {resource_type_str!r} in line {line!r}. Please report this at " + "https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md" ) resource_type = ResourceType.UNKNOWN elif text in DOCKER_STATUS_PULL: resource_type = ResourceType.IMAGE status, text = text, status - elif text in DOCKER_PULL_PROGRESS_DONE or line_data.get('text') in DOCKER_PULL_PROGRESS_WORKING: + elif ( + text in DOCKER_PULL_PROGRESS_DONE + or line_data.get("text") in DOCKER_PULL_PROGRESS_WORKING + ): resource_type = ResourceType.IMAGE_LAYER status, text = text, status - elif status is None and isinstance(text, str) and text.startswith('Skipped - '): - status, text = text.split(' - ', 1) - elif line_data.get('level') in _JSON_LEVEL_TO_STATUS_MAP and 'msg' in line_data: - status = _JSON_LEVEL_TO_STATUS_MAP[line_data['level']] - text = line_data['msg'] - if status not in DOCKER_STATUS_AND_WARNING and text in DOCKER_STATUS_AND_WARNING: + elif ( + status is None + and isinstance(text, str) + and text.startswith("Skipped - ") + ): + status, text = text.split(" - ", 1) + elif ( + line_data.get("level") in _JSON_LEVEL_TO_STATUS_MAP + and "msg" in line_data + ): + status = _JSON_LEVEL_TO_STATUS_MAP[line_data["level"]] + text = line_data["msg"] + if ( + status not in DOCKER_STATUS_AND_WARNING + and text in DOCKER_STATUS_AND_WARNING + ): status, text = text, status event = Event( resource_type, @@ -477,7 +521,7 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): events = [] error_event = None stderr_lines = stderr.splitlines() - if stderr_lines and stderr_lines[-1] == b'': + if stderr_lines and stderr_lines[-1] == b"": del stderr_lines[-1] for index, line in enumerate(stderr_lines): line = to_native(line.strip()) @@ -486,7 +530,7 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): warn_missing_dry_run_prefix = False if dry_run: if line.startswith(_DRY_RUN_MARKER): - line = line[len(_DRY_RUN_MARKER):].lstrip() + line = line[len(_DRY_RUN_MARKER) :].lstrip() else: warn_missing_dry_run_prefix = True event, parsed = _extract_event(line, warn_function=warn_function) @@ -496,7 +540,9 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): error_event = event else: error_event = None - _warn_missing_dry_run_prefix(line, warn_missing_dry_run_prefix, warn_function) + _warn_missing_dry_run_prefix( + line, warn_missing_dry_run_prefix, warn_function + ) continue elif parsed: continue @@ -507,10 +553,10 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): match = _RE_CONTINUE_EVENT.match(line) if match: # Continuing an existing event - index_event = _find_last_event_for(events, match.group('resource_id')) + index_event = _find_last_event_for(events, match.group("resource_id")) if index_event is not None: index, event = index_event - events[index] = _concat_event_msg(event, match.group('msg')) + events[index] = _concat_event_msg(event, match.group("msg")) event, parsed = _extract_logfmt_event(line, warn_function=warn_function) if event is not None: events.append(event) @@ -520,12 +566,12 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): # Unparsable line that apparently belongs to the previous error event events[-1] = _concat_event_msg(error_event, line) continue - if line.startswith('Error '): + if line.startswith("Error "): # Error message that is independent of an error event error_event = Event( ResourceType.UNKNOWN, - '', - 'Error', + "", + "Error", line, ) events.append(error_event) @@ -534,8 +580,8 @@ def parse_events(stderr, dry_run=False, warn_function=None, nonzero_rc=False): # **Very likely** an error message that is independent of an error event error_event = Event( ResourceType.UNKNOWN, - '', - 'Error', + "", + "Error", line, ) events.append(error_event) @@ -553,7 +599,10 @@ def has_changes(events, ignore_service_pull_events=False, ignore_build_events=Fa if ignore_build_events and event.status in DOCKER_STATUS_BUILD: continue return True - if event.resource_type == ResourceType.IMAGE_LAYER and event.status in DOCKER_PULL_PROGRESS_WORKING: + if ( + event.resource_type == ResourceType.IMAGE_LAYER + and event.status in DOCKER_PULL_PROGRESS_WORKING + ): return True return False @@ -562,29 +611,43 @@ def extract_actions(events): actions = [] pull_actions = set() for event in events: - if event.resource_type == ResourceType.IMAGE_LAYER and event.status in DOCKER_PULL_PROGRESS_WORKING: + if ( + event.resource_type == ResourceType.IMAGE_LAYER + and event.status in DOCKER_PULL_PROGRESS_WORKING + ): pull_id = (event.resource_id, event.status) if pull_id not in pull_actions: pull_actions.add(pull_id) - actions.append({ - 'what': event.resource_type, - 'id': event.resource_id, - 'status': event.status, - }) - if event.resource_type != ResourceType.IMAGE_LAYER and event.status in DOCKER_STATUS_WORKING: - actions.append({ - 'what': event.resource_type, - 'id': event.resource_id, - 'status': event.status, - }) + actions.append( + { + "what": event.resource_type, + "id": event.resource_id, + "status": event.status, + } + ) + if ( + event.resource_type != ResourceType.IMAGE_LAYER + and event.status in DOCKER_STATUS_WORKING + ): + actions.append( + { + "what": event.resource_type, + "id": event.resource_id, + "status": event.status, + } + ) return actions def emit_warnings(events, warn_function): for event in events: # If a message is present, assume it is a warning - if (event.status is None and event.msg is not None) or event.status in DOCKER_STATUS_WARNING: - warn_function(f'Docker compose: {event.resource_type} {event.resource_id}: {event.msg}') + if ( + event.status is None and event.msg is not None + ) or event.status in DOCKER_STATUS_WARNING: + warn_function( + f"Docker compose: {event.resource_type} {event.resource_id}: {event.msg}" + ) def is_failed(events, rc): @@ -600,61 +663,64 @@ def update_failed(result, events, args, stdout, stderr, rc, cli): for event in events: if event.status in DOCKER_STATUS_ERROR: if event.resource_id is None: - if event.resource_type == 'unknown': - msg = 'General error: ' if event.resource_type == 'unknown' else f'Error when processing {event.resource_type}: ' + if event.resource_type == "unknown": + msg = ( + "General error: " + if event.resource_type == "unknown" + else f"Error when processing {event.resource_type}: " + ) else: - msg = f'Error when processing {event.resource_type} {event.resource_id}: ' - if event.resource_type == 'unknown': - msg = f'Error when processing {event.resource_id}: ' - if event.resource_id == '': - msg = 'General error: ' - msg += f'{event.status}' if event.msg is None else f'{event.msg}' + msg = ( + f"Error when processing {event.resource_type} {event.resource_id}: " + ) + if event.resource_type == "unknown": + msg = f"Error when processing {event.resource_id}: " + if event.resource_id == "": + msg = "General error: " + msg += f"{event.status}" if event.msg is None else f"{event.msg}" errors.append(msg) if not errors: - errors.append(f'Return code {rc} is non-zero') - result['failed'] = True - result['msg'] = '\n'.join(errors) - result['cmd'] = ' '.join(quote(arg) for arg in [cli] + args) - result['stdout'] = to_native(stdout) - result['stderr'] = to_native(stderr) - result['rc'] = rc + errors.append(f"Return code {rc} is non-zero") + result["failed"] = True + result["msg"] = "\n".join(errors) + result["cmd"] = " ".join(quote(arg) for arg in [cli] + args) + result["stdout"] = to_native(stdout) + result["stderr"] = to_native(stderr) + result["rc"] = rc return True def common_compose_argspec(): return dict( - project_src=dict(type='path'), - project_name=dict(type='str'), - files=dict(type='list', elements='path'), - definition=dict(type='dict'), - env_files=dict(type='list', elements='path'), - profiles=dict(type='list', elements='str'), - check_files_existing=dict(type='bool', default=True), + project_src=dict(type="path"), + project_name=dict(type="str"), + files=dict(type="list", elements="path"), + definition=dict(type="dict"), + env_files=dict(type="list", elements="path"), + profiles=dict(type="list", elements="str"), + check_files_existing=dict(type="bool", default=True), ) def common_compose_argspec_ex(): return dict( argspec=common_compose_argspec(), - mutually_exclusive=[ - ('definition', 'project_src'), - ('definition', 'files') - ], + mutually_exclusive=[("definition", "project_src"), ("definition", "files")], required_one_of=[ - ('definition', 'project_src'), + ("definition", "project_src"), ], required_by={ - 'definition': ('project_name', ), + "definition": ("project_name",), }, ) def combine_binary_output(*outputs): - return b'\n'.join(out for out in outputs if out) + return b"\n".join(out for out in outputs if out) def combine_text_output(*outputs): - return '\n'.join(out for out in outputs if out) + return "\n".join(out for out in outputs if out) class BaseComposeManager(DockerBaseClass): @@ -665,98 +731,115 @@ class BaseComposeManager(DockerBaseClass): self.cleanup_dirs = set() parameters = self.client.module.params - if parameters['definition'] is not None and not HAS_PYYAML: - self.fail( - missing_required_lib('PyYAML'), - exception=PYYAML_IMPORT_ERROR - ) + if parameters["definition"] is not None and not HAS_PYYAML: + self.fail(missing_required_lib("PyYAML"), exception=PYYAML_IMPORT_ERROR) - self.project_name = parameters['project_name'] - if parameters['definition'] is not None: - self.project_src = tempfile.mkdtemp(prefix='ansible') + self.project_name = parameters["project_name"] + if parameters["definition"] is not None: + self.project_src = tempfile.mkdtemp(prefix="ansible") self.cleanup_dirs.add(self.project_src) - compose_file = os.path.join(self.project_src, 'compose.yaml') + compose_file = os.path.join(self.project_src, "compose.yaml") self.client.module.add_cleanup_file(compose_file) try: - with open(compose_file, 'wb') as f: - yaml.dump(parameters['definition'], f, encoding="utf-8", Dumper=_SafeDumper) + with open(compose_file, "wb") as f: + yaml.dump( + parameters["definition"], + f, + encoding="utf-8", + Dumper=_SafeDumper, + ) except Exception as exc: self.fail(f"Error writing to {compose_file} - {exc}") else: - self.project_src = os.path.abspath(parameters['project_src']) + self.project_src = os.path.abspath(parameters["project_src"]) - self.files = parameters['files'] - self.env_files = parameters['env_files'] - self.profiles = parameters['profiles'] + self.files = parameters["files"] + self.env_files = parameters["env_files"] + self.profiles = parameters["profiles"] compose_version = self.get_compose_version() self.compose_version = LooseVersion(compose_version) if self.compose_version < LooseVersion(min_version): - self.fail(f'Docker CLI {self.client.get_cli()} has the compose plugin with version {compose_version}; need version {min_version} or later') + self.fail( + f"Docker CLI {self.client.get_cli()} has the compose plugin with version {compose_version}; need version {min_version} or later" + ) if not os.path.isdir(self.project_src): self.fail(f'"{self.project_src}" is not a directory') - self.check_files_existing = parameters['check_files_existing'] + self.check_files_existing = parameters["check_files_existing"] if self.files: for file in self.files: path = os.path.join(self.project_src, file) if not os.path.exists(path): - self.fail(f'Cannot find Compose file "{file}" relative to project directory "{self.project_src}"') - elif self.check_files_existing and all(not os.path.exists(os.path.join(self.project_src, f)) for f in DOCKER_COMPOSE_FILES): - filenames = ', '.join(DOCKER_COMPOSE_FILES[:-1]) - self.fail(f'"{self.project_src}" does not contain {filenames}, or {DOCKER_COMPOSE_FILES[-1]}') + self.fail( + f'Cannot find Compose file "{file}" relative to project directory "{self.project_src}"' + ) + elif self.check_files_existing and all( + not os.path.exists(os.path.join(self.project_src, f)) + for f in DOCKER_COMPOSE_FILES + ): + filenames = ", ".join(DOCKER_COMPOSE_FILES[:-1]) + self.fail( + f'"{self.project_src}" does not contain {filenames}, or {DOCKER_COMPOSE_FILES[-1]}' + ) # Support for JSON output was added in Compose 2.29.0 (https://github.com/docker/compose/releases/tag/v2.29.0); # more precisely in https://github.com/docker/compose/pull/11478 - self.use_json_events = self.compose_version >= LooseVersion('2.29.0') + self.use_json_events = self.compose_version >= LooseVersion("2.29.0") def get_compose_version(self): - return self.get_compose_version_from_cli() or self.get_compose_version_from_api() + return ( + self.get_compose_version_from_cli() or self.get_compose_version_from_api() + ) def get_compose_version_from_cli(self): - rc, version_info, stderr = self.client.call_cli('compose', 'version', '--format', 'json') + rc, version_info, stderr = self.client.call_cli( + "compose", "version", "--format", "json" + ) if rc: return None try: - version = json.loads(version_info)['version'] - if version == 'dev': + version = json.loads(version_info)["version"] + if version == "dev": return None - return version.lstrip('v') + return version.lstrip("v") except Exception: return None def get_compose_version_from_api(self): - compose = self.client.get_client_plugin_info('compose') + compose = self.client.get_client_plugin_info("compose") if compose is None: - self.fail(f'Docker CLI {self.client.get_cli()} does not have the compose plugin installed') - if compose['Version'] == 'dev': + self.fail( + f"Docker CLI {self.client.get_cli()} does not have the compose plugin installed" + ) + if compose["Version"] == "dev": self.fail( f'Docker CLI {self.client.get_cli()} has a compose plugin installed, but it reports version "dev".' - ' Please use a version of the plugin that returns a proper version.' + " Please use a version of the plugin that returns a proper version." ) - return compose['Version'].lstrip('v') + return compose["Version"].lstrip("v") def fail(self, msg, **kwargs): self.cleanup() self.client.fail(msg, **kwargs) def get_base_args(self, plain_progress=False): - args = ['compose', '--ansi', 'never'] + args = ["compose", "--ansi", "never"] if self.use_json_events and not plain_progress: - args.extend(['--progress', 'json']) - elif self.compose_version >= LooseVersion('2.19.0'): + args.extend(["--progress", "json"]) + elif self.compose_version >= LooseVersion("2.19.0"): # https://github.com/docker/compose/pull/10690 - args.extend(['--progress', 'plain']) - args.extend(['--project-directory', self.project_src]) + args.extend(["--progress", "plain"]) + args.extend(["--project-directory", self.project_src]) if self.project_name: - args.extend(['--project-name', self.project_name]) + args.extend(["--project-name", self.project_name]) for file in self.files or []: - args.extend(['--file', file]) + args.extend(["--file", file]) for env_file in self.env_files or []: - args.extend(['--env-file', env_file]) + args.extend(["--env-file", env_file]) for profile in self.profiles or []: - args.extend(['--profile', profile]) + args.extend(["--profile", profile]) return args def _handle_failed_cli_call(self, args, rc, stdout, stderr): @@ -766,12 +849,12 @@ class BaseComposeManager(DockerBaseClass): self.client.module.exit_json(**result) def list_containers_raw(self): - args = self.get_base_args() + ['ps', '--format', 'json', '--all'] - if self.compose_version >= LooseVersion('2.23.0'): + args = self.get_base_args() + ["ps", "--format", "json", "--all"] + if self.compose_version >= LooseVersion("2.23.0"): # https://github.com/docker/compose/pull/11038 - args.append('--no-trunc') + args.append("--no-trunc") kwargs = dict(cwd=self.project_src, check_rc=not self.use_json_events) - if self.compose_version >= LooseVersion('2.21.0'): + if self.compose_version >= LooseVersion("2.21.0"): # Breaking change in 2.21.0: https://github.com/docker/compose/pull/10918 rc, containers, stderr = self.client.call_cli_json_stream(*args, **kwargs) else: @@ -784,19 +867,21 @@ class BaseComposeManager(DockerBaseClass): result = [] for container in self.list_containers_raw(): labels = {} - if container.get('Labels'): - for part in container['Labels'].split(','): - label_value = part.split('=', 1) - labels[label_value[0]] = label_value[1] if len(label_value) > 1 else '' - container['Labels'] = labels - container['Names'] = container.get('Names', container['Name']).split(',') - container['Networks'] = container.get('Networks', '').split(',') - container['Publishers'] = container.get('Publishers') or [] + if container.get("Labels"): + for part in container["Labels"].split(","): + label_value = part.split("=", 1) + labels[label_value[0]] = ( + label_value[1] if len(label_value) > 1 else "" + ) + container["Labels"] = labels + container["Names"] = container.get("Names", container["Name"]).split(",") + container["Networks"] = container.get("Networks", "").split(",") + container["Publishers"] = container.get("Publishers") or [] result.append(container) return result def list_images(self): - args = self.get_base_args() + ['images', '--format', 'json'] + args = self.get_base_args() + ["images", "--format", "json"] kwargs = dict(cwd=self.project_src, check_rc=not self.use_json_events) rc, images, stderr = self.client.call_cli_json(*args, **kwargs) if self.use_json_events and rc != 0: @@ -811,7 +896,12 @@ class BaseComposeManager(DockerBaseClass): def parse_events(self, stderr, dry_run=False, nonzero_rc=False): if self.use_json_events: return parse_json_events(stderr, warn_function=self.client.warn) - return parse_events(stderr, dry_run=dry_run, warn_function=self.client.warn, nonzero_rc=nonzero_rc) + return parse_events( + stderr, + dry_run=dry_run, + warn_function=self.client.warn, + nonzero_rc=nonzero_rc, + ) def emit_warnings(self, events): emit_warnings(events, warn_function=self.client.warn) @@ -825,14 +915,14 @@ class BaseComposeManager(DockerBaseClass): ignore_service_pull_events=False, ignore_build_events=False, ): - result['changed'] = result.get('changed', False) or has_changes( + result["changed"] = result.get("changed", False) or has_changes( events, ignore_service_pull_events=ignore_service_pull_events, ignore_build_events=ignore_build_events, ) - result['actions'] = result.get('actions', []) + extract_actions(events) - result['stdout'] = combine_text_output(result.get('stdout'), to_native(stdout)) - result['stderr'] = combine_text_output(result.get('stderr'), to_native(stderr)) + result["actions"] = result.get("actions", []) + extract_actions(events) + result["stdout"] = combine_text_output(result.get("stdout"), to_native(stdout)) + result["stderr"] = combine_text_output(result.get("stderr"), to_native(stderr)) def update_failed(self, result, events, args, stdout, stderr, rc): return update_failed( @@ -846,10 +936,10 @@ class BaseComposeManager(DockerBaseClass): ) def cleanup_result(self, result): - if not result.get('failed'): + if not result.get("failed"): # Only return stdout and stderr if it is not empty - for res in ('stdout', 'stderr'): - if result.get(res) == '': + for res in ("stdout", "stderr"): + if result.get(res) == "": result.pop(res) def cleanup(self): diff --git a/plugins/module_utils/copy.py b/plugins/module_utils/copy.py index 92fb52d9..077db76c 100644 --- a/plugins/module_utils/copy.py +++ b/plugins/module_utils/copy.py @@ -15,8 +15,10 @@ import stat import tarfile from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text - -from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, NotFound +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + NotFound, +) class DockerFileCopyError(Exception): @@ -34,52 +36,64 @@ class DockerFileNotFound(DockerFileCopyError): def _put_archive(client, container, path, data): # data can also be file object for streaming. This is because _put uses requests's put(). # See https://requests.readthedocs.io/en/latest/user/advanced/#streaming-uploads - url = client._url('/containers/{0}/archive', container) - res = client._put(url, params={'path': path}, data=data) + url = client._url("/containers/{0}/archive", container) + res = client._put(url, params={"path": path}, data=data) client._raise_for_status(res) return res.status_code == 200 -def _symlink_tar_creator(b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None): +def _symlink_tar_creator( + b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None +): if not stat.S_ISLNK(file_stat.st_mode): - raise DockerUnexpectedError('stat information is not for a symlink') + raise DockerUnexpectedError("stat information is not for a symlink") bio = io.BytesIO() - with tarfile.open(fileobj=bio, mode='w|', dereference=False, encoding='utf-8') as tar: + with tarfile.open( + fileobj=bio, mode="w|", dereference=False, encoding="utf-8" + ) as tar: # Note that without both name (bytes) and arcname (unicode), this either fails for # Python 2.7, Python 3.5/3.6, or Python 3.7+. Only when passing both (in this # form) it works with Python 2.7, 3.5, 3.6, and 3.7 up to 3.11 tarinfo = tar.gettarinfo(b_in_path, arcname=to_text(out_file)) tarinfo.uid = user_id - tarinfo.uname = '' + tarinfo.uname = "" if user_name: tarinfo.uname = user_name tarinfo.gid = group_id - tarinfo.gname = '' + tarinfo.gname = "" tarinfo.mode &= 0o700 if mode is not None: tarinfo.mode = mode if not tarinfo.issym(): - raise DockerUnexpectedError('stat information is not for a symlink') + raise DockerUnexpectedError("stat information is not for a symlink") tar.addfile(tarinfo) return bio.getvalue() -def _symlink_tar_generator(b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None): - yield _symlink_tar_creator(b_in_path, file_stat, out_file, user_id, group_id, mode, user_name) +def _symlink_tar_generator( + b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None +): + yield _symlink_tar_creator( + b_in_path, file_stat, out_file, user_id, group_id, mode, user_name + ) -def _regular_file_tar_generator(b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None): +def _regular_file_tar_generator( + b_in_path, file_stat, out_file, user_id, group_id, mode=None, user_name=None +): if not stat.S_ISREG(file_stat.st_mode): - raise DockerUnexpectedError('stat information is not for a regular file') + raise DockerUnexpectedError("stat information is not for a regular file") tarinfo = tarfile.TarInfo() - tarinfo.name = os.path.splitdrive(to_text(out_file))[1].replace(os.sep, '/').lstrip('/') + tarinfo.name = ( + os.path.splitdrive(to_text(out_file))[1].replace(os.sep, "/").lstrip("/") + ) tarinfo.mode = (file_stat.st_mode & 0o700) if mode is None else mode tarinfo.uid = user_id tarinfo.gid = group_id tarinfo.size = file_stat.st_size tarinfo.mtime = file_stat.st_mtime tarinfo.type = tarfile.REGTYPE - tarinfo.linkname = '' + tarinfo.linkname = "" if user_name: tarinfo.uname = user_name @@ -89,7 +103,7 @@ def _regular_file_tar_generator(b_in_path, file_stat, out_file, user_id, group_i size = tarinfo.size total_size += size - with open(b_in_path, 'rb') as f: + with open(b_in_path, "rb") as f: while size > 0: to_read = min(size, 65536) buf = f.read(to_read) @@ -117,16 +131,20 @@ def _regular_file_tar_generator(b_in_path, file_stat, out_file, user_id, group_i yield tarfile.NUL * (tarfile.RECORDSIZE - remainder) -def _regular_content_tar_generator(content, out_file, user_id, group_id, mode, user_name=None): +def _regular_content_tar_generator( + content, out_file, user_id, group_id, mode, user_name=None +): tarinfo = tarfile.TarInfo() - tarinfo.name = os.path.splitdrive(to_text(out_file))[1].replace(os.sep, '/').lstrip('/') + tarinfo.name = ( + os.path.splitdrive(to_text(out_file))[1].replace(os.sep, "/").lstrip("/") + ) tarinfo.mode = mode tarinfo.uid = user_id tarinfo.gid = group_id tarinfo.size = len(content) tarinfo.mtime = int(datetime.datetime.now().timestamp()) tarinfo.type = tarfile.REGTYPE - tarinfo.linkname = '' + tarinfo.linkname = "" if user_name: tarinfo.uname = user_name @@ -152,13 +170,22 @@ def _regular_content_tar_generator(content, out_file, user_id, group_id, mode, u yield tarfile.NUL * (tarfile.RECORDSIZE - remainder) -def put_file(client, container, in_path, out_path, user_id, group_id, mode=None, user_name=None, follow_links=False): +def put_file( + client, + container, + in_path, + out_path, + user_id, + group_id, + mode=None, + user_name=None, + follow_links=False, +): """Transfer a file from local to Docker container.""" - if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')): - raise DockerFileNotFound( - f"file or module does not exist: {to_native(in_path)}") + if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")): + raise DockerFileNotFound(f"file or module does not exist: {to_native(in_path)}") - b_in_path = to_bytes(in_path, errors='surrogate_or_strict') + b_in_path = to_bytes(in_path, errors="surrogate_or_strict") out_dir, out_file = os.path.split(out_path) @@ -168,28 +195,53 @@ def put_file(client, container, in_path, out_path, user_id, group_id, mode=None, file_stat = os.lstat(b_in_path) if stat.S_ISREG(file_stat.st_mode): - stream = _regular_file_tar_generator(b_in_path, file_stat, out_file, user_id, group_id, mode=mode, user_name=user_name) + stream = _regular_file_tar_generator( + b_in_path, + file_stat, + out_file, + user_id, + group_id, + mode=mode, + user_name=user_name, + ) elif stat.S_ISLNK(file_stat.st_mode): - stream = _symlink_tar_generator(b_in_path, file_stat, out_file, user_id, group_id, mode=mode, user_name=user_name) + stream = _symlink_tar_generator( + b_in_path, + file_stat, + out_file, + user_id, + group_id, + mode=mode, + user_name=user_name, + ) else: - file_part = ' referenced by' if follow_links else '' + file_part = " referenced by" if follow_links else "" raise DockerFileCopyError( - f'File{file_part} {in_path} is neither a regular file nor a symlink (stat mode {oct(file_stat.st_mode)}).') + f"File{file_part} {in_path} is neither a regular file nor a symlink (stat mode {oct(file_stat.st_mode)})." + ) ok = _put_archive(client, container, out_dir, stream) if not ok: - raise DockerUnexpectedError(f'Unknown error while creating file "{out_path}" in container "{container}".') + raise DockerUnexpectedError( + f'Unknown error while creating file "{out_path}" in container "{container}".' + ) -def put_file_content(client, container, content, out_path, user_id, group_id, mode, user_name=None): +def put_file_content( + client, container, content, out_path, user_id, group_id, mode, user_name=None +): """Transfer a file from local to Docker container.""" out_dir, out_file = os.path.split(out_path) - stream = _regular_content_tar_generator(content, out_file, user_id, group_id, mode, user_name=user_name) + stream = _regular_content_tar_generator( + content, out_file, user_id, group_id, mode, user_name=user_name + ) ok = _put_archive(client, container, out_dir, stream) if not ok: - raise DockerUnexpectedError(f'Unknown error while creating file "{out_path}" in container "{container}".') + raise DockerUnexpectedError( + f'Unknown error while creating file "{out_path}" in container "{container}".' + ) def stat_file(client, container, in_path, follow_links=False, log=None): @@ -208,30 +260,32 @@ def stat_file(client, container, in_path, follow_links=False, log=None): while True: if in_path in considered_in_paths: - raise DockerFileCopyError(f'Found infinite symbolic link loop when trying to stating "{in_path}"') + raise DockerFileCopyError( + f'Found infinite symbolic link loop when trying to stating "{in_path}"' + ) considered_in_paths.add(in_path) if log: log(f'FETCH: Stating "{in_path}"') response = client._head( - client._url('/containers/{0}/archive', container), - params={'path': in_path}, + client._url("/containers/{0}/archive", container), + params={"path": in_path}, ) if response.status_code == 404: return in_path, None, None client._raise_for_status(response) - header = response.headers.get('x-docker-container-path-stat') + header = response.headers.get("x-docker-container-path-stat") try: stat_data = json.loads(base64.b64decode(header)) except Exception as exc: raise DockerUnexpectedError( - f'When retrieving information for {in_path} from {container}, obtained header {header!r} that cannot be loaded as JSON: {exc}' + f"When retrieving information for {in_path} from {container}, obtained header {header!r} that cannot be loaded as JSON: {exc}" ) # https://pkg.go.dev/io/fs#FileMode: bit 32 - 5 means ModeSymlink - if stat_data['mode'] & (1 << (32 - 5)) != 0: - link_target = stat_data['linkTarget'] + if stat_data["mode"] & (1 << (32 - 5)) != 0: + link_target = stat_data["linkTarget"] if not follow_links: return in_path, stat_data, link_target in_path = os.path.join(os.path.split(in_path)[0], link_target) @@ -243,7 +297,7 @@ def stat_file(client, container, in_path, follow_links=False, log=None): class _RawGeneratorFileobj(io.RawIOBase): def __init__(self, stream): self._stream = stream - self._buf = b'' + self._buf = b"" def readable(self): return True @@ -251,7 +305,7 @@ class _RawGeneratorFileobj(io.RawIOBase): def _readinto_from_buf(self, b, index, length): cpy = min(length - index, len(self._buf)) if cpy: - b[index:index + cpy] = self._buf[:cpy] + b[index : index + cpy] = self._buf[:cpy] self._buf = self._buf[cpy:] index += cpy return index @@ -273,38 +327,55 @@ class _RawGeneratorFileobj(io.RawIOBase): def _stream_generator_to_fileobj(stream): - '''Given a generator that generates chunks of bytes, create a readable buffered stream.''' + """Given a generator that generates chunks of bytes, create a readable buffered stream.""" raw = _RawGeneratorFileobj(stream) return io.BufferedReader(raw) -def fetch_file_ex(client, container, in_path, process_none, process_regular, process_symlink, process_other, follow_links=False, log=None): +def fetch_file_ex( + client, + container, + in_path, + process_none, + process_regular, + process_symlink, + process_other, + follow_links=False, + log=None, +): """Fetch a file (as a tar file entry) from a Docker container to local.""" considered_in_paths = set() while True: if in_path in considered_in_paths: - raise DockerFileCopyError(f'Found infinite symbolic link loop when trying to fetch "{in_path}"') + raise DockerFileCopyError( + f'Found infinite symbolic link loop when trying to fetch "{in_path}"' + ) considered_in_paths.add(in_path) if log: log(f'FETCH: Fetching "{in_path}"') try: stream = client.get_raw_stream( - '/containers/{0}/archive', container, - params={'path': in_path}, - headers={'Accept-Encoding': 'identity'}, + "/containers/{0}/archive", + container, + params={"path": in_path}, + headers={"Accept-Encoding": "identity"}, ) except NotFound: return process_none(in_path) - with tarfile.open(fileobj=_stream_generator_to_fileobj(stream), mode='r|') as tar: + with tarfile.open( + fileobj=_stream_generator_to_fileobj(stream), mode="r|" + ) as tar: symlink_member = None result = None found = False for member in tar: if found: - raise DockerUnexpectedError('Received tarfile contains more than one file!') + raise DockerUnexpectedError( + "Received tarfile contains more than one file!" + ) found = True if member.issym(): symlink_member = member @@ -316,21 +387,23 @@ def fetch_file_ex(client, container, in_path, process_none, process_regular, pro if symlink_member: if not follow_links: return process_symlink(in_path, symlink_member) - in_path = os.path.join(os.path.split(in_path)[0], symlink_member.linkname) + in_path = os.path.join( + os.path.split(in_path)[0], symlink_member.linkname + ) if log: log(f'FETCH: Following symbolic link to "{in_path}"') continue if found: return result - raise DockerUnexpectedError('Received tarfile is empty!') + raise DockerUnexpectedError("Received tarfile is empty!") def fetch_file(client, container, in_path, out_path, follow_links=False, log=None): - b_out_path = to_bytes(out_path, errors='surrogate_or_strict') + b_out_path = to_bytes(out_path, errors="surrogate_or_strict") def process_none(in_path): raise DockerFileNotFound( - f'File {in_path} does not exist in container {container}' + f"File {in_path} does not exist in container {container}" ) def process_regular(in_path, tar, member): @@ -338,7 +411,7 @@ def fetch_file(client, container, in_path, out_path, follow_links=False, log=Non os.unlink(b_out_path) with tar.extractfile(member) as in_f: - with open(b_out_path, 'wb') as out_f: + with open(b_out_path, "wb") as out_f: shutil.copyfileobj(in_f, out_f) return in_path @@ -350,56 +423,71 @@ def fetch_file(client, container, in_path, out_path, follow_links=False, log=Non return in_path def process_other(in_path, member): - raise DockerFileCopyError(f'Remote file "{in_path}" is not a regular file or a symbolic link') + raise DockerFileCopyError( + f'Remote file "{in_path}" is not a regular file or a symbolic link' + ) - return fetch_file_ex(client, container, in_path, process_none, process_regular, process_symlink, process_other, follow_links=follow_links, log=log) + return fetch_file_ex( + client, + container, + in_path, + process_none, + process_regular, + process_symlink, + process_other, + follow_links=follow_links, + log=log, + ) def _execute_command(client, container, command, log=None, check_rc=False): if log: - log(f'Executing {command} in {container}') + log(f"Executing {command} in {container}") data = { - 'Container': container, - 'User': '', - 'Privileged': False, - 'Tty': False, - 'AttachStdin': False, - 'AttachStdout': True, - 'AttachStderr': True, - 'Cmd': command, + "Container": container, + "User": "", + "Privileged": False, + "Tty": False, + "AttachStdin": False, + "AttachStdout": True, + "AttachStderr": True, + "Cmd": command, } - if 'detachKeys' in client._general_configs: - data['detachKeys'] = client._general_configs['detachKeys'] + if "detachKeys" in client._general_configs: + data["detachKeys"] = client._general_configs["detachKeys"] try: - exec_data = client.post_json_to_json('/containers/{0}/exec', container, data=data) + exec_data = client.post_json_to_json( + "/containers/{0}/exec", container, data=data + ) except NotFound as e: raise DockerFileCopyError(f'Could not find container "{container}"') from e except APIError as e: if e.response is not None and e.response.status_code == 409: - raise DockerFileCopyError(f'Cannot execute command in paused container "{container}"') from e + raise DockerFileCopyError( + f'Cannot execute command in paused container "{container}"' + ) from e raise - exec_id = exec_data['Id'] + exec_id = exec_data["Id"] - data = { - 'Tty': False, - 'Detach': False - } - stdout, stderr = client.post_json_to_stream('/exec/{0}/start', exec_id, stream=False, demux=True, tty=False) + data = {"Tty": False, "Detach": False} + stdout, stderr = client.post_json_to_stream( + "/exec/{0}/start", exec_id, stream=False, demux=True, tty=False + ) - result = client.get_json('/exec/{0}/json', exec_id) + result = client.get_json("/exec/{0}/json", exec_id) - rc = result.get('ExitCode') or 0 - stdout = stdout or b'' - stderr = stderr or b'' + rc = result.get("ExitCode") or 0 + stdout = stdout or b"" + stderr = stderr or b"" if log: - log(f'Exit code {rc}, stdout {stdout!r}, stderr {stderr!r}') + log(f"Exit code {rc}, stdout {stdout!r}, stderr {stderr!r}") if check_rc and rc != 0: - command_str = ' '.join(command) + command_str = " ".join(command) raise DockerUnexpectedError( f'Obtained unexpected exit code {rc} when running "{command_str}" in {container}.\nSTDOUT: {stdout}\nSTDERR: {stderr}' ) @@ -408,12 +496,14 @@ def _execute_command(client, container, command, log=None, check_rc=False): def determine_user_group(client, container, log=None): - dummy, stdout, stderr = _execute_command(client, container, ['/bin/sh', '-c', 'id -u && id -g'], check_rc=True, log=log) + dummy, stdout, stderr = _execute_command( + client, container, ["/bin/sh", "-c", "id -u && id -g"], check_rc=True, log=log + ) stdout_lines = stdout.splitlines() if len(stdout_lines) != 2: raise DockerUnexpectedError( - f'Expected two-line output to obtain user and group ID for container {container}, but got {len(stdout_lines)} lines:\n{stdout}' + f"Expected two-line output to obtain user and group ID for container {container}, but got {len(stdout_lines)} lines:\n{stdout}" ) user_id, group_id = stdout_lines diff --git a/plugins/module_utils/image_archive.py b/plugins/module_utils/image_archive.py index 1e700261..7e3a47db 100644 --- a/plugins/module_utils/image_archive.py +++ b/plugins/module_utils/image_archive.py @@ -10,18 +10,18 @@ import tarfile class ImageArchiveManifestSummary(object): - ''' + """ Represents data extracted from a manifest.json found in the tar archive output of the "docker image save some:tag > some.tar" command. - ''' + """ def __init__(self, image_id, repo_tags): - ''' + """ :param image_id: File name portion of Config entry, e.g. abcde12345 from abcde12345.json :type image_id: str :param repo_tags Docker image names, e.g. ["hello-world:latest"] :type repo_tags: list[str] - ''' + """ self.image_id = image_id self.repo_tags = repo_tags @@ -32,7 +32,7 @@ class ImageArchiveInvalidException(Exception): def api_image_id(archive_image_id): - ''' + """ Accepts an image hash in the format stored in manifest.json, and returns an equivalent identifier that represents the same image hash, but in the format presented by the Docker Engine API. @@ -41,13 +41,13 @@ def api_image_id(archive_image_id): :returns: Prefixed hash used by REST api :rtype: str - ''' + """ - return f'sha256:{archive_image_id}' + return f"sha256:{archive_image_id}" def load_archived_image_manifest(archive_path): - ''' + """ Attempts to get image IDs and image names from metadata stored in the image archive tar file. @@ -63,17 +63,17 @@ def load_archived_image_manifest(archive_path): :return: None, if no file at archive_path, or a list of ImageArchiveManifestSummary objects. :rtype: ImageArchiveManifestSummary - ''' + """ try: # FileNotFoundError does not exist in Python 2 if not os.path.isfile(archive_path): return None - with tarfile.open(archive_path, 'r') as tf: + with tarfile.open(archive_path, "r") as tf: try: try: - with tf.extractfile('manifest.json') as ef: + with tf.extractfile("manifest.json") as ef: manifest = json.load(ef) except Exception as exc: raise ImageArchiveInvalidException( @@ -88,7 +88,7 @@ def load_archived_image_manifest(archive_path): result = [] for index, meta in enumerate(manifest): try: - config_file = meta['Config'] + config_file = meta["Config"] except KeyError as exc: raise ImageArchiveInvalidException( f"Failed to get Config entry from {index + 1}th manifest in manifest.json: {exc}" @@ -103,23 +103,22 @@ def load_archived_image_manifest(archive_path): f"Failed to extract image id from config file name {config_file}: {exc}" ) from exc - for prefix in ( - 'blobs/sha256/', # Moby 25.0.0, Docker API 1.44 - ): + for prefix in ("blobs/sha256/",): # Moby 25.0.0, Docker API 1.44 if image_id.startswith(prefix): - image_id = image_id[len(prefix):] + image_id = image_id[len(prefix) :] try: - repo_tags = meta['RepoTags'] + repo_tags = meta["RepoTags"] except KeyError as exc: raise ImageArchiveInvalidException( f"Failed to get RepoTags entry from {index + 1}th manifest in manifest.json: {exc}" ) from exc - result.append(ImageArchiveManifestSummary( - image_id=image_id, - repo_tags=repo_tags - )) + result.append( + ImageArchiveManifestSummary( + image_id=image_id, repo_tags=repo_tags + ) + ) return result except ImageArchiveInvalidException: @@ -132,11 +131,13 @@ def load_archived_image_manifest(archive_path): except ImageArchiveInvalidException: raise except Exception as exc: - raise ImageArchiveInvalidException(f"Failed to open tar file {archive_path}: {exc}") from exc + raise ImageArchiveInvalidException( + f"Failed to open tar file {archive_path}: {exc}" + ) from exc def archived_image_manifest(archive_path): - ''' + """ Attempts to get Image.Id and image name from metadata stored in the image archive tar file. @@ -152,7 +153,7 @@ def archived_image_manifest(archive_path): :return: None, if no file at archive_path, or the extracted image ID, which will not have a sha256: prefix. :rtype: ImageArchiveManifestSummary - ''' + """ results = load_archived_image_manifest(archive_path) if results is None: diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index c550c034..61e3e84f 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -9,12 +9,16 @@ import abc import os import re import shlex - from functools import partial from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.common.text.formatters import human_to_bytes - +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_env_file, +) +from ansible_collections.community.docker.plugins.module_utils._platform import ( + compare_platform_strings, +) from ansible_collections.community.docker.plugins.module_utils.util import ( clean_dict_booleans_for_docker_api, compare_generic, @@ -23,39 +27,33 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( sanitize_labels, ) -from ansible_collections.community.docker.plugins.module_utils._platform import ( - compare_platform_strings, + +_DEFAULT_IP_REPLACEMENT_STRING = ( + "[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7xaekoh8Geingunaesaeweiy3ooleiwi]]" ) -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_env_file, -) - - -_DEFAULT_IP_REPLACEMENT_STRING = '[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7xaekoh8Geingunaesaeweiy3ooleiwi]]' - _MOUNT_OPTION_TYPES = dict( - create_mountpoint=('bind',), - labels=('volume',), - no_copy=('volume',), - non_recursive=('bind',), - propagation=('bind',), - read_only_force_recursive=('bind',), - read_only_non_recursive=('bind',), - subpath=('volume', 'image'), - tmpfs_size=('tmpfs',), - tmpfs_mode=('tmpfs',), - tmpfs_options=('tmpfs',), - volume_driver=('volume',), - volume_options=('volume',), + create_mountpoint=("bind",), + labels=("volume",), + no_copy=("volume",), + non_recursive=("bind",), + propagation=("bind",), + read_only_force_recursive=("bind",), + read_only_non_recursive=("bind",), + subpath=("volume", "image"), + tmpfs_size=("tmpfs",), + tmpfs_mode=("tmpfs",), + tmpfs_options=("tmpfs",), + volume_driver=("volume",), + volume_options=("volume",), ) def _get_ansible_type(type): - if type == 'set': - return 'list' - if type not in ('list', 'dict', 'bool', 'int', 'float', 'str'): + if type == "set": + return "list" + if type not in ("list", "dict", "bool", "int", "float", "str"): raise Exception(f'Invalid type "{type}"') return type @@ -82,45 +80,68 @@ class Option(object): self.name = name self.type = type self.ansible_type = ansible_type or _get_ansible_type(type) - needs_elements = self.type in ('list', 'set') - needs_ansible_elements = self.ansible_type in ('list', ) + needs_elements = self.type in ("list", "set") + needs_ansible_elements = self.ansible_type in ("list",) if elements is not None and not needs_elements: - raise Exception('elements only allowed for lists/sets') + raise Exception("elements only allowed for lists/sets") if elements is None and needs_elements: - raise Exception('elements required for lists/sets') + raise Exception("elements required for lists/sets") if ansible_elements is not None and not needs_ansible_elements: - raise Exception('Ansible elements only allowed for Ansible lists') + raise Exception("Ansible elements only allowed for Ansible lists") if (elements is None and ansible_elements is None) and needs_ansible_elements: - raise Exception('Ansible elements required for Ansible lists') + raise Exception("Ansible elements required for Ansible lists") self.elements = elements if needs_elements else None - self.ansible_elements = (ansible_elements or _get_ansible_type(elements)) if needs_ansible_elements else None - needs_suboptions = (self.ansible_type == 'list' and self.ansible_elements == 'dict') or (self.ansible_type == 'dict') + self.ansible_elements = ( + (ansible_elements or _get_ansible_type(elements)) + if needs_ansible_elements + else None + ) + needs_suboptions = ( + self.ansible_type == "list" and self.ansible_elements == "dict" + ) or (self.ansible_type == "dict") if ansible_suboptions is not None and not needs_suboptions: - raise Exception('suboptions only allowed for Ansible lists with dicts, or Ansible dicts') - if ansible_suboptions is None and needs_suboptions and not needs_no_suboptions and not not_an_ansible_option: - raise Exception('suboptions required for Ansible lists with dicts, or Ansible dicts') + raise Exception( + "suboptions only allowed for Ansible lists with dicts, or Ansible dicts" + ) + if ( + ansible_suboptions is None + and needs_suboptions + and not needs_no_suboptions + and not not_an_ansible_option + ): + raise Exception( + "suboptions required for Ansible lists with dicts, or Ansible dicts" + ) self.ansible_suboptions = ansible_suboptions if needs_suboptions else None self.ansible_aliases = ansible_aliases or [] self.ansible_choices = ansible_choices comparison_type = self.type - if comparison_type == 'set' and self.elements == 'dict': - comparison_type = 'set(dict)' - elif comparison_type not in ('set', 'list', 'dict'): - comparison_type = 'value' + if comparison_type == "set" and self.elements == "dict": + comparison_type = "set(dict)" + elif comparison_type not in ("set", "list", "dict"): + comparison_type = "value" self.comparison_type = comparison_type if default_comparison is not None: self.comparison = default_comparison - elif comparison_type in ('list', 'value'): - self.comparison = 'strict' + elif comparison_type in ("list", "value"): + self.comparison = "strict" else: - self.comparison = 'allow_more_present' + self.comparison = "allow_more_present" self.not_a_container_option = not_a_container_option self.not_an_ansible_option = not_an_ansible_option self.copy_comparison_from = copy_comparison_from self.compare = ( - lambda param_value, container_value: compare(self, param_value, container_value) - ) if compare else ( - lambda param_value, container_value: compare_generic(param_value, container_value, self.comparison, self.comparison_type) + ( + lambda param_value, container_value: compare( + self, param_value, container_value + ) + ) + if compare + else ( + lambda param_value, container_value: compare_generic( + param_value, container_value, self.comparison, self.comparison_type + ) + ) ) @@ -135,8 +156,10 @@ class OptionGroup(object): ansible_required_by=None, ): if preprocess is None: + def preprocess(module, values): return values + self.preprocess = preprocess self.options = [] self.all_options = [] @@ -155,16 +178,16 @@ class OptionGroup(object): self.all_options.append(option) if not option.not_an_ansible_option: ansible_option = { - 'type': option.ansible_type, + "type": option.ansible_type, } if option.ansible_elements is not None: - ansible_option['elements'] = option.ansible_elements + ansible_option["elements"] = option.ansible_elements if option.ansible_suboptions is not None: - ansible_option['options'] = option.ansible_suboptions + ansible_option["options"] = option.ansible_suboptions if option.ansible_aliases: - ansible_option['aliases'] = option.ansible_aliases + ansible_option["aliases"] = option.ansible_aliases if option.ansible_choices is not None: - ansible_option['choices'] = option.ansible_choices + ansible_option["choices"] = option.ansible_choices self.argument_spec[option.name] = ansible_option return self @@ -196,11 +219,22 @@ class Engine(object): pass @abc.abstractmethod - def get_expected_values(self, module, client, api_version, options, image, values, host_info): + def get_expected_values( + self, module, client, api_version, options, image, values, host_info + ): pass @abc.abstractmethod - def ignore_mismatching_result(self, module, client, api_version, option, image, container_value, expected_value): + def ignore_mismatching_result( + self, + module, + client, + api_version, + option, + image, + container_value, + expected_value, + ): pass @abc.abstractmethod @@ -232,7 +266,15 @@ class EngineDriver(object): name = None # string @abc.abstractmethod - def setup(self, argument_spec, mutually_exclusive=None, required_together=None, required_one_of=None, required_if=None, required_by=None): + def setup( + self, + argument_spec, + mutually_exclusive=None, + required_together=None, + required_one_of=None, + required_if=None, + required_by=None, + ): # Return (module, active_options, client) pass @@ -301,14 +343,18 @@ class EngineDriver(object): pass @abc.abstractmethod - def connect_container_to_network(self, client, container_id, network_id, parameters=None): + def connect_container_to_network( + self, client, container_id, network_id, parameters=None + ): pass def create_container_supports_more_than_one_network(self, client): return False @abc.abstractmethod - def create_container(self, client, container_name, create_parameters, networks=None): + def create_container( + self, client, container_name, create_parameters, networks=None + ): pass @abc.abstractmethod @@ -340,7 +386,9 @@ class EngineDriver(object): pass @abc.abstractmethod - def remove_container(self, client, container_id, remove_volumes=False, link=False, force=False): + def remove_container( + self, client, container_id, remove_volumes=False, link=False, force=False + ): pass @abc.abstractmethod @@ -349,21 +397,36 @@ class EngineDriver(object): def _is_volume_permissions(mode): - for part in mode.split(','): - if part not in ('rw', 'ro', 'z', 'Z', 'consistent', 'delegated', 'cached', 'rprivate', 'private', 'rshared', 'shared', 'rslave', 'slave', 'nocopy'): + for part in mode.split(","): + if part not in ( + "rw", + "ro", + "z", + "Z", + "consistent", + "delegated", + "cached", + "rprivate", + "private", + "rshared", + "shared", + "rslave", + "slave", + "nocopy", + ): return False return True def _parse_port_range(range_or_port, module): - ''' + """ Parses a string containing either a single port or a range of ports. Returns a list of integers for each port in the list. - ''' - if '-' in range_or_port: + """ + if "-" in range_or_port: try: - start, end = [int(port) for port in range_or_port.split('-')] + start, end = [int(port) for port in range_or_port.split("-")] except Exception: module.fail_json(msg=f'Invalid port range: "{range_or_port}"') if end < start: @@ -377,75 +440,81 @@ def _parse_port_range(range_or_port, module): def _split_colon_ipv6(text, module): - ''' + """ Split string by ':', while keeping IPv6 addresses in square brackets in one component. - ''' - if '[' not in text: - return text.split(':') + """ + if "[" not in text: + return text.split(":") start = 0 result = [] while start < len(text): - i = text.find('[', start) + i = text.find("[", start) if i < 0: - result.extend(text[start:].split(':')) + result.extend(text[start:].split(":")) break - j = text.find(']', i) + j = text.find("]", i) if j < 0: - module.fail_json(msg=f'Cannot find closing "]" in input "{text}" for opening "[" at index {i + 1}!') - result.extend(text[start:i].split(':')) - k = text.find(':', j) + module.fail_json( + msg=f'Cannot find closing "]" in input "{text}" for opening "[" at index {i + 1}!' + ) + result.extend(text[start:i].split(":")) + k = text.find(":", j) if k < 0: result[-1] += text[i:] start = len(text) else: result[-1] += text[i:k] if k == len(text): - result.append('') + result.append("") break start = k + 1 return result def _preprocess_command(module, values): - if 'command' not in values: + if "command" not in values: return values - value = values['command'] - if module.params['command_handling'] == 'correct': + value = values["command"] + if module.params["command_handling"] == "correct": if value is not None: if not isinstance(value, list): # convert from str to list - value = shlex.split(to_text(value, errors='surrogate_or_strict')) - value = [to_text(x, errors='surrogate_or_strict') for x in value] + value = shlex.split(to_text(value, errors="surrogate_or_strict")) + value = [to_text(x, errors="surrogate_or_strict") for x in value] elif value: # convert from list to str if isinstance(value, list): - value = shlex.split(' '.join([to_text(x, errors='surrogate_or_strict') for x in value])) - value = [to_text(x, errors='surrogate_or_strict') for x in value] + value = shlex.split( + " ".join([to_text(x, errors="surrogate_or_strict") for x in value]) + ) + value = [to_text(x, errors="surrogate_or_strict") for x in value] else: - value = shlex.split(to_text(value, errors='surrogate_or_strict')) - value = [to_text(x, errors='surrogate_or_strict') for x in value] + value = shlex.split(to_text(value, errors="surrogate_or_strict")) + value = [to_text(x, errors="surrogate_or_strict") for x in value] else: return {} return { - 'command': value, + "command": value, } def _preprocess_entrypoint(module, values): - if 'entrypoint' not in values: + if "entrypoint" not in values: return values - value = values['entrypoint'] - if module.params['command_handling'] == 'correct': + value = values["entrypoint"] + if module.params["command_handling"] == "correct": if value is not None: - value = [to_text(x, errors='surrogate_or_strict') for x in value] + value = [to_text(x, errors="surrogate_or_strict") for x in value] elif value: # convert from list to str. - value = shlex.split(' '.join([to_text(x, errors='surrogate_or_strict') for x in value])) - value = [to_text(x, errors='surrogate_or_strict') for x in value] + value = shlex.split( + " ".join([to_text(x, errors="surrogate_or_strict") for x in value]) + ) + value = [to_text(x, errors="surrogate_or_strict") for x in value] else: return {} return { - 'entrypoint': value, + "entrypoint": value, } @@ -453,21 +522,23 @@ def _preprocess_env(module, values): if not values: return {} final_env = {} - if 'env_file' in values: - parsed_env_file = parse_env_file(values['env_file']) + if "env_file" in values: + parsed_env_file = parse_env_file(values["env_file"]) for name, value in parsed_env_file.items(): - final_env[name] = to_text(value, errors='surrogate_or_strict') - if 'env' in values: - for name, value in values['env'].items(): + final_env[name] = to_text(value, errors="surrogate_or_strict") + if "env" in values: + for name, value in values["env"].items(): if not isinstance(value, str): - module.fail_json(msg='Non-string value found for env option. Ambiguous env options must be ' - f'wrapped in quotes to avoid them being interpreted. Key: {name}') - final_env[name] = to_text(value, errors='surrogate_or_strict') + module.fail_json( + msg="Non-string value found for env option. Ambiguous env options must be " + f"wrapped in quotes to avoid them being interpreted. Key: {name}" + ) + final_env[name] = to_text(value, errors="surrogate_or_strict") formatted_env = [] for key, value in final_env.items(): - formatted_env.append(f'{key}={value}') + formatted_env.append(f"{key}={value}") return { - 'env': formatted_env, + "env": formatted_env, } @@ -475,7 +546,9 @@ def _preprocess_healthcheck(module, values): if not values: return {} return { - 'healthcheck': normalize_healthcheck(values['healthcheck'], normalize_test=False), + "healthcheck": normalize_healthcheck( + values["healthcheck"], normalize_test=False + ), } @@ -484,84 +557,86 @@ def _preprocess_convert_to_bytes(module, values, name, unlimited_value=None): return values try: value = values[name] - if unlimited_value is not None and value in ('unlimited', str(unlimited_value)): + if unlimited_value is not None and value in ("unlimited", str(unlimited_value)): value = unlimited_value else: value = human_to_bytes(value) values[name] = value return values except ValueError as exc: - module.fail_json(msg=f'Failed to convert {name} to bytes: {exc}') + module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}") def _preprocess_mac_address(module, values): - if 'mac_address' not in values: + if "mac_address" not in values: return values return { - 'mac_address': values['mac_address'].replace('-', ':'), + "mac_address": values["mac_address"].replace("-", ":"), } def _preprocess_networks(module, values): - if module.params['networks_cli_compatible'] is True and values.get('networks') and 'network_mode' not in values: + if ( + module.params["networks_cli_compatible"] is True + and values.get("networks") + and "network_mode" not in values + ): # Same behavior as Docker CLI: if networks are specified, use the name of the first network as the value for network_mode # (assuming no explicit value is specified for network_mode) - values['network_mode'] = values['networks'][0]['name'] + values["network_mode"] = values["networks"][0]["name"] - if 'networks' in values: - for network in values['networks']: - if network['links']: + if "networks" in values: + for network in values["networks"]: + if network["links"]: parsed_links = [] - for link in network['links']: - parsed_link = link.split(':', 1) + for link in network["links"]: + parsed_link = link.split(":", 1) if len(parsed_link) == 1: parsed_link = (link, link) parsed_links.append(tuple(parsed_link)) - network['links'] = parsed_links - if network['mac_address']: - network['mac_address'] = network['mac_address'].replace('-', ':') + network["links"] = parsed_links + if network["mac_address"]: + network["mac_address"] = network["mac_address"].replace("-", ":") return values def _preprocess_sysctls(module, values): - if 'sysctls' in values: - for key, value in values['sysctls'].items(): - values['sysctls'][key] = to_text(value, errors='surrogate_or_strict') + if "sysctls" in values: + for key, value in values["sysctls"].items(): + values["sysctls"][key] = to_text(value, errors="surrogate_or_strict") return values def _preprocess_tmpfs(module, values): - if 'tmpfs' not in values: + if "tmpfs" not in values: return values result = {} - for tmpfs_spec in values['tmpfs']: + for tmpfs_spec in values["tmpfs"]: split_spec = tmpfs_spec.split(":", 1) if len(split_spec) > 1: result[split_spec[0]] = split_spec[1] else: result[split_spec[0]] = "" - return { - 'tmpfs': result - } + return {"tmpfs": result} def _preprocess_ulimits(module, values): - if 'ulimits' not in values: + if "ulimits" not in values: return values result = [] - for limit in values['ulimits']: + for limit in values["ulimits"]: limits = dict() - pieces = limit.split(':') + pieces = limit.split(":") if len(pieces) >= 2: - limits['Name'] = pieces[0] - limits['Soft'] = int(pieces[1]) - limits['Hard'] = int(pieces[1]) + limits["Name"] = pieces[0] + limits["Soft"] = int(pieces[1]) + limits["Hard"] = int(pieces[1]) if len(pieces) == 3: - limits['Hard'] = int(pieces[2]) + limits["Hard"] = int(pieces[2]) result.append(limits) return { - 'ulimits': result, + "ulimits": result, } @@ -571,24 +646,35 @@ def _preprocess_mounts(module, values): def check_collision(t, name): if t in last: if name == last[t]: - module.fail_json(msg=f'The mount point "{t}" appears twice in the {name} option') + module.fail_json( + msg=f'The mount point "{t}" appears twice in the {name} option' + ) else: - module.fail_json(msg=f'The mount point "{t}" appears both in the {name} and {last[t]} option') + module.fail_json( + msg=f'The mount point "{t}" appears both in the {name} and {last[t]} option' + ) last[t] = name - if 'mounts' in values: + if "mounts" in values: mounts = [] - for mount in values['mounts']: - target = mount['target'] - mount_type = mount['type'] + for mount in values["mounts"]: + target = mount["target"] + mount_type = mount["type"] - check_collision(target, 'mounts') + check_collision(target, "mounts") mount_dict = dict(mount) # Sanity checks - if mount['source'] is None and mount_type not in ('tmpfs', 'volume', 'image', 'cluster'): - module.fail_json(msg=f'source must be specified for mount "{target}" of type "{mount_type}"') + if mount["source"] is None and mount_type not in ( + "tmpfs", + "volume", + "image", + "cluster", + ): + module.fail_json( + msg=f'source must be specified for mount "{target}" of type "{mount_type}"' + ) for option, req_mount_types in _MOUNT_OPTION_TYPES.items(): if mount[option] is not None and mount_type not in req_mount_types: type_plural = "" if len(req_mount_types) == 1 else "s" @@ -598,158 +684,185 @@ def _preprocess_mounts(module, values): ) # Streamline options - volume_options = mount_dict.pop('volume_options') - if mount_dict['volume_driver'] and volume_options: - mount_dict['volume_options'] = clean_dict_booleans_for_docker_api(volume_options) - if mount_dict['labels']: - mount_dict['labels'] = clean_dict_booleans_for_docker_api(mount_dict['labels']) - if mount_dict['tmpfs_size'] is not None: + volume_options = mount_dict.pop("volume_options") + if mount_dict["volume_driver"] and volume_options: + mount_dict["volume_options"] = clean_dict_booleans_for_docker_api( + volume_options + ) + if mount_dict["labels"]: + mount_dict["labels"] = clean_dict_booleans_for_docker_api( + mount_dict["labels"] + ) + if mount_dict["tmpfs_size"] is not None: try: - mount_dict['tmpfs_size'] = human_to_bytes(mount_dict['tmpfs_size']) + mount_dict["tmpfs_size"] = human_to_bytes(mount_dict["tmpfs_size"]) except ValueError as exc: - module.fail_json(msg=f'Failed to convert tmpfs_size of mount "{target}" to bytes: {exc}') - if mount_dict['tmpfs_mode'] is not None: + module.fail_json( + msg=f'Failed to convert tmpfs_size of mount "{target}" to bytes: {exc}' + ) + if mount_dict["tmpfs_mode"] is not None: try: - mount_dict['tmpfs_mode'] = int(mount_dict['tmpfs_mode'], 8) + mount_dict["tmpfs_mode"] = int(mount_dict["tmpfs_mode"], 8) except Exception as dummy: - module.fail_json(msg=f'tmp_fs mode of mount "{target}" is not an octal string!') - if mount_dict['tmpfs_options']: + module.fail_json( + msg=f'tmp_fs mode of mount "{target}" is not an octal string!' + ) + if mount_dict["tmpfs_options"]: opts = [] - for idx, opt in enumerate(mount_dict['tmpfs_options']): + for idx, opt in enumerate(mount_dict["tmpfs_options"]): if len(opt) != 1: - module.fail_json(msg=f'tmpfs_options[{idx + 1}] of mount "{target}" must be a one-element dictionary!') + module.fail_json( + msg=f'tmpfs_options[{idx + 1}] of mount "{target}" must be a one-element dictionary!' + ) k, v = list(opt.items())[0] if not isinstance(k, str): - module.fail_json(msg=f'key {k!r} in tmpfs_options[{idx + 1}] of mount "{target}" must be a string!') + module.fail_json( + msg=f'key {k!r} in tmpfs_options[{idx + 1}] of mount "{target}" must be a string!' + ) if v is not None and not isinstance(v, str): - module.fail_json(msg=f'value {v!r} in tmpfs_options[{idx + 1}] of mount "{target}" must be a string or null/none!') + module.fail_json( + msg=f'value {v!r} in tmpfs_options[{idx + 1}] of mount "{target}" must be a string or null/none!' + ) opts.append([k, v] if v is not None else [k]) - mount_dict['tmpfs_options'] = opts + mount_dict["tmpfs_options"] = opts # Add result to list mounts.append(omit_none_from_dict(mount_dict)) - values['mounts'] = mounts - if 'volumes' in values: + values["mounts"] = mounts + if "volumes" in values: new_vols = [] - for vol in values['volumes']: - parts = vol.split(':') - if ':' in vol: + for vol in values["volumes"]: + parts = vol.split(":") + if ":" in vol: if len(parts) == 3: host, container, mode = parts if not _is_volume_permissions(mode): - module.fail_json(msg=f'Found invalid volumes mode: {mode}') - if re.match(r'[.~]', host): + module.fail_json(msg=f"Found invalid volumes mode: {mode}") + if re.match(r"[.~]", host): host = os.path.abspath(os.path.expanduser(host)) - check_collision(container, 'volumes') + check_collision(container, "volumes") new_vols.append(f"{host}:{container}:{mode}") continue elif len(parts) == 2: - if not _is_volume_permissions(parts[1]) and re.match(r'[.~]', parts[0]): + if not _is_volume_permissions(parts[1]) and re.match( + r"[.~]", parts[0] + ): host = os.path.abspath(os.path.expanduser(parts[0])) - check_collision(parts[1], 'volumes') + check_collision(parts[1], "volumes") new_vols.append(f"{host}:{parts[1]}:rw") continue - check_collision(parts[min(1, len(parts) - 1)], 'volumes') + check_collision(parts[min(1, len(parts) - 1)], "volumes") new_vols.append(vol) - values['volumes'] = new_vols + values["volumes"] = new_vols new_binds = [] for vol in new_vols: host = None - if ':' in vol: - parts = vol.split(':') + if ":" in vol: + parts = vol.split(":") if len(parts) == 3: host, container, mode = parts if not _is_volume_permissions(mode): - module.fail_json(msg=f'Found invalid volumes mode: {mode}') + module.fail_json(msg=f"Found invalid volumes mode: {mode}") elif len(parts) == 2: if not _is_volume_permissions(parts[1]): - host, container, mode = (parts + ['rw']) + host, container, mode = parts + ["rw"] if host is not None: - new_binds.append(f'{host}:{container}:{mode}') - values['volume_binds'] = new_binds + new_binds.append(f"{host}:{container}:{mode}") + values["volume_binds"] = new_binds return values def _preprocess_labels(module, values): result = {} - if 'labels' in values: - labels = values['labels'] + if "labels" in values: + labels = values["labels"] if labels is not None: labels = dict(labels) - sanitize_labels(labels, 'labels', module=module) - result['labels'] = labels + sanitize_labels(labels, "labels", module=module) + result["labels"] = labels return result def _preprocess_log(module, values): result = {} - if 'log_driver' not in values: + if "log_driver" not in values: return result - result['log_driver'] = values['log_driver'] - if 'log_options' in values: + result["log_driver"] = values["log_driver"] + if "log_options" in values: options = {} - for k, v in values['log_options'].items(): + for k, v in values["log_options"].items(): if not isinstance(v, str): - value = to_text(v, errors='surrogate_or_strict') + value = to_text(v, errors="surrogate_or_strict") module.warn( f"Non-string value found for log_options option '{k}'. The value is automatically converted to {value!r}. " "If this is not correct, or you want to avoid such warnings, please quote the value." ) v = value options[k] = v - result['log_options'] = options + result["log_options"] = options return result def _preprocess_ports(module, values): - if 'published_ports' in values: - if 'all' in values['published_ports']: + if "published_ports" in values: + if "all" in values["published_ports"]: module.fail_json( msg='Specifying "all" in published_ports is no longer allowed. Set publish_all_ports to "true" instead ' - 'to randomly assign port mappings for those not specified by published_ports.') + "to randomly assign port mappings for those not specified by published_ports." + ) binds = {} - for port in values['published_ports']: - parts = _split_colon_ipv6(to_text(port, errors='surrogate_or_strict'), module) + for port in values["published_ports"]: + parts = _split_colon_ipv6( + to_text(port, errors="surrogate_or_strict"), module + ) container_port = parts[-1] - protocol = '' - if '/' in container_port: - container_port, protocol = parts[-1].split('/') + protocol = "" + if "/" in container_port: + container_port, protocol = parts[-1].split("/") container_ports = _parse_port_range(container_port, module) p_len = len(parts) if p_len == 1: - port_binds = len(container_ports) * [(_DEFAULT_IP_REPLACEMENT_STRING, )] + port_binds = len(container_ports) * [(_DEFAULT_IP_REPLACEMENT_STRING,)] elif p_len == 2: if len(container_ports) == 1: port_binds = [(_DEFAULT_IP_REPLACEMENT_STRING, parts[0])] else: - port_binds = [(_DEFAULT_IP_REPLACEMENT_STRING, port) for port in _parse_port_range(parts[0], module)] + port_binds = [ + (_DEFAULT_IP_REPLACEMENT_STRING, port) + for port in _parse_port_range(parts[0], module) + ] elif p_len == 3: # We only allow IPv4 and IPv6 addresses for the bind address ipaddr = parts[0] - if not re.match(r'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$', parts[0]) and not re.match(r'^\[[0-9a-fA-F:]+(?:|%[^\]/]+)\]$', ipaddr): + if not re.match( + r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$", parts[0] + ) and not re.match(r"^\[[0-9a-fA-F:]+(?:|%[^\]/]+)\]$", ipaddr): module.fail_json( - msg='Bind addresses for published ports must be IPv4 or IPv6 addresses, not hostnames. ' - f'Use the dig lookup to resolve hostnames. (Found hostname: {ipaddr})' + msg="Bind addresses for published ports must be IPv4 or IPv6 addresses, not hostnames. " + f"Use the dig lookup to resolve hostnames. (Found hostname: {ipaddr})" ) - if re.match(r'^\[[0-9a-fA-F:]+\]$', ipaddr): + if re.match(r"^\[[0-9a-fA-F:]+\]$", ipaddr): ipaddr = ipaddr[1:-1] if parts[1]: if len(container_ports) == 1: port_binds = [(ipaddr, parts[1])] else: - port_binds = [(ipaddr, port) for port in _parse_port_range(parts[1], module)] + port_binds = [ + (ipaddr, port) + for port in _parse_port_range(parts[1], module) + ] else: port_binds = len(container_ports) * [(ipaddr,)] else: module.fail_json( msg=f'Invalid port description "{port}" - expected 1 to 3 colon-separated parts, but got {p_len}. ' - 'Maybe you forgot to use square brackets ([...]) around an IPv6 address?' + "Maybe you forgot to use square brackets ([...]) around an IPv6 address?" ) for bind, container_port in zip(port_binds, container_ports): - idx = f'{container_port}/{protocol}' if protocol else container_port + idx = f"{container_port}/{protocol}" if protocol else container_port if idx in binds: old_bind = binds[idx] if isinstance(old_bind, list): @@ -758,45 +871,45 @@ def _preprocess_ports(module, values): binds[idx] = [old_bind, bind] else: binds[idx] = bind - values['published_ports'] = binds + values["published_ports"] = binds exposed = [] - if 'exposed_ports' in values: - for port in values['exposed_ports']: - port = to_text(port, errors='surrogate_or_strict').strip() - protocol = 'tcp' - match = re.search(r'(/.+$)', port) + if "exposed_ports" in values: + for port in values["exposed_ports"]: + port = to_text(port, errors="surrogate_or_strict").strip() + protocol = "tcp" + match = re.search(r"(/.+$)", port) if match: - protocol = match.group(1).replace('/', '') - port = re.sub(r'/.+$', '', port) + protocol = match.group(1).replace("/", "") + port = re.sub(r"/.+$", "", port) exposed.append((port, protocol)) - if 'published_ports' in values: + if "published_ports" in values: # Any published port should also be exposed - for publish_port in values['published_ports']: + for publish_port in values["published_ports"]: match = False - if isinstance(publish_port, str) and '/' in publish_port: - port, protocol = publish_port.split('/') + if isinstance(publish_port, str) and "/" in publish_port: + port, protocol = publish_port.split("/") port = int(port) else: - protocol = 'tcp' + protocol = "tcp" port = int(publish_port) for exposed_port in exposed: if exposed_port[1] != protocol: continue - if isinstance(exposed_port[0], str) and '-' in exposed_port[0]: - start_port, end_port = exposed_port[0].split('-') + if isinstance(exposed_port[0], str) and "-" in exposed_port[0]: + start_port, end_port = exposed_port[0].split("-") if int(start_port) <= port <= int(end_port): match = True elif exposed_port[0] == port: match = True if not match: exposed.append((port, protocol)) - values['ports'] = exposed + values["ports"] = exposed return values def _compare_platform(option, param_value, container_value): - if option.comparison == 'ignore': + if option.comparison == "ignore": return True try: return compare_platform_strings(param_value, container_value) @@ -804,416 +917,373 @@ def _compare_platform(option, param_value, container_value): return param_value == container_value -OPTION_AUTO_REMOVE = ( - OptionGroup() - .add_option('auto_remove', type='bool') +OPTION_AUTO_REMOVE = OptionGroup().add_option("auto_remove", type="bool") + +OPTION_BLKIO_WEIGHT = OptionGroup().add_option("blkio_weight", type="int") + +OPTION_CAPABILITIES = OptionGroup().add_option( + "capabilities", type="set", elements="str" ) -OPTION_BLKIO_WEIGHT = ( - OptionGroup() - .add_option('blkio_weight', type='int') +OPTION_CAP_DROP = OptionGroup().add_option("cap_drop", type="set", elements="str") + +OPTION_CGROUP_NS_MODE = OptionGroup().add_option( + "cgroupns_mode", type="str", ansible_choices=["private", "host"] ) -OPTION_CAPABILITIES = ( - OptionGroup() - .add_option('capabilities', type='set', elements='str') +OPTION_CGROUP_PARENT = OptionGroup().add_option("cgroup_parent", type="str") + +OPTION_COMMAND = OptionGroup(preprocess=_preprocess_command).add_option( + "command", type="list", elements="str", ansible_type="raw" ) -OPTION_CAP_DROP = ( - OptionGroup() - .add_option('cap_drop', type='set', elements='str') +OPTION_CPU_PERIOD = OptionGroup().add_option("cpu_period", type="int") + +OPTION_CPU_QUOTA = OptionGroup().add_option("cpu_quota", type="int") + +OPTION_CPUSET_CPUS = OptionGroup().add_option("cpuset_cpus", type="str") + +OPTION_CPUSET_MEMS = OptionGroup().add_option("cpuset_mems", type="str") + +OPTION_CPU_SHARES = OptionGroup().add_option("cpu_shares", type="int") + +OPTION_ENTRYPOINT = OptionGroup(preprocess=_preprocess_entrypoint).add_option( + "entrypoint", type="list", elements="str" ) -OPTION_CGROUP_NS_MODE = ( - OptionGroup() - .add_option('cgroupns_mode', type='str', ansible_choices=['private', 'host']) -) - -OPTION_CGROUP_PARENT = ( - OptionGroup() - .add_option('cgroup_parent', type='str') -) - -OPTION_COMMAND = ( - OptionGroup(preprocess=_preprocess_command) - .add_option('command', type='list', elements='str', ansible_type='raw') -) - -OPTION_CPU_PERIOD = ( - OptionGroup() - .add_option('cpu_period', type='int') -) - -OPTION_CPU_QUOTA = ( - OptionGroup() - .add_option('cpu_quota', type='int') -) - -OPTION_CPUSET_CPUS = ( - OptionGroup() - .add_option('cpuset_cpus', type='str') -) - -OPTION_CPUSET_MEMS = ( - OptionGroup() - .add_option('cpuset_mems', type='str') -) - -OPTION_CPU_SHARES = ( - OptionGroup() - .add_option('cpu_shares', type='int') -) - -OPTION_ENTRYPOINT = ( - OptionGroup(preprocess=_preprocess_entrypoint) - .add_option('entrypoint', type='list', elements='str') -) - -OPTION_CPUS = ( - OptionGroup() - .add_option('cpus', type='int', ansible_type='float') -) +OPTION_CPUS = OptionGroup().add_option("cpus", type="int", ansible_type="float") OPTION_DETACH_INTERACTIVE = ( OptionGroup() - .add_option('detach', type='bool') - .add_option('interactive', type='bool') + .add_option("detach", type="bool") + .add_option("interactive", type="bool") ) -OPTION_DEVICES = ( - OptionGroup() - .add_option('devices', type='set', elements='dict', ansible_elements='str') +OPTION_DEVICES = OptionGroup().add_option( + "devices", type="set", elements="dict", ansible_elements="str" ) -OPTION_DEVICE_READ_BPS = ( - OptionGroup() - .add_option('device_read_bps', type='set', elements='dict', ansible_suboptions=dict( - path=dict(required=True, type='str'), - rate=dict(required=True, type='str'), - )) +OPTION_DEVICE_READ_BPS = OptionGroup().add_option( + "device_read_bps", + type="set", + elements="dict", + ansible_suboptions=dict( + path=dict(required=True, type="str"), + rate=dict(required=True, type="str"), + ), ) -OPTION_DEVICE_WRITE_BPS = ( - OptionGroup() - .add_option('device_write_bps', type='set', elements='dict', ansible_suboptions=dict( - path=dict(required=True, type='str'), - rate=dict(required=True, type='str'), - )) +OPTION_DEVICE_WRITE_BPS = OptionGroup().add_option( + "device_write_bps", + type="set", + elements="dict", + ansible_suboptions=dict( + path=dict(required=True, type="str"), + rate=dict(required=True, type="str"), + ), ) -OPTION_DEVICE_READ_IOPS = ( - OptionGroup() - .add_option('device_read_iops', type='set', elements='dict', ansible_suboptions=dict( - path=dict(required=True, type='str'), - rate=dict(required=True, type='int'), - )) +OPTION_DEVICE_READ_IOPS = OptionGroup().add_option( + "device_read_iops", + type="set", + elements="dict", + ansible_suboptions=dict( + path=dict(required=True, type="str"), + rate=dict(required=True, type="int"), + ), ) -OPTION_DEVICE_WRITE_IOPS = ( - OptionGroup() - .add_option('device_write_iops', type='set', elements='dict', ansible_suboptions=dict( - path=dict(required=True, type='str'), - rate=dict(required=True, type='int'), - )) +OPTION_DEVICE_WRITE_IOPS = OptionGroup().add_option( + "device_write_iops", + type="set", + elements="dict", + ansible_suboptions=dict( + path=dict(required=True, type="str"), + rate=dict(required=True, type="int"), + ), ) -OPTION_DEVICE_REQUESTS = ( - OptionGroup() - .add_option('device_requests', type='set', elements='dict', ansible_suboptions=dict( - capabilities=dict(type='list', elements='list'), - count=dict(type='int'), - device_ids=dict(type='list', elements='str'), - driver=dict(type='str'), - options=dict(type='dict'), - )) +OPTION_DEVICE_REQUESTS = OptionGroup().add_option( + "device_requests", + type="set", + elements="dict", + ansible_suboptions=dict( + capabilities=dict(type="list", elements="list"), + count=dict(type="int"), + device_ids=dict(type="list", elements="str"), + driver=dict(type="str"), + options=dict(type="dict"), + ), ) -OPTION_DEVICE_CGROUP_RULES = ( - OptionGroup() - .add_option('device_cgroup_rules', type='list', elements='str') +OPTION_DEVICE_CGROUP_RULES = OptionGroup().add_option( + "device_cgroup_rules", type="list", elements="str" ) -OPTION_DNS_SERVERS = ( - OptionGroup() - .add_option('dns_servers', type='list', elements='str') +OPTION_DNS_SERVERS = OptionGroup().add_option( + "dns_servers", type="list", elements="str" ) -OPTION_DNS_OPTS = ( - OptionGroup() - .add_option('dns_opts', type='set', elements='str') +OPTION_DNS_OPTS = OptionGroup().add_option("dns_opts", type="set", elements="str") + +OPTION_DNS_SEARCH_DOMAINS = OptionGroup().add_option( + "dns_search_domains", type="list", elements="str" ) -OPTION_DNS_SEARCH_DOMAINS = ( - OptionGroup() - .add_option('dns_search_domains', type='list', elements='str') -) - -OPTION_DOMAINNAME = ( - OptionGroup() - .add_option('domainname', type='str') -) +OPTION_DOMAINNAME = OptionGroup().add_option("domainname", type="str") OPTION_ENVIRONMENT = ( OptionGroup(preprocess=_preprocess_env) - .add_option('env', type='set', ansible_type='dict', elements='str', needs_no_suboptions=True) - .add_option('env_file', type='set', ansible_type='path', elements='str', not_a_container_option=True) + .add_option( + "env", type="set", ansible_type="dict", elements="str", needs_no_suboptions=True + ) + .add_option( + "env_file", + type="set", + ansible_type="path", + elements="str", + not_a_container_option=True, + ) ) -OPTION_ETC_HOSTS = ( - OptionGroup() - .add_option('etc_hosts', type='set', ansible_type='dict', elements='str', needs_no_suboptions=True) +OPTION_ETC_HOSTS = OptionGroup().add_option( + "etc_hosts", + type="set", + ansible_type="dict", + elements="str", + needs_no_suboptions=True, ) -OPTION_GROUPS = ( - OptionGroup() - .add_option('groups', type='set', elements='str') +OPTION_GROUPS = OptionGroup().add_option("groups", type="set", elements="str") + +OPTION_HEALTHCHECK = OptionGroup(preprocess=_preprocess_healthcheck).add_option( + "healthcheck", + type="dict", + ansible_suboptions=dict( + test=dict(type="raw"), + test_cli_compatible=dict(type="bool", default=False), + interval=dict(type="str"), + timeout=dict(type="str"), + start_period=dict(type="str"), + start_interval=dict(type="str"), + retries=dict(type="int"), + ), ) -OPTION_HEALTHCHECK = ( - OptionGroup(preprocess=_preprocess_healthcheck) - .add_option('healthcheck', type='dict', ansible_suboptions=dict( - test=dict(type='raw'), - test_cli_compatible=dict(type='bool', default=False), - interval=dict(type='str'), - timeout=dict(type='str'), - start_period=dict(type='str'), - start_interval=dict(type='str'), - retries=dict(type='int'), - )) +OPTION_HOSTNAME = OptionGroup().add_option("hostname", type="str") + +OPTION_IMAGE = OptionGroup().add_option("image", type="str") + +OPTION_INIT = OptionGroup().add_option("init", type="bool") + +OPTION_IPC_MODE = OptionGroup().add_option("ipc_mode", type="str") + +OPTION_KERNEL_MEMORY = OptionGroup( + preprocess=partial(_preprocess_convert_to_bytes, name="kernel_memory") +).add_option("kernel_memory", type="int", ansible_type="str") + +OPTION_LABELS = OptionGroup(preprocess=_preprocess_labels).add_option( + "labels", type="dict", needs_no_suboptions=True ) -OPTION_HOSTNAME = ( - OptionGroup() - .add_option('hostname', type='str') -) - -OPTION_IMAGE = ( - OptionGroup() - .add_option('image', type='str') -) - -OPTION_INIT = ( - OptionGroup() - .add_option('init', type='bool') -) - -OPTION_IPC_MODE = ( - OptionGroup() - .add_option('ipc_mode', type='str') -) - -OPTION_KERNEL_MEMORY = ( - OptionGroup(preprocess=partial(_preprocess_convert_to_bytes, name='kernel_memory')) - .add_option('kernel_memory', type='int', ansible_type='str') -) - -OPTION_LABELS = ( - OptionGroup(preprocess=_preprocess_labels) - .add_option('labels', type='dict', needs_no_suboptions=True) -) - -OPTION_LINKS = ( - OptionGroup() - .add_option('links', type='set', elements='list', ansible_elements='str') +OPTION_LINKS = OptionGroup().add_option( + "links", type="set", elements="list", ansible_elements="str" ) OPTION_LOG_DRIVER_OPTIONS = ( - OptionGroup(preprocess=_preprocess_log, ansible_required_by={'log_options': ['log_driver']}) - .add_option('log_driver', type='str') - .add_option('log_options', type='dict', ansible_aliases=['log_opt'], needs_no_suboptions=True) + OptionGroup( + preprocess=_preprocess_log, ansible_required_by={"log_options": ["log_driver"]} + ) + .add_option("log_driver", type="str") + .add_option( + "log_options", + type="dict", + ansible_aliases=["log_opt"], + needs_no_suboptions=True, + ) ) -OPTION_MAC_ADDRESS = ( - OptionGroup(preprocess=_preprocess_mac_address) - .add_option('mac_address', type='str') +OPTION_MAC_ADDRESS = OptionGroup(preprocess=_preprocess_mac_address).add_option( + "mac_address", type="str" ) -OPTION_MEMORY = ( - OptionGroup(preprocess=partial(_preprocess_convert_to_bytes, name='memory')) - .add_option('memory', type='int', ansible_type='str') -) +OPTION_MEMORY = OptionGroup( + preprocess=partial(_preprocess_convert_to_bytes, name="memory") +).add_option("memory", type="int", ansible_type="str") -OPTION_MEMORY_RESERVATION = ( - OptionGroup(preprocess=partial(_preprocess_convert_to_bytes, name='memory_reservation')) - .add_option('memory_reservation', type='int', ansible_type='str') -) +OPTION_MEMORY_RESERVATION = OptionGroup( + preprocess=partial(_preprocess_convert_to_bytes, name="memory_reservation") +).add_option("memory_reservation", type="int", ansible_type="str") -OPTION_MEMORY_SWAP = ( - OptionGroup(preprocess=partial(_preprocess_convert_to_bytes, name='memory_swap', unlimited_value=-1)) - .add_option('memory_swap', type='int', ansible_type='str') -) +OPTION_MEMORY_SWAP = OptionGroup( + preprocess=partial( + _preprocess_convert_to_bytes, name="memory_swap", unlimited_value=-1 + ) +).add_option("memory_swap", type="int", ansible_type="str") -OPTION_MEMORY_SWAPPINESS = ( - OptionGroup() - .add_option('memory_swappiness', type='int') -) +OPTION_MEMORY_SWAPPINESS = OptionGroup().add_option("memory_swappiness", type="int") -OPTION_STOP_TIMEOUT = ( - OptionGroup() - .add_option('stop_timeout', type='int', default_comparison='ignore') +OPTION_STOP_TIMEOUT = OptionGroup().add_option( + "stop_timeout", type="int", default_comparison="ignore" ) OPTION_NETWORK = ( OptionGroup(preprocess=_preprocess_networks) - .add_option('network_mode', type='str') - .add_option('networks', type='set', elements='dict', ansible_suboptions=dict( - name=dict(type='str', required=True), - ipv4_address=dict(type='str'), - ipv6_address=dict(type='str'), - aliases=dict(type='list', elements='str'), - links=dict(type='list', elements='str'), - mac_address=dict(type='str'), - )) + .add_option("network_mode", type="str") + .add_option( + "networks", + type="set", + elements="dict", + ansible_suboptions=dict( + name=dict(type="str", required=True), + ipv4_address=dict(type="str"), + ipv6_address=dict(type="str"), + aliases=dict(type="list", elements="str"), + links=dict(type="list", elements="str"), + mac_address=dict(type="str"), + ), + ) ) -OPTION_OOM_KILLER = ( - OptionGroup() - .add_option('oom_killer', type='bool') +OPTION_OOM_KILLER = OptionGroup().add_option("oom_killer", type="bool") + +OPTION_OOM_SCORE_ADJ = OptionGroup().add_option("oom_score_adj", type="int") + +OPTION_PID_MODE = OptionGroup().add_option("pid_mode", type="str") + +OPTION_PIDS_LIMIT = OptionGroup().add_option("pids_limit", type="int") + +OPTION_PLATFORM = OptionGroup().add_option( + "platform", type="str", compare=_compare_platform ) -OPTION_OOM_SCORE_ADJ = ( - OptionGroup() - .add_option('oom_score_adj', type='int') -) +OPTION_PRIVILEGED = OptionGroup().add_option("privileged", type="bool") -OPTION_PID_MODE = ( - OptionGroup() - .add_option('pid_mode', type='str') -) - -OPTION_PIDS_LIMIT = ( - OptionGroup() - .add_option('pids_limit', type='int') -) - -OPTION_PLATFORM = ( - OptionGroup() - .add_option('platform', type='str', compare=_compare_platform) -) - -OPTION_PRIVILEGED = ( - OptionGroup() - .add_option('privileged', type='bool') -) - -OPTION_READ_ONLY = ( - OptionGroup() - .add_option('read_only', type='bool') -) +OPTION_READ_ONLY = OptionGroup().add_option("read_only", type="bool") OPTION_RESTART_POLICY = ( - OptionGroup(ansible_required_by={'restart_retries': ['restart_policy']}) - .add_option('restart_policy', type='str', ansible_choices=['no', 'on-failure', 'always', 'unless-stopped']) - .add_option('restart_retries', type='int') + OptionGroup(ansible_required_by={"restart_retries": ["restart_policy"]}) + .add_option( + "restart_policy", + type="str", + ansible_choices=["no", "on-failure", "always", "unless-stopped"], + ) + .add_option("restart_retries", type="int") ) -OPTION_RUNTIME = ( - OptionGroup() - .add_option('runtime', type='str') +OPTION_RUNTIME = OptionGroup().add_option("runtime", type="str") + +OPTION_SECURITY_OPTS = OptionGroup().add_option( + "security_opts", type="set", elements="str" ) -OPTION_SECURITY_OPTS = ( - OptionGroup() - .add_option('security_opts', type='set', elements='str') +OPTION_SHM_SIZE = OptionGroup( + preprocess=partial(_preprocess_convert_to_bytes, name="shm_size") +).add_option("shm_size", type="int", ansible_type="str") + +OPTION_STOP_SIGNAL = OptionGroup().add_option("stop_signal", type="str") + +OPTION_STORAGE_OPTS = OptionGroup().add_option( + "storage_opts", type="dict", needs_no_suboptions=True ) -OPTION_SHM_SIZE = ( - OptionGroup(preprocess=partial(_preprocess_convert_to_bytes, name='shm_size')) - .add_option('shm_size', type='int', ansible_type='str') +OPTION_SYSCTLS = OptionGroup(preprocess=_preprocess_sysctls).add_option( + "sysctls", type="dict", needs_no_suboptions=True ) -OPTION_STOP_SIGNAL = ( - OptionGroup() - .add_option('stop_signal', type='str') +OPTION_TMPFS = OptionGroup(preprocess=_preprocess_tmpfs).add_option( + "tmpfs", type="dict", ansible_type="list", ansible_elements="str" ) -OPTION_STORAGE_OPTS = ( - OptionGroup() - .add_option('storage_opts', type='dict', needs_no_suboptions=True) +OPTION_TTY = OptionGroup().add_option("tty", type="bool") + +OPTION_ULIMITS = OptionGroup(preprocess=_preprocess_ulimits).add_option( + "ulimits", type="set", elements="dict", ansible_elements="str" ) -OPTION_SYSCTLS = ( - OptionGroup(preprocess=_preprocess_sysctls) - .add_option('sysctls', type='dict', needs_no_suboptions=True) +OPTION_USER = OptionGroup().add_option("user", type="str") + +OPTION_USERNS_MODE = OptionGroup().add_option("userns_mode", type="str") + +OPTION_UTS = OptionGroup().add_option("uts", type="str") + +OPTION_VOLUME_DRIVER = OptionGroup().add_option("volume_driver", type="str") + +OPTION_VOLUMES_FROM = OptionGroup().add_option( + "volumes_from", type="set", elements="str" ) -OPTION_TMPFS = ( - OptionGroup(preprocess=_preprocess_tmpfs) - .add_option('tmpfs', type='dict', ansible_type='list', ansible_elements='str') -) - -OPTION_TTY = ( - OptionGroup() - .add_option('tty', type='bool') -) - -OPTION_ULIMITS = ( - OptionGroup(preprocess=_preprocess_ulimits) - .add_option('ulimits', type='set', elements='dict', ansible_elements='str') -) - -OPTION_USER = ( - OptionGroup() - .add_option('user', type='str') -) - -OPTION_USERNS_MODE = ( - OptionGroup() - .add_option('userns_mode', type='str') -) - -OPTION_UTS = ( - OptionGroup() - .add_option('uts', type='str') -) - -OPTION_VOLUME_DRIVER = ( - OptionGroup() - .add_option('volume_driver', type='str') -) - -OPTION_VOLUMES_FROM = ( - OptionGroup() - .add_option('volumes_from', type='set', elements='str') -) - -OPTION_WORKING_DIR = ( - OptionGroup() - .add_option('working_dir', type='str') -) +OPTION_WORKING_DIR = OptionGroup().add_option("working_dir", type="str") OPTION_MOUNTS_VOLUMES = ( OptionGroup(preprocess=_preprocess_mounts) - .add_option('mounts', type='set', elements='dict', ansible_suboptions=dict( - target=dict(type='str', required=True), - source=dict(type='str'), - type=dict(type='str', choices=['bind', 'volume', 'tmpfs', 'npipe', 'cluster', 'image'], default='volume'), - read_only=dict(type='bool'), - consistency=dict(type='str', choices=['default', 'consistent', 'cached', 'delegated']), - propagation=dict(type='str', choices=['private', 'rprivate', 'shared', 'rshared', 'slave', 'rslave']), - no_copy=dict(type='bool'), - labels=dict(type='dict'), - volume_driver=dict(type='str'), - volume_options=dict(type='dict'), - tmpfs_size=dict(type='str'), - tmpfs_mode=dict(type='str'), - non_recursive=dict(type='bool'), - create_mountpoint=dict(type='bool'), - read_only_non_recursive=dict(type='bool'), - read_only_force_recursive=dict(type='bool'), - subpath=dict(type='str'), - tmpfs_options=dict(type='list', elements='dict'), - )) - .add_option('volumes', type='set', elements='str') - .add_option('volume_binds', type='set', elements='str', not_an_ansible_option=True, copy_comparison_from='volumes') + .add_option( + "mounts", + type="set", + elements="dict", + ansible_suboptions=dict( + target=dict(type="str", required=True), + source=dict(type="str"), + type=dict( + type="str", + choices=["bind", "volume", "tmpfs", "npipe", "cluster", "image"], + default="volume", + ), + read_only=dict(type="bool"), + consistency=dict( + type="str", choices=["default", "consistent", "cached", "delegated"] + ), + propagation=dict( + type="str", + choices=["private", "rprivate", "shared", "rshared", "slave", "rslave"], + ), + no_copy=dict(type="bool"), + labels=dict(type="dict"), + volume_driver=dict(type="str"), + volume_options=dict(type="dict"), + tmpfs_size=dict(type="str"), + tmpfs_mode=dict(type="str"), + non_recursive=dict(type="bool"), + create_mountpoint=dict(type="bool"), + read_only_non_recursive=dict(type="bool"), + read_only_force_recursive=dict(type="bool"), + subpath=dict(type="str"), + tmpfs_options=dict(type="list", elements="dict"), + ), + ) + .add_option("volumes", type="set", elements="str") + .add_option( + "volume_binds", + type="set", + elements="str", + not_an_ansible_option=True, + copy_comparison_from="volumes", + ) ) OPTION_PORTS = ( OptionGroup(preprocess=_preprocess_ports) - .add_option('exposed_ports', type='set', elements='str', ansible_aliases=['exposed', 'expose']) - .add_option('publish_all_ports', type='bool') - .add_option('published_ports', type='dict', ansible_type='list', ansible_elements='str', ansible_aliases=['ports']) - .add_option('ports', type='set', elements='str', not_an_ansible_option=True, default_comparison='ignore') + .add_option( + "exposed_ports", + type="set", + elements="str", + ansible_aliases=["exposed", "expose"], + ) + .add_option("publish_all_ports", type="bool") + .add_option( + "published_ports", + type="dict", + ansible_type="list", + ansible_elements="str", + ansible_aliases=["ports"], + ) + .add_option( + "ports", + type="set", + elements="str", + not_an_ansible_option=True, + default_comparison="ignore", + ) ) OPTIONS = [ diff --git a/plugins/module_utils/module_container/docker_api.py b/plugins/module_utils/module_container/docker_api.py index 68734b00..204dfc8b 100644 --- a/plugins/module_utils/module_container/docker_api.py +++ b/plugins/module_utils/module_container/docker_api.py @@ -10,44 +10,50 @@ import traceback from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.common.text.formatters import human_to_bytes - -from ansible_collections.community.docker.plugins.module_utils.common_api import ( - AnsibleDockerClient, - RequestException, +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, + NotFound, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + convert_port_bindings, + normalize_links, ) - from ansible_collections.community.docker.plugins.module_utils._platform import ( compose_platform_string, normalize_platform_string, ) - +from ansible_collections.community.docker.plugins.module_utils.common_api import ( + AnsibleDockerClient, + RequestException, +) from ansible_collections.community.docker.plugins.module_utils.module_container.base import ( OPTION_AUTO_REMOVE, OPTION_BLKIO_WEIGHT, - OPTION_CAPABILITIES, OPTION_CAP_DROP, + OPTION_CAPABILITIES, OPTION_CGROUP_NS_MODE, OPTION_CGROUP_PARENT, OPTION_COMMAND, OPTION_CPU_PERIOD, OPTION_CPU_QUOTA, + OPTION_CPU_SHARES, + OPTION_CPUS, OPTION_CPUSET_CPUS, OPTION_CPUSET_MEMS, - OPTION_CPU_SHARES, - OPTION_ENTRYPOINT, - OPTION_CPUS, OPTION_DETACH_INTERACTIVE, - OPTION_DEVICES, - OPTION_DEVICE_READ_BPS, - OPTION_DEVICE_WRITE_BPS, - OPTION_DEVICE_READ_IOPS, - OPTION_DEVICE_WRITE_IOPS, - OPTION_DEVICE_REQUESTS, OPTION_DEVICE_CGROUP_RULES, - OPTION_DNS_SERVERS, + OPTION_DEVICE_READ_BPS, + OPTION_DEVICE_READ_IOPS, + OPTION_DEVICE_REQUESTS, + OPTION_DEVICE_WRITE_BPS, + OPTION_DEVICE_WRITE_IOPS, + OPTION_DEVICES, OPTION_DNS_OPTS, OPTION_DNS_SEARCH_DOMAINS, + OPTION_DNS_SERVERS, OPTION_DOMAINNAME, + OPTION_ENTRYPOINT, OPTION_ENVIRONMENT, OPTION_ETC_HOSTS, OPTION_GROUPS, @@ -65,13 +71,14 @@ from ansible_collections.community.docker.plugins.module_utils.module_container. OPTION_MEMORY_RESERVATION, OPTION_MEMORY_SWAP, OPTION_MEMORY_SWAPPINESS, - OPTION_STOP_TIMEOUT, + OPTION_MOUNTS_VOLUMES, OPTION_NETWORK, OPTION_OOM_KILLER, OPTION_OOM_SCORE_ADJ, OPTION_PID_MODE, OPTION_PIDS_LIMIT, OPTION_PLATFORM, + OPTION_PORTS, OPTION_PRIVILEGED, OPTION_READ_ONLY, OPTION_RESTART_POLICY, @@ -79,6 +86,7 @@ from ansible_collections.community.docker.plugins.module_utils.module_container. OPTION_SECURITY_OPTS, OPTION_SHM_SIZE, OPTION_STOP_SIGNAL, + OPTION_STOP_TIMEOUT, OPTION_STORAGE_OPTS, OPTION_SYSCTLS, OPTION_TMPFS, @@ -90,39 +98,28 @@ from ansible_collections.community.docker.plugins.module_utils.module_container. OPTION_VOLUME_DRIVER, OPTION_VOLUMES_FROM, OPTION_WORKING_DIR, - OPTION_MOUNTS_VOLUMES, - OPTION_PORTS, OPTIONS, Engine, EngineDriver, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( normalize_healthcheck_test, omit_none_from_dict, ) - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - -from ansible_collections.community.docker.plugins.module_utils._api.errors import ( - APIError, - DockerException, - NotFound, -) - -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - convert_port_bindings, - normalize_links, +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, ) -_DEFAULT_IP_REPLACEMENT_STRING = '[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7xaekoh8Geingunaesaeweiy3ooleiwi]]' +_DEFAULT_IP_REPLACEMENT_STRING = ( + "[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7xaekoh8Geingunaesaeweiy3ooleiwi]]" +) def _get_ansible_type(type): - if type == 'set': - return 'list' - if type not in ('list', 'dict', 'bool', 'int', 'float', 'str'): + if type == "set": + return "list" + if type not in ("list", "dict", "bool", "int", "float", "str"): raise Exception(f'Invalid type "{type}"') return type @@ -131,9 +128,17 @@ _SENTRY = object() class DockerAPIEngineDriver(EngineDriver): - name = 'docker_api' + name = "docker_api" - def setup(self, argument_spec, mutually_exclusive=None, required_together=None, required_one_of=None, required_if=None, required_by=None): + def setup( + self, + argument_spec, + mutually_exclusive=None, + required_together=None, + required_one_of=None, + required_if=None, + required_by=None, + ): argument_spec = argument_spec or {} mutually_exclusive = mutually_exclusive or [] required_together = required_together or [] @@ -158,7 +163,9 @@ class DockerAPIEngineDriver(EngineDriver): if engine.min_api_version is not None: for option in options.options: if not option.not_an_ansible_option: - option_minimal_versions[option.name] = {'docker_api_version': engine.min_api_version} + option_minimal_versions[option.name] = { + "docker_api_version": engine.min_api_version + } if engine.extra_option_minimal_versions: option_minimal_versions.update(engine.extra_option_minimal_versions) @@ -184,28 +191,30 @@ class DockerAPIEngineDriver(EngineDriver): return client.docker_api_version def get_container_id(self, container): - return container['Id'] + return container["Id"] def get_image_from_container(self, container): - return container['Image'] + return container["Image"] def get_image_name_from_container(self, container): - return container['Config'].get('Image') + return container["Config"].get("Image") def is_container_removing(self, container): - if container.get('State'): - return container['State'].get('Status') == 'removing' + if container.get("State"): + return container["State"].get("Status") == "removing" return False def is_container_running(self, container): - if container.get('State'): - if container['State'].get('Running') and not container['State'].get('Ghost', False): + if container.get("State"): + if container["State"].get("Running") and not container["State"].get( + "Ghost", False + ): return True return False def is_container_paused(self, container): - if container.get('State'): - return container['State'].get('Paused', False) + if container.get("State"): + return container["State"].get("Paused", False) return False def inspect_container_by_name(self, client, container_name): @@ -224,111 +233,131 @@ class DockerAPIEngineDriver(EngineDriver): return client.pull_image(repository, tag, platform=platform) def pause_container(self, client, container_id): - client.post_call('/containers/{0}/pause', container_id) + client.post_call("/containers/{0}/pause", container_id) def unpause_container(self, client, container_id): - client.post_call('/containers/{0}/unpause', container_id) + client.post_call("/containers/{0}/unpause", container_id) def disconnect_container_from_network(self, client, container_id, network_id): - client.post_json('/networks/{0}/disconnect', network_id, data={'Container': container_id}) + client.post_json( + "/networks/{0}/disconnect", network_id, data={"Container": container_id} + ) def _create_endpoint_config(self, parameters): parameters = parameters.copy() params = {} for para, dest_para in { - 'ipv4_address': 'IPv4Address', - 'ipv6_address': 'IPv6Address', - 'links': 'Links', - 'aliases': 'Aliases', - 'mac_address': 'MacAddress', + "ipv4_address": "IPv4Address", + "ipv6_address": "IPv6Address", + "links": "Links", + "aliases": "Aliases", + "mac_address": "MacAddress", }.items(): value = parameters.pop(para, None) if value: - if para == 'links': + if para == "links": value = normalize_links(value) params[dest_para] = value if parameters: - ups = ', '.join([f'"{p}"' for p in sorted(parameters)]) + ups = ", ".join([f'"{p}"' for p in sorted(parameters)]) raise Exception( - f'Unknown parameter(s) for connect_container_to_network for Docker API driver: {ups}') + f"Unknown parameter(s) for connect_container_to_network for Docker API driver: {ups}" + ) ipam_config = {} - for param in ('IPv4Address', 'IPv6Address'): + for param in ("IPv4Address", "IPv6Address"): if param in params: ipam_config[param] = params.pop(param) if ipam_config: - params['IPAMConfig'] = ipam_config + params["IPAMConfig"] = ipam_config return params - def connect_container_to_network(self, client, container_id, network_id, parameters=None): + def connect_container_to_network( + self, client, container_id, network_id, parameters=None + ): parameters = (parameters or {}).copy() params = self._create_endpoint_config(parameters or {}) data = { - 'Container': container_id, - 'EndpointConfig': params, + "Container": container_id, + "EndpointConfig": params, } - client.post_json('/networks/{0}/connect', network_id, data=data) + client.post_json("/networks/{0}/connect", network_id, data=data) def create_container_supports_more_than_one_network(self, client): - return client.docker_api_version >= LooseVersion('1.44') + return client.docker_api_version >= LooseVersion("1.44") - def create_container(self, client, container_name, create_parameters, networks=None): - params = {'name': container_name} - if 'platform' in create_parameters: - params['platform'] = create_parameters.pop('platform') + def create_container( + self, client, container_name, create_parameters, networks=None + ): + params = {"name": container_name} + if "platform" in create_parameters: + params["platform"] = create_parameters.pop("platform") if networks is not None: create_parameters = create_parameters.copy() - create_parameters['NetworkingConfig'] = { - 'EndpointsConfig': dict( + create_parameters["NetworkingConfig"] = { + "EndpointsConfig": dict( (network, self._create_endpoint_config(network_params)) for network, network_params in networks.items() ) } - new_container = client.post_json_to_json('/containers/create', data=create_parameters, params=params) + new_container = client.post_json_to_json( + "/containers/create", data=create_parameters, params=params + ) client.report_warnings(new_container) - return new_container['Id'] + return new_container["Id"] def start_container(self, client, container_id): - client.post_json('/containers/{0}/start', container_id) + client.post_json("/containers/{0}/start", container_id) def wait_for_container(self, client, container_id, timeout=None): - return client.post_json_to_json('/containers/{0}/wait', container_id, timeout=timeout)['StatusCode'] + return client.post_json_to_json( + "/containers/{0}/wait", container_id, timeout=timeout + )["StatusCode"] def get_container_output(self, client, container_id): - config = client.get_json('/containers/{0}/json', container_id) - logging_driver = config['HostConfig']['LogConfig']['Type'] - if logging_driver in ('json-file', 'journald', 'local'): + config = client.get_json("/containers/{0}/json", container_id) + logging_driver = config["HostConfig"]["LogConfig"]["Type"] + if logging_driver in ("json-file", "journald", "local"): params = { - 'stderr': 1, - 'stdout': 1, - 'timestamps': 0, - 'follow': 0, - 'tail': 'all', + "stderr": 1, + "stdout": 1, + "timestamps": 0, + "follow": 0, + "tail": "all", } - res = client._get(client._url('/containers/{0}/logs', container_id), params=params) - output = client._get_result_tty(False, res, config['Config']['Tty']) + res = client._get( + client._url("/containers/{0}/logs", container_id), params=params + ) + output = client._get_result_tty(False, res, config["Config"]["Tty"]) return output, True else: return f"Result logged using `{logging_driver}` driver", False def update_container(self, client, container_id, update_parameters): - result = client.post_json_to_json('/containers/{0}/update', container_id, data=update_parameters) + result = client.post_json_to_json( + "/containers/{0}/update", container_id, data=update_parameters + ) client.report_warnings(result) def restart_container(self, client, container_id, timeout=None): client_timeout = client.timeout if client_timeout is not None: client_timeout += timeout or 10 - client.post_call('/containers/{0}/restart', container_id, params={'t': timeout}, timeout=client_timeout) + client.post_call( + "/containers/{0}/restart", + container_id, + params={"t": timeout}, + timeout=client_timeout, + ) def kill_container(self, client, container_id, kill_signal=None): params = {} if kill_signal is not None: - params['signal'] = kill_signal - client.post_call('/containers/{0}/kill', container_id, params=params) + params["signal"] = kill_signal + client.post_call("/containers/{0}/kill", container_id, params=params) def stop_container(self, client, container_id, timeout=None): if timeout: - params = {'t': timeout} + params = {"t": timeout} else: params = {} timeout = 10 @@ -338,48 +367,64 @@ class DockerAPIEngineDriver(EngineDriver): count = 0 while True: try: - client.post_call('/containers/{0}/stop', container_id, params=params, timeout=client_timeout) + client.post_call( + "/containers/{0}/stop", + container_id, + params=params, + timeout=client_timeout, + ) except APIError as exc: - if 'Unpause the container before stopping or killing' in exc.explanation: + if ( + "Unpause the container before stopping or killing" + in exc.explanation + ): # New docker daemon versions do not allow containers to be removed # if they are paused. Make sure we do not end up in an infinite loop. if count == 3: - raise Exception(f'{exc} [tried to unpause three times]') + raise Exception(f"{exc} [tried to unpause three times]") count += 1 # Unpause try: self.unpause_container(client, container_id) except Exception as exc2: - raise Exception(f'{exc2} [while unpausing]') + raise Exception(f"{exc2} [while unpausing]") # Now try again continue raise # We only loop when explicitly requested by 'continue' break - def remove_container(self, client, container_id, remove_volumes=False, link=False, force=False): - params = {'v': remove_volumes, 'link': link, 'force': force} + def remove_container( + self, client, container_id, remove_volumes=False, link=False, force=False + ): + params = {"v": remove_volumes, "link": link, "force": force} count = 0 while True: try: - client.delete_call('/containers/{0}', container_id, params=params) + client.delete_call("/containers/{0}", container_id, params=params) except NotFound as dummy: pass except APIError as exc: - if 'Unpause the container before stopping or killing' in exc.explanation: + if ( + "Unpause the container before stopping or killing" + in exc.explanation + ): # New docker daemon versions do not allow containers to be removed # if they are paused. Make sure we do not end up in an infinite loop. if count == 3: - raise Exception(f'{exc} [tried to unpause three times]') + raise Exception(f"{exc} [tried to unpause three times]") count += 1 # Unpause try: self.unpause_container(client, container_id) except Exception as exc2: - raise Exception(f'{exc2} [while unpausing]') + raise Exception(f"{exc2} [while unpausing]") # Now try again continue - if 'removal of container ' in exc.explanation and ' is already in progress' in exc.explanation: + if ( + "removal of container " in exc.explanation + and " is already in progress" in exc.explanation + ): pass else: raise @@ -390,11 +435,15 @@ class DockerAPIEngineDriver(EngineDriver): try: runner() except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) class DockerAPIEngine(Engine): @@ -415,16 +464,27 @@ class DockerAPIEngine(Engine): extra_option_minimal_versions=None, ): self.min_api_version = min_api_version - self.min_api_version_obj = None if min_api_version is None else LooseVersion(min_api_version) + self.min_api_version_obj = ( + None if min_api_version is None else LooseVersion(min_api_version) + ) self.get_value = get_value self.set_value = set_value - self.get_expected_values = get_expected_values or (lambda module, client, api_version, options, image, values, host_info: values) - self.ignore_mismatching_result = ignore_mismatching_result or \ - (lambda module, client, api_version, option, image, container_value, expected_value, host_info: False) - self.preprocess_value = preprocess_value or (lambda module, client, api_version, options, values: values) + self.get_expected_values = get_expected_values or ( + lambda module, client, api_version, options, image, values, host_info: values + ) + self.ignore_mismatching_result = ignore_mismatching_result or ( + lambda module, client, api_version, option, image, container_value, expected_value, host_info: False + ) + self.preprocess_value = preprocess_value or ( + lambda module, client, api_version, options, values: values + ) self.update_value = update_value - self.can_set_value = can_set_value or (lambda api_version: set_value is not None) - self.can_update_value = can_update_value or (lambda api_version: update_value is not None) + self.can_set_value = can_set_value or ( + lambda api_version: set_value is not None + ) + self.can_update_value = can_update_value or ( + lambda api_version: update_value is not None + ) self.needs_container_image = needs_container_image or (lambda values: False) self.needs_host_info = needs_host_info or (lambda values: False) if compare_value is not None: @@ -446,9 +506,13 @@ class DockerAPIEngine(Engine): ): def preprocess_value_(module, client, api_version, options, values): if len(options) != 1: - raise AssertionError('config_value can only be used for a single option') + raise AssertionError( + "config_value can only be used for a single option" + ) if preprocess_value is not None and options[0].name in values: - value = preprocess_value(module, client, api_version, values[options[0].name]) + value = preprocess_value( + module, client, api_version, values[options[0].name] + ) if value is None: del values[options[0].name] else: @@ -457,8 +521,10 @@ class DockerAPIEngine(Engine): def get_value(module, container, api_version, options, image, host_info): if len(options) != 1: - raise AssertionError('config_value can only be used for a single option') - value = container['Config'].get(config_name, _SENTRY) + raise AssertionError( + "config_value can only be used for a single option" + ) + value = container["Config"].get(config_name, _SENTRY) if postprocess_for_get: value = postprocess_for_get(module, api_version, value, _SENTRY) if value is _SENTRY: @@ -467,18 +533,27 @@ class DockerAPIEngine(Engine): get_expected_values_ = None if get_expected_value: - def get_expected_values_(module, client, api_version, options, image, values, host_info): + + def get_expected_values_( + module, client, api_version, options, image, values, host_info + ): if len(options) != 1: - raise AssertionError('host_config_value can only be used for a single option') + raise AssertionError( + "host_config_value can only be used for a single option" + ) value = values.get(options[0].name, _SENTRY) - value = get_expected_value(module, client, api_version, image, value, _SENTRY) + value = get_expected_value( + module, client, api_version, image, value, _SENTRY + ) if value is _SENTRY: return values return {options[0].name: value} def set_value(module, data, api_version, options, values): if len(options) != 1: - raise AssertionError('config_value can only be used for a single option') + raise AssertionError( + "config_value can only be used for a single option" + ) if options[0].name not in values: return value = values[options[0].name] @@ -488,9 +563,12 @@ class DockerAPIEngine(Engine): update_value = None if update_parameter: + def update_value(module, data, api_version, options, values): if len(options) != 1: - raise AssertionError('update_parameter can only be used for a single option') + raise AssertionError( + "update_parameter can only be used for a single option" + ) if options[0].name not in values: return value = values[options[0].name] @@ -524,9 +602,13 @@ class DockerAPIEngine(Engine): ): def preprocess_value_(module, client, api_version, options, values): if len(options) != 1: - raise AssertionError('host_config_value can only be used for a single option') + raise AssertionError( + "host_config_value can only be used for a single option" + ) if preprocess_value is not None and options[0].name in values: - value = preprocess_value(module, client, api_version, values[options[0].name]) + value = preprocess_value( + module, client, api_version, values[options[0].name] + ) if value is None: del values[options[0].name] else: @@ -535,8 +617,10 @@ class DockerAPIEngine(Engine): def get_value(module, container, api_version, options, get_value, host_info): if len(options) != 1: - raise AssertionError('host_config_value can only be used for a single option') - value = container['HostConfig'].get(host_config_name, _SENTRY) + raise AssertionError( + "host_config_value can only be used for a single option" + ) + value = container["HostConfig"].get(host_config_name, _SENTRY) if postprocess_for_get: value = postprocess_for_get(module, api_version, value, _SENTRY) if value is _SENTRY: @@ -545,32 +629,44 @@ class DockerAPIEngine(Engine): get_expected_values_ = None if get_expected_value: - def get_expected_values_(module, client, api_version, options, image, values, host_info): + + def get_expected_values_( + module, client, api_version, options, image, values, host_info + ): if len(options) != 1: - raise AssertionError('host_config_value can only be used for a single option') + raise AssertionError( + "host_config_value can only be used for a single option" + ) value = values.get(options[0].name, _SENTRY) - value = get_expected_value(module, client, api_version, image, value, _SENTRY) + value = get_expected_value( + module, client, api_version, image, value, _SENTRY + ) if value is _SENTRY: return values return {options[0].name: value} def set_value(module, data, api_version, options, values): if len(options) != 1: - raise AssertionError('host_config_value can only be used for a single option') + raise AssertionError( + "host_config_value can only be used for a single option" + ) if options[0].name not in values: return - if 'HostConfig' not in data: - data['HostConfig'] = {} + if "HostConfig" not in data: + data["HostConfig"] = {} value = values[options[0].name] if preprocess_for_set: value = preprocess_for_set(module, api_version, value) - data['HostConfig'][host_config_name] = value + data["HostConfig"][host_config_name] = value update_value = None if update_parameter: + def update_value(module, data, api_version, options, values): if len(options) != 1: - raise AssertionError('update_parameter can only be used for a single option') + raise AssertionError( + "update_parameter can only be used for a single option" + ) if options[0].name not in values: return value = values[options[0].name] @@ -591,71 +687,90 @@ class DockerAPIEngine(Engine): def _is_volume_permissions(mode): - for part in mode.split(','): - if part not in ('rw', 'ro', 'z', 'Z', 'consistent', 'delegated', 'cached', 'rprivate', 'private', 'rshared', 'shared', 'rslave', 'slave', 'nocopy'): + for part in mode.split(","): + if part not in ( + "rw", + "ro", + "z", + "Z", + "consistent", + "delegated", + "cached", + "rprivate", + "private", + "rshared", + "shared", + "rslave", + "slave", + "nocopy", + ): return False return True def _normalize_port(port): - if '/' not in port: - return port + '/tcp' + if "/" not in port: + return port + "/tcp" return port def _get_default_host_ip(module, client): - if module.params['default_host_ip'] is not None: - return module.params['default_host_ip'] - ip = '0.0.0.0' - for network_data in module.params['networks'] or []: - if network_data.get('name'): - network = client.get_network(network_data['name']) + if module.params["default_host_ip"] is not None: + return module.params["default_host_ip"] + ip = "0.0.0.0" + for network_data in module.params["networks"] or []: + if network_data.get("name"): + network = client.get_network(network_data["name"]) if network is None: client.fail( f"Cannot inspect the network '{network_data['name']}' to determine the default IP", ) - if network.get('Driver') == 'bridge' and network.get('Options', {}).get('com.docker.network.bridge.host_binding_ipv4'): - ip = network['Options']['com.docker.network.bridge.host_binding_ipv4'] + if network.get("Driver") == "bridge" and network.get("Options", {}).get( + "com.docker.network.bridge.host_binding_ipv4" + ): + ip = network["Options"]["com.docker.network.bridge.host_binding_ipv4"] break return ip -def _get_value_detach_interactive(module, container, api_version, options, image, host_info): - attach_stdin = container['Config'].get('OpenStdin') - attach_stderr = container['Config'].get('AttachStderr') - attach_stdout = container['Config'].get('AttachStdout') +def _get_value_detach_interactive( + module, container, api_version, options, image, host_info +): + attach_stdin = container["Config"].get("OpenStdin") + attach_stderr = container["Config"].get("AttachStderr") + attach_stdout = container["Config"].get("AttachStdout") return { - 'interactive': bool(attach_stdin), - 'detach': not (attach_stderr and attach_stdout), + "interactive": bool(attach_stdin), + "detach": not (attach_stderr and attach_stdout), } def _set_value_detach_interactive(module, data, api_version, options, values): - interactive = values.get('interactive') - detach = values.get('detach') + interactive = values.get("interactive") + detach = values.get("detach") - data['AttachStdout'] = False - data['AttachStderr'] = False - data['AttachStdin'] = False - data['StdinOnce'] = False - data['OpenStdin'] = interactive + data["AttachStdout"] = False + data["AttachStderr"] = False + data["AttachStdin"] = False + data["StdinOnce"] = False + data["OpenStdin"] = interactive if not detach: - data['AttachStdout'] = True - data['AttachStderr'] = True + data["AttachStdout"] = True + data["AttachStderr"] = True if interactive: - data['AttachStdin'] = True - data['StdinOnce'] = True + data["AttachStdin"] = True + data["StdinOnce"] = True def _get_expected_env_value(module, client, api_version, image, value, sentry): expected_env = {} - if image and image['Config'].get('Env'): - for env_var in image['Config']['Env']: - parts = env_var.split('=', 1) + if image and image["Config"].get("Env"): + for env_var in image["Config"]["Env"]: + parts = env_var.split("=", 1) expected_env[parts[0]] = parts[1] if value and value is not sentry: for env_var in value: - parts = env_var.split('=', 1) + parts = env_var.split("=", 1) expected_env[parts[0]] = parts[1] param_env = [] for key, env_value in expected_env.items(): @@ -665,7 +780,7 @@ def _get_expected_env_value(module, client, api_version, image, value, sentry): def _preprocess_cpus(module, client, api_version, value): if value is not None: - value = int(round(value * 1E9)) + value = int(round(value * 1e9)) return value @@ -674,21 +789,22 @@ def _preprocess_devices(module, client, api_version, value): return value expected_devices = [] for device in value: - parts = device.split(':') + parts = device.split(":") if len(parts) == 1: expected_devices.append( dict( - CgroupPermissions='rwm', + CgroupPermissions="rwm", PathInContainer=parts[0], - PathOnHost=parts[0] - )) + PathOnHost=parts[0], + ) + ) elif len(parts) == 2: - parts = device.split(':') + parts = device.split(":") expected_devices.append( dict( - CgroupPermissions='rwm', + CgroupPermissions="rwm", PathInContainer=parts[1], - PathOnHost=parts[0] + PathOnHost=parts[0], ) ) else: @@ -696,8 +812,9 @@ def _preprocess_devices(module, client, api_version, value): dict( CgroupPermissions=parts[2], PathInContainer=parts[1], - PathOnHost=parts[0] - )) + PathOnHost=parts[0], + ) + ) return expected_devices @@ -706,10 +823,12 @@ def _preprocess_rate_bps(module, client, api_version, value): return value devices = [] for device in value: - devices.append({ - 'Path': device['path'], - 'Rate': human_to_bytes(device['rate']), - }) + devices.append( + { + "Path": device["path"], + "Rate": human_to_bytes(device["rate"]), + } + ) return devices @@ -718,10 +837,12 @@ def _preprocess_rate_iops(module, client, api_version, value): return value devices = [] for device in value: - devices.append({ - 'Path': device['path'], - 'Rate': device['rate'], - }) + devices.append( + { + "Path": device["path"], + "Rate": device["rate"], + } + ) return devices @@ -730,13 +851,15 @@ def _preprocess_device_requests(module, client, api_version, value): return value device_requests = [] for dr in value: - device_requests.append({ - 'Driver': dr['driver'], - 'Count': dr['count'], - 'DeviceIDs': dr['device_ids'], - 'Capabilities': dr['capabilities'], - 'Options': dr['options'], - }) + device_requests.append( + { + "Driver": dr["driver"], + "Count": dr["count"], + "DeviceIDs": dr["device_ids"], + "Capabilities": dr["capabilities"], + "Options": dr["options"], + } + ) return device_requests @@ -745,30 +868,35 @@ def _preprocess_etc_hosts(module, client, api_version, value): return value results = [] for key, value in value.items(): - results.append(f'{key}:{value}') + results.append(f"{key}:{value}") return results def _preprocess_healthcheck(module, client, api_version, value): if value is None: return value - if not value or not (value.get('test') or (value.get('test_cli_compatible') and value.get('test') is None)): - value = {'test': ['NONE']} - elif 'test' in value: - value['test'] = normalize_healthcheck_test(value['test']) - return omit_none_from_dict({ - 'Test': value.get('test'), - 'Interval': value.get('interval'), - 'Timeout': value.get('timeout'), - 'StartPeriod': value.get('start_period'), - 'StartInterval': value.get('start_interval'), - 'Retries': value.get('retries'), - }) + if not value or not ( + value.get("test") + or (value.get("test_cli_compatible") and value.get("test") is None) + ): + value = {"test": ["NONE"]} + elif "test" in value: + value["test"] = normalize_healthcheck_test(value["test"]) + return omit_none_from_dict( + { + "Test": value.get("test"), + "Interval": value.get("interval"), + "Timeout": value.get("timeout"), + "StartPeriod": value.get("start_period"), + "StartInterval": value.get("start_interval"), + "Retries": value.get("retries"), + } + ) def _postprocess_healthcheck_get_value(module, api_version, value, sentry): - if value is None or value is sentry or value.get('Test') == ['NONE']: - return {'Test': ['NONE']} + if value is None or value is sentry or value.get("Test") == ["NONE"]: + return {"Test": ["NONE"]} return value @@ -777,14 +905,14 @@ def _preprocess_convert_to_bytes(module, values, name, unlimited_value=None): return values try: value = values[name] - if unlimited_value is not None and value in ('unlimited', str(unlimited_value)): + if unlimited_value is not None and value in ("unlimited", str(unlimited_value)): value = unlimited_value else: value = human_to_bytes(value) values[name] = value return values except ValueError as exc: - module.fail_json(msg=f'Failed to convert {name} to bytes: {exc}') + module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}") def _get_image_labels(image): @@ -792,14 +920,14 @@ def _get_image_labels(image): return {} # Cannot use get('Labels', {}) because 'Labels' may be present and be None - return image['Config'].get('Labels') or {} + return image["Config"].get("Labels") or {} def _get_expected_labels_value(module, client, api_version, image, value, sentry): if value is sentry: return sentry expected_labels = {} - if module.params['image_label_mismatch'] == 'ignore': + if module.params["image_label_mismatch"] == "ignore": expected_labels.update(dict(_get_image_labels(image))) expected_labels.update(value) return expected_labels @@ -811,7 +939,7 @@ def _preprocess_links(module, client, api_version, value): result = [] for link in value: - parsed_link = link.split(':', 1) + parsed_link = link.split(":", 1) if len(parsed_link) == 2: link, alias = parsed_link else: @@ -821,51 +949,78 @@ def _preprocess_links(module, client, api_version, value): return result -def _ignore_mismatching_label_result(module, client, api_version, option, image, container_value, expected_value, host_info): - if option.comparison == 'strict' and module.params['image_label_mismatch'] == 'fail': +def _ignore_mismatching_label_result( + module, + client, + api_version, + option, + image, + container_value, + expected_value, + host_info, +): + if ( + option.comparison == "strict" + and module.params["image_label_mismatch"] == "fail" + ): # If there are labels from the base image that should be removed and # base_image_mismatch is fail we want raise an error. image_labels = _get_image_labels(image) would_remove_labels = [] - labels_param = module.params['labels'] or {} + labels_param = module.params["labels"] or {} for label in image_labels: if label not in labels_param: # Format label for error message would_remove_labels.append(f'"{label}"') if would_remove_labels: - labels = ', '.join(would_remove_labels) - msg = ("Some labels should be removed but are present in the base image. You can set image_label_mismatch to 'ignore' to ignore" - f" this error. Labels: {labels}") + labels = ", ".join(would_remove_labels) + msg = ( + "Some labels should be removed but are present in the base image. You can set image_label_mismatch to 'ignore' to ignore" + f" this error. Labels: {labels}" + ) client.fail(msg) return False def _needs_host_info_network(values): - return values.get('network_mode') == 'default' + return values.get("network_mode") == "default" -def _ignore_mismatching_network_result(module, client, api_version, option, image, container_value, expected_value, host_info): +def _ignore_mismatching_network_result( + module, + client, + api_version, + option, + image, + container_value, + expected_value, + host_info, +): # 'networks' is handled out-of-band - if option.name == 'networks': + if option.name == "networks": return True # The 'default' network_mode value is translated by the Docker daemon to 'bridge' on Linux and 'nat' on Windows. # This happens since Docker 26.1.0 due to https://github.com/moby/moby/pull/47431; before, 'default' was returned. - if option.name == 'network_mode' and expected_value == 'default': - os_type = host_info.get('OSType') if host_info else None - if (container_value, os_type) in (('bridge', 'linux'), ('nat', 'windows')): + if option.name == "network_mode" and expected_value == "default": + os_type = host_info.get("OSType") if host_info else None + if (container_value, os_type) in (("bridge", "linux"), ("nat", "windows")): return True return False def _preprocess_network_values(module, client, api_version, options, values): - if 'networks' in values: - for network in values['networks']: - network['id'] = _get_network_id(module, client, network['name']) - if not network['id']: - client.fail(f"Parameter error: network named {network['name']} could not be found. Does it exist?") + if "networks" in values: + for network in values["networks"]: + network["id"] = _get_network_id(module, client, network["name"]) + if not network["id"]: + client.fail( + f"Parameter error: network named {network['name']} could not be found. Does it exist?" + ) - if 'network_mode' in values: - values['network_mode'] = _preprocess_container_names(module, client, api_version, values['network_mode']) + if "network_mode" in values: + values["network_mode"] = _preprocess_container_names( + module, client, api_version, values["network_mode"] + ) return values @@ -873,10 +1028,10 @@ def _preprocess_network_values(module, client, api_version, options, values): def _get_network_id(module, client, network_name): try: network_id = None - params = {'filters': json.dumps({'name': [network_name]})} - for network in client.get_json('/networks', params=params): - if network['Name'] == network_name: - network_id = network['Id'] + params = {"filters": json.dumps({"name": [network_name]})} + for network in client.get_json("/networks", params=params): + if network["Name"] == network_name: + network_id = network["Id"] break return network_id except Exception as exc: @@ -884,61 +1039,88 @@ def _get_network_id(module, client, network_name): def _get_values_network(module, container, api_version, options, image, host_info): - value = container['HostConfig'].get('NetworkMode', _SENTRY) + value = container["HostConfig"].get("NetworkMode", _SENTRY) if value is _SENTRY: return {} - return {'network_mode': value} + return {"network_mode": value} def _set_values_network(module, data, api_version, options, values): - if 'network_mode' not in values: + if "network_mode" not in values: return - if 'HostConfig' not in data: - data['HostConfig'] = {} - value = values['network_mode'] - data['HostConfig']['NetworkMode'] = value + if "HostConfig" not in data: + data["HostConfig"] = {} + value = values["network_mode"] + data["HostConfig"]["NetworkMode"] = value def _get_values_mounts(module, container, api_version, options, image, host_info): - volumes = container['Config'].get('Volumes') - binds = container['HostConfig'].get('Binds') + volumes = container["Config"].get("Volumes") + binds = container["HostConfig"].get("Binds") # According to https://github.com/moby/moby/, support for HostConfig.Mounts # has been included at least since v17.03.0-ce, which has API version 1.26. # The previous tag, v1.9.1, has API version 1.21 and does not have # HostConfig.Mounts. I have no idea what about API 1.25... - mounts = container['HostConfig'].get('Mounts') + mounts = container["HostConfig"].get("Mounts") if mounts is not None: result = [] empty_dict = {} for mount in mounts: - result.append({ - 'type': mount.get('Type'), - 'source': mount.get('Source'), - 'target': mount.get('Target'), - 'read_only': mount.get('ReadOnly', False), # golang's omitempty for bool returns None for False - 'consistency': mount.get('Consistency'), - 'propagation': mount.get('BindOptions', empty_dict).get('Propagation'), - 'no_copy': mount.get('VolumeOptions', empty_dict).get('NoCopy', False), - 'labels': mount.get('VolumeOptions', empty_dict).get('Labels', empty_dict), - 'volume_driver': mount.get('VolumeOptions', empty_dict).get('DriverConfig', empty_dict).get('Name'), - 'volume_options': mount.get('VolumeOptions', empty_dict).get('DriverConfig', empty_dict).get('Options', empty_dict), - 'tmpfs_size': mount.get('TmpfsOptions', empty_dict).get('SizeBytes'), - 'tmpfs_mode': mount.get('TmpfsOptions', empty_dict).get('Mode'), - 'non_recursive': mount.get('BindOptions', empty_dict).get('NonRecursive'), - 'create_mountpoint': mount.get('BindOptions', empty_dict).get('CreateMountpoint'), - 'read_only_non_recursive': mount.get('BindOptions', empty_dict).get('ReadOnlyNonRecursive'), - 'read_only_force_recursive': mount.get('BindOptions', empty_dict).get('ReadOnlyForceRecursive'), - 'subpath': mount.get('VolumeOptions', empty_dict).get('Subpath') or mount.get('ImageOptions', empty_dict).get('Subpath'), - 'tmpfs_options': mount.get('TmpfsOptions', empty_dict).get('Options'), - }) + result.append( + { + "type": mount.get("Type"), + "source": mount.get("Source"), + "target": mount.get("Target"), + "read_only": mount.get( + "ReadOnly", False + ), # golang's omitempty for bool returns None for False + "consistency": mount.get("Consistency"), + "propagation": mount.get("BindOptions", empty_dict).get( + "Propagation" + ), + "no_copy": mount.get("VolumeOptions", empty_dict).get( + "NoCopy", False + ), + "labels": mount.get("VolumeOptions", empty_dict).get( + "Labels", empty_dict + ), + "volume_driver": mount.get("VolumeOptions", empty_dict) + .get("DriverConfig", empty_dict) + .get("Name"), + "volume_options": mount.get("VolumeOptions", empty_dict) + .get("DriverConfig", empty_dict) + .get("Options", empty_dict), + "tmpfs_size": mount.get("TmpfsOptions", empty_dict).get( + "SizeBytes" + ), + "tmpfs_mode": mount.get("TmpfsOptions", empty_dict).get("Mode"), + "non_recursive": mount.get("BindOptions", empty_dict).get( + "NonRecursive" + ), + "create_mountpoint": mount.get("BindOptions", empty_dict).get( + "CreateMountpoint" + ), + "read_only_non_recursive": mount.get("BindOptions", empty_dict).get( + "ReadOnlyNonRecursive" + ), + "read_only_force_recursive": mount.get( + "BindOptions", empty_dict + ).get("ReadOnlyForceRecursive"), + "subpath": mount.get("VolumeOptions", empty_dict).get("Subpath") + or mount.get("ImageOptions", empty_dict).get("Subpath"), + "tmpfs_options": mount.get("TmpfsOptions", empty_dict).get( + "Options" + ), + } + ) mounts = result result = {} if volumes is not None: - result['volumes'] = volumes + result["volumes"] = volumes if binds is not None: - result['volume_binds'] = binds + result["volume_binds"] = binds if mounts is not None: - result['mounts'] = mounts + result["mounts"] = mounts return result @@ -946,20 +1128,20 @@ def _get_bind_from_dict(volume_dict): results = [] if volume_dict: for host_path, config in volume_dict.items(): - if isinstance(config, dict) and config.get('bind'): - container_path = config.get('bind') - mode = config.get('mode', 'rw') + if isinstance(config, dict) and config.get("bind"): + container_path = config.get("bind") + mode = config.get("mode", "rw") results.append(f"{host_path}:{container_path}:{mode}") return results def _get_image_binds(volumes): - ''' + """ Convert array of binds to array of strings with format host_path:container_path:mode :param volumes: array of bind dicts :return: array of strings - ''' + """ results = [] if isinstance(volumes, dict): results += _get_bind_from_dict(volumes) @@ -969,22 +1151,24 @@ def _get_image_binds(volumes): return results -def _get_expected_values_mounts(module, client, api_version, options, image, values, host_info): +def _get_expected_values_mounts( + module, client, api_version, options, image, values, host_info +): expected_values = {} # binds - if 'mounts' in values: - expected_values['mounts'] = values['mounts'] + if "mounts" in values: + expected_values["mounts"] = values["mounts"] # volumes expected_vols = dict() - if image and image['Config'].get('Volumes'): - expected_vols.update(image['Config'].get('Volumes')) - if 'volumes' in values: - for vol in values['volumes']: + if image and image["Config"].get("Volumes"): + expected_vols.update(image["Config"].get("Volumes")) + if "volumes" in values: + for vol in values["volumes"]: # We only expect anonymous volumes to show up in the list - if ':' in vol: - parts = vol.split(':') + if ":" in vol: + parts = vol.split(":") if len(parts) == 3: continue if len(parts) == 2: @@ -992,292 +1176,321 @@ def _get_expected_values_mounts(module, client, api_version, options, image, val continue expected_vols[vol] = {} if expected_vols: - expected_values['volumes'] = expected_vols + expected_values["volumes"] = expected_vols # binds image_vols = [] if image: - image_vols = _get_image_binds(image['Config'].get('Volumes')) + image_vols = _get_image_binds(image["Config"].get("Volumes")) param_vols = [] - if 'volume_binds' in values: - param_vols = values['volume_binds'] - expected_values['volume_binds'] = list(set(image_vols + param_vols)) + if "volume_binds" in values: + param_vols = values["volume_binds"] + expected_values["volume_binds"] = list(set(image_vols + param_vols)) return expected_values def _set_values_mounts(module, data, api_version, options, values): - if 'mounts' in values: - if 'HostConfig' not in data: - data['HostConfig'] = {} + if "mounts" in values: + if "HostConfig" not in data: + data["HostConfig"] = {} mounts = [] - for mount in values['mounts']: - mount_type = mount.get('type') + for mount in values["mounts"]: + mount_type = mount.get("type") mount_res = { - 'Target': mount.get('target'), - 'Source': mount.get('source'), - 'Type': mount_type, - 'ReadOnly': mount.get('read_only'), + "Target": mount.get("target"), + "Source": mount.get("source"), + "Type": mount_type, + "ReadOnly": mount.get("read_only"), } - if 'consistency' in mount: - mount_res['Consistency'] = mount['consistency'] - if mount_type == 'bind': + if "consistency" in mount: + mount_res["Consistency"] = mount["consistency"] + if mount_type == "bind": bind_opts = {} - if 'propagation' in mount: - bind_opts['Propagation'] = mount['propagation'] - if 'non_recursive' in mount: - bind_opts['NonRecursive'] = mount['non_recursive'] - if 'create_mountpoint' in mount: - bind_opts['CreateMountpoint'] = mount['create_mountpoint'] - if 'read_only_non_recursive' in mount: - bind_opts['ReadOnlyNonRecursive'] = mount['read_only_non_recursive'] - if 'read_only_force_recursive' in mount: - bind_opts['ReadOnlyForceRecursive'] = mount['read_only_force_recursive'] + if "propagation" in mount: + bind_opts["Propagation"] = mount["propagation"] + if "non_recursive" in mount: + bind_opts["NonRecursive"] = mount["non_recursive"] + if "create_mountpoint" in mount: + bind_opts["CreateMountpoint"] = mount["create_mountpoint"] + if "read_only_non_recursive" in mount: + bind_opts["ReadOnlyNonRecursive"] = mount["read_only_non_recursive"] + if "read_only_force_recursive" in mount: + bind_opts["ReadOnlyForceRecursive"] = mount[ + "read_only_force_recursive" + ] if bind_opts: - mount_res['BindOptions'] = bind_opts - if mount_type == 'volume': + mount_res["BindOptions"] = bind_opts + if mount_type == "volume": volume_opts = {} - if mount.get('no_copy'): - volume_opts['NoCopy'] = True - if mount.get('labels'): - volume_opts['Labels'] = mount.get('labels') - if mount.get('volume_driver'): + if mount.get("no_copy"): + volume_opts["NoCopy"] = True + if mount.get("labels"): + volume_opts["Labels"] = mount.get("labels") + if mount.get("volume_driver"): driver_config = { - 'Name': mount.get('volume_driver'), + "Name": mount.get("volume_driver"), } - if mount.get('volume_options'): - driver_config['Options'] = mount.get('volume_options') - volume_opts['DriverConfig'] = driver_config - if 'subpath' in mount: - volume_opts['Subpath'] = mount['subpath'] + if mount.get("volume_options"): + driver_config["Options"] = mount.get("volume_options") + volume_opts["DriverConfig"] = driver_config + if "subpath" in mount: + volume_opts["Subpath"] = mount["subpath"] if volume_opts: - mount_res['VolumeOptions'] = volume_opts - if mount_type == 'tmpfs': + mount_res["VolumeOptions"] = volume_opts + if mount_type == "tmpfs": tmpfs_opts = {} - if mount.get('tmpfs_mode'): - tmpfs_opts['Mode'] = mount.get('tmpfs_mode') - if mount.get('tmpfs_size'): - tmpfs_opts['SizeBytes'] = mount.get('tmpfs_size') - if 'tmpfs_options' in mount: - tmpfs_opts['Options'] = mount['tmpfs_options'] + if mount.get("tmpfs_mode"): + tmpfs_opts["Mode"] = mount.get("tmpfs_mode") + if mount.get("tmpfs_size"): + tmpfs_opts["SizeBytes"] = mount.get("tmpfs_size") + if "tmpfs_options" in mount: + tmpfs_opts["Options"] = mount["tmpfs_options"] if tmpfs_opts: - mount_res['TmpfsOptions'] = tmpfs_opts - if mount_type == 'image': + mount_res["TmpfsOptions"] = tmpfs_opts + if mount_type == "image": image_opts = {} - if 'subpath' in mount: - image_opts['Subpath'] = mount['subpath'] + if "subpath" in mount: + image_opts["Subpath"] = mount["subpath"] if image_opts: - mount_res['ImageOptions'] = image_opts + mount_res["ImageOptions"] = image_opts mounts.append(mount_res) - data['HostConfig']['Mounts'] = mounts - if 'volumes' in values: + data["HostConfig"]["Mounts"] = mounts + if "volumes" in values: volumes = {} - for volume in values['volumes']: + for volume in values["volumes"]: # Only pass anonymous volumes to create container - if ':' in volume: - parts = volume.split(':') + if ":" in volume: + parts = volume.split(":") if len(parts) == 3: continue if len(parts) == 2: if not _is_volume_permissions(parts[1]): continue volumes[volume] = {} - data['Volumes'] = volumes - if 'volume_binds' in values: - if 'HostConfig' not in data: - data['HostConfig'] = {} - data['HostConfig']['Binds'] = values['volume_binds'] + data["Volumes"] = volumes + if "volume_binds" in values: + if "HostConfig" not in data: + data["HostConfig"] = {} + data["HostConfig"]["Binds"] = values["volume_binds"] def _get_values_log(module, container, api_version, options, image, host_info): - log_config = container['HostConfig'].get('LogConfig') or {} + log_config = container["HostConfig"].get("LogConfig") or {} return { - 'log_driver': log_config.get('Type'), - 'log_options': log_config.get('Config'), + "log_driver": log_config.get("Type"), + "log_options": log_config.get("Config"), } def _set_values_log(module, data, api_version, options, values): - if 'log_driver' not in values: + if "log_driver" not in values: return log_config = { - 'Type': values['log_driver'], - 'Config': values.get('log_options') or {}, + "Type": values["log_driver"], + "Config": values.get("log_options") or {}, } - if 'HostConfig' not in data: - data['HostConfig'] = {} - data['HostConfig']['LogConfig'] = log_config + if "HostConfig" not in data: + data["HostConfig"] = {} + data["HostConfig"]["LogConfig"] = log_config def _get_values_platform(module, container, api_version, options, image, host_info): - if image and (image.get('Os') or image.get('Architecture') or image.get('Variant')): + if image and (image.get("Os") or image.get("Architecture") or image.get("Variant")): return { - 'platform': compose_platform_string( - os=image.get('Os'), - arch=image.get('Architecture'), - variant=image.get('Variant'), - daemon_os=host_info.get('OSType') if host_info else None, - daemon_arch=host_info.get('Architecture') if host_info else None, + "platform": compose_platform_string( + os=image.get("Os"), + arch=image.get("Architecture"), + variant=image.get("Variant"), + daemon_os=host_info.get("OSType") if host_info else None, + daemon_arch=host_info.get("Architecture") if host_info else None, ) } return { - 'platform': container.get('Platform'), + "platform": container.get("Platform"), } -def _get_expected_values_platform(module, client, api_version, options, image, values, host_info): +def _get_expected_values_platform( + module, client, api_version, options, image, values, host_info +): expected_values = {} - if 'platform' in values: + if "platform" in values: try: - expected_values['platform'] = normalize_platform_string( - values['platform'], - daemon_os=host_info.get('OSType') if host_info else None, - daemon_arch=host_info.get('Architecture') if host_info else None, + expected_values["platform"] = normalize_platform_string( + values["platform"], + daemon_os=host_info.get("OSType") if host_info else None, + daemon_arch=host_info.get("Architecture") if host_info else None, ) except ValueError as exc: - module.fail_json(msg=f'Error while parsing platform parameer: {exc}') + module.fail_json(msg=f"Error while parsing platform parameer: {exc}") return expected_values def _set_values_platform(module, data, api_version, options, values): - if 'platform' in values: - data['platform'] = values['platform'] + if "platform" in values: + data["platform"] = values["platform"] def _needs_container_image_platform(values): - return 'platform' in values + return "platform" in values def _needs_host_info_platform(values): - return 'platform' in values + return "platform" in values def _get_values_restart(module, container, api_version, options, image, host_info): - restart_policy = container['HostConfig'].get('RestartPolicy') or {} + restart_policy = container["HostConfig"].get("RestartPolicy") or {} return { - 'restart_policy': restart_policy.get('Name'), - 'restart_retries': restart_policy.get('MaximumRetryCount'), + "restart_policy": restart_policy.get("Name"), + "restart_retries": restart_policy.get("MaximumRetryCount"), } def _set_values_restart(module, data, api_version, options, values): - if 'restart_policy' not in values: + if "restart_policy" not in values: return restart_policy = { - 'Name': values['restart_policy'], - 'MaximumRetryCount': values.get('restart_retries'), + "Name": values["restart_policy"], + "MaximumRetryCount": values.get("restart_retries"), } - if 'HostConfig' not in data: - data['HostConfig'] = {} - data['HostConfig']['RestartPolicy'] = restart_policy + if "HostConfig" not in data: + data["HostConfig"] = {} + data["HostConfig"]["RestartPolicy"] = restart_policy def _update_value_restart(module, data, api_version, options, values): - if 'restart_policy' not in values: + if "restart_policy" not in values: return - data['RestartPolicy'] = { - 'Name': values['restart_policy'], - 'MaximumRetryCount': values.get('restart_retries'), + data["RestartPolicy"] = { + "Name": values["restart_policy"], + "MaximumRetryCount": values.get("restart_retries"), } def _get_values_ports(module, container, api_version, options, image, host_info): - host_config = container['HostConfig'] - config = container['Config'] + host_config = container["HostConfig"] + config = container["Config"] # "ExposedPorts": null returns None type & causes AttributeError - PR #5517 - if config.get('ExposedPorts') is not None: - expected_exposed = [_normalize_port(p) for p in config.get('ExposedPorts', dict()).keys()] + if config.get("ExposedPorts") is not None: + expected_exposed = [ + _normalize_port(p) for p in config.get("ExposedPorts", dict()).keys() + ] else: expected_exposed = [] return { - 'published_ports': host_config.get('PortBindings'), - 'exposed_ports': expected_exposed, - 'publish_all_ports': host_config.get('PublishAllPorts'), + "published_ports": host_config.get("PortBindings"), + "exposed_ports": expected_exposed, + "publish_all_ports": host_config.get("PublishAllPorts"), } -def _get_expected_values_ports(module, client, api_version, options, image, values, host_info): +def _get_expected_values_ports( + module, client, api_version, options, image, values, host_info +): expected_values = {} - if 'published_ports' in values: + if "published_ports" in values: expected_bound_ports = {} - for container_port, config in values['published_ports'].items(): + for container_port, config in values["published_ports"].items(): if isinstance(container_port, int): container_port = f"{container_port}/tcp" if len(config) == 1: if isinstance(config[0], int): - expected_bound_ports[container_port] = [{'HostIp': "0.0.0.0", 'HostPort': config[0]}] + expected_bound_ports[container_port] = [ + {"HostIp": "0.0.0.0", "HostPort": config[0]} + ] else: - expected_bound_ports[container_port] = [{'HostIp': config[0], 'HostPort': ""}] + expected_bound_ports[container_port] = [ + {"HostIp": config[0], "HostPort": ""} + ] elif isinstance(config[0], tuple): expected_bound_ports[container_port] = [] for host_ip, host_port in config: - expected_bound_ports[container_port].append({'HostIp': host_ip, 'HostPort': to_text(host_port, errors='surrogate_or_strict')}) + expected_bound_ports[container_port].append( + { + "HostIp": host_ip, + "HostPort": to_text( + host_port, errors="surrogate_or_strict" + ), + } + ) else: - expected_bound_ports[container_port] = [{'HostIp': config[0], 'HostPort': to_text(config[1], errors='surrogate_or_strict')}] - expected_values['published_ports'] = expected_bound_ports + expected_bound_ports[container_port] = [ + { + "HostIp": config[0], + "HostPort": to_text(config[1], errors="surrogate_or_strict"), + } + ] + expected_values["published_ports"] = expected_bound_ports image_ports = [] if image: - image_exposed_ports = image['Config'].get('ExposedPorts') or {} + image_exposed_ports = image["Config"].get("ExposedPorts") or {} image_ports = [_normalize_port(p) for p in image_exposed_ports] param_ports = [] - if 'ports' in values: - param_ports = [to_text(p[0], errors='surrogate_or_strict') + '/' + p[1] for p in values['ports']] + if "ports" in values: + param_ports = [ + to_text(p[0], errors="surrogate_or_strict") + "/" + p[1] + for p in values["ports"] + ] result = list(set(image_ports + param_ports)) - expected_values['exposed_ports'] = result + expected_values["exposed_ports"] = result - if 'publish_all_ports' in values: - expected_values['publish_all_ports'] = values['publish_all_ports'] + if "publish_all_ports" in values: + expected_values["publish_all_ports"] = values["publish_all_ports"] return expected_values def _set_values_ports(module, data, api_version, options, values): - if 'ports' in values: + if "ports" in values: exposed_ports = {} - for port_definition in values['ports']: + for port_definition in values["ports"]: port = port_definition - proto = 'tcp' + proto = "tcp" if isinstance(port_definition, tuple): if len(port_definition) == 2: proto = port_definition[1] port = port_definition[0] - exposed_ports[f'{port}/{proto}'] = {} - data['ExposedPorts'] = exposed_ports - if 'published_ports' in values: - if 'HostConfig' not in data: - data['HostConfig'] = {} - data['HostConfig']['PortBindings'] = convert_port_bindings(values['published_ports']) - if 'publish_all_ports' in values and values['publish_all_ports']: - if 'HostConfig' not in data: - data['HostConfig'] = {} - data['HostConfig']['PublishAllPorts'] = values['publish_all_ports'] + exposed_ports[f"{port}/{proto}"] = {} + data["ExposedPorts"] = exposed_ports + if "published_ports" in values: + if "HostConfig" not in data: + data["HostConfig"] = {} + data["HostConfig"]["PortBindings"] = convert_port_bindings( + values["published_ports"] + ) + if "publish_all_ports" in values and values["publish_all_ports"]: + if "HostConfig" not in data: + data["HostConfig"] = {} + data["HostConfig"]["PublishAllPorts"] = values["publish_all_ports"] def _preprocess_value_ports(module, client, api_version, options, values): - if 'published_ports' not in values: + if "published_ports" not in values: return values found = False - for port_spec in values['published_ports'].values(): + for port_spec in values["published_ports"].values(): if port_spec[0] == _DEFAULT_IP_REPLACEMENT_STRING: found = True break if not found: return values default_ip = _get_default_host_ip(module, client) - for port, port_spec in values['published_ports'].items(): + for port, port_spec in values["published_ports"].items(): if port_spec[0] == _DEFAULT_IP_REPLACEMENT_STRING: - values['published_ports'][port] = tuple([default_ip] + list(port_spec[1:])) + values["published_ports"][port] = tuple([default_ip] + list(port_spec[1:])) return values def _preprocess_container_names(module, client, api_version, value): - if value is None or not value.startswith('container:'): + if value is None or not value.startswith("container:"): return value - container_name = value[len('container:'):] + container_name = value[len("container:") :] # Try to inspect container to see whether this is an ID or a # name (and in the latter case, retrieve its ID) container = client.get_container(container_name) @@ -1290,7 +1503,7 @@ def _preprocess_container_names(module, client, api_version, value): def _get_value_command(module, container, api_version, options, image, host_info): - value = container['Config'].get('Cmd', _SENTRY) + value = container["Config"].get("Cmd", _SENTRY) if value is _SENTRY: return {} return {"command": value} @@ -1300,246 +1513,440 @@ def _set_value_command(module, data, api_version, options, values): if "command" not in values: return value = values["command"] - data['Cmd'] = value + data["Cmd"] = value -def _get_expected_values_command(module, client, api_version, options, image, values, host_info): +def _get_expected_values_command( + module, client, api_version, options, image, values, host_info +): expected_values = {} - if 'command' in values: - command = values['command'] + if "command" in values: + command = values["command"] if command == [] and image and image["Config"].get("Cmd"): command = image["Config"].get("Cmd") - expected_values['command'] = command + expected_values["command"] = command return expected_values def _needs_container_image_command(values): - return values.get('command') == [] + return values.get("command") == [] -OPTION_AUTO_REMOVE.add_engine('docker_api', DockerAPIEngine.host_config_value('AutoRemove')) +OPTION_AUTO_REMOVE.add_engine( + "docker_api", DockerAPIEngine.host_config_value("AutoRemove") +) -OPTION_BLKIO_WEIGHT.add_engine('docker_api', DockerAPIEngine.host_config_value('BlkioWeight', update_parameter='BlkioWeight')) +OPTION_BLKIO_WEIGHT.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("BlkioWeight", update_parameter="BlkioWeight"), +) -OPTION_CAPABILITIES.add_engine('docker_api', DockerAPIEngine.host_config_value('CapAdd')) +OPTION_CAPABILITIES.add_engine( + "docker_api", DockerAPIEngine.host_config_value("CapAdd") +) -OPTION_CAP_DROP.add_engine('docker_api', DockerAPIEngine.host_config_value('CapDrop')) +OPTION_CAP_DROP.add_engine("docker_api", DockerAPIEngine.host_config_value("CapDrop")) -OPTION_CGROUP_NS_MODE.add_engine('docker_api', DockerAPIEngine.host_config_value('CgroupnsMode', min_api_version='1.41')) +OPTION_CGROUP_NS_MODE.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CgroupnsMode", min_api_version="1.41"), +) -OPTION_CGROUP_PARENT.add_engine('docker_api', DockerAPIEngine.host_config_value('CgroupParent')) +OPTION_CGROUP_PARENT.add_engine( + "docker_api", DockerAPIEngine.host_config_value("CgroupParent") +) -OPTION_COMMAND.add_engine('docker_api', DockerAPIEngine( - get_value=_get_value_command, - set_value=_set_value_command, - get_expected_values=_get_expected_values_command, - needs_container_image=_needs_container_image_command, -)) +OPTION_COMMAND.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_value_command, + set_value=_set_value_command, + get_expected_values=_get_expected_values_command, + needs_container_image=_needs_container_image_command, + ), +) -OPTION_CPU_PERIOD.add_engine('docker_api', DockerAPIEngine.host_config_value('CpuPeriod', update_parameter='CpuPeriod')) +OPTION_CPU_PERIOD.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CpuPeriod", update_parameter="CpuPeriod"), +) -OPTION_CPU_QUOTA.add_engine('docker_api', DockerAPIEngine.host_config_value('CpuQuota', update_parameter='CpuQuota')) +OPTION_CPU_QUOTA.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CpuQuota", update_parameter="CpuQuota"), +) -OPTION_CPUSET_CPUS.add_engine('docker_api', DockerAPIEngine.host_config_value('CpusetCpus', update_parameter='CpusetCpus')) +OPTION_CPUSET_CPUS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CpusetCpus", update_parameter="CpusetCpus"), +) -OPTION_CPUSET_MEMS.add_engine('docker_api', DockerAPIEngine.host_config_value('CpusetMems', update_parameter='CpusetMems')) +OPTION_CPUSET_MEMS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CpusetMems", update_parameter="CpusetMems"), +) -OPTION_CPU_SHARES.add_engine('docker_api', DockerAPIEngine.host_config_value('CpuShares', update_parameter='CpuShares')) +OPTION_CPU_SHARES.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("CpuShares", update_parameter="CpuShares"), +) -OPTION_ENTRYPOINT.add_engine('docker_api', DockerAPIEngine.config_value('Entrypoint')) +OPTION_ENTRYPOINT.add_engine("docker_api", DockerAPIEngine.config_value("Entrypoint")) -OPTION_CPUS.add_engine('docker_api', DockerAPIEngine.host_config_value('NanoCpus', preprocess_value=_preprocess_cpus)) +OPTION_CPUS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("NanoCpus", preprocess_value=_preprocess_cpus), +) -OPTION_DETACH_INTERACTIVE.add_engine('docker_api', DockerAPIEngine(get_value=_get_value_detach_interactive, set_value=_set_value_detach_interactive)) +OPTION_DETACH_INTERACTIVE.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_value_detach_interactive, set_value=_set_value_detach_interactive + ), +) -OPTION_DEVICES.add_engine('docker_api', DockerAPIEngine.host_config_value('Devices', preprocess_value=_preprocess_devices)) +OPTION_DEVICES.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("Devices", preprocess_value=_preprocess_devices), +) -OPTION_DEVICE_READ_BPS.add_engine('docker_api', DockerAPIEngine.host_config_value('BlkioDeviceReadBps', preprocess_value=_preprocess_rate_bps)) +OPTION_DEVICE_READ_BPS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "BlkioDeviceReadBps", preprocess_value=_preprocess_rate_bps + ), +) -OPTION_DEVICE_WRITE_BPS.add_engine('docker_api', DockerAPIEngine.host_config_value('BlkioDeviceWriteBps', preprocess_value=_preprocess_rate_bps)) +OPTION_DEVICE_WRITE_BPS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "BlkioDeviceWriteBps", preprocess_value=_preprocess_rate_bps + ), +) -OPTION_DEVICE_READ_IOPS.add_engine('docker_api', DockerAPIEngine.host_config_value('BlkioDeviceReadIOps', preprocess_value=_preprocess_rate_iops)) +OPTION_DEVICE_READ_IOPS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "BlkioDeviceReadIOps", preprocess_value=_preprocess_rate_iops + ), +) -OPTION_DEVICE_WRITE_IOPS.add_engine('docker_api', DockerAPIEngine.host_config_value('BlkioDeviceWriteIOps', preprocess_value=_preprocess_rate_iops)) +OPTION_DEVICE_WRITE_IOPS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "BlkioDeviceWriteIOps", preprocess_value=_preprocess_rate_iops + ), +) -OPTION_DEVICE_REQUESTS.add_engine('docker_api', DockerAPIEngine.host_config_value( - 'DeviceRequests', min_api_version='1.40', preprocess_value=_preprocess_device_requests)) +OPTION_DEVICE_REQUESTS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "DeviceRequests", + min_api_version="1.40", + preprocess_value=_preprocess_device_requests, + ), +) -OPTION_DEVICE_CGROUP_RULES.add_engine('docker_api', DockerAPIEngine.host_config_value('DeviceCgroupRules', min_api_version='1.28')) +OPTION_DEVICE_CGROUP_RULES.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("DeviceCgroupRules", min_api_version="1.28"), +) -OPTION_DNS_SERVERS.add_engine('docker_api', DockerAPIEngine.host_config_value('Dns')) +OPTION_DNS_SERVERS.add_engine("docker_api", DockerAPIEngine.host_config_value("Dns")) -OPTION_DNS_OPTS.add_engine('docker_api', DockerAPIEngine.host_config_value('DnsOptions')) +OPTION_DNS_OPTS.add_engine( + "docker_api", DockerAPIEngine.host_config_value("DnsOptions") +) -OPTION_DNS_SEARCH_DOMAINS.add_engine('docker_api', DockerAPIEngine.host_config_value('DnsSearch')) +OPTION_DNS_SEARCH_DOMAINS.add_engine( + "docker_api", DockerAPIEngine.host_config_value("DnsSearch") +) -OPTION_DOMAINNAME.add_engine('docker_api', DockerAPIEngine.config_value('Domainname')) +OPTION_DOMAINNAME.add_engine("docker_api", DockerAPIEngine.config_value("Domainname")) -OPTION_ENVIRONMENT.add_engine('docker_api', DockerAPIEngine.config_value('Env', get_expected_value=_get_expected_env_value)) +OPTION_ENVIRONMENT.add_engine( + "docker_api", + DockerAPIEngine.config_value("Env", get_expected_value=_get_expected_env_value), +) -OPTION_ETC_HOSTS.add_engine('docker_api', DockerAPIEngine.host_config_value('ExtraHosts', preprocess_value=_preprocess_etc_hosts)) +OPTION_ETC_HOSTS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "ExtraHosts", preprocess_value=_preprocess_etc_hosts + ), +) -OPTION_GROUPS.add_engine('docker_api', DockerAPIEngine.host_config_value('GroupAdd')) +OPTION_GROUPS.add_engine("docker_api", DockerAPIEngine.host_config_value("GroupAdd")) -OPTION_HEALTHCHECK.add_engine('docker_api', DockerAPIEngine.config_value( - 'Healthcheck', - preprocess_value=_preprocess_healthcheck, - postprocess_for_get=_postprocess_healthcheck_get_value, - extra_option_minimal_versions={ - 'healthcheck.start_interval': { - 'docker_api_version': '1.44', - 'detect_usage': lambda c: c.module.params['healthcheck'] and c.module.params['healthcheck']['start_interval'] is not None, +OPTION_HEALTHCHECK.add_engine( + "docker_api", + DockerAPIEngine.config_value( + "Healthcheck", + preprocess_value=_preprocess_healthcheck, + postprocess_for_get=_postprocess_healthcheck_get_value, + extra_option_minimal_versions={ + "healthcheck.start_interval": { + "docker_api_version": "1.44", + "detect_usage": lambda c: c.module.params["healthcheck"] + and c.module.params["healthcheck"]["start_interval"] is not None, + }, }, - }, -)) + ), +) -OPTION_HOSTNAME.add_engine('docker_api', DockerAPIEngine.config_value('Hostname')) +OPTION_HOSTNAME.add_engine("docker_api", DockerAPIEngine.config_value("Hostname")) -OPTION_IMAGE.add_engine('docker_api', DockerAPIEngine.config_value( - 'Image', ignore_mismatching_result=lambda module, client, api_version, option, image, container_value, expected_value, host_info: True)) +OPTION_IMAGE.add_engine( + "docker_api", + DockerAPIEngine.config_value( + "Image", + ignore_mismatching_result=lambda module, client, api_version, option, image, container_value, expected_value, host_info: True, + ), +) -OPTION_INIT.add_engine('docker_api', DockerAPIEngine.host_config_value('Init')) +OPTION_INIT.add_engine("docker_api", DockerAPIEngine.host_config_value("Init")) -OPTION_IPC_MODE.add_engine('docker_api', DockerAPIEngine.host_config_value('IpcMode', preprocess_value=_preprocess_container_names)) +OPTION_IPC_MODE.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "IpcMode", preprocess_value=_preprocess_container_names + ), +) -OPTION_KERNEL_MEMORY.add_engine('docker_api', DockerAPIEngine.host_config_value('KernelMemory', update_parameter='KernelMemory')) +OPTION_KERNEL_MEMORY.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("KernelMemory", update_parameter="KernelMemory"), +) -OPTION_LABELS.add_engine('docker_api', DockerAPIEngine.config_value( - 'Labels', get_expected_value=_get_expected_labels_value, ignore_mismatching_result=_ignore_mismatching_label_result)) +OPTION_LABELS.add_engine( + "docker_api", + DockerAPIEngine.config_value( + "Labels", + get_expected_value=_get_expected_labels_value, + ignore_mismatching_result=_ignore_mismatching_label_result, + ), +) -OPTION_LINKS.add_engine('docker_api', DockerAPIEngine.host_config_value('Links', preprocess_value=_preprocess_links)) +OPTION_LINKS.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("Links", preprocess_value=_preprocess_links), +) -OPTION_LOG_DRIVER_OPTIONS.add_engine('docker_api', DockerAPIEngine( - get_value=_get_values_log, - set_value=_set_values_log, -)) +OPTION_LOG_DRIVER_OPTIONS.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_values_log, + set_value=_set_values_log, + ), +) -OPTION_MAC_ADDRESS.add_engine('docker_api', DockerAPIEngine.config_value('MacAddress')) +OPTION_MAC_ADDRESS.add_engine("docker_api", DockerAPIEngine.config_value("MacAddress")) -OPTION_MEMORY.add_engine('docker_api', DockerAPIEngine.host_config_value('Memory', update_parameter='Memory')) +OPTION_MEMORY.add_engine( + "docker_api", DockerAPIEngine.host_config_value("Memory", update_parameter="Memory") +) -OPTION_MEMORY_RESERVATION.add_engine('docker_api', DockerAPIEngine.host_config_value('MemoryReservation', update_parameter='MemoryReservation')) +OPTION_MEMORY_RESERVATION.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "MemoryReservation", update_parameter="MemoryReservation" + ), +) -OPTION_MEMORY_SWAP.add_engine('docker_api', DockerAPIEngine.host_config_value('MemorySwap', update_parameter='MemorySwap')) +OPTION_MEMORY_SWAP.add_engine( + "docker_api", + DockerAPIEngine.host_config_value("MemorySwap", update_parameter="MemorySwap"), +) -OPTION_MEMORY_SWAPPINESS.add_engine('docker_api', DockerAPIEngine.host_config_value('MemorySwappiness')) +OPTION_MEMORY_SWAPPINESS.add_engine( + "docker_api", DockerAPIEngine.host_config_value("MemorySwappiness") +) -OPTION_STOP_TIMEOUT.add_engine('docker_api', DockerAPIEngine.config_value('StopTimeout')) +OPTION_STOP_TIMEOUT.add_engine( + "docker_api", DockerAPIEngine.config_value("StopTimeout") +) -OPTION_NETWORK.add_engine('docker_api', DockerAPIEngine( - preprocess_value=_preprocess_network_values, - get_value=_get_values_network, - set_value=_set_values_network, - ignore_mismatching_result=_ignore_mismatching_network_result, - needs_host_info=_needs_host_info_network, - extra_option_minimal_versions={ - 'networks.mac_address': { - 'docker_api_version': '1.44', - 'detect_usage': lambda c: any(net_info.get('mac_address') is not None for net_info in (c.module.params['networks'] or [])), +OPTION_NETWORK.add_engine( + "docker_api", + DockerAPIEngine( + preprocess_value=_preprocess_network_values, + get_value=_get_values_network, + set_value=_set_values_network, + ignore_mismatching_result=_ignore_mismatching_network_result, + needs_host_info=_needs_host_info_network, + extra_option_minimal_versions={ + "networks.mac_address": { + "docker_api_version": "1.44", + "detect_usage": lambda c: any( + net_info.get("mac_address") is not None + for net_info in (c.module.params["networks"] or []) + ), + }, }, - }, -)) + ), +) -OPTION_OOM_KILLER.add_engine('docker_api', DockerAPIEngine.host_config_value('OomKillDisable')) +OPTION_OOM_KILLER.add_engine( + "docker_api", DockerAPIEngine.host_config_value("OomKillDisable") +) -OPTION_OOM_SCORE_ADJ.add_engine('docker_api', DockerAPIEngine.host_config_value('OomScoreAdj')) +OPTION_OOM_SCORE_ADJ.add_engine( + "docker_api", DockerAPIEngine.host_config_value("OomScoreAdj") +) -OPTION_PID_MODE.add_engine('docker_api', DockerAPIEngine.host_config_value('PidMode', preprocess_value=_preprocess_container_names)) +OPTION_PID_MODE.add_engine( + "docker_api", + DockerAPIEngine.host_config_value( + "PidMode", preprocess_value=_preprocess_container_names + ), +) -OPTION_PIDS_LIMIT.add_engine('docker_api', DockerAPIEngine.host_config_value('PidsLimit')) +OPTION_PIDS_LIMIT.add_engine( + "docker_api", DockerAPIEngine.host_config_value("PidsLimit") +) -OPTION_PLATFORM.add_engine('docker_api', DockerAPIEngine( - get_value=_get_values_platform, - set_value=_set_values_platform, - get_expected_values=_get_expected_values_platform, - needs_container_image=_needs_container_image_platform, - needs_host_info=_needs_host_info_platform, - min_api_version='1.41', -)) +OPTION_PLATFORM.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_values_platform, + set_value=_set_values_platform, + get_expected_values=_get_expected_values_platform, + needs_container_image=_needs_container_image_platform, + needs_host_info=_needs_host_info_platform, + min_api_version="1.41", + ), +) -OPTION_PRIVILEGED.add_engine('docker_api', DockerAPIEngine.host_config_value('Privileged')) +OPTION_PRIVILEGED.add_engine( + "docker_api", DockerAPIEngine.host_config_value("Privileged") +) -OPTION_READ_ONLY.add_engine('docker_api', DockerAPIEngine.host_config_value('ReadonlyRootfs')) +OPTION_READ_ONLY.add_engine( + "docker_api", DockerAPIEngine.host_config_value("ReadonlyRootfs") +) -OPTION_RESTART_POLICY.add_engine('docker_api', DockerAPIEngine( - get_value=_get_values_restart, - set_value=_set_values_restart, - update_value=_update_value_restart, -)) +OPTION_RESTART_POLICY.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_values_restart, + set_value=_set_values_restart, + update_value=_update_value_restart, + ), +) -OPTION_RUNTIME.add_engine('docker_api', DockerAPIEngine.host_config_value('Runtime')) +OPTION_RUNTIME.add_engine("docker_api", DockerAPIEngine.host_config_value("Runtime")) -OPTION_SECURITY_OPTS.add_engine('docker_api', DockerAPIEngine.host_config_value('SecurityOpt')) +OPTION_SECURITY_OPTS.add_engine( + "docker_api", DockerAPIEngine.host_config_value("SecurityOpt") +) -OPTION_SHM_SIZE.add_engine('docker_api', DockerAPIEngine.host_config_value('ShmSize')) +OPTION_SHM_SIZE.add_engine("docker_api", DockerAPIEngine.host_config_value("ShmSize")) -OPTION_STOP_SIGNAL.add_engine('docker_api', DockerAPIEngine.config_value('StopSignal')) +OPTION_STOP_SIGNAL.add_engine("docker_api", DockerAPIEngine.config_value("StopSignal")) -OPTION_STORAGE_OPTS.add_engine('docker_api', DockerAPIEngine.host_config_value('StorageOpt')) +OPTION_STORAGE_OPTS.add_engine( + "docker_api", DockerAPIEngine.host_config_value("StorageOpt") +) -OPTION_SYSCTLS.add_engine('docker_api', DockerAPIEngine.host_config_value('Sysctls')) +OPTION_SYSCTLS.add_engine("docker_api", DockerAPIEngine.host_config_value("Sysctls")) -OPTION_TMPFS.add_engine('docker_api', DockerAPIEngine.host_config_value('Tmpfs')) +OPTION_TMPFS.add_engine("docker_api", DockerAPIEngine.host_config_value("Tmpfs")) -OPTION_TTY.add_engine('docker_api', DockerAPIEngine.config_value('Tty')) +OPTION_TTY.add_engine("docker_api", DockerAPIEngine.config_value("Tty")) -OPTION_ULIMITS.add_engine('docker_api', DockerAPIEngine.host_config_value('Ulimits')) +OPTION_ULIMITS.add_engine("docker_api", DockerAPIEngine.host_config_value("Ulimits")) -OPTION_USER.add_engine('docker_api', DockerAPIEngine.config_value('User')) +OPTION_USER.add_engine("docker_api", DockerAPIEngine.config_value("User")) -OPTION_USERNS_MODE.add_engine('docker_api', DockerAPIEngine.host_config_value('UsernsMode')) +OPTION_USERNS_MODE.add_engine( + "docker_api", DockerAPIEngine.host_config_value("UsernsMode") +) -OPTION_UTS.add_engine('docker_api', DockerAPIEngine.host_config_value('UTSMode')) +OPTION_UTS.add_engine("docker_api", DockerAPIEngine.host_config_value("UTSMode")) -OPTION_VOLUME_DRIVER.add_engine('docker_api', DockerAPIEngine.host_config_value('VolumeDriver')) +OPTION_VOLUME_DRIVER.add_engine( + "docker_api", DockerAPIEngine.host_config_value("VolumeDriver") +) -OPTION_VOLUMES_FROM.add_engine('docker_api', DockerAPIEngine.host_config_value('VolumesFrom')) +OPTION_VOLUMES_FROM.add_engine( + "docker_api", DockerAPIEngine.host_config_value("VolumesFrom") +) -OPTION_WORKING_DIR.add_engine('docker_api', DockerAPIEngine.config_value('WorkingDir')) +OPTION_WORKING_DIR.add_engine("docker_api", DockerAPIEngine.config_value("WorkingDir")) -OPTION_MOUNTS_VOLUMES.add_engine('docker_api', DockerAPIEngine( - get_value=_get_values_mounts, - get_expected_values=_get_expected_values_mounts, - set_value=_set_values_mounts, - extra_option_minimal_versions={ - 'mounts.non_recursive': { - 'docker_api_version': '1.40', - 'detect_usage': lambda c: any(mount.get('non_recursive') is not None for mount in (c.module.params['mounts'] or [])), +OPTION_MOUNTS_VOLUMES.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_values_mounts, + get_expected_values=_get_expected_values_mounts, + set_value=_set_values_mounts, + extra_option_minimal_versions={ + "mounts.non_recursive": { + "docker_api_version": "1.40", + "detect_usage": lambda c: any( + mount.get("non_recursive") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.create_mountpoint": { + "docker_api_version": "1.42", + "detect_usage": lambda c: any( + mount.get("create_mountpoint") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.type=cluster": { + "docker_api_version": "1.42", + "detect_usage": lambda c: any( + mount.get("type") == "cluster" + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.read_only_non_recursive": { + "docker_api_version": "1.44", + "detect_usage": lambda c: any( + mount.get("read_only_non_recursive") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.read_only_force_recursive": { + "docker_api_version": "1.44", + "detect_usage": lambda c: any( + mount.get("read_only_force_recursive") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.subpath": { + "docker_api_version": "1.45", + "detect_usage": lambda c: any( + mount.get("subpath") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.tmpfs_options": { + "docker_api_version": "1.46", + "detect_usage": lambda c: any( + mount.get("tmpfs_options") is not None + for mount in (c.module.params["mounts"] or []) + ), + }, + "mounts.type=image": { + "docker_api_version": "1.47", + "detect_usage": lambda c: any( + mount.get("type") == "image" + for mount in (c.module.params["mounts"] or []) + ), + }, }, - 'mounts.create_mountpoint': { - 'docker_api_version': '1.42', - 'detect_usage': lambda c: any(mount.get('create_mountpoint') is not None for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.type=cluster': { - 'docker_api_version': '1.42', - 'detect_usage': lambda c: any(mount.get('type') == 'cluster' for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.read_only_non_recursive': { - 'docker_api_version': '1.44', - 'detect_usage': lambda c: any(mount.get('read_only_non_recursive') is not None for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.read_only_force_recursive': { - 'docker_api_version': '1.44', - 'detect_usage': lambda c: any(mount.get('read_only_force_recursive') is not None for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.subpath': { - 'docker_api_version': '1.45', - 'detect_usage': lambda c: any(mount.get('subpath') is not None for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.tmpfs_options': { - 'docker_api_version': '1.46', - 'detect_usage': lambda c: any(mount.get('tmpfs_options') is not None for mount in (c.module.params['mounts'] or [])), - }, - 'mounts.type=image': { - 'docker_api_version': '1.47', - 'detect_usage': lambda c: any(mount.get('type') == 'image' for mount in (c.module.params['mounts'] or [])), - }, - }, -)) + ), +) -OPTION_PORTS.add_engine('docker_api', DockerAPIEngine( - get_value=_get_values_ports, - get_expected_values=_get_expected_values_ports, - set_value=_set_values_ports, - preprocess_value=_preprocess_value_ports, -)) +OPTION_PORTS.add_engine( + "docker_api", + DockerAPIEngine( + get_value=_get_values_ports, + get_expected_values=_get_expected_values_ports, + set_value=_set_values_ports, + preprocess_value=_preprocess_value_ports, + ), +) diff --git a/plugins/module_utils/module_container/module.py b/plugins/module_utils/module_container/module.py index 8cf333d9..d31a27fe 100644 --- a/plugins/module_utils/module_container/module.py +++ b/plugins/module_utils/module_container/module.py @@ -9,7 +9,9 @@ import re from time import sleep from ansible.module_utils.common.text.converters import to_text - +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.util import ( DifferenceTracker, DockerBaseClass, @@ -18,8 +20,6 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( sanitize_result, ) -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import parse_repository_tag - class Container(DockerBaseClass): def __init__(self, container, engine_driver): @@ -42,15 +42,27 @@ class Container(DockerBaseClass): @property def removing(self): - return self.engine_driver.is_container_removing(self.container) if self.container else False + return ( + self.engine_driver.is_container_removing(self.container) + if self.container + else False + ) @property def running(self): - return self.engine_driver.is_container_running(self.container) if self.container else False + return ( + self.engine_driver.is_container_running(self.container) + if self.container + else False + ) @property def paused(self): - return self.engine_driver.is_container_paused(self.container) if self.container else False + return ( + self.engine_driver.is_container_paused(self.container) + if self.container + else False + ) class ContainerManager(DockerBaseClass): @@ -61,52 +73,62 @@ class ContainerManager(DockerBaseClass): self.options = active_options self.all_options = self._collect_all_options(active_options) self.check_mode = self.module.check_mode - self.param_cleanup = self.module.params['cleanup'] - self.param_container_default_behavior = self.module.params['container_default_behavior'] - self.param_default_host_ip = self.module.params['default_host_ip'] - self.param_debug = self.module.params['debug'] - self.param_force_kill = self.module.params['force_kill'] - self.param_image = self.module.params['image'] - self.param_image_comparison = self.module.params['image_comparison'] - self.param_image_label_mismatch = self.module.params['image_label_mismatch'] - self.param_image_name_mismatch = self.module.params['image_name_mismatch'] - self.param_keep_volumes = self.module.params['keep_volumes'] - self.param_kill_signal = self.module.params['kill_signal'] - self.param_name = self.module.params['name'] - self.param_networks_cli_compatible = self.module.params['networks_cli_compatible'] - self.param_output_logs = self.module.params['output_logs'] - self.param_paused = self.module.params['paused'] - self.param_pull = self.module.params['pull'] + self.param_cleanup = self.module.params["cleanup"] + self.param_container_default_behavior = self.module.params[ + "container_default_behavior" + ] + self.param_default_host_ip = self.module.params["default_host_ip"] + self.param_debug = self.module.params["debug"] + self.param_force_kill = self.module.params["force_kill"] + self.param_image = self.module.params["image"] + self.param_image_comparison = self.module.params["image_comparison"] + self.param_image_label_mismatch = self.module.params["image_label_mismatch"] + self.param_image_name_mismatch = self.module.params["image_name_mismatch"] + self.param_keep_volumes = self.module.params["keep_volumes"] + self.param_kill_signal = self.module.params["kill_signal"] + self.param_name = self.module.params["name"] + self.param_networks_cli_compatible = self.module.params[ + "networks_cli_compatible" + ] + self.param_output_logs = self.module.params["output_logs"] + self.param_paused = self.module.params["paused"] + self.param_pull = self.module.params["pull"] if self.param_pull is True: - self.param_pull = 'always' + self.param_pull = "always" if self.param_pull is False: - self.param_pull = 'missing' - self.param_pull_check_mode_behavior = self.module.params['pull_check_mode_behavior'] - self.param_recreate = self.module.params['recreate'] - self.param_removal_wait_timeout = self.module.params['removal_wait_timeout'] - self.param_healthy_wait_timeout = self.module.params['healthy_wait_timeout'] + self.param_pull = "missing" + self.param_pull_check_mode_behavior = self.module.params[ + "pull_check_mode_behavior" + ] + self.param_recreate = self.module.params["recreate"] + self.param_removal_wait_timeout = self.module.params["removal_wait_timeout"] + self.param_healthy_wait_timeout = self.module.params["healthy_wait_timeout"] if self.param_healthy_wait_timeout <= 0: self.param_healthy_wait_timeout = None - self.param_restart = self.module.params['restart'] - self.param_state = self.module.params['state'] + self.param_restart = self.module.params["restart"] + self.param_state = self.module.params["state"] self._parse_comparisons() self._update_params() - self.results = {'changed': False, 'actions': []} + self.results = {"changed": False, "actions": []} self.diff = {} self.diff_tracker = DifferenceTracker() self.facts = {} if self.param_default_host_ip: valid_ip = False - if re.match(r'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$', self.param_default_host_ip): + if re.match( + r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$", self.param_default_host_ip + ): valid_ip = True - if re.match(r'^\[[0-9a-fA-F:]+\]$', self.param_default_host_ip): + if re.match(r"^\[[0-9a-fA-F:]+\]$", self.param_default_host_ip): valid_ip = True - if re.match(r'^[0-9a-fA-F:]+$', self.param_default_host_ip): - self.param_default_host_ip = f'[{self.param_default_host_ip}]' + if re.match(r"^[0-9a-fA-F:]+$", self.param_default_host_ip): + self.param_default_host_ip = f"[{self.param_default_host_ip}]" valid_ip = True if not valid_ip: - self.fail('The value of default_host_ip must be an empty string, an IPv4 address, ' - f'or an IPv6 address. Got "{self.param_default_host_ip}" instead.') + self.fail( + "The value of default_host_ip must be an empty string, an IPv4 address, " + f'or an IPv6 address. Got "{self.param_default_host_ip}" instead.' + ) def _collect_all_options(self, active_options): all_options = {} @@ -119,8 +141,8 @@ class ContainerManager(DockerBaseClass): all_module_options = set() for option, data in self.module.argument_spec.items(): all_module_options.add(option) - if 'aliases' in data: - for alias in data['aliases']: + if "aliases" in data: + for alias in data["aliases"]: all_module_options.add(alias) return all_module_options @@ -135,62 +157,81 @@ class ContainerManager(DockerBaseClass): for alias in option.ansible_aliases: comp_aliases[alias] = option_name # Process comparisons specified by user - if self.module.params.get('comparisons'): + if self.module.params.get("comparisons"): # If '*' appears in comparisons, process it first - if '*' in self.module.params['comparisons']: - value = self.module.params['comparisons']['*'] - if value not in ('strict', 'ignore'): - self.fail("The wildcard can only be used with comparison modes 'strict' and 'ignore'!") + if "*" in self.module.params["comparisons"]: + value = self.module.params["comparisons"]["*"] + if value not in ("strict", "ignore"): + self.fail( + "The wildcard can only be used with comparison modes 'strict' and 'ignore'!" + ) for option in self.all_options.values(): - if option.name == 'networks': + if option.name == "networks": # `networks` is special: only update if # some value is actually specified - if self.module.params['networks'] is None: + if self.module.params["networks"] is None: continue option.comparison = value # Now process all other comparisons. comp_aliases_used = {} - for key, value in self.module.params['comparisons'].items(): - if key == '*': + for key, value in self.module.params["comparisons"].items(): + if key == "*": continue # Find main key key_main = comp_aliases.get(key) if key_main is None: if key_main in all_module_options: - self.fail(f"The module option '{key}' cannot be specified in the comparisons dict, " - "since it does not correspond to container's state!") - if key not in self.all_options or self.all_options[key].not_an_ansible_option: + self.fail( + f"The module option '{key}' cannot be specified in the comparisons dict, " + "since it does not correspond to container's state!" + ) + if ( + key not in self.all_options + or self.all_options[key].not_an_ansible_option + ): self.fail(f"Unknown module option '{key}' in comparisons dict!") key_main = key if key_main in comp_aliases_used: - self.fail(f"Both '{key}' and '{comp_aliases_used[key_main]}' (aliases of {key_main}) are specified in comparisons dict!") + self.fail( + f"Both '{key}' and '{comp_aliases_used[key_main]}' (aliases of {key_main}) are specified in comparisons dict!" + ) comp_aliases_used[key_main] = key # Check value and update accordingly - if value in ('strict', 'ignore'): + if value in ("strict", "ignore"): self.all_options[key_main].comparison = value - elif value == 'allow_more_present': - if self.all_options[key_main].comparison_type == 'value': - self.fail(f"Option '{key}' is a value and not a set/list/dict, so its comparison cannot be {value}") + elif value == "allow_more_present": + if self.all_options[key_main].comparison_type == "value": + self.fail( + f"Option '{key}' is a value and not a set/list/dict, so its comparison cannot be {value}" + ) self.all_options[key_main].comparison = value else: self.fail(f"Unknown comparison mode '{value}'!") # Copy values for option in self.all_options.values(): if option.copy_comparison_from is not None: - option.comparison = self.all_options[option.copy_comparison_from].comparison + option.comparison = self.all_options[ + option.copy_comparison_from + ].comparison def _update_params(self): - if self.param_networks_cli_compatible is True and self.module.params['networks'] and self.module.params['network_mode'] is None: + if ( + self.param_networks_cli_compatible is True + and self.module.params["networks"] + and self.module.params["network_mode"] is None + ): # Same behavior as Docker CLI: if networks are specified, use the name of the first network as the value for network_mode # (assuming no explicit value is specified for network_mode) - self.module.params['network_mode'] = self.module.params['networks'][0]['name'] - if self.param_container_default_behavior == 'compatibility': + self.module.params["network_mode"] = self.module.params["networks"][0][ + "name" + ] + if self.param_container_default_behavior == "compatibility": old_default_values = dict( auto_remove=False, detach=True, init=False, interactive=False, - memory='0', + memory="0", paused=False, privileged=False, read_only=False, @@ -204,37 +245,49 @@ class ContainerManager(DockerBaseClass): self.client.fail(*args, **kwargs) def run(self): - if self.param_state in ('stopped', 'started', 'present', 'healthy'): + if self.param_state in ("stopped", "started", "present", "healthy"): self.present(self.param_state) - elif self.param_state == 'absent': + elif self.param_state == "absent": self.absent() if not self.check_mode and not self.param_debug: - self.results.pop('actions') + self.results.pop("actions") if self.module._diff or self.param_debug: - self.diff['before'], self.diff['after'] = self.diff_tracker.get_before_after() - self.results['diff'] = self.diff + self.diff["before"], self.diff["after"] = ( + self.diff_tracker.get_before_after() + ) + self.results["diff"] = self.diff if self.facts: - self.results['container'] = self.facts + self.results["container"] = self.facts - def wait_for_state(self, container_id, complete_states=None, wait_states=None, accept_removal=False, max_wait=None, health_state=False): + def wait_for_state( + self, + container_id, + complete_states=None, + wait_states=None, + accept_removal=False, + max_wait=None, + health_state=False, + ): delay = 1.0 total_wait = 0 while True: # Inspect container - result = self.engine_driver.inspect_container_by_id(self.client, container_id) + result = self.engine_driver.inspect_container_by_id( + self.client, container_id + ) if result is None: if accept_removal: return result msg = f'Encontered vanished container while waiting for container "{container_id}"' self.fail(msg) # Check container state - state_info = result.get('State') or {} + state_info = result.get("State") or {} if health_state: - state_info = state_info.get('Health') or {} - state = state_info.get('Status') + state_info = state_info.get("Health") or {} + state = state_info.get("Status") if complete_states is not None and state in complete_states: return result if wait_states is not None and state not in wait_states: @@ -242,7 +295,7 @@ class ContainerManager(DockerBaseClass): self.fail(msg, container=result) # Wait if max_wait is not None: - if total_wait > max_wait or delay < 1E-4: + if total_wait > max_wait or delay < 1e-4: msg = f'Timeout of {max_wait} seconds exceeded while waiting for container "{container_id}"' self.fail(msg, container=result) if total_wait + delay > max_wait: @@ -261,10 +314,19 @@ class ContainerManager(DockerBaseClass): values = {} engine = options.get_engine(self.engine_driver.name) for option in options.all_options: - if not option.not_an_ansible_option and self.module.params[option.name] is not None: + if ( + not option.not_an_ansible_option + and self.module.params[option.name] is not None + ): values[option.name] = self.module.params[option.name] values = options.preprocess(self.module, values) - engine.preprocess_value(self.module, self.client, self.engine_driver.get_api_version(self.client), options.options, values) + engine.preprocess_value( + self.module, + self.client, + self.engine_driver.get_api_version(self.client), + options.options, + values, + ) parameters.append((options, values)) return parameters @@ -295,53 +357,82 @@ class ContainerManager(DockerBaseClass): # container needs to be restarted, we use the existing container's # image ID. image, container_image, comparison_image = self._get_image( - container, needs_container_image=self._needs_container_image()) + container, needs_container_image=self._needs_container_image() + ) self.log(image, pretty_print=True) - host_info = self.engine_driver.get_host_info(self.client) if self._needs_host_info() else None + host_info = ( + self.engine_driver.get_host_info(self.client) + if self._needs_host_info() + else None + ) if not container.exists or container.removing: # New container if container.removing: - self.log('Found container in removal phase') + self.log("Found container in removal phase") else: - self.log('No container found') + self.log("No container found") if not self.param_image: - self.fail('Cannot create container when image is not specified!') - self.diff_tracker.add('exists', parameter=True, active=False) + self.fail("Cannot create container when image is not specified!") + self.diff_tracker.add("exists", parameter=True, active=False) if container.removing and not self.check_mode: # Wait for container to be removed before trying to create it self.wait_for_state( - container.id, wait_states=['removing'], accept_removal=True, max_wait=self.param_removal_wait_timeout) + container.id, + wait_states=["removing"], + accept_removal=True, + max_wait=self.param_removal_wait_timeout, + ) new_container = self.container_create(self.param_image) if new_container: container = new_container container_created = True else: # Existing container - different, differences = self.has_different_configuration(container, container_image, comparison_image, host_info) + different, differences = self.has_different_configuration( + container, container_image, comparison_image, host_info + ) image_different = False - if self.all_options['image'].comparison == 'strict': + if self.all_options["image"].comparison == "strict": image_different = self._image_is_different(image, container) - if self.param_image_name_mismatch == 'recreate' and self.param_image is not None and self.param_image != container.image_name: + if ( + self.param_image_name_mismatch == "recreate" + and self.param_image is not None + and self.param_image != container.image_name + ): different = True - self.diff_tracker.add('image_name', parameter=self.param_image, active=container.image_name) + self.diff_tracker.add( + "image_name", + parameter=self.param_image, + active=container.image_name, + ) if image_different or different or self.param_recreate: self.diff_tracker.merge(differences) - self.diff['differences'] = differences.get_legacy_docker_container_diffs() + self.diff["differences"] = ( + differences.get_legacy_docker_container_diffs() + ) if image_different: - self.diff['image_different'] = True + self.diff["image_different"] = True self.log("differences") - self.log(differences.get_legacy_docker_container_diffs(), pretty_print=True) + self.log( + differences.get_legacy_docker_container_diffs(), pretty_print=True + ) image_to_use = self.param_image if not image_to_use and container and container.image: image_to_use = container.image if not image_to_use: - self.fail('Cannot recreate container when image is not specified or cannot be extracted from current container!') + self.fail( + "Cannot recreate container when image is not specified or cannot be extracted from current container!" + ) if container.running: self.container_stop(container.id) self.container_remove(container.id) if not self.check_mode: self.wait_for_state( - container.id, wait_states=['removing'], accept_removal=True, max_wait=self.param_removal_wait_timeout) + container.id, + wait_states=["removing"], + accept_removal=True, + max_wait=self.param_removal_wait_timeout, + ) new_container = self.container_create(image_to_use) if new_container: container = new_container @@ -349,43 +440,57 @@ class ContainerManager(DockerBaseClass): comparison_image = image if container and container.exists: - container = self.update_limits(container, container_image, comparison_image, host_info) + container = self.update_limits( + container, container_image, comparison_image, host_info + ) container = self.update_networks(container, container_created) - if state in ('started', 'healthy') and not container.running: - self.diff_tracker.add('running', parameter=True, active=was_running) + if state in ("started", "healthy") and not container.running: + self.diff_tracker.add("running", parameter=True, active=was_running) container = self.container_start(container.id) - elif state in ('started', 'healthy') and self.param_restart: - self.diff_tracker.add('running', parameter=True, active=was_running) - self.diff_tracker.add('restarted', parameter=True, active=False) + elif state in ("started", "healthy") and self.param_restart: + self.diff_tracker.add("running", parameter=True, active=was_running) + self.diff_tracker.add("restarted", parameter=True, active=False) container = self.container_restart(container.id) - elif state == 'stopped' and container.running: - self.diff_tracker.add('running', parameter=False, active=was_running) + elif state == "stopped" and container.running: + self.diff_tracker.add("running", parameter=False, active=was_running) self.container_stop(container.id) container = self._get_container(container.id) - if state in ('started', 'healthy') and self.param_paused is not None and container.paused != self.param_paused: - self.diff_tracker.add('paused', parameter=self.param_paused, active=was_paused) + if ( + state in ("started", "healthy") + and self.param_paused is not None + and container.paused != self.param_paused + ): + self.diff_tracker.add( + "paused", parameter=self.param_paused, active=was_paused + ) if not self.check_mode: try: if self.param_paused: - self.engine_driver.pause_container(self.client, container.id) + self.engine_driver.pause_container( + self.client, container.id + ) else: - self.engine_driver.unpause_container(self.client, container.id) + self.engine_driver.unpause_container( + self.client, container.id + ) except Exception as exc: - self.fail(f"Error {'pausing' if self.param_paused else 'unpausing'} container {container.id}: {exc}") + self.fail( + f"Error {'pausing' if self.param_paused else 'unpausing'} container {container.id}: {exc}" + ) container = self._get_container(container.id) - self.results['changed'] = True - self.results['actions'].append(dict(set_paused=self.param_paused)) + self.results["changed"] = True + self.results["actions"].append(dict(set_paused=self.param_paused)) self.facts = container.raw - if state == 'healthy' and not self.check_mode: + if state == "healthy" and not self.check_mode: # `None` means that no health check enabled; simply treat this as 'healthy' inspect_result = self.wait_for_state( container.id, - wait_states=['starting', 'unhealthy'], - complete_states=['healthy', None], + wait_states=["starting", "unhealthy"], + complete_states=["healthy", None], max_wait=self.param_healthy_wait_timeout, health_state=True, ) @@ -397,18 +502,18 @@ class ContainerManager(DockerBaseClass): container = self._get_container(self.param_name) if container.exists: if container.running: - self.diff_tracker.add('running', parameter=False, active=True) + self.diff_tracker.add("running", parameter=False, active=True) self.container_stop(container.id) - self.diff_tracker.add('exists', parameter=False, active=True) + self.diff_tracker.add("exists", parameter=False, active=True) self.container_remove(container.id) def _output_logs(self, msg): self.module.log(msg=msg) def _get_container(self, container): - ''' + """ Expects container ID or Name. Returns a container object - ''' + """ container = self.engine_driver.inspect_container_by_name(self.client, container) return Container(container, self.engine_driver) @@ -422,18 +527,22 @@ class ContainerManager(DockerBaseClass): repository, tag = parse_repository_tag(image) if not tag: tag = "latest" - image = self.engine_driver.inspect_image_by_name(self.client, repository, tag) + image = self.engine_driver.inspect_image_by_name( + self.client, repository, tag + ) return image or fallback def _get_image(self, container, needs_container_image=False): image_parameter = self.param_image get_container_image = needs_container_image or not image_parameter - container_image = self._get_container_image(container) if get_container_image else None + container_image = ( + self._get_container_image(container) if get_container_image else None + ) if container_image: self.log("current image") self.log(container_image, pretty_print=True) if not image_parameter: - self.log('No image specified') + self.log("No image specified") return None, container_image, container_image if is_image_name_id(image_parameter): image = self.engine_driver.inspect_image_by_id(self.client, image_parameter) @@ -443,35 +552,47 @@ class ContainerManager(DockerBaseClass): repository, tag = parse_repository_tag(image_parameter) if not tag: tag = "latest" - image = self.engine_driver.inspect_image_by_name(self.client, repository, tag) + image = self.engine_driver.inspect_image_by_name( + self.client, repository, tag + ) if not image and self.param_pull == "never": - self.client.fail(f"Cannot find image with name {repository}:{tag}, and pull=never") + self.client.fail( + f"Cannot find image with name {repository}:{tag}, and pull=never" + ) if not image or self.param_pull == "always": if not self.check_mode: self.log("Pull the image.") image, alreadyToLatest = self.engine_driver.pull_image( - self.client, repository, tag, platform=self.module.params['platform']) + self.client, + repository, + tag, + platform=self.module.params["platform"], + ) if alreadyToLatest: - self.results['changed'] = False - self.results['actions'].append(dict(pulled_image=f"{repository}:{tag}", changed=False)) + self.results["changed"] = False + self.results["actions"].append( + dict(pulled_image=f"{repository}:{tag}", changed=False) + ) else: - self.results['changed'] = True - self.results['actions'].append(dict(pulled_image=f"{repository}:{tag}", changed=True)) - elif not image or self.param_pull_check_mode_behavior == 'always': + self.results["changed"] = True + self.results["actions"].append( + dict(pulled_image=f"{repository}:{tag}", changed=True) + ) + elif not image or self.param_pull_check_mode_behavior == "always": # If the image is not there, or pull_check_mode_behavior == 'always', claim we'll # pull. (Implicitly: if the image is there, claim it already was latest unless # pull_check_mode_behavior == 'always'.) - self.results['changed'] = True + self.results["changed"] = True action = dict(pulled_image=f"{repository}:{tag}") if not image: - action['changed'] = True - self.results['actions'].append(action) + action["changed"] = True + self.results["actions"].append(action) self.log("image") self.log(image, pretty_print=True) comparison_image = image - if self.param_image_comparison == 'current-image': + if self.param_image_comparison == "current-image": if not get_container_image: container_image = self._get_container_image(container) comparison_image = container_image @@ -479,10 +600,12 @@ class ContainerManager(DockerBaseClass): return image, container_image, comparison_image def _image_is_different(self, image, container): - if image and image.get('Id'): + if image and image.get("Id"): if container and container.image: - if image.get('Id') != container.image: - self.diff_tracker.add('image', parameter=image.get('Id'), active=container.image) + if image.get("Id") != container.image: + self.diff_tracker.add( + "image", parameter=image.get("Id"), active=container.image + ) return True return False @@ -491,15 +614,44 @@ class ContainerManager(DockerBaseClass): for options, values in self.parameters: engine = options.get_engine(self.engine_driver.name) if engine.can_set_value(self.engine_driver.get_api_version(self.client)): - engine.set_value(self.module, params, self.engine_driver.get_api_version(self.client), options.options, values) - params['Image'] = image + engine.set_value( + self.module, + params, + self.engine_driver.get_api_version(self.client), + options.options, + values, + ) + params["Image"] = image return params - def _record_differences(self, differences, options, param_values, engine, container, container_image, image, host_info): + def _record_differences( + self, + differences, + options, + param_values, + engine, + container, + container_image, + image, + host_info, + ): container_values = engine.get_value( - self.module, container.raw, self.engine_driver.get_api_version(self.client), options.options, container_image, host_info) + self.module, + container.raw, + self.engine_driver.get_api_version(self.client), + options.options, + container_image, + host_info, + ) expected_values = engine.get_expected_values( - self.module, self.client, self.engine_driver.get_api_version(self.client), options.options, image, param_values.copy(), host_info) + self.module, + self.client, + self.engine_driver.get_api_version(self.client), + options.options, + image, + param_values.copy(), + host_info, + ) for option in options.options: if option.name in expected_values: param_value = expected_values[option.name] @@ -508,30 +660,43 @@ class ContainerManager(DockerBaseClass): if not match: # No match. - if engine.ignore_mismatching_result(self.module, self.client, self.engine_driver.get_api_version(self.client), - option, image, container_value, param_value, host_info): + if engine.ignore_mismatching_result( + self.module, + self.client, + self.engine_driver.get_api_version(self.client), + option, + image, + container_value, + param_value, + host_info, + ): # Ignore the result continue # Record the differences p = param_value c = container_value - if option.comparison_type == 'set': + if option.comparison_type == "set": # Since the order does not matter, sort so that the diff output is better. if p is not None: p = sorted(p) if c is not None: c = sorted(c) - elif option.comparison_type == 'set(dict)': + elif option.comparison_type == "set(dict)": # Since the order does not matter, sort so that the diff output is better. - if option.name == 'expected_mounts': + if option.name == "expected_mounts": # For selected values, use one entry as key def sort_key_fn(x): - return x['target'] + return x["target"] + else: # We sort the list of dictionaries by using the sorted items of a dict as its key. def sort_key_fn(x): - return sorted((a, to_text(b, errors='surrogate_or_strict')) for a, b in x.items()) + return sorted( + (a, to_text(b, errors="surrogate_or_strict")) + for a, b in x.items() + ) + if p is not None: p = sorted(p, key=sort_key_fn) if c is not None: @@ -544,22 +709,53 @@ class ContainerManager(DockerBaseClass): for options, param_values in self.parameters: engine = options.get_engine(self.engine_driver.name) if engine.can_update_value(self.engine_driver.get_api_version(self.client)): - self._record_differences(update_differences, options, param_values, engine, container, container_image, image, host_info) + self._record_differences( + update_differences, + options, + param_values, + engine, + container, + container_image, + image, + host_info, + ) else: - self._record_differences(differences, options, param_values, engine, container, container_image, image, host_info) + self._record_differences( + differences, + options, + param_values, + engine, + container, + container_image, + image, + host_info, + ) has_differences = not differences.empty # Only consider differences of properties that can be updated when there are also other differences if has_differences: differences.merge(update_differences) return has_differences, differences - def has_different_resource_limits(self, container, container_image, image, host_info): + def has_different_resource_limits( + self, container, container_image, image, host_info + ): differences = DifferenceTracker() for options, param_values in self.parameters: engine = options.get_engine(self.engine_driver.name) - if not engine.can_update_value(self.engine_driver.get_api_version(self.client)): + if not engine.can_update_value( + self.engine_driver.get_api_version(self.client) + ): continue - self._record_differences(differences, options, param_values, engine, container, container_image, image, host_info) + self._record_differences( + differences, + options, + param_values, + engine, + container, + container_image, + image, + host_info, + ) has_differences = not differences.empty return has_differences, differences @@ -567,16 +763,28 @@ class ContainerManager(DockerBaseClass): result = {} for options, values in self.parameters: engine = options.get_engine(self.engine_driver.name) - if not engine.can_update_value(self.engine_driver.get_api_version(self.client)): + if not engine.can_update_value( + self.engine_driver.get_api_version(self.client) + ): continue - engine.update_value(self.module, result, self.engine_driver.get_api_version(self.client), options.options, values) + engine.update_value( + self.module, + result, + self.engine_driver.get_api_version(self.client), + options.options, + values, + ) return result def update_limits(self, container, container_image, image, host_info): - limits_differ, different_limits = self.has_different_resource_limits(container, container_image, image, host_info) + limits_differ, different_limits = self.has_different_resource_limits( + container, container_image, image, host_info + ) if limits_differ: self.log("limit differences:") - self.log(different_limits.get_legacy_docker_container_diffs(), pretty_print=True) + self.log( + different_limits.get_legacy_docker_container_diffs(), pretty_print=True + ) self.diff_tracker.merge(different_limits) if limits_differ and not self.check_mode: self.container_update(container.id, self._compose_update_parameters()) @@ -584,148 +792,202 @@ class ContainerManager(DockerBaseClass): return container def has_network_differences(self, container): - ''' + """ Check if the container is connected to requested networks with expected options: links, aliases, ipv4, ipv6 - ''' + """ different = False differences = [] - if not self.module.params['networks']: + if not self.module.params["networks"]: return different, differences - if not container.container.get('NetworkSettings'): - self.fail("has_missing_networks: Error parsing container properties. NetworkSettings missing.") + if not container.container.get("NetworkSettings"): + self.fail( + "has_missing_networks: Error parsing container properties. NetworkSettings missing." + ) - connected_networks = container.container['NetworkSettings']['Networks'] - for network in self.module.params['networks']: - network_info = connected_networks.get(network['name']) + connected_networks = container.container["NetworkSettings"]["Networks"] + for network in self.module.params["networks"]: + network_info = connected_networks.get(network["name"]) if network_info is None: different = True - differences.append(dict( - parameter=network, - container=None - )) + differences.append(dict(parameter=network, container=None)) else: diff = False - network_info_ipam = network_info.get('IPAMConfig') or {} - if network.get('ipv4_address') and network['ipv4_address'] != network_info_ipam.get('IPv4Address'): + network_info_ipam = network_info.get("IPAMConfig") or {} + if network.get("ipv4_address") and network[ + "ipv4_address" + ] != network_info_ipam.get("IPv4Address"): diff = True - if network.get('ipv6_address') and network['ipv6_address'] != network_info_ipam.get('IPv6Address'): + if network.get("ipv6_address") and network[ + "ipv6_address" + ] != network_info_ipam.get("IPv6Address"): diff = True - if network.get('aliases'): - if not compare_generic(network['aliases'], network_info.get('Aliases'), 'allow_more_present', 'set'): + if network.get("aliases"): + if not compare_generic( + network["aliases"], + network_info.get("Aliases"), + "allow_more_present", + "set", + ): diff = True - if network.get('links'): + if network.get("links"): expected_links = [] - for link, alias in network['links']: + for link, alias in network["links"]: expected_links.append(f"{link}:{alias}") - if not compare_generic(expected_links, network_info.get('Links'), 'allow_more_present', 'set'): + if not compare_generic( + expected_links, + network_info.get("Links"), + "allow_more_present", + "set", + ): diff = True - if network.get('mac_address') and network['mac_address'] != network_info.get('MacAddress'): + if network.get("mac_address") and network[ + "mac_address" + ] != network_info.get("MacAddress"): diff = True if diff: different = True - differences.append(dict( - parameter=network, - container=dict( - name=network['name'], - ipv4_address=network_info_ipam.get('IPv4Address'), - ipv6_address=network_info_ipam.get('IPv6Address'), - aliases=network_info.get('Aliases'), - links=network_info.get('Links'), - mac_address=network_info.get('MacAddress'), + differences.append( + dict( + parameter=network, + container=dict( + name=network["name"], + ipv4_address=network_info_ipam.get("IPv4Address"), + ipv6_address=network_info_ipam.get("IPv6Address"), + aliases=network_info.get("Aliases"), + links=network_info.get("Links"), + mac_address=network_info.get("MacAddress"), + ), ) - )) + ) return different, differences def has_extra_networks(self, container): - ''' + """ Check if the container is connected to non-requested networks - ''' + """ extra_networks = [] extra = False - if not container.container.get('NetworkSettings'): - self.fail("has_extra_networks: Error parsing container properties. NetworkSettings missing.") + if not container.container.get("NetworkSettings"): + self.fail( + "has_extra_networks: Error parsing container properties. NetworkSettings missing." + ) - connected_networks = container.container['NetworkSettings'].get('Networks') + connected_networks = container.container["NetworkSettings"].get("Networks") if connected_networks: for network, network_config in connected_networks.items(): keep = False - if self.module.params['networks']: - for expected_network in self.module.params['networks']: - if expected_network['name'] == network: + if self.module.params["networks"]: + for expected_network in self.module.params["networks"]: + if expected_network["name"] == network: keep = True if not keep: extra = True - extra_networks.append(dict(name=network, id=network_config['NetworkID'])) + extra_networks.append( + dict(name=network, id=network_config["NetworkID"]) + ) return extra, extra_networks def update_networks(self, container, container_created): updated_container = container - if self.all_options['networks'].comparison != 'ignore' or container_created: - has_network_differences, network_differences = self.has_network_differences(container) + if self.all_options["networks"].comparison != "ignore" or container_created: + has_network_differences, network_differences = self.has_network_differences( + container + ) if has_network_differences: - if self.diff.get('differences'): - self.diff['differences'].append(dict(network_differences=network_differences)) + if self.diff.get("differences"): + self.diff["differences"].append( + dict(network_differences=network_differences) + ) else: - self.diff['differences'] = [dict(network_differences=network_differences)] + self.diff["differences"] = [ + dict(network_differences=network_differences) + ] for netdiff in network_differences: self.diff_tracker.add( f"network.{netdiff['parameter']['name']}", - parameter=netdiff['parameter'], - active=netdiff['container'] + parameter=netdiff["parameter"], + active=netdiff["container"], ) - self.results['changed'] = True + self.results["changed"] = True updated_container = self._add_networks(container, network_differences) - purge_networks = self.all_options['networks'].comparison == 'strict' and self.module.params['networks'] is not None + purge_networks = ( + self.all_options["networks"].comparison == "strict" + and self.module.params["networks"] is not None + ) if purge_networks: has_extra_networks, extra_networks = self.has_extra_networks(container) if has_extra_networks: - if self.diff.get('differences'): - self.diff['differences'].append(dict(purge_networks=extra_networks)) + if self.diff.get("differences"): + self.diff["differences"].append(dict(purge_networks=extra_networks)) else: - self.diff['differences'] = [dict(purge_networks=extra_networks)] + self.diff["differences"] = [dict(purge_networks=extra_networks)] for extra_network in extra_networks: self.diff_tracker.add( - f"network.{extra_network['name']}", - active=extra_network + f"network.{extra_network['name']}", active=extra_network ) - self.results['changed'] = True + self.results["changed"] = True updated_container = self._purge_networks(container, extra_networks) return updated_container def _add_networks(self, container, differences): for diff in differences: # remove the container from the network, if connected - if diff.get('container'): - self.results['actions'].append(dict(removed_from_network=diff['parameter']['name'])) + if diff.get("container"): + self.results["actions"].append( + dict(removed_from_network=diff["parameter"]["name"]) + ) if not self.check_mode: try: - self.engine_driver.disconnect_container_from_network(self.client, container.id, diff['parameter']['id']) + self.engine_driver.disconnect_container_from_network( + self.client, container.id, diff["parameter"]["id"] + ) except Exception as exc: - self.fail(f"Error disconnecting container from network {diff['parameter']['name']} - {exc}") + self.fail( + f"Error disconnecting container from network {diff['parameter']['name']} - {exc}" + ) # connect to the network - self.results['actions'].append(dict(added_to_network=diff['parameter']['name'], network_parameters=diff['parameter'])) + self.results["actions"].append( + dict( + added_to_network=diff["parameter"]["name"], + network_parameters=diff["parameter"], + ) + ) if not self.check_mode: - params = {key: value for key, value in diff['parameter'].items() if key not in ('id', 'name')} + params = { + key: value + for key, value in diff["parameter"].items() + if key not in ("id", "name") + } try: - self.log(f"Connecting container to network {diff['parameter']['id']}") + self.log( + f"Connecting container to network {diff['parameter']['id']}" + ) self.log(params, pretty_print=True) - self.engine_driver.connect_container_to_network(self.client, container.id, diff['parameter']['id'], params) + self.engine_driver.connect_container_to_network( + self.client, container.id, diff["parameter"]["id"], params + ) except Exception as exc: - self.fail(f"Error connecting container to network {diff['parameter']['name']} - {exc}") + self.fail( + f"Error connecting container to network {diff['parameter']['name']} - {exc}" + ) return self._get_container(container.id) def _purge_networks(self, container, networks): for network in networks: - self.results['actions'].append(dict(removed_from_network=network['name'])) + self.results["actions"].append(dict(removed_from_network=network["name"])) if not self.check_mode: try: - self.engine_driver.disconnect_container_from_network(self.client, container.id, network['name']) + self.engine_driver.disconnect_container_from_network( + self.client, container.id, network["name"] + ) except Exception as exc: - self.fail(f"Error disconnecting container from network {network['name']} - {exc}") + self.fail( + f"Error disconnecting container from network {network['name']} - {exc}" + ) return self._get_container(container.id) def container_create(self, image): @@ -734,21 +996,32 @@ class ContainerManager(DockerBaseClass): self.log(f"image: {image} parameters:") self.log(create_parameters, pretty_print=True) networks = {} - if self.param_networks_cli_compatible and self.module.params['networks']: - network_list = self.module.params['networks'] - if not self.engine_driver.create_container_supports_more_than_one_network(self.client): + if self.param_networks_cli_compatible and self.module.params["networks"]: + network_list = self.module.params["networks"] + if not self.engine_driver.create_container_supports_more_than_one_network( + self.client + ): network_list = network_list[:1] for network in network_list: - networks[network['name']] = { - key: value for key, value in network.items() - if key not in ('name', 'id') + networks[network["name"]] = { + key: value + for key, value in network.items() + if key not in ("name", "id") } - self.results['actions'].append(dict(created="Created container", create_parameters=create_parameters, networks=networks)) - self.results['changed'] = True + self.results["actions"].append( + dict( + created="Created container", + create_parameters=create_parameters, + networks=networks, + ) + ) + self.results["changed"] = True new_container = None if not self.check_mode: try: - container_id = self.engine_driver.create_container(self.client, self.param_name, create_parameters, networks=networks) + container_id = self.engine_driver.create_container( + self.client, self.param_name, create_parameters, networks=networks + ) except Exception as exc: self.fail(f"Error creating container: {exc}") return self._get_container(container_id) @@ -756,25 +1029,31 @@ class ContainerManager(DockerBaseClass): def container_start(self, container_id): self.log(f"start container {container_id}") - self.results['actions'].append(dict(started=container_id)) - self.results['changed'] = True + self.results["actions"].append(dict(started=container_id)) + self.results["changed"] = True if not self.check_mode: try: self.engine_driver.start_container(self.client, container_id) except Exception as exc: self.fail(f"Error starting container {container_id}: {exc}") - if self.module.params['detach'] is False: - status = self.engine_driver.wait_for_container(self.client, container_id) - self.client.fail_results['status'] = status - self.results['status'] = status + if self.module.params["detach"] is False: + status = self.engine_driver.wait_for_container( + self.client, container_id + ) + self.client.fail_results["status"] = status + self.results["status"] = status - if self.module.params['auto_remove']: + if self.module.params["auto_remove"]: output = "Cannot retrieve result as auto_remove is enabled" if self.param_output_logs: - self.module.warn('Cannot output_logs if auto_remove is enabled!') + self.module.warn( + "Cannot output_logs if auto_remove is enabled!" + ) else: - output, real_output = self.engine_driver.get_container_output(self.client, container_id) + output, real_output = self.engine_driver.get_container_output( + self.client, container_id + ) if real_output and self.param_output_logs: self._output_logs(msg=output) @@ -782,24 +1061,36 @@ class ContainerManager(DockerBaseClass): self.container_remove(container_id, force=True) insp = self._get_container(container_id) if insp.raw: - insp.raw['Output'] = output + insp.raw["Output"] = output else: insp.raw = dict(Output=output) if status != 0: # Set `failed` to True and return output as msg - self.results['failed'] = True - self.results['msg'] = output + self.results["failed"] = True + self.results["msg"] = output return insp return self._get_container(container_id) def container_remove(self, container_id, link=False, force=False): - volume_state = (not self.param_keep_volumes) - self.log(f"remove container container:{container_id} v:{volume_state} link:{link} force{force}") - self.results['actions'].append(dict(removed=container_id, volume_state=volume_state, link=link, force=force)) - self.results['changed'] = True + volume_state = not self.param_keep_volumes + self.log( + f"remove container container:{container_id} v:{volume_state} link:{link} force{force}" + ) + self.results["actions"].append( + dict( + removed=container_id, volume_state=volume_state, link=link, force=force + ) + ) + self.results["changed"] = True if not self.check_mode: try: - self.engine_driver.remove_container(self.client, container_id, remove_volumes=volume_state, link=link, force=force) + self.engine_driver.remove_container( + self.client, + container_id, + remove_volumes=volume_state, + link=link, + force=force, + ) except Exception as exc: self.client.fail(f"Error removing container {container_id}: {exc}") @@ -807,30 +1098,42 @@ class ContainerManager(DockerBaseClass): if update_parameters: self.log(f"update container {container_id}") self.log(update_parameters, pretty_print=True) - self.results['actions'].append(dict(updated=container_id, update_parameters=update_parameters)) - self.results['changed'] = True + self.results["actions"].append( + dict(updated=container_id, update_parameters=update_parameters) + ) + self.results["changed"] = True if not self.check_mode: try: - self.engine_driver.update_container(self.client, container_id, update_parameters) + self.engine_driver.update_container( + self.client, container_id, update_parameters + ) except Exception as exc: self.fail(f"Error updating container {container_id}: {exc}") return self._get_container(container_id) def container_kill(self, container_id): - self.results['actions'].append(dict(killed=container_id, signal=self.param_kill_signal)) - self.results['changed'] = True + self.results["actions"].append( + dict(killed=container_id, signal=self.param_kill_signal) + ) + self.results["changed"] = True if not self.check_mode: try: - self.engine_driver.kill_container(self.client, container_id, kill_signal=self.param_kill_signal) + self.engine_driver.kill_container( + self.client, container_id, kill_signal=self.param_kill_signal + ) except Exception as exc: self.fail(f"Error killing container {container_id}: {exc}") def container_restart(self, container_id): - self.results['actions'].append(dict(restarted=container_id, timeout=self.module.params['stop_timeout'])) - self.results['changed'] = True + self.results["actions"].append( + dict(restarted=container_id, timeout=self.module.params["stop_timeout"]) + ) + self.results["changed"] = True if not self.check_mode: try: - self.engine_driver.restart_container(self.client, container_id, self.module.params['stop_timeout'] or 10) + self.engine_driver.restart_container( + self.client, container_id, self.module.params["stop_timeout"] or 10 + ) except Exception as exc: self.fail(f"Error restarting container {container_id}: {exc}") return self._get_container(container_id) @@ -839,11 +1142,15 @@ class ContainerManager(DockerBaseClass): if self.param_force_kill: self.container_kill(container_id) return - self.results['actions'].append(dict(stopped=container_id, timeout=self.module.params['stop_timeout'])) - self.results['changed'] = True + self.results["actions"].append( + dict(stopped=container_id, timeout=self.module.params["stop_timeout"]) + ) + self.results["changed"] = True if not self.check_mode: try: - self.engine_driver.stop_container(self.client, container_id, self.module.params['stop_timeout']) + self.engine_driver.stop_container( + self.client, container_id, self.module.params["stop_timeout"] + ) except Exception as exc: self.fail(f"Error stopping container {container_id}: {exc}") @@ -851,32 +1158,58 @@ class ContainerManager(DockerBaseClass): def run_module(engine_driver): module, active_options, client = engine_driver.setup( argument_spec=dict( - cleanup=dict(type='bool', default=False), - comparisons=dict(type='dict'), - container_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']), - command_handling=dict(type='str', choices=['compatibility', 'correct'], default='correct'), - default_host_ip=dict(type='str'), - force_kill=dict(type='bool', default=False, aliases=['forcekill']), - image=dict(type='str'), - image_comparison=dict(type='str', choices=['desired-image', 'current-image'], default='desired-image'), - image_label_mismatch=dict(type='str', choices=['ignore', 'fail'], default='ignore'), - image_name_mismatch=dict(type='str', choices=['ignore', 'recreate'], default='recreate'), - keep_volumes=dict(type='bool', default=True), - kill_signal=dict(type='str'), - name=dict(type='str', required=True), - networks_cli_compatible=dict(type='bool', default=True), - output_logs=dict(type='bool', default=False), - paused=dict(type='bool'), - pull=dict(type='raw', choices=['never', 'missing', 'always', True, False], default='missing'), - pull_check_mode_behavior=dict(type='str', choices=['image_not_present', 'always'], default='image_not_present'), - recreate=dict(type='bool', default=False), - removal_wait_timeout=dict(type='float'), - restart=dict(type='bool', default=False), - state=dict(type='str', default='started', choices=['absent', 'present', 'healthy', 'started', 'stopped']), - healthy_wait_timeout=dict(type='float', default=300), + cleanup=dict(type="bool", default=False), + comparisons=dict(type="dict"), + container_default_behavior=dict( + type="str", + default="no_defaults", + choices=["compatibility", "no_defaults"], + ), + command_handling=dict( + type="str", choices=["compatibility", "correct"], default="correct" + ), + default_host_ip=dict(type="str"), + force_kill=dict(type="bool", default=False, aliases=["forcekill"]), + image=dict(type="str"), + image_comparison=dict( + type="str", + choices=["desired-image", "current-image"], + default="desired-image", + ), + image_label_mismatch=dict( + type="str", choices=["ignore", "fail"], default="ignore" + ), + image_name_mismatch=dict( + type="str", choices=["ignore", "recreate"], default="recreate" + ), + keep_volumes=dict(type="bool", default=True), + kill_signal=dict(type="str"), + name=dict(type="str", required=True), + networks_cli_compatible=dict(type="bool", default=True), + output_logs=dict(type="bool", default=False), + paused=dict(type="bool"), + pull=dict( + type="raw", + choices=["never", "missing", "always", True, False], + default="missing", + ), + pull_check_mode_behavior=dict( + type="str", + choices=["image_not_present", "always"], + default="image_not_present", + ), + recreate=dict(type="bool", default=False), + removal_wait_timeout=dict(type="float"), + restart=dict(type="bool", default=False), + state=dict( + type="str", + default="started", + choices=["absent", "present", "healthy", "started", "stopped"], + ), + healthy_wait_timeout=dict(type="float", default=300), ), required_if=[ - ('state', 'present', ['image']), + ("state", "present", ["image"]), ], ) diff --git a/plugins/module_utils/selectors.py b/plugins/module_utils/selectors.py index fcb48e63..d4954f75 100644 --- a/plugins/module_utils/selectors.py +++ b/plugins/module_utils/selectors.py @@ -8,5 +8,4 @@ from __future__ import annotations - import selectors # noqa: F401, pylint: disable=unused-import diff --git a/plugins/module_utils/socket_handler.py b/plugins/module_utils/socket_handler.py index 67ed518a..46fd3e96 100644 --- a/plugins/module_utils/socket_handler.py +++ b/plugins/module_utils/socket_handler.py @@ -4,14 +4,14 @@ from __future__ import annotations - import os import os.path import socket as pysocket import struct -from ansible_collections.community.docker.plugins.module_utils._api.utils import socket as docker_socket - +from ansible_collections.community.docker.plugins.module_utils._api.utils import ( + socket as docker_socket, +) from ansible_collections.community.docker.plugins.module_utils.socket_helper import ( make_unblocking, shutdown_writing, @@ -31,19 +31,21 @@ class DockerSocketHandlerBase(object): self._log = log else: self._log = lambda msg: True - self._paramiko_read_workaround = hasattr(sock, 'send_ready') and 'paramiko' in str(type(sock)) + self._paramiko_read_workaround = hasattr( + sock, "send_ready" + ) and "paramiko" in str(type(sock)) self._sock = sock self._block_done_callback = None self._block_buffer = [] self._eof = False - self._read_buffer = b'' - self._write_buffer = b'' + self._read_buffer = b"" + self._write_buffer = b"" self._end_of_writing = False self._current_stream = None self._current_missing = 0 - self._current_buffer = b'' + self._current_buffer = b"" self._selector = self._selectors.DefaultSelector() self._selector.register(self._sock, self._selectors.EVENT_READ) @@ -70,26 +72,26 @@ class DockerSocketHandlerBase(object): def _read(self): if self._eof: return - if hasattr(self._sock, 'recv'): + if hasattr(self._sock, "recv"): try: data = self._sock.recv(262144) except Exception as e: # After calling self._sock.shutdown(), OpenSSL's/urllib3's # WrappedSocket seems to eventually raise ZeroReturnError in # case of EOF - if 'OpenSSL.SSL.ZeroReturnError' in str(type(e)): + if "OpenSSL.SSL.ZeroReturnError" in str(type(e)): self._eof = True return else: raise - elif isinstance(self._sock, getattr(pysocket, 'SocketIO')): + elif isinstance(self._sock, getattr(pysocket, "SocketIO")): data = self._sock.read() else: data = os.read(self._sock.fileno()) if data is None: # no data available return - self._log(f'read {len(data)} bytes') + self._log(f"read {len(data)} bytes") if len(data) == 0: # Stream EOF self._eof = True @@ -103,10 +105,12 @@ class DockerSocketHandlerBase(object): self._current_missing -= n if self._current_missing == 0: self._add_block(self._current_stream, self._current_buffer) - self._current_buffer = b'' + self._current_buffer = b"" if len(self._read_buffer) < 8: break - self._current_stream, self._current_missing = struct.unpack('>BxxxL', self._read_buffer[:8]) + self._current_stream, self._current_missing = struct.unpack( + ">BxxxL", self._read_buffer[:8] + ) self._read_buffer = self._read_buffer[8:] if self._current_missing < 0: # Stream EOF (as reported by docker daemon) @@ -116,22 +120,28 @@ class DockerSocketHandlerBase(object): def _handle_end_of_writing(self): if self._end_of_writing and len(self._write_buffer) == 0: self._end_of_writing = False - self._log('Shutting socket down for writing') + self._log("Shutting socket down for writing") shutdown_writing(self._sock, self._log) def _write(self): if len(self._write_buffer) > 0: written = write_to_socket(self._sock, self._write_buffer) self._write_buffer = self._write_buffer[written:] - self._log(f'wrote {written} bytes, {len(self._write_buffer)} are left') + self._log(f"wrote {written} bytes, {len(self._write_buffer)} are left") if len(self._write_buffer) > 0: - self._selector.modify(self._sock, self._selectors.EVENT_READ | self._selectors.EVENT_WRITE) + self._selector.modify( + self._sock, self._selectors.EVENT_READ | self._selectors.EVENT_WRITE + ) else: self._selector.modify(self._sock, self._selectors.EVENT_READ) self._handle_end_of_writing() def select(self, timeout=None, _internal_recursion=False): - if not _internal_recursion and self._paramiko_read_workaround and len(self._write_buffer) > 0: + if ( + not _internal_recursion + and self._paramiko_read_workaround + and len(self._write_buffer) > 0 + ): # When the SSH transport is used, Docker SDK for Python internally uses Paramiko, whose # Channel object supports select(), but only for reading # (https://github.com/paramiko/paramiko/issues/695). @@ -147,13 +157,13 @@ class DockerSocketHandlerBase(object): return True if timeout is not None: timeout -= PARAMIKO_POLL_TIMEOUT - self._log(f'select... ({timeout})') + self._log(f"select... ({timeout})") events = self._selector.select(timeout) for key, event in events: if key.fileobj == self._sock: ev_read = event & self._selectors.EVENT_READ != 0 ev_write = event & self._selectors.EVENT_WRITE != 0 - self._log(f'select event read:{ev_read} write:{ev_write}') + self._log(f"select event read:{ev_read} write:{ev_write}") if event & self._selectors.EVENT_READ != 0: self._read() if event & self._selectors.EVENT_WRITE != 0: @@ -182,14 +192,14 @@ class DockerSocketHandlerBase(object): elif stream_id == docker_socket.STDERR: stderr.append(data) else: - raise ValueError(f'{stream_id} is not a valid stream ID') + raise ValueError(f"{stream_id} is not a valid stream ID") self.end_of_writing() self.set_block_done_callback(append_block) while not self._eof: self.select() - return b''.join(stdout), b''.join(stderr) + return b"".join(stdout), b"".join(stderr) def write(self, str): self._write_buffer += str diff --git a/plugins/module_utils/socket_helper.py b/plugins/module_utils/socket_helper.py index 5300404e..2c5e06db 100644 --- a/plugins/module_utils/socket_helper.py +++ b/plugins/module_utils/socket_helper.py @@ -4,7 +4,6 @@ from __future__ import annotations - import fcntl import os import os.path @@ -12,17 +11,25 @@ import socket as pysocket def make_file_unblocking(file): - fcntl.fcntl(file.fileno(), fcntl.F_SETFL, fcntl.fcntl(file.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK) + fcntl.fcntl( + file.fileno(), + fcntl.F_SETFL, + fcntl.fcntl(file.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK, + ) def make_file_blocking(file): - fcntl.fcntl(file.fileno(), fcntl.F_SETFL, fcntl.fcntl(file.fileno(), fcntl.F_GETFL) & ~os.O_NONBLOCK) + fcntl.fcntl( + file.fileno(), + fcntl.F_SETFL, + fcntl.fcntl(file.fileno(), fcntl.F_GETFL) & ~os.O_NONBLOCK, + ) def make_unblocking(sock): - if hasattr(sock, '_sock'): + if hasattr(sock, "_sock"): sock._sock.setblocking(0) - elif hasattr(sock, 'setblocking'): + elif hasattr(sock, "setblocking"): sock.setblocking(0) else: make_file_unblocking(sock) @@ -37,27 +44,27 @@ def shutdown_writing(sock, log=_empty_writer): # a close_notify TLS alert without completely shutting down the connection. # Calling sock.shutdown(pysocket.SHUT_WR) simply turns of TLS encryption and from that # point on the raw encrypted data is returned when sock.recv() is called. :-( - if hasattr(sock, 'shutdown_write'): + if hasattr(sock, "shutdown_write"): sock.shutdown_write() - elif hasattr(sock, 'shutdown'): + elif hasattr(sock, "shutdown"): try: sock.shutdown(pysocket.SHUT_WR) except TypeError as e: # probably: "TypeError: shutdown() takes 1 positional argument but 2 were given" - log(f'Shutting down for writing not possible; trying shutdown instead: {e}') + log(f"Shutting down for writing not possible; trying shutdown instead: {e}") sock.shutdown() - elif isinstance(sock, getattr(pysocket, 'SocketIO')): + elif isinstance(sock, getattr(pysocket, "SocketIO")): sock._sock.shutdown(pysocket.SHUT_WR) else: - log('No idea how to signal end of writing') + log("No idea how to signal end of writing") def write_to_socket(sock, data): - if hasattr(sock, '_send_until_done'): + if hasattr(sock, "_send_until_done"): # WrappedSocket (urllib3/contrib/pyopenssl) does not have `send`, but # only `sendall`, which uses `_send_until_done` under the hood. return sock._send_until_done(data) - elif hasattr(sock, 'send'): + elif hasattr(sock, "send"): return sock.send(data) else: return os.write(sock.fileno(), data) diff --git a/plugins/module_utils/swarm.py b/plugins/module_utils/swarm.py index b995adc7..301e34c3 100644 --- a/plugins/module_utils/swarm.py +++ b/plugins/module_utils/swarm.py @@ -5,19 +5,22 @@ from __future__ import annotations - import json from time import sleep + try: from docker.errors import APIError, NotFound except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - -from ansible_collections.community.docker.plugins.module_utils.common import AnsibleDockerClient +from ansible_collections.community.docker.plugins.module_utils.common import ( + AnsibleDockerClient, +) +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) class AnsibleDockerSwarmClient(AnsibleDockerClient): @@ -41,8 +44,8 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): if info: json_str = json.dumps(info, ensure_ascii=False) swarm_info = json.loads(json_str) - if swarm_info['Swarm']['NodeID']: - return swarm_info['Swarm']['NodeID'] + if swarm_info["Swarm"]["NodeID"]: + return swarm_info["Swarm"]["NodeID"] return None def check_if_swarm_node(self, node_id=None): @@ -66,9 +69,13 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): if info: json_str = json.dumps(info, ensure_ascii=False) swarm_info = json.loads(json_str) - if swarm_info['Swarm']['NodeID']: + if swarm_info["Swarm"]["NodeID"]: return True - if swarm_info['Swarm']['LocalNodeState'] in ('active', 'pending', 'locked'): + if swarm_info["Swarm"]["LocalNodeState"] in ( + "active", + "pending", + "locked", + ): return True return False else: @@ -77,7 +84,7 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): except APIError: return - if node_info['ID'] is not None: + if node_info["ID"] is not None: return True return False @@ -100,7 +107,9 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): If host is not a swarm manager then Ansible task on this host should end with 'failed' state """ if not self.check_if_swarm_manager(): - self.fail("Error running docker swarm module: must run on swarm manager node") + self.fail( + "Error running docker swarm module: must run on swarm manager node" + ) def check_if_swarm_worker(self): """ @@ -136,7 +145,7 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): if retry > 0: sleep(5) node_info = self.get_node_inspect(node_id=node_id) - if node_info['Status']['State'] == 'down': + if node_info["Status"]["State"] == "down": return True return False @@ -160,7 +169,9 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): node_info = self.inspect_node(node_id=node_id) except APIError as exc: if exc.status_code == 503: - self.fail("Cannot inspect node: To inspect node execute module on Swarm Manager") + self.fail( + "Cannot inspect node: To inspect node execute module on Swarm Manager" + ) if exc.status_code == 404: if skip_missing: return None @@ -171,16 +182,19 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): json_str = json.dumps(node_info, ensure_ascii=False) node_info = json.loads(json_str) - if 'ManagerStatus' in node_info: - if node_info['ManagerStatus'].get('Leader'): + if "ManagerStatus" in node_info: + if node_info["ManagerStatus"].get("Leader"): # This is workaround of bug in Docker when in some cases the Leader IP is 0.0.0.0 # Check moby/moby#35437 for details - count_colons = node_info['ManagerStatus']['Addr'].count(":") + count_colons = node_info["ManagerStatus"]["Addr"].count(":") if count_colons == 1: - swarm_leader_ip = node_info['ManagerStatus']['Addr'].split(":", 1)[0] or node_info['Status']['Addr'] + swarm_leader_ip = ( + node_info["ManagerStatus"]["Addr"].split(":", 1)[0] + or node_info["Status"]["Addr"] + ) else: - swarm_leader_ip = node_info['Status']['Addr'] - node_info['Status']['Addr'] = swarm_leader_ip + swarm_leader_ip = node_info["Status"]["Addr"] + node_info["Status"]["Addr"] = swarm_leader_ip return node_info def get_all_nodes_inspect(self): @@ -194,7 +208,9 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): node_info = self.nodes() except APIError as exc: if exc.status_code == 503: - self.fail("Cannot inspect node: To inspect node execute module on Swarm Manager") + self.fail( + "Cannot inspect node: To inspect node execute module on Swarm Manager" + ) self.fail(f"Error while reading from Swarm manager: {exc}") except Exception as exc: self.fail(f"Error inspecting swarm node: {exc}") @@ -203,7 +219,7 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): node_info = json.loads(json_str) return node_info - def get_all_nodes_list(self, output='short'): + def get_all_nodes_list(self, output="short"): """ Returns list of nodes registered in Swarm @@ -219,22 +235,26 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): if nodes_inspect is None: return None - if output == 'short': + if output == "short": for node in nodes_inspect: - nodes_list.append(node['Description']['Hostname']) - elif output == 'long': + nodes_list.append(node["Description"]["Hostname"]) + elif output == "long": for node in nodes_inspect: node_property = {} - node_property.update({'ID': node['ID']}) - node_property.update({'Hostname': node['Description']['Hostname']}) - node_property.update({'Status': node['Status']['State']}) - node_property.update({'Availability': node['Spec']['Availability']}) - if 'ManagerStatus' in node: - if node['ManagerStatus']['Leader'] is True: - node_property.update({'Leader': True}) - node_property.update({'ManagerStatus': node['ManagerStatus']['Reachability']}) - node_property.update({'EngineVersion': node['Description']['Engine']['EngineVersion']}) + node_property.update({"ID": node["ID"]}) + node_property.update({"Hostname": node["Description"]["Hostname"]}) + node_property.update({"Status": node["Status"]["State"]}) + node_property.update({"Availability": node["Spec"]["Availability"]}) + if "ManagerStatus" in node: + if node["ManagerStatus"]["Leader"] is True: + node_property.update({"Leader": True}) + node_property.update( + {"ManagerStatus": node["ManagerStatus"]["Reachability"]} + ) + node_property.update( + {"EngineVersion": node["Description"]["Engine"]["EngineVersion"]} + ) nodes_list.append(node_property) else: @@ -243,10 +263,10 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): return nodes_list def get_node_name_by_id(self, nodeid): - return self.get_node_inspect(nodeid)['Description']['Hostname'] + return self.get_node_inspect(nodeid)["Description"]["Hostname"] def get_unlock_key(self): - if self.docker_py_version < LooseVersion('2.7.0'): + if self.docker_py_version < LooseVersion("2.7.0"): return None return super(AnsibleDockerSwarmClient, self).get_unlock_key() @@ -268,7 +288,9 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): return None except APIError as exc: if exc.status_code == 503: - self.fail("Cannot inspect service: To inspect service execute module on Swarm Manager") + self.fail( + "Cannot inspect service: To inspect service execute module on Swarm Manager" + ) self.fail(f"Error inspecting swarm service: {exc}") except Exception as exc: self.fail(f"Error inspecting swarm service: {exc}") diff --git a/plugins/module_utils/util.py b/plugins/module_utils/util.py index 7952e84c..2cee3160 100644 --- a/plugins/module_utils/util.py +++ b/plugins/module_utils/util.py @@ -4,7 +4,6 @@ from __future__ import annotations - import json import re from datetime import timedelta @@ -15,45 +14,64 @@ from ansible.module_utils.common.collections import is_sequence from ansible.module_utils.common.text.converters import to_text -DEFAULT_DOCKER_HOST = 'unix:///var/run/docker.sock' +DEFAULT_DOCKER_HOST = "unix:///var/run/docker.sock" DEFAULT_TLS = False DEFAULT_TLS_VERIFY = False -DEFAULT_TLS_HOSTNAME = 'localhost' # deprecated +DEFAULT_TLS_HOSTNAME = "localhost" # deprecated DEFAULT_TIMEOUT_SECONDS = 60 DOCKER_COMMON_ARGS = dict( - docker_host=dict(type='str', default=DEFAULT_DOCKER_HOST, fallback=(env_fallback, ['DOCKER_HOST']), aliases=['docker_url']), - tls_hostname=dict(type='str', fallback=(env_fallback, ['DOCKER_TLS_HOSTNAME'])), - api_version=dict(type='str', default='auto', fallback=(env_fallback, ['DOCKER_API_VERSION']), aliases=['docker_api_version']), - timeout=dict(type='int', default=DEFAULT_TIMEOUT_SECONDS, fallback=(env_fallback, ['DOCKER_TIMEOUT'])), - ca_path=dict(type='path', aliases=['ca_cert', 'tls_ca_cert', 'cacert_path']), - client_cert=dict(type='path', aliases=['tls_client_cert', 'cert_path']), - client_key=dict(type='path', aliases=['tls_client_key', 'key_path']), - tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])), - use_ssh_client=dict(type='bool', default=False), - validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']), - debug=dict(type='bool', default=False) + docker_host=dict( + type="str", + default=DEFAULT_DOCKER_HOST, + fallback=(env_fallback, ["DOCKER_HOST"]), + aliases=["docker_url"], + ), + tls_hostname=dict(type="str", fallback=(env_fallback, ["DOCKER_TLS_HOSTNAME"])), + api_version=dict( + type="str", + default="auto", + fallback=(env_fallback, ["DOCKER_API_VERSION"]), + aliases=["docker_api_version"], + ), + timeout=dict( + type="int", + default=DEFAULT_TIMEOUT_SECONDS, + fallback=(env_fallback, ["DOCKER_TIMEOUT"]), + ), + ca_path=dict(type="path", aliases=["ca_cert", "tls_ca_cert", "cacert_path"]), + client_cert=dict(type="path", aliases=["tls_client_cert", "cert_path"]), + client_key=dict(type="path", aliases=["tls_client_key", "key_path"]), + tls=dict(type="bool", default=DEFAULT_TLS, fallback=(env_fallback, ["DOCKER_TLS"])), + use_ssh_client=dict(type="bool", default=False), + validate_certs=dict( + type="bool", + default=DEFAULT_TLS_VERIFY, + fallback=(env_fallback, ["DOCKER_TLS_VERIFY"]), + aliases=["tls_verify"], + ), + debug=dict(type="bool", default=False), ) -DOCKER_COMMON_ARGS_VARS = dict([ - [option_name, f'ansible_docker_{option_name}'] - for option_name in DOCKER_COMMON_ARGS - if option_name != 'debug' -]) +DOCKER_COMMON_ARGS_VARS = dict( + [ + [option_name, f"ansible_docker_{option_name}"] + for option_name in DOCKER_COMMON_ARGS + if option_name != "debug" + ] +) DOCKER_MUTUALLY_EXCLUSIVE = [] -DOCKER_REQUIRED_TOGETHER = [ - ['client_cert', 'client_key'] -] +DOCKER_REQUIRED_TOGETHER = [["client_cert", "client_key"]] -DEFAULT_DOCKER_REGISTRY = 'https://index.docker.io/v1/' -BYTE_SUFFIXES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] +DEFAULT_DOCKER_REGISTRY = "https://index.docker.io/v1/" +BYTE_SUFFIXES = ["B", "KB", "MB", "GB", "TB", "PB"] def is_image_name_id(name): """Check whether the given image name is in fact an image ID (hash).""" - if re.match('^sha256:[0-9a-fA-F]{64}$', name): + if re.match("^sha256:[0-9a-fA-F]{64}$", name): return True return False @@ -64,7 +82,7 @@ def is_valid_tag(tag, allow_empty=False): return allow_empty # See here ("Extended description") for a definition what tags can be: # https://docs.docker.com/engine/reference/commandline/tag/ - return bool(re.match('^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$', tag)) + return bool(re.match("^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$", tag)) def sanitize_result(data): @@ -90,10 +108,12 @@ def log_debug(msg, pretty_print=False): If ``pretty_print=True``, the message will be pretty-printed as JSON. """ - with open('docker.log', 'a') as log_file: + with open("docker.log", "a") as log_file: if pretty_print: - log_file.write(json.dumps(msg, sort_keys=True, indent=4, separators=(',', ': '))) - log_file.write('\n') + log_file.write( + json.dumps(msg, sort_keys=True, indent=4, separators=(",", ": ")) + ) + log_file.write("\n") else: log_file.write(f"{msg}\n") @@ -108,17 +128,19 @@ class DockerBaseClass(object): # log_debug(msg, pretty_print=pretty_print) -def update_tls_hostname(result, old_behavior=False, deprecate_function=None, uses_tls=True): - if result['tls_hostname'] is None: +def update_tls_hostname( + result, old_behavior=False, deprecate_function=None, uses_tls=True +): + if result["tls_hostname"] is None: # get default machine name from the url - parsed_url = urlparse(result['docker_host']) - result['tls_hostname'] = parsed_url.netloc.rsplit(':', 1)[0] + parsed_url = urlparse(result["docker_host"]) + result["tls_hostname"] = parsed_url.netloc.rsplit(":", 1)[0] def compare_dict_allow_more_present(av, bv): - ''' + """ Compare two dictionaries for whether every entry of the first is in the second. - ''' + """ for key, value in av.items(): if key not in bv: return False @@ -128,7 +150,7 @@ def compare_dict_allow_more_present(av, bv): def compare_generic(a, b, method, datatype): - ''' + """ Compare values a and b as described by method and datatype. Returns ``True`` if the values compare equal, and ``False`` if not. @@ -151,8 +173,8 @@ def compare_generic(a, b, method, datatype): not matter and which contain ``dict``s; ``allow_more_present`` is used for the ``dict``s, and these are assumed to be dictionaries of values; - ``dict``: for dictionaries of values. - ''' - if method == 'ignore': + """ + if method == "ignore": return True # If a or b is None: if a is None or b is None: @@ -161,18 +183,18 @@ def compare_generic(a, b, method, datatype): return True # Otherwise, not equal for values, and equal # if the other is empty for set/list/dict - if datatype == 'value': + if datatype == "value": return False # For allow_more_present, allow a to be None - if method == 'allow_more_present' and a is None: + if method == "allow_more_present" and a is None: return True # Otherwise, the iterable object which is not None must have length 0 return len(b if a is None else a) == 0 # Do proper comparison (both objects not None) - if datatype == 'value': + if datatype == "value": return a == b - elif datatype == 'list': - if method == 'strict': + elif datatype == "list": + if method == "strict": return a == b else: i = 0 @@ -183,19 +205,19 @@ def compare_generic(a, b, method, datatype): return False i += 1 return True - elif datatype == 'dict': - if method == 'strict': + elif datatype == "dict": + if method == "strict": return a == b else: return compare_dict_allow_more_present(a, b) - elif datatype == 'set': + elif datatype == "set": set_a = set(a) set_b = set(b) - if method == 'strict': + if method == "strict": return set_a == set_b else: return set_b >= set_a - elif datatype == 'set(dict)': + elif datatype == "set(dict)": for av in a: found = False for bv in b: @@ -204,7 +226,7 @@ def compare_generic(a, b, method, datatype): break if not found: return False - if method == 'strict': + if method == "strict": # If we would know that both a and b do not contain duplicates, # we could simply compare len(a) to len(b) to finish this test. # We can assume that b has no duplicates (as it is returned by @@ -225,11 +247,13 @@ class DifferenceTracker(object): self._diff = [] def add(self, name, parameter=None, active=None): - self._diff.append(dict( - name=name, - parameter=parameter, - active=active, - )) + self._diff.append( + dict( + name=name, + parameter=parameter, + active=active, + ) + ) def merge(self, other_tracker): self._diff.extend(other_tracker._diff) @@ -239,41 +263,41 @@ class DifferenceTracker(object): return len(self._diff) == 0 def get_before_after(self): - ''' + """ Return texts ``before`` and ``after``. - ''' + """ before = dict() after = dict() for item in self._diff: - before[item['name']] = item['active'] - after[item['name']] = item['parameter'] + before[item["name"]] = item["active"] + after[item["name"]] = item["parameter"] return before, after def has_difference_for(self, name): - ''' + """ Returns a boolean if a difference exists for name - ''' - return any(diff for diff in self._diff if diff['name'] == name) + """ + return any(diff for diff in self._diff if diff["name"] == name) def get_legacy_docker_container_diffs(self): - ''' + """ Return differences in the docker_container legacy format. - ''' + """ result = [] for entry in self._diff: item = dict() - item[entry['name']] = dict( - parameter=entry['parameter'], - container=entry['active'], + item[entry["name"]] = dict( + parameter=entry["parameter"], + container=entry["active"], ) result.append(item) return result def get_legacy_docker_diffs(self): - ''' + """ Return differences in the docker_container legacy format. - ''' - result = [entry['name'] for entry in self._diff] + """ + result = [entry["name"] for entry in self._diff] return result @@ -291,31 +315,38 @@ def sanitize_labels(labels, labels_field, client=None, module=None): if not isinstance(k, str): fail(f"The key {k!r} of {labels_field} is not a string!") if isinstance(v, (bool, float)): - fail(f"The value {v!r} for {k!r} of {labels_field} is not a string or something than can be safely converted to a string!") + fail( + f"The value {v!r} for {k!r} of {labels_field} is not a string or something than can be safely converted to a string!" + ) labels[k] = to_text(v) def clean_dict_booleans_for_docker_api(data, allow_sequences=False): - ''' + """ Go does not like Python booleans 'True' or 'False', while Ansible is just fine with them in YAML. As such, they need to be converted in cases where we pass dictionaries to the Docker API (e.g. docker_network's driver_options and docker_prune's filters). When `allow_sequences=True` YAML sequences (lists, tuples) are converted to [str] instead of str([...]) which is the expected format of filters which accept lists such as labels. - ''' + """ + def sanitize(value): if value is True: - return 'true' + return "true" elif value is False: - return 'false' + return "false" else: return str(value) result = dict() if data is not None: for k, v in data.items(): - result[str(k)] = [sanitize(e) for e in v] if allow_sequences and is_sequence(v) else sanitize(v) + result[str(k)] = ( + [sanitize(e) for e in v] + if allow_sequences and is_sequence(v) + else sanitize(v) + ) return result @@ -324,30 +355,30 @@ def convert_duration_to_nanosecond(time_str): Return time duration in nanosecond. """ if not isinstance(time_str, str): - raise ValueError(f'Missing unit in duration - {time_str}') + raise ValueError(f"Missing unit in duration - {time_str}") regex = re.compile( - r'^(((?P\d+)h)?' - r'((?P\d+)m(?!s))?' - r'((?P\d+)s)?' - r'((?P\d+)ms)?' - r'((?P\d+)us)?)$' + r"^(((?P\d+)h)?" + r"((?P\d+)m(?!s))?" + r"((?P\d+)s)?" + r"((?P\d+)ms)?" + r"((?P\d+)us)?)$" ) parts = regex.match(time_str) if not parts: - raise ValueError(f'Invalid time duration - {time_str}') + raise ValueError(f"Invalid time duration - {time_str}") parts = parts.groupdict() time_params = {} - for (name, value) in parts.items(): + for name, value in parts.items(): if value: time_params[name] = int(value) delta = timedelta(**time_params) time_in_nanoseconds = ( - delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6 - ) * 10 ** 3 + delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6 + ) * 10**3 return time_in_nanoseconds @@ -355,7 +386,7 @@ def convert_duration_to_nanosecond(time_str): def normalize_healthcheck_test(test): if isinstance(test, (tuple, list)): return [str(e) for e in test] - return ['CMD-SHELL', str(test)] + return ["CMD-SHELL", str(test)] def normalize_healthcheck(healthcheck, normalize_test=False): @@ -365,9 +396,17 @@ def normalize_healthcheck(healthcheck, normalize_test=False): result = dict() # All supported healthcheck parameters - options = ('test', 'test_cli_compatible', 'interval', 'timeout', 'start_period', 'start_interval', 'retries') + options = ( + "test", + "test_cli_compatible", + "interval", + "timeout", + "start_period", + "start_interval", + "retries", + ) - duration_options = ('interval', 'timeout', 'start_period', 'start_interval') + duration_options = ("interval", "timeout", "start_period", "start_interval") for key in options: if key in healthcheck: @@ -378,16 +417,18 @@ def normalize_healthcheck(healthcheck, normalize_test=False): continue if key in duration_options: value = convert_duration_to_nanosecond(value) - if not value and not (healthcheck.get('test_cli_compatible') and key == 'test'): + if not value and not ( + healthcheck.get("test_cli_compatible") and key == "test" + ): continue - if key == 'retries': + if key == "retries": try: value = int(value) except ValueError: raise ValueError( f'Cannot parse number of retries for healthcheck. Expected an integer, got "{value}".' ) - if key == 'test' and value and normalize_test: + if key == "test" and value and normalize_test: value = normalize_healthcheck_test(value) result[key] = value @@ -399,12 +440,12 @@ def parse_healthcheck(healthcheck): Return dictionary of healthcheck parameters and boolean if healthcheck defined in image was requested to be disabled. """ - if (not healthcheck) or (not healthcheck.get('test')): + if (not healthcheck) or (not healthcheck.get("test")): return None, None result = normalize_healthcheck(healthcheck, normalize_test=True) - if result['test'] == ['NONE']: + if result["test"] == ["NONE"]: # If the user explicitly disables the healthcheck, return None # as the healthcheck object, and set disable_healthcheck to True return None, True diff --git a/plugins/module_utils/version.py b/plugins/module_utils/version.py index be22eeae..b17b796d 100644 --- a/plugins/module_utils/version.py +++ b/plugins/module_utils/version.py @@ -8,5 +8,7 @@ from __future__ import annotations - -from ansible.module_utils.compat.version import LooseVersion, StrictVersion # noqa: F401, pylint: disable=unused-import +from ansible.module_utils.compat.version import ( # noqa: F401, pylint: disable=unused-import + LooseVersion, + StrictVersion, +) diff --git a/plugins/modules/current_container_facts.py b/plugins/modules/current_container_facts.py index 6f502891..b52e82cd 100644 --- a/plugins/modules/current_container_facts.py +++ b/plugins/modules/current_container_facts.py @@ -82,11 +82,11 @@ from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule(dict(), supports_check_mode=True) - cpuset_path = '/proc/self/cpuset' - mountinfo_path = '/proc/self/mountinfo' + cpuset_path = "/proc/self/cpuset" + mountinfo_path = "/proc/self/mountinfo" - container_id = '' - container_type = '' + container_id = "" + container_type = "" contents = None if os.path.exists(cpuset_path): @@ -98,49 +98,51 @@ def main(): # While this was true and worked well for a long time, this seems to be no longer accurate # with newer Docker / Podman versions and/or with cgroupv2. That's why the /proc/self/mountinfo # detection further down is done when this test is inconclusive. - with open(cpuset_path, 'rb') as f: - contents = f.read().decode('utf-8') + with open(cpuset_path, "rb") as f: + contents = f.read().decode("utf-8") cgroup_path, cgroup_name = os.path.split(contents.strip()) - if cgroup_path == '/docker': + if cgroup_path == "/docker": container_id = cgroup_name - container_type = 'docker' + container_type = "docker" - if cgroup_path == '/azpl_job': + if cgroup_path == "/azpl_job": container_id = cgroup_name - container_type = 'azure_pipelines' + container_type = "azure_pipelines" - if cgroup_path == '/actions_job': + if cgroup_path == "/actions_job": container_id = cgroup_name - container_type = 'github_actions' + container_type = "github_actions" if not container_id and os.path.exists(mountinfo_path): - with open(mountinfo_path, 'rb') as f: - contents = f.read().decode('utf-8') + with open(mountinfo_path, "rb") as f: + contents = f.read().decode("utf-8") # As to why this works, see the explanations by Matt Clay in # https://github.com/ansible/ansible/blob/80d2f8da02052f64396da6b8caaf820eedbf18e2/test/lib/ansible_test/_internal/docker_util.py#L571-L610 for line in contents.splitlines(): parts = line.split() - if len(parts) >= 5 and parts[4] == '/etc/hostname': - m = re.match('.*/([a-f0-9]{64})/hostname$', parts[3]) + if len(parts) >= 5 and parts[4] == "/etc/hostname": + m = re.match(".*/([a-f0-9]{64})/hostname$", parts[3]) if m: container_id = m.group(1) - container_type = 'docker' + container_type = "docker" - m = re.match('.*/([a-f0-9]{64})/userdata/hostname$', parts[3]) + m = re.match(".*/([a-f0-9]{64})/userdata/hostname$", parts[3]) if m: container_id = m.group(1) - container_type = 'podman' + container_type = "podman" - module.exit_json(ansible_facts=dict( - ansible_module_running_in_container=container_id != '', - ansible_module_container_id=container_id, - ansible_module_container_type=container_type, - )) + module.exit_json( + ansible_facts=dict( + ansible_module_running_in_container=container_id != "", + ansible_module_container_id=container_id, + ansible_module_container_type=container_type, + ) + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_compose_v2.py b/plugins/modules/docker_compose_v2.py index 4e8d02c9..61b0fc7b 100644 --- a/plugins/modules/docker_compose_v2.py +++ b/plugins/modules/docker_compose_v2.py @@ -439,19 +439,18 @@ actions: import traceback from ansible.module_utils.common.validation import check_type_int - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, ) - from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( BaseComposeManager, common_compose_argspec_ex, is_failed, ) - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) class ServicesManager(BaseComposeManager): @@ -459,86 +458,90 @@ class ServicesManager(BaseComposeManager): super(ServicesManager, self).__init__(client) parameters = self.client.module.params - self.state = parameters['state'] - self.dependencies = parameters['dependencies'] - self.pull = parameters['pull'] - self.build = parameters['build'] - self.ignore_build_events = parameters['ignore_build_events'] - self.recreate = parameters['recreate'] - self.remove_images = parameters['remove_images'] - self.remove_volumes = parameters['remove_volumes'] - self.remove_orphans = parameters['remove_orphans'] - self.renew_anon_volumes = parameters['renew_anon_volumes'] - self.timeout = parameters['timeout'] - self.services = parameters['services'] or [] - self.scale = parameters['scale'] or {} - self.wait = parameters['wait'] - self.wait_timeout = parameters['wait_timeout'] - self.yes = parameters['assume_yes'] - if self.compose_version < LooseVersion('2.32.0') and self.yes: - self.fail(f'assume_yes=true needs Docker Compose 2.32.0 or newer, not version {self.compose_version}') + self.state = parameters["state"] + self.dependencies = parameters["dependencies"] + self.pull = parameters["pull"] + self.build = parameters["build"] + self.ignore_build_events = parameters["ignore_build_events"] + self.recreate = parameters["recreate"] + self.remove_images = parameters["remove_images"] + self.remove_volumes = parameters["remove_volumes"] + self.remove_orphans = parameters["remove_orphans"] + self.renew_anon_volumes = parameters["renew_anon_volumes"] + self.timeout = parameters["timeout"] + self.services = parameters["services"] or [] + self.scale = parameters["scale"] or {} + self.wait = parameters["wait"] + self.wait_timeout = parameters["wait_timeout"] + self.yes = parameters["assume_yes"] + if self.compose_version < LooseVersion("2.32.0") and self.yes: + self.fail( + f"assume_yes=true needs Docker Compose 2.32.0 or newer, not version {self.compose_version}" + ) for key, value in self.scale.items(): if not isinstance(key, str): - self.fail(f'The key {key!r} for `scale` is not a string') + self.fail(f"The key {key!r} for `scale` is not a string") try: value = check_type_int(value) except TypeError as exc: - self.fail(f'The value {value!r} for `scale[{key!r}]` is not an integer') + self.fail(f"The value {value!r} for `scale[{key!r}]` is not an integer") if value < 0: - self.fail(f'The value {value!r} for `scale[{key!r}]` is negative') + self.fail(f"The value {value!r} for `scale[{key!r}]` is negative") self.scale[key] = value def run(self): - if self.state == 'present': + if self.state == "present": result = self.cmd_up() - elif self.state == 'stopped': + elif self.state == "stopped": result = self.cmd_stop() - elif self.state == 'restarted': + elif self.state == "restarted": result = self.cmd_restart() - elif self.state == 'absent': + elif self.state == "absent": result = self.cmd_down() - result['containers'] = self.list_containers() - result['images'] = self.list_images() + result["containers"] = self.list_containers() + result["images"] = self.list_images() self.cleanup_result(result) return result def get_up_cmd(self, dry_run, no_start=False): - args = self.get_base_args() + ['up', '--detach', '--no-color', '--quiet-pull'] - if self.pull != 'policy': - args.extend(['--pull', self.pull]) + args = self.get_base_args() + ["up", "--detach", "--no-color", "--quiet-pull"] + if self.pull != "policy": + args.extend(["--pull", self.pull]) if self.remove_orphans: - args.append('--remove-orphans') - if self.recreate == 'always': - args.append('--force-recreate') - if self.recreate == 'never': - args.append('--no-recreate') + args.append("--remove-orphans") + if self.recreate == "always": + args.append("--force-recreate") + if self.recreate == "never": + args.append("--no-recreate") if self.renew_anon_volumes: - args.append('--renew-anon-volumes') + args.append("--renew-anon-volumes") if not self.dependencies: - args.append('--no-deps') + args.append("--no-deps") if self.timeout is not None: - args.extend(['--timeout', f'{self.timeout}']) - if self.build == 'always': - args.append('--build') - elif self.build == 'never': - args.append('--no-build') + args.extend(["--timeout", f"{self.timeout}"]) + if self.build == "always": + args.append("--build") + elif self.build == "never": + args.append("--no-build") for key, value in sorted(self.scale.items()): - args.extend(['--scale', f'{key}={value}']) + args.extend(["--scale", f"{key}={value}"]) if self.wait: - args.append('--wait') + args.append("--wait") if self.wait_timeout is not None: - args.extend(['--wait-timeout', str(self.wait_timeout)]) + args.extend(["--wait-timeout", str(self.wait_timeout)]) if no_start: - args.append('--no-start') + args.append("--no-start") if dry_run: - args.append('--dry-run') + args.append("--dry-run") if self.yes: # Note that for Docker Compose 2.32.x and 2.33.x, the long form is '--y' and not '--yes'. # This was fixed in Docker Compose 2.34.0 (https://github.com/docker/compose/releases/tag/v2.34.0). - args.append('-y' if self.compose_version < LooseVersion('2.34.0') else '--yes') - args.append('--') + args.append( + "-y" if self.compose_version < LooseVersion("2.34.0") else "--yes" + ) + args.append("--") for service in self.services: args.append(service) return args @@ -549,24 +552,31 @@ class ServicesManager(BaseComposeManager): rc, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src) events = self.parse_events(stderr, dry_run=self.check_mode, nonzero_rc=rc != 0) self.emit_warnings(events) - self.update_result(result, events, stdout, stderr, ignore_service_pull_events=True, ignore_build_events=self.ignore_build_events) + self.update_result( + result, + events, + stdout, + stderr, + ignore_service_pull_events=True, + ignore_build_events=self.ignore_build_events, + ) self.update_failed(result, events, args, stdout, stderr, rc) return result def get_stop_cmd(self, dry_run): - args = self.get_base_args() + ['stop'] + args = self.get_base_args() + ["stop"] if self.timeout is not None: - args.extend(['--timeout', f'{self.timeout}']) + args.extend(["--timeout", f"{self.timeout}"]) if dry_run: - args.append('--dry-run') - args.append('--') + args.append("--dry-run") + args.append("--") for service in self.services: args.append(service) return args def _are_containers_stopped(self): for container in self.list_containers_raw(): - if container['State'] not in ('created', 'exited', 'stopped', 'killed'): + if container["State"] not in ("created", "exited", "stopped", "killed"): return False return True @@ -578,20 +588,33 @@ class ServicesManager(BaseComposeManager): # Make sure all containers are created args_1 = self.get_up_cmd(self.check_mode, no_start=True) rc_1, stdout_1, stderr_1 = self.client.call_cli(*args_1, cwd=self.project_src) - events_1 = self.parse_events(stderr_1, dry_run=self.check_mode, nonzero_rc=rc_1 != 0) + events_1 = self.parse_events( + stderr_1, dry_run=self.check_mode, nonzero_rc=rc_1 != 0 + ) self.emit_warnings(events_1) - self.update_result(result, events_1, stdout_1, stderr_1, ignore_service_pull_events=True, ignore_build_events=self.ignore_build_events) + self.update_result( + result, + events_1, + stdout_1, + stderr_1, + ignore_service_pull_events=True, + ignore_build_events=self.ignore_build_events, + ) is_failed_1 = is_failed(events_1, rc_1) if not is_failed_1 and not self._are_containers_stopped(): # Make sure all containers are stopped args_2 = self.get_stop_cmd(self.check_mode) - rc_2, stdout_2, stderr_2 = self.client.call_cli(*args_2, cwd=self.project_src) - events_2 = self.parse_events(stderr_2, dry_run=self.check_mode, nonzero_rc=rc_2 != 0) + rc_2, stdout_2, stderr_2 = self.client.call_cli( + *args_2, cwd=self.project_src + ) + events_2 = self.parse_events( + stderr_2, dry_run=self.check_mode, nonzero_rc=rc_2 != 0 + ) self.emit_warnings(events_2) self.update_result(result, events_2, stdout_2, stderr_2) else: args_2 = [] - rc_2, stdout_2, stderr_2 = 0, b'', b'' + rc_2, stdout_2, stderr_2 = 0, b"", b"" events_2 = [] # Compose result self.update_failed( @@ -605,14 +628,14 @@ class ServicesManager(BaseComposeManager): return result def get_restart_cmd(self, dry_run): - args = self.get_base_args() + ['restart'] + args = self.get_base_args() + ["restart"] if not self.dependencies: - args.append('--no-deps') + args.append("--no-deps") if self.timeout is not None: - args.extend(['--timeout', f'{self.timeout}']) + args.extend(["--timeout", f"{self.timeout}"]) if dry_run: - args.append('--dry-run') - args.append('--') + args.append("--dry-run") + args.append("--") for service in self.services: args.append(service) return args @@ -628,18 +651,18 @@ class ServicesManager(BaseComposeManager): return result def get_down_cmd(self, dry_run): - args = self.get_base_args() + ['down'] + args = self.get_base_args() + ["down"] if self.remove_orphans: - args.append('--remove-orphans') + args.append("--remove-orphans") if self.remove_images: - args.extend(['--rmi', self.remove_images]) + args.extend(["--rmi", self.remove_images]) if self.remove_volumes: - args.append('--volumes') + args.append("--volumes") if self.timeout is not None: - args.extend(['--timeout', f'{self.timeout}']) + args.extend(["--timeout", f"{self.timeout}"]) if dry_run: - args.append('--dry-run') - args.append('--') + args.append("--dry-run") + args.append("--") for service in self.services: args.append(service) return args @@ -657,31 +680,39 @@ class ServicesManager(BaseComposeManager): def main(): argument_spec = dict( - state=dict(type='str', default='present', choices=['absent', 'present', 'stopped', 'restarted']), - dependencies=dict(type='bool', default=True), - pull=dict(type='str', choices=['always', 'missing', 'never', 'policy'], default='policy'), - build=dict(type='str', choices=['always', 'never', 'policy'], default='policy'), - recreate=dict(type='str', default='auto', choices=['always', 'never', 'auto']), - renew_anon_volumes=dict(type='bool', default=False), - remove_images=dict(type='str', choices=['all', 'local']), - remove_volumes=dict(type='bool', default=False), - remove_orphans=dict(type='bool', default=False), - timeout=dict(type='int'), - services=dict(type='list', elements='str'), - scale=dict(type='dict'), - wait=dict(type='bool', default=False), - wait_timeout=dict(type='int'), - ignore_build_events=dict(type='bool', default=True), - assume_yes=dict(type='bool', default=False), + state=dict( + type="str", + default="present", + choices=["absent", "present", "stopped", "restarted"], + ), + dependencies=dict(type="bool", default=True), + pull=dict( + type="str", + choices=["always", "missing", "never", "policy"], + default="policy", + ), + build=dict(type="str", choices=["always", "never", "policy"], default="policy"), + recreate=dict(type="str", default="auto", choices=["always", "never", "auto"]), + renew_anon_volumes=dict(type="bool", default=False), + remove_images=dict(type="str", choices=["all", "local"]), + remove_volumes=dict(type="bool", default=False), + remove_orphans=dict(type="bool", default=False), + timeout=dict(type="int"), + services=dict(type="list", elements="str"), + scale=dict(type="dict"), + wait=dict(type="bool", default=False), + wait_timeout=dict(type="int"), + ignore_build_events=dict(type="bool", default=True), + assume_yes=dict(type="bool", default=False), ) argspec_ex = common_compose_argspec_ex() - argument_spec.update(argspec_ex.pop('argspec')) + argument_spec.update(argspec_ex.pop("argspec")) client = AnsibleModuleDockerClient( argument_spec=argument_spec, supports_check_mode=True, needs_api_version=False, - **argspec_ex + **argspec_ex, ) try: @@ -690,8 +721,11 @@ def main(): manager.cleanup() client.module.exit_json(**result) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_compose_v2_exec.py b/plugins/modules/docker_compose_v2_exec.py index 24b29304..7bc3c813 100644 --- a/plugins/modules/docker_compose_v2_exec.py +++ b/plugins/modules/docker_compose_v2_exec.py @@ -168,12 +168,10 @@ import shlex import traceback from ansible.module_utils.common.text.converters import to_text - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, ) - from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( BaseComposeManager, common_compose_argspec_ex, @@ -185,26 +183,26 @@ class ExecManager(BaseComposeManager): super(ExecManager, self).__init__(client) parameters = self.client.module.params - self.service = parameters['service'] - self.index = parameters['index'] - self.chdir = parameters['chdir'] - self.detach = parameters['detach'] - self.user = parameters['user'] - self.stdin = parameters['stdin'] - self.strip_empty_ends = parameters['strip_empty_ends'] - self.privileged = parameters['privileged'] - self.tty = parameters['tty'] - self.env = parameters['env'] + self.service = parameters["service"] + self.index = parameters["index"] + self.chdir = parameters["chdir"] + self.detach = parameters["detach"] + self.user = parameters["user"] + self.stdin = parameters["stdin"] + self.strip_empty_ends = parameters["strip_empty_ends"] + self.privileged = parameters["privileged"] + self.tty = parameters["tty"] + self.env = parameters["env"] - self.argv = parameters['argv'] - if parameters['command'] is not None: - self.argv = shlex.split(parameters['command']) + self.argv = parameters["argv"] + if parameters["command"] is not None: + self.argv = shlex.split(parameters["command"]) if self.detach and self.stdin is not None: - self.mail('If detach=true, stdin cannot be provided.') + self.mail("If detach=true, stdin cannot be provided.") - if self.stdin is not None and parameters['stdin_add_newline']: - self.stdin += '\n' + if self.stdin is not None and parameters["stdin_add_newline"]: + self.stdin += "\n" if self.env is not None: for name, value in list(self.env.items()): @@ -213,27 +211,27 @@ class ExecManager(BaseComposeManager): "Non-string value found for env option. Ambiguous env options must be " f"wrapped in quotes to avoid them being interpreted. Key: {name}" ) - self.env[name] = to_text(value, errors='surrogate_or_strict') + self.env[name] = to_text(value, errors="surrogate_or_strict") def get_exec_cmd(self, dry_run, no_start=False): - args = self.get_base_args(plain_progress=True) + ['exec'] + args = self.get_base_args(plain_progress=True) + ["exec"] if self.index is not None: - args.extend(['--index', str(self.index)]) + args.extend(["--index", str(self.index)]) if self.chdir is not None: - args.extend(['--workdir', self.chdir]) + args.extend(["--workdir", self.chdir]) if self.detach: - args.extend(['--detach']) + args.extend(["--detach"]) if self.user is not None: - args.extend(['--user', self.user]) + args.extend(["--user", self.user]) if self.privileged: - args.append('--privileged') + args.append("--privileged") if not self.tty: - args.append('--no-TTY') + args.append("--no-TTY") if self.env: for name, value in list(self.env.items()): - args.append('--env') - args.append(f'{name}={value}') - args.append('--') + args.append("--env") + args.append(f"{name}={value}") + args.append("--") args.append(self.service) args.extend(self.argv) return args @@ -241,52 +239,52 @@ class ExecManager(BaseComposeManager): def run(self): args = self.get_exec_cmd(self.check_mode) kwargs = { - 'cwd': self.project_src, + "cwd": self.project_src, } if self.stdin is not None: - kwargs['data'] = self.stdin.encode('utf-8') + kwargs["data"] = self.stdin.encode("utf-8") if self.detach: - kwargs['check_rc'] = True + kwargs["check_rc"] = True rc, stdout, stderr = self.client.call_cli(*args, **kwargs) if self.detach: return {} stdout = to_text(stdout) stderr = to_text(stderr) if self.strip_empty_ends: - stdout = stdout.rstrip('\r\n') - stderr = stderr.rstrip('\r\n') + stdout = stdout.rstrip("\r\n") + stderr = stderr.rstrip("\r\n") return { - 'changed': True, - 'rc': rc, - 'stdout': stdout, - 'stderr': stderr, + "changed": True, + "rc": rc, + "stdout": stdout, + "stderr": stderr, } def main(): argument_spec = dict( - service=dict(type='str', required=True), - index=dict(type='int'), - argv=dict(type='list', elements='str'), - command=dict(type='str'), - chdir=dict(type='str'), - detach=dict(type='bool', default=False), - user=dict(type='str'), - stdin=dict(type='str'), - stdin_add_newline=dict(type='bool', default=True), - strip_empty_ends=dict(type='bool', default=True), - privileged=dict(type='bool', default=False), - tty=dict(type='bool', default=True), - env=dict(type='dict'), + service=dict(type="str", required=True), + index=dict(type="int"), + argv=dict(type="list", elements="str"), + command=dict(type="str"), + chdir=dict(type="str"), + detach=dict(type="bool", default=False), + user=dict(type="str"), + stdin=dict(type="str"), + stdin_add_newline=dict(type="bool", default=True), + strip_empty_ends=dict(type="bool", default=True), + privileged=dict(type="bool", default=False), + tty=dict(type="bool", default=True), + env=dict(type="dict"), ) argspec_ex = common_compose_argspec_ex() - argument_spec.update(argspec_ex.pop('argspec')) + argument_spec.update(argspec_ex.pop("argspec")) client = AnsibleModuleDockerClient( argument_spec=argument_spec, supports_check_mode=False, needs_api_version=False, - **argspec_ex + **argspec_ex, ) try: @@ -295,8 +293,11 @@ def main(): manager.cleanup() client.module.exit_json(**result) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_compose_v2_pull.py b/plugins/modules/docker_compose_v2_pull.py index d2b4f68f..c6493ff3 100644 --- a/plugins/modules/docker_compose_v2_pull.py +++ b/plugins/modules/docker_compose_v2_pull.py @@ -116,13 +116,13 @@ from ansible_collections.community.docker.plugins.module_utils.common_cli import AnsibleModuleDockerClient, DockerException, ) - from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( BaseComposeManager, common_compose_argspec_ex, ) - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) class PullManager(BaseComposeManager): @@ -130,31 +130,33 @@ class PullManager(BaseComposeManager): super(PullManager, self).__init__(client) parameters = self.client.module.params - self.policy = parameters['policy'] - self.ignore_buildable = parameters['ignore_buildable'] - self.include_deps = parameters['include_deps'] - self.services = parameters['services'] or [] + self.policy = parameters["policy"] + self.ignore_buildable = parameters["ignore_buildable"] + self.include_deps = parameters["include_deps"] + self.services = parameters["services"] or [] - if self.policy != 'always' and self.compose_version < LooseVersion('2.22.0'): + if self.policy != "always" and self.compose_version < LooseVersion("2.22.0"): # https://github.com/docker/compose/pull/10981 - 2.22.0 self.fail( - f'A pull policy other than always is only supported since Docker Compose 2.22.0. {self.client.get_cli()} has version {self.compose_version}') - if self.ignore_buildable and self.compose_version < LooseVersion('2.15.0'): + f"A pull policy other than always is only supported since Docker Compose 2.22.0. {self.client.get_cli()} has version {self.compose_version}" + ) + if self.ignore_buildable and self.compose_version < LooseVersion("2.15.0"): # https://github.com/docker/compose/pull/10134 - 2.15.0 self.fail( - f'--ignore-buildable is only supported since Docker Compose 2.15.0. {self.client.get_cli()} has version {self.compose_version}') + f"--ignore-buildable is only supported since Docker Compose 2.15.0. {self.client.get_cli()} has version {self.compose_version}" + ) def get_pull_cmd(self, dry_run, no_start=False): - args = self.get_base_args() + ['pull'] - if self.policy != 'always': - args.extend(['--policy', self.policy]) + args = self.get_base_args() + ["pull"] + if self.policy != "always": + args.extend(["--policy", self.policy]) if self.ignore_buildable: - args.append('--ignore-buildable') + args.append("--ignore-buildable") if self.include_deps: - args.append('--include-deps') + args.append("--include-deps") if dry_run: - args.append('--dry-run') - args.append('--') + args.append("--dry-run") + args.append("--") for service in self.services: args.append(service) return args @@ -165,7 +167,13 @@ class PullManager(BaseComposeManager): rc, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src) events = self.parse_events(stderr, dry_run=self.check_mode, nonzero_rc=rc != 0) self.emit_warnings(events) - self.update_result(result, events, stdout, stderr, ignore_service_pull_events=self.policy != 'missing' and not self.check_mode) + self.update_result( + result, + events, + stdout, + stderr, + ignore_service_pull_events=self.policy != "missing" and not self.check_mode, + ) self.update_failed(result, events, args, stdout, stderr, rc) self.cleanup_result(result) return result @@ -173,19 +181,19 @@ class PullManager(BaseComposeManager): def main(): argument_spec = dict( - policy=dict(type='str', choices=['always', 'missing'], default='always'), - ignore_buildable=dict(type='bool', default=False), - include_deps=dict(type='bool', default=False), - services=dict(type='list', elements='str'), + policy=dict(type="str", choices=["always", "missing"], default="always"), + ignore_buildable=dict(type="bool", default=False), + include_deps=dict(type="bool", default=False), + services=dict(type="list", elements="str"), ) argspec_ex = common_compose_argspec_ex() - argument_spec.update(argspec_ex.pop('argspec')) + argument_spec.update(argspec_ex.pop("argspec")) client = AnsibleModuleDockerClient( argument_spec=argument_spec, supports_check_mode=True, needs_api_version=False, - **argspec_ex + **argspec_ex, ) try: @@ -194,8 +202,11 @@ def main(): manager.cleanup() client.module.exit_json(**result) except DockerException as e: - client.fail(f'An unexpected docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_compose_v2_run.py b/plugins/modules/docker_compose_v2_run.py index afec8608..765b18ce 100644 --- a/plugins/modules/docker_compose_v2_run.py +++ b/plugins/modules/docker_compose_v2_run.py @@ -241,12 +241,10 @@ import shlex import traceback from ansible.module_utils.common.text.converters import to_text - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, ) - from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( BaseComposeManager, common_compose_argspec_ex, @@ -258,39 +256,39 @@ class ExecManager(BaseComposeManager): super(ExecManager, self).__init__(client) parameters = self.client.module.params - self.service = parameters['service'] - self.build = parameters['build'] - self.cap_add = parameters['cap_add'] - self.cap_drop = parameters['cap_drop'] - self.entrypoint = parameters['entrypoint'] - self.interactive = parameters['interactive'] - self.labels = parameters['labels'] - self.name = parameters['name'] - self.no_deps = parameters['no_deps'] - self.publish = parameters['publish'] - self.quiet_pull = parameters['quiet_pull'] - self.remove_orphans = parameters['remove_orphans'] - self.do_cleanup = parameters['cleanup'] - self.service_ports = parameters['service_ports'] - self.use_aliases = parameters['use_aliases'] - self.volumes = parameters['volumes'] - self.chdir = parameters['chdir'] - self.detach = parameters['detach'] - self.user = parameters['user'] - self.stdin = parameters['stdin'] - self.strip_empty_ends = parameters['strip_empty_ends'] - self.tty = parameters['tty'] - self.env = parameters['env'] + self.service = parameters["service"] + self.build = parameters["build"] + self.cap_add = parameters["cap_add"] + self.cap_drop = parameters["cap_drop"] + self.entrypoint = parameters["entrypoint"] + self.interactive = parameters["interactive"] + self.labels = parameters["labels"] + self.name = parameters["name"] + self.no_deps = parameters["no_deps"] + self.publish = parameters["publish"] + self.quiet_pull = parameters["quiet_pull"] + self.remove_orphans = parameters["remove_orphans"] + self.do_cleanup = parameters["cleanup"] + self.service_ports = parameters["service_ports"] + self.use_aliases = parameters["use_aliases"] + self.volumes = parameters["volumes"] + self.chdir = parameters["chdir"] + self.detach = parameters["detach"] + self.user = parameters["user"] + self.stdin = parameters["stdin"] + self.strip_empty_ends = parameters["strip_empty_ends"] + self.tty = parameters["tty"] + self.env = parameters["env"] - self.argv = parameters['argv'] - if parameters['command'] is not None: - self.argv = shlex.split(parameters['command']) + self.argv = parameters["argv"] + if parameters["command"] is not None: + self.argv = shlex.split(parameters["command"]) if self.detach and self.stdin is not None: - self.mail('If detach=true, stdin cannot be provided.') + self.mail("If detach=true, stdin cannot be provided.") - if self.stdin is not None and parameters['stdin_add_newline']: - self.stdin += '\n' + if self.stdin is not None and parameters["stdin_add_newline"]: + self.stdin += "\n" if self.env is not None: for name, value in list(self.env.items()): @@ -299,58 +297,58 @@ class ExecManager(BaseComposeManager): "Non-string value found for env option. Ambiguous env options must be " f"wrapped in quotes to avoid them being interpreted. Key: {name}" ) - self.env[name] = to_text(value, errors='surrogate_or_strict') + self.env[name] = to_text(value, errors="surrogate_or_strict") def get_run_cmd(self, dry_run, no_start=False): - args = self.get_base_args(plain_progress=True) + ['run'] + args = self.get_base_args(plain_progress=True) + ["run"] if self.build: - args.append('--build') + args.append("--build") if self.cap_add: for cap in self.cap_add: - args.extend(['--cap-add', cap]) + args.extend(["--cap-add", cap]) if self.cap_drop: for cap in self.cap_drop: - args.extend(['--cap-drop', cap]) + args.extend(["--cap-drop", cap]) if self.entrypoint is not None: - args.extend(['--entrypoint', self.entrypoint]) + args.extend(["--entrypoint", self.entrypoint]) if not self.interactive: - args.append('--no-interactive') + args.append("--no-interactive") if self.labels: for label in self.labels: - args.extend(['--label', label]) + args.extend(["--label", label]) if self.name is not None: - args.extend(['--name', self.name]) + args.extend(["--name", self.name]) if self.no_deps: - args.append('--no-deps') + args.append("--no-deps") if self.publish: for publish in self.publish: - args.extend(['--publish', publish]) + args.extend(["--publish", publish]) if self.quiet_pull: - args.append('--quiet-pull') + args.append("--quiet-pull") if self.remove_orphans: - args.append('--remove-orphans') + args.append("--remove-orphans") if self.do_cleanup: - args.append('--rm') + args.append("--rm") if self.service_ports: - args.append('--service-ports') + args.append("--service-ports") if self.use_aliases: - args.append('--use-aliases') + args.append("--use-aliases") if self.volumes: for volume in self.volumes: - args.extend(['--volume', volume]) + args.extend(["--volume", volume]) if self.chdir is not None: - args.extend(['--workdir', self.chdir]) + args.extend(["--workdir", self.chdir]) if self.detach: - args.extend(['--detach']) + args.extend(["--detach"]) if self.user is not None: - args.extend(['--user', self.user]) + args.extend(["--user", self.user]) if not self.tty: - args.append('--no-TTY') + args.append("--no-TTY") if self.env: for name, value in list(self.env.items()): - args.append('--env') - args.append(f'{name}={value}') - args.append('--') + args.append("--env") + args.append(f"{name}={value}") + args.append("--") args.append(self.service) if self.argv: args.extend(self.argv) @@ -359,67 +357,67 @@ class ExecManager(BaseComposeManager): def run(self): args = self.get_run_cmd(self.check_mode) kwargs = { - 'cwd': self.project_src, + "cwd": self.project_src, } if self.stdin is not None: - kwargs['data'] = self.stdin.encode('utf-8') + kwargs["data"] = self.stdin.encode("utf-8") if self.detach: - kwargs['check_rc'] = True + kwargs["check_rc"] = True rc, stdout, stderr = self.client.call_cli(*args, **kwargs) if self.detach: return { - 'container_id': stdout.strip(), + "container_id": stdout.strip(), } stdout = to_text(stdout) stderr = to_text(stderr) if self.strip_empty_ends: - stdout = stdout.rstrip('\r\n') - stderr = stderr.rstrip('\r\n') + stdout = stdout.rstrip("\r\n") + stderr = stderr.rstrip("\r\n") return { - 'changed': True, - 'rc': rc, - 'stdout': stdout, - 'stderr': stderr, + "changed": True, + "rc": rc, + "stdout": stdout, + "stderr": stderr, } def main(): argument_spec = dict( - service=dict(type='str', required=True), - argv=dict(type='list', elements='str'), - command=dict(type='str'), - build=dict(type='bool', default=False), - cap_add=dict(type='list', elements='str'), - cap_drop=dict(type='list', elements='str'), - entrypoint=dict(type='str'), - interactive=dict(type='bool', default=True), - labels=dict(type='list', elements='str'), - name=dict(type='str'), - no_deps=dict(type='bool', default=False), - publish=dict(type='list', elements='str'), - quiet_pull=dict(type='bool', default=False), - remove_orphans=dict(type='bool', default=False), - cleanup=dict(type='bool', default=False), - service_ports=dict(type='bool', default=False), - use_aliases=dict(type='bool', default=False), - volumes=dict(type='list', elements='str'), - chdir=dict(type='str'), - detach=dict(type='bool', default=False), - user=dict(type='str'), - stdin=dict(type='str'), - stdin_add_newline=dict(type='bool', default=True), - strip_empty_ends=dict(type='bool', default=True), - tty=dict(type='bool', default=True), - env=dict(type='dict'), + service=dict(type="str", required=True), + argv=dict(type="list", elements="str"), + command=dict(type="str"), + build=dict(type="bool", default=False), + cap_add=dict(type="list", elements="str"), + cap_drop=dict(type="list", elements="str"), + entrypoint=dict(type="str"), + interactive=dict(type="bool", default=True), + labels=dict(type="list", elements="str"), + name=dict(type="str"), + no_deps=dict(type="bool", default=False), + publish=dict(type="list", elements="str"), + quiet_pull=dict(type="bool", default=False), + remove_orphans=dict(type="bool", default=False), + cleanup=dict(type="bool", default=False), + service_ports=dict(type="bool", default=False), + use_aliases=dict(type="bool", default=False), + volumes=dict(type="list", elements="str"), + chdir=dict(type="str"), + detach=dict(type="bool", default=False), + user=dict(type="str"), + stdin=dict(type="str"), + stdin_add_newline=dict(type="bool", default=True), + strip_empty_ends=dict(type="bool", default=True), + tty=dict(type="bool", default=True), + env=dict(type="dict"), ) argspec_ex = common_compose_argspec_ex() - argument_spec.update(argspec_ex.pop('argspec')) + argument_spec.update(argspec_ex.pop("argspec")) client = AnsibleModuleDockerClient( argument_spec=argument_spec, supports_check_mode=False, needs_api_version=False, - **argspec_ex + **argspec_ex, ) try: @@ -428,8 +426,11 @@ def main(): manager.cleanup() client.module.exit_json(**result) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_config.py b/plugins/modules/docker_config.py index 5ca8d0c7..0d1b6231 100644 --- a/plugins/modules/docker_config.py +++ b/plugins/modules/docker_config.py @@ -199,12 +199,14 @@ import base64 import hashlib import traceback + try: - from docker.errors import DockerException, APIError + from docker.errors import APIError, DockerException except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass +from ansible.module_utils.common.text.converters import to_bytes from ansible_collections.community.docker.plugins.module_utils.common import ( AnsibleDockerClient, RequestException, @@ -214,7 +216,6 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( compare_generic, sanitize_labels, ) -from ansible.module_utils.common.text.converters import to_bytes class ConfigManager(DockerBaseClass): @@ -228,26 +229,26 @@ class ConfigManager(DockerBaseClass): self.check_mode = self.client.check_mode parameters = self.client.module.params - self.name = parameters.get('name') - self.state = parameters.get('state') - self.data = parameters.get('data') + self.name = parameters.get("name") + self.state = parameters.get("state") + self.data = parameters.get("data") if self.data is not None: - if parameters.get('data_is_b64'): + if parameters.get("data_is_b64"): self.data = base64.b64decode(self.data) else: self.data = to_bytes(self.data) - data_src = parameters.get('data_src') + data_src = parameters.get("data_src") if data_src is not None: try: - with open(data_src, 'rb') as f: + with open(data_src, "rb") as f: self.data = f.read() except Exception as exc: - self.client.fail(f'Error while reading {data_src}: {exc}') - self.labels = parameters.get('labels') - self.force = parameters.get('force') - self.rolling_versions = parameters.get('rolling_versions') - self.versions_to_keep = parameters.get('versions_to_keep') - self.template_driver = parameters.get('template_driver') + self.client.fail(f"Error while reading {data_src}: {exc}") + self.labels = parameters.get("labels") + self.force = parameters.get("force") + self.rolling_versions = parameters.get("rolling_versions") + self.versions_to_keep = parameters.get("versions_to_keep") + self.template_driver = parameters.get("template_driver") if self.rolling_versions: self.version = 0 @@ -256,16 +257,18 @@ class ConfigManager(DockerBaseClass): def __call__(self): self.get_config() - if self.state == 'present': + if self.state == "present": self.data_key = hashlib.sha224(self.data).hexdigest() self.present() self.remove_old_versions() - elif self.state == 'absent': + elif self.state == "absent": self.absent() def get_version(self, config): try: - return int(config.get('Spec', {}).get('Labels', {}).get('ansible_version', 0)) + return int( + config.get("Spec", {}).get("Labels", {}).get("ansible_version", 0) + ) except ValueError: return 0 @@ -277,9 +280,9 @@ class ConfigManager(DockerBaseClass): self.remove_config(self.configs.pop(0)) def get_config(self): - ''' Find an existing config. ''' + """Find an existing config.""" try: - configs = self.client.configs(filters={'name': self.name}) + configs = self.client.configs(filters={"name": self.name}) except APIError as exc: self.client.fail(f"Error accessing config {self.name}: {exc}") @@ -287,25 +290,23 @@ class ConfigManager(DockerBaseClass): self.configs = [ config for config in configs - if config['Spec']['Name'].startswith(f'{self.name}_v') + if config["Spec"]["Name"].startswith(f"{self.name}_v") ] self.configs.sort(key=self.get_version) else: self.configs = [ - config for config in configs if config['Spec']['Name'] == self.name + config for config in configs if config["Spec"]["Name"] == self.name ] def create_config(self): - ''' Create a new config ''' + """Create a new config""" config_id = None # We ca not see the data after creation, so adding a label we can use for idempotency check - labels = { - 'ansible_key': self.data_key - } + labels = {"ansible_key": self.data_key} if self.rolling_versions: self.version += 1 - labels['ansible_version'] = str(self.version) - self.name = f'{self.name}_v{self.version}' + labels["ansible_version"] = str(self.version) + self.name = f"{self.name}_v{self.version}" if self.labels: labels.update(self.labels) @@ -314,49 +315,53 @@ class ConfigManager(DockerBaseClass): # only use templating argument when self.template_driver is defined kwargs = {} if self.template_driver: - kwargs['templating'] = { - 'name': self.template_driver - } - config_id = self.client.create_config(self.name, self.data, labels=labels, **kwargs) - self.configs += self.client.configs(filters={'id': config_id}) + kwargs["templating"] = {"name": self.template_driver} + config_id = self.client.create_config( + self.name, self.data, labels=labels, **kwargs + ) + self.configs += self.client.configs(filters={"id": config_id}) except APIError as exc: self.client.fail(f"Error creating config: {exc}") if isinstance(config_id, dict): - config_id = config_id['ID'] + config_id = config_id["ID"] return config_id def remove_config(self, config): try: if not self.check_mode: - self.client.remove_config(config['ID']) + self.client.remove_config(config["ID"]) except APIError as exc: self.client.fail(f"Error removing config {config['Spec']['Name']}: {exc}") def present(self): - ''' Handles state == 'present', creating or updating the config ''' + """Handles state == 'present', creating or updating the config""" if self.configs: config = self.configs[-1] - self.results['config_id'] = config['ID'] - self.results['config_name'] = config['Spec']['Name'] + self.results["config_id"] = config["ID"] + self.results["config_name"] = config["Spec"]["Name"] data_changed = False template_driver_changed = False - attrs = config.get('Spec', {}) - if attrs.get('Labels', {}).get('ansible_key'): - if attrs['Labels']['ansible_key'] != self.data_key: + attrs = config.get("Spec", {}) + if attrs.get("Labels", {}).get("ansible_key"): + if attrs["Labels"]["ansible_key"] != self.data_key: data_changed = True else: if not self.force: - self.client.module.warn("'ansible_key' label not found. Config will not be changed unless the force parameter is set to 'true'") + self.client.module.warn( + "'ansible_key' label not found. Config will not be changed unless the force parameter is set to 'true'" + ) # template_driver has changed if it was set in the previous config # and now it differs, or if it was not set but now it is. - if attrs.get('Templating', {}).get('Name'): - if attrs['Templating']['Name'] != self.template_driver: + if attrs.get("Templating", {}).get("Name"): + if attrs["Templating"]["Name"] != self.template_driver: template_driver_changed = True elif self.template_driver: template_driver_changed = True - labels_changed = not compare_generic(self.labels, attrs.get('Labels'), 'allow_more_present', 'dict') + labels_changed = not compare_generic( + self.labels, attrs.get("Labels"), "allow_more_present", "dict" + ) if self.rolling_versions: self.version = self.get_version(config) if data_changed or template_driver_changed or labels_changed or self.force: @@ -364,46 +369,46 @@ class ConfigManager(DockerBaseClass): if not self.rolling_versions: self.absent() config_id = self.create_config() - self.results['changed'] = True - self.results['config_id'] = config_id - self.results['config_name'] = self.name + self.results["changed"] = True + self.results["config_id"] = config_id + self.results["config_name"] = self.name else: - self.results['changed'] = True - self.results['config_id'] = self.create_config() - self.results['config_name'] = self.name + self.results["changed"] = True + self.results["config_id"] = self.create_config() + self.results["config_name"] = self.name def absent(self): - ''' Handles state == 'absent', removing the config ''' + """Handles state == 'absent', removing the config""" if self.configs: for config in self.configs: self.remove_config(config) - self.results['changed'] = True + self.results["changed"] = True def main(): argument_spec = dict( - name=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - data=dict(type='str'), - data_is_b64=dict(type='bool', default=False), - data_src=dict(type='path'), - labels=dict(type='dict'), - force=dict(type='bool', default=False), - rolling_versions=dict(type='bool', default=False), - versions_to_keep=dict(type='int', default=5), - template_driver=dict(type='str', choices=['golang']), + name=dict(type="str", required=True), + state=dict(type="str", default="present", choices=["absent", "present"]), + data=dict(type="str"), + data_is_b64=dict(type="bool", default=False), + data_src=dict(type="path"), + labels=dict(type="dict"), + force=dict(type="bool", default=False), + rolling_versions=dict(type="bool", default=False), + versions_to_keep=dict(type="int", default=5), + template_driver=dict(type="str", choices=["golang"]), ) required_if = [ - ('state', 'present', ['data', 'data_src'], True), + ("state", "present", ["data", "data_src"], True), ] mutually_exclusive = [ - ('data', 'data_src'), + ("data", "data_src"), ] option_minimal_versions = dict( - template_driver=dict(docker_py_version='5.0.3', docker_api_version='1.37'), + template_driver=dict(docker_py_version="5.0.3", docker_api_version="1.37"), ) client = AnsibleDockerClient( @@ -411,11 +416,11 @@ def main(): supports_check_mode=True, required_if=required_if, mutually_exclusive=mutually_exclusive, - min_docker_version='2.6.0', - min_docker_api_version='1.30', + min_docker_version="2.6.0", + min_docker_api_version="1.30", option_minimal_versions=option_minimal_versions, ) - sanitize_labels(client.module.params['labels'], 'labels', client) + sanitize_labels(client.module.params["labels"], "labels", client) try: results = dict( @@ -425,12 +430,16 @@ def main(): ConfigManager(client, results)() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_container.py b/plugins/modules/docker_container.py index 47b32562..1c1f5ec1 100644 --- a/plugins/modules/docker_container.py +++ b/plugins/modules/docker_container.py @@ -1334,7 +1334,6 @@ status: from ansible_collections.community.docker.plugins.module_utils.module_container.docker_api import ( DockerAPIEngineDriver, ) - from ansible_collections.community.docker.plugins.module_utils.module_container.module import ( run_module, ) @@ -1345,5 +1344,5 @@ def main(): run_module(engine_driver) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_container_copy_into.py b/plugins/modules/docker_container_copy_into.py index 4896d62c..e5548a43 100644 --- a/plugins/modules/docker_container_copy_into.py +++ b/plugins/modules/docker_container_copy_into.py @@ -172,14 +172,19 @@ import traceback from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.module_utils.common.validation import check_type_int - -from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException, NotFound - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, + NotFound, +) +from ansible_collections.community.docker.plugins.module_utils._scramble import ( + generate_insecure_key, + scramble, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.copy import ( DockerFileCopyError, DockerFileNotFound, @@ -191,14 +196,12 @@ from ansible_collections.community.docker.plugins.module_utils.copy import ( stat_file, ) -from ansible_collections.community.docker.plugins.module_utils._scramble import generate_insecure_key, scramble - def are_fileobjs_equal(f1, f2): - '''Given two (buffered) file objects, compare their contents.''' + """Given two (buffered) file objects, compare their contents.""" blocksize = 65536 - b1buf = b'' - b2buf = b'' + b1buf = b"" + b2buf = b"" while True: if f1 and len(b1buf) < blocksize: f1b = f1.read(blocksize) @@ -226,14 +229,14 @@ def are_fileobjs_equal(f1, f2): def are_fileobjs_equal_read_first(f1, f2): - '''Given two (buffered) file objects, compare their contents. + """Given two (buffered) file objects, compare their contents. Returns a tuple (is_equal, content_of_f1), where the first element indicates whether the two file objects have the same content, and the second element is - the content of the first file object.''' + the content of the first file object.""" blocksize = 65536 - b1buf = b'' - b2buf = b'' + b1buf = b"" + b2buf = b"" is_equal = True content = [] while True: @@ -268,7 +271,7 @@ def are_fileobjs_equal_read_first(f1, f2): if f1: content.append(f1.read()) - return is_equal, b''.join(content) + return is_equal, b"".join(content) def is_container_file_not_regular_file(container_stat): @@ -283,18 +286,18 @@ def is_container_file_not_regular_file(container_stat): 32 - 11, # ModeCharDevice 32 - 13, # ModeIrregular ): - if container_stat['mode'] & (1 << bit) != 0: + if container_stat["mode"] & (1 << bit) != 0: return True return False def get_container_file_mode(container_stat): - mode = container_stat['mode'] & 0xFFF - if container_stat['mode'] & (1 << (32 - 9)) != 0: # ModeSetuid + mode = container_stat["mode"] & 0xFFF + if container_stat["mode"] & (1 << (32 - 9)) != 0: # ModeSetuid mode |= stat.S_ISUID # set UID bit - if container_stat['mode'] & (1 << (32 - 10)) != 0: # ModeSetgid + if container_stat["mode"] & (1 << (32 - 10)) != 0: # ModeSetgid mode |= stat.S_ISGID # set GID bit - if container_stat['mode'] & (1 << (32 - 12)) != 0: # ModeSticky + if container_stat["mode"] & (1 << (32 - 12)) != 0: # ModeSticky mode |= stat.S_ISVTX # sticky bit return mode @@ -302,77 +305,88 @@ def get_container_file_mode(container_stat): def add_other_diff(diff, in_path, member): if diff is None: return - diff['before_header'] = in_path + diff["before_header"] = in_path if member.isdir(): - diff['before'] = '(directory)' + diff["before"] = "(directory)" elif member.issym() or member.islnk(): - diff['before'] = member.linkname + diff["before"] = member.linkname elif member.ischr(): - diff['before'] = '(character device)' + diff["before"] = "(character device)" elif member.isblk(): - diff['before'] = '(block device)' + diff["before"] = "(block device)" elif member.isfifo(): - diff['before'] = '(fifo)' + diff["before"] = "(fifo)" elif member.isdev(): - diff['before'] = '(device)' + diff["before"] = "(device)" elif member.isfile(): - raise DockerUnexpectedError('should not be a regular file') + raise DockerUnexpectedError("should not be a regular file") else: - diff['before'] = '(unknown filesystem object)' + diff["before"] = "(unknown filesystem object)" -def retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat=None, link_target=None): +def retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat=None, + link_target=None, +): if diff is None: return if regular_stat is not None: # First handle all filesystem object types that are not regular files - if regular_stat['mode'] & (1 << (32 - 1)) != 0: - diff['before_header'] = container_path - diff['before'] = '(directory)' + if regular_stat["mode"] & (1 << (32 - 1)) != 0: + diff["before_header"] = container_path + diff["before"] = "(directory)" return - elif regular_stat['mode'] & (1 << (32 - 4)) != 0: - diff['before_header'] = container_path - diff['before'] = '(temporary file)' + elif regular_stat["mode"] & (1 << (32 - 4)) != 0: + diff["before_header"] = container_path + diff["before"] = "(temporary file)" return - elif regular_stat['mode'] & (1 << (32 - 5)) != 0: - diff['before_header'] = container_path - diff['before'] = link_target + elif regular_stat["mode"] & (1 << (32 - 5)) != 0: + diff["before_header"] = container_path + diff["before"] = link_target return - elif regular_stat['mode'] & (1 << (32 - 6)) != 0: - diff['before_header'] = container_path - diff['before'] = '(device)' + elif regular_stat["mode"] & (1 << (32 - 6)) != 0: + diff["before_header"] = container_path + diff["before"] = "(device)" return - elif regular_stat['mode'] & (1 << (32 - 7)) != 0: - diff['before_header'] = container_path - diff['before'] = '(named pipe)' + elif regular_stat["mode"] & (1 << (32 - 7)) != 0: + diff["before_header"] = container_path + diff["before"] = "(named pipe)" return - elif regular_stat['mode'] & (1 << (32 - 8)) != 0: - diff['before_header'] = container_path - diff['before'] = '(socket)' + elif regular_stat["mode"] & (1 << (32 - 8)) != 0: + diff["before_header"] = container_path + diff["before"] = "(socket)" return - elif regular_stat['mode'] & (1 << (32 - 11)) != 0: - diff['before_header'] = container_path - diff['before'] = '(character device)' + elif regular_stat["mode"] & (1 << (32 - 11)) != 0: + diff["before_header"] = container_path + diff["before"] = "(character device)" return - elif regular_stat['mode'] & (1 << (32 - 13)) != 0: - diff['before_header'] = container_path - diff['before'] = '(unknown filesystem object)' + elif regular_stat["mode"] & (1 << (32 - 13)) != 0: + diff["before_header"] = container_path + diff["before"] = "(unknown filesystem object)" return # Check whether file is too large - if regular_stat['size'] > max_file_size_for_diff > 0: - diff['dst_larger'] = max_file_size_for_diff + if regular_stat["size"] > max_file_size_for_diff > 0: + diff["dst_larger"] = max_file_size_for_diff return # We need to get hold of the content def process_none(in_path): - diff['before'] = '' + diff["before"] = "" def process_regular(in_path, tar, member): - add_diff_dst_from_regular_member(diff, max_file_size_for_diff, in_path, tar, member) + add_diff_dst_from_regular_member( + diff, max_file_size_for_diff, in_path, tar, member + ) def process_symlink(in_path, member): - diff['before_header'] = in_path - diff['before'] = member.linkname + diff["before_header"] = in_path + diff["before"] = member.linkname def process_other(in_path, member): add_other_diff(diff, in_path, member) @@ -390,53 +404,57 @@ def retrieve_diff(client, container, container_path, follow_links, diff, max_fil def is_binary(content): - if b'\x00' in content: + if b"\x00" in content: return True # TODO: better detection # (ansible-core also just checks for 0x00, and even just sticks to the first 8k, so this is not too bad...) return False -def are_fileobjs_equal_with_diff_of_first(f1, f2, size, diff, max_file_size_for_diff, container_path): +def are_fileobjs_equal_with_diff_of_first( + f1, f2, size, diff, max_file_size_for_diff, container_path +): if diff is None: return are_fileobjs_equal(f1, f2) if size > max_file_size_for_diff > 0: - diff['dst_larger'] = max_file_size_for_diff + diff["dst_larger"] = max_file_size_for_diff return are_fileobjs_equal(f1, f2) is_equal, content = are_fileobjs_equal_read_first(f1, f2) if is_binary(content): - diff['dst_binary'] = 1 + diff["dst_binary"] = 1 else: - diff['before_header'] = container_path - diff['before'] = to_text(content) + diff["before_header"] = container_path + diff["before"] = to_text(content) return is_equal -def add_diff_dst_from_regular_member(diff, max_file_size_for_diff, container_path, tar, member): +def add_diff_dst_from_regular_member( + diff, max_file_size_for_diff, container_path, tar, member +): if diff is None: return if member.size > max_file_size_for_diff > 0: - diff['dst_larger'] = max_file_size_for_diff + diff["dst_larger"] = max_file_size_for_diff return with tar.extractfile(member) as tar_f: content = tar_f.read() if is_binary(content): - diff['dst_binary'] = 1 + diff["dst_binary"] = 1 else: - diff['before_header'] = container_path - diff['before'] = to_text(content) + diff["before_header"] = container_path + diff["before"] = to_text(content) def copy_dst_to_src(diff): if diff is None: return for f, t in [ - ('dst_size', 'src_size'), - ('dst_binary', 'src_binary'), - ('before_header', 'after_header'), - ('before', 'after'), + ("dst_size", "src_size"), + ("dst_binary", "src_binary"), + ("before_header", "after_header"), + ("before", "after"), ]: if f in diff: diff[t] = diff[f] @@ -444,38 +462,61 @@ def copy_dst_to_src(diff): diff.pop(t) -def is_file_idempotent(client, container, managed_path, container_path, follow_links, local_follow_links, owner_id, group_id, mode, - force=False, diff=None, max_file_size_for_diff=1): +def is_file_idempotent( + client, + container, + managed_path, + container_path, + follow_links, + local_follow_links, + owner_id, + group_id, + mode, + force=False, + diff=None, + max_file_size_for_diff=1, +): # Retrieve information of local file try: - file_stat = os.stat(managed_path) if local_follow_links else os.lstat(managed_path) + file_stat = ( + os.stat(managed_path) if local_follow_links else os.lstat(managed_path) + ) except OSError as exc: if exc.errno == 2: - raise DockerFileNotFound(f'Cannot find local file {managed_path}') + raise DockerFileNotFound(f"Cannot find local file {managed_path}") raise if mode is None: mode = stat.S_IMODE(file_stat.st_mode) if not stat.S_ISLNK(file_stat.st_mode) and not stat.S_ISREG(file_stat.st_mode): - raise DockerFileCopyError('Local path {managed_path} is not a symbolic link or file') + raise DockerFileCopyError( + "Local path {managed_path} is not a symbolic link or file" + ) if diff is not None: if file_stat.st_size > max_file_size_for_diff > 0: - diff['src_larger'] = max_file_size_for_diff + diff["src_larger"] = max_file_size_for_diff elif stat.S_ISLNK(file_stat.st_mode): - diff['after_header'] = managed_path - diff['after'] = os.readlink(managed_path) + diff["after_header"] = managed_path + diff["after"] = os.readlink(managed_path) else: - with open(managed_path, 'rb') as f: + with open(managed_path, "rb") as f: content = f.read() if is_binary(content): - diff['src_binary'] = 1 + diff["src_binary"] = 1 else: - diff['after_header'] = managed_path - diff['after'] = to_text(content) + diff["after_header"] = managed_path + diff["after"] = to_text(content) # When forcing and we are not following links in the container, go! if force and not follow_links: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + ) return container_path, mode, False # Resolve symlinks in the container (if requested), and get information on container's file @@ -493,40 +534,112 @@ def is_file_idempotent(client, container, managed_path, container_path, follow_l # If the file was not found, continue if regular_stat is None: if diff is not None: - diff['before_header'] = container_path - diff['before'] = '' + diff["before_header"] = container_path + diff["before"] = "" return container_path, mode, False # When forcing, go! if force: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False # If force is set to False, and the destination exists, assume there's nothing to do if force is False: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) copy_dst_to_src(diff) return container_path, mode, True # Basic idempotency checks if stat.S_ISLNK(file_stat.st_mode): if link_target is None: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False local_link_target = os.readlink(managed_path) - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, local_link_target == link_target if link_target is not None: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False if is_container_file_not_regular_file(regular_stat): - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False - if file_stat.st_size != regular_stat['size']: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + if file_stat.st_size != regular_stat["size"]: + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False if mode != get_container_file_mode(regular_stat): - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False # Fetch file from container @@ -535,25 +648,31 @@ def is_file_idempotent(client, container, managed_path, container_path, follow_l def process_regular(in_path, tar, member): # Check things like user/group ID and mode - if any([ - member.mode & 0xFFF != mode, - member.uid != owner_id, - member.gid != group_id, - not stat.S_ISREG(file_stat.st_mode), - member.size != file_stat.st_size, - ]): - add_diff_dst_from_regular_member(diff, max_file_size_for_diff, in_path, tar, member) + if any( + [ + member.mode & 0xFFF != mode, + member.uid != owner_id, + member.gid != group_id, + not stat.S_ISREG(file_stat.st_mode), + member.size != file_stat.st_size, + ] + ): + add_diff_dst_from_regular_member( + diff, max_file_size_for_diff, in_path, tar, member + ) return container_path, mode, False with tar.extractfile(member) as tar_f: - with open(managed_path, 'rb') as local_f: - is_equal = are_fileobjs_equal_with_diff_of_first(tar_f, local_f, member.size, diff, max_file_size_for_diff, in_path) + with open(managed_path, "rb") as local_f: + is_equal = are_fileobjs_equal_with_diff_of_first( + tar_f, local_f, member.size, diff, max_file_size_for_diff, in_path + ) return container_path, mode, is_equal def process_symlink(in_path, member): if diff is not None: - diff['before_header'] = in_path - diff['before'] = member.linkname + diff["before_header"] = in_path + diff["before"] = member.linkname # Check things like user/group ID and mode if member.mode & 0xFFF != mode: @@ -585,8 +704,20 @@ def is_file_idempotent(client, container, managed_path, container_path, follow_l ) -def copy_file_into_container(client, container, managed_path, container_path, follow_links, local_follow_links, - owner_id, group_id, mode, force=False, diff=False, max_file_size_for_diff=1): +def copy_file_into_container( + client, + container, + managed_path, + container_path, + follow_links, + local_follow_links, + owner_id, + group_id, + mode, + force=False, + diff=False, + max_file_size_for_diff=1, +): if diff: diff = {} else: @@ -625,24 +756,42 @@ def copy_file_into_container(client, container, managed_path, container_path, fo changed=changed, ) if diff: - result['diff'] = diff + result["diff"] = diff client.module.exit_json(**result) -def is_content_idempotent(client, container, content, container_path, follow_links, owner_id, group_id, mode, - force=False, diff=None, max_file_size_for_diff=1): +def is_content_idempotent( + client, + container, + content, + container_path, + follow_links, + owner_id, + group_id, + mode, + force=False, + diff=None, + max_file_size_for_diff=1, +): if diff is not None: if len(content) > max_file_size_for_diff > 0: - diff['src_larger'] = max_file_size_for_diff + diff["src_larger"] = max_file_size_for_diff elif is_binary(content): - diff['src_binary'] = 1 + diff["src_binary"] = 1 else: - diff['after_header'] = 'dynamically generated' - diff['after'] = to_text(content) + diff["after_header"] = "dynamically generated" + diff["after"] = to_text(content) # When forcing and we are not following links in the container, go! if force and not follow_links: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + ) return container_path, mode, False # Resolve symlinks in the container (if requested), and get information on container's file @@ -660,60 +809,125 @@ def is_content_idempotent(client, container, content, container_path, follow_lin # If the file was not found, continue if regular_stat is None: if diff is not None: - diff['before_header'] = container_path - diff['before'] = '' + diff["before_header"] = container_path + diff["before"] = "" return container_path, mode, False # When forcing, go! if force: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False # If force is set to False, and the destination exists, assume there's nothing to do if force is False: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) copy_dst_to_src(diff) return container_path, mode, True # Basic idempotency checks if link_target is not None: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False if is_container_file_not_regular_file(regular_stat): - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False - if len(content) != regular_stat['size']: - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + if len(content) != regular_stat["size"]: + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False if mode != get_container_file_mode(regular_stat): - retrieve_diff(client, container, container_path, follow_links, diff, max_file_size_for_diff, regular_stat, link_target) + retrieve_diff( + client, + container, + container_path, + follow_links, + diff, + max_file_size_for_diff, + regular_stat, + link_target, + ) return container_path, mode, False # Fetch file from container def process_none(in_path): if diff is not None: - diff['before'] = '' + diff["before"] = "" return container_path, mode, False def process_regular(in_path, tar, member): # Check things like user/group ID and mode - if any([ - member.mode & 0xFFF != mode, - member.uid != owner_id, - member.gid != group_id, - member.size != len(content), - ]): - add_diff_dst_from_regular_member(diff, max_file_size_for_diff, in_path, tar, member) + if any( + [ + member.mode & 0xFFF != mode, + member.uid != owner_id, + member.gid != group_id, + member.size != len(content), + ] + ): + add_diff_dst_from_regular_member( + diff, max_file_size_for_diff, in_path, tar, member + ) return container_path, mode, False with tar.extractfile(member) as tar_f: - is_equal = are_fileobjs_equal_with_diff_of_first(tar_f, io.BytesIO(content), member.size, diff, max_file_size_for_diff, in_path) + is_equal = are_fileobjs_equal_with_diff_of_first( + tar_f, + io.BytesIO(content), + member.size, + diff, + max_file_size_for_diff, + in_path, + ) return container_path, mode, is_equal def process_symlink(in_path, member): if diff is not None: - diff['before_header'] = in_path - diff['before'] = member.linkname + diff["before_header"] = in_path + diff["before"] = member.linkname return container_path, mode, False @@ -733,8 +947,19 @@ def is_content_idempotent(client, container, content, container_path, follow_lin ) -def copy_content_into_container(client, container, content, container_path, follow_links, - owner_id, group_id, mode, force=False, diff=False, max_file_size_for_diff=1): +def copy_content_into_container( + client, + container, + content, + container_path, + follow_links, + owner_id, + group_id, + mode, + force=False, + diff=False, + max_file_size_for_diff=1, +): if diff: diff = {} else: @@ -773,11 +998,11 @@ def copy_content_into_container(client, container, content, container_path, foll if diff: # Since the content is no_log, make sure that the before/after strings look sufficiently different key = generate_insecure_key() - diff['scrambled_diff'] = base64.b64encode(key) - for k in ('before', 'after'): + diff["scrambled_diff"] = base64.b64encode(key) + for k in ("before", "after"): if k in diff: diff[k] = scramble(diff[k], key) - result['diff'] = diff + result["diff"] = diff client.module.exit_json(**result) @@ -786,65 +1011,68 @@ def parse_modern(mode): return int(to_native(mode), 8) if isinstance(mode, int): return mode - raise TypeError(f'must be an octal string or an integer, got {mode!r}') + raise TypeError(f"must be an octal string or an integer, got {mode!r}") def parse_octal_string_only(mode): if isinstance(mode, str): return int(to_native(mode), 8) - raise TypeError(f'must be an octal string, got {mode!r}') + raise TypeError(f"must be an octal string, got {mode!r}") def main(): argument_spec = dict( - container=dict(type='str', required=True), - path=dict(type='path'), - container_path=dict(type='str', required=True), - follow=dict(type='bool', default=False), - local_follow=dict(type='bool', default=True), - owner_id=dict(type='int'), - group_id=dict(type='int'), - mode=dict(type='raw'), - mode_parse=dict(type='str', choices=['legacy', 'modern', 'octal_string_only'], default='legacy'), - force=dict(type='bool'), - content=dict(type='str', no_log=True), - content_is_b64=dict(type='bool', default=False), - + container=dict(type="str", required=True), + path=dict(type="path"), + container_path=dict(type="str", required=True), + follow=dict(type="bool", default=False), + local_follow=dict(type="bool", default=True), + owner_id=dict(type="int"), + group_id=dict(type="int"), + mode=dict(type="raw"), + mode_parse=dict( + type="str", + choices=["legacy", "modern", "octal_string_only"], + default="legacy", + ), + force=dict(type="bool"), + content=dict(type="str", no_log=True), + content_is_b64=dict(type="bool", default=False), # Undocumented parameters for use by the action plugin - _max_file_size_for_diff=dict(type='int'), + _max_file_size_for_diff=dict(type="int"), ) client = AnsibleDockerClient( argument_spec=argument_spec, - min_docker_api_version='1.20', + min_docker_api_version="1.20", supports_check_mode=True, - mutually_exclusive=[('path', 'content')], - required_together=[('owner_id', 'group_id')], + mutually_exclusive=[("path", "content")], + required_together=[("owner_id", "group_id")], required_by={ - 'content': ['mode'], + "content": ["mode"], }, ) - container = client.module.params['container'] - managed_path = client.module.params['path'] - container_path = client.module.params['container_path'] - follow = client.module.params['follow'] - local_follow = client.module.params['local_follow'] - owner_id = client.module.params['owner_id'] - group_id = client.module.params['group_id'] - mode = client.module.params['mode'] - force = client.module.params['force'] - content = client.module.params['content'] - max_file_size_for_diff = client.module.params['_max_file_size_for_diff'] or 1 + container = client.module.params["container"] + managed_path = client.module.params["path"] + container_path = client.module.params["container_path"] + follow = client.module.params["follow"] + local_follow = client.module.params["local_follow"] + owner_id = client.module.params["owner_id"] + group_id = client.module.params["group_id"] + mode = client.module.params["mode"] + force = client.module.params["force"] + content = client.module.params["content"] + max_file_size_for_diff = client.module.params["_max_file_size_for_diff"] or 1 if mode is not None: - mode_parse = client.module.params['mode_parse'] + mode_parse = client.module.params["mode_parse"] try: - if mode_parse == 'legacy': + if mode_parse == "legacy": mode = check_type_int(mode) - elif mode_parse == 'modern': + elif mode_parse == "modern": mode = parse_modern(mode) - elif mode_parse == 'octal_string_only': + elif mode_parse == "octal_string_only": mode = parse_octal_string_only(mode) except (TypeError, ValueError) as e: client.fail(f"Error while parsing 'mode': {e}") @@ -852,11 +1080,13 @@ def main(): client.fail(f"'mode' must not be negative; got {mode}") if content is not None: - if client.module.params['content_is_b64']: + if client.module.params["content_is_b64"]: try: content = base64.b64decode(content) - except Exception as e: # depending on Python version and error, multiple different exceptions can be raised - client.fail(f'Cannot Base64 decode the content option: {e}') + except ( + Exception + ) as e: # depending on Python version and error, multiple different exceptions can be raised + client.fail(f"Cannot Base64 decode the content option: {e}") else: content = to_bytes(content) @@ -899,24 +1129,31 @@ def main(): ) else: # Can happen if a user explicitly passes `content: null` or `path: null`... - client.fail('One of path and content must be supplied') + client.fail("One of path and content must be supplied") except NotFound as exc: client.fail(f'Could not find container "{container}" or resource in it ({exc})') except APIError as exc: - client.fail(f'An unexpected Docker error occurred for container "{container}": {exc}', exception=traceback.format_exc()) + client.fail( + f'An unexpected Docker error occurred for container "{container}": {exc}', + exception=traceback.format_exc(), + ) except DockerException as exc: - client.fail(f'An unexpected Docker error occurred for container "{container}": {exc}', exception=traceback.format_exc()) + client.fail( + f'An unexpected Docker error occurred for container "{container}": {exc}', + exception=traceback.format_exc(), + ) except RequestException as exc: client.fail( f'An unexpected requests error occurred for container "{container}" when trying to talk to the Docker daemon: {exc}', - exception=traceback.format_exc()) + exception=traceback.format_exc(), + ) except DockerUnexpectedError as exc: - client.fail(f'Unexpected error: {exc}', exception=traceback.format_exc()) + client.fail(f"Unexpected error: {exc}", exception=traceback.format_exc()) except DockerFileCopyError as exc: client.fail(to_native(exc)) except OSError as exc: - client.fail(f'Unexpected error: {exc}', exception=traceback.format_exc()) + client.fail(f"Unexpected error: {exc}", exception=traceback.format_exc()) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_container_exec.py b/plugins/modules/docker_container_exec.py index ec4edd36..092a3d58 100644 --- a/plugins/modules/docker_container_exec.py +++ b/plugins/modules/docker_container_exec.py @@ -167,110 +167,116 @@ import selectors import shlex import traceback -from ansible.module_utils.common.text.converters import to_text, to_bytes - -from ansible_collections.community.docker.plugins.module_utils.common_api import ( - AnsibleDockerClient, - RequestException, -) - -from ansible_collections.community.docker.plugins.module_utils.socket_handler import ( - DockerSocketHandlerModule, -) - +from ansible.module_utils.common.text.converters import to_bytes, to_text from ansible_collections.community.docker.plugins.module_utils._api.errors import ( APIError, DockerException, NotFound, ) -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import format_environment +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + format_environment, +) +from ansible_collections.community.docker.plugins.module_utils.common_api import ( + AnsibleDockerClient, + RequestException, +) +from ansible_collections.community.docker.plugins.module_utils.socket_handler import ( + DockerSocketHandlerModule, +) def main(): argument_spec = dict( - container=dict(type='str', required=True), - argv=dict(type='list', elements='str'), - command=dict(type='str'), - chdir=dict(type='str'), - detach=dict(type='bool', default=False), - user=dict(type='str'), - stdin=dict(type='str'), - stdin_add_newline=dict(type='bool', default=True), - strip_empty_ends=dict(type='bool', default=True), - tty=dict(type='bool', default=False), - env=dict(type='dict'), + container=dict(type="str", required=True), + argv=dict(type="list", elements="str"), + command=dict(type="str"), + chdir=dict(type="str"), + detach=dict(type="bool", default=False), + user=dict(type="str"), + stdin=dict(type="str"), + stdin_add_newline=dict(type="bool", default=True), + strip_empty_ends=dict(type="bool", default=True), + tty=dict(type="bool", default=False), + env=dict(type="dict"), ) option_minimal_versions = dict( - chdir=dict(docker_api_version='1.35'), + chdir=dict(docker_api_version="1.35"), ) client = AnsibleDockerClient( argument_spec=argument_spec, option_minimal_versions=option_minimal_versions, - mutually_exclusive=[('argv', 'command')], - required_one_of=[('argv', 'command')], + mutually_exclusive=[("argv", "command")], + required_one_of=[("argv", "command")], ) - container = client.module.params['container'] - argv = client.module.params['argv'] - command = client.module.params['command'] - chdir = client.module.params['chdir'] - detach = client.module.params['detach'] - user = client.module.params['user'] - stdin = client.module.params['stdin'] - strip_empty_ends = client.module.params['strip_empty_ends'] - tty = client.module.params['tty'] - env = client.module.params['env'] + container = client.module.params["container"] + argv = client.module.params["argv"] + command = client.module.params["command"] + chdir = client.module.params["chdir"] + detach = client.module.params["detach"] + user = client.module.params["user"] + stdin = client.module.params["stdin"] + strip_empty_ends = client.module.params["strip_empty_ends"] + tty = client.module.params["tty"] + env = client.module.params["env"] if env is not None: for name, value in list(env.items()): if not isinstance(value, str): client.module.fail_json( msg="Non-string value found for env option. Ambiguous env options must be " - f"wrapped in quotes to avoid them being interpreted. Key: {name}") - env[name] = to_text(value, errors='surrogate_or_strict') + f"wrapped in quotes to avoid them being interpreted. Key: {name}" + ) + env[name] = to_text(value, errors="surrogate_or_strict") if command is not None: argv = shlex.split(command) if detach and stdin is not None: - client.module.fail_json(msg='If detach=true, stdin cannot be provided.') + client.module.fail_json(msg="If detach=true, stdin cannot be provided.") - if stdin is not None and client.module.params['stdin_add_newline']: - stdin += '\n' + if stdin is not None and client.module.params["stdin_add_newline"]: + stdin += "\n" try: data = { - 'Container': container, - 'User': user or '', - 'Privileged': False, - 'Tty': False, - 'AttachStdin': bool(stdin), - 'AttachStdout': True, - 'AttachStderr': True, - 'Cmd': argv, - 'Env': format_environment(env) if env is not None else None, + "Container": container, + "User": user or "", + "Privileged": False, + "Tty": False, + "AttachStdin": bool(stdin), + "AttachStdout": True, + "AttachStderr": True, + "Cmd": argv, + "Env": format_environment(env) if env is not None else None, } if chdir is not None: - data['WorkingDir'] = chdir + data["WorkingDir"] = chdir - exec_data = client.post_json_to_json('/containers/{0}/exec', container, data=data) - exec_id = exec_data['Id'] + exec_data = client.post_json_to_json( + "/containers/{0}/exec", container, data=data + ) + exec_id = exec_data["Id"] data = { - 'Tty': tty, - 'Detach': detach, + "Tty": tty, + "Detach": detach, } if detach: - client.post_json_to_text('/exec/{0}/start', exec_id, data=data) + client.post_json_to_text("/exec/{0}/start", exec_id, data=data) client.module.exit_json(changed=True, exec_id=exec_id) else: if stdin and not detach: - exec_socket = client.post_json_to_stream_socket('/exec/{0}/start', exec_id, data=data) + exec_socket = client.post_json_to_stream_socket( + "/exec/{0}/start", exec_id, data=data + ) try: - with DockerSocketHandlerModule(exec_socket, client.module, selectors) as exec_socket_handler: + with DockerSocketHandlerModule( + exec_socket, client.module, selectors + ) as exec_socket_handler: if stdin: exec_socket_handler.write(to_bytes(stdin)) @@ -278,35 +284,49 @@ def main(): finally: exec_socket.close() else: - stdout, stderr = client.post_json_to_stream('/exec/{0}/start', exec_id, data=data, stream=False, tty=tty, demux=True) + stdout, stderr = client.post_json_to_stream( + "/exec/{0}/start", + exec_id, + data=data, + stream=False, + tty=tty, + demux=True, + ) - result = client.get_json('/exec/{0}/json', exec_id) + result = client.get_json("/exec/{0}/json", exec_id) - stdout = to_text(stdout or b'') - stderr = to_text(stderr or b'') + stdout = to_text(stdout or b"") + stderr = to_text(stderr or b"") if strip_empty_ends: - stdout = stdout.rstrip('\r\n') - stderr = stderr.rstrip('\r\n') + stdout = stdout.rstrip("\r\n") + stderr = stderr.rstrip("\r\n") client.module.exit_json( changed=True, stdout=stdout, stderr=stderr, - rc=result.get('ExitCode') or 0, + rc=result.get("ExitCode") or 0, ) except NotFound: client.fail(f'Could not find container "{container}"') except APIError as e: if e.response is not None and e.response.status_code == 409: client.fail(f'The container "{container}" has been paused ({e})') - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_container_info.py b/plugins/modules/docker_container_info.py index 8b47b63d..ac9ef90c 100644 --- a/plugins/modules/docker_container_info.py +++ b/plugins/modules/docker_container_info.py @@ -77,16 +77,18 @@ container: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException def main(): argument_spec = dict( - name=dict(type='str', required=True), + name=dict(type="str", required=True), ) client = AnsibleDockerClient( @@ -95,7 +97,7 @@ def main(): ) try: - container = client.get_container(client.module.params['name']) + container = client.get_container(client.module.params["name"]) client.module.exit_json( changed=False, @@ -103,12 +105,16 @@ def main(): container=container, ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_context_info.py b/plugins/modules/docker_context_info.py index 5c504167..60585afe 100644 --- a/plugins/modules/docker_context_info.py +++ b/plugins/modules/docker_context_info.py @@ -176,7 +176,6 @@ import traceback from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_text - from ansible_collections.community.docker.plugins.module_utils._api.context.api import ( ContextAPI, ) @@ -196,10 +195,10 @@ def tls_context_to_json(context): if context is None: return None return { - 'client_cert': context.cert[0] if context.cert else None, - 'client_key': context.cert[1] if context.cert else None, - 'ca_cert': context.ca_cert, - 'verify': context.verify, + "client_cert": context.cert[0] if context.cert else None, + "client_key": context.cert[1] if context.cert else None, + "ca_cert": context.ca_cert, + "verify": context.verify, # 'ssl_version': context.ssl_version, -- this isn't used anymore } @@ -210,52 +209,52 @@ def to_bool(value): def context_to_json(context, current): module_config = {} - if 'docker' in context.endpoints: - endpoint = context.endpoints['docker'] - if isinstance(endpoint.get('Host'), str): - host_str = to_text(endpoint['Host']) + if "docker" in context.endpoints: + endpoint = context.endpoints["docker"] + if isinstance(endpoint.get("Host"), str): + host_str = to_text(endpoint["Host"]) # Adjust protocol name so that it works with the Docker CLI tool as well proto = None - idx = host_str.find('://') + idx = host_str.find("://") if idx >= 0: proto = host_str[:idx] - host_str = host_str[idx + 3:] - if proto in ('http', 'https'): - proto = 'tcp' - if proto == 'http+unix': - proto = 'unix' + host_str = host_str[idx + 3 :] + if proto in ("http", "https"): + proto = "tcp" + if proto == "http+unix": + proto = "unix" if proto: host_str = f"{proto}://{host_str}" # Create config for the modules - module_config['docker_host'] = host_str - if context.tls_cfg.get('docker'): - tls_cfg = context.tls_cfg['docker'] + module_config["docker_host"] = host_str + if context.tls_cfg.get("docker"): + tls_cfg = context.tls_cfg["docker"] if tls_cfg.ca_cert: - module_config['ca_path'] = tls_cfg.ca_cert + module_config["ca_path"] = tls_cfg.ca_cert if tls_cfg.cert: - module_config['client_cert'] = tls_cfg.cert[0] - module_config['client_key'] = tls_cfg.cert[1] - module_config['validate_certs'] = tls_cfg.verify - module_config['tls'] = True + module_config["client_cert"] = tls_cfg.cert[0] + module_config["client_key"] = tls_cfg.cert[1] + module_config["validate_certs"] = tls_cfg.verify + module_config["tls"] = True else: - module_config['tls'] = to_bool(endpoint.get('SkipTLSVerify')) + module_config["tls"] = to_bool(endpoint.get("SkipTLSVerify")) return { - 'current': current, - 'name': context.name, - 'description': context.description, - 'meta_path': None if context.meta_path is IN_MEMORY else context.meta_path, - 'tls_path': None if context.tls_path is IN_MEMORY else context.tls_path, - 'config': module_config, + "current": current, + "name": context.name, + "description": context.description, + "meta_path": None if context.meta_path is IN_MEMORY else context.meta_path, + "tls_path": None if context.tls_path is IN_MEMORY else context.tls_path, + "config": module_config, } def main(): argument_spec = dict( - only_current=dict(type='bool', default=False), - name=dict(type='str'), - cli_context=dict(type='str'), + only_current=dict(type="bool", default=False), + name=dict(type="str"), + cli_context=dict(type="str"), ) module = AnsibleModule( @@ -267,15 +266,22 @@ def main(): ) try: - if module.params['cli_context']: - current_context_name, current_context_source = module.params['cli_context'], "cli_context module option" + if module.params["cli_context"]: + current_context_name, current_context_source = ( + module.params["cli_context"], + "cli_context module option", + ) else: - current_context_name, current_context_source = get_current_context_name_with_source() - if module.params['name']: - contexts = [ContextAPI.get_context(module.params['name'])] + current_context_name, current_context_source = ( + get_current_context_name_with_source() + ) + if module.params["name"]: + contexts = [ContextAPI.get_context(module.params["name"])] if not contexts[0]: - module.fail_json(msg=f"There is no context of name {module.params['name']!r}") - elif module.params['only_current']: + module.fail_json( + msg=f"There is no context of name {module.params['name']!r}" + ) + elif module.params["only_current"]: contexts = [ContextAPI.get_context(current_context_name)] if not contexts[0]: module.fail_json( @@ -284,10 +290,13 @@ def main(): else: contexts = ContextAPI.contexts() - json_contexts = sorted([ - context_to_json(context, context.name == current_context_name) - for context in contexts - ], key=lambda entry: entry['name']) + json_contexts = sorted( + [ + context_to_json(context, context.name == current_context_name) + for context in contexts + ], + key=lambda entry: entry["name"], + ) module.exit_json( changed=False, @@ -295,10 +304,16 @@ def main(): current_context_name=current_context_name, ) except ContextException as e: - module.fail_json(msg=f'Error when handling Docker contexts: {e}', exception=traceback.format_exc()) + module.fail_json( + msg=f"Error when handling Docker contexts: {e}", + exception=traceback.format_exc(), + ) except DockerException as e: - module.fail_json(msg=f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + module.fail_json( + msg=f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_host_info.py b/plugins/modules/docker_host_info.py index 335d0f79..3c3b2ab2 100644 --- a/plugins/modules/docker_host_info.py +++ b/plugins/modules/docker_host_info.py @@ -213,17 +213,21 @@ disk_usage: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + convert_filters, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, clean_dict_booleans_for_docker_api, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException, APIError -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import convert_filters class DockerHostManager(DockerBaseClass): @@ -234,24 +238,28 @@ class DockerHostManager(DockerBaseClass): self.client = client self.results = results - self.verbose_output = self.client.module.params['verbose_output'] + self.verbose_output = self.client.module.params["verbose_output"] - listed_objects = ['volumes', 'networks', 'containers', 'images'] + listed_objects = ["volumes", "networks", "containers", "images"] - self.results['host_info'] = self.get_docker_host_info() + self.results["host_info"] = self.get_docker_host_info() # At this point we definitely know that we can talk to the Docker daemon - self.results['can_talk_to_docker'] = True - self.client.fail_results['can_talk_to_docker'] = True + self.results["can_talk_to_docker"] = True + self.client.fail_results["can_talk_to_docker"] = True - if self.client.module.params['disk_usage']: - self.results['disk_usage'] = self.get_docker_disk_usage_facts() + if self.client.module.params["disk_usage"]: + self.results["disk_usage"] = self.get_docker_disk_usage_facts() for docker_object in listed_objects: if self.client.module.params[docker_object]: returned_name = docker_object filter_name = docker_object + "_filters" - filters = clean_dict_booleans_for_docker_api(client.module.params.get(filter_name), True) - self.results[returned_name] = self.get_docker_items_list(docker_object, filters) + filters = clean_dict_booleans_for_docker_api( + client.module.params.get(filter_name), True + ) + self.results[returned_name] = self.get_docker_items_list( + docker_object, filters + ) def get_docker_host_info(self): try: @@ -264,7 +272,7 @@ class DockerHostManager(DockerBaseClass): if self.verbose_output: return self.client.df() else: - return dict(LayersSize=self.client.df()['LayersSize']) + return dict(LayersSize=self.client.df()["LayersSize"]) except APIError as exc: self.client.fail(f"Error inspecting docker host: {exc}") @@ -272,44 +280,52 @@ class DockerHostManager(DockerBaseClass): items = None items_list = [] - header_containers = ['Id', 'Image', 'Command', 'Created', 'Status', 'Ports', 'Names'] - header_volumes = ['Driver', 'Name'] - header_images = ['Id', 'RepoTags', 'Created', 'Size'] - header_networks = ['Id', 'Driver', 'Name', 'Scope'] + header_containers = [ + "Id", + "Image", + "Command", + "Created", + "Status", + "Ports", + "Names", + ] + header_volumes = ["Driver", "Name"] + header_images = ["Id", "RepoTags", "Created", "Size"] + header_networks = ["Id", "Driver", "Name", "Scope"] filter_arg = dict() if filters: - filter_arg['filters'] = filters + filter_arg["filters"] = filters try: - if docker_object == 'containers': + if docker_object == "containers": params = { - 'limit': -1, - 'all': 1 if self.client.module.params['containers_all'] else 0, - 'size': 0, - 'trunc_cmd': 0, - 'filters': convert_filters(filters) if filters else None, + "limit": -1, + "all": 1 if self.client.module.params["containers_all"] else 0, + "size": 0, + "trunc_cmd": 0, + "filters": convert_filters(filters) if filters else None, } items = self.client.get_json("/containers/json", params=params) - elif docker_object == 'networks': - params = { - 'filters': convert_filters(filters or {}) - } + elif docker_object == "networks": + params = {"filters": convert_filters(filters or {})} items = self.client.get_json("/networks", params=params) - elif docker_object == 'images': + elif docker_object == "images": params = { - 'only_ids': 0, - 'all': 0, - 'filters': convert_filters(filters) if filters else None, + "only_ids": 0, + "all": 0, + "filters": convert_filters(filters) if filters else None, } items = self.client.get_json("/images/json", params=params) - elif docker_object == 'volumes': + elif docker_object == "volumes": params = { - 'filters': convert_filters(filters) if filters else None, + "filters": convert_filters(filters) if filters else None, } - items = self.client.get_json('/volumes', params=params) - items = items['Volumes'] + items = self.client.get_json("/volumes", params=params) + items = items["Volumes"] except APIError as exc: - self.client.fail(f"Error inspecting docker host for object '{docker_object}': {exc}") + self.client.fail( + f"Error inspecting docker host for object '{docker_object}': {exc}" + ) if self.verbose_output: return items @@ -317,16 +333,16 @@ class DockerHostManager(DockerBaseClass): for item in items: item_record = dict() - if docker_object == 'containers': + if docker_object == "containers": for key in header_containers: item_record[key] = item.get(key) - elif docker_object == 'networks': + elif docker_object == "networks": for key in header_networks: item_record[key] = item.get(key) - elif docker_object == 'images': + elif docker_object == "images": for key in header_images: item_record[key] = item.get(key) - elif docker_object == 'volumes': + elif docker_object == "volumes": for key in header_volumes: item_record[key] = item.get(key) items_list.append(item_record) @@ -336,17 +352,17 @@ class DockerHostManager(DockerBaseClass): def main(): argument_spec = dict( - containers=dict(type='bool', default=False), - containers_all=dict(type='bool', default=False), - containers_filters=dict(type='dict'), - images=dict(type='bool', default=False), - images_filters=dict(type='dict'), - networks=dict(type='bool', default=False), - networks_filters=dict(type='dict'), - volumes=dict(type='bool', default=False), - volumes_filters=dict(type='dict'), - disk_usage=dict(type='bool', default=False), - verbose_output=dict(type='bool', default=False), + containers=dict(type="bool", default=False), + containers_all=dict(type="bool", default=False), + containers_filters=dict(type="dict"), + images=dict(type="bool", default=False), + images_filters=dict(type="dict"), + networks=dict(type="bool", default=False), + networks_filters=dict(type="dict"), + volumes=dict(type="bool", default=False), + volumes_filters=dict(type="dict"), + disk_usage=dict(type="bool", default=False), + verbose_output=dict(type="bool", default=False), ) client = AnsibleDockerClient( @@ -356,9 +372,12 @@ def main(): can_talk_to_docker=False, ), ) - if client.module.params['api_version'] is None or client.module.params['api_version'].lower() == 'auto': + if ( + client.module.params["api_version"] is None + or client.module.params["api_version"].lower() == "auto" + ): # At this point we know that we can talk to Docker, since we asked it for the API version - client.fail_results['can_talk_to_docker'] = True + client.fail_results["can_talk_to_docker"] = True try: results = dict( @@ -368,12 +387,16 @@ def main(): DockerHostManager(client, results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image.py b/plugins/modules/docker_image.py index 2ff36dd7..8d478213 100644 --- a/plugins/modules/docker_image.py +++ b/plugins/modules/docker_image.py @@ -370,35 +370,18 @@ import traceback from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.formatters import human_to_bytes - -from ansible_collections.community.docker.plugins.module_utils.common_api import ( - AnsibleDockerClient, - RequestException, -) - -from ansible_collections.community.docker.plugins.module_utils.image_archive import ( - archived_image_manifest, - api_image_id, - ImageArchiveInvalidException, -) - -from ansible_collections.community.docker.plugins.module_utils.util import ( - clean_dict_booleans_for_docker_api, - DockerBaseClass, - is_image_name_id, - is_valid_tag, -) -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - from ansible_collections.community.docker.plugins.module_utils._api.auth import ( get_config_header, resolve_repository_name, ) from ansible_collections.community.docker.plugins.module_utils._api.constants import ( - DEFAULT_DATA_CHUNK_SIZE, CONTAINER_LIMITS_KEYS, + DEFAULT_DATA_CHUNK_SIZE, +) +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, + NotFound, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException, NotFound from ansible_collections.community.docker.plugins.module_utils._api.utils.build import ( process_dockerfile, tar, @@ -407,30 +390,48 @@ from ansible_collections.community.docker.plugins.module_utils._api.utils.utils format_extra_hosts, parse_repository_tag, ) +from ansible_collections.community.docker.plugins.module_utils.common_api import ( + AnsibleDockerClient, + RequestException, +) +from ansible_collections.community.docker.plugins.module_utils.image_archive import ( + ImageArchiveInvalidException, + api_image_id, + archived_image_manifest, +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + DockerBaseClass, + clean_dict_booleans_for_docker_api, + is_image_name_id, + is_valid_tag, +) +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) def convert_to_bytes(value, module, name, unlimited_value=None): if value is None: return value try: - if unlimited_value is not None and value in ('unlimited', str(unlimited_value)): + if unlimited_value is not None and value in ("unlimited", str(unlimited_value)): return unlimited_value return human_to_bytes(value) except ValueError as exc: - module.fail_json(msg=f'Failed to convert {name} to bytes: {exc}') + module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}") class ImageManager(DockerBaseClass): def __init__(self, client, results): - ''' + """ Configure a docker_image task. :param client: Ansible Docker Client wrapper over Docker client :type client: AnsibleDockerClient :param results: This task adds its output values to this dictionary :type results: dict - ''' + """ super(ImageManager, self).__init__() @@ -439,42 +440,51 @@ class ImageManager(DockerBaseClass): parameters = self.client.module.params self.check_mode = self.client.check_mode - self.source = parameters['source'] - build = parameters['build'] or dict() - pull = parameters['pull'] or dict() - self.archive_path = parameters['archive_path'] - self.cache_from = build.get('cache_from') - self.container_limits = build.get('container_limits') - if self.container_limits and 'memory' in self.container_limits: - self.container_limits['memory'] = convert_to_bytes( - self.container_limits['memory'], self.client.module, 'build.container_limits.memory') - if self.container_limits and 'memswap' in self.container_limits: - self.container_limits['memswap'] = convert_to_bytes( - self.container_limits['memswap'], self.client.module, 'build.container_limits.memswap', unlimited_value=-1) - self.dockerfile = build.get('dockerfile') - self.force_source = parameters['force_source'] - self.force_absent = parameters['force_absent'] - self.force_tag = parameters['force_tag'] - self.load_path = parameters['load_path'] - self.name = parameters['name'] - self.network = build.get('network') - self.extra_hosts = clean_dict_booleans_for_docker_api(build.get('etc_hosts')) - self.nocache = build.get('nocache', False) - self.build_path = build.get('path') - self.pull = build.get('pull') - self.target = build.get('target') - self.repository = parameters['repository'] - self.rm = build.get('rm', True) - self.state = parameters['state'] - self.tag = parameters['tag'] - self.http_timeout = build.get('http_timeout') - self.pull_platform = pull.get('platform') - self.push = parameters['push'] - self.buildargs = build.get('args') - self.build_platform = build.get('platform') - self.use_config_proxy = build.get('use_config_proxy') - self.shm_size = convert_to_bytes(build.get('shm_size'), self.client.module, 'build.shm_size') - self.labels = clean_dict_booleans_for_docker_api(build.get('labels')) + self.source = parameters["source"] + build = parameters["build"] or dict() + pull = parameters["pull"] or dict() + self.archive_path = parameters["archive_path"] + self.cache_from = build.get("cache_from") + self.container_limits = build.get("container_limits") + if self.container_limits and "memory" in self.container_limits: + self.container_limits["memory"] = convert_to_bytes( + self.container_limits["memory"], + self.client.module, + "build.container_limits.memory", + ) + if self.container_limits and "memswap" in self.container_limits: + self.container_limits["memswap"] = convert_to_bytes( + self.container_limits["memswap"], + self.client.module, + "build.container_limits.memswap", + unlimited_value=-1, + ) + self.dockerfile = build.get("dockerfile") + self.force_source = parameters["force_source"] + self.force_absent = parameters["force_absent"] + self.force_tag = parameters["force_tag"] + self.load_path = parameters["load_path"] + self.name = parameters["name"] + self.network = build.get("network") + self.extra_hosts = clean_dict_booleans_for_docker_api(build.get("etc_hosts")) + self.nocache = build.get("nocache", False) + self.build_path = build.get("path") + self.pull = build.get("pull") + self.target = build.get("target") + self.repository = parameters["repository"] + self.rm = build.get("rm", True) + self.state = parameters["state"] + self.tag = parameters["tag"] + self.http_timeout = build.get("http_timeout") + self.pull_platform = pull.get("platform") + self.push = parameters["push"] + self.buildargs = build.get("args") + self.build_platform = build.get("platform") + self.use_config_proxy = build.get("use_config_proxy") + self.shm_size = convert_to_bytes( + build.get("shm_size"), self.client.module, "build.shm_size" + ) + self.labels = clean_dict_booleans_for_docker_api(build.get("labels")) # If name contains a tag, it takes precedence over tag parameter. if not is_image_name_id(self.name): @@ -487,75 +497,95 @@ class ImageManager(DockerBaseClass): if self.repository and is_image_name_id(self.repository): self.fail(f"`repository` must not be an image ID; got: {self.repository}") if not self.repository and self.push and is_image_name_id(self.name): - self.fail(f"Cannot push an image by ID; specify `repository` to tag and push the image with ID {self.name} instead") + self.fail( + f"Cannot push an image by ID; specify `repository` to tag and push the image with ID {self.name} instead" + ) - if self.state == 'present': + if self.state == "present": self.present() - elif self.state == 'absent': + elif self.state == "absent": self.absent() def fail(self, msg): self.client.fail(msg) def present(self): - ''' + """ Handles state = 'present', which includes building, loading or pulling an image, depending on user provided parameters. :returns None - ''' + """ if is_image_name_id(self.name): image = self.client.find_image_by_id(self.name, accept_missing_image=True) else: image = self.client.find_image(name=self.name, tag=self.tag) if not image or self.force_source: - if self.source == 'build': + if self.source == "build": if is_image_name_id(self.name): - self.fail(f"Image name must not be an image ID for source=build; got: {self.name}") + self.fail( + f"Image name must not be an image ID for source=build; got: {self.name}" + ) # Build the image if not os.path.isdir(self.build_path): - self.fail(f"Requested build path {self.build_path} could not be found or you do not have access.") + self.fail( + f"Requested build path {self.build_path} could not be found or you do not have access." + ) image_name = self.name if self.tag: image_name = f"{self.name}:{self.tag}" self.log(f"Building image {image_name}") - self.results['actions'].append(f"Built image {image_name} from {self.build_path}") - self.results['changed'] = True + self.results["actions"].append( + f"Built image {image_name} from {self.build_path}" + ) + self.results["changed"] = True if not self.check_mode: self.results.update(self.build_image()) - elif self.source == 'load': + elif self.source == "load": # Load the image from an archive if not os.path.isfile(self.load_path): - self.fail(f"Error loading image {self.name}. Specified path {self.load_path} does not exist.") + self.fail( + f"Error loading image {self.name}. Specified path {self.load_path} does not exist." + ) image_name = self.name if self.tag and not is_image_name_id(image_name): image_name = f"{self.name}:{self.tag}" - self.results['actions'].append(f"Loaded image {image_name} from {self.load_path}") - self.results['changed'] = True + self.results["actions"].append( + f"Loaded image {image_name} from {self.load_path}" + ) + self.results["changed"] = True if not self.check_mode: - self.results['image'] = self.load_image() - elif self.source == 'pull': + self.results["image"] = self.load_image() + elif self.source == "pull": if is_image_name_id(self.name): - self.fail(f"Image name must not be an image ID for source=pull; got: {self.name}") + self.fail( + f"Image name must not be an image ID for source=pull; got: {self.name}" + ) # pull the image - self.results['actions'].append(f'Pulled image {self.name}:{self.tag}') - self.results['changed'] = True + self.results["actions"].append(f"Pulled image {self.name}:{self.tag}") + self.results["changed"] = True if not self.check_mode: - self.results['image'], dummy = self.client.pull_image(self.name, tag=self.tag, platform=self.pull_platform) - elif self.source == 'local': + self.results["image"], dummy = self.client.pull_image( + self.name, tag=self.tag, platform=self.pull_platform + ) + elif self.source == "local": if image is None: name = self.name if self.tag and not is_image_name_id(name): name = f"{self.name}:{self.tag}" - self.client.fail(f'Cannot find the image {name} locally.') - if not self.check_mode and image and image['Id'] == self.results['image']['Id']: - self.results['changed'] = False + self.client.fail(f"Cannot find the image {name} locally.") + if ( + not self.check_mode + and image + and image["Id"] == self.results["image"]["Id"] + ): + self.results["changed"] = False else: - self.results['image'] = image + self.results["image"] = image if self.archive_path: self.archive_image(self.name, self.tag) @@ -566,11 +596,11 @@ class ImageManager(DockerBaseClass): self.tag_image(self.name, self.tag, self.repository, push=self.push) def absent(self): - ''' + """ Handles state = 'absent', which removes an image. :return None - ''' + """ name = self.name if is_image_name_id(name): image = self.client.find_image_by_id(name, accept_missing_image=True) @@ -581,20 +611,24 @@ class ImageManager(DockerBaseClass): if image: if not self.check_mode: try: - self.client.delete_json('/images/{0}', name, params={'force': self.force_absent}) + self.client.delete_json( + "/images/{0}", name, params={"force": self.force_absent} + ) except NotFound: # If the image vanished while we were trying to remove it, do not fail pass except Exception as exc: self.fail(f"Error removing image {name} - {exc}") - self.results['changed'] = True - self.results['actions'].append(f"Removed image {name}") - self.results['image']['state'] = 'Deleted' + self.results["changed"] = True + self.results["actions"].append(f"Removed image {name}") + self.results["image"]["state"] = "Deleted" @staticmethod - def archived_image_action(failure_logger, archive_path, current_image_name, current_image_id): - ''' + def archived_image_action( + failure_logger, archive_path, current_image_name, current_image_id + ): + """ If the archive is missing or requires replacement, return an action message. :param failure_logger: a logging function that accepts one parameter of type str @@ -608,35 +642,40 @@ class ImageManager(DockerBaseClass): :returns: Either None, or an Ansible action message. :rtype: str - ''' + """ def build_msg(reason): - return f'Archived image {current_image_name} to {archive_path}, {reason}' + return f"Archived image {current_image_name} to {archive_path}, {reason}" try: archived = archived_image_manifest(archive_path) except ImageArchiveInvalidException as exc: - failure_logger(f'Unable to extract manifest summary from archive: {exc}') - return build_msg('overwriting an unreadable archive file') + failure_logger(f"Unable to extract manifest summary from archive: {exc}") + return build_msg("overwriting an unreadable archive file") if archived is None: - return build_msg('since none present') - elif current_image_id == api_image_id(archived.image_id) and [current_image_name] == archived.repo_tags: + return build_msg("since none present") + elif ( + current_image_id == api_image_id(archived.image_id) + and [current_image_name] == archived.repo_tags + ): return None else: - name = ', '.join(archived.repo_tags) + name = ", ".join(archived.repo_tags) - return build_msg(f'overwriting archive with image {archived.image_id} named {name}') + return build_msg( + f"overwriting archive with image {archived.image_id} named {name}" + ) def archive_image(self, name, tag): - ''' + """ Archive an image to a .tar file. Called when archive_path is passed. :param name: Name/repository of the image :type name: str :param tag: Optional image tag; assumed to be "latest" if None :type tag: str | None - ''' + """ if not tag: tag = "latest" @@ -653,20 +692,24 @@ class ImageManager(DockerBaseClass): return # Will have a 'sha256:' prefix - image_id = image['Id'] + image_id = image["Id"] - action = self.archived_image_action(self.client.module.debug, self.archive_path, image_name, image_id) + action = self.archived_image_action( + self.client.module.debug, self.archive_path, image_name, image_id + ) if action: - self.results['actions'].append(action) + self.results["actions"].append(action) - self.results['changed'] = action is not None + self.results["changed"] = action is not None - if (not self.check_mode) and self.results['changed']: + if (not self.check_mode) and self.results["changed"]: self.log(f"Getting archive of image {image_name}") try: saved_image = self.client._stream_raw_result( - self.client._get(self.client._url('/images/{0}/get', image_name), stream=True), + self.client._get( + self.client._url("/images/{0}/get", image_name), stream=True + ), DEFAULT_DATA_CHUNK_SIZE, False, ) @@ -674,22 +717,22 @@ class ImageManager(DockerBaseClass): self.fail(f"Error getting image {image_name} - {exc}") try: - with open(self.archive_path, 'wb') as fd: + with open(self.archive_path, "wb") as fd: for chunk in saved_image: fd.write(chunk) except Exception as exc: self.fail(f"Error writing image archive {self.archive_path} - {exc}") - self.results['image'] = image + self.results["image"] = image def push_image(self, name, tag=None): - ''' + """ If the name of the image contains a repository path, then push the image. :param name Name of the image to push. :param tag Use a specific tag. :return: None - ''' + """ if is_image_name_id(name): self.fail(f"Cannot push an image ID: {name}") @@ -702,8 +745,10 @@ class ImageManager(DockerBaseClass): self.log(f"push {self.name} to {registry}/{repo_name}:{tag}") if registry: - self.results['actions'].append(f"Pushed image {self.name} to {registry}/{repo_name}:{tag}") - self.results['changed'] = True + self.results["actions"].append( + f"Pushed image {self.name} to {registry}/{repo_name}:{tag}" + ) + self.results["changed"] = True if not self.check_mode: status = None try: @@ -711,7 +756,9 @@ class ImageManager(DockerBaseClass): push_repository, push_tag = repository, tag if not push_tag: - push_repository, push_tag = parse_repository_tag(push_repository) + push_repository, push_tag = parse_repository_tag( + push_repository + ) push_registry, dummy = resolve_repository_name(push_repository) headers = {} header = get_config_header(self.client, push_registry) @@ -719,37 +766,41 @@ class ImageManager(DockerBaseClass): # For some reason, from Docker 28.3.3 on not specifying X-Registry-Auth seems to be invalid. # See https://github.com/moby/moby/issues/50614. header = base64.urlsafe_b64encode(b"{}") - headers['X-Registry-Auth'] = header + headers["X-Registry-Auth"] = header response = self.client._post_json( self.client._url("/images/{0}/push", push_repository), data=None, headers=headers, stream=True, - params={'tag': push_tag}, + params={"tag": push_tag}, ) self.client._raise_for_status(response) for line in self.client._stream_helper(response, decode=True): self.log(line, pretty_print=True) - if line.get('errorDetail'): - raise Exception(line['errorDetail']['message']) - status = line.get('status') - if status == 'Pushing': + if line.get("errorDetail"): + raise Exception(line["errorDetail"]["message"]) + status = line.get("status") + if status == "Pushing": changed = True - self.results['changed'] = changed + self.results["changed"] = changed except Exception as exc: - if 'unauthorized' in str(exc): - if 'authentication required' in str(exc): - self.fail(f"Error pushing image {registry}/{repo_name}:{tag} - {exc}. Try logging into {registry} first.") + if "unauthorized" in str(exc): + if "authentication required" in str(exc): + self.fail( + f"Error pushing image {registry}/{repo_name}:{tag} - {exc}. Try logging into {registry} first." + ) else: - self.fail(f"Error pushing image {registry}/{repo_name}:{tag} - {exc}. Does the repository exist?") + self.fail( + f"Error pushing image {registry}/{repo_name}:{tag} - {exc}. Does the repository exist?" + ) self.fail(f"Error pushing image {repository}: {exc}") - self.results['image'] = self.client.find_image(name=repository, tag=tag) - if not self.results['image']: - self.results['image'] = dict() - self.results['image']['push_status'] = status + self.results["image"] = self.client.find_image(name=repository, tag=tag) + if not self.results["image"]: + self.results["image"] = dict() + self.results["image"]["push_status"] = status def tag_image(self, name, tag, repository, push=False): - ''' + """ Tag an image into a repository. :param name: name of the image. required. @@ -757,62 +808,66 @@ class ImageManager(DockerBaseClass): :param repository: path to the repository. required. :param push: bool. push the image once it is tagged. :return: None - ''' + """ repo, repo_tag = parse_repository_tag(repository) if not repo_tag: repo_tag = "latest" if tag: repo_tag = tag image = self.client.find_image(name=repo, tag=repo_tag) - found = 'found' if image else 'not found' + found = "found" if image else "not found" self.log(f"image {repo} was {found}") if not image or self.force_tag: image_name = name - if not is_image_name_id(name) and tag and not name.endswith(':' + tag): + if not is_image_name_id(name) and tag and not name.endswith(":" + tag): image_name = f"{name}:{tag}" self.log(f"tagging {image_name} to {repo}:{repo_tag}") - self.results['changed'] = True - self.results['actions'].append(f"Tagged image {image_name} to {repo}:{repo_tag}") + self.results["changed"] = True + self.results["actions"].append( + f"Tagged image {image_name} to {repo}:{repo_tag}" + ) if not self.check_mode: try: # Finding the image does not always work, especially running a localhost registry. In those # cases, if we do not set force=True, it errors. params = { - 'tag': repo_tag, - 'repo': repo, - 'force': True, + "tag": repo_tag, + "repo": repo, + "force": True, } - res = self.client._post(self.client._url('/images/{0}/tag', image_name), params=params) + res = self.client._post( + self.client._url("/images/{0}/tag", image_name), params=params + ) self.client._raise_for_status(res) if res.status_code != 201: raise Exception("Tag operation failed.") except Exception as exc: self.fail(f"Error: failed to tag image - {exc}") - self.results['image'] = self.client.find_image(name=repo, tag=repo_tag) - if image and image['Id'] == self.results['image']['Id']: - self.results['changed'] = False + self.results["image"] = self.client.find_image(name=repo, tag=repo_tag) + if image and image["Id"] == self.results["image"]["Id"]: + self.results["changed"] = False if push: self.push_image(repo, repo_tag) @staticmethod def _extract_output_line(line, output): - ''' + """ Extract text line from stream output and, if found, adds it to output. - ''' - if 'stream' in line or 'status' in line: + """ + if "stream" in line or "status" in line: # Make sure we have a string (assuming that line['stream'] and # line['status'] are either not defined, falsish, or a string) - text_line = line.get('stream') or line.get('status') or '' + text_line = line.get("stream") or line.get("status") or "" output.extend(text_line.splitlines()) def build_image(self): - ''' + """ Build an image :return: image dict - ''' + """ remote = context = None headers = {} buildargs = {} @@ -823,34 +878,40 @@ class ImageManager(DockerBaseClass): container_limits = self.container_limits or {} for key in container_limits.keys(): if key not in CONTAINER_LIMITS_KEYS: - raise DockerException(f'Invalid container_limits key {key}') + raise DockerException(f"Invalid container_limits key {key}") dockerfile = self.dockerfile - if self.build_path.startswith(('http://', 'https://', 'git://', 'github.com/', 'git@')): + if self.build_path.startswith( + ("http://", "https://", "git://", "github.com/", "git@") + ): remote = self.build_path elif not os.path.isdir(self.build_path): raise TypeError("You must specify a directory to build in path") else: - dockerignore = os.path.join(self.build_path, '.dockerignore') + dockerignore = os.path.join(self.build_path, ".dockerignore") exclude = None if os.path.exists(dockerignore): with open(dockerignore) as f: - exclude = list(filter( - lambda x: x != '' and x[0] != '#', - [line.strip() for line in f.read().splitlines()] - )) + exclude = list( + filter( + lambda x: x != "" and x[0] != "#", + [line.strip() for line in f.read().splitlines()], + ) + ) dockerfile = process_dockerfile(dockerfile, self.build_path) - context = tar(self.build_path, exclude=exclude, dockerfile=dockerfile, gzip=False) + context = tar( + self.build_path, exclude=exclude, dockerfile=dockerfile, gzip=False + ) params = { - 't': f"{self.name}:{self.tag}" if self.tag else self.name, - 'remote': remote, - 'q': False, - 'nocache': self.nocache, - 'rm': self.rm, - 'forcerm': self.rm, - 'pull': self.pull, - 'dockerfile': dockerfile, + "t": f"{self.name}:{self.tag}" if self.tag else self.name, + "remote": remote, + "q": False, + "nocache": self.nocache, + "rm": self.rm, + "forcerm": self.rm, + "pull": self.pull, + "dockerfile": dockerfile, } params.update(container_limits) @@ -859,36 +920,36 @@ class ImageManager(DockerBaseClass): for k, v in proxy_args.items(): buildargs.setdefault(k, v) if buildargs: - params.update({'buildargs': json.dumps(buildargs)}) + params.update({"buildargs": json.dumps(buildargs)}) if self.cache_from: - params.update({'cachefrom': json.dumps(self.cache_from)}) + params.update({"cachefrom": json.dumps(self.cache_from)}) if self.target: - params.update({'target': self.target}) + params.update({"target": self.target}) if self.network: - params.update({'networkmode': self.network}) + params.update({"networkmode": self.network}) if self.extra_hosts is not None: - params.update({'extrahosts': format_extra_hosts(self.extra_hosts)}) + params.update({"extrahosts": format_extra_hosts(self.extra_hosts)}) if self.build_platform is not None: - params['platform'] = self.build_platform + params["platform"] = self.build_platform if self.shm_size is not None: - params['shmsize'] = self.shm_size + params["shmsize"] = self.shm_size if self.labels: - params['labels'] = json.dumps(self.labels) + params["labels"] = json.dumps(self.labels) if context is not None: - headers['Content-Type'] = 'application/tar' + headers["Content-Type"] = "application/tar" self.client._set_auth_headers(headers) response = self.client._post( - self.client._url('/build'), + self.client._url("/build"), data=context, params=params, headers=headers, @@ -905,13 +966,16 @@ class ImageManager(DockerBaseClass): self.log(line, pretty_print=True) self._extract_output_line(line, build_output) - if line.get('error'): - if line.get('errorDetail'): - errorDetail = line.get('errorDetail') + if line.get("error"): + if line.get("errorDetail"): + errorDetail = line.get("errorDetail") self.fail( - f"Error building {self.name} - code: {errorDetail.get('code')}, message: {errorDetail.get('message')}, logs: {build_output}") + f"Error building {self.name} - code: {errorDetail.get('code')}, message: {errorDetail.get('message')}, logs: {build_output}" + ) else: - self.fail(f"Error building {self.name} - message: {line.get('error')}, logs: {build_output}") + self.fail( + f"Error building {self.name} - message: {line.get('error')}, logs: {build_output}" + ) return { "stdout": "\n".join(build_output), @@ -919,20 +983,22 @@ class ImageManager(DockerBaseClass): } def load_image(self): - ''' + """ Load an image from a .tar archive :return: image dict - ''' + """ # Load image(s) from file load_output = [] has_output = False try: self.log(f"Opening image {self.load_path}") - with open(self.load_path, 'rb') as image_tar: + with open(self.load_path, "rb") as image_tar: self.log(f"Loading image from {self.load_path}") - res = self.client._post(self.client._url("/images/load"), data=image_tar, stream=True) - if LooseVersion(self.client.api_version) >= LooseVersion('1.23'): + res = self.client._post( + self.client._url("/images/load"), data=image_tar, stream=True + ) + if LooseVersion(self.client.api_version) >= LooseVersion("1.23"): has_output = True for line in self.client._stream_helper(res, decode=True): self.log(line, pretty_print=True) @@ -940,17 +1006,23 @@ class ImageManager(DockerBaseClass): else: self.client._raise_for_status(res) self.client.module.warn( - 'The API version of your Docker daemon is < 1.23, which does not return the image' - ' loading result from the Docker daemon. Therefore, we cannot verify whether the' - ' expected image was loaded, whether multiple images where loaded, or whether the load' - ' actually succeeded. You should consider upgrading your Docker daemon.' + "The API version of your Docker daemon is < 1.23, which does not return the image" + " loading result from the Docker daemon. Therefore, we cannot verify whether the" + " expected image was loaded, whether multiple images where loaded, or whether the load" + " actually succeeded. You should consider upgrading your Docker daemon." ) except EnvironmentError as exc: if exc.errno == errno.ENOENT: self.client.fail(f"Error opening image {self.load_path} - {exc}") - self.client.fail(f"Error loading image {self.name} - {exc}", stdout='\n'.join(load_output)) + self.client.fail( + f"Error loading image {self.name} - {exc}", + stdout="\n".join(load_output), + ) except Exception as exc: - self.client.fail(f"Error loading image {self.name} - {exc}", stdout='\n'.join(load_output)) + self.client.fail( + f"Error loading image {self.name} - {exc}", + stdout="\n".join(load_output), + ) # Collect loaded images if has_output: @@ -958,31 +1030,48 @@ class ImageManager(DockerBaseClass): loaded_images = set() loaded_image_ids = set() for line in load_output: - if line.startswith('Loaded image:'): - loaded_images.add(line[len('Loaded image:'):].strip()) - if line.startswith('Loaded image ID:'): - loaded_image_ids.add(line[len('Loaded image ID:'):].strip().lower()) + if line.startswith("Loaded image:"): + loaded_images.add(line[len("Loaded image:") :].strip()) + if line.startswith("Loaded image ID:"): + loaded_image_ids.add( + line[len("Loaded image ID:") :].strip().lower() + ) if not loaded_images and not loaded_image_ids: - self.client.fail("Detected no loaded images. Archive potentially corrupt?", stdout='\n'.join(load_output)) + self.client.fail( + "Detected no loaded images. Archive potentially corrupt?", + stdout="\n".join(load_output), + ) if is_image_name_id(self.name): expected_image = self.name.lower() found_image = expected_image not in loaded_image_ids else: - expected_image = f'{self.name}:{self.tag}' + expected_image = f"{self.name}:{self.tag}" found_image = expected_image not in loaded_images if found_image: - found_instead = ', '.join(sorted([f"'{image}'" for image in loaded_images] + list(loaded_image_ids))) + found_instead = ", ".join( + sorted( + [f"'{image}'" for image in loaded_images] + + list(loaded_image_ids) + ) + ) self.client.fail( f"The archive did not contain image '{expected_image}'. Instead, found {found_instead}.", - stdout='\n'.join(load_output)) + stdout="\n".join(load_output), + ) loaded_images.remove(expected_image) if loaded_images: - found_more = ', '.join(sorted([f"'{image}'" for image in loaded_images] + list(loaded_image_ids))) + found_more = ", ".join( + sorted( + [f"'{image}'" for image in loaded_images] + + list(loaded_image_ids) + ) + ) self.client.module.warn( - f"The archive contained more images than specified: {found_more}") + f"The archive contained more images than specified: {found_more}" + ) if is_image_name_id(self.name): return self.client.find_image_by_id(self.name, accept_missing_image=True) @@ -992,64 +1081,87 @@ class ImageManager(DockerBaseClass): def main(): argument_spec = dict( - source=dict(type='str', choices=['build', 'load', 'pull', 'local']), - build=dict(type='dict', options=dict( - cache_from=dict(type='list', elements='str'), - container_limits=dict(type='dict', options=dict( - memory=dict(type='str'), - memswap=dict(type='str'), - cpushares=dict(type='int'), - cpusetcpus=dict(type='str'), - )), - dockerfile=dict(type='str'), - http_timeout=dict(type='int'), - network=dict(type='str'), - nocache=dict(type='bool', default=False), - path=dict(type='path', required=True), - pull=dict(type='bool', default=False), - rm=dict(type='bool', default=True), - args=dict(type='dict'), - use_config_proxy=dict(type='bool'), - target=dict(type='str'), - etc_hosts=dict(type='dict'), - platform=dict(type='str'), - shm_size=dict(type='str'), - labels=dict(type='dict'), - )), - archive_path=dict(type='path'), - force_source=dict(type='bool', default=False), - force_absent=dict(type='bool', default=False), - force_tag=dict(type='bool', default=False), - load_path=dict(type='path'), - name=dict(type='str', required=True), - pull=dict(type='dict', options=dict( - platform=dict(type='str'), - )), - push=dict(type='bool', default=False), - repository=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present']), - tag=dict(type='str', default='latest'), + source=dict(type="str", choices=["build", "load", "pull", "local"]), + build=dict( + type="dict", + options=dict( + cache_from=dict(type="list", elements="str"), + container_limits=dict( + type="dict", + options=dict( + memory=dict(type="str"), + memswap=dict(type="str"), + cpushares=dict(type="int"), + cpusetcpus=dict(type="str"), + ), + ), + dockerfile=dict(type="str"), + http_timeout=dict(type="int"), + network=dict(type="str"), + nocache=dict(type="bool", default=False), + path=dict(type="path", required=True), + pull=dict(type="bool", default=False), + rm=dict(type="bool", default=True), + args=dict(type="dict"), + use_config_proxy=dict(type="bool"), + target=dict(type="str"), + etc_hosts=dict(type="dict"), + platform=dict(type="str"), + shm_size=dict(type="str"), + labels=dict(type="dict"), + ), + ), + archive_path=dict(type="path"), + force_source=dict(type="bool", default=False), + force_absent=dict(type="bool", default=False), + force_tag=dict(type="bool", default=False), + load_path=dict(type="path"), + name=dict(type="str", required=True), + pull=dict( + type="dict", + options=dict( + platform=dict(type="str"), + ), + ), + push=dict(type="bool", default=False), + repository=dict(type="str"), + state=dict(type="str", default="present", choices=["absent", "present"]), + tag=dict(type="str", default="latest"), ) required_if = [ - ('state', 'present', ['source']), - ('source', 'build', ['build']), - ('source', 'load', ['load_path']), + ("state", "present", ["source"]), + ("source", "build", ["build"]), + ("source", "load", ["load_path"]), ] def detect_etc_hosts(client): - return client.module.params['build'] and bool(client.module.params['build'].get('etc_hosts')) + return client.module.params["build"] and bool( + client.module.params["build"].get("etc_hosts") + ) def detect_build_platform(client): - return client.module.params['build'] and client.module.params['build'].get('platform') is not None + return ( + client.module.params["build"] + and client.module.params["build"].get("platform") is not None + ) def detect_pull_platform(client): - return client.module.params['pull'] and client.module.params['pull'].get('platform') is not None + return ( + client.module.params["pull"] + and client.module.params["pull"].get("platform") is not None + ) option_minimal_versions = dict() - option_minimal_versions["build.etc_hosts"] = dict(docker_api_version='1.27', detect_usage=detect_etc_hosts) - option_minimal_versions["build.platform"] = dict(docker_api_version='1.32', detect_usage=detect_build_platform) - option_minimal_versions["pull.platform"] = dict(docker_api_version='1.32', detect_usage=detect_pull_platform) + option_minimal_versions["build.etc_hosts"] = dict( + docker_api_version="1.27", detect_usage=detect_etc_hosts + ) + option_minimal_versions["build.platform"] = dict( + docker_api_version="1.32", detect_usage=detect_build_platform + ) + option_minimal_versions["pull.platform"] = dict( + docker_api_version="1.32", detect_usage=detect_pull_platform + ) client = AnsibleDockerClient( argument_spec=argument_spec, @@ -1058,29 +1170,33 @@ def main(): option_minimal_versions=option_minimal_versions, ) - if not is_valid_tag(client.module.params['tag'], allow_empty=True): + if not is_valid_tag(client.module.params["tag"], allow_empty=True): client.fail(f'"{client.module.params["tag"]}" is not a valid docker tag!') - if client.module.params['source'] == 'build': - if not client.module.params['build'] or not client.module.params['build'].get('path'): - client.fail('If "source" is set to "build", the "build.path" option must be specified.') + if client.module.params["source"] == "build": + if not client.module.params["build"] or not client.module.params["build"].get( + "path" + ): + client.fail( + 'If "source" is set to "build", the "build.path" option must be specified.' + ) try: - results = dict( - changed=False, - actions=[], - image={} - ) + results = dict(changed=False, actions=[], image={}) ImageManager(client, results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_build.py b/plugins/modules/docker_image_build.py index b54aaa1d..0ebd6fe3 100644 --- a/plugins/modules/docker_image_build.py +++ b/plugins/modules/docker_image_build.py @@ -285,23 +285,21 @@ import traceback from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.formatters import human_to_bytes - +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, clean_dict_booleans_for_docker_api, is_image_name_id, is_valid_tag, ) - -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, ) @@ -309,15 +307,15 @@ def convert_to_bytes(value, module, name, unlimited_value=None): if value is None: return value try: - if unlimited_value is not None and value in ('unlimited', str(unlimited_value)): + if unlimited_value is not None and value in ("unlimited", str(unlimited_value)): return unlimited_value return human_to_bytes(value) except ValueError as exc: - module.fail_json(msg=f'Failed to convert {name} to bytes: {exc}') + module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}") -def dict_to_list(dictionary, concat='='): - return [f'{k}{concat}{v}' for k, v in sorted(dictionary.items())] +def dict_to_list(dictionary, concat="="): + return [f"{k}{concat}{v}" for k, v in sorted(dictionary.items())] def _quote_csv(input): @@ -334,47 +332,59 @@ class ImageBuilder(DockerBaseClass): self.check_mode = self.client.check_mode parameters = self.client.module.params - self.cache_from = parameters['cache_from'] - self.pull = parameters['pull'] - self.network = parameters['network'] - self.nocache = parameters['nocache'] - self.etc_hosts = clean_dict_booleans_for_docker_api(parameters['etc_hosts']) - self.args = clean_dict_booleans_for_docker_api(parameters['args']) - self.target = parameters['target'] - self.platform = parameters['platform'] - self.shm_size = convert_to_bytes(parameters['shm_size'], self.client.module, 'shm_size') - self.labels = clean_dict_booleans_for_docker_api(parameters['labels']) - self.rebuild = parameters['rebuild'] - self.secrets = parameters['secrets'] - self.outputs = parameters['outputs'] + self.cache_from = parameters["cache_from"] + self.pull = parameters["pull"] + self.network = parameters["network"] + self.nocache = parameters["nocache"] + self.etc_hosts = clean_dict_booleans_for_docker_api(parameters["etc_hosts"]) + self.args = clean_dict_booleans_for_docker_api(parameters["args"]) + self.target = parameters["target"] + self.platform = parameters["platform"] + self.shm_size = convert_to_bytes( + parameters["shm_size"], self.client.module, "shm_size" + ) + self.labels = clean_dict_booleans_for_docker_api(parameters["labels"]) + self.rebuild = parameters["rebuild"] + self.secrets = parameters["secrets"] + self.outputs = parameters["outputs"] - buildx = self.client.get_client_plugin_info('buildx') + buildx = self.client.get_client_plugin_info("buildx") if buildx is None: - self.fail(f'Docker CLI {self.client.get_cli()} does not have the buildx plugin installed') - buildx_version = buildx['Version'].lstrip('v') + self.fail( + f"Docker CLI {self.client.get_cli()} does not have the buildx plugin installed" + ) + buildx_version = buildx["Version"].lstrip("v") if self.secrets: for secret in self.secrets: - if secret['type'] in ('env', 'value'): - if LooseVersion(buildx_version) < LooseVersion('0.6.0'): - self.fail(f'The Docker buildx plugin has version {buildx_version}, but 0.6.0 is needed for secrets of type=env and type=value') + if secret["type"] in ("env", "value"): + if LooseVersion(buildx_version) < LooseVersion("0.6.0"): + self.fail( + f"The Docker buildx plugin has version {buildx_version}, but 0.6.0 is needed for secrets of type=env and type=value" + ) if self.outputs and len(self.outputs) > 1: - if LooseVersion(buildx_version) < LooseVersion('0.13.0'): - self.fail(f'The Docker buildx plugin has version {buildx_version}, but 0.13.0 is needed to specify more than one output') + if LooseVersion(buildx_version) < LooseVersion("0.13.0"): + self.fail( + f"The Docker buildx plugin has version {buildx_version}, but 0.13.0 is needed to specify more than one output" + ) - self.path = parameters['path'] + self.path = parameters["path"] if not os.path.isdir(self.path): self.fail(f'"{self.path}" is not an existing directory') - self.dockerfile = parameters['dockerfile'] - if self.dockerfile and not os.path.isfile(os.path.join(self.path, self.dockerfile)): - self.fail(f'"{os.path.join(self.path, self.dockerfile)}" is not an existing file') + self.dockerfile = parameters["dockerfile"] + if self.dockerfile and not os.path.isfile( + os.path.join(self.path, self.dockerfile) + ): + self.fail( + f'"{os.path.join(self.path, self.dockerfile)}" is not an existing file' + ) - self.name = parameters['name'] - self.tag = parameters['tag'] + self.name = parameters["name"] + self.tag = parameters["tag"] if not is_valid_tag(self.tag, allow_empty=True): self.fail(f'"{self.tag}" is not a valid docker tag') if is_image_name_id(self.name): - self.fail('Image name must not be a digest') + self.fail("Image name must not be a digest") # If name contains a tag, it takes precedence over tag parameter. repo, repo_tag = parse_repository_tag(self.name) @@ -383,25 +393,27 @@ class ImageBuilder(DockerBaseClass): self.tag = repo_tag if is_image_name_id(self.tag): - self.fail('Image name must not contain a digest, but have a tag') + self.fail("Image name must not contain a digest, but have a tag") if self.outputs: found = False - name_tag = f'{self.name}:{self.tag}' + name_tag = f"{self.name}:{self.tag}" for output in self.outputs: - if output['type'] == 'image': - if not output['name']: + if output["type"] == "image": + if not output["name"]: # Since we no longer pass --tag if --output is provided, we need to set this manually - output['name'] = [name_tag] - if output['name'] and name_tag in output['name']: + output["name"] = [name_tag] + if output["name"] and name_tag in output["name"]: found = True if not found: - self.outputs.append({ - 'type': 'image', - 'name': [name_tag], - 'push': False, - }) - if LooseVersion(buildx_version) < LooseVersion('0.13.0'): + self.outputs.append( + { + "type": "image", + "name": [name_tag], + "push": False, + } + ) + if LooseVersion(buildx_version) < LooseVersion("0.13.0"): self.fail( f"The output does not include an image with name {name_tag}, and the Docker" f" buildx plugin has version {buildx_version} which only supports one output." @@ -417,78 +429,86 @@ class ImageBuilder(DockerBaseClass): def add_args(self, args): environ_update = {} if not self.outputs: - args.extend(['--tag', f'{self.name}:{self.tag}']) + args.extend(["--tag", f"{self.name}:{self.tag}"]) if self.dockerfile: - args.extend(['--file', os.path.join(self.path, self.dockerfile)]) + args.extend(["--file", os.path.join(self.path, self.dockerfile)]) if self.cache_from: - self.add_list_arg(args, '--cache-from', self.cache_from) + self.add_list_arg(args, "--cache-from", self.cache_from) if self.pull: - args.append('--pull') + args.append("--pull") if self.network: - args.extend(['--network', self.network]) + args.extend(["--network", self.network]) if self.nocache: - args.append('--no-cache') + args.append("--no-cache") if self.etc_hosts: - self.add_list_arg(args, '--add-host', dict_to_list(self.etc_hosts, ':')) + self.add_list_arg(args, "--add-host", dict_to_list(self.etc_hosts, ":")) if self.args: - self.add_list_arg(args, '--build-arg', dict_to_list(self.args)) + self.add_list_arg(args, "--build-arg", dict_to_list(self.args)) if self.target: - args.extend(['--target', self.target]) + args.extend(["--target", self.target]) if self.platform: for platform in self.platform: - args.extend(['--platform', platform]) + args.extend(["--platform", platform]) if self.shm_size: - args.extend(['--shm-size', str(self.shm_size)]) + args.extend(["--shm-size", str(self.shm_size)]) if self.labels: - self.add_list_arg(args, '--label', dict_to_list(self.labels)) + self.add_list_arg(args, "--label", dict_to_list(self.labels)) if self.secrets: random_prefix = None for index, secret in enumerate(self.secrets): - sid = secret['id'] - if secret['type'] == 'file': - src = secret['src'] - args.extend(['--secret', f'id={sid},type=file,src={src}']) - if secret['type'] == 'env': - env = secret['src'] - args.extend(['--secret', f'id={sid},type=env,env={env}']) - if secret['type'] == 'value': + sid = secret["id"] + if secret["type"] == "file": + src = secret["src"] + args.extend(["--secret", f"id={sid},type=file,src={src}"]) + if secret["type"] == "env": + env = secret["src"] + args.extend(["--secret", f"id={sid},type=env,env={env}"]) + if secret["type"] == "value": # We pass values on using environment variables. The user has been warned in the documentation # that they should only use this mechanism when being comfortable with it. if random_prefix is None: # Use /dev/urandom to generate some entropy to make the environment variable's name unguessable - random_prefix = base64.b64encode(os.urandom(16)).decode('utf-8').replace('=', '') - env_name = f'ANSIBLE_DOCKER_COMPOSE_ENV_SECRET_{random_prefix}_{index}' - environ_update[env_name] = secret['value'] - args.extend(['--secret', f'id={sid},type=env,env={env_name}']) + random_prefix = ( + base64.b64encode(os.urandom(16)) + .decode("utf-8") + .replace("=", "") + ) + env_name = ( + f"ANSIBLE_DOCKER_COMPOSE_ENV_SECRET_{random_prefix}_{index}" + ) + environ_update[env_name] = secret["value"] + args.extend(["--secret", f"id={sid},type=env,env={env_name}"]) if self.outputs: for output in self.outputs: subargs = [] - if output['type'] == 'local': - dest = output['dest'] - subargs.extend(['type=local', f'dest={dest}']) - if output['type'] == 'tar': - dest = output['dest'] - subargs.extend(['type=tar', f'dest={dest}']) - if output['type'] == 'oci': - dest = output['dest'] - subargs.extend(['type=oci', f'dest={dest}']) - if output['type'] == 'docker': - subargs.append('type=docker') - dest = output['dest'] - if output['dest'] is not None: - subargs.append(f'dest={dest}') - if output['context'] is not None: - context = output['context'] - subargs.append(f'context={context}') - if output['type'] == 'image': - subargs.append('type=image') - if output['name'] is not None: - name = ','.join(output['name']) - subargs.append(f'name={name}') - if output['push']: - subargs.append('push=true') + if output["type"] == "local": + dest = output["dest"] + subargs.extend(["type=local", f"dest={dest}"]) + if output["type"] == "tar": + dest = output["dest"] + subargs.extend(["type=tar", f"dest={dest}"]) + if output["type"] == "oci": + dest = output["dest"] + subargs.extend(["type=oci", f"dest={dest}"]) + if output["type"] == "docker": + subargs.append("type=docker") + dest = output["dest"] + if output["dest"] is not None: + subargs.append(f"dest={dest}") + if output["context"] is not None: + context = output["context"] + subargs.append(f"context={context}") + if output["type"] == "image": + subargs.append("type=image") + if output["name"] is not None: + name = ",".join(output["name"]) + subargs.append(f"name={name}") + if output["push"]: + subargs.append("push=true") if subargs: - args.extend(['--output', ','.join(_quote_csv(subarg) for subarg in subargs)]) + args.extend( + ["--output", ",".join(_quote_csv(subarg) for subarg in subargs)] + ) return environ_update def build_image(self): @@ -500,82 +520,93 @@ class ImageBuilder(DockerBaseClass): ) if image: - if self.rebuild == 'never': + if self.rebuild == "never": return results - results['changed'] = True + results["changed"] = True if not self.check_mode: - args = ['buildx', 'build', '--progress', 'plain'] + args = ["buildx", "build", "--progress", "plain"] environ_update = self.add_args(args) - args.extend(['--', self.path]) - rc, stdout, stderr = self.client.call_cli(*args, environ_update=environ_update) + args.extend(["--", self.path]) + rc, stdout, stderr = self.client.call_cli( + *args, environ_update=environ_update + ) if rc != 0: - self.fail(f'Building {self.name}:{self.tag} failed', stdout=to_native(stdout), stderr=to_native(stderr), command=args) - results['stdout'] = to_native(stdout) - results['stderr'] = to_native(stderr) - results['image'] = self.client.find_image(self.name, self.tag) or {} - results['command'] = args + self.fail( + f"Building {self.name}:{self.tag} failed", + stdout=to_native(stdout), + stderr=to_native(stderr), + command=args, + ) + results["stdout"] = to_native(stdout) + results["stderr"] = to_native(stderr) + results["image"] = self.client.find_image(self.name, self.tag) or {} + results["command"] = args return results def main(): argument_spec = dict( - name=dict(type='str', required=True), - tag=dict(type='str', default='latest'), - path=dict(type='path', required=True), - dockerfile=dict(type='str'), - cache_from=dict(type='list', elements='str'), - pull=dict(type='bool', default=False), - network=dict(type='str'), - nocache=dict(type='bool', default=False), - etc_hosts=dict(type='dict'), - args=dict(type='dict'), - target=dict(type='str'), - platform=dict(type='list', elements='str'), - shm_size=dict(type='str'), - labels=dict(type='dict'), - rebuild=dict(type='str', choices=['never', 'always'], default='never'), + name=dict(type="str", required=True), + tag=dict(type="str", default="latest"), + path=dict(type="path", required=True), + dockerfile=dict(type="str"), + cache_from=dict(type="list", elements="str"), + pull=dict(type="bool", default=False), + network=dict(type="str"), + nocache=dict(type="bool", default=False), + etc_hosts=dict(type="dict"), + args=dict(type="dict"), + target=dict(type="str"), + platform=dict(type="list", elements="str"), + shm_size=dict(type="str"), + labels=dict(type="dict"), + rebuild=dict(type="str", choices=["never", "always"], default="never"), secrets=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - id=dict(type='str', required=True), - type=dict(type='str', choices=['file', 'env', 'value'], required=True), - src=dict(type='path'), - env=dict(type='str'), - value=dict(type='str', no_log=True), + id=dict(type="str", required=True), + type=dict(type="str", choices=["file", "env", "value"], required=True), + src=dict(type="path"), + env=dict(type="str"), + value=dict(type="str", no_log=True), ), required_if=[ - ('type', 'file', ['src']), - ('type', 'env', ['env']), - ('type', 'value', ['value']), + ("type", "file", ["src"]), + ("type", "env", ["env"]), + ("type", "value", ["value"]), ], mutually_exclusive=[ - ('src', 'env', 'value'), + ("src", "env", "value"), ], no_log=False, ), outputs=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - type=dict(type='str', choices=['local', 'tar', 'oci', 'docker', 'image'], required=True), - dest=dict(type='path'), - context=dict(type='str'), - name=dict(type='list', elements='str'), - push=dict(type='bool', default=False), + type=dict( + type="str", + choices=["local", "tar", "oci", "docker", "image"], + required=True, + ), + dest=dict(type="path"), + context=dict(type="str"), + name=dict(type="list", elements="str"), + push=dict(type="bool", default=False), ), required_if=[ - ('type', 'local', ['dest']), - ('type', 'tar', ['dest']), - ('type', 'oci', ['dest']), + ("type", "local", ["dest"]), + ("type", "tar", ["dest"]), + ("type", "oci", ["dest"]), ], mutually_exclusive=[ - ('dest', 'name'), - ('dest', 'push'), - ('context', 'name'), - ('context', 'push'), + ("dest", "name"), + ("dest", "push"), + ("context", "name"), + ("context", "push"), ], ), ) @@ -590,8 +621,11 @@ def main(): results = ImageBuilder(client).build_image() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index 41e349ec..581959d0 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -95,29 +95,29 @@ images: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.constants import ( + DEFAULT_DATA_CHUNK_SIZE, +) +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.image_archive import ( - load_archived_image_manifest, - api_image_id, ImageArchiveInvalidException, + api_image_id, + load_archived_image_manifest, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, is_valid_tag, ) -from ansible_collections.community.docker.plugins.module_utils._api.constants import ( - DEFAULT_DATA_CHUNK_SIZE, -) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, -) class ImageExportManager(DockerBaseClass): @@ -128,47 +128,52 @@ class ImageExportManager(DockerBaseClass): parameters = self.client.module.params self.check_mode = self.client.check_mode - self.path = parameters['path'] - self.force = parameters['force'] - self.tag = parameters['tag'] + self.path = parameters["path"] + self.force = parameters["force"] + self.tag = parameters["tag"] if not is_valid_tag(self.tag, allow_empty=True): self.fail(f'"{self.tag}" is not a valid docker tag') # If name contains a tag, it takes precedence over tag parameter. self.names = [] - for name in parameters['names']: + for name in parameters["names"]: if is_image_name_id(name): - self.names.append({'id': name, 'joined': name}) + self.names.append({"id": name, "joined": name}) else: repo, repo_tag = parse_repository_tag(name) if not repo_tag: repo_tag = self.tag - self.names.append({'name': repo, 'tag': repo_tag, 'joined': f'{repo}:{repo_tag}'}) + self.names.append( + {"name": repo, "tag": repo_tag, "joined": f"{repo}:{repo_tag}"} + ) if not self.names: - self.fail('At least one image name must be specified') + self.fail("At least one image name must be specified") def fail(self, msg): self.client.fail(msg) def get_export_reason(self): if self.force: - return 'Exporting since force=true' + return "Exporting since force=true" try: archived_images = load_archived_image_manifest(self.path) if archived_images is None: - return 'Overwriting since no image is present in archive' + return "Overwriting since no image is present in archive" except ImageArchiveInvalidException as exc: - self.log(f'Unable to extract manifest summary from archive: {exc}') - return 'Overwriting an unreadable archive file' + self.log(f"Unable to extract manifest summary from archive: {exc}") + return "Overwriting an unreadable archive file" left_names = list(self.names) for archived_image in archived_images: found = False for i, name in enumerate(left_names): - if name['id'] == api_image_id(archived_image.image_id) and [name['joined']] == archived_image.repo_tags: + if ( + name["id"] == api_image_id(archived_image.image_id) + and [name["joined"]] == archived_image.repo_tags + ): del left_names[i] found = True break @@ -181,20 +186,22 @@ class ImageExportManager(DockerBaseClass): def write_chunks(self, chunks): try: - with open(self.path, 'wb') as fd: + with open(self.path, "wb") as fd: for chunk in chunks: fd.write(chunk) except Exception as exc: self.fail(f"Error writing image archive {self.path} - {exc}") def export_images(self): - image_names = [name['joined'] for name in self.names] - image_names_str = ', '.join(image_names) + image_names = [name["joined"] for name in self.names] + image_names_str = ", ".join(image_names) if len(image_names) == 1: self.log(f"Getting archive of image {image_names[0]}") try: chunks = self.client._stream_raw_result( - self.client._get(self.client._url('/images/{0}/get', image_names[0]), stream=True), + self.client._get( + self.client._url("/images/{0}/get", image_names[0]), stream=True + ), DEFAULT_DATA_CHUNK_SIZE, False, ) @@ -205,9 +212,9 @@ class ImageExportManager(DockerBaseClass): try: chunks = self.client._stream_raw_result( self.client._get( - self.client._url('/images/get'), + self.client._url("/images/get"), stream=True, - params={'names': image_names}, + params={"names": image_names}, ), DEFAULT_DATA_CHUNK_SIZE, False, @@ -224,26 +231,28 @@ class ImageExportManager(DockerBaseClass): images = [] for name in self.names: - if 'id' in name: - image = self.client.find_image_by_id(name['id'], accept_missing_image=True) + if "id" in name: + image = self.client.find_image_by_id( + name["id"], accept_missing_image=True + ) else: - image = self.client.find_image(name=name['name'], tag=name['tag']) + image = self.client.find_image(name=name["name"], tag=name["tag"]) if not image: self.fail(f"Image {name['joined']} not found") images.append(image) # Will have a 'sha256:' prefix - name['id'] = image['Id'] + name["id"] = image["Id"] results = { - 'changed': False, - 'images': images, + "changed": False, + "images": images, } reason = self.get_export_reason() if reason is not None: - results['msg'] = reason - results['changed'] = True + results["msg"] = reason + results["changed"] = True if not self.check_mode: self.export_images() @@ -253,10 +262,10 @@ class ImageExportManager(DockerBaseClass): def main(): argument_spec = dict( - path=dict(type='path'), - force=dict(type='bool', default=False), - names=dict(type='list', elements='str', required=True, aliases=['name']), - tag=dict(type='str', default='latest'), + path=dict(type="path"), + force=dict(type="bool", default=False), + names=dict(type="list", elements="str", required=True, aliases=["name"]), + tag=dict(type="str", default="latest"), ) client = AnsibleDockerClient( @@ -268,12 +277,16 @@ def main(): results = ImageExportManager(client).run() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_info.py b/plugins/modules/docker_image_info.py index 3fa8d72d..7247b9f1 100644 --- a/plugins/modules/docker_image_info.py +++ b/plugins/modules/docker_image_info.py @@ -137,6 +137,13 @@ images: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, + NotFound, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, @@ -145,8 +152,6 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException, NotFound -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import parse_repository_tag class ImageManager(DockerBaseClass): @@ -157,23 +162,23 @@ class ImageManager(DockerBaseClass): self.client = client self.results = results - self.name = self.client.module.params.get('name') + self.name = self.client.module.params.get("name") self.log(f"Gathering facts for images: {self.name}") if self.name: - self.results['images'] = self.get_facts() + self.results["images"] = self.get_facts() else: - self.results['images'] = self.get_all_images() + self.results["images"] = self.get_all_images() def fail(self, msg): self.client.fail(msg) def get_facts(self): - ''' + """ Lookup and inspect each image name found in the names parameter. :returns array of image dictionaries - ''' + """ results = [] @@ -183,13 +188,13 @@ class ImageManager(DockerBaseClass): for name in names: if is_image_name_id(name): - self.log(f'Fetching image {name} (ID)') + self.log(f"Fetching image {name} (ID)") image = self.client.find_image_by_id(name, accept_missing_image=True) else: repository, tag = parse_repository_tag(name) if not tag: - tag = 'latest' - self.log(f'Fetching image {repository}:{tag}') + tag = "latest" + self.log(f"Fetching image {repository}:{tag}") image = self.client.find_image(name=repository, tag=tag) if image: results.append(image) @@ -198,13 +203,13 @@ class ImageManager(DockerBaseClass): def get_all_images(self): results = [] params = { - 'only_ids': 0, - 'all': 0, + "only_ids": 0, + "all": 0, } images = self.client.get_json("/images/json", params=params) for image in images: try: - inspection = self.client.get_json('/images/{0}/json', image['Id']) + inspection = self.client.get_json("/images/{0}/json", image["Id"]) except NotFound: inspection = None except Exception as exc: @@ -215,7 +220,7 @@ class ImageManager(DockerBaseClass): def main(): argument_spec = dict( - name=dict(type='list', elements='str'), + name=dict(type="list", elements="str"), ) client = AnsibleDockerClient( @@ -224,20 +229,21 @@ def main(): ) try: - results = dict( - changed=False, - images=[] - ) + results = dict(changed=False, images=[]) ImageManager(client, results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_load.py b/plugins/modules/docker_image_load.py index 0ca6ea3d..fb89ca6e 100644 --- a/plugins/modules/docker_image_load.py +++ b/plugins/modules/docker_image_load.py @@ -82,6 +82,9 @@ images: import errno import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, @@ -91,8 +94,6 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( is_image_name_id, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException - class ImageManager(DockerBaseClass): def __init__(self, client, results): @@ -103,73 +104,86 @@ class ImageManager(DockerBaseClass): parameters = self.client.module.params self.check_mode = self.client.check_mode - self.path = parameters['path'] + self.path = parameters["path"] self.load_images() @staticmethod def _extract_output_line(line, output): - ''' + """ Extract text line from stream output and, if found, adds it to output. - ''' - if 'stream' in line or 'status' in line: + """ + if "stream" in line or "status" in line: # Make sure we have a string (assuming that line['stream'] and # line['status'] are either not defined, falsish, or a string) - text_line = line.get('stream') or line.get('status') or '' + text_line = line.get("stream") or line.get("status") or "" output.extend(text_line.splitlines()) def load_images(self): - ''' + """ Load images from a .tar archive - ''' + """ # Load image(s) from file load_output = [] try: self.log(f"Opening image {self.path}") - with open(self.path, 'rb') as image_tar: + with open(self.path, "rb") as image_tar: self.log(f"Loading images from {self.path}") - res = self.client._post(self.client._url("/images/load"), data=image_tar, stream=True) + res = self.client._post( + self.client._url("/images/load"), data=image_tar, stream=True + ) for line in self.client._stream_helper(res, decode=True): self.log(line, pretty_print=True) self._extract_output_line(line, load_output) except EnvironmentError as exc: if exc.errno == errno.ENOENT: self.client.fail(f"Error opening archive {self.path} - {exc}") - self.client.fail(f"Error loading archive {self.path} - {exc}", stdout='\n'.join(load_output)) + self.client.fail( + f"Error loading archive {self.path} - {exc}", + stdout="\n".join(load_output), + ) except Exception as exc: - self.client.fail(f"Error loading archive {self.path} - {exc}", stdout='\n'.join(load_output)) + self.client.fail( + f"Error loading archive {self.path} - {exc}", + stdout="\n".join(load_output), + ) # Collect loaded images loaded_images = [] for line in load_output: - if line.startswith('Loaded image:'): - loaded_images.append(line[len('Loaded image:'):].strip()) - if line.startswith('Loaded image ID:'): - loaded_images.append(line[len('Loaded image ID:'):].strip()) + if line.startswith("Loaded image:"): + loaded_images.append(line[len("Loaded image:") :].strip()) + if line.startswith("Loaded image ID:"): + loaded_images.append(line[len("Loaded image ID:") :].strip()) if not loaded_images: - self.client.fail("Detected no loaded images. Archive potentially corrupt?", stdout='\n'.join(load_output)) + self.client.fail( + "Detected no loaded images. Archive potentially corrupt?", + stdout="\n".join(load_output), + ) images = [] for image_name in loaded_images: if is_image_name_id(image_name): images.append(self.client.find_image_by_id(image_name)) - elif ':' in image_name: - image_name, tag = image_name.rsplit(':', 1) + elif ":" in image_name: + image_name, tag = image_name.rsplit(":", 1) images.append(self.client.find_image(image_name, tag)) else: - self.client.module.warn(f'Image name "{image_name}" is neither ID nor has a tag') + self.client.module.warn( + f'Image name "{image_name}" is neither ID nor has a tag' + ) - self.results['image_names'] = loaded_images - self.results['images'] = images - self.results['changed'] = True - self.results['stdout'] = '\n'.join(load_output) + self.results["image_names"] = loaded_images + self.results["images"] = images + self.results["changed"] = True + self.results["stdout"] = "\n".join(load_output) def main(): client = AnsibleDockerClient( argument_spec=dict( - path=dict(type='path', required=True), + path=dict(type="path", required=True), ), supports_check_mode=False, ) @@ -183,12 +197,16 @@ def main(): ImageManager(client, results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_pull.py b/plugins/modules/docker_image_pull.py index 0df7a41f..824b6a60 100644 --- a/plugins/modules/docker_image_pull.py +++ b/plugins/modules/docker_image_pull.py @@ -92,35 +92,34 @@ image: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) +from ansible_collections.community.docker.plugins.module_utils._platform import ( + compare_platform_strings, + compose_platform_string, + normalize_platform_string, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, is_valid_tag, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, -) - -from ansible_collections.community.docker.plugins.module_utils._platform import ( - normalize_platform_string, - compare_platform_strings, - compose_platform_string, -) - def image_info(image): result = {} if image: - result['id'] = image['Id'] + result["id"] = image["Id"] else: - result['exists'] = False + result["exists"] = False return result @@ -132,10 +131,10 @@ class ImagePuller(DockerBaseClass): self.check_mode = self.client.check_mode parameters = self.client.module.params - self.name = parameters['name'] - self.tag = parameters['tag'] - self.platform = parameters['platform'] - self.pull_mode = parameters['pull'] + self.name = parameters["name"] + self.tag = parameters["tag"] + self.platform = parameters["platform"] + self.pull_mode = parameters["pull"] if is_image_name_id(self.name): self.client.fail("Cannot pull an image by ID") @@ -157,47 +156,49 @@ class ImagePuller(DockerBaseClass): diff=dict(before=image_info(image), after=image_info(image)), ) - if image and self.pull_mode == 'not_present': + if image and self.pull_mode == "not_present": if self.platform is None: return results host_info = self.client.info() wanted_platform = normalize_platform_string( self.platform, - daemon_os=host_info.get('OSType'), - daemon_arch=host_info.get('Architecture'), + daemon_os=host_info.get("OSType"), + daemon_arch=host_info.get("Architecture"), ) image_platform = compose_platform_string( - os=image.get('Os'), - arch=image.get('Architecture'), - variant=image.get('Variant'), - daemon_os=host_info.get('OSType'), - daemon_arch=host_info.get('Architecture'), + os=image.get("Os"), + arch=image.get("Architecture"), + variant=image.get("Variant"), + daemon_os=host_info.get("OSType"), + daemon_arch=host_info.get("Architecture"), ) if compare_platform_strings(wanted_platform, image_platform): return results - results['actions'].append(f'Pulled image {self.name}:{self.tag}') + results["actions"].append(f"Pulled image {self.name}:{self.tag}") if self.check_mode: - results['changed'] = True - results['diff']['after'] = image_info(dict(Id='unknown')) + results["changed"] = True + results["diff"]["after"] = image_info(dict(Id="unknown")) else: - results['image'], not_changed = self.client.pull_image(self.name, tag=self.tag, platform=self.platform) - results['changed'] = not not_changed - results['diff']['after'] = image_info(results['image']) + results["image"], not_changed = self.client.pull_image( + self.name, tag=self.tag, platform=self.platform + ) + results["changed"] = not not_changed + results["diff"]["after"] = image_info(results["image"]) return results def main(): argument_spec = dict( - name=dict(type='str', required=True), - tag=dict(type='str', default='latest'), - platform=dict(type='str'), - pull=dict(type='str', choices=['always', 'not_present'], default='always'), + name=dict(type="str", required=True), + tag=dict(type="str", default="latest"), + platform=dict(type="str"), + pull=dict(type="str", choices=["always", "not_present"], default="always"), ) option_minimal_versions = dict( - platform=dict(docker_api_version='1.32'), + platform=dict(docker_api_version="1.32"), ) client = AnsibleDockerClient( @@ -210,12 +211,16 @@ def main(): results = ImagePuller(client).pull() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_push.py b/plugins/modules/docker_image_push.py index 883e4c91..26fe7b47 100644 --- a/plugins/modules/docker_image_push.py +++ b/plugins/modules/docker_image_push.py @@ -74,27 +74,26 @@ image: import base64 import traceback +from ansible_collections.community.docker.plugins.module_utils._api.auth import ( + get_config_header, + resolve_repository_name, +) +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, is_valid_tag, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, -) - -from ansible_collections.community.docker.plugins.module_utils._api.auth import ( - get_config_header, - resolve_repository_name, -) - class ImagePusher(DockerBaseClass): def __init__(self, client): @@ -104,8 +103,8 @@ class ImagePusher(DockerBaseClass): self.check_mode = self.client.check_mode parameters = self.client.module.params - self.name = parameters['name'] - self.tag = parameters['tag'] + self.name = parameters["name"] + self.tag = parameters["tag"] if is_image_name_id(self.name): self.client.fail("Cannot push an image by ID") @@ -126,7 +125,7 @@ class ImagePusher(DockerBaseClass): def push(self): image = self.client.find_image(name=self.name, tag=self.tag) if not image: - self.client.fail(f'Cannot find image {self.name}:{self.tag}') + self.client.fail(f"Cannot find image {self.name}:{self.tag}") results = dict( changed=False, @@ -136,7 +135,7 @@ class ImagePusher(DockerBaseClass): push_registry, push_repo = resolve_repository_name(self.name) try: - results['actions'].append(f'Pushed image {self.name}:{self.tag}') + results["actions"].append(f"Pushed image {self.name}:{self.tag}") headers = {} header = get_config_header(self.client, push_registry) @@ -144,28 +143,32 @@ class ImagePusher(DockerBaseClass): # For some reason, from Docker 28.3.3 on not specifying X-Registry-Auth seems to be invalid. # See https://github.com/moby/moby/issues/50614. header = base64.urlsafe_b64encode(b"{}") - headers['X-Registry-Auth'] = header + headers["X-Registry-Auth"] = header response = self.client._post_json( self.client._url("/images/{0}/push", self.name), data=None, headers=headers, stream=True, - params={'tag': self.tag}, + params={"tag": self.tag}, ) self.client._raise_for_status(response) for line in self.client._stream_helper(response, decode=True): self.log(line, pretty_print=True) - if line.get('errorDetail'): - raise Exception(line['errorDetail']['message']) - status = line.get('status') - if status == 'Pushing': - results['changed'] = True + if line.get("errorDetail"): + raise Exception(line["errorDetail"]["message"]) + status = line.get("status") + if status == "Pushing": + results["changed"] = True except Exception as exc: - if 'unauthorized' in str(exc): - if 'authentication required' in str(exc): - self.client.fail(f"Error pushing image {push_registry}/{push_repo}:{self.tag} - {exc}. Try logging into {push_registry} first.") + if "unauthorized" in str(exc): + if "authentication required" in str(exc): + self.client.fail( + f"Error pushing image {push_registry}/{push_repo}:{self.tag} - {exc}. Try logging into {push_registry} first." + ) else: - self.client.fail(f"Error pushing image {push_registry}/{push_repo}:{self.tag} - {exc}. Does the repository exist?") + self.client.fail( + f"Error pushing image {push_registry}/{push_repo}:{self.tag} - {exc}. Does the repository exist?" + ) self.client.fail(f"Error pushing image {self.name}:{self.tag}: {exc}") return results @@ -173,8 +176,8 @@ class ImagePusher(DockerBaseClass): def main(): argument_spec = dict( - name=dict(type='str', required=True), - tag=dict(type='str', default='latest'), + name=dict(type="str", required=True), + tag=dict(type="str", default="latest"), ) client = AnsibleDockerClient( @@ -186,12 +189,16 @@ def main(): results = ImagePusher(client).push() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_remove.py b/plugins/modules/docker_image_remove.py index 294cfc4f..fa7e4225 100644 --- a/plugins/modules/docker_image_remove.py +++ b/plugins/modules/docker_image_remove.py @@ -99,22 +99,23 @@ untagged: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, + NotFound, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, is_valid_tag, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException, NotFound -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, -) - class ImageRemover(DockerBaseClass): @@ -126,10 +127,10 @@ class ImageRemover(DockerBaseClass): self.diff = self.client.module._diff parameters = self.client.module.params - self.name = parameters['name'] - self.tag = parameters['tag'] - self.force = parameters['force'] - self.prune = parameters['prune'] + self.name = parameters["name"] + self.tag = parameters["tag"] + self.force = parameters["force"] + self.prune = parameters["prune"] if not is_valid_tag(self.tag, allow_empty=True): self.fail(f'"{self.tag}" is not a valid docker tag') @@ -149,9 +150,9 @@ class ImageRemover(DockerBaseClass): return dict(exists=False) return dict( exists=True, - id=image['Id'], - tags=sorted(image.get('RepoTags') or []), - digests=sorted(image.get('RepoDigests') or []), + id=image["Id"], + tags=sorted(image.get("RepoTags") or []), + digests=sorted(image.get("RepoDigests") or []), ) def absent(self): @@ -172,20 +173,24 @@ class ImageRemover(DockerBaseClass): name = f"{self.name}:{self.tag}" if self.diff: - results['diff'] = dict(before=self.get_diff_state(image)) + results["diff"] = dict(before=self.get_diff_state(image)) if not image: if self.diff: - results['diff']['after'] = self.get_diff_state(image) + results["diff"]["after"] = self.get_diff_state(image) return results - results['changed'] = True - results['actions'].append(f"Removed image {name}") - results['image'] = image + results["changed"] = True + results["actions"].append(f"Removed image {name}") + results["image"] = image if not self.check_mode: try: - res = self.client.delete_json('/images/{0}', name, params={'force': self.force, 'noprune': not self.prune}) + res = self.client.delete_json( + "/images/{0}", + name, + params={"force": self.force, "noprune": not self.prune}, + ) except NotFound: # If the image vanished while we were trying to remove it, do not fail res = [] @@ -193,45 +198,57 @@ class ImageRemover(DockerBaseClass): self.fail(f"Error removing image {name} - {exc}") for entry in res: - if entry.get('Untagged'): - results['untagged'].append(entry['Untagged']) - if entry.get('Deleted'): - results['deleted'].append(entry['Deleted']) + if entry.get("Untagged"): + results["untagged"].append(entry["Untagged"]) + if entry.get("Deleted"): + results["deleted"].append(entry["Deleted"]) - results['untagged'] = sorted(results['untagged']) - results['deleted'] = sorted(results['deleted']) + results["untagged"] = sorted(results["untagged"]) + results["deleted"] = sorted(results["deleted"]) if self.diff: - image_after = self.client.find_image_by_id(image['Id'], accept_missing_image=True) - results['diff']['after'] = self.get_diff_state(image_after) + image_after = self.client.find_image_by_id( + image["Id"], accept_missing_image=True + ) + results["diff"]["after"] = self.get_diff_state(image_after) elif is_image_name_id(name): - results['deleted'].append(image['Id']) - results['untagged'] = sorted((image.get('RepoTags') or []) + (image.get('RepoDigests') or [])) - if not self.force and results['untagged']: - self.fail('Cannot delete image by ID that is still in use - use force=true') + results["deleted"].append(image["Id"]) + results["untagged"] = sorted( + (image.get("RepoTags") or []) + (image.get("RepoDigests") or []) + ) + if not self.force and results["untagged"]: + self.fail( + "Cannot delete image by ID that is still in use - use force=true" + ) if self.diff: - results['diff']['after'] = self.get_diff_state({}) + results["diff"]["after"] = self.get_diff_state({}) elif is_image_name_id(self.tag): - results['untagged'].append(name) - if len(image.get('RepoTags') or []) < 1 and len(image.get('RepoDigests') or []) < 2: - results['deleted'].append(image['Id']) + results["untagged"].append(name) + if ( + len(image.get("RepoTags") or []) < 1 + and len(image.get("RepoDigests") or []) < 2 + ): + results["deleted"].append(image["Id"]) if self.diff: - results['diff']['after'] = self.get_diff_state(image) + results["diff"]["after"] = self.get_diff_state(image) try: - results['diff']['after']['digests'].remove(name) + results["diff"]["after"]["digests"].remove(name) except ValueError: pass else: - results['untagged'].append(name) - if len(image.get('RepoTags') or []) < 2 and len(image.get('RepoDigests') or []) < 1: - results['deleted'].append(image['Id']) + results["untagged"].append(name) + if ( + len(image.get("RepoTags") or []) < 2 + and len(image.get("RepoDigests") or []) < 1 + ): + results["deleted"].append(image["Id"]) if self.diff: - results['diff']['after'] = self.get_diff_state(image) + results["diff"]["after"] = self.get_diff_state(image) try: - results['diff']['after']['tags'].remove(name) + results["diff"]["after"]["tags"].remove(name) except ValueError: pass @@ -240,10 +257,10 @@ class ImageRemover(DockerBaseClass): def main(): argument_spec = dict( - name=dict(type='str', required=True), - tag=dict(type='str', default='latest'), - force=dict(type='bool', default=False), - prune=dict(type='bool', default=True), + name=dict(type="str", required=True), + tag=dict(type="str", default="latest"), + force=dict(type="bool", default=False), + prune=dict(type="bool", default=True), ) client = AnsibleDockerClient( @@ -255,12 +272,16 @@ def main(): results = ImageRemover(client).absent() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_image_tag.py b/plugins/modules/docker_image_tag.py index fcd23619..4daae0d3 100644 --- a/plugins/modules/docker_image_tag.py +++ b/plugins/modules/docker_image_tag.py @@ -103,41 +103,40 @@ tagged_images: import traceback from ansible.module_utils.common.text.formatters import human_to_bytes - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, is_image_name_id, is_valid_tag, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - parse_repository_tag, -) - def convert_to_bytes(value, module, name, unlimited_value=None): if value is None: return value try: - if unlimited_value is not None and value in ('unlimited', str(unlimited_value)): + if unlimited_value is not None and value in ("unlimited", str(unlimited_value)): return unlimited_value return human_to_bytes(value) except ValueError as exc: - module.fail_json(msg=f'Failed to convert {name} to bytes: {exc}') + module.fail_json(msg=f"Failed to convert {name} to bytes: {exc}") def image_info(name, tag, image): result = dict(name=name, tag=tag) if image: - result['id'] = image['Id'] + result["id"] = image["Id"] else: - result['exists'] = False + result["exists"] = False return result @@ -149,8 +148,8 @@ class ImageTagger(DockerBaseClass): parameters = self.client.module.params self.check_mode = self.client.check_mode - self.name = parameters['name'] - self.tag = parameters['tag'] + self.name = parameters["name"] + self.tag = parameters["tag"] if not is_valid_tag(self.tag, allow_empty=True): self.fail(f'"{self.tag}" is not a valid docker tag') @@ -161,18 +160,22 @@ class ImageTagger(DockerBaseClass): self.name = repo self.tag = repo_tag - self.keep_existing_images = parameters['existing_images'] == 'keep' + self.keep_existing_images = parameters["existing_images"] == "keep" # Make sure names in repository are valid images, and add tag if needed self.repositories = [] - for i, repository in enumerate(parameters['repository']): + for i, repository in enumerate(parameters["repository"]): if is_image_name_id(repository): - self.fail(f"repository[{i + 1}] must not be an image ID; got: {repository}") + self.fail( + f"repository[{i + 1}] must not be an image ID; got: {repository}" + ) repo, repo_tag = parse_repository_tag(repository) if not repo_tag: - repo_tag = parameters['tag'] + repo_tag = parameters["tag"] elif not is_valid_tag(repo_tag, allow_empty=False): - self.fail(f"repository[{i + 1}] must not have a digest; got: {repository}") + self.fail( + f"repository[{i + 1}] must not have a digest; got: {repository}" + ) self.repositories.append((repo, repo_tag)) def fail(self, msg): @@ -182,7 +185,7 @@ class ImageTagger(DockerBaseClass): tagged_image = self.client.find_image(name=name, tag=tag) if tagged_image: # Idempotency checks - if tagged_image['Id'] == image['Id']: + if tagged_image["Id"] == image["Id"]: return ( False, f"target image already exists ({tagged_image['Id']}) and is as expected", @@ -201,11 +204,13 @@ class ImageTagger(DockerBaseClass): if not self.check_mode: try: params = { - 'tag': tag, - 'repo': name, - 'force': True, + "tag": tag, + "repo": name, + "force": True, } - res = self.client._post(self.client._url('/images/{0}/tag', image['Id']), params=params) + res = self.client._post( + self.client._url("/images/{0}/tag", image["Id"]), params=params + ) self.client._raise_for_status(res) if res.status_code != 201: raise Exception("Tag operation failed.") @@ -237,21 +242,27 @@ class ImageTagger(DockerBaseClass): before.append(image_info(repository, tag, old_image)) after.append(image_info(repository, tag, image if tagged else old_image)) if tagged: - results['changed'] = True - results['actions'].append(f"Tagged image {image['Id']} as {repository}:{tag}: {msg}") - tagged_images.append(f'{repository}:{tag}') + results["changed"] = True + results["actions"].append( + f"Tagged image {image['Id']} as {repository}:{tag}: {msg}" + ) + tagged_images.append(f"{repository}:{tag}") else: - results['actions'].append(f"Not tagged image {image['Id']} as {repository}:{tag}: {msg}") + results["actions"].append( + f"Not tagged image {image['Id']} as {repository}:{tag}: {msg}" + ) return results def main(): argument_spec = dict( - name=dict(type='str', required=True), - tag=dict(type='str', default='latest'), - repository=dict(type='list', elements='str', required=True), - existing_images=dict(type='str', choices=['keep', 'overwrite'], default='overwrite'), + name=dict(type="str", required=True), + tag=dict(type="str", default="latest"), + repository=dict(type="list", elements="str", required=True), + existing_images=dict( + type="str", choices=["keep", "overwrite"], default="overwrite" + ), ) client = AnsibleDockerClient( @@ -263,12 +274,16 @@ def main(): results = ImageTagger(client).tag_images() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_login.py b/plugins/modules/docker_login.py index 225e653c..1a2b6e74 100644 --- a/plugins/modules/docker_login.py +++ b/plugins/modules/docker_login.py @@ -122,7 +122,19 @@ import os import traceback from ansible.module_utils.common.text.converters import to_bytes, to_text - +from ansible_collections.community.docker.plugins.module_utils._api import auth +from ansible_collections.community.docker.plugins.module_utils._api.auth import ( + decode_auth, +) +from ansible_collections.community.docker.plugins.module_utils._api.credentials.errors import ( + CredentialsNotFound, +) +from ansible_collections.community.docker.plugins.module_utils._api.credentials.store import ( + Store, +) +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, @@ -132,18 +144,12 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, ) -from ansible_collections.community.docker.plugins.module_utils._api import auth -from ansible_collections.community.docker.plugins.module_utils._api.auth import decode_auth -from ansible_collections.community.docker.plugins.module_utils._api.credentials.errors import CredentialsNotFound -from ansible_collections.community.docker.plugins.module_utils._api.credentials.store import Store -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException - class DockerFileStore(object): - ''' + """ A custom credential store class that implements only the functionality we need to update the docker config file when no credential helpers is provided. - ''' + """ program = "" @@ -151,9 +157,7 @@ class DockerFileStore(object): self._config_path = config_path # Make sure we have a minimal config if none is available. - self._config = dict( - auths=dict() - ) + self._config = dict(auths=dict()) try: # Attempt to read the existing config. @@ -168,39 +172,36 @@ class DockerFileStore(object): @property def config_path(self): - ''' + """ Return the config path configured in this DockerFileStore instance. - ''' + """ return self._config_path def get(self, server): - ''' + """ Retrieve credentials for `server` if there are any in the config file. Otherwise raise a `StoreError` - ''' + """ - server_creds = self._config['auths'].get(server) + server_creds = self._config["auths"].get(server) if not server_creds: - raise CredentialsNotFound('No matching credentials') + raise CredentialsNotFound("No matching credentials") - (username, password) = decode_auth(server_creds['auth']) + (username, password) = decode_auth(server_creds["auth"]) - return dict( - Username=username, - Secret=password - ) + return dict(Username=username, Secret=password) def _write(self): - ''' + """ Write config back out to disk. - ''' + """ # Make sure directory exists dir = os.path.dirname(self._config_path) if not os.path.exists(dir): os.makedirs(dir) # Write config; make sure it has permissions 0x600 - content = json.dumps(self._config, indent=4, sort_keys=True).encode('utf-8') + content = json.dumps(self._config, indent=4, sort_keys=True).encode("utf-8") f = os.open(self._config_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) try: os.write(f, content) @@ -208,32 +209,28 @@ class DockerFileStore(object): os.close(f) def store(self, server, username, password): - ''' + """ Add a credentials for `server` to the current configuration. - ''' + """ - b64auth = base64.b64encode( - to_bytes(username) + b':' + to_bytes(password) - ) + b64auth = base64.b64encode(to_bytes(username) + b":" + to_bytes(password)) auth = to_text(b64auth) # build up the auth structure - if 'auths' not in self._config: - self._config['auths'] = dict() + if "auths" not in self._config: + self._config["auths"] = dict() - self._config['auths'][server] = dict( - auth=auth - ) + self._config["auths"][server] = dict(auth=auth) self._write() def erase(self, server): - ''' + """ Remove credentials for the given server from the configuration. - ''' + """ - if 'auths' in self._config and server in self._config['auths']: - self._config['auths'].pop(server) + if "auths" in self._config and server in self._config["auths"]: + self._config["auths"].pop(server) self._write() @@ -248,20 +245,20 @@ class LoginManager(DockerBaseClass): parameters = self.client.module.params self.check_mode = self.client.check_mode - self.registry_url = parameters.get('registry_url') - self.username = parameters.get('username') - self.password = parameters.get('password') - self.reauthorize = parameters.get('reauthorize') - self.config_path = parameters.get('config_path') - self.state = parameters.get('state') + self.registry_url = parameters.get("registry_url") + self.username = parameters.get("username") + self.password = parameters.get("password") + self.reauthorize = parameters.get("reauthorize") + self.config_path = parameters.get("config_path") + self.state = parameters.get("state") def run(self): - ''' + """ Do the actual work of this task here. This allows instantiation for partial testing. - ''' + """ - if self.state == 'present': + if self.state == "present": self.login() else: self.logout() @@ -282,57 +279,63 @@ class LoginManager(DockerBaseClass): authcfg = self.client._auth_configs.resolve_authconfig(self.registry_url) # If we found an existing auth config for this registry and username # combination, we can return it immediately unless reauth is requested. - if authcfg and authcfg.get('username') == self.username and not reauth: + if authcfg and authcfg.get("username") == self.username and not reauth: return authcfg req_data = { - 'username': self.username, - 'password': self.password, - 'email': None, - 'serveraddress': self.registry_url, + "username": self.username, + "password": self.password, + "email": None, + "serveraddress": self.registry_url, } - response = self.client._post_json(self.client._url('/auth'), data=req_data) + response = self.client._post_json(self.client._url("/auth"), data=req_data) if response.status_code == 200: - self.client._auth_configs.add_auth(self.registry_url or auth.INDEX_NAME, req_data) + self.client._auth_configs.add_auth( + self.registry_url or auth.INDEX_NAME, req_data + ) return self.client._result(response, json=True) def login(self): - ''' + """ Log into the registry with provided username/password. On success update the config file with the new authorization. :return: None - ''' + """ - self.results['actions'].append(f"Logged into {self.registry_url}") + self.results["actions"].append(f"Logged into {self.registry_url}") self.log(f"Log into {self.registry_url} with username {self.username}") try: response = self._login(self.reauthorize) except Exception as exc: - self.fail(f"Logging into {self.registry_url} for user {self.username} failed - {exc}") + self.fail( + f"Logging into {self.registry_url} for user {self.username} failed - {exc}" + ) # If user is already logged in, then response contains password for user - if 'password' in response: + if "password" in response: # This returns correct password if user is logged in and wrong password is given. # So if it returns another password as we passed, and the user did not request to # reauthorize, still do it. - if not self.reauthorize and response['password'] != self.password: + if not self.reauthorize and response["password"] != self.password: try: response = self._login(True) except Exception as exc: - self.fail(f"Logging into {self.registry_url} for user {self.username} failed - {exc}") - response.pop('password', None) - self.results['login_result'] = response + self.fail( + f"Logging into {self.registry_url} for user {self.username} failed - {exc}" + ) + response.pop("password", None) + self.results["login_result"] = response self.update_credentials() def logout(self): - ''' + """ Log out of the registry. On success update the config file. :return: None - ''' + """ # Get the configuration store. store = self.get_credential_store_instance(self.registry_url, self.config_path) @@ -342,20 +345,20 @@ class LoginManager(DockerBaseClass): except CredentialsNotFound: # get raises an exception on not found. self.log(f"Credentials for {self.registry_url} not present, doing nothing.") - self.results['changed'] = False + self.results["changed"] = False return if not self.check_mode: store.erase(self.registry_url) - self.results['changed'] = True + self.results["changed"] = True def update_credentials(self): - ''' + """ If the authorization is not stored attempt to store authorization values via the appropriate credential helper or to the config file. :return: None - ''' + """ # Check to see if credentials already exist. store = self.get_credential_store_instance(self.registry_url, self.config_path) @@ -364,25 +367,30 @@ class LoginManager(DockerBaseClass): current = store.get(self.registry_url) except CredentialsNotFound: # get raises an exception on not found. - current = dict( - Username='', - Secret='' - ) + current = dict(Username="", Secret="") - if current['Username'] != self.username or current['Secret'] != self.password or self.reauthorize: + if ( + current["Username"] != self.username + or current["Secret"] != self.password + or self.reauthorize + ): if not self.check_mode: store.store(self.registry_url, self.username, self.password) - self.log(f"Writing credentials to configured helper {store.program} for {self.registry_url}") - self.results['actions'].append(f"Wrote credentials to configured helper {store.program} for {self.registry_url}") - self.results['changed'] = True + self.log( + f"Writing credentials to configured helper {store.program} for {self.registry_url}" + ) + self.results["actions"].append( + f"Wrote credentials to configured helper {store.program} for {self.registry_url}" + ) + self.results["changed"] = True def get_credential_store_instance(self, registry, dockercfg_path): - ''' + """ Return an instance of docker.credentials.Store used by the given registry. :return: A Store or None :rtype: Union[docker.credentials.Store, NoneType] - ''' + """ credstore_env = self.client.credstore_env @@ -402,16 +410,20 @@ class LoginManager(DockerBaseClass): def main(): argument_spec = dict( - registry_url=dict(type='str', default=DEFAULT_DOCKER_REGISTRY, aliases=['registry', 'url']), - username=dict(type='str'), - password=dict(type='str', no_log=True), - reauthorize=dict(type='bool', default=False, aliases=['reauth']), - state=dict(type='str', default='present', choices=['present', 'absent']), - config_path=dict(type='path', default='~/.docker/config.json', aliases=['dockercfg_path']), + registry_url=dict( + type="str", default=DEFAULT_DOCKER_REGISTRY, aliases=["registry", "url"] + ), + username=dict(type="str"), + password=dict(type="str", no_log=True), + reauthorize=dict(type="bool", default=False, aliases=["reauth"]), + state=dict(type="str", default="present", choices=["present", "absent"]), + config_path=dict( + type="path", default="~/.docker/config.json", aliases=["dockercfg_path"] + ), ) required_if = [ - ('state', 'present', ['username', 'password']), + ("state", "present", ["username", "password"]), ] client = AnsibleDockerClient( @@ -421,25 +433,25 @@ def main(): ) try: - results = dict( - changed=False, - actions=[], - login_result={} - ) + results = dict(changed=False, actions=[], login_result={}) manager = LoginManager(client, results) manager.run() - if 'actions' in results: - del results['actions'] + if "actions" in results: + del results["actions"] client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_network.py b/plugins/modules/docker_network.py index 149eb250..3c02e9b5 100644 --- a/plugins/modules/docker_network.py +++ b/plugins/modules/docker_network.py @@ -282,22 +282,23 @@ network: """ import re -import traceback import time +import traceback from ansible.module_utils.common.text.converters import to_native - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) from ansible_collections.community.docker.plugins.module_utils.util import ( - DockerBaseClass, DifferenceTracker, + DockerBaseClass, clean_dict_booleans_for_docker_api, sanitize_labels, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException class TaskParameters(DockerBaseClass): @@ -331,15 +332,19 @@ class TaskParameters(DockerBaseClass): # config_only sets driver to 'null' (and scope to 'local') so force that here. Otherwise we get # diffs of 'null' --> 'bridge' given that the driver option defaults to 'bridge'. if self.config_only: - self.driver = 'null' + self.driver = "null" def container_names_in_network(network): - return [c['Name'] for c in network['Containers'].values()] if network['Containers'] else [] + return ( + [c["Name"] for c in network["Containers"].values()] + if network["Containers"] + else [] + ) -CIDR_IPV4 = re.compile(r'^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$') -CIDR_IPV6 = re.compile(r'^[0-9a-fA-F:]+/([0-9]|[1-9][0-9]|1[0-2][0-9])$') +CIDR_IPV4 = re.compile(r"^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$") +CIDR_IPV6 = re.compile(r"^[0-9a-fA-F:]+/([0-9]|[1-9][0-9]|1[0-2][0-9])$") def validate_cidr(cidr): @@ -352,9 +357,9 @@ def validate_cidr(cidr): :raises ValueError: If ``cidr`` is not a valid CIDR """ if CIDR_IPV4.match(cidr): - return 'ipv4' + return "ipv4" elif CIDR_IPV6.match(cidr): - return 'ipv6' + return "ipv6" raise ValueError(f'"{cidr}" is not a valid CIDR') @@ -366,9 +371,7 @@ def normalize_ipam_config_key(key): :return Ansible module key :rtype str """ - special_cases = { - 'AuxiliaryAddresses': 'aux_addresses' - } + special_cases = {"AuxiliaryAddresses": "aux_addresses"} return special_cases.get(key, key.lower()) @@ -388,10 +391,7 @@ class DockerNetworkManager(object): self.client = client self.parameters = TaskParameters(client) self.check_mode = self.client.check_mode - self.results = { - 'changed': False, - 'actions': [] - } + self.results = {"changed": False, "actions": []} self.diff = self.client.module._diff self.diff_tracker = DifferenceTracker() self.diff_result = dict() @@ -399,87 +399,117 @@ class DockerNetworkManager(object): self.existing_network = self.get_existing_network() if not self.parameters.connected and self.existing_network: - self.parameters.connected = container_names_in_network(self.existing_network) + self.parameters.connected = container_names_in_network( + self.existing_network + ) if self.parameters.ipam_config: try: for ipam_config in self.parameters.ipam_config: - validate_cidr(ipam_config['subnet']) + validate_cidr(ipam_config["subnet"]) except ValueError as e: self.client.fail(to_native(e)) if self.parameters.driver_options: - self.parameters.driver_options = clean_dict_booleans_for_docker_api(self.parameters.driver_options) + self.parameters.driver_options = clean_dict_booleans_for_docker_api( + self.parameters.driver_options + ) state = self.parameters.state - if state == 'present': + if state == "present": self.present() - elif state == 'absent': + elif state == "absent": self.absent() if self.diff or self.check_mode or self.parameters.debug: if self.diff: - self.diff_result['before'], self.diff_result['after'] = self.diff_tracker.get_before_after() - self.results['diff'] = self.diff_result + self.diff_result["before"], self.diff_result["after"] = ( + self.diff_tracker.get_before_after() + ) + self.results["diff"] = self.diff_result def get_existing_network(self): return self.client.get_network(name=self.parameters.name) def has_different_config(self, net): - ''' + """ Evaluates an existing network and returns a tuple containing a boolean indicating if the configuration is different and a list of differences. :param net: the inspection output for an existing network :return: (bool, list) - ''' + """ differences = DifferenceTracker() - if self.parameters.config_only is not None and self.parameters.config_only != net.get('ConfigOnly', False): - differences.add('config_only', - parameter=self.parameters.config_only, - active=net.get('ConfigOnly', False)) - if self.parameters.config_from is not None and self.parameters.config_from != net.get('ConfigFrom', {}).get('Network', ''): - differences.add('config_from', - parameter=self.parameters.config_from, - active=net.get('ConfigFrom', {}).get('Network', '')) - if self.parameters.driver and self.parameters.driver != net['Driver']: - differences.add('driver', - parameter=self.parameters.driver, - active=net['Driver']) + if ( + self.parameters.config_only is not None + and self.parameters.config_only != net.get("ConfigOnly", False) + ): + differences.add( + "config_only", + parameter=self.parameters.config_only, + active=net.get("ConfigOnly", False), + ) + if ( + self.parameters.config_from is not None + and self.parameters.config_from + != net.get("ConfigFrom", {}).get("Network", "") + ): + differences.add( + "config_from", + parameter=self.parameters.config_from, + active=net.get("ConfigFrom", {}).get("Network", ""), + ) + if self.parameters.driver and self.parameters.driver != net["Driver"]: + differences.add( + "driver", parameter=self.parameters.driver, active=net["Driver"] + ) if self.parameters.driver_options: - if not net.get('Options'): - differences.add('driver_options', - parameter=self.parameters.driver_options, - active=net.get('Options')) + if not net.get("Options"): + differences.add( + "driver_options", + parameter=self.parameters.driver_options, + active=net.get("Options"), + ) else: for key, value in self.parameters.driver_options.items(): - if not (key in net['Options']) or value != net['Options'][key]: - differences.add(f'driver_options.{key}', - parameter=value, - active=net['Options'].get(key)) + if not (key in net["Options"]) or value != net["Options"][key]: + differences.add( + f"driver_options.{key}", + parameter=value, + active=net["Options"].get(key), + ) if self.parameters.ipam_driver: - if not net.get('IPAM') or net['IPAM']['Driver'] != self.parameters.ipam_driver: - differences.add('ipam_driver', - parameter=self.parameters.ipam_driver, - active=net.get('IPAM')) + if ( + not net.get("IPAM") + or net["IPAM"]["Driver"] != self.parameters.ipam_driver + ): + differences.add( + "ipam_driver", + parameter=self.parameters.ipam_driver, + active=net.get("IPAM"), + ) if self.parameters.ipam_driver_options is not None: - ipam_driver_options = net['IPAM'].get('Options') or {} + ipam_driver_options = net["IPAM"].get("Options") or {} if ipam_driver_options != self.parameters.ipam_driver_options: - differences.add('ipam_driver_options', - parameter=self.parameters.ipam_driver_options, - active=ipam_driver_options) + differences.add( + "ipam_driver_options", + parameter=self.parameters.ipam_driver_options, + active=ipam_driver_options, + ) if self.parameters.ipam_config is not None and self.parameters.ipam_config: - if not net.get('IPAM') or not net['IPAM']['Config']: - differences.add('ipam_config', - parameter=self.parameters.ipam_config, - active=net.get('IPAM', {}).get('Config')) + if not net.get("IPAM") or not net["IPAM"]["Config"]: + differences.add( + "ipam_config", + parameter=self.parameters.ipam_config, + active=net.get("IPAM", {}).get("Config"), + ) else: # Put network's IPAM config into the same format as module's IPAM config net_ipam_configs = [] - for net_ipam_config in net['IPAM']['Config']: + for net_ipam_config in net["IPAM"]["Config"]: config = dict() for k, v in net_ipam_config.items(): config[normalize_ipam_config_key(k)] = v @@ -497,118 +527,154 @@ class DockerNetworkManager(object): # (but have default value None if not specified) continue if value != net_config.get(key): - differences.add(f'ipam_config[{idx}].{key}', - parameter=value, - active=net_config.get(key)) + differences.add( + f"ipam_config[{idx}].{key}", + parameter=value, + active=net_config.get(key), + ) - if self.parameters.enable_ipv4 is not None and self.parameters.enable_ipv4 != net.get('EnableIPv4', False): - differences.add('enable_ipv4', - parameter=self.parameters.enable_ipv4, - active=net.get('EnableIPv4', False)) - if self.parameters.enable_ipv6 is not None and self.parameters.enable_ipv6 != net.get('EnableIPv6', False): - differences.add('enable_ipv6', - parameter=self.parameters.enable_ipv6, - active=net.get('EnableIPv6', False)) + if ( + self.parameters.enable_ipv4 is not None + and self.parameters.enable_ipv4 != net.get("EnableIPv4", False) + ): + differences.add( + "enable_ipv4", + parameter=self.parameters.enable_ipv4, + active=net.get("EnableIPv4", False), + ) + if ( + self.parameters.enable_ipv6 is not None + and self.parameters.enable_ipv6 != net.get("EnableIPv6", False) + ): + differences.add( + "enable_ipv6", + parameter=self.parameters.enable_ipv6, + active=net.get("EnableIPv6", False), + ) - if self.parameters.internal is not None and self.parameters.internal != net.get('Internal', False): - differences.add('internal', - parameter=self.parameters.internal, - active=net.get('Internal')) + if ( + self.parameters.internal is not None + and self.parameters.internal != net.get("Internal", False) + ): + differences.add( + "internal", + parameter=self.parameters.internal, + active=net.get("Internal"), + ) - if self.parameters.scope is not None and self.parameters.scope != net.get('Scope'): - differences.add('scope', - parameter=self.parameters.scope, - active=net.get('Scope')) + if self.parameters.scope is not None and self.parameters.scope != net.get( + "Scope" + ): + differences.add( + "scope", parameter=self.parameters.scope, active=net.get("Scope") + ) - if self.parameters.attachable is not None and self.parameters.attachable != net.get('Attachable', False): - differences.add('attachable', - parameter=self.parameters.attachable, - active=net.get('Attachable')) - if self.parameters.ingress is not None and self.parameters.ingress != net.get('Ingress', False): - differences.add('ingress', - parameter=self.parameters.ingress, - active=net.get('Ingress')) + if ( + self.parameters.attachable is not None + and self.parameters.attachable != net.get("Attachable", False) + ): + differences.add( + "attachable", + parameter=self.parameters.attachable, + active=net.get("Attachable"), + ) + if self.parameters.ingress is not None and self.parameters.ingress != net.get( + "Ingress", False + ): + differences.add( + "ingress", parameter=self.parameters.ingress, active=net.get("Ingress") + ) if self.parameters.labels: - if not net.get('Labels'): - differences.add('labels', - parameter=self.parameters.labels, - active=net.get('Labels')) + if not net.get("Labels"): + differences.add( + "labels", parameter=self.parameters.labels, active=net.get("Labels") + ) else: for key, value in self.parameters.labels.items(): - if not (key in net['Labels']) or value != net['Labels'][key]: - differences.add(f'labels.{key}', - parameter=value, - active=net['Labels'].get(key)) + if not (key in net["Labels"]) or value != net["Labels"][key]: + differences.add( + f"labels.{key}", + parameter=value, + active=net["Labels"].get(key), + ) return not differences.empty, differences def create_network(self): if not self.existing_network: data = { - 'Name': self.parameters.name, - 'Driver': self.parameters.driver, - 'Options': self.parameters.driver_options, - 'IPAM': None, - 'CheckDuplicate': None, + "Name": self.parameters.name, + "Driver": self.parameters.driver, + "Options": self.parameters.driver_options, + "IPAM": None, + "CheckDuplicate": None, } if self.parameters.config_only is not None: - data['ConfigOnly'] = self.parameters.config_only + data["ConfigOnly"] = self.parameters.config_only if self.parameters.config_from: - data['ConfigFrom'] = {'Network': self.parameters.config_from} + data["ConfigFrom"] = {"Network": self.parameters.config_from} if self.parameters.enable_ipv6 is not None: - data['EnableIPv6'] = self.parameters.enable_ipv6 + data["EnableIPv6"] = self.parameters.enable_ipv6 if self.parameters.enable_ipv4 is not None: - data['EnableIPv4'] = self.parameters.enable_ipv4 + data["EnableIPv4"] = self.parameters.enable_ipv4 if self.parameters.internal: - data['Internal'] = True + data["Internal"] = True if self.parameters.scope is not None: - data['Scope'] = self.parameters.scope + data["Scope"] = self.parameters.scope if self.parameters.attachable is not None: - data['Attachable'] = self.parameters.attachable + data["Attachable"] = self.parameters.attachable if self.parameters.ingress is not None: - data['Ingress'] = self.parameters.ingress + data["Ingress"] = self.parameters.ingress if self.parameters.labels is not None: data["Labels"] = self.parameters.labels ipam_pools = [] if self.parameters.ipam_config: for ipam_pool in self.parameters.ipam_config: - ipam_pools.append({ - 'Subnet': ipam_pool['subnet'], - 'IPRange': ipam_pool['iprange'], - 'Gateway': ipam_pool['gateway'], - 'AuxiliaryAddresses': ipam_pool['aux_addresses'], - }) + ipam_pools.append( + { + "Subnet": ipam_pool["subnet"], + "IPRange": ipam_pool["iprange"], + "Gateway": ipam_pool["gateway"], + "AuxiliaryAddresses": ipam_pool["aux_addresses"], + } + ) - if self.parameters.ipam_driver or self.parameters.ipam_driver_options or ipam_pools: + if ( + self.parameters.ipam_driver + or self.parameters.ipam_driver_options + or ipam_pools + ): # Only add IPAM if a driver was specified or if IPAM parameters were # specified. Leaving this parameter out can significantly speed up # creation; on my machine creation with this option needs ~15 seconds, # and without just a few seconds. - data['IPAM'] = { - 'Driver': self.parameters.ipam_driver, - 'Config': ipam_pools or [], - 'Options': self.parameters.ipam_driver_options, + data["IPAM"] = { + "Driver": self.parameters.ipam_driver, + "Config": ipam_pools or [], + "Options": self.parameters.ipam_driver_options, } if not self.check_mode: - resp = self.client.post_json_to_json('/networks/create', data=data) - self.client.report_warnings(resp, ['Warning']) - self.existing_network = self.client.get_network(network_id=resp['Id']) - self.results['actions'].append(f"Created network {self.parameters.name} with driver {self.parameters.driver}") - self.results['changed'] = True + resp = self.client.post_json_to_json("/networks/create", data=data) + self.client.report_warnings(resp, ["Warning"]) + self.existing_network = self.client.get_network(network_id=resp["Id"]) + self.results["actions"].append( + f"Created network {self.parameters.name} with driver {self.parameters.driver}" + ) + self.results["changed"] = True def remove_network(self): if self.existing_network: self.disconnect_all_containers() if not self.check_mode: - self.client.delete_call('/networks/{0}', self.parameters.name) - if self.existing_network.get('Scope', 'local') == 'swarm': + self.client.delete_call("/networks/{0}", self.parameters.name) + if self.existing_network.get("Scope", "local") == "swarm": while self.get_existing_network(): time.sleep(0.1) - self.results['actions'].append(f"Removed network {self.parameters.name}") - self.results['changed'] = True + self.results["actions"].append(f"Removed network {self.parameters.name}") + self.results["changed"] = True def is_container_connected(self, container_name): if not self.existing_network: @@ -621,11 +687,15 @@ class DockerNetworkManager(object): return bool(container) except DockerException as e: - self.client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + self.client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: self.client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) def connect_containers(self): for name in self.parameters.connected: @@ -635,38 +705,42 @@ class DockerNetworkManager(object): "Container": name, "EndpointConfig": None, } - self.client.post_json('/networks/{0}/connect', self.parameters.name, data=data) - self.results['actions'].append(f"Connected container {name}") - self.results['changed'] = True - self.diff_tracker.add(f'connected.{name}', parameter=True, active=False) + self.client.post_json( + "/networks/{0}/connect", self.parameters.name, data=data + ) + self.results["actions"].append(f"Connected container {name}") + self.results["changed"] = True + self.diff_tracker.add(f"connected.{name}", parameter=True, active=False) def disconnect_missing(self): if not self.existing_network: return - containers = self.existing_network['Containers'] + containers = self.existing_network["Containers"] if not containers: return for c in containers.values(): - name = c['Name'] + name = c["Name"] if name not in self.parameters.connected: self.disconnect_container(name) def disconnect_all_containers(self): - containers = self.client.get_network(name=self.parameters.name)['Containers'] + containers = self.client.get_network(name=self.parameters.name)["Containers"] if not containers: return for cont in containers.values(): - self.disconnect_container(cont['Name']) + self.disconnect_container(cont["Name"]) def disconnect_container(self, container_name): if not self.check_mode: data = {"Container": container_name, "Force": True} - self.client.post_json('/networks/{0}/disconnect', self.parameters.name, data=data) - self.results['actions'].append(f"Disconnected container {container_name}") - self.results['changed'] = True - self.diff_tracker.add(f'connected.{container_name}', - parameter=False, - active=True) + self.client.post_json( + "/networks/{0}/disconnect", self.parameters.name, data=data + ) + self.results["actions"].append(f"Disconnected container {container_name}") + self.results["changed"] = True + self.diff_tracker.add( + f"connected.{container_name}", parameter=False, active=True + ) def present(self): different = False @@ -674,7 +748,9 @@ class DockerNetworkManager(object): if self.existing_network: different, differences = self.has_different_config(self.existing_network) - self.diff_tracker.add('exists', parameter=True, active=self.existing_network is not None) + self.diff_tracker.add( + "exists", parameter=True, active=self.existing_network is not None + ) if self.parameters.force or different: self.remove_network() self.existing_network = None @@ -685,55 +761,61 @@ class DockerNetworkManager(object): self.disconnect_missing() if self.diff or self.check_mode or self.parameters.debug: - self.diff_result['differences'] = differences.get_legacy_docker_diffs() + self.diff_result["differences"] = differences.get_legacy_docker_diffs() self.diff_tracker.merge(differences) if not self.check_mode and not self.parameters.debug: - self.results.pop('actions') + self.results.pop("actions") network_facts = self.get_existing_network() - self.results['network'] = network_facts + self.results["network"] = network_facts def absent(self): - self.diff_tracker.add('exists', parameter=False, active=self.existing_network is not None) + self.diff_tracker.add( + "exists", parameter=False, active=self.existing_network is not None + ) self.remove_network() def main(): argument_spec = dict( - name=dict(type='str', required=True, aliases=['network_name']), - config_from=dict(type='str'), - config_only=dict(type='bool'), - connected=dict(type='list', default=[], elements='str', aliases=['containers']), - state=dict(type='str', default='present', choices=['present', 'absent']), - driver=dict(type='str', default='bridge'), - driver_options=dict(type='dict', default={}), - force=dict(type='bool', default=False), - appends=dict(type='bool', default=False, aliases=['incremental']), - ipam_driver=dict(type='str'), - ipam_driver_options=dict(type='dict'), - ipam_config=dict(type='list', elements='dict', options=dict( - subnet=dict(type='str'), - iprange=dict(type='str'), - gateway=dict(type='str'), - aux_addresses=dict(type='dict'), - )), - enable_ipv4=dict(type='bool'), - enable_ipv6=dict(type='bool'), - internal=dict(type='bool'), - labels=dict(type='dict', default={}), - debug=dict(type='bool', default=False), - scope=dict(type='str', choices=['local', 'global', 'swarm']), - attachable=dict(type='bool'), - ingress=dict(type='bool'), + name=dict(type="str", required=True, aliases=["network_name"]), + config_from=dict(type="str"), + config_only=dict(type="bool"), + connected=dict(type="list", default=[], elements="str", aliases=["containers"]), + state=dict(type="str", default="present", choices=["present", "absent"]), + driver=dict(type="str", default="bridge"), + driver_options=dict(type="dict", default={}), + force=dict(type="bool", default=False), + appends=dict(type="bool", default=False, aliases=["incremental"]), + ipam_driver=dict(type="str"), + ipam_driver_options=dict(type="dict"), + ipam_config=dict( + type="list", + elements="dict", + options=dict( + subnet=dict(type="str"), + iprange=dict(type="str"), + gateway=dict(type="str"), + aux_addresses=dict(type="dict"), + ), + ), + enable_ipv4=dict(type="bool"), + enable_ipv6=dict(type="bool"), + internal=dict(type="bool"), + labels=dict(type="dict", default={}), + debug=dict(type="bool", default=False), + scope=dict(type="str", choices=["local", "global", "swarm"]), + attachable=dict(type="bool"), + ingress=dict(type="bool"), ) option_minimal_versions = dict( - config_from=dict(docker_api_version='1.30'), - config_only=dict(docker_api_version='1.30'), - scope=dict(docker_api_version='1.30'), - attachable=dict(docker_api_version='1.26'), - enable_ipv4=dict(docker_api_version='1.47'), + config_from=dict(docker_api_version="1.30"), + config_only=dict(docker_api_version="1.30"), + scope=dict(docker_api_version="1.30"), + attachable=dict(docker_api_version="1.26"), + enable_ipv4=dict(docker_api_version="1.47"), ) client = AnsibleDockerClient( @@ -742,17 +824,21 @@ def main(): # "The docker server >= 1.10.0" option_minimal_versions=option_minimal_versions, ) - sanitize_labels(client.module.params['labels'], 'labels', client) + sanitize_labels(client.module.params["labels"], "labels", client) try: cm = DockerNetworkManager(client) client.module.exit_json(**cm.results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_network_info.py b/plugins/modules/docker_network_info.py index 96745223..05890bfc 100644 --- a/plugins/modules/docker_network_info.py +++ b/plugins/modules/docker_network_info.py @@ -98,16 +98,18 @@ network: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException def main(): argument_spec = dict( - name=dict(type='str', required=True), + name=dict(type="str", required=True), ) client = AnsibleDockerClient( @@ -116,7 +118,7 @@ def main(): ) try: - network = client.get_network(client.module.params['name']) + network = client.get_network(client.module.params["name"]) client.module.exit_json( changed=False, @@ -124,12 +126,16 @@ def main(): network=network, ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_node.py b/plugins/modules/docker_node.py index 177a492e..9f28f201 100644 --- a/plugins/modules/docker_node.py +++ b/plugins/modules/docker_node.py @@ -6,6 +6,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_node short_description: Manage Docker Swarm node @@ -134,21 +135,24 @@ node: import traceback + try: - from docker.errors import DockerException, APIError + from docker.errors import APIError, DockerException except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass +from ansible.module_utils.common.text.converters import to_native from ansible_collections.community.docker.plugins.module_utils.common import ( DockerBaseClass, RequestException, ) - -from ansible.module_utils.common.text.converters import to_native - -from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient -from ansible_collections.community.docker.plugins.module_utils.util import sanitize_labels +from ansible_collections.community.docker.plugins.module_utils.swarm import ( + AnsibleDockerSwarmClient, +) +from ansible_collections.community.docker.plugins.module_utils.util import ( + sanitize_labels, +) class TaskParameters(DockerBaseClass): @@ -208,80 +212,84 @@ class SwarmNodeManager(DockerBaseClass): ) if self.parameters.role is None: - node_spec['Role'] = node_info['Spec']['Role'] + node_spec["Role"] = node_info["Spec"]["Role"] else: - if not node_info['Spec']['Role'] == self.parameters.role: - node_spec['Role'] = self.parameters.role + if not node_info["Spec"]["Role"] == self.parameters.role: + node_spec["Role"] = self.parameters.role changed = True if self.parameters.availability is None: - node_spec['Availability'] = node_info['Spec']['Availability'] + node_spec["Availability"] = node_info["Spec"]["Availability"] else: - if not node_info['Spec']['Availability'] == self.parameters.availability: - node_info['Spec']['Availability'] = self.parameters.availability + if not node_info["Spec"]["Availability"] == self.parameters.availability: + node_info["Spec"]["Availability"] = self.parameters.availability changed = True - if self.parameters.labels_state == 'replace': + if self.parameters.labels_state == "replace": if self.parameters.labels is None: - node_spec['Labels'] = {} - if node_info['Spec']['Labels']: + node_spec["Labels"] = {} + if node_info["Spec"]["Labels"]: changed = True else: - if (node_info['Spec']['Labels'] or {}) != self.parameters.labels: - node_spec['Labels'] = self.parameters.labels + if (node_info["Spec"]["Labels"] or {}) != self.parameters.labels: + node_spec["Labels"] = self.parameters.labels changed = True - elif self.parameters.labels_state == 'merge': - node_spec['Labels'] = dict(node_info['Spec']['Labels'] or {}) + elif self.parameters.labels_state == "merge": + node_spec["Labels"] = dict(node_info["Spec"]["Labels"] or {}) if self.parameters.labels is not None: for key, value in self.parameters.labels.items(): - if node_spec['Labels'].get(key) != value: - node_spec['Labels'][key] = value + if node_spec["Labels"].get(key) != value: + node_spec["Labels"][key] = value changed = True if self.parameters.labels_to_remove is not None: for key in self.parameters.labels_to_remove: if self.parameters.labels is not None: if not self.parameters.labels.get(key): - if node_spec['Labels'].get(key): - node_spec['Labels'].pop(key) + if node_spec["Labels"].get(key): + node_spec["Labels"].pop(key) changed = True else: self.client.module.warn( f"Label '{to_native(key)}' listed both in 'labels' and 'labels_to_remove'. " - "Keeping the assigned label value.") + "Keeping the assigned label value." + ) else: - if node_spec['Labels'].get(key): - node_spec['Labels'].pop(key) + if node_spec["Labels"].get(key): + node_spec["Labels"].pop(key) changed = True if changed is True: if not self.check_mode: try: - self.client.update_node(node_id=node_info['ID'], version=node_info['Version']['Index'], - node_spec=node_spec) + self.client.update_node( + node_id=node_info["ID"], + version=node_info["Version"]["Index"], + node_spec=node_spec, + ) except APIError as exc: self.client.fail(f"Failed to update node : {exc}") - self.results['node'] = self.client.get_node_inspect(node_id=node_info['ID']) - self.results['changed'] = changed + self.results["node"] = self.client.get_node_inspect(node_id=node_info["ID"]) + self.results["changed"] = changed else: - self.results['node'] = node_info - self.results['changed'] = changed + self.results["node"] = node_info + self.results["changed"] = changed def main(): argument_spec = dict( - hostname=dict(type='str', required=True), - labels=dict(type='dict'), - labels_state=dict(type='str', default='merge', choices=['merge', 'replace']), - labels_to_remove=dict(type='list', elements='str'), - availability=dict(type='str', choices=['active', 'pause', 'drain']), - role=dict(type='str', choices=['worker', 'manager']), + hostname=dict(type="str", required=True), + labels=dict(type="dict"), + labels_state=dict(type="str", default="merge", choices=["merge", "replace"]), + labels_to_remove=dict(type="list", elements="str"), + availability=dict(type="str", choices=["active", "pause", "drain"]), + role=dict(type="str", choices=["worker", "manager"]), ) client = AnsibleDockerSwarmClient( argument_spec=argument_spec, supports_check_mode=True, - min_docker_version='2.4.0', + min_docker_version="2.4.0", ) try: @@ -292,12 +300,16 @@ def main(): SwarmNodeManager(client, results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_node_info.py b/plugins/modules/docker_node_info.py index f548e3d1..34f1a776 100644 --- a/plugins/modules/docker_node_info.py +++ b/plugins/modules/docker_node_info.py @@ -91,7 +91,10 @@ import traceback from ansible_collections.community.docker.plugins.module_utils.common import ( RequestException, ) -from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient +from ansible_collections.community.docker.plugins.module_utils.swarm import ( + AnsibleDockerSwarmClient, +) + try: from docker.errors import DockerException @@ -104,22 +107,24 @@ def get_node_facts(client): results = [] - if client.module.params['self'] is True: + if client.module.params["self"] is True: self_node_id = client.get_swarm_node_id() node_info = client.get_node_inspect(node_id=self_node_id) results.append(node_info) return results - if client.module.params['name'] is None: + if client.module.params["name"] is None: node_info = client.get_all_nodes_inspect() return node_info - nodes = client.module.params['name'] + nodes = client.module.params["name"] if not isinstance(nodes, list): nodes = [nodes] for next_node_name in nodes: - next_node_info = client.get_node_inspect(node_id=next_node_name, skip_missing=True) + next_node_info = client.get_node_inspect( + node_id=next_node_name, skip_missing=True + ) if next_node_info: results.append(next_node_info) return results @@ -127,14 +132,14 @@ def get_node_facts(client): def main(): argument_spec = dict( - name=dict(type='list', elements='str'), - self=dict(type='bool', default=False), + name=dict(type="list", elements="str"), + self=dict(type="bool", default=False), ) client = AnsibleDockerSwarmClient( argument_spec=argument_spec, supports_check_mode=True, - min_docker_version='2.4.0', + min_docker_version="2.4.0", ) client.fail_task_if_not_swarm_manager() @@ -147,12 +152,16 @@ def main(): nodes=nodes, ) except DockerException as e: - client.fail(f'An unexpected docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_plugin.py b/plugins/modules/docker_plugin.py index 878fdd92..b4d41418 100644 --- a/plugins/modules/docker_plugin.py +++ b/plugins/modules/docker_plugin.py @@ -131,20 +131,21 @@ actions: import traceback from ansible.module_utils.common.text.converters import to_native - +from ansible_collections.community.docker.plugins.module_utils._api import auth +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, + NotFound, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, - RequestException + RequestException, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( - DockerBaseClass, DifferenceTracker, + DockerBaseClass, ) -from ansible_collections.community.docker.plugins.module_utils._api import auth -from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException, NotFound - class TaskParameters(DockerBaseClass): def __init__(self, client): @@ -162,11 +163,15 @@ class TaskParameters(DockerBaseClass): def prepare_options(options): - return [f'{k}={v if v is not None else ""}' for k, v in options.items()] if options else [] + return ( + [f'{k}={v if v is not None else ""}' for k, v in options.items()] + if options + else [] + ) def parse_options(options_list): - return dict(x.split('=', 1) for x in options_list) if options_list else {} + return dict(x.split("=", 1) for x in options_list) if options_list else {} class DockerPluginManager(object): @@ -187,23 +192,25 @@ class DockerPluginManager(object): self.existing_plugin = self.get_existing_plugin() state = self.parameters.state - if state == 'present': + if state == "present": self.present() - elif state == 'absent': + elif state == "absent": self.absent() - elif state == 'enable': + elif state == "enable": self.enable() - elif state == 'disable': + elif state == "disable": self.disable() if self.diff or self.check_mode or self.parameters.debug: if self.diff: - self.diff_result['before'], self.diff_result['after'] = self.diff_tracker.get_before_after() + self.diff_result["before"], self.diff_result["after"] = ( + self.diff_tracker.get_before_after() + ) self.diff = self.diff_result def get_existing_plugin(self): try: - return self.client.get_json('/plugins/{0}/json', self.preferred_name) + return self.client.get_json("/plugins/{0}/json", self.preferred_name) except NotFound: return None except APIError as e: @@ -217,19 +224,27 @@ class DockerPluginManager(object): """ differences = DifferenceTracker() if self.parameters.plugin_options: - settings = self.existing_plugin.get('Settings') + settings = self.existing_plugin.get("Settings") if not settings: - differences.add('plugin_options', parameters=self.parameters.plugin_options, active=settings) + differences.add( + "plugin_options", + parameters=self.parameters.plugin_options, + active=settings, + ) else: - existing_options = parse_options(settings.get('Env')) + existing_options = parse_options(settings.get("Env")) for key, value in self.parameters.plugin_options.items(): - if ((not existing_options.get(key) and value) or - not value or - value != existing_options[key]): - differences.add(f'plugin_options.{key}', - parameter=value, - active=existing_options.get(key)) + if ( + (not existing_options.get(key) and value) + or not value + or value != existing_options[key] + ): + differences.add( + f"plugin_options.{key}", + parameter=value, + active=existing_options.get(key), + ) return differences @@ -239,26 +254,42 @@ class DockerPluginManager(object): try: # Get privileges headers = {} - registry, repo_name = auth.resolve_repository_name(self.parameters.plugin_name) + registry, repo_name = auth.resolve_repository_name( + self.parameters.plugin_name + ) header = auth.get_config_header(self.client, registry) if header: - headers['X-Registry-Auth'] = header - privileges = self.client.get_json('/plugins/privileges', params={'remote': self.parameters.plugin_name}, headers=headers) + headers["X-Registry-Auth"] = header + privileges = self.client.get_json( + "/plugins/privileges", + params={"remote": self.parameters.plugin_name}, + headers=headers, + ) # Pull plugin params = { - 'remote': self.parameters.plugin_name, + "remote": self.parameters.plugin_name, } if self.parameters.alias: - params['name'] = self.parameters.alias - response = self.client._post_json(self.client._url('/plugins/pull'), params=params, headers=headers, data=privileges, stream=True) + params["name"] = self.parameters.alias + response = self.client._post_json( + self.client._url("/plugins/pull"), + params=params, + headers=headers, + data=privileges, + stream=True, + ) self.client._raise_for_status(response) for data in self.client._stream_helper(response, decode=True): pass # Inspect and configure plugin - self.existing_plugin = self.client.get_json('/plugins/{0}/json', self.preferred_name) + self.existing_plugin = self.client.get_json( + "/plugins/{0}/json", self.preferred_name + ) if self.parameters.plugin_options: data = prepare_options(self.parameters.plugin_options) - self.client.post_json('/plugins/{0}/set', self.preferred_name, data=data) + self.client.post_json( + "/plugins/{0}/set", self.preferred_name, data=data + ) except APIError as e: self.client.fail(to_native(e)) @@ -270,7 +301,9 @@ class DockerPluginManager(object): if self.existing_plugin: if not self.check_mode: try: - self.client.delete_call('/plugins/{0}', self.preferred_name, params={'force': force}) + self.client.delete_call( + "/plugins/{0}", self.preferred_name, params={"force": force} + ) except APIError as e: self.client.fail(to_native(e)) @@ -284,7 +317,9 @@ class DockerPluginManager(object): if not self.check_mode: try: data = prepare_options(self.parameters.plugin_options) - self.client.post_json('/plugins/{0}/set', self.preferred_name, data=data) + self.client.post_json( + "/plugins/{0}/set", self.preferred_name, data=data + ) except APIError as e: self.client.fail(to_native(e)) self.actions.append(f"Updated plugin {self.preferred_name} settings") @@ -297,7 +332,9 @@ class DockerPluginManager(object): if self.existing_plugin: differences = self.has_different_config() - self.diff_tracker.add('exists', parameter=True, active=self.existing_plugin is not None) + self.diff_tracker.add( + "exists", parameter=True, active=self.existing_plugin is not None + ) if self.existing_plugin: self.update_plugin() @@ -316,10 +353,14 @@ class DockerPluginManager(object): def enable(self): timeout = self.parameters.enable_timeout if self.existing_plugin: - if not self.existing_plugin.get('Enabled'): + if not self.existing_plugin.get("Enabled"): if not self.check_mode: try: - self.client.post_json('/plugins/{0}/enable', self.preferred_name, params={'timeout': timeout}) + self.client.post_json( + "/plugins/{0}/enable", + self.preferred_name, + params={"timeout": timeout}, + ) except APIError as e: self.client.fail(to_native(e)) self.actions.append(f"Enabled plugin {self.preferred_name}") @@ -328,7 +369,11 @@ class DockerPluginManager(object): self.install_plugin() if not self.check_mode: try: - self.client.post_json('/plugins/{0}/enable', self.preferred_name, params={'timeout': timeout}) + self.client.post_json( + "/plugins/{0}/enable", + self.preferred_name, + params={"timeout": timeout}, + ) except APIError as e: self.client.fail(to_native(e)) self.actions.append(f"Enabled plugin {self.preferred_name}") @@ -336,10 +381,12 @@ class DockerPluginManager(object): def disable(self): if self.existing_plugin: - if self.existing_plugin.get('Enabled'): + if self.existing_plugin.get("Enabled"): if not self.check_mode: try: - self.client.post_json('/plugins/{0}/disable', self.preferred_name) + self.client.post_json( + "/plugins/{0}/disable", self.preferred_name + ) except APIError as e: self.client.fail(to_native(e)) self.actions.append(f"Disable plugin {self.preferred_name}") @@ -350,30 +397,36 @@ class DockerPluginManager(object): @property def result(self): plugin_data = {} - if self.parameters.state != 'absent': + if self.parameters.state != "absent": try: - plugin_data = self.client.get_json('/plugins/{0}/json', self.preferred_name) + plugin_data = self.client.get_json( + "/plugins/{0}/json", self.preferred_name + ) except NotFound: # This can happen in check mode pass result = { - 'actions': self.actions, - 'changed': self.changed, - 'diff': self.diff, - 'plugin': plugin_data, + "actions": self.actions, + "changed": self.changed, + "diff": self.diff, + "plugin": plugin_data, } return dict((k, v) for k, v in result.items() if v is not None) def main(): argument_spec = dict( - alias=dict(type='str'), - plugin_name=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['present', 'absent', 'enable', 'disable']), - plugin_options=dict(type='dict', default={}), - debug=dict(type='bool', default=False), - force_remove=dict(type='bool', default=False), - enable_timeout=dict(type='int', default=0), + alias=dict(type="str"), + plugin_name=dict(type="str", required=True), + state=dict( + type="str", + default="present", + choices=["present", "absent", "enable", "disable"], + ), + plugin_options=dict(type="dict", default={}), + debug=dict(type="bool", default=False), + force_remove=dict(type="bool", default=False), + enable_timeout=dict(type="int", default=0), ) client = AnsibleDockerClient( argument_spec=argument_spec, @@ -384,12 +437,16 @@ def main(): cm = DockerPluginManager(client) client.module.exit_json(**cm.result) except DockerException as e: - client.fail(f'An unexpected docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_prune.py b/plugins/modules/docker_prune.py index 0095709f..b8c06cae 100644 --- a/plugins/modules/docker_prune.py +++ b/plugins/modules/docker_prune.py @@ -231,117 +231,138 @@ builder_cache_caches_deleted: import traceback from ansible.module_utils.common.text.formatters import human_to_bytes - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + convert_filters, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) - -from ansible_collections.community.docker.plugins.module_utils.util import clean_dict_booleans_for_docker_api - -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import convert_filters +from ansible_collections.community.docker.plugins.module_utils.util import ( + clean_dict_booleans_for_docker_api, +) def main(): argument_spec = dict( - containers=dict(type='bool', default=False), - containers_filters=dict(type='dict'), - images=dict(type='bool', default=False), - images_filters=dict(type='dict'), - networks=dict(type='bool', default=False), - networks_filters=dict(type='dict'), - volumes=dict(type='bool', default=False), - volumes_filters=dict(type='dict'), - builder_cache=dict(type='bool', default=False), - builder_cache_all=dict(type='bool', default=False), - builder_cache_filters=dict(type='dict'), - builder_cache_keep_storage=dict(type='str'), # convert to bytes + containers=dict(type="bool", default=False), + containers_filters=dict(type="dict"), + images=dict(type="bool", default=False), + images_filters=dict(type="dict"), + networks=dict(type="bool", default=False), + networks_filters=dict(type="dict"), + volumes=dict(type="bool", default=False), + volumes_filters=dict(type="dict"), + builder_cache=dict(type="bool", default=False), + builder_cache_all=dict(type="bool", default=False), + builder_cache_filters=dict(type="dict"), + builder_cache_keep_storage=dict(type="str"), # convert to bytes ) client = AnsibleDockerClient( argument_spec=argument_spec, option_minimal_versions=dict( - builder_cache=dict(docker_py_version='1.31'), - builder_cache_all=dict(docker_py_version='1.39'), - builder_cache_filters=dict(docker_py_version='1.31'), - builder_cache_keep_storage=dict(docker_py_version='1.39'), + builder_cache=dict(docker_py_version="1.31"), + builder_cache_all=dict(docker_py_version="1.39"), + builder_cache_filters=dict(docker_py_version="1.31"), + builder_cache_keep_storage=dict(docker_py_version="1.39"), ), # supports_check_mode=True, ) builder_cache_keep_storage = None - if client.module.params.get('builder_cache_keep_storage') is not None: + if client.module.params.get("builder_cache_keep_storage") is not None: try: - builder_cache_keep_storage = human_to_bytes(client.module.params.get('builder_cache_keep_storage')) + builder_cache_keep_storage = human_to_bytes( + client.module.params.get("builder_cache_keep_storage") + ) except ValueError as exc: - client.module.fail_json(msg=f'Error while parsing value of builder_cache_keep_storage: {exc}') + client.module.fail_json( + msg=f"Error while parsing value of builder_cache_keep_storage: {exc}" + ) try: result = dict() changed = False - if client.module.params['containers']: - filters = clean_dict_booleans_for_docker_api(client.module.params.get('containers_filters'), allow_sequences=True) - params = {'filters': convert_filters(filters)} - res = client.post_to_json('/containers/prune', params=params) - result['containers'] = res.get('ContainersDeleted') or [] - result['containers_space_reclaimed'] = res['SpaceReclaimed'] - if result['containers'] or result['containers_space_reclaimed']: + if client.module.params["containers"]: + filters = clean_dict_booleans_for_docker_api( + client.module.params.get("containers_filters"), allow_sequences=True + ) + params = {"filters": convert_filters(filters)} + res = client.post_to_json("/containers/prune", params=params) + result["containers"] = res.get("ContainersDeleted") or [] + result["containers_space_reclaimed"] = res["SpaceReclaimed"] + if result["containers"] or result["containers_space_reclaimed"]: changed = True - if client.module.params['images']: - filters = clean_dict_booleans_for_docker_api(client.module.params.get('images_filters'), allow_sequences=True) - params = {'filters': convert_filters(filters)} - res = client.post_to_json('/images/prune', params=params) - result['images'] = res.get('ImagesDeleted') or [] - result['images_space_reclaimed'] = res['SpaceReclaimed'] - if result['images'] or result['images_space_reclaimed']: + if client.module.params["images"]: + filters = clean_dict_booleans_for_docker_api( + client.module.params.get("images_filters"), allow_sequences=True + ) + params = {"filters": convert_filters(filters)} + res = client.post_to_json("/images/prune", params=params) + result["images"] = res.get("ImagesDeleted") or [] + result["images_space_reclaimed"] = res["SpaceReclaimed"] + if result["images"] or result["images_space_reclaimed"]: changed = True - if client.module.params['networks']: - filters = clean_dict_booleans_for_docker_api(client.module.params.get('networks_filters'), allow_sequences=True) - params = {'filters': convert_filters(filters)} - res = client.post_to_json('/networks/prune', params=params) - result['networks'] = res.get('NetworksDeleted') or [] - if result['networks']: + if client.module.params["networks"]: + filters = clean_dict_booleans_for_docker_api( + client.module.params.get("networks_filters"), allow_sequences=True + ) + params = {"filters": convert_filters(filters)} + res = client.post_to_json("/networks/prune", params=params) + result["networks"] = res.get("NetworksDeleted") or [] + if result["networks"]: changed = True - if client.module.params['volumes']: - filters = clean_dict_booleans_for_docker_api(client.module.params.get('volumes_filters'), allow_sequences=True) - params = {'filters': convert_filters(filters)} - res = client.post_to_json('/volumes/prune', params=params) - result['volumes'] = res.get('VolumesDeleted') or [] - result['volumes_space_reclaimed'] = res['SpaceReclaimed'] - if result['volumes'] or result['volumes_space_reclaimed']: + if client.module.params["volumes"]: + filters = clean_dict_booleans_for_docker_api( + client.module.params.get("volumes_filters"), allow_sequences=True + ) + params = {"filters": convert_filters(filters)} + res = client.post_to_json("/volumes/prune", params=params) + result["volumes"] = res.get("VolumesDeleted") or [] + result["volumes_space_reclaimed"] = res["SpaceReclaimed"] + if result["volumes"] or result["volumes_space_reclaimed"]: changed = True - if client.module.params['builder_cache']: - filters = clean_dict_booleans_for_docker_api(client.module.params.get('builder_cache_filters'), allow_sequences=True) - params = {'filters': convert_filters(filters)} - if client.module.params.get('builder_cache_all'): - params['all'] = 'true' + if client.module.params["builder_cache"]: + filters = clean_dict_booleans_for_docker_api( + client.module.params.get("builder_cache_filters"), allow_sequences=True + ) + params = {"filters": convert_filters(filters)} + if client.module.params.get("builder_cache_all"): + params["all"] = "true" if builder_cache_keep_storage is not None: - params['keep-storage'] = builder_cache_keep_storage - res = client.post_to_json('/build/prune', params=params) - result['builder_cache_space_reclaimed'] = res['SpaceReclaimed'] - if result['builder_cache_space_reclaimed']: + params["keep-storage"] = builder_cache_keep_storage + res = client.post_to_json("/build/prune", params=params) + result["builder_cache_space_reclaimed"] = res["SpaceReclaimed"] + if result["builder_cache_space_reclaimed"]: changed = True - if 'CachesDeleted' in res: + if "CachesDeleted" in res: # API version 1.39+: return value CachesDeleted (list of str) - result['builder_cache_caches_deleted'] = res['CachesDeleted'] - if result['builder_cache_caches_deleted']: + result["builder_cache_caches_deleted"] = res["CachesDeleted"] + if result["builder_cache_caches_deleted"]: changed = True - result['changed'] = changed + result["changed"] = changed client.module.exit_json(**result) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_secret.py b/plugins/modules/docker_secret.py index a4481937..e8965e81 100644 --- a/plugins/modules/docker_secret.py +++ b/plugins/modules/docker_secret.py @@ -191,12 +191,14 @@ import base64 import hashlib import traceback + try: - from docker.errors import DockerException, APIError + from docker.errors import APIError, DockerException except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass +from ansible.module_utils.common.text.converters import to_bytes from ansible_collections.community.docker.plugins.module_utils.common import ( AnsibleDockerClient, RequestException, @@ -206,7 +208,6 @@ from ansible_collections.community.docker.plugins.module_utils.util import ( compare_generic, sanitize_labels, ) -from ansible.module_utils.common.text.converters import to_bytes class SecretManager(DockerBaseClass): @@ -220,25 +221,25 @@ class SecretManager(DockerBaseClass): self.check_mode = self.client.check_mode parameters = self.client.module.params - self.name = parameters.get('name') - self.state = parameters.get('state') - self.data = parameters.get('data') + self.name = parameters.get("name") + self.state = parameters.get("state") + self.data = parameters.get("data") if self.data is not None: - if parameters.get('data_is_b64'): + if parameters.get("data_is_b64"): self.data = base64.b64decode(self.data) else: self.data = to_bytes(self.data) - data_src = parameters.get('data_src') + data_src = parameters.get("data_src") if data_src is not None: try: - with open(data_src, 'rb') as f: + with open(data_src, "rb") as f: self.data = f.read() except Exception as exc: - self.client.fail(f'Error while reading {data_src}: {exc}') - self.labels = parameters.get('labels') - self.force = parameters.get('force') - self.rolling_versions = parameters.get('rolling_versions') - self.versions_to_keep = parameters.get('versions_to_keep') + self.client.fail(f"Error while reading {data_src}: {exc}") + self.labels = parameters.get("labels") + self.force = parameters.get("force") + self.rolling_versions = parameters.get("rolling_versions") + self.versions_to_keep = parameters.get("versions_to_keep") if self.rolling_versions: self.version = 0 @@ -247,16 +248,18 @@ class SecretManager(DockerBaseClass): def __call__(self): self.get_secret() - if self.state == 'present': + if self.state == "present": self.data_key = hashlib.sha224(self.data).hexdigest() self.present() self.remove_old_versions() - elif self.state == 'absent': + elif self.state == "absent": self.absent() def get_version(self, secret): try: - return int(secret.get('Spec', {}).get('Labels', {}).get('ansible_version', 0)) + return int( + secret.get("Spec", {}).get("Labels", {}).get("ansible_version", 0) + ) except ValueError: return 0 @@ -268,9 +271,9 @@ class SecretManager(DockerBaseClass): self.remove_secret(self.secrets.pop(0)) def get_secret(self): - ''' Find an existing secret. ''' + """Find an existing secret.""" try: - secrets = self.client.secrets(filters={'name': self.name}) + secrets = self.client.secrets(filters={"name": self.name}) except APIError as exc: self.client.fail(f"Error accessing secret {self.name}: {exc}") @@ -278,62 +281,66 @@ class SecretManager(DockerBaseClass): self.secrets = [ secret for secret in secrets - if secret['Spec']['Name'].startswith(f'{self.name}_v') + if secret["Spec"]["Name"].startswith(f"{self.name}_v") ] self.secrets.sort(key=self.get_version) else: self.secrets = [ - secret for secret in secrets if secret['Spec']['Name'] == self.name + secret for secret in secrets if secret["Spec"]["Name"] == self.name ] def create_secret(self): - ''' Create a new secret ''' + """Create a new secret""" secret_id = None # We cannot see the data after creation, so adding a label we can use for idempotency check - labels = { - 'ansible_key': self.data_key - } + labels = {"ansible_key": self.data_key} if self.rolling_versions: self.version += 1 - labels['ansible_version'] = str(self.version) - self.name = f'{self.name}_v{self.version}' + labels["ansible_version"] = str(self.version) + self.name = f"{self.name}_v{self.version}" if self.labels: labels.update(self.labels) try: if not self.check_mode: - secret_id = self.client.create_secret(self.name, self.data, labels=labels) - self.secrets += self.client.secrets(filters={'id': secret_id}) + secret_id = self.client.create_secret( + self.name, self.data, labels=labels + ) + self.secrets += self.client.secrets(filters={"id": secret_id}) except APIError as exc: self.client.fail(f"Error creating secret: {exc}") if isinstance(secret_id, dict): - secret_id = secret_id['ID'] + secret_id = secret_id["ID"] return secret_id def remove_secret(self, secret): try: if not self.check_mode: - self.client.remove_secret(secret['ID']) + self.client.remove_secret(secret["ID"]) except APIError as exc: self.client.fail(f"Error removing secret {secret['Spec']['Name']}: {exc}") def present(self): - ''' Handles state == 'present', creating or updating the secret ''' + """Handles state == 'present', creating or updating the secret""" if self.secrets: secret = self.secrets[-1] - self.results['secret_id'] = secret['ID'] - self.results['secret_name'] = secret['Spec']['Name'] + self.results["secret_id"] = secret["ID"] + self.results["secret_name"] = secret["Spec"]["Name"] data_changed = False - attrs = secret.get('Spec', {}) - if attrs.get('Labels', {}).get('ansible_key'): - if attrs['Labels']['ansible_key'] != self.data_key: + attrs = secret.get("Spec", {}) + if attrs.get("Labels", {}).get("ansible_key"): + if attrs["Labels"]["ansible_key"] != self.data_key: data_changed = True else: if not self.force: - self.client.module.warn("'ansible_key' label not found. Secret will not be changed unless the force parameter is set to 'true'") - labels_changed = not compare_generic(self.labels, attrs.get('Labels'), 'allow_more_present', 'dict') + self.client.module.warn( + "'ansible_key' label not found. Secret will not be changed unless the force parameter is set to 'true'" + ) + labels_changed = not compare_generic( + self.labels, attrs.get("Labels"), "allow_more_present", "dict" + ) if self.rolling_versions: self.version = self.get_version(secret) if data_changed or labels_changed or self.force: @@ -341,41 +348,41 @@ class SecretManager(DockerBaseClass): if not self.rolling_versions: self.absent() secret_id = self.create_secret() - self.results['changed'] = True - self.results['secret_id'] = secret_id - self.results['secret_name'] = self.name + self.results["changed"] = True + self.results["secret_id"] = secret_id + self.results["secret_name"] = self.name else: - self.results['changed'] = True - self.results['secret_id'] = self.create_secret() - self.results['secret_name'] = self.name + self.results["changed"] = True + self.results["secret_id"] = self.create_secret() + self.results["secret_name"] = self.name def absent(self): - ''' Handles state == 'absent', removing the secret ''' + """Handles state == 'absent', removing the secret""" if self.secrets: for secret in self.secrets: self.remove_secret(secret) - self.results['changed'] = True + self.results["changed"] = True def main(): argument_spec = dict( - name=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present']), - data=dict(type='str', no_log=True), - data_is_b64=dict(type='bool', default=False), - data_src=dict(type='path'), - labels=dict(type='dict'), - force=dict(type='bool', default=False), - rolling_versions=dict(type='bool', default=False), - versions_to_keep=dict(type='int', default=5), + name=dict(type="str", required=True), + state=dict(type="str", default="present", choices=["absent", "present"]), + data=dict(type="str", no_log=True), + data_is_b64=dict(type="bool", default=False), + data_src=dict(type="path"), + labels=dict(type="dict"), + force=dict(type="bool", default=False), + rolling_versions=dict(type="bool", default=False), + versions_to_keep=dict(type="int", default=5), ) required_if = [ - ('state', 'present', ['data', 'data_src'], True), + ("state", "present", ["data", "data_src"], True), ] mutually_exclusive = [ - ('data', 'data_src'), + ("data", "data_src"), ] client = AnsibleDockerClient( @@ -383,26 +390,26 @@ def main(): supports_check_mode=True, required_if=required_if, mutually_exclusive=mutually_exclusive, - min_docker_version='2.1.0', + min_docker_version="2.1.0", ) - sanitize_labels(client.module.params['labels'], 'labels', client) + sanitize_labels(client.module.params["labels"], "labels", client) try: - results = dict( - changed=False, - secret_id='', - secret_name='' - ) + results = dict(changed=False, secret_id="", secret_name="") SecretManager(client, results)() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_stack.py b/plugins/modules/docker_stack.py index 64765e47..6a951380 100644 --- a/plugins/modules/docker_stack.py +++ b/plugins/modules/docker_stack.py @@ -159,34 +159,37 @@ import json import os import tempfile import traceback - from time import sleep from ansible.module_utils.common.text.converters import to_native - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, ) + try: from jsondiff import diff as json_diff + HAS_JSONDIFF = True except ImportError: HAS_JSONDIFF = False try: from yaml import dump as yaml_dump + HAS_YAML = True except ImportError: HAS_YAML = False def docker_stack_services(client, stack_name): - rc, out, err = client.call_cli("stack", "services", stack_name, "--format", "{{.Name}}") + rc, out, err = client.call_cli( + "stack", "services", stack_name, "--format", "{{.Name}}" + ) if to_native(err) == f"Nothing found in stack: {stack_name}\n": return [] - return to_native(out).strip().split('\n') + return to_native(out).strip().split("\n") def docker_service_inspect(client, service_name): @@ -194,7 +197,7 @@ def docker_service_inspect(client, service_name): if rc != 0: return None else: - ret = json.loads(out)[0]['Spec'] + ret = json.loads(out)[0]["Spec"] return ret @@ -207,11 +210,9 @@ def docker_stack_deploy(client, stack_name, compose_files): if client.module.params["with_registry_auth"]: command += ["--with-registry-auth"] if client.module.params["resolve_image"]: - command += ["--resolve-image", - client.module.params["resolve_image"]] + command += ["--resolve-image", client.module.params["resolve_image"]] for compose_file in compose_files: - command += ["--compose-file", - compose_file] + command += ["--compose-file", compose_file] command += [stack_name] rc, out, err = client.call_cli(*command) return rc, to_native(out), to_native(err) @@ -240,15 +241,15 @@ def docker_stack_rm(client, stack_name, retries, interval): def main(): client = AnsibleModuleDockerClient( argument_spec={ - 'name': dict(type='str', required=True), - 'compose': dict(type='list', elements='raw', default=[]), - 'prune': dict(type='bool', default=False), - 'detach': dict(type='bool', default=True), - 'with_registry_auth': dict(type='bool', default=False), - 'resolve_image': dict(type='str', choices=['always', 'changed', 'never']), - 'state': dict(type='str', default='present', choices=['present', 'absent']), - 'absent_retries': dict(type='int', default=0), - 'absent_retries_interval': dict(type='int', default=1) + "name": dict(type="str", required=True), + "compose": dict(type="list", elements="raw", default=[]), + "prune": dict(type="bool", default=False), + "detach": dict(type="bool", default=True), + "with_registry_auth": dict(type="bool", default=False), + "resolve_image": dict(type="str", choices=["always", "changed", "never"]), + "state": dict(type="str", default="present", choices=["present", "absent"]), + "absent_retries": dict(type="int", default=0), + "absent_retries_interval": dict(type="int", default=1), }, supports_check_mode=False, ) @@ -260,28 +261,32 @@ def main(): return client.fail("yaml is not installed, try 'pip install pyyaml'") try: - state = client.module.params['state'] - compose = client.module.params['compose'] - name = client.module.params['name'] - absent_retries = client.module.params['absent_retries'] - absent_retries_interval = client.module.params['absent_retries_interval'] + state = client.module.params["state"] + compose = client.module.params["compose"] + name = client.module.params["name"] + absent_retries = client.module.params["absent_retries"] + absent_retries_interval = client.module.params["absent_retries_interval"] - if state == 'present': + if state == "present": if not compose: - client.fail("compose parameter must be a list containing at least one element") + client.fail( + "compose parameter must be a list containing at least one element" + ) compose_files = [] for i, compose_def in enumerate(compose): if isinstance(compose_def, dict): compose_file_fd, compose_file = tempfile.mkstemp() client.module.add_cleanup_file(compose_file) - with os.fdopen(compose_file_fd, 'w') as stack_file: + with os.fdopen(compose_file_fd, "w") as stack_file: compose_files.append(compose_file) stack_file.write(yaml_dump(compose_def)) elif isinstance(compose_def, str): compose_files.append(compose_def) else: - client.fail(f"compose element '{compose_def}' must be a string or a dictionary") + client.fail( + f"compose element '{compose_def}' must be a string or a dictionary" + ) before_stack_services = docker_stack_inspect(client, name) @@ -290,13 +295,20 @@ def main(): after_stack_services = docker_stack_inspect(client, name) if rc != 0: - client.fail("docker stack up deploy command failed", rc=rc, stdout=out, stderr=err) + client.fail( + "docker stack up deploy command failed", + rc=rc, + stdout=out, + stderr=err, + ) - before_after_differences = json_diff(before_stack_services, after_stack_services) + before_after_differences = json_diff( + before_stack_services, after_stack_services + ) for k in before_after_differences.keys(): if isinstance(before_after_differences[k], dict): - before_after_differences[k].pop('UpdatedAt', None) - before_after_differences[k].pop('Version', None) + before_after_differences[k].pop("UpdatedAt", None) + before_after_differences[k].pop("Version", None) if not list(before_after_differences[k].keys()): before_after_differences.pop(k) @@ -322,7 +334,9 @@ def main(): else: if docker_stack_services(client, name): - rc, out, err = docker_stack_rm(client, name, absent_retries, absent_retries_interval) + rc, out, err = docker_stack_rm( + client, name, absent_retries, absent_retries_interval + ) if rc != 0: client.module.fail_json( msg="'docker stack down' command failed", @@ -340,7 +354,10 @@ def main(): ) client.module.exit_json(changed=False) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) if __name__ == "__main__": diff --git a/plugins/modules/docker_stack_info.py b/plugins/modules/docker_stack_info.py index 72dbd9c0..174e8a23 100644 --- a/plugins/modules/docker_stack_info.py +++ b/plugins/modules/docker_stack_info.py @@ -7,6 +7,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_stack_info author: "Jose Angel Munoz (@imjoseangel)" @@ -78,7 +79,6 @@ import json import traceback from ansible.module_utils.common.text.converters import to_native - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, @@ -86,31 +86,36 @@ from ansible_collections.community.docker.plugins.module_utils.common_cli import def docker_stack_list(module): - docker_bin = module.get_bin_path('docker', required=True) + docker_bin = module.get_bin_path("docker", required=True) rc, out, err = module.run_command( - [docker_bin, "stack", "ls", "--format={{json .}}"]) + [docker_bin, "stack", "ls", "--format={{json .}}"] + ) return rc, out.strip(), err.strip() def main(): client = AnsibleModuleDockerClient( - argument_spec={ - }, + argument_spec={}, supports_check_mode=True, ) try: - rc, ret, stderr = client.call_cli_json_stream('stack', 'ls', '--format={{json .}}', check_rc=True) + rc, ret, stderr = client.call_cli_json_stream( + "stack", "ls", "--format={{json .}}", check_rc=True + ) client.module.exit_json( changed=False, rc=rc, - stdout='\n'.join([json.dumps(entry) for entry in ret]), + stdout="\n".join([json.dumps(entry) for entry in ret]), stderr=to_native(stderr).strip(), results=ret, ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) if __name__ == "__main__": diff --git a/plugins/modules/docker_stack_task_info.py b/plugins/modules/docker_stack_task_info.py index 1269c235..ba89dc14 100644 --- a/plugins/modules/docker_stack_task_info.py +++ b/plugins/modules/docker_stack_task_info.py @@ -7,6 +7,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_stack_task_info author: "Jose Angel Munoz (@imjoseangel)" @@ -86,7 +87,6 @@ import json import traceback from ansible.module_utils.common.text.converters import to_native - from ansible_collections.community.docker.plugins.module_utils.common_cli import ( AnsibleModuleDockerClient, DockerException, @@ -94,33 +94,37 @@ from ansible_collections.community.docker.plugins.module_utils.common_cli import def docker_stack_task(module, stack_name): - docker_bin = module.get_bin_path('docker', required=True) + docker_bin = module.get_bin_path("docker", required=True) rc, out, err = module.run_command( - [docker_bin, "stack", "ps", stack_name, "--format={{json .}}"]) + [docker_bin, "stack", "ps", stack_name, "--format={{json .}}"] + ) return rc, out.strip(), err.strip() def main(): client = AnsibleModuleDockerClient( - argument_spec={ - 'name': dict(type='str', required=True) - }, + argument_spec={"name": dict(type="str", required=True)}, supports_check_mode=True, ) try: - name = client.module.params['name'] - rc, ret, stderr = client.call_cli_json_stream('stack', 'ps', name, '--format={{json .}}', check_rc=True) + name = client.module.params["name"] + rc, ret, stderr = client.call_cli_json_stream( + "stack", "ps", name, "--format={{json .}}", check_rc=True + ) client.module.exit_json( changed=False, rc=rc, - stdout='\n'.join([json.dumps(entry) for entry in ret]), + stdout="\n".join([json.dumps(entry) for entry in ret]), stderr=to_native(stderr).strip(), results=ret, ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) if __name__ == "__main__": diff --git a/plugins/modules/docker_swarm.py b/plugins/modules/docker_swarm.py index a20987a1..d9d560bf 100644 --- a/plugins/modules/docker_swarm.py +++ b/plugins/modules/docker_swarm.py @@ -6,6 +6,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_swarm short_description: Manage Swarm cluster @@ -292,8 +293,9 @@ actions: import json import traceback + try: - from docker.errors import DockerException, APIError + from docker.errors import APIError, DockerException except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass @@ -302,13 +304,14 @@ from ansible_collections.community.docker.plugins.module_utils.common import ( DockerBaseClass, RequestException, ) +from ansible_collections.community.docker.plugins.module_utils.swarm import ( + AnsibleDockerSwarmClient, +) from ansible_collections.community.docker.plugins.module_utils.util import ( DifferenceTracker, sanitize_labels, ) -from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient - class TaskParameters(DockerBaseClass): def __init__(self): @@ -353,68 +356,70 @@ class TaskParameters(DockerBaseClass): return result def update_from_swarm_info(self, swarm_info): - spec = swarm_info['Spec'] + spec = swarm_info["Spec"] - ca_config = spec.get('CAConfig') or dict() + ca_config = spec.get("CAConfig") or dict() if self.node_cert_expiry is None: - self.node_cert_expiry = ca_config.get('NodeCertExpiry') + self.node_cert_expiry = ca_config.get("NodeCertExpiry") if self.ca_force_rotate is None: - self.ca_force_rotate = ca_config.get('ForceRotate') + self.ca_force_rotate = ca_config.get("ForceRotate") - dispatcher = spec.get('Dispatcher') or dict() + dispatcher = spec.get("Dispatcher") or dict() if self.dispatcher_heartbeat_period is None: - self.dispatcher_heartbeat_period = dispatcher.get('HeartbeatPeriod') + self.dispatcher_heartbeat_period = dispatcher.get("HeartbeatPeriod") - raft = spec.get('Raft') or dict() + raft = spec.get("Raft") or dict() if self.snapshot_interval is None: - self.snapshot_interval = raft.get('SnapshotInterval') + self.snapshot_interval = raft.get("SnapshotInterval") if self.keep_old_snapshots is None: - self.keep_old_snapshots = raft.get('KeepOldSnapshots') + self.keep_old_snapshots = raft.get("KeepOldSnapshots") if self.heartbeat_tick is None: - self.heartbeat_tick = raft.get('HeartbeatTick') + self.heartbeat_tick = raft.get("HeartbeatTick") if self.log_entries_for_slow_followers is None: - self.log_entries_for_slow_followers = raft.get('LogEntriesForSlowFollowers') + self.log_entries_for_slow_followers = raft.get("LogEntriesForSlowFollowers") if self.election_tick is None: - self.election_tick = raft.get('ElectionTick') + self.election_tick = raft.get("ElectionTick") - orchestration = spec.get('Orchestration') or dict() + orchestration = spec.get("Orchestration") or dict() if self.task_history_retention_limit is None: - self.task_history_retention_limit = orchestration.get('TaskHistoryRetentionLimit') + self.task_history_retention_limit = orchestration.get( + "TaskHistoryRetentionLimit" + ) - encryption_config = spec.get('EncryptionConfig') or dict() + encryption_config = spec.get("EncryptionConfig") or dict() if self.autolock_managers is None: - self.autolock_managers = encryption_config.get('AutoLockManagers') + self.autolock_managers = encryption_config.get("AutoLockManagers") if self.name is None: - self.name = spec['Name'] + self.name = spec["Name"] if self.labels is None: - self.labels = spec.get('Labels') or {} + self.labels = spec.get("Labels") or {} - if 'LogDriver' in spec['TaskDefaults']: - self.log_driver = spec['TaskDefaults']['LogDriver'] + if "LogDriver" in spec["TaskDefaults"]: + self.log_driver = spec["TaskDefaults"]["LogDriver"] def update_parameters(self, client): assign = dict( - snapshot_interval='snapshot_interval', - task_history_retention_limit='task_history_retention_limit', - keep_old_snapshots='keep_old_snapshots', - log_entries_for_slow_followers='log_entries_for_slow_followers', - heartbeat_tick='heartbeat_tick', - election_tick='election_tick', - dispatcher_heartbeat_period='dispatcher_heartbeat_period', - node_cert_expiry='node_cert_expiry', - name='name', - labels='labels', - signing_ca_cert='signing_ca_cert', - signing_ca_key='signing_ca_key', - ca_force_rotate='ca_force_rotate', - autolock_managers='autolock_managers', - log_driver='log_driver', + snapshot_interval="snapshot_interval", + task_history_retention_limit="task_history_retention_limit", + keep_old_snapshots="keep_old_snapshots", + log_entries_for_slow_followers="log_entries_for_slow_followers", + heartbeat_tick="heartbeat_tick", + election_tick="election_tick", + dispatcher_heartbeat_period="dispatcher_heartbeat_period", + node_cert_expiry="node_cert_expiry", + name="name", + labels="labels", + signing_ca_cert="signing_ca_cert", + signing_ca_key="signing_ca_key", + ca_force_rotate="ca_force_rotate", + autolock_managers="autolock_managers", + log_driver="log_driver", ) params = dict() for dest, source in assign.items(): - if not client.option_minimal_versions[source]['supported']: + if not client.option_minimal_versions[source]["supported"]: continue value = getattr(self, source) if value is not None: @@ -423,12 +428,21 @@ class TaskParameters(DockerBaseClass): def compare_to_active(self, other, client, differences): for k in self.__dict__: - if k in ('advertise_addr', 'listen_addr', 'remote_addrs', 'join_token', - 'rotate_worker_token', 'rotate_manager_token', 'spec', - 'default_addr_pool', 'subnet_size', 'data_path_addr', - 'data_path_port'): + if k in ( + "advertise_addr", + "listen_addr", + "remote_addrs", + "join_token", + "rotate_worker_token", + "rotate_manager_token", + "spec", + "default_addr_pool", + "subnet_size", + "data_path_addr", + "data_path_port", + ): continue - if not client.option_minimal_versions[k]['supported']: + if not client.option_minimal_versions[k]["supported"]: continue value = getattr(self, k) if value is None: @@ -437,9 +451,9 @@ class TaskParameters(DockerBaseClass): if value != other_value: differences.add(k, parameter=value, active=other_value) if self.rotate_worker_token: - differences.add('rotate_worker_token', parameter=True, active=False) + differences.add("rotate_worker_token", parameter=True, active=False) if self.rotate_manager_token: - differences.add('rotate_manager_token', parameter=True, active=False) + differences.add("rotate_manager_token", parameter=True, active=False) return differences @@ -454,9 +468,9 @@ class SwarmManager(DockerBaseClass): self.check_mode = self.client.check_mode self.swarm_info = {} - self.state = client.module.params['state'] - self.force = client.module.params['force'] - self.node_id = client.module.params['node_id'] + self.state = client.module.params["state"] + self.force = client.module.params["force"] + self.node_id = client.module.params["node_id"] self.differences = DifferenceTracker() self.parameters = TaskParameters.from_ansible_params(client) @@ -475,8 +489,8 @@ class SwarmManager(DockerBaseClass): if self.client.module._diff or self.parameters.debug: diff = dict() - diff['before'], diff['after'] = self.differences.get_before_after() - self.results['diff'] = diff + diff["before"], diff["after"] = self.differences.get_before_after() + self.results["diff"] = diff def inspect_swarm(self): try: @@ -484,8 +498,8 @@ class SwarmManager(DockerBaseClass): json_str = json.dumps(data, ensure_ascii=False) self.swarm_info = json.loads(json_str) - self.results['changed'] = False - self.results['swarm_facts'] = self.swarm_info + self.results["changed"] = False + self.results["swarm_facts"] = self.swarm_info unlock_key = self.get_unlock_key() self.swarm_info.update(unlock_key) @@ -493,7 +507,7 @@ class SwarmManager(DockerBaseClass): return def get_unlock_key(self): - default = {'UnlockKey': None} + default = {"UnlockKey": None} if not self.has_swarm_lock_changed(): return default try: @@ -503,7 +517,7 @@ class SwarmManager(DockerBaseClass): def has_swarm_lock_changed(self): return self.parameters.autolock_managers and ( - self.created or self.differences.has_difference_for('autolock_managers') + self.created or self.differences.has_difference_for("autolock_managers") ) def init_swarm(self): @@ -513,19 +527,19 @@ class SwarmManager(DockerBaseClass): if not self.check_mode: init_arguments = { - 'advertise_addr': self.parameters.advertise_addr, - 'listen_addr': self.parameters.listen_addr, - 'force_new_cluster': self.force, - 'swarm_spec': self.parameters.spec, + "advertise_addr": self.parameters.advertise_addr, + "listen_addr": self.parameters.listen_addr, + "force_new_cluster": self.force, + "swarm_spec": self.parameters.spec, } if self.parameters.default_addr_pool is not None: - init_arguments['default_addr_pool'] = self.parameters.default_addr_pool + init_arguments["default_addr_pool"] = self.parameters.default_addr_pool if self.parameters.subnet_size is not None: - init_arguments['subnet_size'] = self.parameters.subnet_size + init_arguments["subnet_size"] = self.parameters.subnet_size if self.parameters.data_path_addr is not None: - init_arguments['data_path_addr'] = self.parameters.data_path_addr + init_arguments["data_path_addr"] = self.parameters.data_path_addr if self.parameters.data_path_port is not None: - init_arguments['data_path_port'] = self.parameters.data_path_port + init_arguments["data_path_port"] = self.parameters.data_path_port try: self.client.init_swarm(**init_arguments) except APIError as exc: @@ -537,180 +551,196 @@ class SwarmManager(DockerBaseClass): self.created = True self.inspect_swarm() - self.results['actions'].append(f"New Swarm cluster created: {self.swarm_info.get('ID')}") - self.differences.add('state', parameter='present', active='absent') - self.results['changed'] = True - self.results['swarm_facts'] = { - 'JoinTokens': self.swarm_info.get('JoinTokens'), - 'UnlockKey': self.swarm_info.get('UnlockKey') + self.results["actions"].append( + f"New Swarm cluster created: {self.swarm_info.get('ID')}" + ) + self.differences.add("state", parameter="present", active="absent") + self.results["changed"] = True + self.results["swarm_facts"] = { + "JoinTokens": self.swarm_info.get("JoinTokens"), + "UnlockKey": self.swarm_info.get("UnlockKey"), } def __update_swarm(self): try: self.inspect_swarm() - version = self.swarm_info['Version']['Index'] + version = self.swarm_info["Version"]["Index"] self.parameters.update_from_swarm_info(self.swarm_info) old_parameters = TaskParameters() old_parameters.update_from_swarm_info(self.swarm_info) - self.parameters.compare_to_active(old_parameters, self.client, self.differences) + self.parameters.compare_to_active( + old_parameters, self.client, self.differences + ) if self.differences.empty: - self.results['actions'].append("No modification") - self.results['changed'] = False + self.results["actions"].append("No modification") + self.results["changed"] = False return update_parameters = TaskParameters.from_ansible_params(self.client) update_parameters.update_parameters(self.client) if not self.check_mode: self.client.update_swarm( - version=version, swarm_spec=update_parameters.spec, + version=version, + swarm_spec=update_parameters.spec, rotate_worker_token=self.parameters.rotate_worker_token, - rotate_manager_token=self.parameters.rotate_manager_token) + rotate_manager_token=self.parameters.rotate_manager_token, + ) except APIError as exc: self.client.fail(f"Can not update a Swarm Cluster: {exc}") return self.inspect_swarm() - self.results['actions'].append("Swarm cluster updated") - self.results['changed'] = True + self.results["actions"].append("Swarm cluster updated") + self.results["changed"] = True def join(self): if self.client.check_if_swarm_node(): - self.results['actions'].append("This node is already part of a swarm.") + self.results["actions"].append("This node is already part of a swarm.") return if not self.check_mode: join_arguments = { - 'remote_addrs': self.parameters.remote_addrs, - 'join_token': self.parameters.join_token, - 'listen_addr': self.parameters.listen_addr, - 'advertise_addr': self.parameters.advertise_addr, + "remote_addrs": self.parameters.remote_addrs, + "join_token": self.parameters.join_token, + "listen_addr": self.parameters.listen_addr, + "advertise_addr": self.parameters.advertise_addr, } if self.parameters.data_path_addr is not None: - join_arguments['data_path_addr'] = self.parameters.data_path_addr + join_arguments["data_path_addr"] = self.parameters.data_path_addr try: self.client.join_swarm(**join_arguments) except APIError as exc: self.client.fail(f"Can not join the Swarm Cluster: {exc}") - self.results['actions'].append("New node is added to swarm cluster") - self.differences.add('joined', parameter=True, active=False) - self.results['changed'] = True + self.results["actions"].append("New node is added to swarm cluster") + self.differences.add("joined", parameter=True, active=False) + self.results["changed"] = True def leave(self): if not self.client.check_if_swarm_node(): - self.results['actions'].append("This node is not part of a swarm.") + self.results["actions"].append("This node is not part of a swarm.") return if not self.check_mode: try: self.client.leave_swarm(force=self.force) except APIError as exc: self.client.fail(f"This node can not leave the Swarm Cluster: {exc}") - self.results['actions'].append("Node has left the swarm cluster") - self.differences.add('joined', parameter='absent', active='present') - self.results['changed'] = True + self.results["actions"].append("Node has left the swarm cluster") + self.differences.add("joined", parameter="absent", active="present") + self.results["changed"] = True def remove(self): if not self.client.check_if_swarm_manager(): self.client.fail("This node is not a manager.") try: - status_down = self.client.check_if_swarm_node_is_down(node_id=self.node_id, repeat_check=5) + status_down = self.client.check_if_swarm_node_is_down( + node_id=self.node_id, repeat_check=5 + ) except APIError: return if not status_down: - self.client.fail("Can not remove the node. The status node is ready and not down.") + self.client.fail( + "Can not remove the node. The status node is ready and not down." + ) if not self.check_mode: try: self.client.remove_node(node_id=self.node_id, force=self.force) except APIError as exc: - self.client.fail(f"Can not remove the node from the Swarm Cluster: {exc}") - self.results['actions'].append("Node is removed from swarm cluster.") - self.differences.add('joined', parameter=False, active=True) - self.results['changed'] = True + self.client.fail( + f"Can not remove the node from the Swarm Cluster: {exc}" + ) + self.results["actions"].append("Node is removed from swarm cluster.") + self.differences.add("joined", parameter=False, active=True) + self.results["changed"] = True def _detect_remove_operation(client): - return client.module.params['state'] == 'remove' + return client.module.params["state"] == "remove" def main(): argument_spec = dict( - advertise_addr=dict(type='str'), - data_path_addr=dict(type='str'), - data_path_port=dict(type='int'), - state=dict(type='str', default='present', choices=['present', 'join', 'absent', 'remove']), - force=dict(type='bool', default=False), - listen_addr=dict(type='str', default='0.0.0.0:2377'), - remote_addrs=dict(type='list', elements='str'), - join_token=dict(type='str', no_log=True), - snapshot_interval=dict(type='int'), - task_history_retention_limit=dict(type='int'), - keep_old_snapshots=dict(type='int'), - log_entries_for_slow_followers=dict(type='int'), - heartbeat_tick=dict(type='int'), - election_tick=dict(type='int'), - dispatcher_heartbeat_period=dict(type='int'), - node_cert_expiry=dict(type='int'), - name=dict(type='str'), - labels=dict(type='dict'), - signing_ca_cert=dict(type='str'), - signing_ca_key=dict(type='str', no_log=True), - ca_force_rotate=dict(type='int'), - autolock_managers=dict(type='bool'), - node_id=dict(type='str'), - rotate_worker_token=dict(type='bool', default=False), - rotate_manager_token=dict(type='bool', default=False), - default_addr_pool=dict(type='list', elements='str'), - subnet_size=dict(type='int'), + advertise_addr=dict(type="str"), + data_path_addr=dict(type="str"), + data_path_port=dict(type="int"), + state=dict( + type="str", + default="present", + choices=["present", "join", "absent", "remove"], + ), + force=dict(type="bool", default=False), + listen_addr=dict(type="str", default="0.0.0.0:2377"), + remote_addrs=dict(type="list", elements="str"), + join_token=dict(type="str", no_log=True), + snapshot_interval=dict(type="int"), + task_history_retention_limit=dict(type="int"), + keep_old_snapshots=dict(type="int"), + log_entries_for_slow_followers=dict(type="int"), + heartbeat_tick=dict(type="int"), + election_tick=dict(type="int"), + dispatcher_heartbeat_period=dict(type="int"), + node_cert_expiry=dict(type="int"), + name=dict(type="str"), + labels=dict(type="dict"), + signing_ca_cert=dict(type="str"), + signing_ca_key=dict(type="str", no_log=True), + ca_force_rotate=dict(type="int"), + autolock_managers=dict(type="bool"), + node_id=dict(type="str"), + rotate_worker_token=dict(type="bool", default=False), + rotate_manager_token=dict(type="bool", default=False), + default_addr_pool=dict(type="list", elements="str"), + subnet_size=dict(type="int"), ) required_if = [ - ('state', 'join', ['remote_addrs', 'join_token']), - ('state', 'remove', ['node_id']) + ("state", "join", ["remote_addrs", "join_token"]), + ("state", "remove", ["node_id"]), ] option_minimal_versions = dict( - labels=dict(docker_py_version='2.6.0', docker_api_version='1.32'), - signing_ca_cert=dict(docker_py_version='2.6.0', docker_api_version='1.30'), - signing_ca_key=dict(docker_py_version='2.6.0', docker_api_version='1.30'), - ca_force_rotate=dict(docker_py_version='2.6.0', docker_api_version='1.30'), - autolock_managers=dict(docker_py_version='2.6.0'), - log_driver=dict(docker_py_version='2.6.0'), + labels=dict(docker_py_version="2.6.0", docker_api_version="1.32"), + signing_ca_cert=dict(docker_py_version="2.6.0", docker_api_version="1.30"), + signing_ca_key=dict(docker_py_version="2.6.0", docker_api_version="1.30"), + ca_force_rotate=dict(docker_py_version="2.6.0", docker_api_version="1.30"), + autolock_managers=dict(docker_py_version="2.6.0"), + log_driver=dict(docker_py_version="2.6.0"), remove_operation=dict( - docker_py_version='2.4.0', + docker_py_version="2.4.0", detect_usage=_detect_remove_operation, - usage_msg='remove swarm nodes' + usage_msg="remove swarm nodes", ), - default_addr_pool=dict(docker_py_version='4.0.0', docker_api_version='1.39'), - subnet_size=dict(docker_py_version='4.0.0', docker_api_version='1.39'), - data_path_addr=dict(docker_py_version='4.0.0', docker_api_version='1.30'), - data_path_port=dict(docker_py_version='6.0.0', docker_api_version='1.40'), + default_addr_pool=dict(docker_py_version="4.0.0", docker_api_version="1.39"), + subnet_size=dict(docker_py_version="4.0.0", docker_api_version="1.39"), + data_path_addr=dict(docker_py_version="4.0.0", docker_api_version="1.30"), + data_path_port=dict(docker_py_version="6.0.0", docker_api_version="1.40"), ) client = AnsibleDockerSwarmClient( argument_spec=argument_spec, supports_check_mode=True, required_if=required_if, - min_docker_version='1.10.0', + min_docker_version="1.10.0", option_minimal_versions=option_minimal_versions, ) - sanitize_labels(client.module.params['labels'], 'labels', client) + sanitize_labels(client.module.params["labels"], "labels", client) try: - results = dict( - changed=False, - result='', - actions=[] - ) + results = dict(changed=False, result="", actions=[]) SwarmManager(client, results)() client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_swarm_info.py b/plugins/modules/docker_swarm_info.py index c57aa3d7..83df531a 100644 --- a/plugins/modules/docker_swarm_info.py +++ b/plugins/modules/docker_swarm_info.py @@ -6,6 +6,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_swarm_info @@ -186,14 +187,19 @@ tasks: import traceback + try: - from docker.errors import DockerException, APIError + from docker.errors import APIError, DockerException except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker_common pass -from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient -from ansible_collections.community.docker.plugins.module_utils.common import RequestException +from ansible_collections.community.docker.plugins.module_utils.common import ( + RequestException, +) +from ansible_collections.community.docker.plugins.module_utils.swarm import ( + AnsibleDockerSwarmClient, +) from ansible_collections.community.docker.plugins.module_utils.util import ( DockerBaseClass, clean_dict_booleans_for_docker_api, @@ -208,22 +214,26 @@ class DockerSwarmManager(DockerBaseClass): self.client = client self.results = results - self.verbose_output = self.client.module.params['verbose_output'] + self.verbose_output = self.client.module.params["verbose_output"] - listed_objects = ['tasks', 'services', 'nodes'] + listed_objects = ["tasks", "services", "nodes"] self.client.fail_task_if_not_swarm_manager() - self.results['swarm_facts'] = self.get_docker_swarm_facts() + self.results["swarm_facts"] = self.get_docker_swarm_facts() for docker_object in listed_objects: if self.client.module.params[docker_object]: returned_name = docker_object filter_name = docker_object + "_filters" - filters = clean_dict_booleans_for_docker_api(client.module.params.get(filter_name)) - self.results[returned_name] = self.get_docker_items_list(docker_object, filters) - if self.client.module.params['unlock_key']: - self.results['swarm_unlock_key'] = self.get_docker_swarm_unlock_key() + filters = clean_dict_booleans_for_docker_api( + client.module.params.get(filter_name) + ) + self.results[returned_name] = self.get_docker_items_list( + docker_object, filters + ) + if self.client.module.params["unlock_key"]: + self.results["swarm_unlock_key"] = self.get_docker_swarm_unlock_key() def get_docker_swarm_facts(self): try: @@ -236,14 +246,16 @@ class DockerSwarmManager(DockerBaseClass): items_list = [] try: - if docker_object == 'nodes': + if docker_object == "nodes": items = self.client.nodes(filters=filters) - elif docker_object == 'tasks': + elif docker_object == "tasks": items = self.client.tasks(filters=filters) - elif docker_object == 'services': + elif docker_object == "services": items = self.client.services(filters=filters) except APIError as exc: - self.client.fail(f"Error inspecting docker swarm for object '{docker_object}': {exc}") + self.client.fail( + f"Error inspecting docker swarm for object '{docker_object}': {exc}" + ) if self.verbose_output: return items @@ -251,14 +263,14 @@ class DockerSwarmManager(DockerBaseClass): for item in items: item_record = dict() - if docker_object == 'nodes': + if docker_object == "nodes": item_record = self.get_essential_facts_nodes(item) - elif docker_object == 'tasks': + elif docker_object == "tasks": item_record = self.get_essential_facts_tasks(item) - elif docker_object == 'services': + elif docker_object == "services": item_record = self.get_essential_facts_services(item) - if item_record.get('Mode') == 'Global': - item_record['Replicas'] = len(items) + if item_record.get("Mode") == "Global": + item_record["Replicas"] = len(items) items_list.append(item_record) return items_list @@ -267,35 +279,42 @@ class DockerSwarmManager(DockerBaseClass): def get_essential_facts_nodes(item): object_essentials = dict() - object_essentials['ID'] = item.get('ID') - object_essentials['Hostname'] = item['Description']['Hostname'] - object_essentials['Status'] = item['Status']['State'] - object_essentials['Availability'] = item['Spec']['Availability'] - if 'ManagerStatus' in item: - object_essentials['ManagerStatus'] = item['ManagerStatus']['Reachability'] - if 'Leader' in item['ManagerStatus'] and item['ManagerStatus']['Leader'] is True: - object_essentials['ManagerStatus'] = "Leader" + object_essentials["ID"] = item.get("ID") + object_essentials["Hostname"] = item["Description"]["Hostname"] + object_essentials["Status"] = item["Status"]["State"] + object_essentials["Availability"] = item["Spec"]["Availability"] + if "ManagerStatus" in item: + object_essentials["ManagerStatus"] = item["ManagerStatus"]["Reachability"] + if ( + "Leader" in item["ManagerStatus"] + and item["ManagerStatus"]["Leader"] is True + ): + object_essentials["ManagerStatus"] = "Leader" else: - object_essentials['ManagerStatus'] = None - object_essentials['EngineVersion'] = item['Description']['Engine']['EngineVersion'] + object_essentials["ManagerStatus"] = None + object_essentials["EngineVersion"] = item["Description"]["Engine"][ + "EngineVersion" + ] return object_essentials def get_essential_facts_tasks(self, item): object_essentials = dict() - object_essentials['ID'] = item['ID'] + object_essentials["ID"] = item["ID"] # Returning container ID to not trigger another connection to host # Container ID is sufficient to get extended info in other tasks - object_essentials['ContainerID'] = item['Status']['ContainerStatus']['ContainerID'] - object_essentials['Image'] = item['Spec']['ContainerSpec']['Image'] - object_essentials['Node'] = self.client.get_node_name_by_id(item['NodeID']) - object_essentials['DesiredState'] = item['DesiredState'] - object_essentials['CurrentState'] = item['Status']['State'] - if 'Err' in item['Status']: - object_essentials['Error'] = item['Status']['Err'] + object_essentials["ContainerID"] = item["Status"]["ContainerStatus"][ + "ContainerID" + ] + object_essentials["Image"] = item["Spec"]["ContainerSpec"]["Image"] + object_essentials["Node"] = self.client.get_node_name_by_id(item["NodeID"]) + object_essentials["DesiredState"] = item["DesiredState"] + object_essentials["CurrentState"] = item["Status"]["State"] + if "Err" in item["Status"]: + object_essentials["Error"] = item["Status"]["Err"] else: - object_essentials['Error'] = None + object_essentials["Error"] = None return object_essentials @@ -303,47 +322,51 @@ class DockerSwarmManager(DockerBaseClass): def get_essential_facts_services(item): object_essentials = dict() - object_essentials['ID'] = item['ID'] - object_essentials['Name'] = item['Spec']['Name'] - if 'Replicated' in item['Spec']['Mode']: - object_essentials['Mode'] = "Replicated" - object_essentials['Replicas'] = item['Spec']['Mode']['Replicated']['Replicas'] - elif 'Global' in item['Spec']['Mode']: - object_essentials['Mode'] = "Global" + object_essentials["ID"] = item["ID"] + object_essentials["Name"] = item["Spec"]["Name"] + if "Replicated" in item["Spec"]["Mode"]: + object_essentials["Mode"] = "Replicated" + object_essentials["Replicas"] = item["Spec"]["Mode"]["Replicated"][ + "Replicas" + ] + elif "Global" in item["Spec"]["Mode"]: + object_essentials["Mode"] = "Global" # Number of replicas have to be updated in calling method or may be left as None - object_essentials['Replicas'] = None - object_essentials['Image'] = item['Spec']['TaskTemplate']['ContainerSpec']['Image'] - if item['Spec'].get('EndpointSpec') and 'Ports' in item['Spec']['EndpointSpec']: - object_essentials['Ports'] = item['Spec']['EndpointSpec']['Ports'] + object_essentials["Replicas"] = None + object_essentials["Image"] = item["Spec"]["TaskTemplate"]["ContainerSpec"][ + "Image" + ] + if item["Spec"].get("EndpointSpec") and "Ports" in item["Spec"]["EndpointSpec"]: + object_essentials["Ports"] = item["Spec"]["EndpointSpec"]["Ports"] else: - object_essentials['Ports'] = [] + object_essentials["Ports"] = [] return object_essentials def get_docker_swarm_unlock_key(self): unlock_key = self.client.get_unlock_key() or {} - return unlock_key.get('UnlockKey') or None + return unlock_key.get("UnlockKey") or None def main(): argument_spec = dict( - nodes=dict(type='bool', default=False), - nodes_filters=dict(type='dict'), - tasks=dict(type='bool', default=False), - tasks_filters=dict(type='dict'), - services=dict(type='bool', default=False), - services_filters=dict(type='dict'), - unlock_key=dict(type='bool', default=False), - verbose_output=dict(type='bool', default=False), + nodes=dict(type="bool", default=False), + nodes_filters=dict(type="dict"), + tasks=dict(type="bool", default=False), + tasks_filters=dict(type="dict"), + services=dict(type="bool", default=False), + services_filters=dict(type="dict"), + unlock_key=dict(type="bool", default=False), + verbose_output=dict(type="bool", default=False), ) option_minimal_versions = dict( - unlock_key=dict(docker_py_version='2.7.0'), + unlock_key=dict(docker_py_version="2.7.0"), ) client = AnsibleDockerSwarmClient( argument_spec=argument_spec, supports_check_mode=True, - min_docker_version='1.10.0', + min_docker_version="1.10.0", option_minimal_versions=option_minimal_versions, fail_results=dict( can_talk_to_docker=False, @@ -351,9 +374,9 @@ def main(): docker_swarm_manager=False, ), ) - client.fail_results['can_talk_to_docker'] = True - client.fail_results['docker_swarm_active'] = client.check_if_swarm_node() - client.fail_results['docker_swarm_manager'] = client.check_if_swarm_manager() + client.fail_results["can_talk_to_docker"] = True + client.fail_results["docker_swarm_active"] = client.check_if_swarm_node() + client.fail_results["docker_swarm_manager"] = client.check_if_swarm_manager() try: results = dict( @@ -364,12 +387,16 @@ def main(): results.update(client.fail_results) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_swarm_service.py b/plugins/modules/docker_swarm_service.py index c9b443fb..c4920bf2 100644 --- a/plugins/modules/docker_swarm_service.py +++ b/plugins/modules/docker_swarm_service.py @@ -6,6 +6,7 @@ from __future__ import annotations + DOCUMENTATION = r""" module: docker_swarm_service author: @@ -853,8 +854,8 @@ import shlex import time import traceback -from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion - +from ansible.module_utils.basic import human_to_bytes +from ansible.module_utils.common.text.converters import to_text from ansible_collections.community.docker.plugins.module_utils.common import ( AnsibleDockerClient, RequestException, @@ -862,27 +863,28 @@ from ansible_collections.community.docker.plugins.module_utils.common import ( from ansible_collections.community.docker.plugins.module_utils.util import ( DifferenceTracker, DockerBaseClass, + clean_dict_booleans_for_docker_api, convert_duration_to_nanosecond, parse_healthcheck, - clean_dict_booleans_for_docker_api, sanitize_labels, ) +from ansible_collections.community.docker.plugins.module_utils.version import ( + LooseVersion, +) -from ansible.module_utils.basic import human_to_bytes -from ansible.module_utils.common.text.converters import to_text try: from docker import types - from docker.utils import ( - parse_repository_tag, - parse_env_file, - format_environment, - ) from docker.errors import ( APIError, DockerException, NotFound, ) + from docker.utils import ( + format_environment, + parse_env_file, + parse_repository_tag, + ) except ImportError: # missing Docker SDK for Python handled in ansible.module_utils.docker.common pass @@ -903,25 +905,27 @@ def get_docker_environment(env, env_files): for name, value in parsed_env_file.items(): env_dict[name] = str(value) if env is not None and isinstance(env, str): - env = env.split(',') + env = env.split(",") if env is not None and isinstance(env, dict): for name, value in env.items(): if not isinstance(value, str): raise ValueError( - 'Non-string value found for env option. ' - f'Ambiguous env options must be wrapped in quotes to avoid YAML parsing. Key: {name}' + "Non-string value found for env option. " + f"Ambiguous env options must be wrapped in quotes to avoid YAML parsing. Key: {name}" ) env_dict[name] = str(value) elif env is not None and isinstance(env, list): for item in env: try: - name, value = item.split('=', 1) + name, value = item.split("=", 1) except ValueError: - raise ValueError('Invalid environment variable found in list, needs to be in format KEY=VALUE.') + raise ValueError( + "Invalid environment variable found in list, needs to be in format KEY=VALUE." + ) env_dict[name] = value elif env is not None: raise ValueError( - f'Invalid type for env {env} ({type(env)}). Only list or dict allowed.' + f"Invalid type for env {env} ({type(env)}). Only list or dict allowed." ) env_list = format_environment(env_dict) if not env_list: @@ -942,44 +946,44 @@ def get_docker_networks(networks, network_ids): parsed_networks = [] for network in networks: if isinstance(network, str): - parsed_network = {'name': network} + parsed_network = {"name": network} elif isinstance(network, dict): - if 'name' not in network: + if "name" not in network: raise TypeError( '"name" is required when networks are passed as dictionaries.' ) - name = network.pop('name') - parsed_network = {'name': name} - aliases = network.pop('aliases', None) + name = network.pop("name") + parsed_network = {"name": name} + aliases = network.pop("aliases", None) if aliases is not None: if not isinstance(aliases, list): - raise TypeError('"aliases" network option is only allowed as a list') - if not all( - isinstance(alias, str) for alias in aliases - ): - raise TypeError('Only strings are allowed as network aliases.') - parsed_network['aliases'] = aliases - options = network.pop('options', None) + raise TypeError( + '"aliases" network option is only allowed as a list' + ) + if not all(isinstance(alias, str) for alias in aliases): + raise TypeError("Only strings are allowed as network aliases.") + parsed_network["aliases"] = aliases + options = network.pop("options", None) if options is not None: if not isinstance(options, dict): - raise TypeError('Only dict is allowed as network options.') - parsed_network['options'] = clean_dict_booleans_for_docker_api(options) + raise TypeError("Only dict is allowed as network options.") + parsed_network["options"] = clean_dict_booleans_for_docker_api(options) # Check if any invalid keys left if network: - invalid_keys = ', '.join(network.keys()) + invalid_keys = ", ".join(network.keys()) raise TypeError( - f'{invalid_keys} are not valid keys for the networks option' + f"{invalid_keys} are not valid keys for the networks option" ) else: raise TypeError( - 'Only a list of strings or dictionaries are allowed to be passed as networks.' + "Only a list of strings or dictionaries are allowed to be passed as networks." ) - network_name = parsed_network.pop('name') + network_name = parsed_network.pop("name") try: - parsed_network['id'] = network_ids[network_name] + parsed_network["id"] = network_ids[network_name] except KeyError as e: - raise ValueError(f'Could not find a network named: {e}.') + raise ValueError(f"Could not find a network named: {e}.") parsed_networks.append(parsed_network) return parsed_networks or [] @@ -996,7 +1000,7 @@ def get_nanoseconds_from_raw_option(name, value): return convert_duration_to_nanosecond(value) else: raise ValueError( - f'Invalid type for {name} {value} ({type(value)}). Only string or int allowed.' + f"Invalid type for {name} {value} ({type(value)}). Only string or int allowed." ) @@ -1017,8 +1021,7 @@ def has_dict_changed(new_dict, old_dict): if not old_dict and new_dict: return True defined_options = dict( - (option, value) for option, value in new_dict.items() - if value is not None + (option, value) for option, value in new_dict.items() if value is not None ) for option, value in defined_options.items(): old_value = old_dict.get(option) @@ -1042,9 +1045,7 @@ def has_list_changed(new_list, old_list, sort_lists=True, sort_key=None): if unsorted_list and isinstance(unsorted_list[0], dict): if not sort_key: - raise Exception( - 'A sort key was not specified when sorting list' - ) + raise Exception("A sort key was not specified when sorting list") else: return sorted(unsorted_list, key=lambda k: k[sort_key]) @@ -1065,7 +1066,9 @@ def has_list_changed(new_list, old_list, sort_lists=True, sort_key=None): else: zip_data = zip(new_list, old_list) for new_item, old_item in zip_data: - is_same_type = type(new_item) == type(old_item) # noqa: E721, pylint: disable=unidiomatic-typecheck + is_same_type = type(new_item) == type( + old_item + ) # noqa: E721, pylint: disable=unidiomatic-typecheck if not is_same_type: if isinstance(new_item, str) and isinstance(old_item, str): # Even though the types are different between these items, @@ -1101,18 +1104,18 @@ def have_networks_changed(new_networks, old_networks): return True zip_data = zip( - sorted(new_networks, key=lambda k: k['id']), - sorted(old_networks, key=lambda k: k['id']) + sorted(new_networks, key=lambda k: k["id"]), + sorted(old_networks, key=lambda k: k["id"]), ) for new_item, old_item in zip_data: new_item = dict(new_item) old_item = dict(old_item) # Sort the aliases - if 'aliases' in new_item: - new_item['aliases'] = sorted(new_item['aliases'] or []) - if 'aliases' in old_item: - old_item['aliases'] = sorted(old_item['aliases'] or []) + if "aliases" in new_item: + new_item["aliases"] = sorted(new_item["aliases"] or []) + if "aliases" in old_item: + old_item["aliases"] = sorted(old_item["aliases"] or []) if has_dict_changed(new_item, old_item): return True @@ -1184,221 +1187,205 @@ class DockerService(DockerBaseClass): def get_facts(self): return { - 'image': self.image, - 'mounts': self.mounts, - 'configs': self.configs, - 'networks': self.networks, - 'command': self.command, - 'args': self.args, - 'tty': self.tty, - 'dns': self.dns, - 'dns_search': self.dns_search, - 'dns_options': self.dns_options, - 'healthcheck': self.healthcheck, - 'healthcheck_disabled': self.healthcheck_disabled, - 'hostname': self.hostname, - 'hosts': self.hosts, - 'env': self.env, - 'force_update': self.force_update, - 'groups': self.groups, - 'log_driver': self.log_driver, - 'log_driver_options': self.log_driver_options, - 'publish': self.publish, - 'constraints': self.constraints, - 'replicas_max_per_node': self.replicas_max_per_node, - 'placement_preferences': self.placement_preferences, - 'labels': self.labels, - 'container_labels': self.container_labels, - 'sysctls': self.sysctls, - 'mode': self.mode, - 'replicas': self.replicas, - 'endpoint_mode': self.endpoint_mode, - 'restart_policy': self.restart_policy, - 'secrets': self.secrets, - 'stop_grace_period': self.stop_grace_period, - 'stop_signal': self.stop_signal, - 'limit_cpu': self.limit_cpu, - 'limit_memory': self.limit_memory, - 'read_only': self.read_only, - 'reserve_cpu': self.reserve_cpu, - 'reserve_memory': self.reserve_memory, - 'restart_policy_delay': self.restart_policy_delay, - 'restart_policy_attempts': self.restart_policy_attempts, - 'restart_policy_window': self.restart_policy_window, - 'rollback_config': self.rollback_config, - 'update_delay': self.update_delay, - 'update_parallelism': self.update_parallelism, - 'update_failure_action': self.update_failure_action, - 'update_monitor': self.update_monitor, - 'update_max_failure_ratio': self.update_max_failure_ratio, - 'update_order': self.update_order, - 'user': self.user, - 'working_dir': self.working_dir, - 'init': self.init, - 'cap_add': self.cap_add, - 'cap_drop': self.cap_drop, + "image": self.image, + "mounts": self.mounts, + "configs": self.configs, + "networks": self.networks, + "command": self.command, + "args": self.args, + "tty": self.tty, + "dns": self.dns, + "dns_search": self.dns_search, + "dns_options": self.dns_options, + "healthcheck": self.healthcheck, + "healthcheck_disabled": self.healthcheck_disabled, + "hostname": self.hostname, + "hosts": self.hosts, + "env": self.env, + "force_update": self.force_update, + "groups": self.groups, + "log_driver": self.log_driver, + "log_driver_options": self.log_driver_options, + "publish": self.publish, + "constraints": self.constraints, + "replicas_max_per_node": self.replicas_max_per_node, + "placement_preferences": self.placement_preferences, + "labels": self.labels, + "container_labels": self.container_labels, + "sysctls": self.sysctls, + "mode": self.mode, + "replicas": self.replicas, + "endpoint_mode": self.endpoint_mode, + "restart_policy": self.restart_policy, + "secrets": self.secrets, + "stop_grace_period": self.stop_grace_period, + "stop_signal": self.stop_signal, + "limit_cpu": self.limit_cpu, + "limit_memory": self.limit_memory, + "read_only": self.read_only, + "reserve_cpu": self.reserve_cpu, + "reserve_memory": self.reserve_memory, + "restart_policy_delay": self.restart_policy_delay, + "restart_policy_attempts": self.restart_policy_attempts, + "restart_policy_window": self.restart_policy_window, + "rollback_config": self.rollback_config, + "update_delay": self.update_delay, + "update_parallelism": self.update_parallelism, + "update_failure_action": self.update_failure_action, + "update_monitor": self.update_monitor, + "update_max_failure_ratio": self.update_max_failure_ratio, + "update_order": self.update_order, + "user": self.user, + "working_dir": self.working_dir, + "init": self.init, + "cap_add": self.cap_add, + "cap_drop": self.cap_drop, } @property def can_update_networks(self): # Before Docker API 1.29 adding/removing networks was not supported - return ( - self.docker_api_version >= LooseVersion('1.29') and - self.docker_py_version >= LooseVersion('2.7') - ) + return self.docker_api_version >= LooseVersion( + "1.29" + ) and self.docker_py_version >= LooseVersion("2.7") @property def can_use_task_template_networks(self): # In Docker API 1.25 attaching networks to TaskTemplate is preferred over Spec - return self.docker_py_version >= LooseVersion('2.7') + return self.docker_py_version >= LooseVersion("2.7") @staticmethod def get_restart_config_from_ansible_params(params): - restart_config = params['restart_config'] or {} + restart_config = params["restart_config"] or {} condition = get_value( - 'condition', + "condition", restart_config, ) delay = get_value( - 'delay', + "delay", restart_config, ) - delay = get_nanoseconds_from_raw_option( - 'restart_policy_delay', - delay - ) + delay = get_nanoseconds_from_raw_option("restart_policy_delay", delay) max_attempts = get_value( - 'max_attempts', + "max_attempts", restart_config, ) window = get_value( - 'window', + "window", restart_config, ) - window = get_nanoseconds_from_raw_option( - 'restart_policy_window', - window - ) + window = get_nanoseconds_from_raw_option("restart_policy_window", window) return { - 'restart_policy': condition, - 'restart_policy_delay': delay, - 'restart_policy_attempts': max_attempts, - 'restart_policy_window': window + "restart_policy": condition, + "restart_policy_delay": delay, + "restart_policy_attempts": max_attempts, + "restart_policy_window": window, } @staticmethod def get_update_config_from_ansible_params(params): - update_config = params['update_config'] or {} + update_config = params["update_config"] or {} parallelism = get_value( - 'parallelism', + "parallelism", update_config, ) delay = get_value( - 'delay', + "delay", update_config, ) - delay = get_nanoseconds_from_raw_option( - 'update_delay', - delay - ) + delay = get_nanoseconds_from_raw_option("update_delay", delay) failure_action = get_value( - 'failure_action', + "failure_action", update_config, ) monitor = get_value( - 'monitor', + "monitor", update_config, ) - monitor = get_nanoseconds_from_raw_option( - 'update_monitor', - monitor - ) + monitor = get_nanoseconds_from_raw_option("update_monitor", monitor) max_failure_ratio = get_value( - 'max_failure_ratio', + "max_failure_ratio", update_config, ) order = get_value( - 'order', + "order", update_config, ) return { - 'update_parallelism': parallelism, - 'update_delay': delay, - 'update_failure_action': failure_action, - 'update_monitor': monitor, - 'update_max_failure_ratio': max_failure_ratio, - 'update_order': order + "update_parallelism": parallelism, + "update_delay": delay, + "update_failure_action": failure_action, + "update_monitor": monitor, + "update_max_failure_ratio": max_failure_ratio, + "update_order": order, } @staticmethod def get_rollback_config_from_ansible_params(params): - if params['rollback_config'] is None: + if params["rollback_config"] is None: return None - rollback_config = params['rollback_config'] or {} + rollback_config = params["rollback_config"] or {} delay = get_nanoseconds_from_raw_option( - 'rollback_config.delay', - rollback_config.get('delay') + "rollback_config.delay", rollback_config.get("delay") ) monitor = get_nanoseconds_from_raw_option( - 'rollback_config.monitor', - rollback_config.get('monitor') + "rollback_config.monitor", rollback_config.get("monitor") ) return { - 'parallelism': rollback_config.get('parallelism'), - 'delay': delay, - 'failure_action': rollback_config.get('failure_action'), - 'monitor': monitor, - 'max_failure_ratio': rollback_config.get('max_failure_ratio'), - 'order': rollback_config.get('order'), - + "parallelism": rollback_config.get("parallelism"), + "delay": delay, + "failure_action": rollback_config.get("failure_action"), + "monitor": monitor, + "max_failure_ratio": rollback_config.get("max_failure_ratio"), + "order": rollback_config.get("order"), } @staticmethod def get_logging_from_ansible_params(params): - logging_config = params['logging'] or {} + logging_config = params["logging"] or {} driver = get_value( - 'driver', + "driver", logging_config, ) options = get_value( - 'options', + "options", logging_config, ) return { - 'log_driver': driver, - 'log_driver_options': options, + "log_driver": driver, + "log_driver_options": options, } @staticmethod def get_limits_from_ansible_params(params): - limits = params['limits'] or {} + limits = params["limits"] or {} cpus = get_value( - 'cpus', + "cpus", limits, ) memory = get_value( - 'memory', + "memory", limits, ) if memory is not None: try: memory = human_to_bytes(memory) except ValueError as exc: - raise Exception(f'Failed to convert limit_memory to bytes: {exc}') + raise Exception(f"Failed to convert limit_memory to bytes: {exc}") return { - 'limit_cpu': cpus, - 'limit_memory': memory, + "limit_cpu": cpus, + "limit_memory": memory, } @staticmethod def get_reservations_from_ansible_params(params): - reservations = params['reservations'] or {} + reservations = params["reservations"] or {} cpus = get_value( - 'cpus', + "cpus", reservations, ) memory = get_value( - 'memory', + "memory", reservations, ) @@ -1406,30 +1393,24 @@ class DockerService(DockerBaseClass): try: memory = human_to_bytes(memory) except ValueError as exc: - raise Exception(f'Failed to convert reserve_memory to bytes: {exc}') + raise Exception(f"Failed to convert reserve_memory to bytes: {exc}") return { - 'reserve_cpu': cpus, - 'reserve_memory': memory, + "reserve_cpu": cpus, + "reserve_memory": memory, } @staticmethod def get_placement_from_ansible_params(params): - placement = params['placement'] or {} - constraints = get_value( - 'constraints', - placement - ) + placement = params["placement"] or {} + constraints = get_value("constraints", placement) - preferences = placement.get('preferences') - replicas_max_per_node = get_value( - 'replicas_max_per_node', - placement - ) + preferences = placement.get("preferences") + replicas_max_per_node = get_value("replicas_max_per_node", placement) return { - 'constraints': constraints, - 'placement_preferences': preferences, - 'replicas_max_per_node': replicas_max_per_node, + "constraints": constraints, + "placement_preferences": preferences, + "replicas_max_per_node": replicas_max_per_node, } @classmethod @@ -1445,32 +1426,32 @@ class DockerService(DockerBaseClass): ): s = DockerService(client.docker_api_version, client.docker_py_version) s.image = image_digest - s.args = ap['args'] - s.endpoint_mode = ap['endpoint_mode'] - s.dns = ap['dns'] - s.dns_search = ap['dns_search'] - s.dns_options = ap['dns_options'] - s.healthcheck, s.healthcheck_disabled = parse_healthcheck(ap['healthcheck']) - s.hostname = ap['hostname'] - s.hosts = ap['hosts'] - s.tty = ap['tty'] - s.labels = ap['labels'] - sanitize_labels(s.labels, 'labels', client) - s.container_labels = ap['container_labels'] - sanitize_labels(s.container_labels, 'container_labels', client) - s.sysctls = ap['sysctls'] - s.mode = ap['mode'] - s.stop_signal = ap['stop_signal'] - s.user = ap['user'] - s.working_dir = ap['working_dir'] - s.read_only = ap['read_only'] - s.init = ap['init'] - s.cap_add = ap['cap_add'] - s.cap_drop = ap['cap_drop'] + s.args = ap["args"] + s.endpoint_mode = ap["endpoint_mode"] + s.dns = ap["dns"] + s.dns_search = ap["dns_search"] + s.dns_options = ap["dns_options"] + s.healthcheck, s.healthcheck_disabled = parse_healthcheck(ap["healthcheck"]) + s.hostname = ap["hostname"] + s.hosts = ap["hosts"] + s.tty = ap["tty"] + s.labels = ap["labels"] + sanitize_labels(s.labels, "labels", client) + s.container_labels = ap["container_labels"] + sanitize_labels(s.container_labels, "container_labels", client) + s.sysctls = ap["sysctls"] + s.mode = ap["mode"] + s.stop_signal = ap["stop_signal"] + s.user = ap["user"] + s.working_dir = ap["working_dir"] + s.read_only = ap["read_only"] + s.init = ap["init"] + s.cap_add = ap["cap_add"] + s.cap_drop = ap["cap_drop"] - s.networks = get_docker_networks(ap['networks'], network_ids) + s.networks = get_docker_networks(ap["networks"], network_ids) - s.command = ap['command'] + s.command = ap["command"] if isinstance(s.command, str): s.command = shlex.split(s.command) elif isinstance(s.command, list): @@ -1480,24 +1461,24 @@ class DockerService(DockerBaseClass): if not isinstance(item, str) ] if invalid_items: - errors = ', '.join( + errors = ", ".join( [ - f'{item} ({type(item)}) at index {index}' + f"{item} ({type(item)}) at index {index}" for index, item in invalid_items ] ) raise Exception( - 'All items in a command list need to be strings. ' - f'Check quoting. Invalid items: {errors}.' + "All items in a command list need to be strings. " + f"Check quoting. Invalid items: {errors}." ) - s.command = ap['command'] + s.command = ap["command"] elif s.command is not None: raise ValueError( - f'Invalid type for command {s.command} ({type(s.command)}). ' - 'Only string or list allowed. Check quoting.' + f"Invalid type for command {s.command} ({type(s.command)}). " + "Only string or list allowed. Check quoting." ) - s.env = get_docker_environment(ap['env'], ap['env_files']) + s.env = get_docker_environment(ap["env"], ap["env_files"]) s.rollback_config = cls.get_rollback_config_from_ansible_params(ap) update_config = cls.get_update_config_from_ansible_params(ap) @@ -1524,86 +1505,90 @@ class DockerService(DockerBaseClass): for key, value in placement.items(): setattr(s, key, value) - if ap['stop_grace_period'] is not None: - s.stop_grace_period = convert_duration_to_nanosecond(ap['stop_grace_period']) + if ap["stop_grace_period"] is not None: + s.stop_grace_period = convert_duration_to_nanosecond( + ap["stop_grace_period"] + ) - if ap['force_update']: - s.force_update = int(str(time.time()).replace('.', '')) + if ap["force_update"]: + s.force_update = int(str(time.time()).replace(".", "")) - if ap['groups'] is not None: + if ap["groups"] is not None: # In case integers are passed as groups, we need to convert them to # strings as docker internally treats them as strings. - s.groups = [str(g) for g in ap['groups']] + s.groups = [str(g) for g in ap["groups"]] - if ap['replicas'] == -1: + if ap["replicas"] == -1: if old_service: s.replicas = old_service.replicas else: s.replicas = 1 else: - s.replicas = ap['replicas'] + s.replicas = ap["replicas"] - if ap['publish'] is not None: + if ap["publish"] is not None: s.publish = [] - for param_p in ap['publish']: + for param_p in ap["publish"]: service_p = {} - service_p['protocol'] = param_p['protocol'] - service_p['mode'] = param_p['mode'] - service_p['published_port'] = param_p['published_port'] - service_p['target_port'] = param_p['target_port'] + service_p["protocol"] = param_p["protocol"] + service_p["mode"] = param_p["mode"] + service_p["published_port"] = param_p["published_port"] + service_p["target_port"] = param_p["target_port"] s.publish.append(service_p) - if ap['mounts'] is not None: + if ap["mounts"] is not None: s.mounts = [] - for param_m in ap['mounts']: + for param_m in ap["mounts"]: service_m = {} - service_m['readonly'] = param_m['readonly'] - service_m['type'] = param_m['type'] - if param_m['source'] is None and param_m['type'] != 'tmpfs': - raise ValueError('Source must be specified for mounts which are not of type tmpfs') - service_m['source'] = param_m['source'] or '' - service_m['target'] = param_m['target'] - service_m['labels'] = param_m['labels'] - service_m['no_copy'] = param_m['no_copy'] - service_m['propagation'] = param_m['propagation'] - service_m['driver_config'] = param_m['driver_config'] - service_m['tmpfs_mode'] = param_m['tmpfs_mode'] - tmpfs_size = param_m['tmpfs_size'] + service_m["readonly"] = param_m["readonly"] + service_m["type"] = param_m["type"] + if param_m["source"] is None and param_m["type"] != "tmpfs": + raise ValueError( + "Source must be specified for mounts which are not of type tmpfs" + ) + service_m["source"] = param_m["source"] or "" + service_m["target"] = param_m["target"] + service_m["labels"] = param_m["labels"] + service_m["no_copy"] = param_m["no_copy"] + service_m["propagation"] = param_m["propagation"] + service_m["driver_config"] = param_m["driver_config"] + service_m["tmpfs_mode"] = param_m["tmpfs_mode"] + tmpfs_size = param_m["tmpfs_size"] if tmpfs_size is not None: try: tmpfs_size = human_to_bytes(tmpfs_size) except ValueError as exc: raise ValueError( - f'Failed to convert tmpfs_size to bytes: {exc}' + f"Failed to convert tmpfs_size to bytes: {exc}" ) - service_m['tmpfs_size'] = tmpfs_size + service_m["tmpfs_size"] = tmpfs_size s.mounts.append(service_m) - if ap['configs'] is not None: + if ap["configs"] is not None: s.configs = [] - for param_m in ap['configs']: + for param_m in ap["configs"]: service_c = {} - config_name = param_m['config_name'] - service_c['config_id'] = param_m['config_id'] or config_ids[config_name] - service_c['config_name'] = config_name - service_c['filename'] = param_m['filename'] or config_name - service_c['uid'] = param_m['uid'] - service_c['gid'] = param_m['gid'] - service_c['mode'] = param_m['mode'] + config_name = param_m["config_name"] + service_c["config_id"] = param_m["config_id"] or config_ids[config_name] + service_c["config_name"] = config_name + service_c["filename"] = param_m["filename"] or config_name + service_c["uid"] = param_m["uid"] + service_c["gid"] = param_m["gid"] + service_c["mode"] = param_m["mode"] s.configs.append(service_c) - if ap['secrets'] is not None: + if ap["secrets"] is not None: s.secrets = [] - for param_m in ap['secrets']: + for param_m in ap["secrets"]: service_s = {} - secret_name = param_m['secret_name'] - service_s['secret_id'] = param_m['secret_id'] or secret_ids[secret_name] - service_s['secret_name'] = secret_name - service_s['filename'] = param_m['filename'] or secret_name - service_s['uid'] = param_m['uid'] - service_s['gid'] = param_m['gid'] - service_s['mode'] = param_m['mode'] + secret_name = param_m["secret_name"] + service_s["secret_id"] = param_m["secret_id"] or secret_ids[secret_name] + service_s["secret_name"] = secret_name + service_s["filename"] = param_m["filename"] or secret_name + service_s["uid"] = param_m["uid"] + service_s["gid"] = param_m["gid"] + service_s["mode"] = param_m["mode"] s.secrets.append(service_s) return s @@ -1613,113 +1598,232 @@ class DockerService(DockerBaseClass): needs_rebuild = False force_update = False if self.endpoint_mode is not None and self.endpoint_mode != os.endpoint_mode: - differences.add('endpoint_mode', parameter=self.endpoint_mode, active=os.endpoint_mode) + differences.add( + "endpoint_mode", parameter=self.endpoint_mode, active=os.endpoint_mode + ) if has_list_changed(self.env, os.env): - differences.add('env', parameter=self.env, active=os.env) + differences.add("env", parameter=self.env, active=os.env) if self.log_driver is not None and self.log_driver != os.log_driver: - differences.add('log_driver', parameter=self.log_driver, active=os.log_driver) - if self.log_driver_options is not None and self.log_driver_options != (os.log_driver_options or {}): - differences.add('log_opt', parameter=self.log_driver_options, active=os.log_driver_options) + differences.add( + "log_driver", parameter=self.log_driver, active=os.log_driver + ) + if self.log_driver_options is not None and self.log_driver_options != ( + os.log_driver_options or {} + ): + differences.add( + "log_opt", + parameter=self.log_driver_options, + active=os.log_driver_options, + ) if self.mode != os.mode: needs_rebuild = True - differences.add('mode', parameter=self.mode, active=os.mode) - if has_list_changed(self.mounts, os.mounts, sort_key='target'): - differences.add('mounts', parameter=self.mounts, active=os.mounts) - if has_list_changed(self.configs, os.configs, sort_key='config_name'): - differences.add('configs', parameter=self.configs, active=os.configs) - if has_list_changed(self.secrets, os.secrets, sort_key='secret_name'): - differences.add('secrets', parameter=self.secrets, active=os.secrets) + differences.add("mode", parameter=self.mode, active=os.mode) + if has_list_changed(self.mounts, os.mounts, sort_key="target"): + differences.add("mounts", parameter=self.mounts, active=os.mounts) + if has_list_changed(self.configs, os.configs, sort_key="config_name"): + differences.add("configs", parameter=self.configs, active=os.configs) + if has_list_changed(self.secrets, os.secrets, sort_key="secret_name"): + differences.add("secrets", parameter=self.secrets, active=os.secrets) if have_networks_changed(self.networks, os.networks): - differences.add('networks', parameter=self.networks, active=os.networks) + differences.add("networks", parameter=self.networks, active=os.networks) needs_rebuild = not self.can_update_networks if self.replicas != os.replicas: - differences.add('replicas', parameter=self.replicas, active=os.replicas) + differences.add("replicas", parameter=self.replicas, active=os.replicas) if has_list_changed(self.command, os.command, sort_lists=False): - differences.add('command', parameter=self.command, active=os.command) + differences.add("command", parameter=self.command, active=os.command) if has_list_changed(self.args, os.args, sort_lists=False): - differences.add('args', parameter=self.args, active=os.args) + differences.add("args", parameter=self.args, active=os.args) if has_list_changed(self.constraints, os.constraints): - differences.add('constraints', parameter=self.constraints, active=os.constraints) - if self.replicas_max_per_node is not None and self.replicas_max_per_node != os.replicas_max_per_node: - differences.add('replicas_max_per_node', parameter=self.replicas_max_per_node, active=os.replicas_max_per_node) - if has_list_changed(self.placement_preferences, os.placement_preferences, sort_lists=False): - differences.add('placement_preferences', parameter=self.placement_preferences, active=os.placement_preferences) + differences.add( + "constraints", parameter=self.constraints, active=os.constraints + ) + if ( + self.replicas_max_per_node is not None + and self.replicas_max_per_node != os.replicas_max_per_node + ): + differences.add( + "replicas_max_per_node", + parameter=self.replicas_max_per_node, + active=os.replicas_max_per_node, + ) + if has_list_changed( + self.placement_preferences, os.placement_preferences, sort_lists=False + ): + differences.add( + "placement_preferences", + parameter=self.placement_preferences, + active=os.placement_preferences, + ) if has_list_changed(self.groups, os.groups): - differences.add('groups', parameter=self.groups, active=os.groups) + differences.add("groups", parameter=self.groups, active=os.groups) if self.labels is not None and self.labels != (os.labels or {}): - differences.add('labels', parameter=self.labels, active=os.labels) + differences.add("labels", parameter=self.labels, active=os.labels) if self.limit_cpu is not None and self.limit_cpu != os.limit_cpu: - differences.add('limit_cpu', parameter=self.limit_cpu, active=os.limit_cpu) + differences.add("limit_cpu", parameter=self.limit_cpu, active=os.limit_cpu) if self.limit_memory is not None and self.limit_memory != os.limit_memory: - differences.add('limit_memory', parameter=self.limit_memory, active=os.limit_memory) + differences.add( + "limit_memory", parameter=self.limit_memory, active=os.limit_memory + ) if self.reserve_cpu is not None and self.reserve_cpu != os.reserve_cpu: - differences.add('reserve_cpu', parameter=self.reserve_cpu, active=os.reserve_cpu) + differences.add( + "reserve_cpu", parameter=self.reserve_cpu, active=os.reserve_cpu + ) if self.reserve_memory is not None and self.reserve_memory != os.reserve_memory: - differences.add('reserve_memory', parameter=self.reserve_memory, active=os.reserve_memory) - if self.container_labels is not None and self.container_labels != (os.container_labels or {}): - differences.add('container_labels', parameter=self.container_labels, active=os.container_labels) + differences.add( + "reserve_memory", + parameter=self.reserve_memory, + active=os.reserve_memory, + ) + if self.container_labels is not None and self.container_labels != ( + os.container_labels or {} + ): + differences.add( + "container_labels", + parameter=self.container_labels, + active=os.container_labels, + ) if self.sysctls is not None and self.sysctls != (os.sysctls or {}): - differences.add('sysctls', parameter=self.sysctls, active=os.sysctls) + differences.add("sysctls", parameter=self.sysctls, active=os.sysctls) if self.stop_signal is not None and self.stop_signal != os.stop_signal: - differences.add('stop_signal', parameter=self.stop_signal, active=os.stop_signal) - if self.stop_grace_period is not None and self.stop_grace_period != os.stop_grace_period: - differences.add('stop_grace_period', parameter=self.stop_grace_period, active=os.stop_grace_period) + differences.add( + "stop_signal", parameter=self.stop_signal, active=os.stop_signal + ) + if ( + self.stop_grace_period is not None + and self.stop_grace_period != os.stop_grace_period + ): + differences.add( + "stop_grace_period", + parameter=self.stop_grace_period, + active=os.stop_grace_period, + ) if self.has_publish_changed(os.publish): - differences.add('publish', parameter=self.publish, active=os.publish) + differences.add("publish", parameter=self.publish, active=os.publish) if self.read_only is not None and self.read_only != os.read_only: - differences.add('read_only', parameter=self.read_only, active=os.read_only) + differences.add("read_only", parameter=self.read_only, active=os.read_only) if self.restart_policy is not None and self.restart_policy != os.restart_policy: - differences.add('restart_policy', parameter=self.restart_policy, active=os.restart_policy) - if self.restart_policy_attempts is not None and self.restart_policy_attempts != os.restart_policy_attempts: - differences.add('restart_policy_attempts', parameter=self.restart_policy_attempts, active=os.restart_policy_attempts) - if self.restart_policy_delay is not None and self.restart_policy_delay != os.restart_policy_delay: - differences.add('restart_policy_delay', parameter=self.restart_policy_delay, active=os.restart_policy_delay) - if self.restart_policy_window is not None and self.restart_policy_window != os.restart_policy_window: - differences.add('restart_policy_window', parameter=self.restart_policy_window, active=os.restart_policy_window) + differences.add( + "restart_policy", + parameter=self.restart_policy, + active=os.restart_policy, + ) + if ( + self.restart_policy_attempts is not None + and self.restart_policy_attempts != os.restart_policy_attempts + ): + differences.add( + "restart_policy_attempts", + parameter=self.restart_policy_attempts, + active=os.restart_policy_attempts, + ) + if ( + self.restart_policy_delay is not None + and self.restart_policy_delay != os.restart_policy_delay + ): + differences.add( + "restart_policy_delay", + parameter=self.restart_policy_delay, + active=os.restart_policy_delay, + ) + if ( + self.restart_policy_window is not None + and self.restart_policy_window != os.restart_policy_window + ): + differences.add( + "restart_policy_window", + parameter=self.restart_policy_window, + active=os.restart_policy_window, + ) if has_dict_changed(self.rollback_config, os.rollback_config): - differences.add('rollback_config', parameter=self.rollback_config, active=os.rollback_config) + differences.add( + "rollback_config", + parameter=self.rollback_config, + active=os.rollback_config, + ) if self.update_delay is not None and self.update_delay != os.update_delay: - differences.add('update_delay', parameter=self.update_delay, active=os.update_delay) - if self.update_parallelism is not None and self.update_parallelism != os.update_parallelism: - differences.add('update_parallelism', parameter=self.update_parallelism, active=os.update_parallelism) - if self.update_failure_action is not None and self.update_failure_action != os.update_failure_action: - differences.add('update_failure_action', parameter=self.update_failure_action, active=os.update_failure_action) + differences.add( + "update_delay", parameter=self.update_delay, active=os.update_delay + ) + if ( + self.update_parallelism is not None + and self.update_parallelism != os.update_parallelism + ): + differences.add( + "update_parallelism", + parameter=self.update_parallelism, + active=os.update_parallelism, + ) + if ( + self.update_failure_action is not None + and self.update_failure_action != os.update_failure_action + ): + differences.add( + "update_failure_action", + parameter=self.update_failure_action, + active=os.update_failure_action, + ) if self.update_monitor is not None and self.update_monitor != os.update_monitor: - differences.add('update_monitor', parameter=self.update_monitor, active=os.update_monitor) - if self.update_max_failure_ratio is not None and self.update_max_failure_ratio != os.update_max_failure_ratio: - differences.add('update_max_failure_ratio', parameter=self.update_max_failure_ratio, active=os.update_max_failure_ratio) + differences.add( + "update_monitor", + parameter=self.update_monitor, + active=os.update_monitor, + ) + if ( + self.update_max_failure_ratio is not None + and self.update_max_failure_ratio != os.update_max_failure_ratio + ): + differences.add( + "update_max_failure_ratio", + parameter=self.update_max_failure_ratio, + active=os.update_max_failure_ratio, + ) if self.update_order is not None and self.update_order != os.update_order: - differences.add('update_order', parameter=self.update_order, active=os.update_order) + differences.add( + "update_order", parameter=self.update_order, active=os.update_order + ) has_image_changed, change = self.has_image_changed(os.image) if has_image_changed: - differences.add('image', parameter=self.image, active=change) + differences.add("image", parameter=self.image, active=change) if self.user and self.user != os.user: - differences.add('user', parameter=self.user, active=os.user) + differences.add("user", parameter=self.user, active=os.user) if has_list_changed(self.dns, os.dns, sort_lists=False): - differences.add('dns', parameter=self.dns, active=os.dns) + differences.add("dns", parameter=self.dns, active=os.dns) if has_list_changed(self.dns_search, os.dns_search, sort_lists=False): - differences.add('dns_search', parameter=self.dns_search, active=os.dns_search) + differences.add( + "dns_search", parameter=self.dns_search, active=os.dns_search + ) if has_list_changed(self.dns_options, os.dns_options): - differences.add('dns_options', parameter=self.dns_options, active=os.dns_options) + differences.add( + "dns_options", parameter=self.dns_options, active=os.dns_options + ) if self.has_healthcheck_changed(os): - differences.add('healthcheck', parameter=self.healthcheck, active=os.healthcheck) + differences.add( + "healthcheck", parameter=self.healthcheck, active=os.healthcheck + ) if self.hostname is not None and self.hostname != os.hostname: - differences.add('hostname', parameter=self.hostname, active=os.hostname) + differences.add("hostname", parameter=self.hostname, active=os.hostname) if self.hosts is not None and self.hosts != (os.hosts or {}): - differences.add('hosts', parameter=self.hosts, active=os.hosts) + differences.add("hosts", parameter=self.hosts, active=os.hosts) if self.tty is not None and self.tty != os.tty: - differences.add('tty', parameter=self.tty, active=os.tty) + differences.add("tty", parameter=self.tty, active=os.tty) if self.working_dir is not None and self.working_dir != os.working_dir: - differences.add('working_dir', parameter=self.working_dir, active=os.working_dir) + differences.add( + "working_dir", parameter=self.working_dir, active=os.working_dir + ) if self.force_update: force_update = True if self.init is not None and self.init != os.init: - differences.add('init', parameter=self.init, active=os.init) + differences.add("init", parameter=self.init, active=os.init) if has_list_changed(self.cap_add, os.cap_add): - differences.add('cap_add', parameter=self.cap_add, active=os.cap_add) + differences.add("cap_add", parameter=self.cap_add, active=os.cap_add) if has_list_changed(self.cap_drop, os.cap_drop): - differences.add('cap_drop', parameter=self.cap_drop, active=os.cap_drop) - return not differences.empty or force_update, differences, needs_rebuild, force_update + differences.add("cap_drop", parameter=self.cap_drop, active=os.cap_drop) + return ( + not differences.empty or force_update, + differences, + needs_rebuild, + force_update, + ) def has_healthcheck_changed(self, old_publish): if self.healthcheck_disabled is False and self.healthcheck is None: @@ -1727,7 +1831,7 @@ class DockerService(DockerBaseClass): if self.healthcheck_disabled: if old_publish.healthcheck is None: return False - if old_publish.healthcheck.get('test') == ['NONE']: + if old_publish.healthcheck.get("test") == ["NONE"]: return False return self.healthcheck != old_publish.healthcheck @@ -1739,14 +1843,18 @@ class DockerService(DockerBaseClass): return True def publish_sorter(item): - return (item.get('published_port') or 0, item.get('target_port') or 0, item.get('protocol') or '') + return ( + item.get("published_port") or 0, + item.get("target_port") or 0, + item.get("protocol") or "", + ) publish = sorted(self.publish, key=publish_sorter) old_publish = sorted(old_publish, key=publish_sorter) for publish_item, old_publish_item in zip(publish, old_publish): ignored_keys = set() - if not publish_item.get('mode'): - ignored_keys.add('mode') + if not publish_item.get("mode"): + ignored_keys.add("mode") # Create copies of publish_item dicts where keys specified in ignored_keys are left out filtered_old_publish_item = dict( (k, v) for k, v in old_publish_item.items() if k not in ignored_keys @@ -1759,8 +1867,8 @@ class DockerService(DockerBaseClass): return False def has_image_changed(self, old_image): - if '@' not in self.image: - old_image = old_image.split('@')[0] + if "@" not in self.image: + old_image = old_image.split("@")[0] return self.image != old_image, old_image def build_container_spec(self): @@ -1769,16 +1877,16 @@ class DockerService(DockerBaseClass): mounts = [] for mount_config in self.mounts: mount_options = { - 'target': 'target', - 'source': 'source', - 'type': 'type', - 'readonly': 'read_only', - 'propagation': 'propagation', - 'labels': 'labels', - 'no_copy': 'no_copy', - 'driver_config': 'driver_config', - 'tmpfs_size': 'tmpfs_size', - 'tmpfs_mode': 'tmpfs_mode' + "target": "target", + "source": "source", + "type": "type", + "readonly": "read_only", + "propagation": "propagation", + "labels": "labels", + "no_copy": "no_copy", + "driver_config": "driver_config", + "tmpfs_size": "tmpfs_size", + "tmpfs_mode": "tmpfs_mode", } mount_args = {} for option, mount_arg in mount_options.items(): @@ -1793,21 +1901,21 @@ class DockerService(DockerBaseClass): configs = [] for config_config in self.configs: config_args = { - 'config_id': config_config['config_id'], - 'config_name': config_config['config_name'] + "config_id": config_config["config_id"], + "config_name": config_config["config_name"], } - filename = config_config.get('filename') + filename = config_config.get("filename") if filename: - config_args['filename'] = filename - uid = config_config.get('uid') + config_args["filename"] = filename + uid = config_config.get("uid") if uid: - config_args['uid'] = uid - gid = config_config.get('gid') + config_args["uid"] = uid + gid = config_config.get("gid") if gid: - config_args['gid'] = gid - mode = config_config.get('mode') + config_args["gid"] = gid + mode = config_config.get("mode") if mode: - config_args['mode'] = mode + config_args["mode"] = mode configs.append(types.ConfigReference(**config_args)) @@ -1816,92 +1924,92 @@ class DockerService(DockerBaseClass): secrets = [] for secret_config in self.secrets: secret_args = { - 'secret_id': secret_config['secret_id'], - 'secret_name': secret_config['secret_name'] + "secret_id": secret_config["secret_id"], + "secret_name": secret_config["secret_name"], } - filename = secret_config.get('filename') + filename = secret_config.get("filename") if filename: - secret_args['filename'] = filename - uid = secret_config.get('uid') + secret_args["filename"] = filename + uid = secret_config.get("uid") if uid: - secret_args['uid'] = uid - gid = secret_config.get('gid') + secret_args["uid"] = uid + gid = secret_config.get("gid") if gid: - secret_args['gid'] = gid - mode = secret_config.get('mode') + secret_args["gid"] = gid + mode = secret_config.get("mode") if mode: - secret_args['mode'] = mode + secret_args["mode"] = mode secrets.append(types.SecretReference(**secret_args)) dns_config_args = {} if self.dns is not None: - dns_config_args['nameservers'] = self.dns + dns_config_args["nameservers"] = self.dns if self.dns_search is not None: - dns_config_args['search'] = self.dns_search + dns_config_args["search"] = self.dns_search if self.dns_options is not None: - dns_config_args['options'] = self.dns_options + dns_config_args["options"] = self.dns_options dns_config = types.DNSConfig(**dns_config_args) if dns_config_args else None container_spec_args = {} if self.command is not None: - container_spec_args['command'] = self.command + container_spec_args["command"] = self.command if self.args is not None: - container_spec_args['args'] = self.args + container_spec_args["args"] = self.args if self.env is not None: - container_spec_args['env'] = self.env + container_spec_args["env"] = self.env if self.user is not None: - container_spec_args['user'] = self.user + container_spec_args["user"] = self.user if self.container_labels is not None: - container_spec_args['labels'] = self.container_labels + container_spec_args["labels"] = self.container_labels if self.sysctls is not None: - container_spec_args['sysctls'] = self.sysctls + container_spec_args["sysctls"] = self.sysctls if self.healthcheck is not None: - container_spec_args['healthcheck'] = types.Healthcheck(**self.healthcheck) + container_spec_args["healthcheck"] = types.Healthcheck(**self.healthcheck) elif self.healthcheck_disabled: - container_spec_args['healthcheck'] = types.Healthcheck(test=['NONE']) + container_spec_args["healthcheck"] = types.Healthcheck(test=["NONE"]) if self.hostname is not None: - container_spec_args['hostname'] = self.hostname + container_spec_args["hostname"] = self.hostname if self.hosts is not None: - container_spec_args['hosts'] = self.hosts + container_spec_args["hosts"] = self.hosts if self.read_only is not None: - container_spec_args['read_only'] = self.read_only + container_spec_args["read_only"] = self.read_only if self.stop_grace_period is not None: - container_spec_args['stop_grace_period'] = self.stop_grace_period + container_spec_args["stop_grace_period"] = self.stop_grace_period if self.stop_signal is not None: - container_spec_args['stop_signal'] = self.stop_signal + container_spec_args["stop_signal"] = self.stop_signal if self.tty is not None: - container_spec_args['tty'] = self.tty + container_spec_args["tty"] = self.tty if self.groups is not None: - container_spec_args['groups'] = self.groups + container_spec_args["groups"] = self.groups if self.working_dir is not None: - container_spec_args['workdir'] = self.working_dir + container_spec_args["workdir"] = self.working_dir if secrets is not None: - container_spec_args['secrets'] = secrets + container_spec_args["secrets"] = secrets if mounts is not None: - container_spec_args['mounts'] = mounts + container_spec_args["mounts"] = mounts if dns_config is not None: - container_spec_args['dns_config'] = dns_config + container_spec_args["dns_config"] = dns_config if configs is not None: - container_spec_args['configs'] = configs + container_spec_args["configs"] = configs if self.init is not None: - container_spec_args['init'] = self.init + container_spec_args["init"] = self.init if self.cap_add is not None: - container_spec_args['cap_add'] = self.cap_add + container_spec_args["cap_add"] = self.cap_add if self.cap_drop is not None: - container_spec_args['cap_drop'] = self.cap_drop + container_spec_args["cap_drop"] = self.cap_drop return types.ContainerSpec(self.image, **container_spec_args) def build_placement(self): placement_args = {} if self.constraints is not None: - placement_args['constraints'] = self.constraints + placement_args["constraints"] = self.constraints if self.replicas_max_per_node is not None: - placement_args['maxreplicas'] = self.replicas_max_per_node + placement_args["maxreplicas"] = self.replicas_max_per_node if self.placement_preferences is not None: - placement_args['preferences'] = [ - {key.title(): {'SpreadDescriptor': value}} + placement_args["preferences"] = [ + {key.title(): {"SpreadDescriptor": value}} for preference in self.placement_preferences for key, value in preference.items() ] @@ -1910,67 +2018,73 @@ class DockerService(DockerBaseClass): def build_update_config(self): update_config_args = {} if self.update_parallelism is not None: - update_config_args['parallelism'] = self.update_parallelism + update_config_args["parallelism"] = self.update_parallelism if self.update_delay is not None: - update_config_args['delay'] = self.update_delay + update_config_args["delay"] = self.update_delay if self.update_failure_action is not None: - update_config_args['failure_action'] = self.update_failure_action + update_config_args["failure_action"] = self.update_failure_action if self.update_monitor is not None: - update_config_args['monitor'] = self.update_monitor + update_config_args["monitor"] = self.update_monitor if self.update_max_failure_ratio is not None: - update_config_args['max_failure_ratio'] = self.update_max_failure_ratio + update_config_args["max_failure_ratio"] = self.update_max_failure_ratio if self.update_order is not None: - update_config_args['order'] = self.update_order + update_config_args["order"] = self.update_order return types.UpdateConfig(**update_config_args) if update_config_args else None def build_log_driver(self): log_driver_args = {} if self.log_driver is not None: - log_driver_args['name'] = self.log_driver + log_driver_args["name"] = self.log_driver if self.log_driver_options is not None: - log_driver_args['options'] = self.log_driver_options + log_driver_args["options"] = self.log_driver_options return types.DriverConfig(**log_driver_args) if log_driver_args else None def build_restart_policy(self): restart_policy_args = {} if self.restart_policy is not None: - restart_policy_args['condition'] = self.restart_policy + restart_policy_args["condition"] = self.restart_policy if self.restart_policy_delay is not None: - restart_policy_args['delay'] = self.restart_policy_delay + restart_policy_args["delay"] = self.restart_policy_delay if self.restart_policy_attempts is not None: - restart_policy_args['max_attempts'] = self.restart_policy_attempts + restart_policy_args["max_attempts"] = self.restart_policy_attempts if self.restart_policy_window is not None: - restart_policy_args['window'] = self.restart_policy_window - return types.RestartPolicy(**restart_policy_args) if restart_policy_args else None + restart_policy_args["window"] = self.restart_policy_window + return ( + types.RestartPolicy(**restart_policy_args) if restart_policy_args else None + ) def build_rollback_config(self): if self.rollback_config is None: return None rollback_config_options = [ - 'parallelism', - 'delay', - 'failure_action', - 'monitor', - 'max_failure_ratio', - 'order', + "parallelism", + "delay", + "failure_action", + "monitor", + "max_failure_ratio", + "order", ] rollback_config_args = {} for option in rollback_config_options: value = self.rollback_config.get(option) if value is not None: rollback_config_args[option] = value - return types.RollbackConfig(**rollback_config_args) if rollback_config_args else None + return ( + types.RollbackConfig(**rollback_config_args) + if rollback_config_args + else None + ) def build_resources(self): resources_args = {} if self.limit_cpu is not None: - resources_args['cpu_limit'] = int(self.limit_cpu * 1000000000.0) + resources_args["cpu_limit"] = int(self.limit_cpu * 1000000000.0) if self.limit_memory is not None: - resources_args['mem_limit'] = self.limit_memory + resources_args["mem_limit"] = self.limit_memory if self.reserve_cpu is not None: - resources_args['cpu_reservation'] = int(self.reserve_cpu * 1000000000.0) + resources_args["cpu_reservation"] = int(self.reserve_cpu * 1000000000.0) if self.reserve_memory is not None: - resources_args['mem_reservation'] = self.reserve_memory + resources_args["mem_reservation"] = self.reserve_memory return types.Resources(**resources_args) if resources_args else None def build_task_template(self, container_spec, placement=None): @@ -1980,23 +2094,23 @@ class DockerService(DockerBaseClass): task_template_args = {} if placement is not None: - task_template_args['placement'] = placement + task_template_args["placement"] = placement if log_driver is not None: - task_template_args['log_driver'] = log_driver + task_template_args["log_driver"] = log_driver if restart_policy is not None: - task_template_args['restart_policy'] = restart_policy + task_template_args["restart_policy"] = restart_policy if resources is not None: - task_template_args['resources'] = resources + task_template_args["resources"] = resources if self.force_update: - task_template_args['force_update'] = self.force_update + task_template_args["force_update"] = self.force_update if self.can_use_task_template_networks: networks = self.build_networks() if networks: - task_template_args['networks'] = networks + task_template_args["networks"] = networks return types.TaskTemplate(container_spec=container_spec, **task_template_args) def build_service_mode(self): - if self.mode == 'global': + if self.mode == "global": self.replicas = None return types.ServiceMode(self.mode, replicas=self.replicas) @@ -2005,11 +2119,11 @@ class DockerService(DockerBaseClass): if self.networks is not None: networks = [] for network in self.networks: - docker_network = {'Target': network['id']} - if 'aliases' in network: - docker_network['Aliases'] = network['aliases'] - if 'options' in network: - docker_network['DriverOpts'] = network['options'] + docker_network = {"Target": network["id"]} + if "aliases" in network: + docker_network["Aliases"] = network["aliases"] + if "options" in network: + docker_network["DriverOpts"] = network["options"] networks.append(docker_network) return networks @@ -2019,17 +2133,17 @@ class DockerService(DockerBaseClass): ports = [] for port in self.publish: port_spec = { - 'Protocol': port['protocol'], - 'TargetPort': port['target_port'] + "Protocol": port["protocol"], + "TargetPort": port["target_port"], } - if port.get('published_port'): - port_spec['PublishedPort'] = port['published_port'] - if port.get('mode'): - port_spec['PublishMode'] = port['mode'] + if port.get("published_port"): + port_spec["PublishedPort"] = port["published_port"] + if port.get("mode"): + port_spec["PublishMode"] = port["mode"] ports.append(port_spec) - endpoint_spec_args['ports'] = ports + endpoint_spec_args["ports"] = ports if self.endpoint_mode is not None: - endpoint_spec_args['mode'] = self.endpoint_mode + endpoint_spec_args["mode"] = self.endpoint_mode return types.EndpointSpec(**endpoint_spec_args) if endpoint_spec_args else None def build_docker_service(self): @@ -2042,19 +2156,19 @@ class DockerService(DockerBaseClass): service_mode = self.build_service_mode() endpoint_spec = self.build_endpoint_spec() - service = {'task_template': task_template, 'mode': service_mode} + service = {"task_template": task_template, "mode": service_mode} if update_config: - service['update_config'] = update_config + service["update_config"] = update_config if rollback_config: - service['rollback_config'] = rollback_config + service["rollback_config"] = rollback_config if endpoint_spec: - service['endpoint_spec'] = endpoint_spec + service["endpoint_spec"] = endpoint_spec if self.labels: - service['labels'] = self.labels + service["labels"] = self.labels if not self.can_use_task_template_networks: networks = self.build_networks() if networks: - service['networks'] = networks + service["networks"] = networks return service @@ -2070,213 +2184,233 @@ class DockerServiceManager(object): raw_data = self.client.inspect_service(name) except NotFound: return None - ds = DockerService(self.client.docker_api_version, self.client.docker_py_version) + ds = DockerService( + self.client.docker_api_version, self.client.docker_py_version + ) - task_template_data = raw_data['Spec']['TaskTemplate'] - ds.image = task_template_data['ContainerSpec']['Image'] - ds.user = task_template_data['ContainerSpec'].get('User') - ds.env = task_template_data['ContainerSpec'].get('Env') - ds.command = task_template_data['ContainerSpec'].get('Command') - ds.args = task_template_data['ContainerSpec'].get('Args') - ds.groups = task_template_data['ContainerSpec'].get('Groups') - ds.stop_grace_period = task_template_data['ContainerSpec'].get('StopGracePeriod') - ds.stop_signal = task_template_data['ContainerSpec'].get('StopSignal') - ds.working_dir = task_template_data['ContainerSpec'].get('Dir') - ds.read_only = task_template_data['ContainerSpec'].get('ReadOnly') - ds.cap_add = task_template_data['ContainerSpec'].get('CapabilityAdd') - ds.cap_drop = task_template_data['ContainerSpec'].get('CapabilityDrop') - ds.sysctls = task_template_data['ContainerSpec'].get('Sysctls') + task_template_data = raw_data["Spec"]["TaskTemplate"] + ds.image = task_template_data["ContainerSpec"]["Image"] + ds.user = task_template_data["ContainerSpec"].get("User") + ds.env = task_template_data["ContainerSpec"].get("Env") + ds.command = task_template_data["ContainerSpec"].get("Command") + ds.args = task_template_data["ContainerSpec"].get("Args") + ds.groups = task_template_data["ContainerSpec"].get("Groups") + ds.stop_grace_period = task_template_data["ContainerSpec"].get( + "StopGracePeriod" + ) + ds.stop_signal = task_template_data["ContainerSpec"].get("StopSignal") + ds.working_dir = task_template_data["ContainerSpec"].get("Dir") + ds.read_only = task_template_data["ContainerSpec"].get("ReadOnly") + ds.cap_add = task_template_data["ContainerSpec"].get("CapabilityAdd") + ds.cap_drop = task_template_data["ContainerSpec"].get("CapabilityDrop") + ds.sysctls = task_template_data["ContainerSpec"].get("Sysctls") - healthcheck_data = task_template_data['ContainerSpec'].get('Healthcheck') + healthcheck_data = task_template_data["ContainerSpec"].get("Healthcheck") if healthcheck_data: options = { - 'Test': 'test', - 'Interval': 'interval', - 'Timeout': 'timeout', - 'StartPeriod': 'start_period', - 'Retries': 'retries' + "Test": "test", + "Interval": "interval", + "Timeout": "timeout", + "StartPeriod": "start_period", + "Retries": "retries", } healthcheck = dict( - (options[key], value) for key, value in healthcheck_data.items() + (options[key], value) + for key, value in healthcheck_data.items() if value is not None and key in options ) ds.healthcheck = healthcheck - update_config_data = raw_data['Spec'].get('UpdateConfig') + update_config_data = raw_data["Spec"].get("UpdateConfig") if update_config_data: - ds.update_delay = update_config_data.get('Delay') - ds.update_parallelism = update_config_data.get('Parallelism') - ds.update_failure_action = update_config_data.get('FailureAction') - ds.update_monitor = update_config_data.get('Monitor') - ds.update_max_failure_ratio = update_config_data.get('MaxFailureRatio') - ds.update_order = update_config_data.get('Order') + ds.update_delay = update_config_data.get("Delay") + ds.update_parallelism = update_config_data.get("Parallelism") + ds.update_failure_action = update_config_data.get("FailureAction") + ds.update_monitor = update_config_data.get("Monitor") + ds.update_max_failure_ratio = update_config_data.get("MaxFailureRatio") + ds.update_order = update_config_data.get("Order") - rollback_config_data = raw_data['Spec'].get('RollbackConfig') + rollback_config_data = raw_data["Spec"].get("RollbackConfig") if rollback_config_data: ds.rollback_config = { - 'parallelism': rollback_config_data.get('Parallelism'), - 'delay': rollback_config_data.get('Delay'), - 'failure_action': rollback_config_data.get('FailureAction'), - 'monitor': rollback_config_data.get('Monitor'), - 'max_failure_ratio': rollback_config_data.get('MaxFailureRatio'), - 'order': rollback_config_data.get('Order'), + "parallelism": rollback_config_data.get("Parallelism"), + "delay": rollback_config_data.get("Delay"), + "failure_action": rollback_config_data.get("FailureAction"), + "monitor": rollback_config_data.get("Monitor"), + "max_failure_ratio": rollback_config_data.get("MaxFailureRatio"), + "order": rollback_config_data.get("Order"), } - dns_config = task_template_data['ContainerSpec'].get('DNSConfig') + dns_config = task_template_data["ContainerSpec"].get("DNSConfig") if dns_config: - ds.dns = dns_config.get('Nameservers') - ds.dns_search = dns_config.get('Search') - ds.dns_options = dns_config.get('Options') + ds.dns = dns_config.get("Nameservers") + ds.dns_search = dns_config.get("Search") + ds.dns_options = dns_config.get("Options") - ds.hostname = task_template_data['ContainerSpec'].get('Hostname') + ds.hostname = task_template_data["ContainerSpec"].get("Hostname") - hosts = task_template_data['ContainerSpec'].get('Hosts') + hosts = task_template_data["ContainerSpec"].get("Hosts") if hosts: hosts = [ - list(reversed(host.split(":", 1))) - if ":" in host - else host.split(" ", 1) + ( + list(reversed(host.split(":", 1))) + if ":" in host + else host.split(" ", 1) + ) for host in hosts ] ds.hosts = dict((hostname, ip) for ip, hostname in hosts) - ds.tty = task_template_data['ContainerSpec'].get('TTY') + ds.tty = task_template_data["ContainerSpec"].get("TTY") - placement = task_template_data.get('Placement') + placement = task_template_data.get("Placement") if placement: - ds.constraints = placement.get('Constraints') - ds.replicas_max_per_node = placement.get('MaxReplicas') + ds.constraints = placement.get("Constraints") + ds.replicas_max_per_node = placement.get("MaxReplicas") placement_preferences = [] - for preference in placement.get('Preferences', []): + for preference in placement.get("Preferences", []): placement_preferences.append( dict( - (key.lower(), value['SpreadDescriptor']) + (key.lower(), value["SpreadDescriptor"]) for key, value in preference.items() ) ) ds.placement_preferences = placement_preferences or None - restart_policy_data = task_template_data.get('RestartPolicy') + restart_policy_data = task_template_data.get("RestartPolicy") if restart_policy_data: - ds.restart_policy = restart_policy_data.get('Condition') - ds.restart_policy_delay = restart_policy_data.get('Delay') - ds.restart_policy_attempts = restart_policy_data.get('MaxAttempts') - ds.restart_policy_window = restart_policy_data.get('Window') + ds.restart_policy = restart_policy_data.get("Condition") + ds.restart_policy_delay = restart_policy_data.get("Delay") + ds.restart_policy_attempts = restart_policy_data.get("MaxAttempts") + ds.restart_policy_window = restart_policy_data.get("Window") - raw_data_endpoint_spec = raw_data['Spec'].get('EndpointSpec') + raw_data_endpoint_spec = raw_data["Spec"].get("EndpointSpec") if raw_data_endpoint_spec: - ds.endpoint_mode = raw_data_endpoint_spec.get('Mode') - raw_data_ports = raw_data_endpoint_spec.get('Ports') + ds.endpoint_mode = raw_data_endpoint_spec.get("Mode") + raw_data_ports = raw_data_endpoint_spec.get("Ports") if raw_data_ports: ds.publish = [] for port in raw_data_ports: - ds.publish.append({ - 'protocol': port['Protocol'], - 'mode': port.get('PublishMode', None), - 'published_port': port.get('PublishedPort', None), - 'target_port': int(port['TargetPort']) - }) + ds.publish.append( + { + "protocol": port["Protocol"], + "mode": port.get("PublishMode", None), + "published_port": port.get("PublishedPort", None), + "target_port": int(port["TargetPort"]), + } + ) - raw_data_limits = task_template_data.get('Resources', {}).get('Limits') + raw_data_limits = task_template_data.get("Resources", {}).get("Limits") if raw_data_limits: - raw_cpu_limits = raw_data_limits.get('NanoCPUs') + raw_cpu_limits = raw_data_limits.get("NanoCPUs") if raw_cpu_limits: ds.limit_cpu = float(raw_cpu_limits) / 1000000000 - raw_memory_limits = raw_data_limits.get('MemoryBytes') + raw_memory_limits = raw_data_limits.get("MemoryBytes") if raw_memory_limits: ds.limit_memory = int(raw_memory_limits) - raw_data_reservations = task_template_data.get('Resources', {}).get('Reservations') + raw_data_reservations = task_template_data.get("Resources", {}).get( + "Reservations" + ) if raw_data_reservations: - raw_cpu_reservations = raw_data_reservations.get('NanoCPUs') + raw_cpu_reservations = raw_data_reservations.get("NanoCPUs") if raw_cpu_reservations: ds.reserve_cpu = float(raw_cpu_reservations) / 1000000000 - raw_memory_reservations = raw_data_reservations.get('MemoryBytes') + raw_memory_reservations = raw_data_reservations.get("MemoryBytes") if raw_memory_reservations: ds.reserve_memory = int(raw_memory_reservations) - ds.labels = raw_data['Spec'].get('Labels') - ds.log_driver = task_template_data.get('LogDriver', {}).get('Name') - ds.log_driver_options = task_template_data.get('LogDriver', {}).get('Options') - ds.container_labels = task_template_data['ContainerSpec'].get('Labels') + ds.labels = raw_data["Spec"].get("Labels") + ds.log_driver = task_template_data.get("LogDriver", {}).get("Name") + ds.log_driver_options = task_template_data.get("LogDriver", {}).get("Options") + ds.container_labels = task_template_data["ContainerSpec"].get("Labels") - mode = raw_data['Spec']['Mode'] - if 'Replicated' in mode.keys(): - ds.mode = to_text('replicated', encoding='utf-8') - ds.replicas = mode['Replicated']['Replicas'] - elif 'Global' in mode.keys(): - ds.mode = 'global' - elif 'ReplicatedJob' in mode.keys(): - ds.mode = to_text('replicated-job', encoding='utf-8') - ds.replicas = mode['ReplicatedJob']['TotalCompletions'] + mode = raw_data["Spec"]["Mode"] + if "Replicated" in mode.keys(): + ds.mode = to_text("replicated", encoding="utf-8") + ds.replicas = mode["Replicated"]["Replicas"] + elif "Global" in mode.keys(): + ds.mode = "global" + elif "ReplicatedJob" in mode.keys(): + ds.mode = to_text("replicated-job", encoding="utf-8") + ds.replicas = mode["ReplicatedJob"]["TotalCompletions"] else: - raise Exception(f'Unknown service mode: {mode}') + raise Exception(f"Unknown service mode: {mode}") - raw_data_mounts = task_template_data['ContainerSpec'].get('Mounts') + raw_data_mounts = task_template_data["ContainerSpec"].get("Mounts") if raw_data_mounts: ds.mounts = [] for mount_data in raw_data_mounts: - bind_options = mount_data.get('BindOptions', {}) - volume_options = mount_data.get('VolumeOptions', {}) - tmpfs_options = mount_data.get('TmpfsOptions', {}) - driver_config = volume_options.get('DriverConfig', {}) - driver_config = dict( - (key.lower(), value) for key, value in driver_config.items() - ) or None - ds.mounts.append({ - 'source': mount_data.get('Source', ''), - 'type': mount_data['Type'], - 'target': mount_data['Target'], - 'readonly': mount_data.get('ReadOnly'), - 'propagation': bind_options.get('Propagation'), - 'no_copy': volume_options.get('NoCopy'), - 'labels': volume_options.get('Labels'), - 'driver_config': driver_config, - 'tmpfs_mode': tmpfs_options.get('Mode'), - 'tmpfs_size': tmpfs_options.get('SizeBytes'), - }) + bind_options = mount_data.get("BindOptions", {}) + volume_options = mount_data.get("VolumeOptions", {}) + tmpfs_options = mount_data.get("TmpfsOptions", {}) + driver_config = volume_options.get("DriverConfig", {}) + driver_config = ( + dict((key.lower(), value) for key, value in driver_config.items()) + or None + ) + ds.mounts.append( + { + "source": mount_data.get("Source", ""), + "type": mount_data["Type"], + "target": mount_data["Target"], + "readonly": mount_data.get("ReadOnly"), + "propagation": bind_options.get("Propagation"), + "no_copy": volume_options.get("NoCopy"), + "labels": volume_options.get("Labels"), + "driver_config": driver_config, + "tmpfs_mode": tmpfs_options.get("Mode"), + "tmpfs_size": tmpfs_options.get("SizeBytes"), + } + ) - raw_data_configs = task_template_data['ContainerSpec'].get('Configs') + raw_data_configs = task_template_data["ContainerSpec"].get("Configs") if raw_data_configs: ds.configs = [] for config_data in raw_data_configs: - ds.configs.append({ - 'config_id': config_data['ConfigID'], - 'config_name': config_data['ConfigName'], - 'filename': config_data['File'].get('Name'), - 'uid': config_data['File'].get('UID'), - 'gid': config_data['File'].get('GID'), - 'mode': config_data['File'].get('Mode') - }) + ds.configs.append( + { + "config_id": config_data["ConfigID"], + "config_name": config_data["ConfigName"], + "filename": config_data["File"].get("Name"), + "uid": config_data["File"].get("UID"), + "gid": config_data["File"].get("GID"), + "mode": config_data["File"].get("Mode"), + } + ) - raw_data_secrets = task_template_data['ContainerSpec'].get('Secrets') + raw_data_secrets = task_template_data["ContainerSpec"].get("Secrets") if raw_data_secrets: ds.secrets = [] for secret_data in raw_data_secrets: - ds.secrets.append({ - 'secret_id': secret_data['SecretID'], - 'secret_name': secret_data['SecretName'], - 'filename': secret_data['File'].get('Name'), - 'uid': secret_data['File'].get('UID'), - 'gid': secret_data['File'].get('GID'), - 'mode': secret_data['File'].get('Mode') - }) + ds.secrets.append( + { + "secret_id": secret_data["SecretID"], + "secret_name": secret_data["SecretName"], + "filename": secret_data["File"].get("Name"), + "uid": secret_data["File"].get("UID"), + "gid": secret_data["File"].get("GID"), + "mode": secret_data["File"].get("Mode"), + } + ) - raw_networks_data = task_template_data.get('Networks', raw_data['Spec'].get('Networks')) + raw_networks_data = task_template_data.get( + "Networks", raw_data["Spec"].get("Networks") + ) if raw_networks_data: ds.networks = [] for network_data in raw_networks_data: - network = {'id': network_data['Target']} - if 'Aliases' in network_data: - network['aliases'] = network_data['Aliases'] - if 'DriverOpts' in network_data: - network['options'] = network_data['DriverOpts'] + network = {"id": network_data["Target"]} + if "Aliases" in network_data: + network["aliases"] = network_data["Aliases"] + if "DriverOpts" in network_data: + network["options"] = network_data["DriverOpts"] ds.networks.append(network) - ds.service_version = raw_data['Version']['Index'] - ds.service_id = raw_data['ID'] + ds.service_version = raw_data["Version"]["Index"] + ds.service_id = raw_data["ID"] - ds.init = task_template_data['ContainerSpec'].get('Init', False) + ds.init = task_template_data["ContainerSpec"].get("Init", False) return ds def update_service(self, name, old_service, new_service): @@ -2285,37 +2419,34 @@ class DockerServiceManager(object): old_service.service_id, old_service.service_version, name=name, - **service_data + **service_data, ) # Prior to Docker SDK 4.0.0 no warnings were returned and will thus be ignored. # (see https://github.com/docker/docker-py/pull/2272) - self.client.report_warnings(result, ['Warning']) + self.client.report_warnings(result, ["Warning"]) def create_service(self, name, service): service_data = service.build_docker_service() result = self.client.create_service(name=name, **service_data) - self.client.report_warnings(result, ['Warning']) + self.client.report_warnings(result, ["Warning"]) def remove_service(self, name): self.client.remove_service(name) def get_image_digest(self, name, resolve=False): - if ( - not name - or not resolve - ): + if not name or not resolve: return name repo, tag = parse_repository_tag(name) if not tag: - tag = 'latest' - name = repo + ':' + tag + tag = "latest" + name = repo + ":" + tag distribution_data = self.client.inspect_distribution(name) - digest = distribution_data['Descriptor']['digest'] - return f'{name}@{digest}' + digest = distribution_data["Descriptor"]["digest"] + return f"{name}@{digest}" def get_networks_names_ids(self): return dict( - (network['Name'], network['Id']) for network in self.client.networks() + (network["Name"], network["Id"]) for network in self.client.networks() ) def get_missing_secret_ids(self): @@ -2323,23 +2454,21 @@ class DockerServiceManager(object): Resolve missing secret ids by looking them up by name """ secret_names = [ - secret['secret_name'] - for secret in self.client.module.params.get('secrets') or [] - if secret['secret_id'] is None + secret["secret_name"] + for secret in self.client.module.params.get("secrets") or [] + if secret["secret_id"] is None ] if not secret_names: return {} - secrets = self.client.secrets(filters={'name': secret_names}) + secrets = self.client.secrets(filters={"name": secret_names}) secrets = dict( - (secret['Spec']['Name'], secret['ID']) + (secret["Spec"]["Name"], secret["ID"]) for secret in secrets - if secret['Spec']['Name'] in secret_names + if secret["Spec"]["Name"] in secret_names ) for secret_name in secret_names: if secret_name not in secrets: - self.client.fail( - f'Could not find a secret named "{secret_name}"' - ) + self.client.fail(f'Could not find a secret named "{secret_name}"') return secrets def get_missing_config_ids(self): @@ -2347,42 +2476,37 @@ class DockerServiceManager(object): Resolve missing config ids by looking them up by name """ config_names = [ - config['config_name'] - for config in self.client.module.params.get('configs') or [] - if config['config_id'] is None + config["config_name"] + for config in self.client.module.params.get("configs") or [] + if config["config_id"] is None ] if not config_names: return {} - configs = self.client.configs(filters={'name': config_names}) + configs = self.client.configs(filters={"name": config_names}) configs = dict( - (config['Spec']['Name'], config['ID']) + (config["Spec"]["Name"], config["ID"]) for config in configs - if config['Spec']['Name'] in config_names + if config["Spec"]["Name"] in config_names ) for config_name in config_names: if config_name not in configs: - self.client.fail( - f'Could not find a config named "{config_name}"' - ) + self.client.fail(f'Could not find a config named "{config_name}"') return configs def run(self): self.diff_tracker = DifferenceTracker() module = self.client.module - image = module.params['image'] + image = module.params["image"] try: image_digest = self.get_image_digest( - name=image, - resolve=module.params['resolve_image'] + name=image, resolve=module.params["resolve_image"] ) except DockerException as e: - self.client.fail( - f'Error looking for an image named {image}: {e}' - ) + self.client.fail(f"Error looking for an image named {image}: {e}") try: - current_service = self.get_service(module.params['name']) + current_service = self.get_service(module.params["name"]) except Exception as e: self.client.fail( f"Error looking for service named {module.params['name']}: {e}" @@ -2401,21 +2525,19 @@ class DockerServiceManager(object): self.client, ) except Exception as e: - return self.client.fail( - f'Error parsing module parameters: {e}' - ) + return self.client.fail(f"Error parsing module parameters: {e}") changed = False - msg = 'noop' + msg = "noop" rebuilt = False differences = DifferenceTracker() facts = {} if current_service: - if module.params['state'] == 'absent': + if module.params["state"] == "absent": if not module.check_mode: - self.remove_service(module.params['name']) - msg = 'Service removed' + self.remove_service(module.params["name"]) + msg = "Service removed" changed = True else: changed, differences, need_rebuild, force_update = new_service.compare( @@ -2425,43 +2547,36 @@ class DockerServiceManager(object): self.diff_tracker.merge(differences) if need_rebuild: if not module.check_mode: - self.remove_service(module.params['name']) - self.create_service( - module.params['name'], - new_service - ) - msg = 'Service rebuilt' + self.remove_service(module.params["name"]) + self.create_service(module.params["name"], new_service) + msg = "Service rebuilt" rebuilt = True else: if not module.check_mode: self.update_service( - module.params['name'], - current_service, - new_service + module.params["name"], current_service, new_service ) - msg = 'Service updated' + msg = "Service updated" rebuilt = False else: if force_update: if not module.check_mode: self.update_service( - module.params['name'], - current_service, - new_service + module.params["name"], current_service, new_service ) - msg = 'Service forcefully updated' + msg = "Service forcefully updated" rebuilt = False changed = True else: - msg = 'Service unchanged' + msg = "Service unchanged" facts = new_service.get_facts() else: - if module.params['state'] == 'absent': - msg = 'Service absent' + if module.params["state"] == "absent": + msg = "Service absent" else: if not module.check_mode: - self.create_service(module.params['name'], new_service) - msg = 'Service created' + self.create_service(module.params["name"], new_service) + msg = "Service created" changed = True facts = new_service.get_facts() @@ -2474,7 +2589,7 @@ class DockerServiceManager(object): except APIError as e: # Sometimes Version.Index will have changed between an inspect and # update. If this is encountered we'll retry the update. - if self.retries > 0 and 'update out of sequence' in str(e.explanation): + if self.retries > 0 and "update out of sequence" in str(e.explanation): self.retries -= 1 time.sleep(1) else: @@ -2482,291 +2597,331 @@ class DockerServiceManager(object): def _detect_publish_mode_usage(client): - for publish_def in client.module.params['publish'] or []: - if publish_def.get('mode'): + for publish_def in client.module.params["publish"] or []: + if publish_def.get("mode"): return True return False def _detect_healthcheck_start_period(client): - if client.module.params['healthcheck']: - return client.module.params['healthcheck']['start_period'] is not None + if client.module.params["healthcheck"]: + return client.module.params["healthcheck"]["start_period"] is not None return False def _detect_mount_tmpfs_usage(client): - for mount in client.module.params['mounts'] or []: - if mount.get('type') == 'tmpfs': + for mount in client.module.params["mounts"] or []: + if mount.get("type") == "tmpfs": return True - if mount.get('tmpfs_size') is not None: + if mount.get("tmpfs_size") is not None: return True - if mount.get('tmpfs_mode') is not None: + if mount.get("tmpfs_mode") is not None: return True return False def _detect_update_config_failure_action_rollback(client): - rollback_config_failure_action = ( - (client.module.params['update_config'] or {}).get('failure_action') + rollback_config_failure_action = (client.module.params["update_config"] or {}).get( + "failure_action" ) - return rollback_config_failure_action == 'rollback' + return rollback_config_failure_action == "rollback" def main(): argument_spec = dict( - name=dict(type='str', required=True), - image=dict(type='str'), - state=dict(type='str', default='present', choices=['present', 'absent']), - mounts=dict(type='list', elements='dict', options=dict( - source=dict(type='str'), - target=dict(type='str', required=True), - type=dict( - type='str', - default='bind', - choices=['bind', 'volume', 'tmpfs', 'npipe'], + name=dict(type="str", required=True), + image=dict(type="str"), + state=dict(type="str", default="present", choices=["present", "absent"]), + mounts=dict( + type="list", + elements="dict", + options=dict( + source=dict(type="str"), + target=dict(type="str", required=True), + type=dict( + type="str", + default="bind", + choices=["bind", "volume", "tmpfs", "npipe"], + ), + readonly=dict(type="bool"), + labels=dict(type="dict"), + propagation=dict( + type="str", + choices=[ + "shared", + "slave", + "private", + "rshared", + "rslave", + "rprivate", + ], + ), + no_copy=dict(type="bool"), + driver_config=dict( + type="dict", + options=dict(name=dict(type="str"), options=dict(type="dict")), + ), + tmpfs_size=dict(type="str"), + tmpfs_mode=dict(type="int"), ), - readonly=dict(type='bool'), - labels=dict(type='dict'), - propagation=dict( - type='str', - choices=[ - 'shared', - 'slave', - 'private', - 'rshared', - 'rslave', - 'rprivate' - ] - ), - no_copy=dict(type='bool'), - driver_config=dict(type='dict', options=dict( - name=dict(type='str'), - options=dict(type='dict') - )), - tmpfs_size=dict(type='str'), - tmpfs_mode=dict(type='int') - )), - configs=dict(type='list', elements='dict', options=dict( - config_id=dict(type='str'), - config_name=dict(type='str', required=True), - filename=dict(type='str'), - uid=dict(type='str'), - gid=dict(type='str'), - mode=dict(type='int'), - )), - secrets=dict(type='list', elements='dict', no_log=False, options=dict( - secret_id=dict(type='str', no_log=False), - secret_name=dict(type='str', required=True, no_log=False), - filename=dict(type='str'), - uid=dict(type='str'), - gid=dict(type='str'), - mode=dict(type='int'), - )), - networks=dict(type='list', elements='raw'), - command=dict(type='raw'), - args=dict(type='list', elements='str'), - env=dict(type='raw'), - env_files=dict(type='list', elements='path'), - force_update=dict(type='bool', default=False), - groups=dict(type='list', elements='str'), - logging=dict(type='dict', options=dict( - driver=dict(type='str'), - options=dict(type='dict'), - )), - publish=dict(type='list', elements='dict', options=dict( - published_port=dict(type='int', required=False), - target_port=dict(type='int', required=True), - protocol=dict(type='str', default='tcp', choices=['tcp', 'udp']), - mode=dict(type='str', choices=['ingress', 'host']), - )), - placement=dict(type='dict', options=dict( - constraints=dict(type='list', elements='str'), - preferences=dict(type='list', elements='dict'), - replicas_max_per_node=dict(type='int'), - )), - tty=dict(type='bool'), - dns=dict(type='list', elements='str'), - dns_search=dict(type='list', elements='str'), - dns_options=dict(type='list', elements='str'), - healthcheck=dict(type='dict', options=dict( - test=dict(type='raw'), - interval=dict(type='str'), - timeout=dict(type='str'), - start_period=dict(type='str'), - retries=dict(type='int'), - )), - hostname=dict(type='str'), - hosts=dict(type='dict'), - labels=dict(type='dict'), - container_labels=dict(type='dict'), - sysctls=dict(type='dict'), - mode=dict( - type='str', - default='replicated', - choices=['replicated', 'global', 'replicated-job'] ), - replicas=dict(type='int', default=-1), - endpoint_mode=dict(type='str', choices=['vip', 'dnsrr']), - stop_grace_period=dict(type='str'), - stop_signal=dict(type='str'), - limits=dict(type='dict', options=dict( - cpus=dict(type='float'), - memory=dict(type='str'), - )), - read_only=dict(type='bool'), - reservations=dict(type='dict', options=dict( - cpus=dict(type='float'), - memory=dict(type='str'), - )), - resolve_image=dict(type='bool', default=False), - restart_config=dict(type='dict', options=dict( - condition=dict(type='str', choices=['none', 'on-failure', 'any']), - delay=dict(type='str'), - max_attempts=dict(type='int'), - window=dict(type='str'), - )), - rollback_config=dict(type='dict', options=dict( - parallelism=dict(type='int'), - delay=dict(type='str'), - failure_action=dict( - type='str', - choices=['continue', 'pause'] + configs=dict( + type="list", + elements="dict", + options=dict( + config_id=dict(type="str"), + config_name=dict(type="str", required=True), + filename=dict(type="str"), + uid=dict(type="str"), + gid=dict(type="str"), + mode=dict(type="int"), ), - monitor=dict(type='str'), - max_failure_ratio=dict(type='float'), - order=dict(type='str'), - )), - update_config=dict(type='dict', options=dict( - parallelism=dict(type='int'), - delay=dict(type='str'), - failure_action=dict( - type='str', - choices=['continue', 'pause', 'rollback'] + ), + secrets=dict( + type="list", + elements="dict", + no_log=False, + options=dict( + secret_id=dict(type="str", no_log=False), + secret_name=dict(type="str", required=True, no_log=False), + filename=dict(type="str"), + uid=dict(type="str"), + gid=dict(type="str"), + mode=dict(type="int"), ), - monitor=dict(type='str'), - max_failure_ratio=dict(type='float'), - order=dict(type='str'), - )), - user=dict(type='str'), - working_dir=dict(type='str'), - init=dict(type='bool'), - cap_add=dict(type='list', elements='str'), - cap_drop=dict(type='list', elements='str'), + ), + networks=dict(type="list", elements="raw"), + command=dict(type="raw"), + args=dict(type="list", elements="str"), + env=dict(type="raw"), + env_files=dict(type="list", elements="path"), + force_update=dict(type="bool", default=False), + groups=dict(type="list", elements="str"), + logging=dict( + type="dict", + options=dict( + driver=dict(type="str"), + options=dict(type="dict"), + ), + ), + publish=dict( + type="list", + elements="dict", + options=dict( + published_port=dict(type="int", required=False), + target_port=dict(type="int", required=True), + protocol=dict(type="str", default="tcp", choices=["tcp", "udp"]), + mode=dict(type="str", choices=["ingress", "host"]), + ), + ), + placement=dict( + type="dict", + options=dict( + constraints=dict(type="list", elements="str"), + preferences=dict(type="list", elements="dict"), + replicas_max_per_node=dict(type="int"), + ), + ), + tty=dict(type="bool"), + dns=dict(type="list", elements="str"), + dns_search=dict(type="list", elements="str"), + dns_options=dict(type="list", elements="str"), + healthcheck=dict( + type="dict", + options=dict( + test=dict(type="raw"), + interval=dict(type="str"), + timeout=dict(type="str"), + start_period=dict(type="str"), + retries=dict(type="int"), + ), + ), + hostname=dict(type="str"), + hosts=dict(type="dict"), + labels=dict(type="dict"), + container_labels=dict(type="dict"), + sysctls=dict(type="dict"), + mode=dict( + type="str", + default="replicated", + choices=["replicated", "global", "replicated-job"], + ), + replicas=dict(type="int", default=-1), + endpoint_mode=dict(type="str", choices=["vip", "dnsrr"]), + stop_grace_period=dict(type="str"), + stop_signal=dict(type="str"), + limits=dict( + type="dict", + options=dict( + cpus=dict(type="float"), + memory=dict(type="str"), + ), + ), + read_only=dict(type="bool"), + reservations=dict( + type="dict", + options=dict( + cpus=dict(type="float"), + memory=dict(type="str"), + ), + ), + resolve_image=dict(type="bool", default=False), + restart_config=dict( + type="dict", + options=dict( + condition=dict(type="str", choices=["none", "on-failure", "any"]), + delay=dict(type="str"), + max_attempts=dict(type="int"), + window=dict(type="str"), + ), + ), + rollback_config=dict( + type="dict", + options=dict( + parallelism=dict(type="int"), + delay=dict(type="str"), + failure_action=dict(type="str", choices=["continue", "pause"]), + monitor=dict(type="str"), + max_failure_ratio=dict(type="float"), + order=dict(type="str"), + ), + ), + update_config=dict( + type="dict", + options=dict( + parallelism=dict(type="int"), + delay=dict(type="str"), + failure_action=dict( + type="str", choices=["continue", "pause", "rollback"] + ), + monitor=dict(type="str"), + max_failure_ratio=dict(type="float"), + order=dict(type="str"), + ), + ), + user=dict(type="str"), + working_dir=dict(type="str"), + init=dict(type="bool"), + cap_add=dict(type="list", elements="str"), + cap_drop=dict(type="list", elements="str"), ) option_minimal_versions = dict( - dns=dict(docker_py_version='2.6.0'), - dns_options=dict(docker_py_version='2.6.0'), - dns_search=dict(docker_py_version='2.6.0'), - endpoint_mode=dict(docker_py_version='3.0.0'), - force_update=dict(docker_py_version='2.1.0'), - healthcheck=dict(docker_py_version='2.6.0'), - hostname=dict(docker_py_version='2.2.0'), - hosts=dict(docker_py_version='2.6.0'), - groups=dict(docker_py_version='2.6.0'), - tty=dict(docker_py_version='2.4.0'), - secrets=dict(docker_py_version='2.4.0'), - configs=dict(docker_py_version='2.6.0', docker_api_version='1.30'), - stop_signal=dict(docker_py_version='2.6.0', docker_api_version='1.28'), - publish=dict(docker_py_version='3.0.0'), - read_only=dict(docker_py_version='2.6.0', docker_api_version='1.28'), - resolve_image=dict(docker_api_version='1.30', docker_py_version='3.2.0'), - rollback_config=dict(docker_py_version='3.5.0', docker_api_version='1.28'), - init=dict(docker_py_version='4.0.0', docker_api_version='1.37'), - cap_add=dict(docker_py_version='5.0.3', docker_api_version='1.41'), - cap_drop=dict(docker_py_version='5.0.3', docker_api_version='1.41'), - sysctls=dict(docker_py_version='6.0.0', docker_api_version='1.40'), + dns=dict(docker_py_version="2.6.0"), + dns_options=dict(docker_py_version="2.6.0"), + dns_search=dict(docker_py_version="2.6.0"), + endpoint_mode=dict(docker_py_version="3.0.0"), + force_update=dict(docker_py_version="2.1.0"), + healthcheck=dict(docker_py_version="2.6.0"), + hostname=dict(docker_py_version="2.2.0"), + hosts=dict(docker_py_version="2.6.0"), + groups=dict(docker_py_version="2.6.0"), + tty=dict(docker_py_version="2.4.0"), + secrets=dict(docker_py_version="2.4.0"), + configs=dict(docker_py_version="2.6.0", docker_api_version="1.30"), + stop_signal=dict(docker_py_version="2.6.0", docker_api_version="1.28"), + publish=dict(docker_py_version="3.0.0"), + read_only=dict(docker_py_version="2.6.0", docker_api_version="1.28"), + resolve_image=dict(docker_api_version="1.30", docker_py_version="3.2.0"), + rollback_config=dict(docker_py_version="3.5.0", docker_api_version="1.28"), + init=dict(docker_py_version="4.0.0", docker_api_version="1.37"), + cap_add=dict(docker_py_version="5.0.3", docker_api_version="1.41"), + cap_drop=dict(docker_py_version="5.0.3", docker_api_version="1.41"), + sysctls=dict(docker_py_version="6.0.0", docker_api_version="1.40"), # specials publish_mode=dict( - docker_py_version='3.0.0', + docker_py_version="3.0.0", detect_usage=_detect_publish_mode_usage, - usage_msg='set publish.mode' + usage_msg="set publish.mode", ), healthcheck_start_period=dict( - docker_py_version='2.6.0', - docker_api_version='1.29', + docker_py_version="2.6.0", + docker_api_version="1.29", detect_usage=_detect_healthcheck_start_period, - usage_msg='set healthcheck.start_period' + usage_msg="set healthcheck.start_period", ), update_config_max_failure_ratio=dict( - docker_py_version='2.1.0', - detect_usage=lambda c: (c.module.params['update_config'] or {}).get( - 'max_failure_ratio' - ) is not None, - usage_msg='set update_config.max_failure_ratio' + docker_py_version="2.1.0", + detect_usage=lambda c: (c.module.params["update_config"] or {}).get( + "max_failure_ratio" + ) + is not None, + usage_msg="set update_config.max_failure_ratio", ), update_config_failure_action=dict( - docker_py_version='3.5.0', - docker_api_version='1.28', + docker_py_version="3.5.0", + docker_api_version="1.28", detect_usage=_detect_update_config_failure_action_rollback, - usage_msg='set update_config.failure_action.rollback' + usage_msg="set update_config.failure_action.rollback", ), update_config_monitor=dict( - docker_py_version='2.1.0', - detect_usage=lambda c: (c.module.params['update_config'] or {}).get( - 'monitor' - ) is not None, - usage_msg='set update_config.monitor' + docker_py_version="2.1.0", + detect_usage=lambda c: (c.module.params["update_config"] or {}).get( + "monitor" + ) + is not None, + usage_msg="set update_config.monitor", ), update_config_order=dict( - docker_py_version='2.7.0', - docker_api_version='1.29', - detect_usage=lambda c: (c.module.params['update_config'] or {}).get( - 'order' - ) is not None, - usage_msg='set update_config.order' + docker_py_version="2.7.0", + docker_api_version="1.29", + detect_usage=lambda c: (c.module.params["update_config"] or {}).get("order") + is not None, + usage_msg="set update_config.order", ), placement_config_preferences=dict( - docker_py_version='2.4.0', - docker_api_version='1.27', - detect_usage=lambda c: (c.module.params['placement'] or {}).get( - 'preferences' - ) is not None, - usage_msg='set placement.preferences' + docker_py_version="2.4.0", + docker_api_version="1.27", + detect_usage=lambda c: (c.module.params["placement"] or {}).get( + "preferences" + ) + is not None, + usage_msg="set placement.preferences", ), placement_config_constraints=dict( - docker_py_version='2.4.0', - detect_usage=lambda c: (c.module.params['placement'] or {}).get( - 'constraints' - ) is not None, - usage_msg='set placement.constraints' + docker_py_version="2.4.0", + detect_usage=lambda c: (c.module.params["placement"] or {}).get( + "constraints" + ) + is not None, + usage_msg="set placement.constraints", ), placement_config_replicas_max_per_node=dict( - docker_py_version='4.4.3', - docker_api_version='1.40', - detect_usage=lambda c: (c.module.params['placement'] or {}).get( - 'replicas_max_per_node' - ) is not None, - usage_msg='set placement.replicas_max_per_node' + docker_py_version="4.4.3", + docker_api_version="1.40", + detect_usage=lambda c: (c.module.params["placement"] or {}).get( + "replicas_max_per_node" + ) + is not None, + usage_msg="set placement.replicas_max_per_node", ), mounts_tmpfs=dict( - docker_py_version='2.6.0', + docker_py_version="2.6.0", detect_usage=_detect_mount_tmpfs_usage, - usage_msg='set mounts.tmpfs' + usage_msg="set mounts.tmpfs", ), rollback_config_order=dict( - docker_api_version='1.29', - detect_usage=lambda c: (c.module.params['rollback_config'] or {}).get( - 'order' - ) is not None, - usage_msg='set rollback_config.order' + docker_api_version="1.29", + detect_usage=lambda c: (c.module.params["rollback_config"] or {}).get( + "order" + ) + is not None, + usage_msg="set rollback_config.order", ), mode_replicated_job=dict( - docker_py_version='6.0.0', - docker_api_version='1.41', - detect_usage=lambda c: c.module.params.get('mode') == 'replicated-job', - usage_msg='set mode' + docker_py_version="6.0.0", + docker_api_version="1.41", + detect_usage=lambda c: c.module.params.get("mode") == "replicated-job", + usage_msg="set mode", ), ) - required_if = [ - ('state', 'present', ['image']) - ] + required_if = [("state", "present", ["image"])] client = AnsibleDockerClient( argument_spec=argument_spec, required_if=required_if, supports_check_mode=True, - min_docker_version='2.0.2', + min_docker_version="2.0.2", option_minimal_versions=option_minimal_versions, ) @@ -2783,16 +2938,20 @@ def main(): ) if client.module._diff: before, after = dsm.diff_tracker.get_before_after() - results['diff'] = dict(before=before, after=after) + results["diff"] = dict(before=before, after=after) client.module.exit_json(**results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_swarm_service_info.py b/plugins/modules/docker_swarm_service_info.py index 3fa084eb..85a2f3f8 100644 --- a/plugins/modules/docker_swarm_service_info.py +++ b/plugins/modules/docker_swarm_service_info.py @@ -64,6 +64,7 @@ service: import traceback + try: from docker.errors import DockerException except ImportError: @@ -73,27 +74,25 @@ except ImportError: from ansible_collections.community.docker.plugins.module_utils.common import ( RequestException, ) - -from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient +from ansible_collections.community.docker.plugins.module_utils.swarm import ( + AnsibleDockerSwarmClient, +) def get_service_info(client): - service = client.module.params['name'] - return client.get_service_inspect( - service_id=service, - skip_missing=True - ) + service = client.module.params["name"] + return client.get_service_inspect(service_id=service, skip_missing=True) def main(): argument_spec = dict( - name=dict(type='str', required=True), + name=dict(type="str", required=True), ) client = AnsibleDockerSwarmClient( argument_spec=argument_spec, supports_check_mode=True, - min_docker_version='2.0.0', + min_docker_version="2.0.0", ) client.fail_task_if_not_swarm_manager() @@ -101,18 +100,18 @@ def main(): try: service = get_service_info(client) - client.module.exit_json( - changed=False, - service=service, - exists=bool(service) - ) + client.module.exit_json(changed=False, service=service, exists=bool(service)) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_volume.py b/plugins/modules/docker_volume.py index 16d43627..9c896df6 100644 --- a/plugins/modules/docker_volume.py +++ b/plugins/modules/docker_volume.py @@ -120,20 +120,19 @@ volume: import traceback from ansible.module_utils.common.text.converters import to_native - +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + APIError, + DockerException, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) from ansible_collections.community.docker.plugins.module_utils.util import ( - DockerBaseClass, DifferenceTracker, + DockerBaseClass, sanitize_labels, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import ( - APIError, - DockerException, -) class TaskParameters(DockerBaseClass): @@ -158,10 +157,7 @@ class DockerVolumeManager(object): self.client = client self.parameters = TaskParameters(client) self.check_mode = self.client.check_mode - self.results = { - 'changed': False, - 'actions': [] - } + self.results = {"changed": False, "actions": []} self.diff = self.client.module._diff self.diff_tracker = DifferenceTracker() self.diff_result = dict() @@ -169,27 +165,29 @@ class DockerVolumeManager(object): self.existing_volume = self.get_existing_volume() state = self.parameters.state - if state == 'present': + if state == "present": self.present() - elif state == 'absent': + elif state == "absent": self.absent() if self.diff or self.check_mode or self.parameters.debug: if self.diff: - self.diff_result['before'], self.diff_result['after'] = self.diff_tracker.get_before_after() - self.results['diff'] = self.diff_result + self.diff_result["before"], self.diff_result["after"] = ( + self.diff_tracker.get_before_after() + ) + self.results["diff"] = self.diff_result def get_existing_volume(self): try: - volumes = self.client.get_json('/volumes') + volumes = self.client.get_json("/volumes") except APIError as e: self.client.fail(to_native(e)) - if volumes['Volumes'] is None: + if volumes["Volumes"] is None: return None - for volume in volumes['Volumes']: - if volume['Name'] == self.parameters.volume_name: + for volume in volumes["Volumes"]: + if volume["Name"] == self.parameters.volume_name: return volume return None @@ -201,27 +199,42 @@ class DockerVolumeManager(object): :return: list of options that differ """ differences = DifferenceTracker() - if self.parameters.driver and self.parameters.driver != self.existing_volume['Driver']: - differences.add('driver', parameter=self.parameters.driver, active=self.existing_volume['Driver']) + if ( + self.parameters.driver + and self.parameters.driver != self.existing_volume["Driver"] + ): + differences.add( + "driver", + parameter=self.parameters.driver, + active=self.existing_volume["Driver"], + ) if self.parameters.driver_options: - if not self.existing_volume.get('Options'): - differences.add('driver_options', - parameter=self.parameters.driver_options, - active=self.existing_volume.get('Options')) + if not self.existing_volume.get("Options"): + differences.add( + "driver_options", + parameter=self.parameters.driver_options, + active=self.existing_volume.get("Options"), + ) else: for key, value in self.parameters.driver_options.items(): - if (not self.existing_volume['Options'].get(key) or - value != self.existing_volume['Options'][key]): - differences.add(f'driver_options.{key}', - parameter=value, - active=self.existing_volume['Options'].get(key)) + if ( + not self.existing_volume["Options"].get(key) + or value != self.existing_volume["Options"][key] + ): + differences.add( + f"driver_options.{key}", + parameter=value, + active=self.existing_volume["Options"].get(key), + ) if self.parameters.labels: - existing_labels = self.existing_volume.get('Labels') or {} + existing_labels = self.existing_volume.get("Labels") or {} for label in self.parameters.labels: if existing_labels.get(label) != self.parameters.labels.get(label): - differences.add(f'labels.{label}', - parameter=self.parameters.labels.get(label), - active=existing_labels.get(label)) + differences.add( + f"labels.{label}", + parameter=self.parameters.labels.get(label), + active=existing_labels.get(label), + ) return differences @@ -230,67 +243,81 @@ class DockerVolumeManager(object): if not self.check_mode: try: data = { - 'Name': self.parameters.volume_name, - 'Driver': self.parameters.driver, - 'DriverOpts': self.parameters.driver_options, + "Name": self.parameters.volume_name, + "Driver": self.parameters.driver, + "DriverOpts": self.parameters.driver_options, } if self.parameters.labels is not None: - data['Labels'] = self.parameters.labels - resp = self.client.post_json_to_json('/volumes/create', data=data) - self.existing_volume = self.client.get_json('/volumes/{0}', resp['Name']) + data["Labels"] = self.parameters.labels + resp = self.client.post_json_to_json("/volumes/create", data=data) + self.existing_volume = self.client.get_json( + "/volumes/{0}", resp["Name"] + ) except APIError as e: self.client.fail(to_native(e)) - self.results['actions'].append(f"Created volume {self.parameters.volume_name} with driver {self.parameters.driver}") - self.results['changed'] = True + self.results["actions"].append( + f"Created volume {self.parameters.volume_name} with driver {self.parameters.driver}" + ) + self.results["changed"] = True def remove_volume(self): if self.existing_volume: if not self.check_mode: try: - self.client.delete_call('/volumes/{0}', self.parameters.volume_name) + self.client.delete_call("/volumes/{0}", self.parameters.volume_name) except APIError as e: self.client.fail(to_native(e)) - self.results['actions'].append(f"Removed volume {self.parameters.volume_name}") - self.results['changed'] = True + self.results["actions"].append( + f"Removed volume {self.parameters.volume_name}" + ) + self.results["changed"] = True def present(self): differences = DifferenceTracker() if self.existing_volume: differences = self.has_different_config() - self.diff_tracker.add('exists', parameter=True, active=self.existing_volume is not None) - if (not differences.empty and self.parameters.recreate == 'options-changed') or self.parameters.recreate == 'always': + self.diff_tracker.add( + "exists", parameter=True, active=self.existing_volume is not None + ) + if ( + not differences.empty and self.parameters.recreate == "options-changed" + ) or self.parameters.recreate == "always": self.remove_volume() self.existing_volume = None self.create_volume() if self.diff or self.check_mode or self.parameters.debug: - self.diff_result['differences'] = differences.get_legacy_docker_diffs() + self.diff_result["differences"] = differences.get_legacy_docker_diffs() self.diff_tracker.merge(differences) if not self.check_mode and not self.parameters.debug: - self.results.pop('actions') + self.results.pop("actions") volume_facts = self.get_existing_volume() - self.results['volume'] = volume_facts + self.results["volume"] = volume_facts def absent(self): - self.diff_tracker.add('exists', parameter=False, active=self.existing_volume is not None) + self.diff_tracker.add( + "exists", parameter=False, active=self.existing_volume is not None + ) self.remove_volume() def main(): argument_spec = dict( - volume_name=dict(type='str', required=True, aliases=['name']), - state=dict(type='str', default='present', choices=['present', 'absent']), - driver=dict(type='str', default='local'), - driver_options=dict(type='dict', default={}), - labels=dict(type='dict'), - recreate=dict(type='str', default='never', choices=['always', 'never', 'options-changed']), - debug=dict(type='bool', default=False) + volume_name=dict(type="str", required=True, aliases=["name"]), + state=dict(type="str", default="present", choices=["present", "absent"]), + driver=dict(type="str", default="local"), + driver_options=dict(type="dict", default={}), + labels=dict(type="dict"), + recreate=dict( + type="str", default="never", choices=["always", "never", "options-changed"] + ), + debug=dict(type="bool", default=False), ) client = AnsibleDockerClient( @@ -298,18 +325,22 @@ def main(): supports_check_mode=True, # "The docker server >= 1.9.0" ) - sanitize_labels(client.module.params['labels'], 'labels', client) + sanitize_labels(client.module.params["labels"], "labels", client) try: cm = DockerVolumeManager(client) client.module.exit_json(**cm.results) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/docker_volume_info.py b/plugins/modules/docker_volume_info.py index 986fe797..e595f707 100644 --- a/plugins/modules/docker_volume_info.py +++ b/plugins/modules/docker_volume_info.py @@ -72,16 +72,19 @@ volume: import traceback +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, + NotFound, +) from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClient, RequestException, ) -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException, NotFound def get_existing_volume(client, volume_name): try: - return client.get_json('/volumes/{0}', volume_name) + return client.get_json("/volumes/{0}", volume_name) except NotFound as dummy: return None except Exception as exc: @@ -90,7 +93,7 @@ def get_existing_volume(client, volume_name): def main(): argument_spec = dict( - name=dict(type='str', required=True, aliases=['volume_name']), + name=dict(type="str", required=True, aliases=["volume_name"]), ) client = AnsibleDockerClient( @@ -99,7 +102,7 @@ def main(): ) try: - volume = get_existing_volume(client, client.module.params['name']) + volume = get_existing_volume(client, client.module.params["name"]) client.module.exit_json( changed=False, @@ -107,12 +110,16 @@ def main(): volume=volume, ) except DockerException as e: - client.fail(f'An unexpected Docker error occurred: {e}', exception=traceback.format_exc()) + client.fail( + f"An unexpected Docker error occurred: {e}", + exception=traceback.format_exc(), + ) except RequestException as e: client.fail( - f'An unexpected requests error occurred when trying to talk to the Docker daemon: {e}', - exception=traceback.format_exc()) + f"An unexpected requests error occurred when trying to talk to the Docker daemon: {e}", + exception=traceback.format_exc(), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/plugin_utils/common.py b/plugins/plugin_utils/common.py index 65941f84..0ac5bd0b 100644 --- a/plugins/plugin_utils/common.py +++ b/plugins/plugin_utils/common.py @@ -2,17 +2,17 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type from ansible.errors import AnsibleConnectionFailure from ansible.utils.display import Display - from ansible_collections.community.docker.plugins.module_utils.common import ( AnsibleDockerClientBase, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DOCKER_COMMON_ARGS, ) @@ -24,18 +24,22 @@ class AnsibleDockerClient(AnsibleDockerClientBase): self.display = Display() super(AnsibleDockerClient, self).__init__( min_docker_version=min_docker_version, - min_docker_api_version=min_docker_api_version) + min_docker_api_version=min_docker_api_version, + ) def fail(self, msg, **kwargs): if kwargs: - msg += '\nContext:\n' + '\n'.join(f' {k} = {v!r}' for (k, v) in kwargs.items()) + msg += "\nContext:\n" + "\n".join( + f" {k} = {v!r}" for (k, v) in kwargs.items() + ) raise AnsibleConnectionFailure(msg) def deprecate(self, msg, version=None, date=None, collection_name=None): - self.display.deprecated(msg, version=version, date=date, collection_name=collection_name) + self.display.deprecated( + msg, version=version, date=date, collection_name=collection_name + ) def _get_params(self): - return dict([ - (option, self.plugin.get_option(option)) - for option in DOCKER_COMMON_ARGS - ]) + return dict( + [(option, self.plugin.get_option(option)) for option in DOCKER_COMMON_ARGS] + ) diff --git a/plugins/plugin_utils/common_api.py b/plugins/plugin_utils/common_api.py index f096a9a3..99740a6d 100644 --- a/plugins/plugin_utils/common_api.py +++ b/plugins/plugin_utils/common_api.py @@ -4,14 +4,11 @@ from __future__ import annotations - from ansible.errors import AnsibleConnectionFailure from ansible.utils.display import Display - from ansible_collections.community.docker.plugins.module_utils.common_api import ( AnsibleDockerClientBase, ) - from ansible_collections.community.docker.plugins.module_utils.util import ( DOCKER_COMMON_ARGS, ) @@ -22,18 +19,22 @@ class AnsibleDockerClient(AnsibleDockerClientBase): self.plugin = plugin self.display = Display() super(AnsibleDockerClient, self).__init__( - min_docker_api_version=min_docker_api_version) + min_docker_api_version=min_docker_api_version + ) def fail(self, msg, **kwargs): if kwargs: - msg += '\nContext:\n' + '\n'.join(f' {k} = {v!r}' for (k, v) in kwargs.items()) + msg += "\nContext:\n" + "\n".join( + f" {k} = {v!r}" for (k, v) in kwargs.items() + ) raise AnsibleConnectionFailure(msg) def deprecate(self, msg, version=None, date=None, collection_name=None): - self.display.deprecated(msg, version=version, date=date, collection_name=collection_name) + self.display.deprecated( + msg, version=version, date=date, collection_name=collection_name + ) def _get_params(self): - return dict([ - (option, self.plugin.get_option(option)) - for option in DOCKER_COMMON_ARGS - ]) + return dict( + [(option, self.plugin.get_option(option)) for option in DOCKER_COMMON_ARGS] + ) diff --git a/plugins/plugin_utils/socket_handler.py b/plugins/plugin_utils/socket_handler.py index 54d0e1e9..4050ed8d 100644 --- a/plugins/plugin_utils/socket_handler.py +++ b/plugins/plugin_utils/socket_handler.py @@ -2,7 +2,9 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type @@ -15,4 +17,6 @@ from ansible_collections.community.docker.plugins.module_utils.socket_handler im class DockerSocketHandler(DockerSocketHandlerBase): def __init__(self, display, sock, log=None, container=None): - super(DockerSocketHandler, self).__init__(sock, selectors, log=lambda msg: display.vvvv(msg, host=container)) + super(DockerSocketHandler, self).__init__( + sock, selectors, log=lambda msg: display.vvvv(msg, host=container) + ) diff --git a/plugins/plugin_utils/unsafe.py b/plugins/plugin_utils/unsafe.py index a6ad363f..6c3c658f 100644 --- a/plugins/plugin_utils/unsafe.py +++ b/plugins/plugin_utils/unsafe.py @@ -5,16 +5,17 @@ from __future__ import annotations import re - from collections.abc import Mapping, Set + from ansible.module_utils.common.collections import is_sequence from ansible.utils.unsafe_proxy import ( AnsibleUnsafe, - wrap_var as _make_unsafe, ) +from ansible.utils.unsafe_proxy import wrap_var as _make_unsafe -_RE_TEMPLATE_CHARS = re.compile('[{}]') -_RE_TEMPLATE_CHARS_BYTES = re.compile(b'[{}]') + +_RE_TEMPLATE_CHARS = re.compile("[{}]") +_RE_TEMPLATE_CHARS_BYTES = re.compile(b"[{}]") def make_unsafe(value): diff --git a/tests/unit/plugins/connection/test_docker.py b/tests/unit/plugins/connection/test_docker.py index e0a2c7fc..c0278fc2 100644 --- a/tests/unit/plugins/connection/test_docker.py +++ b/tests/unit/plugins/connection/test_docker.py @@ -2,17 +2,20 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type from io import StringIO -from ansible_collections.community.internal_test_tools.tests.unit.compat import mock -from ansible_collections.community.internal_test_tools.tests.unit.compat import unittest - from ansible.errors import AnsibleError from ansible.playbook.play_context import PlayContext from ansible.plugins.loader import connection_loader +from ansible_collections.community.internal_test_tools.tests.unit.compat import ( + mock, + unittest, +) class TestDockerConnectionClass(unittest.TestCase): @@ -20,38 +23,69 @@ class TestDockerConnectionClass(unittest.TestCase): def setUp(self): self.play_context = PlayContext() self.play_context.prompt = ( - '[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: ' + "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " ) self.in_stream = StringIO() - with mock.patch('ansible_collections.community.docker.plugins.connection.docker.get_bin_path', return_value='docker'): - self.dc = connection_loader.get('community.docker.docker', self.play_context, self.in_stream) + with mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.get_bin_path", + return_value="docker", + ): + self.dc = connection_loader.get( + "community.docker.docker", self.play_context, self.in_stream + ) def tearDown(self): pass - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version', - return_value=('false', 'garbage', '', 1)) - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version', - return_value=('docker version', '1.2.3', '', 0)) - def test_docker_connection_module_too_old(self, mock_new_docker_version, mock_old_docker_version): + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version", + return_value=("false", "garbage", "", 1), + ) + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version", + return_value=("docker version", "1.2.3", "", 0), + ) + def test_docker_connection_module_too_old( + self, mock_new_docker_version, mock_old_docker_version + ): self.dc._version = None - self.dc.remote_user = 'foo' - self.assertRaisesRegex(AnsibleError, '^docker connection type requires docker 1.3 or higher$', self.dc._get_actual_user) + self.dc.remote_user = "foo" + self.assertRaisesRegex( + AnsibleError, + "^docker connection type requires docker 1.3 or higher$", + self.dc._get_actual_user, + ) - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version', - return_value=('false', 'garbage', '', 1)) - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version', - return_value=('docker version', '1.7.0', '', 0)) - def test_docker_connection_module(self, mock_new_docker_version, mock_old_docker_version): + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version", + return_value=("false", "garbage", "", 1), + ) + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version", + return_value=("docker version", "1.7.0", "", 0), + ) + def test_docker_connection_module( + self, mock_new_docker_version, mock_old_docker_version + ): self.dc._version = None version = self.dc.docker_version # old version and new version fail - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version', - return_value=('false', 'garbage', '', 1)) - @mock.patch('ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version', - return_value=('false', 'garbage', '', 1)) - def test_docker_connection_module_wrong_cmd(self, mock_new_docker_version, mock_old_docker_version): + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._old_docker_version", + return_value=("false", "garbage", "", 1), + ) + @mock.patch( + "ansible_collections.community.docker.plugins.connection.docker.Connection._new_docker_version", + return_value=("false", "garbage", "", 1), + ) + def test_docker_connection_module_wrong_cmd( + self, mock_new_docker_version, mock_old_docker_version + ): self.dc._version = None - self.dc.remote_user = 'foo' - self.assertRaisesRegex(AnsibleError, '^Docker version check (.*?) failed:', self.dc._get_actual_user) + self.dc.remote_user = "foo" + self.assertRaisesRegex( + AnsibleError, + "^Docker version check (.*?) failed:", + self.dc._get_actual_user, + ) diff --git a/tests/unit/plugins/inventory/test_docker_containers.py b/tests/unit/plugins/inventory/test_docker_containers.py index 7bdb7c3f..e8c61d20 100644 --- a/tests/unit/plugins/inventory/test_docker_containers.py +++ b/tests/unit/plugins/inventory/test_docker_containers.py @@ -2,20 +2,25 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible.inventory.data import InventoryData from ansible.parsing.dataloader import DataLoader from ansible.template import Templar - -from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import create_autospec -from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import make_trusted - -from ansible_collections.community.docker.plugins.inventory.docker_containers import InventoryModule +from ansible_collections.community.docker.plugins.inventory.docker_containers import ( + InventoryModule, +) +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import ( + create_autospec, +) +from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( + make_trusted, +) @pytest.fixture(scope="module") @@ -33,55 +38,50 @@ def inventory(templar): LOVING_THARP = { - 'Id': '7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a', - 'Name': '/loving_tharp', - 'Image': 'sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385', - 'State': { - 'Running': True, + "Id": "7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a", + "Name": "/loving_tharp", + "Image": "sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385", + "State": { + "Running": True, }, - 'Config': { - 'Image': 'quay.io/ansible/ubuntu1804-test-container:1.21.0', + "Config": { + "Image": "quay.io/ansible/ubuntu1804-test-container:1.21.0", }, } LOVING_THARP_STACK = { - 'Id': '7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a', - 'Name': '/loving_tharp', - 'Image': 'sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385', - 'State': { - 'Running': True, + "Id": "7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a", + "Name": "/loving_tharp", + "Image": "sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385", + "State": { + "Running": True, }, - 'Config': { - 'Image': 'quay.io/ansible/ubuntu1804-test-container:1.21.0', - 'Labels': { - 'com.docker.stack.namespace': 'my_stack', + "Config": { + "Image": "quay.io/ansible/ubuntu1804-test-container:1.21.0", + "Labels": { + "com.docker.stack.namespace": "my_stack", }, }, - 'NetworkSettings': { - 'Ports': { - '22/tcp': [ - { - 'HostIp': '0.0.0.0', - 'HostPort': '32802' - } - ], + "NetworkSettings": { + "Ports": { + "22/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32802"}], }, }, } LOVING_THARP_SERVICE = { - 'Id': '7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a', - 'Name': '/loving_tharp', - 'Image': 'sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385', - 'State': { - 'Running': True, + "Id": "7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a", + "Name": "/loving_tharp", + "Image": "sha256:349f492ff18add678364a62a67ce9a13487f14293ae0af1baf02398aa432f385", + "State": { + "Running": True, }, - 'Config': { - 'Image': 'quay.io/ansible/ubuntu1804-test-container:1.21.0', - 'Labels': { - 'com.docker.swarm.service.name': 'my_service', + "Config": { + "Image": "quay.io/ansible/ubuntu1804-test-container:1.21.0", + "Labels": { + "com.docker.swarm.service.name": "my_service", }, }, } @@ -101,15 +101,17 @@ class FakeClient(object): self.get_results = {} list_reply = [] for host in hosts: - list_reply.append({ - 'Id': host['Id'], - 'Names': [host['Name']] if host['Name'] else [], - 'Image': host['Config']['Image'], - 'ImageId': host['Image'], - }) + list_reply.append( + { + "Id": host["Id"], + "Names": [host["Name"]] if host["Name"] else [], + "Image": host["Config"]["Image"], + "ImageId": host["Image"], + } + ) self.get_results[f"/containers/{host['Name']}/json"] = host self.get_results[f"/containers/{host['Id']}/json"] = host - self.get_results['/containers/json'] = list_reply + self.get_results["/containers/json"] = list_reply def get_json(self, url, *param, **kwargs): url = url.format(*param) @@ -119,30 +121,34 @@ class FakeClient(object): def test_populate(inventory, mocker): client = FakeClient(LOVING_THARP) - inventory.get_option = mocker.MagicMock(side_effect=create_get_option({ - 'verbose_output': True, - 'connection_type': 'docker-api', - 'add_legacy_groups': False, - 'compose': {}, - 'groups': {}, - 'keyed_groups': {}, - 'filters': None, - })) + inventory.get_option = mocker.MagicMock( + side_effect=create_get_option( + { + "verbose_output": True, + "connection_type": "docker-api", + "add_legacy_groups": False, + "compose": {}, + "groups": {}, + "keyed_groups": {}, + "filters": None, + } + ) + ) inventory._populate(client) - host_1 = inventory.inventory.get_host('loving_tharp') + host_1 = inventory.inventory.get_host("loving_tharp") host_1_vars = host_1.get_vars() - assert host_1_vars['ansible_host'] == 'loving_tharp' - assert host_1_vars['ansible_connection'] == 'community.docker.docker_api' - assert 'ansible_ssh_host' not in host_1_vars - assert 'ansible_ssh_port' not in host_1_vars - assert 'docker_state' in host_1_vars - assert 'docker_config' in host_1_vars - assert 'docker_image' in host_1_vars + assert host_1_vars["ansible_host"] == "loving_tharp" + assert host_1_vars["ansible_connection"] == "community.docker.docker_api" + assert "ansible_ssh_host" not in host_1_vars + assert "ansible_ssh_port" not in host_1_vars + assert "docker_state" in host_1_vars + assert "docker_config" in host_1_vars + assert "docker_image" in host_1_vars - assert len(inventory.inventory.groups['ungrouped'].hosts) == 0 - assert len(inventory.inventory.groups['all'].hosts) == 0 + assert len(inventory.inventory.groups["ungrouped"].hosts) == 0 + assert len(inventory.inventory.groups["all"].hosts) == 0 assert len(inventory.inventory.groups) == 2 assert len(inventory.inventory.hosts) == 1 @@ -150,39 +156,57 @@ def test_populate(inventory, mocker): def test_populate_service(inventory, mocker): client = FakeClient(LOVING_THARP_SERVICE) - inventory.get_option = mocker.MagicMock(side_effect=create_get_option({ - 'verbose_output': False, - 'connection_type': 'docker-cli', - 'add_legacy_groups': True, - 'compose': {}, - 'groups': {}, - 'keyed_groups': {}, - 'docker_host': 'unix://var/run/docker.sock', - 'filters': None, - })) + inventory.get_option = mocker.MagicMock( + side_effect=create_get_option( + { + "verbose_output": False, + "connection_type": "docker-cli", + "add_legacy_groups": True, + "compose": {}, + "groups": {}, + "keyed_groups": {}, + "docker_host": "unix://var/run/docker.sock", + "filters": None, + } + ) + ) inventory._populate(client) - host_1 = inventory.inventory.get_host('loving_tharp') + host_1 = inventory.inventory.get_host("loving_tharp") host_1_vars = host_1.get_vars() - assert host_1_vars['ansible_host'] == 'loving_tharp' - assert host_1_vars['ansible_connection'] == 'community.docker.docker' - assert 'ansible_ssh_host' not in host_1_vars - assert 'ansible_ssh_port' not in host_1_vars - assert 'docker_state' not in host_1_vars - assert 'docker_config' not in host_1_vars - assert 'docker_image' not in host_1_vars + assert host_1_vars["ansible_host"] == "loving_tharp" + assert host_1_vars["ansible_connection"] == "community.docker.docker" + assert "ansible_ssh_host" not in host_1_vars + assert "ansible_ssh_port" not in host_1_vars + assert "docker_state" not in host_1_vars + assert "docker_config" not in host_1_vars + assert "docker_image" not in host_1_vars - assert len(inventory.inventory.groups['ungrouped'].hosts) == 0 - assert len(inventory.inventory.groups['all'].hosts) == 0 - assert len(inventory.inventory.groups['7bd547963679e'].hosts) == 1 - assert len(inventory.inventory.groups['7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a'].hosts) == 1 - assert len(inventory.inventory.groups['image_quay.io/ansible/ubuntu1804-test-container:1.21.0'].hosts) == 1 - assert len(inventory.inventory.groups['loving_tharp'].hosts) == 1 - assert len(inventory.inventory.groups['running'].hosts) == 1 - assert len(inventory.inventory.groups['stopped'].hosts) == 0 - assert len(inventory.inventory.groups['service_my_service'].hosts) == 1 - assert len(inventory.inventory.groups['unix://var/run/docker.sock'].hosts) == 1 + assert len(inventory.inventory.groups["ungrouped"].hosts) == 0 + assert len(inventory.inventory.groups["all"].hosts) == 0 + assert len(inventory.inventory.groups["7bd547963679e"].hosts) == 1 + assert ( + len( + inventory.inventory.groups[ + "7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a" + ].hosts + ) + == 1 + ) + assert ( + len( + inventory.inventory.groups[ + "image_quay.io/ansible/ubuntu1804-test-container:1.21.0" + ].hosts + ) + == 1 + ) + assert len(inventory.inventory.groups["loving_tharp"].hosts) == 1 + assert len(inventory.inventory.groups["running"].hosts) == 1 + assert len(inventory.inventory.groups["stopped"].hosts) == 0 + assert len(inventory.inventory.groups["service_my_service"].hosts) == 1 + assert len(inventory.inventory.groups["unix://var/run/docker.sock"].hosts) == 1 assert len(inventory.inventory.groups) == 10 assert len(inventory.inventory.hosts) == 1 @@ -190,41 +214,59 @@ def test_populate_service(inventory, mocker): def test_populate_stack(inventory, mocker): client = FakeClient(LOVING_THARP_STACK) - inventory.get_option = mocker.MagicMock(side_effect=create_get_option({ - 'verbose_output': False, - 'connection_type': 'ssh', - 'add_legacy_groups': True, - 'compose': {}, - 'groups': {}, - 'keyed_groups': {}, - 'docker_host': 'unix://var/run/docker.sock', - 'default_ip': '127.0.0.1', - 'private_ssh_port': 22, - 'filters': None, - })) + inventory.get_option = mocker.MagicMock( + side_effect=create_get_option( + { + "verbose_output": False, + "connection_type": "ssh", + "add_legacy_groups": True, + "compose": {}, + "groups": {}, + "keyed_groups": {}, + "docker_host": "unix://var/run/docker.sock", + "default_ip": "127.0.0.1", + "private_ssh_port": 22, + "filters": None, + } + ) + ) inventory._populate(client) - host_1 = inventory.inventory.get_host('loving_tharp') + host_1 = inventory.inventory.get_host("loving_tharp") host_1_vars = host_1.get_vars() - assert host_1_vars['ansible_ssh_host'] == '127.0.0.1' - assert host_1_vars['ansible_ssh_port'] == '32802' - assert 'ansible_host' not in host_1_vars - assert 'ansible_connection' not in host_1_vars - assert 'docker_state' not in host_1_vars - assert 'docker_config' not in host_1_vars - assert 'docker_image' not in host_1_vars + assert host_1_vars["ansible_ssh_host"] == "127.0.0.1" + assert host_1_vars["ansible_ssh_port"] == "32802" + assert "ansible_host" not in host_1_vars + assert "ansible_connection" not in host_1_vars + assert "docker_state" not in host_1_vars + assert "docker_config" not in host_1_vars + assert "docker_image" not in host_1_vars - assert len(inventory.inventory.groups['ungrouped'].hosts) == 0 - assert len(inventory.inventory.groups['all'].hosts) == 0 - assert len(inventory.inventory.groups['7bd547963679e'].hosts) == 1 - assert len(inventory.inventory.groups['7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a'].hosts) == 1 - assert len(inventory.inventory.groups['image_quay.io/ansible/ubuntu1804-test-container:1.21.0'].hosts) == 1 - assert len(inventory.inventory.groups['loving_tharp'].hosts) == 1 - assert len(inventory.inventory.groups['running'].hosts) == 1 - assert len(inventory.inventory.groups['stopped'].hosts) == 0 - assert len(inventory.inventory.groups['stack_my_stack'].hosts) == 1 - assert len(inventory.inventory.groups['unix://var/run/docker.sock'].hosts) == 1 + assert len(inventory.inventory.groups["ungrouped"].hosts) == 0 + assert len(inventory.inventory.groups["all"].hosts) == 0 + assert len(inventory.inventory.groups["7bd547963679e"].hosts) == 1 + assert ( + len( + inventory.inventory.groups[ + "7bd547963679e3209cafd52aff21840b755c96fd37abcd7a6e19da8da6a7f49a" + ].hosts + ) + == 1 + ) + assert ( + len( + inventory.inventory.groups[ + "image_quay.io/ansible/ubuntu1804-test-container:1.21.0" + ].hosts + ) + == 1 + ) + assert len(inventory.inventory.groups["loving_tharp"].hosts) == 1 + assert len(inventory.inventory.groups["running"].hosts) == 1 + assert len(inventory.inventory.groups["stopped"].hosts) == 0 + assert len(inventory.inventory.groups["stack_my_stack"].hosts) == 1 + assert len(inventory.inventory.groups["unix://var/run/docker.sock"].hosts) == 1 assert len(inventory.inventory.groups) == 10 assert len(inventory.inventory.hosts) == 1 @@ -232,17 +274,21 @@ def test_populate_stack(inventory, mocker): def test_populate_filter_none(inventory, mocker): client = FakeClient(LOVING_THARP) - inventory.get_option = mocker.MagicMock(side_effect=create_get_option({ - 'verbose_output': True, - 'connection_type': 'docker-api', - 'add_legacy_groups': False, - 'compose': {}, - 'groups': {}, - 'keyed_groups': {}, - 'filters': [ - {'exclude': True}, - ], - })) + inventory.get_option = mocker.MagicMock( + side_effect=create_get_option( + { + "verbose_output": True, + "connection_type": "docker-api", + "add_legacy_groups": False, + "compose": {}, + "groups": {}, + "keyed_groups": {}, + "filters": [ + {"exclude": True}, + ], + } + ) + ) inventory._populate(client) assert len(inventory.inventory.hosts) == 0 @@ -251,22 +297,26 @@ def test_populate_filter_none(inventory, mocker): def test_populate_filter(inventory, mocker): client = FakeClient(LOVING_THARP) - inventory.get_option = mocker.MagicMock(side_effect=create_get_option({ - 'verbose_output': True, - 'connection_type': 'docker-api', - 'add_legacy_groups': False, - 'compose': {}, - 'groups': {}, - 'keyed_groups': {}, - 'filters': [ - {'include': make_trusted('docker_state.Running is true')}, - {'exclude': True}, - ], - })) + inventory.get_option = mocker.MagicMock( + side_effect=create_get_option( + { + "verbose_output": True, + "connection_type": "docker-api", + "add_legacy_groups": False, + "compose": {}, + "groups": {}, + "keyed_groups": {}, + "filters": [ + {"include": make_trusted("docker_state.Running is true")}, + {"exclude": True}, + ], + } + ) + ) inventory._populate(client) - host_1 = inventory.inventory.get_host('loving_tharp') + host_1 = inventory.inventory.get_host("loving_tharp") host_1_vars = host_1.get_vars() - assert host_1_vars['ansible_host'] == 'loving_tharp' + assert host_1_vars["ansible_host"] == "loving_tharp" assert len(inventory.inventory.hosts) == 1 diff --git a/tests/unit/plugins/module_utils/_api/api/test_client.py b/tests/unit/plugins/module_utils/_api/api/test_client.py index 483e1944..6e987fcf 100644 --- a/tests/unit/plugins/module_utils/_api/api/test_client.py +++ b/tests/unit/plugins/module_utils/_api/api/test_client.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import datetime @@ -27,14 +29,21 @@ from socketserver import ThreadingTCPServer import pytest import requests - -from ansible_collections.community.docker.plugins.module_utils._api import constants, errors -from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient -from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION +from ansible_collections.community.docker.plugins.module_utils._api import ( + constants, + errors, +) +from ansible_collections.community.docker.plugins.module_utils._api.api.client import ( + APIClient, +) +from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import ( + DEFAULT_DOCKER_API_VERSION, +) from requests.packages import urllib3 from .. import fake_api + try: from unittest import mock except ImportError: @@ -44,12 +53,19 @@ except ImportError: DEFAULT_TIMEOUT_SECONDS = constants.DEFAULT_TIMEOUT_SECONDS -def response(status_code=200, content='', headers=None, reason=None, elapsed=0, - request=None, raw=None): +def response( + status_code=200, + content="", + headers=None, + reason=None, + elapsed=0, + request=None, + raw=None, +): res = requests.Response() res.status_code = status_code if not isinstance(content, bytes): - content = json.dumps(content).encode('ascii') + content = json.dumps(content).encode("ascii") res._content = content res.headers = requests.structures.CaseInsensitiveDict(headers or {}) res.reason = reason @@ -74,7 +90,7 @@ def fake_resp(method, url, *args, **kwargs): elif (url, method) in fake_api.fake_responses: key = (url, method) if not key: - raise Exception(f'{method} {url}') + raise Exception(f"{method} {url}") status_code, content = fake_api.fake_responses[key]() return response(status_code=status_code, content=content) @@ -83,38 +99,38 @@ fake_request = mock.Mock(side_effect=fake_resp) def fake_get(self, url, *args, **kwargs): - return fake_request('GET', url, *args, **kwargs) + return fake_request("GET", url, *args, **kwargs) def fake_post(self, url, *args, **kwargs): - return fake_request('POST', url, *args, **kwargs) + return fake_request("POST", url, *args, **kwargs) def fake_put(self, url, *args, **kwargs): - return fake_request('PUT', url, *args, **kwargs) + return fake_request("PUT", url, *args, **kwargs) def fake_delete(self, url, *args, **kwargs): - return fake_request('DELETE', url, *args, **kwargs) + return fake_request("DELETE", url, *args, **kwargs) def fake_read_from_socket(self, response, stream, tty=False, demux=False): return b"" -url_base = f'{fake_api.prefix}/' -url_prefix = f'{url_base}v{DEFAULT_DOCKER_API_VERSION}/' +url_base = f"{fake_api.prefix}/" +url_prefix = f"{url_base}v{DEFAULT_DOCKER_API_VERSION}/" class BaseAPIClientTest(unittest.TestCase): def setUp(self): self.patcher = mock.patch.multiple( - 'ansible_collections.community.docker.plugins.module_utils._api.api.client.APIClient', + "ansible_collections.community.docker.plugins.module_utils._api.api.client.APIClient", get=fake_get, post=fake_post, put=fake_put, delete=fake_delete, - _read_from_socket=fake_read_from_socket + _read_from_socket=fake_read_from_socket, ) self.patcher.start() self.client = APIClient(version=DEFAULT_DOCKER_API_VERSION) @@ -123,15 +139,20 @@ class BaseAPIClientTest(unittest.TestCase): self.client.close() self.patcher.stop() - def base_create_payload(self, img='busybox', cmd=None): + def base_create_payload(self, img="busybox", cmd=None): if not cmd: - cmd = ['true'] - return {"Tty": False, "Image": img, "Cmd": cmd, - "AttachStdin": False, - "AttachStderr": True, "AttachStdout": True, - "StdinOnce": False, - "OpenStdin": False, "NetworkDisabled": False, - } + cmd = ["true"] + return { + "Tty": False, + "Image": img, + "Cmd": cmd, + "AttachStdin": False, + "AttachStderr": True, + "AttachStdout": True, + "StdinOnce": False, + "OpenStdin": False, + "NetworkDisabled": False, + } class DockerApiTest(BaseAPIClientTest): @@ -139,55 +160,48 @@ class DockerApiTest(BaseAPIClientTest): with pytest.raises(errors.DockerException) as excinfo: APIClient(version=1.12) - assert str( - excinfo.value - ) == 'Version parameter must be a string or None. Found float' + assert ( + str(excinfo.value) + == "Version parameter must be a string or None. Found float" + ) def test_url_valid_resource(self): - url = self.client._url('/hello/{0}/world', 'somename') - assert url == f'{url_prefix}hello/somename/world' + url = self.client._url("/hello/{0}/world", "somename") + assert url == f"{url_prefix}hello/somename/world" - url = self.client._url( - '/hello/{0}/world/{1}', 'somename', 'someothername' - ) - assert url == f'{url_prefix}hello/somename/world/someothername' + url = self.client._url("/hello/{0}/world/{1}", "somename", "someothername") + assert url == f"{url_prefix}hello/somename/world/someothername" - url = self.client._url('/hello/{0}/world', 'some?name') - assert url == f'{url_prefix}hello/some%3Fname/world' + url = self.client._url("/hello/{0}/world", "some?name") + assert url == f"{url_prefix}hello/some%3Fname/world" url = self.client._url("/images/{0}/push", "localhost:5000/image") - assert url == f'{url_prefix}images/localhost:5000/image/push' + assert url == f"{url_prefix}images/localhost:5000/image/push" def test_url_invalid_resource(self): with pytest.raises(ValueError): - self.client._url('/hello/{0}/world', ['sakuya', 'izayoi']) + self.client._url("/hello/{0}/world", ["sakuya", "izayoi"]) def test_url_no_resource(self): - url = self.client._url('/simple') - assert url == f'{url_prefix}simple' + url = self.client._url("/simple") + assert url == f"{url_prefix}simple" def test_url_unversioned_api(self): - url = self.client._url( - '/hello/{0}/world', 'somename', versioned_api=False - ) - assert url == f'{url_base}hello/somename/world' + url = self.client._url("/hello/{0}/world", "somename", versioned_api=False) + assert url == f"{url_base}hello/somename/world" def test_version(self): self.client.version() fake_request.assert_called_with( - 'GET', - url_prefix + 'version', - timeout=DEFAULT_TIMEOUT_SECONDS + "GET", url_prefix + "version", timeout=DEFAULT_TIMEOUT_SECONDS ) def test_version_no_api_version(self): self.client.version(False) fake_request.assert_called_with( - 'GET', - url_base + 'version', - timeout=DEFAULT_TIMEOUT_SECONDS + "GET", url_base + "version", timeout=DEFAULT_TIMEOUT_SECONDS ) def test_retrieve_server_version(self): @@ -204,95 +218,94 @@ class DockerApiTest(BaseAPIClientTest): self.client.info() fake_request.assert_called_with( - 'GET', - url_prefix + 'info', - timeout=DEFAULT_TIMEOUT_SECONDS + "GET", url_prefix + "info", timeout=DEFAULT_TIMEOUT_SECONDS ) def test_search(self): - self.client.get_json('/images/search', params={'term': 'busybox'}) + self.client.get_json("/images/search", params={"term": "busybox"}) fake_request.assert_called_with( - 'GET', - url_prefix + 'images/search', - params={'term': 'busybox'}, - timeout=DEFAULT_TIMEOUT_SECONDS + "GET", + url_prefix + "images/search", + params={"term": "busybox"}, + timeout=DEFAULT_TIMEOUT_SECONDS, ) def test_login(self): - self.client.login('sakuya', 'izayoi') + self.client.login("sakuya", "izayoi") args = fake_request.call_args - assert args[0][0] == 'POST' - assert args[0][1] == url_prefix + 'auth' - assert json.loads(args[1]['data']) == { - 'username': 'sakuya', 'password': 'izayoi' + assert args[0][0] == "POST" + assert args[0][1] == url_prefix + "auth" + assert json.loads(args[1]["data"]) == { + "username": "sakuya", + "password": "izayoi", } - assert args[1]['headers'] == {'Content-Type': 'application/json'} - assert self.client._auth_configs.auths['docker.io'] == { - 'email': None, - 'password': 'izayoi', - 'username': 'sakuya', - 'serveraddress': None, + assert args[1]["headers"] == {"Content-Type": "application/json"} + assert self.client._auth_configs.auths["docker.io"] == { + "email": None, + "password": "izayoi", + "username": "sakuya", + "serveraddress": None, } def _socket_path_for_client_session(self, client): - socket_adapter = client.get_adapter('http+docker://') + socket_adapter = client.get_adapter("http+docker://") return socket_adapter.socket_path def test_url_compatibility_unix(self): - c = APIClient( - base_url="unix://socket", - version=DEFAULT_DOCKER_API_VERSION) + c = APIClient(base_url="unix://socket", version=DEFAULT_DOCKER_API_VERSION) - assert self._socket_path_for_client_session(c) == '/socket' + assert self._socket_path_for_client_session(c) == "/socket" def test_url_compatibility_unix_triple_slash(self): - c = APIClient( - base_url="unix:///socket", - version=DEFAULT_DOCKER_API_VERSION) + c = APIClient(base_url="unix:///socket", version=DEFAULT_DOCKER_API_VERSION) - assert self._socket_path_for_client_session(c) == '/socket' + assert self._socket_path_for_client_session(c) == "/socket" def test_url_compatibility_http_unix_triple_slash(self): c = APIClient( - base_url="http+unix:///socket", - version=DEFAULT_DOCKER_API_VERSION) + base_url="http+unix:///socket", version=DEFAULT_DOCKER_API_VERSION + ) - assert self._socket_path_for_client_session(c) == '/socket' + assert self._socket_path_for_client_session(c) == "/socket" def test_url_compatibility_http(self): c = APIClient( - base_url="http://hostname:1234", - version=DEFAULT_DOCKER_API_VERSION) + base_url="http://hostname:1234", version=DEFAULT_DOCKER_API_VERSION + ) assert c.base_url == "http://hostname:1234" def test_url_compatibility_tcp(self): c = APIClient( - base_url="tcp://hostname:1234", - version=DEFAULT_DOCKER_API_VERSION) + base_url="tcp://hostname:1234", version=DEFAULT_DOCKER_API_VERSION + ) assert c.base_url == "http://hostname:1234" def test_remove_link(self): - self.client.delete_call('/containers/{0}', '3cc2351ab11b', params={'v': False, 'link': True, 'force': False}) + self.client.delete_call( + "/containers/{0}", + "3cc2351ab11b", + params={"v": False, "link": True, "force": False}, + ) fake_request.assert_called_with( - 'DELETE', - url_prefix + 'containers/3cc2351ab11b', - params={'v': False, 'link': True, 'force': False}, - timeout=DEFAULT_TIMEOUT_SECONDS + "DELETE", + url_prefix + "containers/3cc2351ab11b", + params={"v": False, "link": True, "force": False}, + timeout=DEFAULT_TIMEOUT_SECONDS, ) def test_stream_helper_decoding(self): - status_code, content = fake_api.fake_responses[url_prefix + 'events']() - content_str = json.dumps(content).encode('utf-8') + status_code, content = fake_api.fake_responses[url_prefix + "events"]() + content_str = json.dumps(content).encode("utf-8") body = io.BytesIO(content_str) # mock a stream interface raw_resp = urllib3.HTTPResponse(body=body) - setattr(raw_resp._fp, 'chunked', True) - setattr(raw_resp._fp, 'chunk_left', len(body.getvalue()) - 1) + setattr(raw_resp._fp, "chunked", True) + setattr(raw_resp._fp, "chunk_left", len(body.getvalue()) - 1) # pass `decode=False` to the helper raw_resp._fp.seek(0) @@ -307,11 +320,11 @@ class DockerApiTest(BaseAPIClientTest): assert result == content # non-chunked response, pass `decode=False` to the helper - setattr(raw_resp._fp, 'chunked', False) + setattr(raw_resp._fp, "chunked", False) raw_resp._fp.seek(0) resp = response(status_code=status_code, content=content, raw=raw_resp) result = next(self.client._stream_helper(resp)) - assert result == content_str.decode('utf-8') + assert result == content_str.decode("utf-8") # non-chunked response, pass `decode=True` to the helper raw_resp._fp.seek(0) @@ -326,7 +339,7 @@ class UnixSocketStreamTest(unittest.TestCase): self.build_context = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, socket_dir) self.addCleanup(shutil.rmtree, self.build_context) - self.socket_file = os.path.join(socket_dir, 'test_sock.sock') + self.socket_file = os.path.join(socket_dir, "test_sock.sock") self.server_socket = self._setup_socket() self.stop_server = False server_thread = threading.Thread(target=self.run_server) @@ -367,17 +380,17 @@ class UnixSocketStreamTest(unittest.TestCase): self.server_socket.close() def early_response_sending_handler(self, connection): - data = b'' + data = b"" headers = None connection.sendall(self.response) while not headers: data += connection.recv(2048) - parts = data.split(b'\r\n\r\n', 1) + parts = data.split(b"\r\n\r\n", 1) if len(parts) == 2: headers, data = parts - mo = re.search(r'Content-Length: ([0-9]+)', headers.decode()) + mo = re.search(r"Content-Length: ([0-9]+)", headers.decode()) assert mo content_length = int(mo.group(1)) @@ -387,77 +400,78 @@ class UnixSocketStreamTest(unittest.TestCase): data += connection.recv(2048) - @pytest.mark.skipif( - constants.IS_WINDOWS_PLATFORM, reason='Unix only' - ) + @pytest.mark.skipif(constants.IS_WINDOWS_PLATFORM, reason="Unix only") def test_early_stream_response(self): self.request_handler = self.early_response_sending_handler lines = [] for i in range(0, 50): line = str(i).encode() - lines += [f'{len(line):x}'.encode(), line] - lines.append(b'0') - lines.append(b'') + lines += [f"{len(line):x}".encode(), line] + lines.append(b"0") + lines.append(b"") self.response = ( - b'HTTP/1.1 200 OK\r\n' - b'Transfer-Encoding: chunked\r\n' - b'\r\n' - ) + b'\r\n'.join(lines) + b"HTTP/1.1 200 OK\r\n" b"Transfer-Encoding: chunked\r\n" b"\r\n" + ) + b"\r\n".join(lines) with APIClient( - base_url="http+unix://" + self.socket_file, - version=DEFAULT_DOCKER_API_VERSION) as client: + base_url="http+unix://" + self.socket_file, + version=DEFAULT_DOCKER_API_VERSION, + ) as client: for i in range(5): try: params = { - 't': None, - 'remote': None, - 'q': False, - 'nocache': False, - 'rm': False, - 'forcerm': False, - 'pull': False, - 'dockerfile': 'Dockerfile', + "t": None, + "remote": None, + "q": False, + "nocache": False, + "rm": False, + "forcerm": False, + "pull": False, + "dockerfile": "Dockerfile", } - headers = {'Content-Type': 'application/tar'} - data = b'...' - response = client._post(client._url('/build'), params=params, headers=headers, data=data, stream=True) + headers = {"Content-Type": "application/tar"} + data = b"..." + response = client._post( + client._url("/build"), + params=params, + headers=headers, + data=data, + stream=True, + ) stream = client._stream_helper(response, decode=False) break except requests.ConnectionError as e: if i == 4: raise e - assert list(stream) == [ - str(i).encode() for i in range(50) - ] + assert list(stream) == [str(i).encode() for i in range(50)] @pytest.mark.skip( - 'This test requires starting a networking server and tries to access it. ' - 'This does not work with network separation with Docker-based unit tests, ' - 'but it does work with podman-based unit tests.' + "This test requires starting a networking server and tries to access it. " + "This does not work with network separation with Docker-based unit tests, " + "but it does work with podman-based unit tests." ) class TCPSocketStreamTest(unittest.TestCase): - stdout_data = b''' + stdout_data = b""" Now, those children out there, they're jumping through the flames in the hope that the god of the fire will make them fruitful. Really, you can't blame them. After all, what girl would not prefer the child of a god to that of some acne-scarred artisan? - ''' - stderr_data = b''' + """ + stderr_data = b""" And what of the true God? To whose glory churches and monasteries have been built on these islands for generations past? Now shall what of Him? - ''' + """ @classmethod def setup_class(cls): - cls.server = ThreadingTCPServer(('', 0), cls.get_handler_class()) + cls.server = ThreadingTCPServer(("", 0), cls.get_handler_class()) cls.thread = threading.Thread(target=cls.server.serve_forever) cls.thread.daemon = True cls.thread.start() - cls.address = f'http://{socket.gethostname()}:{cls.server.server_address[1]}' + cls.address = f"http://{socket.gethostname()}:{cls.server.server_address[1]}" @classmethod def teardown_class(cls): @@ -474,10 +488,9 @@ class TCPSocketStreamTest(unittest.TestCase): def do_POST(self): resp_data = self.get_resp_data() self.send_response(101) - self.send_header( - 'Content-Type', 'application/vnd.docker.raw-stream') - self.send_header('Connection', 'Upgrade') - self.send_header('Upgrade', 'tcp') + self.send_header("Content-Type", "application/vnd.docker.raw-stream") + self.send_header("Connection", "Upgrade") + self.send_header("Upgrade", "tcp") self.end_headers() self.wfile.flush() time.sleep(0.2) @@ -485,22 +498,22 @@ class TCPSocketStreamTest(unittest.TestCase): self.wfile.flush() def get_resp_data(self): - path = self.path.split('/')[-1] - if path == 'tty': + path = self.path.split("/")[-1] + if path == "tty": return stdout_data + stderr_data - elif path == 'no-tty': - data = b'' + elif path == "no-tty": + data = b"" data += self.frame_header(1, stdout_data) data += stdout_data data += self.frame_header(2, stderr_data) data += stderr_data return data else: - raise Exception(f'Unknown path {path}') + raise Exception(f"Unknown path {path}") @staticmethod def frame_header(stream, data): - return struct.pack('>BxxxL', stream, len(data)) + return struct.pack(">BxxxL", stream, len(data)) return Handler @@ -511,12 +524,11 @@ class TCPSocketStreamTest(unittest.TestCase): version=DEFAULT_DOCKER_API_VERSION, ) as client: if tty: - url = client._url('/tty') + url = client._url("/tty") else: - url = client._url('/no-tty') + url = client._url("/no-tty") resp = client._post(url, stream=True) - return client._read_from_socket( - resp, stream=stream, tty=tty, demux=demux) + return client._read_from_socket(resp, stream=stream, tty=tty, demux=demux) def test_read_from_socket_tty(self): res = self.request(stream=True, tty=True, demux=False) @@ -565,8 +577,8 @@ class UserAgentTest(unittest.TestCase): def setUp(self): self.patcher = mock.patch.object( APIClient, - 'send', - return_value=fake_resp("GET", f"{fake_api.prefix}/version") + "send", + return_value=fake_resp("GET", f"{fake_api.prefix}/version"), ) self.mock_send = self.patcher.start() @@ -579,18 +591,16 @@ class UserAgentTest(unittest.TestCase): assert self.mock_send.call_count == 1 headers = self.mock_send.call_args[0][0].headers - expected = 'ansible-community.docker' - assert headers['User-Agent'] == expected + expected = "ansible-community.docker" + assert headers["User-Agent"] == expected def test_custom_user_agent(self): - client = APIClient( - user_agent='foo/bar', - version=DEFAULT_DOCKER_API_VERSION) + client = APIClient(user_agent="foo/bar", version=DEFAULT_DOCKER_API_VERSION) client.version() assert self.mock_send.call_count == 1 headers = self.mock_send.call_args[0][0].headers - assert headers['User-Agent'] == 'foo/bar' + assert headers["User-Agent"] == "foo/bar" class DisableSocketTest(unittest.TestCase): diff --git a/tests/unit/plugins/module_utils/_api/constants.py b/tests/unit/plugins/module_utils/_api/constants.py index f2d32288..81b7ce57 100644 --- a/tests/unit/plugins/module_utils/_api/constants.py +++ b/tests/unit/plugins/module_utils/_api/constants.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type -DEFAULT_DOCKER_API_VERSION = '1.45' +DEFAULT_DOCKER_API_VERSION = "1.45" diff --git a/tests/unit/plugins/module_utils/_api/fake_api.py b/tests/unit/plugins/module_utils/_api/fake_api.py index 9f099ec0..6554614c 100644 --- a/tests/unit/plugins/module_utils/_api/fake_api.py +++ b/tests/unit/plugins/module_utils/_api/fake_api.py @@ -7,31 +7,36 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type from ansible_collections.community.docker.plugins.module_utils._api import constants -from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION +from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import ( + DEFAULT_DOCKER_API_VERSION, +) from . import fake_stat -CURRENT_VERSION = f'v{DEFAULT_DOCKER_API_VERSION}' -FAKE_CONTAINER_ID = '3cc2351ab11b' -FAKE_IMAGE_ID = 'e9aa60c60128' -FAKE_EXEC_ID = 'd5d177f121dc' -FAKE_NETWORK_ID = '33fb6a3462b8' -FAKE_IMAGE_NAME = 'test_image' -FAKE_TARBALL_PATH = '/path/to/tarball' -FAKE_REPO_NAME = 'repo' -FAKE_TAG_NAME = 'tag' -FAKE_FILE_NAME = 'file' -FAKE_URL = 'myurl' -FAKE_PATH = '/path' -FAKE_VOLUME_NAME = 'perfectcherryblossom' -FAKE_NODE_ID = '24ifsmvkjbyhk' -FAKE_SECRET_ID = 'epdyrw4tsi03xy3deu8g8ly6o' -FAKE_SECRET_NAME = 'super_secret' +CURRENT_VERSION = f"v{DEFAULT_DOCKER_API_VERSION}" + +FAKE_CONTAINER_ID = "3cc2351ab11b" +FAKE_IMAGE_ID = "e9aa60c60128" +FAKE_EXEC_ID = "d5d177f121dc" +FAKE_NETWORK_ID = "33fb6a3462b8" +FAKE_IMAGE_NAME = "test_image" +FAKE_TARBALL_PATH = "/path/to/tarball" +FAKE_REPO_NAME = "repo" +FAKE_TAG_NAME = "tag" +FAKE_FILE_NAME = "file" +FAKE_URL = "myurl" +FAKE_PATH = "/path" +FAKE_VOLUME_NAME = "perfectcherryblossom" +FAKE_NODE_ID = "24ifsmvkjbyhk" +FAKE_SECRET_ID = "epdyrw4tsi03xy3deu8g8ly6o" +FAKE_SECRET_NAME = "super_secret" # Each method is prefixed with HTTP method (get, post...) # for clarity and readability @@ -40,31 +45,33 @@ FAKE_SECRET_NAME = 'super_secret' def get_fake_version(): status_code = 200 response = { - 'ApiVersion': '1.35', - 'Arch': 'amd64', - 'BuildTime': '2018-01-10T20:09:37.000000000+00:00', - 'Components': [{ - 'Details': { - 'ApiVersion': '1.35', - 'Arch': 'amd64', - 'BuildTime': '2018-01-10T20:09:37.000000000+00:00', - 'Experimental': 'false', - 'GitCommit': '03596f5', - 'GoVersion': 'go1.9.2', - 'KernelVersion': '4.4.0-112-generic', - 'MinAPIVersion': '1.12', - 'Os': 'linux' - }, - 'Name': 'Engine', - 'Version': '18.01.0-ce' - }], - 'GitCommit': '03596f5', - 'GoVersion': 'go1.9.2', - 'KernelVersion': '4.4.0-112-generic', - 'MinAPIVersion': '1.12', - 'Os': 'linux', - 'Platform': {'Name': ''}, - 'Version': '18.01.0-ce' + "ApiVersion": "1.35", + "Arch": "amd64", + "BuildTime": "2018-01-10T20:09:37.000000000+00:00", + "Components": [ + { + "Details": { + "ApiVersion": "1.35", + "Arch": "amd64", + "BuildTime": "2018-01-10T20:09:37.000000000+00:00", + "Experimental": "false", + "GitCommit": "03596f5", + "GoVersion": "go1.9.2", + "KernelVersion": "4.4.0-112-generic", + "MinAPIVersion": "1.12", + "Os": "linux", + }, + "Name": "Engine", + "Version": "18.01.0-ce", + } + ], + "GitCommit": "03596f5", + "GoVersion": "go1.9.2", + "KernelVersion": "4.4.0-112-generic", + "MinAPIVersion": "1.12", + "Os": "linux", + "Platform": {"Name": ""}, + "Version": "18.01.0-ce", } return status_code, response @@ -72,16 +79,20 @@ def get_fake_version(): def get_fake_info(): status_code = 200 - response = {'Containers': 1, 'Images': 1, 'Debug': False, - 'MemoryLimit': False, 'SwapLimit': False, - 'IPv4Forwarding': True} + response = { + "Containers": 1, + "Images": 1, + "Debug": False, + "MemoryLimit": False, + "SwapLimit": False, + "IPv4Forwarding": True, + } return status_code, response def post_fake_auth(): status_code = 200 - response = {'Status': 'Login Succeeded', - 'IdentityToken': '9cbaf023786cd7'} + response = {"Status": "Login Succeeded", "IdentityToken": "9cbaf023786cd7"} return status_code, response @@ -91,34 +102,28 @@ def get_fake_ping(): def get_fake_search(): status_code = 200 - response = [{'Name': 'busybox', 'Description': 'Fake Description'}] + response = [{"Name": "busybox", "Description": "Fake Description"}] return status_code, response def get_fake_images(): status_code = 200 - response = [{ - 'Id': FAKE_IMAGE_ID, - 'Created': '2 days ago', - 'Repository': 'busybox', - 'RepoTags': ['busybox:latest', 'busybox:1.0'], - }] + response = [ + { + "Id": FAKE_IMAGE_ID, + "Created": "2 days ago", + "Repository": "busybox", + "RepoTags": ["busybox:latest", "busybox:1.0"], + } + ] return status_code, response def get_fake_image_history(): status_code = 200 response = [ - { - "Id": "b750fe79269d", - "Created": 1364102658, - "CreatedBy": "/bin/bash" - }, - { - "Id": "27cf78414709", - "Created": 1364068391, - "CreatedBy": "" - } + {"Id": "b750fe79269d", "Created": 1364102658, "CreatedBy": "/bin/bash"}, + {"Id": "27cf78414709", "Created": 1364068391, "CreatedBy": ""}, ] return status_code, response @@ -126,64 +131,63 @@ def get_fake_image_history(): def post_fake_import_image(): status_code = 200 - response = 'Import messages...' + response = "Import messages..." return status_code, response def get_fake_containers(): status_code = 200 - response = [{ - 'Id': FAKE_CONTAINER_ID, - 'Image': 'busybox:latest', - 'Created': '2 days ago', - 'Command': 'true', - 'Status': 'fake status' - }] + response = [ + { + "Id": FAKE_CONTAINER_ID, + "Image": "busybox:latest", + "Created": "2 days ago", + "Command": "true", + "Status": "fake status", + } + ] return status_code, response def post_fake_start_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_resize_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_create_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def get_fake_inspect_container(tty=False): status_code = 200 response = { - 'Id': FAKE_CONTAINER_ID, - 'Config': {'Labels': {'foo': 'bar'}, 'Privileged': True, 'Tty': tty}, - 'ID': FAKE_CONTAINER_ID, - 'Image': 'busybox:latest', - 'Name': 'foobar', + "Id": FAKE_CONTAINER_ID, + "Config": {"Labels": {"foo": "bar"}, "Privileged": True, "Tty": tty}, + "ID": FAKE_CONTAINER_ID, + "Image": "busybox:latest", + "Name": "foobar", "State": { "Status": "running", "Running": True, "Pid": 0, "ExitCode": 0, "StartedAt": "2013-09-25T14:01:18.869545111+02:00", - "Ghost": False + "Ghost": False, }, "HostConfig": { - "LogConfig": { - "Type": "json-file", - "Config": {} - }, + "LogConfig": {"Type": "json-file", "Config": {}}, }, - "MacAddress": "02:42:ac:11:00:0a" + "MacAddress": "02:42:ac:11:00:0a", } return status_code, response @@ -191,13 +195,12 @@ def get_fake_inspect_container(tty=False): def get_fake_inspect_image(): status_code = 200 response = { - 'Id': FAKE_IMAGE_ID, - 'Parent': "27cf784147099545", - 'Created': "2013-03-23T22:24:18.818426-07:00", - 'Container': FAKE_CONTAINER_ID, - 'Config': {'Labels': {'bar': 'foo'}}, - 'ContainerConfig': - { + "Id": FAKE_IMAGE_ID, + "Parent": "27cf784147099545", + "Created": "2013-03-23T22:24:18.818426-07:00", + "Container": FAKE_CONTAINER_ID, + "Config": {"Labels": {"bar": "foo"}}, + "ContainerConfig": { "Hostname": "", "User": "", "Memory": 0, @@ -215,118 +218,128 @@ def get_fake_inspect_image(): "Image": "base", "Volumes": "", "VolumesFrom": "", - "WorkingDir": "" + "WorkingDir": "", }, - 'Size': 6823592 + "Size": 6823592, } return status_code, response def get_fake_insert_image(): status_code = 200 - response = {'StatusCode': 0} + response = {"StatusCode": 0} return status_code, response def get_fake_wait(): status_code = 200 - response = {'StatusCode': 0} + response = {"StatusCode": 0} return status_code, response def get_fake_logs(): status_code = 200 - response = (b'\x01\x00\x00\x00\x00\x00\x00\x00' - b'\x02\x00\x00\x00\x00\x00\x00\x00' - b'\x01\x00\x00\x00\x00\x00\x00\x11Flowering Nights\n' - b'\x01\x00\x00\x00\x00\x00\x00\x10(Sakuya Iyazoi)\n') + response = ( + b"\x01\x00\x00\x00\x00\x00\x00\x00" + b"\x02\x00\x00\x00\x00\x00\x00\x00" + b"\x01\x00\x00\x00\x00\x00\x00\x11Flowering Nights\n" + b"\x01\x00\x00\x00\x00\x00\x00\x10(Sakuya Iyazoi)\n" + ) return status_code, response def get_fake_diff(): status_code = 200 - response = [{'Path': '/test', 'Kind': 1}] + response = [{"Path": "/test", "Kind": 1}] return status_code, response def get_fake_events(): status_code = 200 - response = [{'status': 'stop', 'id': FAKE_CONTAINER_ID, - 'from': FAKE_IMAGE_ID, 'time': 1423247867}] + response = [ + { + "status": "stop", + "id": FAKE_CONTAINER_ID, + "from": FAKE_IMAGE_ID, + "time": 1423247867, + } + ] return status_code, response def get_fake_export(): status_code = 200 - response = 'Byte Stream....' + response = "Byte Stream...." return status_code, response def post_fake_exec_create(): status_code = 200 - response = {'Id': FAKE_EXEC_ID} + response = {"Id": FAKE_EXEC_ID} return status_code, response def post_fake_exec_start(): status_code = 200 - response = (b'\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n' - b'\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n' - b'\x01\x00\x00\x00\x00\x00\x00\x0csbin\nusr\nvar\n') + response = ( + b"\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n" + b"\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n" + b"\x01\x00\x00\x00\x00\x00\x00\x0csbin\nusr\nvar\n" + ) return status_code, response def post_fake_exec_resize(): status_code = 201 - return status_code, '' + return status_code, "" def get_fake_exec_inspect(): return 200, { - 'OpenStderr': True, - 'OpenStdout': True, - 'Container': get_fake_inspect_container()[1], - 'Running': False, - 'ProcessConfig': { - 'arguments': ['hello world'], - 'tty': False, - 'entrypoint': 'echo', - 'privileged': False, - 'user': '' + "OpenStderr": True, + "OpenStdout": True, + "Container": get_fake_inspect_container()[1], + "Running": False, + "ProcessConfig": { + "arguments": ["hello world"], + "tty": False, + "entrypoint": "echo", + "privileged": False, + "user": "", }, - 'ExitCode': 0, - 'ID': FAKE_EXEC_ID, - 'OpenStdin': False + "ExitCode": 0, + "ID": FAKE_EXEC_ID, + "OpenStdin": False, } def post_fake_stop_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_kill_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_pause_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_unpause_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_restart_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response @@ -337,55 +350,55 @@ def post_fake_rename_container(): def delete_fake_remove_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_image_create(): status_code = 200 - response = {'Id': FAKE_IMAGE_ID} + response = {"Id": FAKE_IMAGE_ID} return status_code, response def delete_fake_remove_image(): status_code = 200 - response = {'Id': FAKE_IMAGE_ID} + response = {"Id": FAKE_IMAGE_ID} return status_code, response def get_fake_get_image(): status_code = 200 - response = 'Byte Stream....' + response = "Byte Stream...." return status_code, response def post_fake_load_image(): status_code = 200 - response = {'Id': FAKE_IMAGE_ID} + response = {"Id": FAKE_IMAGE_ID} return status_code, response def post_fake_commit(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_push(): status_code = 200 - response = {'Id': FAKE_IMAGE_ID} + response = {"Id": FAKE_IMAGE_ID} return status_code, response def post_fake_build_container(): status_code = 200 - response = {'Id': FAKE_CONTAINER_ID} + response = {"Id": FAKE_CONTAINER_ID} return status_code, response def post_fake_tag_image(): status_code = 200 - response = {'Id': FAKE_IMAGE_ID} + response = {"Id": FAKE_IMAGE_ID} return status_code, response @@ -397,27 +410,27 @@ def get_fake_stats(): def get_fake_top(): return 200, { - 'Processes': [ + "Processes": [ [ - 'root', - '26501', - '6907', - '0', - '10:32', - 'pts/55', - '00:00:00', - 'sleep 60', + "root", + "26501", + "6907", + "0", + "10:32", + "pts/55", + "00:00:00", + "sleep 60", ], ], - 'Titles': [ - 'UID', - 'PID', - 'PPID', - 'C', - 'STIME', - 'TTY', - 'TIME', - 'CMD', + "Titles": [ + "UID", + "PID", + "PPID", + "C", + "STIME", + "TTY", + "TIME", + "CMD", ], } @@ -425,18 +438,19 @@ def get_fake_top(): def get_fake_volume_list(): status_code = 200 response = { - 'Volumes': [ + "Volumes": [ { - 'Name': 'perfectcherryblossom', - 'Driver': 'local', - 'Mountpoint': '/var/lib/docker/volumes/perfectcherryblossom', - 'Scope': 'local' - }, { - 'Name': 'subterraneananimism', - 'Driver': 'local', - 'Mountpoint': '/var/lib/docker/volumes/subterraneananimism', - 'Scope': 'local' - } + "Name": "perfectcherryblossom", + "Driver": "local", + "Mountpoint": "/var/lib/docker/volumes/perfectcherryblossom", + "Scope": "local", + }, + { + "Name": "subterraneananimism", + "Driver": "local", + "Mountpoint": "/var/lib/docker/volumes/subterraneananimism", + "Scope": "local", + }, ] } return status_code, response @@ -445,13 +459,11 @@ def get_fake_volume_list(): def get_fake_volume(): status_code = 200 response = { - 'Name': 'perfectcherryblossom', - 'Driver': 'local', - 'Mountpoint': '/var/lib/docker/volumes/perfectcherryblossom', - 'Labels': { - 'com.example.some-label': 'some-value' - }, - 'Scope': 'local' + "Name": "perfectcherryblossom", + "Driver": "local", + "Mountpoint": "/var/lib/docker/volumes/perfectcherryblossom", + "Labels": {"com.example.some-label": "some-value"}, + "Scope": "local", } return status_code, response @@ -461,7 +473,7 @@ def fake_remove_volume(): def post_fake_update_container(): - return 200, {'Warnings': []} + return 200, {"Warnings": []} def post_fake_update_node(): @@ -473,38 +485,33 @@ def post_fake_join_swarm(): def get_fake_network_list(): - return 200, [{ - "Name": "bridge", - "Id": FAKE_NETWORK_ID, - "Scope": "local", - "Driver": "bridge", - "EnableIPv6": False, - "Internal": False, - "IPAM": { - "Driver": "default", - "Config": [ - { - "Subnet": "172.17.0.0/16" + return 200, [ + { + "Name": "bridge", + "Id": FAKE_NETWORK_ID, + "Scope": "local", + "Driver": "bridge", + "EnableIPv6": False, + "Internal": False, + "IPAM": {"Driver": "default", "Config": [{"Subnet": "172.17.0.0/16"}]}, + "Containers": { + FAKE_CONTAINER_ID: { + "EndpointID": "ed2419a97c1d99", + "MacAddress": "02:42:ac:11:00:02", + "IPv4Address": "172.17.0.2/16", + "IPv6Address": "", } - ] - }, - "Containers": { - FAKE_CONTAINER_ID: { - "EndpointID": "ed2419a97c1d99", - "MacAddress": "02:42:ac:11:00:02", - "IPv4Address": "172.17.0.2/16", - "IPv6Address": "" - } - }, - "Options": { - "com.docker.network.bridge.default_bridge": "true", - "com.docker.network.bridge.enable_icc": "true", - "com.docker.network.bridge.enable_ip_masquerade": "true", - "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", - "com.docker.network.bridge.name": "docker0", - "com.docker.network.driver.mtu": "1500" + }, + "Options": { + "com.docker.network.bridge.default_bridge": "true", + "com.docker.network.bridge.enable_icc": "true", + "com.docker.network.bridge.enable_ip_masquerade": "true", + "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", + "com.docker.network.bridge.name": "docker0", + "com.docker.network.driver.mtu": "1500", + }, } - }] + ] def get_fake_network(): @@ -529,127 +536,85 @@ def post_fake_network_disconnect(): def post_fake_secret(): status_code = 200 - response = {'ID': FAKE_SECRET_ID} + response = {"ID": FAKE_SECRET_ID} return status_code, response # Maps real api url to fake response callback -prefix = 'http+docker://localhost' +prefix = "http+docker://localhost" if constants.IS_WINDOWS_PLATFORM: - prefix = 'http+docker://localnpipe' + prefix = "http+docker://localnpipe" fake_responses = { - f'{prefix}/version': - get_fake_version, - f'{prefix}/{CURRENT_VERSION}/version': - get_fake_version, - f'{prefix}/{CURRENT_VERSION}/info': - get_fake_info, - f'{prefix}/{CURRENT_VERSION}/auth': - post_fake_auth, - f'{prefix}/{CURRENT_VERSION}/_ping': - get_fake_ping, - f'{prefix}/{CURRENT_VERSION}/images/search': - get_fake_search, - f'{prefix}/{CURRENT_VERSION}/images/json': - get_fake_images, - f'{prefix}/{CURRENT_VERSION}/images/test_image/history': - get_fake_image_history, - f'{prefix}/{CURRENT_VERSION}/images/create': - post_fake_import_image, - f'{prefix}/{CURRENT_VERSION}/containers/json': - get_fake_containers, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/start': - post_fake_start_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/resize': - post_fake_resize_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/json': - get_fake_inspect_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/rename': - post_fake_rename_container, - f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/tag': - post_fake_tag_image, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/wait': - get_fake_wait, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/logs': - get_fake_logs, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/changes': - get_fake_diff, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/export': - get_fake_export, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/update': - post_fake_update_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/exec': - post_fake_exec_create, - f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/start': - post_fake_exec_start, - f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/json': - get_fake_exec_inspect, - f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/resize': - post_fake_exec_resize, - - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stats': - get_fake_stats, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/top': - get_fake_top, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stop': - post_fake_stop_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/kill': - post_fake_kill_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/pause': - post_fake_pause_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/unpause': - post_fake_unpause_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/restart': - post_fake_restart_container, - f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b': - delete_fake_remove_container, - f'{prefix}/{CURRENT_VERSION}/images/create': - post_fake_image_create, - f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128': - delete_fake_remove_image, - f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/get': - get_fake_get_image, - f'{prefix}/{CURRENT_VERSION}/images/load': - post_fake_load_image, - f'{prefix}/{CURRENT_VERSION}/images/test_image/json': - get_fake_inspect_image, - f'{prefix}/{CURRENT_VERSION}/images/test_image/insert': - get_fake_insert_image, - f'{prefix}/{CURRENT_VERSION}/images/test_image/push': - post_fake_push, - f'{prefix}/{CURRENT_VERSION}/commit': - post_fake_commit, - f'{prefix}/{CURRENT_VERSION}/containers/create': - post_fake_create_container, - f'{prefix}/{CURRENT_VERSION}/build': - post_fake_build_container, - f'{prefix}/{CURRENT_VERSION}/events': - get_fake_events, - (f'{prefix}/{CURRENT_VERSION}/volumes', 'GET'): - get_fake_volume_list, - (f'{prefix}/{CURRENT_VERSION}/volumes/create', 'POST'): - get_fake_volume, - (f'{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}', 'GET'): - get_fake_volume, - (f'{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}', 'DELETE'): - fake_remove_volume, - (f'{prefix}/{CURRENT_VERSION}/nodes/{FAKE_NODE_ID}/update?version=1', 'POST'): - post_fake_update_node, - (f'{prefix}/{CURRENT_VERSION}/swarm/join', 'POST'): - post_fake_join_swarm, - (f'{prefix}/{CURRENT_VERSION}/networks', 'GET'): - get_fake_network_list, - (f'{prefix}/{CURRENT_VERSION}/networks/create', 'POST'): - post_fake_network, - (f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}', 'GET'): - get_fake_network, - (f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}', 'DELETE'): - delete_fake_network, - (f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/connect', 'POST'): - post_fake_network_connect, - (f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/disconnect', 'POST'): - post_fake_network_disconnect, - f'{prefix}/{CURRENT_VERSION}/secrets/create': - post_fake_secret, + f"{prefix}/version": get_fake_version, + f"{prefix}/{CURRENT_VERSION}/version": get_fake_version, + f"{prefix}/{CURRENT_VERSION}/info": get_fake_info, + f"{prefix}/{CURRENT_VERSION}/auth": post_fake_auth, + f"{prefix}/{CURRENT_VERSION}/_ping": get_fake_ping, + f"{prefix}/{CURRENT_VERSION}/images/search": get_fake_search, + f"{prefix}/{CURRENT_VERSION}/images/json": get_fake_images, + f"{prefix}/{CURRENT_VERSION}/images/test_image/history": get_fake_image_history, + f"{prefix}/{CURRENT_VERSION}/images/create": post_fake_import_image, + f"{prefix}/{CURRENT_VERSION}/containers/json": get_fake_containers, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/start": post_fake_start_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/resize": post_fake_resize_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/json": get_fake_inspect_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/rename": post_fake_rename_container, + f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/tag": post_fake_tag_image, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/wait": get_fake_wait, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/logs": get_fake_logs, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/changes": get_fake_diff, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/export": get_fake_export, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/update": post_fake_update_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/exec": post_fake_exec_create, + f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/start": post_fake_exec_start, + f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/json": get_fake_exec_inspect, + f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/resize": post_fake_exec_resize, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stats": get_fake_stats, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/top": get_fake_top, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stop": post_fake_stop_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/kill": post_fake_kill_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/pause": post_fake_pause_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/unpause": post_fake_unpause_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/restart": post_fake_restart_container, + f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b": delete_fake_remove_container, + f"{prefix}/{CURRENT_VERSION}/images/create": post_fake_image_create, + f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128": delete_fake_remove_image, + f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/get": get_fake_get_image, + f"{prefix}/{CURRENT_VERSION}/images/load": post_fake_load_image, + f"{prefix}/{CURRENT_VERSION}/images/test_image/json": get_fake_inspect_image, + f"{prefix}/{CURRENT_VERSION}/images/test_image/insert": get_fake_insert_image, + f"{prefix}/{CURRENT_VERSION}/images/test_image/push": post_fake_push, + f"{prefix}/{CURRENT_VERSION}/commit": post_fake_commit, + f"{prefix}/{CURRENT_VERSION}/containers/create": post_fake_create_container, + f"{prefix}/{CURRENT_VERSION}/build": post_fake_build_container, + f"{prefix}/{CURRENT_VERSION}/events": get_fake_events, + (f"{prefix}/{CURRENT_VERSION}/volumes", "GET"): get_fake_volume_list, + (f"{prefix}/{CURRENT_VERSION}/volumes/create", "POST"): get_fake_volume, + (f"{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}", "GET"): get_fake_volume, + ( + f"{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}", + "DELETE", + ): fake_remove_volume, + ( + f"{prefix}/{CURRENT_VERSION}/nodes/{FAKE_NODE_ID}/update?version=1", + "POST", + ): post_fake_update_node, + (f"{prefix}/{CURRENT_VERSION}/swarm/join", "POST"): post_fake_join_swarm, + (f"{prefix}/{CURRENT_VERSION}/networks", "GET"): get_fake_network_list, + (f"{prefix}/{CURRENT_VERSION}/networks/create", "POST"): post_fake_network, + (f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}", "GET"): get_fake_network, + ( + f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}", + "DELETE", + ): delete_fake_network, + ( + f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/connect", + "POST", + ): post_fake_network_connect, + ( + f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/disconnect", + "POST", + ): post_fake_network_disconnect, + f"{prefix}/{CURRENT_VERSION}/secrets/create": post_fake_secret, } diff --git a/tests/unit/plugins/module_utils/_api/fake_stat.py b/tests/unit/plugins/module_utils/_api/fake_stat.py index 97547328..538cc124 100644 --- a/tests/unit/plugins/module_utils/_api/fake_stat.py +++ b/tests/unit/plugins/module_utils/_api/fake_stat.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type OBJ = { @@ -20,26 +22,17 @@ OBJ = { "tx_bytes": 1176, "tx_packets": 13, "tx_errors": 0, - "tx_dropped": 0 + "tx_dropped": 0, }, "cpu_stats": { "cpu_usage": { "total_usage": 157260874053, - "percpu_usage": [ - 52196306950, - 24118413549, - 53292684398, - 27653469156 - ], + "percpu_usage": [52196306950, 24118413549, 53292684398, 27653469156], "usage_in_kernelmode": 37140000000, - "usage_in_usermode": 62140000000 + "usage_in_usermode": 62140000000, }, - "system_cpu_usage": 3.0881377e+14, - "throttling_data": { - "periods": 0, - "throttled_periods": 0, - "throttled_time": 0 - } + "system_cpu_usage": 3.0881377e14, + "throttling_data": {"periods": 0, "throttled_periods": 0, "throttled_time": 0}, }, "memory_stats": { "usage": 179314688, @@ -48,7 +41,7 @@ OBJ = { "active_anon": 90804224, "active_file": 2195456, "cache": 3096576, - "hierarchical_memory_limit": 1.844674407371e+19, + "hierarchical_memory_limit": 1.844674407371e19, "inactive_anon": 85516288, "inactive_file": 798720, "mapped_file": 2646016, @@ -73,73 +66,31 @@ OBJ = { "total_unevictable": 0, "total_writeback": 0, "unevictable": 0, - "writeback": 0 + "writeback": 0, }, "failcnt": 0, - "limit": 8039038976 + "limit": 8039038976, }, "blkio_stats": { "io_service_bytes_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 72843264 - }, { - "major": 8, - "minor": 0, - "op": "Write", - "value": 4096 - }, { - "major": 8, - "minor": 0, - "op": "Sync", - "value": 4096 - }, { - "major": 8, - "minor": 0, - "op": "Async", - "value": 72843264 - }, { - "major": 8, - "minor": 0, - "op": "Total", - "value": 72847360 - } + {"major": 8, "minor": 0, "op": "Read", "value": 72843264}, + {"major": 8, "minor": 0, "op": "Write", "value": 4096}, + {"major": 8, "minor": 0, "op": "Sync", "value": 4096}, + {"major": 8, "minor": 0, "op": "Async", "value": 72843264}, + {"major": 8, "minor": 0, "op": "Total", "value": 72847360}, ], "io_serviced_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 10581 - }, { - "major": 8, - "minor": 0, - "op": "Write", - "value": 1 - }, { - "major": 8, - "minor": 0, - "op": "Sync", - "value": 1 - }, { - "major": 8, - "minor": 0, - "op": "Async", - "value": 10581 - }, { - "major": 8, - "minor": 0, - "op": "Total", - "value": 10582 - } + {"major": 8, "minor": 0, "op": "Read", "value": 10581}, + {"major": 8, "minor": 0, "op": "Write", "value": 1}, + {"major": 8, "minor": 0, "op": "Sync", "value": 1}, + {"major": 8, "minor": 0, "op": "Async", "value": 10581}, + {"major": 8, "minor": 0, "op": "Total", "value": 10582}, ], "io_queue_recursive": [], "io_service_time_recursive": [], "io_wait_time_recursive": [], "io_merged_recursive": [], "io_time_recursive": [], - "sectors_recursive": [] - } + "sectors_recursive": [], + }, } diff --git a/tests/unit/plugins/module_utils/_api/test_auth.py b/tests/unit/plugins/module_utils/_api/test_auth.py index 4fed09d7..0dfa827a 100644 --- a/tests/unit/plugins/module_utils/_api/test_auth.py +++ b/tests/unit/plugins/module_utils/_api/test_auth.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import base64 @@ -20,10 +22,14 @@ import tempfile import unittest import pytest - from ansible_collections.community.docker.plugins.module_utils._api import auth, errors -from ansible_collections.community.docker.plugins.module_utils._api.credentials.errors import CredentialsNotFound -from ansible_collections.community.docker.plugins.module_utils._api.credentials.store import Store +from ansible_collections.community.docker.plugins.module_utils._api.credentials.errors import ( + CredentialsNotFound, +) +from ansible_collections.community.docker.plugins.module_utils._api.credentials.store import ( + Store, +) + try: from unittest import mock @@ -33,210 +39,253 @@ except ImportError: class RegressionTest(unittest.TestCase): def test_803_urlsafe_encode(self): - auth_data = { - 'username': 'root', - 'password': 'GR?XGR?XGR?XGR?X' - } + auth_data = {"username": "root", "password": "GR?XGR?XGR?XGR?X"} encoded = auth.encode_header(auth_data) - assert b'/' not in encoded - assert b'_' in encoded + assert b"/" not in encoded + assert b"_" in encoded class ResolveRepositoryNameTest(unittest.TestCase): def test_resolve_repository_name_hub_library_image(self): - assert auth.resolve_repository_name('image') == ( - 'docker.io', 'image' - ) + assert auth.resolve_repository_name("image") == ("docker.io", "image") def test_resolve_repository_name_dotted_hub_library_image(self): - assert auth.resolve_repository_name('image.valid') == ( - 'docker.io', 'image.valid' + assert auth.resolve_repository_name("image.valid") == ( + "docker.io", + "image.valid", ) def test_resolve_repository_name_hub_image(self): - assert auth.resolve_repository_name('username/image') == ( - 'docker.io', 'username/image' + assert auth.resolve_repository_name("username/image") == ( + "docker.io", + "username/image", ) def test_explicit_hub_index_library_image(self): - assert auth.resolve_repository_name('docker.io/image') == ( - 'docker.io', 'image' - ) + assert auth.resolve_repository_name("docker.io/image") == ("docker.io", "image") def test_explicit_legacy_hub_index_library_image(self): - assert auth.resolve_repository_name('index.docker.io/image') == ( - 'docker.io', 'image' + assert auth.resolve_repository_name("index.docker.io/image") == ( + "docker.io", + "image", ) def test_resolve_repository_name_private_registry(self): - assert auth.resolve_repository_name('my.registry.net/image') == ( - 'my.registry.net', 'image' + assert auth.resolve_repository_name("my.registry.net/image") == ( + "my.registry.net", + "image", ) def test_resolve_repository_name_private_registry_with_port(self): - assert auth.resolve_repository_name('my.registry.net:5000/image') == ( - 'my.registry.net:5000', 'image' + assert auth.resolve_repository_name("my.registry.net:5000/image") == ( + "my.registry.net:5000", + "image", ) def test_resolve_repository_name_private_registry_with_username(self): - assert auth.resolve_repository_name( - 'my.registry.net/username/image' - ) == ('my.registry.net', 'username/image') + assert auth.resolve_repository_name("my.registry.net/username/image") == ( + "my.registry.net", + "username/image", + ) def test_resolve_repository_name_no_dots_but_port(self): - assert auth.resolve_repository_name('hostname:5000/image') == ( - 'hostname:5000', 'image' + assert auth.resolve_repository_name("hostname:5000/image") == ( + "hostname:5000", + "image", ) def test_resolve_repository_name_no_dots_but_port_and_username(self): - assert auth.resolve_repository_name( - 'hostname:5000/username/image' - ) == ('hostname:5000', 'username/image') - - def test_resolve_repository_name_localhost(self): - assert auth.resolve_repository_name('localhost/image') == ( - 'localhost', 'image' + assert auth.resolve_repository_name("hostname:5000/username/image") == ( + "hostname:5000", + "username/image", ) + def test_resolve_repository_name_localhost(self): + assert auth.resolve_repository_name("localhost/image") == ("localhost", "image") + def test_resolve_repository_name_localhost_with_username(self): - assert auth.resolve_repository_name('localhost/username/image') == ( - 'localhost', 'username/image' + assert auth.resolve_repository_name("localhost/username/image") == ( + "localhost", + "username/image", ) def test_invalid_index_name(self): with pytest.raises(errors.InvalidRepository): - auth.resolve_repository_name('-gecko.com/image') + auth.resolve_repository_name("-gecko.com/image") def encode_auth(auth_info): return base64.b64encode( - auth_info.get('username', '').encode('utf-8') + b':' + - auth_info.get('password', '').encode('utf-8')) + auth_info.get("username", "").encode("utf-8") + + b":" + + auth_info.get("password", "").encode("utf-8") + ) class ResolveAuthTest(unittest.TestCase): - index_config = {'auth': encode_auth({'username': 'indexuser'})} - private_config = {'auth': encode_auth({'username': 'privateuser'})} - legacy_config = {'auth': encode_auth({'username': 'legacyauth'})} + index_config = {"auth": encode_auth({"username": "indexuser"})} + private_config = {"auth": encode_auth({"username": "privateuser"})} + legacy_config = {"auth": encode_auth({"username": "legacyauth"})} - auth_config = auth.AuthConfig({ - 'auths': auth.parse_auth({ - 'https://index.docker.io/v1/': index_config, - 'my.registry.net': private_config, - 'http://legacy.registry.url/v1/': legacy_config, - }) - }) + auth_config = auth.AuthConfig( + { + "auths": auth.parse_auth( + { + "https://index.docker.io/v1/": index_config, + "my.registry.net": private_config, + "http://legacy.registry.url/v1/": legacy_config, + } + ) + } + ) def test_resolve_authconfig_hostname_only(self): - assert auth.resolve_authconfig( - self.auth_config, 'my.registry.net' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "my.registry.net")["username"] + == "privateuser" + ) def test_resolve_authconfig_no_protocol(self): - assert auth.resolve_authconfig( - self.auth_config, 'my.registry.net/v1/' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "my.registry.net/v1/")["username"] + == "privateuser" + ) def test_resolve_authconfig_no_path(self): - assert auth.resolve_authconfig( - self.auth_config, 'http://my.registry.net' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "http://my.registry.net")[ + "username" + ] + == "privateuser" + ) def test_resolve_authconfig_no_path_trailing_slash(self): - assert auth.resolve_authconfig( - self.auth_config, 'http://my.registry.net/' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "http://my.registry.net/")[ + "username" + ] + == "privateuser" + ) def test_resolve_authconfig_no_path_wrong_secure_proto(self): - assert auth.resolve_authconfig( - self.auth_config, 'https://my.registry.net' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "https://my.registry.net")[ + "username" + ] + == "privateuser" + ) def test_resolve_authconfig_no_path_wrong_insecure_proto(self): - assert auth.resolve_authconfig( - self.auth_config, 'http://index.docker.io' - )['username'] == 'indexuser' + assert ( + auth.resolve_authconfig(self.auth_config, "http://index.docker.io")[ + "username" + ] + == "indexuser" + ) def test_resolve_authconfig_path_wrong_proto(self): - assert auth.resolve_authconfig( - self.auth_config, 'https://my.registry.net/v1/' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "https://my.registry.net/v1/")[ + "username" + ] + == "privateuser" + ) def test_resolve_authconfig_default_registry(self): - assert auth.resolve_authconfig( - self.auth_config - )['username'] == 'indexuser' + assert auth.resolve_authconfig(self.auth_config)["username"] == "indexuser" def test_resolve_authconfig_default_explicit_none(self): - assert auth.resolve_authconfig( - self.auth_config, None - )['username'] == 'indexuser' + assert ( + auth.resolve_authconfig(self.auth_config, None)["username"] == "indexuser" + ) def test_resolve_authconfig_fully_explicit(self): - assert auth.resolve_authconfig( - self.auth_config, 'http://my.registry.net/v1/' - )['username'] == 'privateuser' + assert ( + auth.resolve_authconfig(self.auth_config, "http://my.registry.net/v1/")[ + "username" + ] + == "privateuser" + ) def test_resolve_authconfig_legacy_config(self): - assert auth.resolve_authconfig( - self.auth_config, 'legacy.registry.url' - )['username'] == 'legacyauth' + assert ( + auth.resolve_authconfig(self.auth_config, "legacy.registry.url")["username"] + == "legacyauth" + ) def test_resolve_authconfig_no_match(self): - assert auth.resolve_authconfig( - self.auth_config, 'does.not.exist' - ) is None + assert auth.resolve_authconfig(self.auth_config, "does.not.exist") is None def test_resolve_registry_and_auth_library_image(self): - image = 'image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - )['username'] == 'indexuser' + image = "image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + )["username"] + == "indexuser" + ) def test_resolve_registry_and_auth_hub_image(self): - image = 'username/image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - )['username'] == 'indexuser' + image = "username/image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + )["username"] + == "indexuser" + ) def test_resolve_registry_and_auth_explicit_hub(self): - image = 'docker.io/username/image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - )['username'] == 'indexuser' + image = "docker.io/username/image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + )["username"] + == "indexuser" + ) def test_resolve_registry_and_auth_explicit_legacy_hub(self): - image = 'index.docker.io/username/image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - )['username'] == 'indexuser' + image = "index.docker.io/username/image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + )["username"] + == "indexuser" + ) def test_resolve_registry_and_auth_private_registry(self): - image = 'my.registry.net/image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - )['username'] == 'privateuser' + image = "my.registry.net/image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + )["username"] + == "privateuser" + ) def test_resolve_registry_and_auth_unauthenticated_registry(self): - image = 'other.registry.net/image' - assert auth.resolve_authconfig( - self.auth_config, auth.resolve_repository_name(image)[0] - ) is None + image = "other.registry.net/image" + assert ( + auth.resolve_authconfig( + self.auth_config, auth.resolve_repository_name(image)[0] + ) + is None + ) def test_resolve_auth_with_empty_credstore_and_auth_dict(self): - auth_config = auth.AuthConfig({ - 'auths': auth.parse_auth({ - 'https://index.docker.io/v1/': self.index_config, - }), - 'credsStore': 'blackbox' - }) + auth_config = auth.AuthConfig( + { + "auths": auth.parse_auth( + { + "https://index.docker.io/v1/": self.index_config, + } + ), + "credsStore": "blackbox", + } + ) with mock.patch( - 'ansible_collections.community.docker.plugins.module_utils._api.auth.AuthConfig._resolve_authconfig_credstore' + "ansible_collections.community.docker.plugins.module_utils._api.auth.AuthConfig._resolve_authconfig_credstore" ) as m: m.return_value = None - assert 'indexuser' == auth.resolve_authconfig( - auth_config, None - )['username'] + assert "indexuser" == auth.resolve_authconfig(auth_config, None)["username"] class LoadConfigTest(unittest.TestCase): @@ -249,541 +298,507 @@ class LoadConfigTest(unittest.TestCase): def test_load_legacy_config(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - cfg_path = os.path.join(folder, '.dockercfg') - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') - with open(cfg_path, 'w') as f: - f.write(f'auth = {auth_}\n') - f.write('email = sakuya@scarlet.net') + cfg_path = os.path.join(folder, ".dockercfg") + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") + with open(cfg_path, "w") as f: + f.write(f"auth = {auth_}\n") + f.write("email = sakuya@scarlet.net") cfg = auth.load_config(cfg_path) assert auth.resolve_authconfig(cfg) is not None assert cfg.auths[auth.INDEX_NAME] is not None cfg = cfg.auths[auth.INDEX_NAME] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == 'sakuya@scarlet.net' - assert cfg.get('Auth') is None + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == "sakuya@scarlet.net" + assert cfg.get("Auth") is None def test_load_json_config(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - cfg_path = os.path.join(folder, '.dockercfg') - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') - email = 'sakuya@scarlet.net' - with open(cfg_path, 'w') as f: - json.dump( - {auth.INDEX_URL: {'auth': auth_, 'email': email}}, f - ) + cfg_path = os.path.join(folder, ".dockercfg") + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") + email = "sakuya@scarlet.net" + with open(cfg_path, "w") as f: + json.dump({auth.INDEX_URL: {"auth": auth_, "email": email}}, f) cfg = auth.load_config(cfg_path) assert auth.resolve_authconfig(cfg) is not None assert cfg.auths[auth.INDEX_URL] is not None cfg = cfg.auths[auth.INDEX_URL] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == email - assert cfg.get('Auth') is None + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == email + assert cfg.get("Auth") is None def test_load_modern_json_config(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - cfg_path = os.path.join(folder, 'config.json') - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') - email = 'sakuya@scarlet.net' - with open(cfg_path, 'w') as f: - json.dump({ - 'auths': { - auth.INDEX_URL: { - 'auth': auth_, 'email': email - } - } - }, f) + cfg_path = os.path.join(folder, "config.json") + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") + email = "sakuya@scarlet.net" + with open(cfg_path, "w") as f: + json.dump({"auths": {auth.INDEX_URL: {"auth": auth_, "email": email}}}, f) cfg = auth.load_config(cfg_path) assert auth.resolve_authconfig(cfg) is not None assert cfg.auths[auth.INDEX_URL] is not None cfg = cfg.auths[auth.INDEX_URL] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == email + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == email def test_load_config_with_random_name(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, f'.{random.randrange(100000)}.dockercfg') - registry = 'https://your.private.registry.io' - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') - config = { - registry: { - 'auth': f'{auth_}', - 'email': 'sakuya@scarlet.net' - } - } + dockercfg_path = os.path.join(folder, f".{random.randrange(100000)}.dockercfg") + registry = "https://your.private.registry.io" + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") + config = {registry: {"auth": f"{auth_}", "email": "sakuya@scarlet.net"}} - with open(dockercfg_path, 'w') as f: + with open(dockercfg_path, "w") as f: json.dump(config, f) cfg = auth.load_config(dockercfg_path).auths assert registry in cfg assert cfg[registry] is not None cfg = cfg[registry] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == 'sakuya@scarlet.net' - assert cfg.get('auth') is None + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == "sakuya@scarlet.net" + assert cfg.get("auth") is None def test_load_config_custom_config_env(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - registry = 'https://your.private.registry.io' - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') - config = { - registry: { - 'auth': f'{auth_}', - 'email': 'sakuya@scarlet.net' - } - } + dockercfg_path = os.path.join(folder, "config.json") + registry = "https://your.private.registry.io" + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") + config = {registry: {"auth": f"{auth_}", "email": "sakuya@scarlet.net"}} - with open(dockercfg_path, 'w') as f: + with open(dockercfg_path, "w") as f: json.dump(config, f) - with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): + with mock.patch.dict(os.environ, {"DOCKER_CONFIG": folder}): cfg = auth.load_config(None).auths assert registry in cfg assert cfg[registry] is not None cfg = cfg[registry] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == 'sakuya@scarlet.net' - assert cfg.get('auth') is None + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == "sakuya@scarlet.net" + assert cfg.get("auth") is None def test_load_config_custom_config_env_with_auths(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - registry = 'https://your.private.registry.io' - auth_ = base64.b64encode(b'sakuya:izayoi').decode('ascii') + dockercfg_path = os.path.join(folder, "config.json") + registry = "https://your.private.registry.io" + auth_ = base64.b64encode(b"sakuya:izayoi").decode("ascii") config = { - 'auths': { - registry: { - 'auth': f'{auth_}', - 'email': 'sakuya@scarlet.net' - } - } + "auths": {registry: {"auth": f"{auth_}", "email": "sakuya@scarlet.net"}} } - with open(dockercfg_path, 'w') as f: + with open(dockercfg_path, "w") as f: json.dump(config, f) - with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): + with mock.patch.dict(os.environ, {"DOCKER_CONFIG": folder}): cfg = auth.load_config(None) assert registry in cfg.auths cfg = cfg.auths[registry] - assert cfg['username'] == 'sakuya' - assert cfg['password'] == 'izayoi' - assert cfg['email'] == 'sakuya@scarlet.net' - assert cfg.get('auth') is None + assert cfg["username"] == "sakuya" + assert cfg["password"] == "izayoi" + assert cfg["email"] == "sakuya@scarlet.net" + assert cfg.get("auth") is None def test_load_config_custom_config_env_utf8(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - registry = 'https://your.private.registry.io' - auth_ = base64.b64encode( - b'sakuya\xc3\xa6:izayoi\xc3\xa6').decode('ascii') + dockercfg_path = os.path.join(folder, "config.json") + registry = "https://your.private.registry.io" + auth_ = base64.b64encode(b"sakuya\xc3\xa6:izayoi\xc3\xa6").decode("ascii") config = { - 'auths': { - registry: { - 'auth': f'{auth_}', - 'email': 'sakuya@scarlet.net' - } - } + "auths": {registry: {"auth": f"{auth_}", "email": "sakuya@scarlet.net"}} } - with open(dockercfg_path, 'w') as f: + with open(dockercfg_path, "w") as f: json.dump(config, f) - with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): + with mock.patch.dict(os.environ, {"DOCKER_CONFIG": folder}): cfg = auth.load_config(None) assert registry in cfg.auths cfg = cfg.auths[registry] - assert cfg['username'] == b'sakuya\xc3\xa6'.decode('utf8') - assert cfg['password'] == b'izayoi\xc3\xa6'.decode('utf8') - assert cfg['email'] == 'sakuya@scarlet.net' - assert cfg.get('auth') is None + assert cfg["username"] == b"sakuya\xc3\xa6".decode("utf8") + assert cfg["password"] == b"izayoi\xc3\xa6".decode("utf8") + assert cfg["email"] == "sakuya@scarlet.net" + assert cfg.get("auth") is None def test_load_config_unknown_keys(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - config = { - 'detachKeys': 'ctrl-q, ctrl-u, ctrl-i' - } - with open(dockercfg_path, 'w') as f: + dockercfg_path = os.path.join(folder, "config.json") + config = {"detachKeys": "ctrl-q, ctrl-u, ctrl-i"} + with open(dockercfg_path, "w") as f: json.dump(config, f) cfg = auth.load_config(dockercfg_path) - assert dict(cfg) == {'auths': {}} + assert dict(cfg) == {"auths": {}} def test_load_config_invalid_auth_dict(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - config = { - 'auths': { - 'scarlet.net': {'sakuya': 'izayoi'} - } - } - with open(dockercfg_path, 'w') as f: + dockercfg_path = os.path.join(folder, "config.json") + config = {"auths": {"scarlet.net": {"sakuya": "izayoi"}}} + with open(dockercfg_path, "w") as f: json.dump(config, f) cfg = auth.load_config(dockercfg_path) - assert dict(cfg) == {'auths': {'scarlet.net': {}}} + assert dict(cfg) == {"auths": {"scarlet.net": {}}} def test_load_config_identity_token(self): folder = tempfile.mkdtemp() - registry = 'scarlet.net' - token = '1ce1cebb-503e-7043-11aa-7feb8bd4a1ce' + registry = "scarlet.net" + token = "1ce1cebb-503e-7043-11aa-7feb8bd4a1ce" self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - auth_entry = encode_auth({'username': 'sakuya'}).decode('ascii') - config = { - 'auths': { - registry: { - 'auth': auth_entry, - 'identitytoken': token - } - } - } - with open(dockercfg_path, 'w') as f: + dockercfg_path = os.path.join(folder, "config.json") + auth_entry = encode_auth({"username": "sakuya"}).decode("ascii") + config = {"auths": {registry: {"auth": auth_entry, "identitytoken": token}}} + with open(dockercfg_path, "w") as f: json.dump(config, f) cfg = auth.load_config(dockercfg_path) assert registry in cfg.auths cfg = cfg.auths[registry] - assert 'IdentityToken' in cfg - assert cfg['IdentityToken'] == token + assert "IdentityToken" in cfg + assert cfg["IdentityToken"] == token class CredstoreTest(unittest.TestCase): def setUp(self): - self.authconfig = auth.AuthConfig({'credsStore': 'default'}) - self.default_store = InMemoryStore('default') - self.authconfig._stores['default'] = self.default_store + self.authconfig = auth.AuthConfig({"credsStore": "default"}) + self.default_store = InMemoryStore("default") + self.authconfig._stores["default"] = self.default_store self.default_store.store( - 'https://gensokyo.jp/v2', 'sakuya', 'izayoi', + "https://gensokyo.jp/v2", + "sakuya", + "izayoi", ) self.default_store.store( - 'https://default.com/v2', 'user', 'hunter2', + "https://default.com/v2", + "user", + "hunter2", ) def test_get_credential_store(self): - auth_config = auth.AuthConfig({ - 'credHelpers': { - 'registry1.io': 'truesecret', - 'registry2.io': 'powerlock' - }, - 'credsStore': 'blackbox', - }) + auth_config = auth.AuthConfig( + { + "credHelpers": { + "registry1.io": "truesecret", + "registry2.io": "powerlock", + }, + "credsStore": "blackbox", + } + ) - assert auth_config.get_credential_store('registry1.io') == 'truesecret' - assert auth_config.get_credential_store('registry2.io') == 'powerlock' - assert auth_config.get_credential_store('registry3.io') == 'blackbox' + assert auth_config.get_credential_store("registry1.io") == "truesecret" + assert auth_config.get_credential_store("registry2.io") == "powerlock" + assert auth_config.get_credential_store("registry3.io") == "blackbox" def test_get_credential_store_no_default(self): - auth_config = auth.AuthConfig({ - 'credHelpers': { - 'registry1.io': 'truesecret', - 'registry2.io': 'powerlock' - }, - }) - assert auth_config.get_credential_store('registry2.io') == 'powerlock' - assert auth_config.get_credential_store('registry3.io') is None + auth_config = auth.AuthConfig( + { + "credHelpers": { + "registry1.io": "truesecret", + "registry2.io": "powerlock", + }, + } + ) + assert auth_config.get_credential_store("registry2.io") == "powerlock" + assert auth_config.get_credential_store("registry3.io") is None def test_get_credential_store_default_index(self): - auth_config = auth.AuthConfig({ - 'credHelpers': { - 'https://index.docker.io/v1/': 'powerlock' - }, - 'credsStore': 'truesecret' - }) + auth_config = auth.AuthConfig( + { + "credHelpers": {"https://index.docker.io/v1/": "powerlock"}, + "credsStore": "truesecret", + } + ) - assert auth_config.get_credential_store(None) == 'powerlock' - assert auth_config.get_credential_store('docker.io') == 'powerlock' - assert auth_config.get_credential_store('images.io') == 'truesecret' + assert auth_config.get_credential_store(None) == "powerlock" + assert auth_config.get_credential_store("docker.io") == "powerlock" + assert auth_config.get_credential_store("images.io") == "truesecret" def test_get_credential_store_with_plain_dict(self): auth_config = { - 'credHelpers': { - 'registry1.io': 'truesecret', - 'registry2.io': 'powerlock' - }, - 'credsStore': 'blackbox', + "credHelpers": {"registry1.io": "truesecret", "registry2.io": "powerlock"}, + "credsStore": "blackbox", } - assert auth.get_credential_store( - auth_config, 'registry1.io' - ) == 'truesecret' - assert auth.get_credential_store( - auth_config, 'registry2.io' - ) == 'powerlock' - assert auth.get_credential_store( - auth_config, 'registry3.io' - ) == 'blackbox' + assert auth.get_credential_store(auth_config, "registry1.io") == "truesecret" + assert auth.get_credential_store(auth_config, "registry2.io") == "powerlock" + assert auth.get_credential_store(auth_config, "registry3.io") == "blackbox" def test_get_all_credentials_credstore_only(self): assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, } def test_get_all_credentials_with_empty_credhelper(self): - self.authconfig['credHelpers'] = { - 'registry1.io': 'truesecret', + self.authconfig["credHelpers"] = { + "registry1.io": "truesecret", } - self.authconfig._stores['truesecret'] = InMemoryStore() + self.authconfig._stores["truesecret"] = InMemoryStore() assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'registry1.io': None, + "registry1.io": None, } def test_get_all_credentials_with_credhelpers_only(self): - del self.authconfig['credsStore'] + del self.authconfig["credsStore"] assert self.authconfig.get_all_credentials() == {} - self.authconfig['credHelpers'] = { - 'https://gensokyo.jp/v2': 'default', - 'https://default.com/v2': 'default', + self.authconfig["credHelpers"] = { + "https://gensokyo.jp/v2": "default", + "https://default.com/v2": "default", } assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, } def test_get_all_credentials_with_auths_entries(self): - self.authconfig.add_auth('registry1.io', { - 'ServerAddress': 'registry1.io', - 'Username': 'reimu', - 'Password': 'hakurei', - }) + self.authconfig.add_auth( + "registry1.io", + { + "ServerAddress": "registry1.io", + "Username": "reimu", + "Password": "hakurei", + }, + ) assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'registry1.io': { - 'ServerAddress': 'registry1.io', - 'Username': 'reimu', - 'Password': 'hakurei', + "registry1.io": { + "ServerAddress": "registry1.io", + "Username": "reimu", + "Password": "hakurei", }, } def test_get_all_credentials_with_empty_auths_entry(self): - self.authconfig.add_auth('default.com', {}) + self.authconfig.add_auth("default.com", {}) assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, } def test_get_all_credentials_credstore_overrides_auth_entry(self): - self.authconfig.add_auth('default.com', { - 'Username': 'shouldnotsee', - 'Password': 'thisentry', - 'ServerAddress': 'https://default.com/v2', - }) + self.authconfig.add_auth( + "default.com", + { + "Username": "shouldnotsee", + "Password": "thisentry", + "ServerAddress": "https://default.com/v2", + }, + ) assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, } def test_get_all_credentials_helpers_override_default(self): - self.authconfig['credHelpers'] = { - 'https://default.com/v2': 'truesecret', + self.authconfig["credHelpers"] = { + "https://default.com/v2": "truesecret", } - truesecret = InMemoryStore('truesecret') - truesecret.store('https://default.com/v2', 'reimu', 'hakurei') - self.authconfig._stores['truesecret'] = truesecret + truesecret = InMemoryStore("truesecret") + truesecret.store("https://default.com/v2", "reimu", "hakurei") + self.authconfig._stores["truesecret"] = truesecret assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'reimu', - 'Password': 'hakurei', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "reimu", + "Password": "hakurei", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'reimu', - 'Password': 'hakurei', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "reimu", + "Password": "hakurei", + "ServerAddress": "https://default.com/v2", }, } def test_get_all_credentials_3_sources(self): - self.authconfig['credHelpers'] = { - 'registry1.io': 'truesecret', + self.authconfig["credHelpers"] = { + "registry1.io": "truesecret", } - truesecret = InMemoryStore('truesecret') - truesecret.store('registry1.io', 'reimu', 'hakurei') - self.authconfig._stores['truesecret'] = truesecret - self.authconfig.add_auth('registry2.io', { - 'ServerAddress': 'registry2.io', - 'Username': 'reimu', - 'Password': 'hakurei', - }) + truesecret = InMemoryStore("truesecret") + truesecret.store("registry1.io", "reimu", "hakurei") + self.authconfig._stores["truesecret"] = truesecret + self.authconfig.add_auth( + "registry2.io", + { + "ServerAddress": "registry2.io", + "Username": "reimu", + "Password": "hakurei", + }, + ) assert self.authconfig.get_all_credentials() == { - 'https://gensokyo.jp/v2': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "https://gensokyo.jp/v2": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'gensokyo.jp': { - 'Username': 'sakuya', - 'Password': 'izayoi', - 'ServerAddress': 'https://gensokyo.jp/v2', + "gensokyo.jp": { + "Username": "sakuya", + "Password": "izayoi", + "ServerAddress": "https://gensokyo.jp/v2", }, - 'https://default.com/v2': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "https://default.com/v2": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'default.com': { - 'Username': 'user', - 'Password': 'hunter2', - 'ServerAddress': 'https://default.com/v2', + "default.com": { + "Username": "user", + "Password": "hunter2", + "ServerAddress": "https://default.com/v2", }, - 'registry1.io': { - 'ServerAddress': 'registry1.io', - 'Username': 'reimu', - 'Password': 'hakurei', + "registry1.io": { + "ServerAddress": "registry1.io", + "Username": "reimu", + "Password": "hakurei", + }, + "registry2.io": { + "ServerAddress": "registry2.io", + "Username": "reimu", + "Password": "hakurei", }, - 'registry2.io': { - 'ServerAddress': 'registry2.io', - 'Username': 'reimu', - 'Password': 'hakurei', - } } @@ -799,15 +814,13 @@ class InMemoryStore(Store): def store(self, server, username, secret): self.__store[server] = { - 'ServerURL': server, - 'Username': username, - 'Secret': secret, + "ServerURL": server, + "Username": username, + "Secret": secret, } def list(self): - return dict( - (k, v['Username']) for k, v in self.__store.items() - ) + return dict((k, v["Username"]) for k, v in self.__store.items()) def erase(self, server): del self.__store[server] diff --git a/tests/unit/plugins/module_utils/_api/test_context.py b/tests/unit/plugins/module_utils/_api/test_context.py index 04fec26e..36fb1128 100644 --- a/tests/unit/plugins/module_utils/_api/test_context.py +++ b/tests/unit/plugins/module_utils/_api/test_context.py @@ -7,34 +7,35 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest import pytest - from ansible_collections.community.docker.plugins.module_utils._api import errors from ansible_collections.community.docker.plugins.module_utils._api.constants import ( DEFAULT_NPIPE, DEFAULT_UNIX_SOCKET, IS_WINDOWS_PLATFORM, ) -from ansible_collections.community.docker.plugins.module_utils._api.context.api import ContextAPI -from ansible_collections.community.docker.plugins.module_utils._api.context.context import Context +from ansible_collections.community.docker.plugins.module_utils._api.context.api import ( + ContextAPI, +) +from ansible_collections.community.docker.plugins.module_utils._api.context.context import ( + Context, +) class BaseContextTest(unittest.TestCase): - @pytest.mark.skipif( - IS_WINDOWS_PLATFORM, reason='Linux specific path check' - ) + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="Linux specific path check") def test_url_compatibility_on_linux(self): c = Context("test") assert c.Host == DEFAULT_UNIX_SOCKET[5:] - @pytest.mark.skipif( - not IS_WINDOWS_PLATFORM, reason='Windows specific path check' - ) + @pytest.mark.skipif(not IS_WINDOWS_PLATFORM, reason="Windows specific path check") def test_url_compatibility_on_windows(self): c = Context("test") assert c.Host == DEFAULT_NPIPE diff --git a/tests/unit/plugins/module_utils/_api/test_errors.py b/tests/unit/plugins/module_utils/_api/test_errors.py index f434ca54..d3d1432b 100644 --- a/tests/unit/plugins/module_utils/_api/test_errors.py +++ b/tests/unit/plugins/module_utils/_api/test_errors.py @@ -7,17 +7,19 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest import requests - from ansible_collections.community.docker.plugins.module_utils._api.errors import ( - APIError, DockerException, - create_unexpected_kwargs_error, + APIError, + DockerException, create_api_error_from_http_exception, + create_unexpected_kwargs_error, ) @@ -32,90 +34,90 @@ class APIErrorTest(unittest.TestCase): """The status_code property is present with 200 response.""" resp = requests.Response() resp.status_code = 200 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.status_code == 200 def test_status_code_400(self): """The status_code property is present with 400 response.""" resp = requests.Response() resp.status_code = 400 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.status_code == 400 def test_status_code_500(self): """The status_code property is present with 500 response.""" resp = requests.Response() resp.status_code = 500 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.status_code == 500 def test_is_server_error_200(self): """Report not server error on 200 response.""" resp = requests.Response() resp.status_code = 200 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_server_error() is False def test_is_server_error_300(self): """Report not server error on 300 response.""" resp = requests.Response() resp.status_code = 300 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_server_error() is False def test_is_server_error_400(self): """Report not server error on 400 response.""" resp = requests.Response() resp.status_code = 400 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_server_error() is False def test_is_server_error_500(self): """Report server error on 500 response.""" resp = requests.Response() resp.status_code = 500 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_server_error() is True def test_is_client_error_500(self): """Report not client error on 500 response.""" resp = requests.Response() resp.status_code = 500 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_client_error() is False def test_is_client_error_400(self): """Report client error on 400 response.""" resp = requests.Response() resp.status_code = 400 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_client_error() is True def test_is_error_300(self): """Report no error on 300 response.""" resp = requests.Response() resp.status_code = 300 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_error() is False def test_is_error_400(self): """Report error on 400 response.""" resp = requests.Response() resp.status_code = 400 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_error() is True def test_is_error_500(self): """Report error on 500 response.""" resp = requests.Response() resp.status_code = 500 - err = APIError('', response=resp) + err = APIError("", response=resp) assert err.is_error() is True def test_create_error_from_exception(self): resp = requests.Response() resp.status_code = 500 - err = APIError('') + err = APIError("") try: resp.raise_for_status() except requests.exceptions.HTTPError as e: @@ -128,9 +130,9 @@ class APIErrorTest(unittest.TestCase): class CreateUnexpectedKwargsErrorTest(unittest.TestCase): def test_create_unexpected_kwargs_error_single(self): - e = create_unexpected_kwargs_error('f', {'foo': 'bar'}) + e = create_unexpected_kwargs_error("f", {"foo": "bar"}) assert str(e) == "f() got an unexpected keyword argument 'foo'" def test_create_unexpected_kwargs_error_multiple(self): - e = create_unexpected_kwargs_error('f', {'foo': 'bar', 'baz': 'bosh'}) + e = create_unexpected_kwargs_error("f", {"foo": "bar", "baz": "bosh"}) assert str(e) == "f() got unexpected keyword arguments 'baz', 'foo'" diff --git a/tests/unit/plugins/module_utils/_api/transport/test_sshconn.py b/tests/unit/plugins/module_utils/_api/transport/test_sshconn.py index 0ff7364f..dffbaf7b 100644 --- a/tests/unit/plugins/module_utils/_api/transport/test_sshconn.py +++ b/tests/unit/plugins/module_utils/_api/transport/test_sshconn.py @@ -7,19 +7,23 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest -from ansible_collections.community.docker.plugins.module_utils._api.transport.sshconn import SSHSocket, SSHHTTPAdapter +from ansible_collections.community.docker.plugins.module_utils._api.transport.sshconn import ( + SSHHTTPAdapter, + SSHSocket, +) class SSHAdapterTest(unittest.TestCase): @staticmethod def test_ssh_hostname_prefix_trim(): - conn = SSHHTTPAdapter( - base_url="ssh://user@hostname:1234", shell_out=True) + conn = SSHHTTPAdapter(base_url="ssh://user@hostname:1234", shell_out=True) assert conn.ssh_host == "user@hostname:1234" @staticmethod diff --git a/tests/unit/plugins/module_utils/_api/transport/test_ssladapter.py b/tests/unit/plugins/module_utils/_api/transport/test_ssladapter.py index 1f00b08d..57968611 100644 --- a/tests/unit/plugins/module_utils/_api/transport/test_ssladapter.py +++ b/tests/unit/plugins/module_utils/_api/transport/test_ssladapter.py @@ -7,30 +7,30 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest import pytest +from ansible_collections.community.docker.plugins.module_utils._api.transport import ( + ssladapter, +) -from ansible_collections.community.docker.plugins.module_utils._api.transport import ssladapter HAS_MATCH_HOSTNAME = True try: - from backports.ssl_match_hostname import ( - match_hostname, CertificateError - ) + from backports.ssl_match_hostname import CertificateError, match_hostname except ImportError: try: - from ssl import ( - match_hostname, CertificateError - ) + from ssl import CertificateError, match_hostname except ImportError: HAS_MATCH_HOSTNAME = False try: - from ssl import OP_NO_SSLv3, OP_NO_SSLv2, OP_NO_TLSv1 + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1 except ImportError: OP_NO_SSLv2 = 0x1000000 OP_NO_SSLv3 = 0x2000000 @@ -47,51 +47,51 @@ class SSLAdapterTest(unittest.TestCase): assert not ssl_context.options & OP_NO_TLSv1 -@pytest.mark.skipif(not HAS_MATCH_HOSTNAME, reason='match_hostname is not available') +@pytest.mark.skipif(not HAS_MATCH_HOSTNAME, reason="match_hostname is not available") class MatchHostnameTest(unittest.TestCase): cert = { - 'issuer': ( - (('countryName', 'US'),), - (('stateOrProvinceName', 'California'),), - (('localityName', 'San Francisco'),), - (('organizationName', 'Docker Inc'),), - (('organizationalUnitName', 'Docker-Python'),), - (('commonName', 'localhost'),), - (('emailAddress', 'info@docker.com'),) + "issuer": ( + (("countryName", "US"),), + (("stateOrProvinceName", "California"),), + (("localityName", "San Francisco"),), + (("organizationName", "Docker Inc"),), + (("organizationalUnitName", "Docker-Python"),), + (("commonName", "localhost"),), + (("emailAddress", "info@docker.com"),), ), - 'notAfter': 'Mar 25 23:08:23 2030 GMT', - 'notBefore': 'Mar 25 23:08:23 2016 GMT', - 'serialNumber': 'BD5F894C839C548F', - 'subject': ( - (('countryName', 'US'),), - (('stateOrProvinceName', 'California'),), - (('localityName', 'San Francisco'),), - (('organizationName', 'Docker Inc'),), - (('organizationalUnitName', 'Docker-Python'),), - (('commonName', 'localhost'),), - (('emailAddress', 'info@docker.com'),) + "notAfter": "Mar 25 23:08:23 2030 GMT", + "notBefore": "Mar 25 23:08:23 2016 GMT", + "serialNumber": "BD5F894C839C548F", + "subject": ( + (("countryName", "US"),), + (("stateOrProvinceName", "California"),), + (("localityName", "San Francisco"),), + (("organizationName", "Docker Inc"),), + (("organizationalUnitName", "Docker-Python"),), + (("commonName", "localhost"),), + (("emailAddress", "info@docker.com"),), ), - 'subjectAltName': ( - ('DNS', 'localhost'), - ('DNS', '*.gensokyo.jp'), - ('IP Address', '127.0.0.1'), + "subjectAltName": ( + ("DNS", "localhost"), + ("DNS", "*.gensokyo.jp"), + ("IP Address", "127.0.0.1"), ), - 'version': 3 + "version": 3, } def test_match_ip_address_success(self): - assert match_hostname(self.cert, '127.0.0.1') is None + assert match_hostname(self.cert, "127.0.0.1") is None def test_match_localhost_success(self): - assert match_hostname(self.cert, 'localhost') is None + assert match_hostname(self.cert, "localhost") is None def test_match_dns_success(self): - assert match_hostname(self.cert, 'touhou.gensokyo.jp') is None + assert match_hostname(self.cert, "touhou.gensokyo.jp") is None def test_match_ip_address_failure(self): with pytest.raises(CertificateError): - match_hostname(self.cert, '192.168.0.25') + match_hostname(self.cert, "192.168.0.25") def test_match_dns_failure(self): with pytest.raises(CertificateError): - match_hostname(self.cert, 'foobar.co.uk') + match_hostname(self.cert, "foobar.co.uk") diff --git a/tests/unit/plugins/module_utils/_api/utils/test_build.py b/tests/unit/plugins/module_utils/_api/utils/test_build.py index a7fd74d6..fb5e8878 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_build.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_build.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import os @@ -19,9 +21,13 @@ import tempfile import unittest import pytest - -from ansible_collections.community.docker.plugins.module_utils._api.constants import IS_WINDOWS_PLATFORM -from ansible_collections.community.docker.plugins.module_utils._api.utils.build import exclude_paths, tar +from ansible_collections.community.docker.plugins.module_utils._api.constants import ( + IS_WINDOWS_PLATFORM, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.build import ( + exclude_paths, + tar, +) def make_tree(dirs, files): @@ -31,7 +37,7 @@ def make_tree(dirs, files): os.makedirs(os.path.join(base, path)) for path in files: - with open(os.path.join(base, path), 'w') as f: + with open(os.path.join(base, path), "w") as f: f.write("content") return base @@ -42,45 +48,45 @@ def convert_paths(collection): def convert_path(path): - return path.replace('/', os.path.sep) + return path.replace("/", os.path.sep) class ExcludePathsTest(unittest.TestCase): dirs = [ - 'foo', - 'foo/bar', - 'bar', - 'target', - 'target/subdir', - 'subdir', - 'subdir/target', - 'subdir/target/subdir', - 'subdir/subdir2', - 'subdir/subdir2/target', - 'subdir/subdir2/target/subdir' + "foo", + "foo/bar", + "bar", + "target", + "target/subdir", + "subdir", + "subdir/target", + "subdir/target/subdir", + "subdir/subdir2", + "subdir/subdir2/target", + "subdir/subdir2/target/subdir", ] files = [ - 'Dockerfile', - 'Dockerfile.alt', - '.dockerignore', - 'a.py', - 'a.go', - 'b.py', - 'cde.py', - 'foo/a.py', - 'foo/b.py', - 'foo/bar/a.py', - 'bar/a.py', - 'foo/Dockerfile3', - 'target/file.txt', - 'target/subdir/file.txt', - 'subdir/file.txt', - 'subdir/target/file.txt', - 'subdir/target/subdir/file.txt', - 'subdir/subdir2/file.txt', - 'subdir/subdir2/target/file.txt', - 'subdir/subdir2/target/subdir/file.txt', + "Dockerfile", + "Dockerfile.alt", + ".dockerignore", + "a.py", + "a.go", + "b.py", + "cde.py", + "foo/a.py", + "foo/b.py", + "foo/bar/a.py", + "bar/a.py", + "foo/Dockerfile3", + "target/file.txt", + "target/subdir/file.txt", + "subdir/file.txt", + "subdir/target/file.txt", + "subdir/target/subdir/file.txt", + "subdir/subdir2/file.txt", + "subdir/subdir2/target/file.txt", + "subdir/subdir2/target/subdir/file.txt", ] all_paths = set(dirs + files) @@ -95,14 +101,14 @@ class ExcludePathsTest(unittest.TestCase): return set(exclude_paths(self.base, patterns, dockerfile=dockerfile)) def test_no_excludes(self): - assert self.exclude(['']) == convert_paths(self.all_paths) + assert self.exclude([""]) == convert_paths(self.all_paths) def test_no_dupes(self): - paths = exclude_paths(self.base, ['!a.py']) + paths = exclude_paths(self.base, ["!a.py"]) assert sorted(paths) == sorted(set(paths)) def test_wildcard_exclude(self): - assert self.exclude(['*']) == set(['Dockerfile', '.dockerignore']) + assert self.exclude(["*"]) == set(["Dockerfile", ".dockerignore"]) def test_exclude_dockerfile_dockerignore(self): """ @@ -110,7 +116,7 @@ class ExcludePathsTest(unittest.TestCase): Dockerfile and/or .dockerignore, don't exclude them from the actual tar file. """ - assert self.exclude(['Dockerfile', '.dockerignore']) == convert_paths( + assert self.exclude(["Dockerfile", ".dockerignore"]) == convert_paths( self.all_paths ) @@ -119,287 +125,296 @@ class ExcludePathsTest(unittest.TestCase): If we're using a custom Dockerfile, make sure that's not excluded. """ - assert self.exclude(['*'], dockerfile='Dockerfile.alt') == set(['Dockerfile.alt', '.dockerignore']) + assert self.exclude(["*"], dockerfile="Dockerfile.alt") == set( + ["Dockerfile.alt", ".dockerignore"] + ) - assert self.exclude( - ['*'], dockerfile='foo/Dockerfile3' - ) == convert_paths(set(['foo/Dockerfile3', '.dockerignore'])) + assert self.exclude(["*"], dockerfile="foo/Dockerfile3") == convert_paths( + set(["foo/Dockerfile3", ".dockerignore"]) + ) # https://github.com/docker/docker-py/issues/1956 - assert self.exclude( - ['*'], dockerfile='./foo/Dockerfile3' - ) == convert_paths(set(['foo/Dockerfile3', '.dockerignore'])) + assert self.exclude(["*"], dockerfile="./foo/Dockerfile3") == convert_paths( + set(["foo/Dockerfile3", ".dockerignore"]) + ) def test_exclude_dockerfile_child(self): - includes = self.exclude(['foo/'], dockerfile='foo/Dockerfile3') - assert convert_path('foo/Dockerfile3') in includes - assert convert_path('foo/a.py') not in includes + includes = self.exclude(["foo/"], dockerfile="foo/Dockerfile3") + assert convert_path("foo/Dockerfile3") in includes + assert convert_path("foo/a.py") not in includes def test_single_filename(self): - assert self.exclude(['a.py']) == convert_paths( - self.all_paths - set(['a.py']) - ) + assert self.exclude(["a.py"]) == convert_paths(self.all_paths - set(["a.py"])) def test_single_filename_leading_dot_slash(self): - assert self.exclude(['./a.py']) == convert_paths( - self.all_paths - set(['a.py']) - ) + assert self.exclude(["./a.py"]) == convert_paths(self.all_paths - set(["a.py"])) # As odd as it sounds, a filename pattern with a trailing slash on the # end *will* result in that file being excluded. def test_single_filename_trailing_slash(self): - assert self.exclude(['a.py/']) == convert_paths( - self.all_paths - set(['a.py']) - ) + assert self.exclude(["a.py/"]) == convert_paths(self.all_paths - set(["a.py"])) def test_wildcard_filename_start(self): - assert self.exclude(['*.py']) == convert_paths( - self.all_paths - set(['a.py', 'b.py', 'cde.py']) + assert self.exclude(["*.py"]) == convert_paths( + self.all_paths - set(["a.py", "b.py", "cde.py"]) ) def test_wildcard_with_exception(self): - assert self.exclude(['*.py', '!b.py']) == convert_paths( - self.all_paths - set(['a.py', 'cde.py']) + assert self.exclude(["*.py", "!b.py"]) == convert_paths( + self.all_paths - set(["a.py", "cde.py"]) ) def test_wildcard_with_wildcard_exception(self): - assert self.exclude(['*.*', '!*.go']) == convert_paths( - self.all_paths - set([ - 'a.py', 'b.py', 'cde.py', 'Dockerfile.alt', - ]) + assert self.exclude(["*.*", "!*.go"]) == convert_paths( + self.all_paths + - set( + [ + "a.py", + "b.py", + "cde.py", + "Dockerfile.alt", + ] + ) ) def test_wildcard_filename_end(self): - assert self.exclude(['a.*']) == convert_paths( - self.all_paths - set(['a.py', 'a.go']) + assert self.exclude(["a.*"]) == convert_paths( + self.all_paths - set(["a.py", "a.go"]) ) def test_question_mark(self): - assert self.exclude(['?.py']) == convert_paths( - self.all_paths - set(['a.py', 'b.py']) + assert self.exclude(["?.py"]) == convert_paths( + self.all_paths - set(["a.py", "b.py"]) ) def test_single_subdir_single_filename(self): - assert self.exclude(['foo/a.py']) == convert_paths( - self.all_paths - set(['foo/a.py']) + assert self.exclude(["foo/a.py"]) == convert_paths( + self.all_paths - set(["foo/a.py"]) ) def test_single_subdir_single_filename_leading_slash(self): - assert self.exclude(['/foo/a.py']) == convert_paths( - self.all_paths - set(['foo/a.py']) + assert self.exclude(["/foo/a.py"]) == convert_paths( + self.all_paths - set(["foo/a.py"]) ) def test_exclude_include_absolute_path(self): - base = make_tree([], ['a.py', 'b.py']) - assert exclude_paths( - base, - ['/*', '!/*.py'] - ) == set(['a.py', 'b.py']) + base = make_tree([], ["a.py", "b.py"]) + assert exclude_paths(base, ["/*", "!/*.py"]) == set(["a.py", "b.py"]) def test_single_subdir_with_path_traversal(self): - assert self.exclude(['foo/whoops/../a.py']) == convert_paths( - self.all_paths - set(['foo/a.py']) + assert self.exclude(["foo/whoops/../a.py"]) == convert_paths( + self.all_paths - set(["foo/a.py"]) ) def test_single_subdir_wildcard_filename(self): - assert self.exclude(['foo/*.py']) == convert_paths( - self.all_paths - set(['foo/a.py', 'foo/b.py']) + assert self.exclude(["foo/*.py"]) == convert_paths( + self.all_paths - set(["foo/a.py", "foo/b.py"]) ) def test_wildcard_subdir_single_filename(self): - assert self.exclude(['*/a.py']) == convert_paths( - self.all_paths - set(['foo/a.py', 'bar/a.py']) + assert self.exclude(["*/a.py"]) == convert_paths( + self.all_paths - set(["foo/a.py", "bar/a.py"]) ) def test_wildcard_subdir_wildcard_filename(self): - assert self.exclude(['*/*.py']) == convert_paths( - self.all_paths - set(['foo/a.py', 'foo/b.py', 'bar/a.py']) + assert self.exclude(["*/*.py"]) == convert_paths( + self.all_paths - set(["foo/a.py", "foo/b.py", "bar/a.py"]) ) def test_directory(self): - assert self.exclude(['foo']) == convert_paths( - self.all_paths - set([ - 'foo', 'foo/a.py', 'foo/b.py', 'foo/bar', 'foo/bar/a.py', - 'foo/Dockerfile3' - ]) + assert self.exclude(["foo"]) == convert_paths( + self.all_paths + - set( + [ + "foo", + "foo/a.py", + "foo/b.py", + "foo/bar", + "foo/bar/a.py", + "foo/Dockerfile3", + ] + ) ) def test_directory_with_trailing_slash(self): - assert self.exclude(['foo']) == convert_paths( - self.all_paths - set([ - 'foo', 'foo/a.py', 'foo/b.py', - 'foo/bar', 'foo/bar/a.py', 'foo/Dockerfile3' - ]) + assert self.exclude(["foo"]) == convert_paths( + self.all_paths + - set( + [ + "foo", + "foo/a.py", + "foo/b.py", + "foo/bar", + "foo/bar/a.py", + "foo/Dockerfile3", + ] + ) ) def test_directory_with_single_exception(self): - assert self.exclude(['foo', '!foo/bar/a.py']) == convert_paths( - self.all_paths - set([ - 'foo/a.py', 'foo/b.py', 'foo', 'foo/bar', - 'foo/Dockerfile3' - ]) + assert self.exclude(["foo", "!foo/bar/a.py"]) == convert_paths( + self.all_paths + - set(["foo/a.py", "foo/b.py", "foo", "foo/bar", "foo/Dockerfile3"]) ) def test_directory_with_subdir_exception(self): - assert self.exclude(['foo', '!foo/bar']) == convert_paths( - self.all_paths - set([ - 'foo/a.py', 'foo/b.py', 'foo', 'foo/Dockerfile3' - ]) + assert self.exclude(["foo", "!foo/bar"]) == convert_paths( + self.all_paths - set(["foo/a.py", "foo/b.py", "foo", "foo/Dockerfile3"]) ) @pytest.mark.skipif( - not IS_WINDOWS_PLATFORM, reason='Backslash patterns only on Windows' + not IS_WINDOWS_PLATFORM, reason="Backslash patterns only on Windows" ) def test_directory_with_subdir_exception_win32_pathsep(self): - assert self.exclude(['foo', '!foo\\bar']) == convert_paths( - self.all_paths - set([ - 'foo/a.py', 'foo/b.py', 'foo', 'foo/Dockerfile3' - ]) + assert self.exclude(["foo", "!foo\\bar"]) == convert_paths( + self.all_paths - set(["foo/a.py", "foo/b.py", "foo", "foo/Dockerfile3"]) ) def test_directory_with_wildcard_exception(self): - assert self.exclude(['foo', '!foo/*.py']) == convert_paths( - self.all_paths - set([ - 'foo/bar', 'foo/bar/a.py', 'foo', 'foo/Dockerfile3' - ]) + assert self.exclude(["foo", "!foo/*.py"]) == convert_paths( + self.all_paths - set(["foo/bar", "foo/bar/a.py", "foo", "foo/Dockerfile3"]) ) def test_subdirectory(self): - assert self.exclude(['foo/bar']) == convert_paths( - self.all_paths - set(['foo/bar', 'foo/bar/a.py']) + assert self.exclude(["foo/bar"]) == convert_paths( + self.all_paths - set(["foo/bar", "foo/bar/a.py"]) ) @pytest.mark.skipif( - not IS_WINDOWS_PLATFORM, reason='Backslash patterns only on Windows' + not IS_WINDOWS_PLATFORM, reason="Backslash patterns only on Windows" ) def test_subdirectory_win32_pathsep(self): - assert self.exclude(['foo\\bar']) == convert_paths( - self.all_paths - set(['foo/bar', 'foo/bar/a.py']) + assert self.exclude(["foo\\bar"]) == convert_paths( + self.all_paths - set(["foo/bar", "foo/bar/a.py"]) ) def test_double_wildcard(self): - assert self.exclude(['**/a.py']) == convert_paths( - self.all_paths - set([ - 'a.py', 'foo/a.py', 'foo/bar/a.py', 'bar/a.py' - ]) + assert self.exclude(["**/a.py"]) == convert_paths( + self.all_paths - set(["a.py", "foo/a.py", "foo/bar/a.py", "bar/a.py"]) ) - assert self.exclude(['foo/**/bar']) == convert_paths( - self.all_paths - set(['foo/bar', 'foo/bar/a.py']) + assert self.exclude(["foo/**/bar"]) == convert_paths( + self.all_paths - set(["foo/bar", "foo/bar/a.py"]) ) def test_single_and_double_wildcard(self): - assert self.exclude(['**/target/*/*']) == convert_paths( - self.all_paths - set([ - 'target/subdir/file.txt', - 'subdir/target/subdir/file.txt', - 'subdir/subdir2/target/subdir/file.txt', - ]) + assert self.exclude(["**/target/*/*"]) == convert_paths( + self.all_paths + - set( + [ + "target/subdir/file.txt", + "subdir/target/subdir/file.txt", + "subdir/subdir2/target/subdir/file.txt", + ] + ) ) def test_trailing_double_wildcard(self): - assert self.exclude(['subdir/**']) == convert_paths( - self.all_paths - set([ - 'subdir/file.txt', - 'subdir/target/file.txt', - 'subdir/target/subdir/file.txt', - 'subdir/subdir2/file.txt', - 'subdir/subdir2/target/file.txt', - 'subdir/subdir2/target/subdir/file.txt', - 'subdir/target', - 'subdir/target/subdir', - 'subdir/subdir2', - 'subdir/subdir2/target', - 'subdir/subdir2/target/subdir', - ]) + assert self.exclude(["subdir/**"]) == convert_paths( + self.all_paths + - set( + [ + "subdir/file.txt", + "subdir/target/file.txt", + "subdir/target/subdir/file.txt", + "subdir/subdir2/file.txt", + "subdir/subdir2/target/file.txt", + "subdir/subdir2/target/subdir/file.txt", + "subdir/target", + "subdir/target/subdir", + "subdir/subdir2", + "subdir/subdir2/target", + "subdir/subdir2/target/subdir", + ] + ) ) def test_double_wildcard_with_exception(self): - assert self.exclude(['**', '!bar', '!foo/bar']) == convert_paths( - set([ - 'foo/bar', 'foo/bar/a.py', 'bar', 'bar/a.py', 'Dockerfile', - '.dockerignore', - ]) + assert self.exclude(["**", "!bar", "!foo/bar"]) == convert_paths( + set( + [ + "foo/bar", + "foo/bar/a.py", + "bar", + "bar/a.py", + "Dockerfile", + ".dockerignore", + ] + ) ) def test_include_wildcard(self): # This may be surprising but it matches the CLI's behavior # (tested with 18.05.0-ce on linux) - base = make_tree(['a'], ['a/b.py']) - assert exclude_paths( - base, - ['*', '!*/b.py'] - ) == set() + base = make_tree(["a"], ["a/b.py"]) + assert exclude_paths(base, ["*", "!*/b.py"]) == set() def test_last_line_precedence(self): base = make_tree( [], - ['garbage.md', - 'trash.md', - 'README.md', - 'README-bis.md', - 'README-secret.md']) - assert exclude_paths( - base, - ['*.md', '!README*.md', 'README-secret.md'] - ) == set(['README.md', 'README-bis.md']) + [ + "garbage.md", + "trash.md", + "README.md", + "README-bis.md", + "README-secret.md", + ], + ) + assert exclude_paths(base, ["*.md", "!README*.md", "README-secret.md"]) == set( + ["README.md", "README-bis.md"] + ) def test_parent_directory(self): - base = make_tree( - [], - ['a.py', - 'b.py', - 'c.py']) + base = make_tree([], ["a.py", "b.py", "c.py"]) # Dockerignore reference stipulates that absolute paths are # equivalent to relative paths, hence /../foo should be # equivalent to ../foo. It also stipulates that paths are run # through Go's filepath.Clean, which explicitly "replace # "/.." by "/" at the beginning of a path". - assert exclude_paths( - base, - ['../a.py', '/../b.py'] - ) == set(['c.py']) + assert exclude_paths(base, ["../a.py", "/../b.py"]) == set(["c.py"]) class TarTest(unittest.TestCase): def test_tar_with_excludes(self): dirs = [ - 'foo', - 'foo/bar', - 'bar', + "foo", + "foo/bar", + "bar", ] files = [ - 'Dockerfile', - 'Dockerfile.alt', - '.dockerignore', - 'a.py', - 'a.go', - 'b.py', - 'cde.py', - 'foo/a.py', - 'foo/b.py', - 'foo/bar/a.py', - 'bar/a.py', + "Dockerfile", + "Dockerfile.alt", + ".dockerignore", + "a.py", + "a.go", + "b.py", + "cde.py", + "foo/a.py", + "foo/b.py", + "foo/bar/a.py", + "bar/a.py", ] exclude = [ - '*.py', - '!b.py', - '!a.go', - 'foo', - 'Dockerfile*', - '.dockerignore', + "*.py", + "!b.py", + "!a.go", + "foo", + "Dockerfile*", + ".dockerignore", ] - expected_names = set([ - 'Dockerfile', - '.dockerignore', - 'a.go', - 'b.py', - 'bar', - 'bar/a.py', - ]) + expected_names = set( + [ + "Dockerfile", + ".dockerignore", + "a.go", + "b.py", + "bar", + "bar/a.py", + ] + ) base = make_tree(dirs, files) self.addCleanup(shutil.rmtree, base) @@ -411,99 +426,99 @@ class TarTest(unittest.TestCase): def test_tar_with_empty_directory(self): base = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, base) - for d in ['foo', 'bar']: + for d in ["foo", "bar"]: os.makedirs(os.path.join(base, d)) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert sorted(tar_data.getnames()) == ['bar', 'foo'] + assert sorted(tar_data.getnames()) == ["bar", "foo"] @pytest.mark.skipif( IS_WINDOWS_PLATFORM or os.geteuid() == 0, - reason='root user always has access ; no chmod on Windows' + reason="root user always has access ; no chmod on Windows", ) def test_tar_with_inaccessible_file(self): base = tempfile.mkdtemp() - full_path = os.path.join(base, 'foo') + full_path = os.path.join(base, "foo") self.addCleanup(shutil.rmtree, base) - with open(full_path, 'w') as f: - f.write('content') + with open(full_path, "w") as f: + f.write("content") os.chmod(full_path, 0o222) with pytest.raises(IOError) as ei: tar(base) - assert f'Can not read file in context: {full_path}' in ei.exconly() + assert f"Can not read file in context: {full_path}" in ei.exconly() - @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows') + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows") def test_tar_with_file_symlinks(self): base = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, base) - with open(os.path.join(base, 'foo'), 'w') as f: + with open(os.path.join(base, "foo"), "w") as f: f.write("content") - os.makedirs(os.path.join(base, 'bar')) - os.symlink('../foo', os.path.join(base, 'bar/foo')) + os.makedirs(os.path.join(base, "bar")) + os.symlink("../foo", os.path.join(base, "bar/foo")) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo'] + assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"] - @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows') + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows") def test_tar_with_directory_symlinks(self): base = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, base) - for d in ['foo', 'bar']: + for d in ["foo", "bar"]: os.makedirs(os.path.join(base, d)) - os.symlink('../foo', os.path.join(base, 'bar/foo')) + os.symlink("../foo", os.path.join(base, "bar/foo")) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo'] + assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"] - @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows') + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows") def test_tar_with_broken_symlinks(self): base = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, base) - for d in ['foo', 'bar']: + for d in ["foo", "bar"]: os.makedirs(os.path.join(base, d)) - os.symlink('../baz', os.path.join(base, 'bar/foo')) + os.symlink("../baz", os.path.join(base, "bar/foo")) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo'] + assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"] - @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No UNIX sockets on Win32') + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No UNIX sockets on Win32") def test_tar_socket_file(self): base = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, base) - for d in ['foo', 'bar']: + for d in ["foo", "bar"]: os.makedirs(os.path.join(base, d)) sock = socket.socket(socket.AF_UNIX) self.addCleanup(sock.close) - sock.bind(os.path.join(base, 'test.sock')) + sock.bind(os.path.join(base, "test.sock")) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert sorted(tar_data.getnames()) == ['bar', 'foo'] + assert sorted(tar_data.getnames()) == ["bar", "foo"] def tar_test_negative_mtime_bug(self): base = tempfile.mkdtemp() - filename = os.path.join(base, 'th.txt') + filename = os.path.join(base, "th.txt") self.addCleanup(shutil.rmtree, base) - with open(filename, 'w') as f: - f.write('Invisible Full Moon') + with open(filename, "w") as f: + f.write("Invisible Full Moon") os.utime(filename, (12345, -3600.0)) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) - assert tar_data.getnames() == ['th.txt'] - assert tar_data.getmember('th.txt').mtime == -3600 + assert tar_data.getnames() == ["th.txt"] + assert tar_data.getmember("th.txt").mtime == -3600 - @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows') + @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows") def test_tar_directory_link(self): - dirs = ['a', 'b', 'a/c'] - files = ['a/hello.py', 'b/utils.py', 'a/c/descend.py'] + dirs = ["a", "b", "a/c"] + files = ["a/hello.py", "b/utils.py", "a/c/descend.py"] base = make_tree(dirs, files) self.addCleanup(shutil.rmtree, base) - os.symlink(os.path.join(base, 'b'), os.path.join(base, 'a/c/b')) + os.symlink(os.path.join(base, "b"), os.path.join(base, "a/c/b")) with tar(base) as archive: tar_data = tarfile.open(fileobj=archive) names = tar_data.getnames() for member in dirs + files: assert member in names - assert 'a/c/b' in names - assert 'a/c/b/utils.py' not in names + assert "a/c/b" in names + assert "a/c/b/utils.py" not in names diff --git a/tests/unit/plugins/module_utils/_api/utils/test_config.py b/tests/unit/plugins/module_utils/_api/utils/test_config.py index 0264cd9c..250bb478 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_config.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_config.py @@ -7,18 +7,20 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type +import json import os -import unittest import shutil import tempfile -import json - -from pytest import mark, fixture +import unittest from ansible_collections.community.docker.plugins.module_utils._api.utils import config +from pytest import fixture, mark + try: from unittest import mock @@ -33,46 +35,46 @@ class FindConfigFileTest(unittest.TestCase): self.mkdir = tmpdir.mkdir def test_find_config_fallback(self): - tmpdir = self.mkdir('test_find_config_fallback') + tmpdir = self.mkdir("test_find_config_fallback") - with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + with mock.patch.dict(os.environ, {"HOME": str(tmpdir)}): assert config.find_config_file() is None def test_find_config_from_explicit_path(self): - tmpdir = self.mkdir('test_find_config_from_explicit_path') - config_path = tmpdir.ensure('my-config-file.json') + tmpdir = self.mkdir("test_find_config_from_explicit_path") + config_path = tmpdir.ensure("my-config-file.json") assert config.find_config_file(str(config_path)) == str(config_path) def test_find_config_from_environment(self): - tmpdir = self.mkdir('test_find_config_from_environment') - config_path = tmpdir.ensure('config.json') + tmpdir = self.mkdir("test_find_config_from_environment") + config_path = tmpdir.ensure("config.json") - with mock.patch.dict(os.environ, {'DOCKER_CONFIG': str(tmpdir)}): + with mock.patch.dict(os.environ, {"DOCKER_CONFIG": str(tmpdir)}): assert config.find_config_file() == str(config_path) @mark.skipif("sys.platform == 'win32'") def test_find_config_from_home_posix(self): - tmpdir = self.mkdir('test_find_config_from_home_posix') - config_path = tmpdir.ensure('.docker', 'config.json') + tmpdir = self.mkdir("test_find_config_from_home_posix") + config_path = tmpdir.ensure(".docker", "config.json") - with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + with mock.patch.dict(os.environ, {"HOME": str(tmpdir)}): assert config.find_config_file() == str(config_path) @mark.skipif("sys.platform == 'win32'") def test_find_config_from_home_legacy_name(self): - tmpdir = self.mkdir('test_find_config_from_home_legacy_name') - config_path = tmpdir.ensure('.dockercfg') + tmpdir = self.mkdir("test_find_config_from_home_legacy_name") + config_path = tmpdir.ensure(".dockercfg") - with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}): + with mock.patch.dict(os.environ, {"HOME": str(tmpdir)}): assert config.find_config_file() == str(config_path) @mark.skipif("sys.platform != 'win32'") def test_find_config_from_home_windows(self): - tmpdir = self.mkdir('test_find_config_from_home_windows') - config_path = tmpdir.ensure('.docker', 'config.json') + tmpdir = self.mkdir("test_find_config_from_home_windows") + config_path = tmpdir.ensure(".docker", "config.json") - with mock.patch.dict(os.environ, {'USERPROFILE': str(tmpdir)}): + with mock.patch.dict(os.environ, {"USERPROFILE": str(tmpdir)}): assert config.find_config_file() == str(config_path) @@ -89,32 +91,24 @@ class LoadConfigTest(unittest.TestCase): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') + dockercfg_path = os.path.join(folder, "config.json") config_data = { - 'HttpHeaders': { - 'Name': 'Spike', - 'Surname': 'Spiegel' - }, + "HttpHeaders": {"Name": "Spike", "Surname": "Spiegel"}, } - with open(dockercfg_path, 'w') as f: + with open(dockercfg_path, "w") as f: json.dump(config_data, f) cfg = config.load_general_config(dockercfg_path) - assert 'HttpHeaders' in cfg - assert cfg['HttpHeaders'] == { - 'Name': 'Spike', - 'Surname': 'Spiegel' - } + assert "HttpHeaders" in cfg + assert cfg["HttpHeaders"] == {"Name": "Spike", "Surname": "Spiegel"} def test_load_config_detach_keys(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - config_data = { - 'detachKeys': 'ctrl-q, ctrl-u, ctrl-i' - } - with open(dockercfg_path, 'w') as f: + dockercfg_path = os.path.join(folder, "config.json") + config_data = {"detachKeys": "ctrl-q, ctrl-u, ctrl-i"} + with open(dockercfg_path, "w") as f: json.dump(config_data, f) cfg = config.load_general_config(dockercfg_path) @@ -123,13 +117,11 @@ class LoadConfigTest(unittest.TestCase): def test_load_config_from_env(self): folder = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, folder) - dockercfg_path = os.path.join(folder, 'config.json') - config_data = { - 'detachKeys': 'ctrl-q, ctrl-u, ctrl-i' - } - with open(dockercfg_path, 'w') as f: + dockercfg_path = os.path.join(folder, "config.json") + config_data = {"detachKeys": "ctrl-q, ctrl-u, ctrl-i"} + with open(dockercfg_path, "w") as f: json.dump(config_data, f) - with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): + with mock.patch.dict(os.environ, {"DOCKER_CONFIG": folder}): cfg = config.load_general_config(None) assert cfg == config_data diff --git a/tests/unit/plugins/module_utils/_api/utils/test_decorators.py b/tests/unit/plugins/module_utils/_api/utils/test_decorators.py index 16e09bb6..3ebb13ec 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_decorators.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_decorators.py @@ -7,20 +7,28 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest -from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient -from ansible_collections.community.docker.plugins.module_utils._api.utils.decorators import update_headers -from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION +from ansible_collections.community.docker.plugins.module_utils._api.api.client import ( + APIClient, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.decorators import ( + update_headers, +) +from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import ( + DEFAULT_DOCKER_API_VERSION, +) class DecoratorsTest(unittest.TestCase): def test_update_headers(self): sample_headers = { - 'X-Docker-Locale': 'en-US', + "X-Docker-Locale": "en-US", } def f(self, headers=None): @@ -32,17 +40,15 @@ class DecoratorsTest(unittest.TestCase): g = update_headers(f) assert g(client, headers=None) is None assert g(client, headers={}) == {} - assert g(client, headers={'Content-type': 'application/json'}) == { - 'Content-type': 'application/json', + assert g(client, headers={"Content-type": "application/json"}) == { + "Content-type": "application/json", } - client._general_configs = { - 'HttpHeaders': sample_headers - } + client._general_configs = {"HttpHeaders": sample_headers} assert g(client, headers=None) == sample_headers assert g(client, headers={}) == sample_headers - assert g(client, headers={'Content-type': 'application/json'}) == { - 'Content-type': 'application/json', - 'X-Docker-Locale': 'en-US', + assert g(client, headers={"Content-type": "application/json"}) == { + "Content-type": "application/json", + "X-Docker-Locale": "en-US", } diff --git a/tests/unit/plugins/module_utils/_api/utils/test_json_stream.py b/tests/unit/plugins/module_utils/_api/utils/test_json_stream.py index d6aae484..04742002 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_json_stream.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_json_stream.py @@ -7,10 +7,16 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type -from ansible_collections.community.docker.plugins.module_utils._api.utils.json_stream import json_splitter, stream_as_text, json_stream +from ansible_collections.community.docker.plugins.module_utils._api.utils.json_stream import ( + json_splitter, + json_stream, + stream_as_text, +) class TestJsonSplitter: @@ -21,24 +27,24 @@ class TestJsonSplitter: def test_json_splitter_with_object(self): data = '{"foo": "bar"}\n \n{"next": "obj"}' - assert json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}') + assert json_splitter(data) == ({"foo": "bar"}, '{"next": "obj"}') def test_json_splitter_leading_whitespace(self): data = '\n \r{"foo": "bar"}\n\n {"next": "obj"}' - assert json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}') + assert json_splitter(data) == ({"foo": "bar"}, '{"next": "obj"}') class TestStreamAsText: def test_stream_with_non_utf_unicode_character(self): - stream = [b'\xed\xf3\xf3'] - output, = stream_as_text(stream) - assert output == '���' + stream = [b"\xed\xf3\xf3"] + (output,) = stream_as_text(stream) + assert output == "���" def test_stream_with_utf_character(self): - stream = ['ěĝ'.encode('utf-8')] - output, = stream_as_text(stream) - assert output == 'ěĝ' + stream = ["ěĝ".encode("utf-8")] + (output,) = stream_as_text(stream) + assert output == "ěĝ" class TestJsonStream: @@ -50,21 +56,13 @@ class TestJsonStream: ] output = list(json_stream(stream)) assert output == [ - {'one': 'two'}, + {"one": "two"}, {}, [1, 2, 3], [], ] def test_with_leading_whitespace(self): - stream = [ - '\n \r\n {"one": "two"}{"x": 1}', - ' {"three": "four"}\t\t{"x": 2}' - ] + stream = ['\n \r\n {"one": "two"}{"x": 1}', ' {"three": "four"}\t\t{"x": 2}'] output = list(json_stream(stream)) - assert output == [ - {'one': 'two'}, - {'x': 1}, - {'three': 'four'}, - {'x': 2} - ] + assert output == [{"one": "two"}, {"x": 1}, {"three": "four"}, {"x": 2}] diff --git a/tests/unit/plugins/module_utils/_api/utils/test_ports.py b/tests/unit/plugins/module_utils/_api/utils/test_ports.py index b7f67927..8dfb8e2e 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_ports.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_ports.py @@ -7,14 +7,18 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest import pytest - -from ansible_collections.community.docker.plugins.module_utils._api.utils.ports import build_port_bindings, split_port +from ansible_collections.community.docker.plugins.module_utils._api.utils.ports import ( + build_port_bindings, + split_port, +) class PortsTest(unittest.TestCase): @@ -24,10 +28,8 @@ class PortsTest(unittest.TestCase): assert external_port == [("127.0.0.1", "1000")] def test_split_port_with_protocol(self): - for protocol in ['tcp', 'udp', 'sctp']: - internal_port, external_port = split_port( - "127.0.0.1:1000:2000/" + protocol - ) + for protocol in ["tcp", "udp", "sctp"]: + internal_port, external_port = split_port("127.0.0.1:1000:2000/" + protocol) assert internal_port == ["2000/" + protocol] assert external_port == [("127.0.0.1", "1000")] @@ -67,20 +69,17 @@ class PortsTest(unittest.TestCase): assert external_port is None def test_split_port_range_with_protocol(self): - internal_port, external_port = split_port( - "127.0.0.1:1000-1001:2000-2001/udp") + internal_port, external_port = split_port("127.0.0.1:1000-1001:2000-2001/udp") assert internal_port == ["2000/udp", "2001/udp"] assert external_port == [("127.0.0.1", "1000"), ("127.0.0.1", "1001")] def test_split_port_with_ipv6_address(self): - internal_port, external_port = split_port( - "2001:abcd:ef00::2:1000:2000") + internal_port, external_port = split_port("2001:abcd:ef00::2:1000:2000") assert internal_port == ["2000"] assert external_port == [("2001:abcd:ef00::2", "1000")] def test_split_port_with_ipv6_square_brackets_address(self): - internal_port, external_port = split_port( - "[2001:abcd:ef00::2]:1000:2000") + internal_port, external_port = split_port("[2001:abcd:ef00::2]:1000:2000") assert internal_port == ["2000"] assert external_port == [("2001:abcd:ef00::2", "1000")] @@ -117,7 +116,7 @@ class PortsTest(unittest.TestCase): split_port("") def test_split_port_non_string(self): - assert split_port(1243) == (['1243'], None) + assert split_port(1243) == (["1243"], None) def test_build_port_bindings_with_one_port(self): port_bindings = build_port_bindings(["127.0.0.1:1000:1000"]) @@ -125,14 +124,14 @@ class PortsTest(unittest.TestCase): def test_build_port_bindings_with_matching_internal_ports(self): port_bindings = build_port_bindings( - ["127.0.0.1:1000:1000", "127.0.0.1:2000:1000"]) - assert port_bindings["1000"] == [ - ("127.0.0.1", "1000"), ("127.0.0.1", "2000") - ] + ["127.0.0.1:1000:1000", "127.0.0.1:2000:1000"] + ) + assert port_bindings["1000"] == [("127.0.0.1", "1000"), ("127.0.0.1", "2000")] def test_build_port_bindings_with_nonmatching_internal_ports(self): port_bindings = build_port_bindings( - ["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"]) + ["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"] + ) assert port_bindings["1000"] == [("127.0.0.1", "1000")] assert port_bindings["2000"] == [("127.0.0.1", "2000")] @@ -143,16 +142,14 @@ class PortsTest(unittest.TestCase): def test_build_port_bindings_with_matching_internal_port_ranges(self): port_bindings = build_port_bindings( - ["127.0.0.1:1000-1001:1000-1001", "127.0.0.1:2000-2001:1000-1001"]) - assert port_bindings["1000"] == [ - ("127.0.0.1", "1000"), ("127.0.0.1", "2000") - ] - assert port_bindings["1001"] == [ - ("127.0.0.1", "1001"), ("127.0.0.1", "2001") - ] + ["127.0.0.1:1000-1001:1000-1001", "127.0.0.1:2000-2001:1000-1001"] + ) + assert port_bindings["1000"] == [("127.0.0.1", "1000"), ("127.0.0.1", "2000")] + assert port_bindings["1001"] == [("127.0.0.1", "1001"), ("127.0.0.1", "2001")] def test_build_port_bindings_with_nonmatching_internal_port_ranges(self): port_bindings = build_port_bindings( - ["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"]) + ["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"] + ) assert port_bindings["1000"] == [("127.0.0.1", "1000")] assert port_bindings["2000"] == [("127.0.0.1", "2000")] diff --git a/tests/unit/plugins/module_utils/_api/utils/test_proxy.py b/tests/unit/plugins/module_utils/_api/utils/test_proxy.py index b25674c8..b8127546 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_proxy.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_proxy.py @@ -7,40 +7,46 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import unittest -from ansible_collections.community.docker.plugins.module_utils._api.utils.proxy import ProxyConfig +from ansible_collections.community.docker.plugins.module_utils._api.utils.proxy import ( + ProxyConfig, +) -HTTP = 'http://test:80' -HTTPS = 'https://test:443' -FTP = 'ftp://user:password@host:23' -NO_PROXY = 'localhost,.localdomain' +HTTP = "http://test:80" +HTTPS = "https://test:443" +FTP = "ftp://user:password@host:23" +NO_PROXY = "localhost,.localdomain" CONFIG = ProxyConfig(http=HTTP, https=HTTPS, ftp=FTP, no_proxy=NO_PROXY) ENV = { - 'http_proxy': HTTP, - 'HTTP_PROXY': HTTP, - 'https_proxy': HTTPS, - 'HTTPS_PROXY': HTTPS, - 'ftp_proxy': FTP, - 'FTP_PROXY': FTP, - 'no_proxy': NO_PROXY, - 'NO_PROXY': NO_PROXY, + "http_proxy": HTTP, + "HTTP_PROXY": HTTP, + "https_proxy": HTTPS, + "HTTPS_PROXY": HTTPS, + "ftp_proxy": FTP, + "FTP_PROXY": FTP, + "no_proxy": NO_PROXY, + "NO_PROXY": NO_PROXY, } class ProxyConfigTest(unittest.TestCase): def test_from_dict(self): - config = ProxyConfig.from_dict({ - 'httpProxy': HTTP, - 'httpsProxy': HTTPS, - 'ftpProxy': FTP, - 'noProxy': NO_PROXY - }) + config = ProxyConfig.from_dict( + { + "httpProxy": HTTP, + "httpsProxy": HTTPS, + "ftpProxy": FTP, + "noProxy": NO_PROXY, + } + ) self.assertEqual(CONFIG.http, config.http) self.assertEqual(CONFIG.https, config.https) self.assertEqual(CONFIG.ftp, config.ftp) @@ -53,18 +59,18 @@ class ProxyConfigTest(unittest.TestCase): self.assertIsNone(config.ftp) self.assertIsNone(config.no_proxy) - config = ProxyConfig(http='a', https='b', ftp='c', no_proxy='d') - self.assertEqual(config.http, 'a') - self.assertEqual(config.https, 'b') - self.assertEqual(config.ftp, 'c') - self.assertEqual(config.no_proxy, 'd') + config = ProxyConfig(http="a", https="b", ftp="c", no_proxy="d") + self.assertEqual(config.http, "a") + self.assertEqual(config.https, "b") + self.assertEqual(config.ftp, "c") + self.assertEqual(config.no_proxy, "d") def test_truthiness(self): assert not ProxyConfig() - assert ProxyConfig(http='non-zero') - assert ProxyConfig(https='non-zero') - assert ProxyConfig(ftp='non-zero') - assert ProxyConfig(no_proxy='non-zero') + assert ProxyConfig(http="non-zero") + assert ProxyConfig(https="non-zero") + assert ProxyConfig(ftp="non-zero") + assert ProxyConfig(no_proxy="non-zero") def test_environment(self): self.assertDictEqual(CONFIG.get_environment(), ENV) @@ -75,16 +81,17 @@ class ProxyConfigTest(unittest.TestCase): # Proxy config is non null, env is None. self.assertSetEqual( set(CONFIG.inject_proxy_environment(None)), - set(f'{k}={v}' for k, v in ENV.items())) + set(f"{k}={v}" for k, v in ENV.items()), + ) # Proxy config is null, env is None. self.assertIsNone(ProxyConfig().inject_proxy_environment(None), None) - env = ['FOO=BAR', 'BAR=BAZ'] + env = ["FOO=BAR", "BAR=BAZ"] # Proxy config is non null, env is non null actual = CONFIG.inject_proxy_environment(env) - expected = [f'{k}={v}' for k, v in ENV.items()] + env + expected = [f"{k}={v}" for k, v in ENV.items()] + env # It's important that the first 8 variables are the ones from the proxy # config, and the last 2 are the ones from the input environment self.assertSetEqual(set(actual[:8]), set(expected[:8])) diff --git a/tests/unit/plugins/module_utils/_api/utils/test_utils.py b/tests/unit/plugins/module_utils/_api/utils/test_utils.py index 905bf52c..902557e9 100644 --- a/tests/unit/plugins/module_utils/_api/utils/test_utils.py +++ b/tests/unit/plugins/module_utils/_api/utils/test_utils.py @@ -7,7 +7,9 @@ # It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection) # SPDX-License-Identifier: Apache-2.0 -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import base64 @@ -19,22 +21,36 @@ import tempfile import unittest import pytest - -from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient -from ansible_collections.community.docker.plugins.module_utils._api.constants import IS_WINDOWS_PLATFORM -from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException -from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( - convert_filters, convert_volume_binds, - decode_json_header, kwargs_from_env, parse_bytes, - parse_devices, parse_env_file, parse_host, - parse_repository_tag, split_command, format_environment, +from ansible_collections.community.docker.plugins.module_utils._api.api.client import ( + APIClient, +) +from ansible_collections.community.docker.plugins.module_utils._api.constants import ( + IS_WINDOWS_PLATFORM, +) +from ansible_collections.community.docker.plugins.module_utils._api.errors import ( + DockerException, +) +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + convert_filters, + convert_volume_binds, + decode_json_header, + format_environment, + kwargs_from_env, + parse_bytes, + parse_devices, + parse_env_file, + parse_host, + parse_repository_tag, + split_command, +) +from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import ( + DEFAULT_DOCKER_API_VERSION, ) -from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION TEST_CERT_DIR = os.path.join( os.path.dirname(__file__), - 'testdata/certs', + "testdata/certs", ) @@ -46,84 +62,85 @@ class KwargsFromEnvTest(unittest.TestCase): os.environ = self.os_environ def test_kwargs_from_env_empty(self): - os.environ.update(DOCKER_HOST='', - DOCKER_CERT_PATH='') - os.environ.pop('DOCKER_TLS_VERIFY', None) + os.environ.update(DOCKER_HOST="", DOCKER_CERT_PATH="") + os.environ.pop("DOCKER_TLS_VERIFY", None) kwargs = kwargs_from_env() - assert kwargs.get('base_url') is None - assert kwargs.get('tls') is None + assert kwargs.get("base_url") is None + assert kwargs.get("tls") is None def test_kwargs_from_env_tls(self): - os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376', - DOCKER_CERT_PATH=TEST_CERT_DIR, - DOCKER_TLS_VERIFY='1') + os.environ.update( + DOCKER_HOST="tcp://192.168.59.103:2376", + DOCKER_CERT_PATH=TEST_CERT_DIR, + DOCKER_TLS_VERIFY="1", + ) kwargs = kwargs_from_env(assert_hostname=False) - assert 'tcp://192.168.59.103:2376' == kwargs['base_url'] - assert 'ca.pem' in kwargs['tls'].ca_cert - assert 'cert.pem' in kwargs['tls'].cert[0] - assert 'key.pem' in kwargs['tls'].cert[1] - assert kwargs['tls'].assert_hostname is False - assert kwargs['tls'].verify + assert "tcp://192.168.59.103:2376" == kwargs["base_url"] + assert "ca.pem" in kwargs["tls"].ca_cert + assert "cert.pem" in kwargs["tls"].cert[0] + assert "key.pem" in kwargs["tls"].cert[1] + assert kwargs["tls"].assert_hostname is False + assert kwargs["tls"].verify - parsed_host = parse_host(kwargs['base_url'], IS_WINDOWS_PLATFORM, True) - kwargs['version'] = DEFAULT_DOCKER_API_VERSION + parsed_host = parse_host(kwargs["base_url"], IS_WINDOWS_PLATFORM, True) + kwargs["version"] = DEFAULT_DOCKER_API_VERSION try: client = APIClient(**kwargs) assert parsed_host == client.base_url - assert kwargs['tls'].ca_cert == client.verify - assert kwargs['tls'].cert == client.cert + assert kwargs["tls"].ca_cert == client.verify + assert kwargs["tls"].cert == client.cert except TypeError as e: self.fail(e) def test_kwargs_from_env_tls_verify_false(self): - os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376', - DOCKER_CERT_PATH=TEST_CERT_DIR, - DOCKER_TLS_VERIFY='') + os.environ.update( + DOCKER_HOST="tcp://192.168.59.103:2376", + DOCKER_CERT_PATH=TEST_CERT_DIR, + DOCKER_TLS_VERIFY="", + ) kwargs = kwargs_from_env(assert_hostname=True) - assert 'tcp://192.168.59.103:2376' == kwargs['base_url'] - assert 'ca.pem' in kwargs['tls'].ca_cert - assert 'cert.pem' in kwargs['tls'].cert[0] - assert 'key.pem' in kwargs['tls'].cert[1] - assert kwargs['tls'].assert_hostname is True - assert kwargs['tls'].verify is False - parsed_host = parse_host(kwargs['base_url'], IS_WINDOWS_PLATFORM, True) - kwargs['version'] = DEFAULT_DOCKER_API_VERSION + assert "tcp://192.168.59.103:2376" == kwargs["base_url"] + assert "ca.pem" in kwargs["tls"].ca_cert + assert "cert.pem" in kwargs["tls"].cert[0] + assert "key.pem" in kwargs["tls"].cert[1] + assert kwargs["tls"].assert_hostname is True + assert kwargs["tls"].verify is False + parsed_host = parse_host(kwargs["base_url"], IS_WINDOWS_PLATFORM, True) + kwargs["version"] = DEFAULT_DOCKER_API_VERSION try: client = APIClient(**kwargs) assert parsed_host == client.base_url - assert kwargs['tls'].cert == client.cert - assert not kwargs['tls'].verify + assert kwargs["tls"].cert == client.cert + assert not kwargs["tls"].verify except TypeError as e: self.fail(e) def test_kwargs_from_env_tls_verify_false_no_cert(self): temp_dir = tempfile.mkdtemp() - cert_dir = os.path.join(temp_dir, '.docker') + cert_dir = os.path.join(temp_dir, ".docker") shutil.copytree(TEST_CERT_DIR, cert_dir) - os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376', - HOME=temp_dir, - DOCKER_TLS_VERIFY='') - os.environ.pop('DOCKER_CERT_PATH', None) + os.environ.update( + DOCKER_HOST="tcp://192.168.59.103:2376", HOME=temp_dir, DOCKER_TLS_VERIFY="" + ) + os.environ.pop("DOCKER_CERT_PATH", None) kwargs = kwargs_from_env(assert_hostname=True) - assert 'tcp://192.168.59.103:2376' == kwargs['base_url'] + assert "tcp://192.168.59.103:2376" == kwargs["base_url"] def test_kwargs_from_env_no_cert_path(self): try: temp_dir = tempfile.mkdtemp() - cert_dir = os.path.join(temp_dir, '.docker') + cert_dir = os.path.join(temp_dir, ".docker") shutil.copytree(TEST_CERT_DIR, cert_dir) - os.environ.update(HOME=temp_dir, - DOCKER_CERT_PATH='', - DOCKER_TLS_VERIFY='1') + os.environ.update(HOME=temp_dir, DOCKER_CERT_PATH="", DOCKER_TLS_VERIFY="1") kwargs = kwargs_from_env() - assert kwargs['tls'].verify - assert cert_dir in kwargs['tls'].ca_cert - assert cert_dir in kwargs['tls'].cert[0] - assert cert_dir in kwargs['tls'].cert[1] + assert kwargs["tls"].verify + assert cert_dir in kwargs["tls"].ca_cert + assert cert_dir in kwargs["tls"].cert[0] + assert cert_dir in kwargs["tls"].cert[1] finally: if temp_dir: shutil.rmtree(temp_dir) @@ -132,15 +149,17 @@ class KwargsFromEnvTest(unittest.TestCase): # Values in os.environ are entirely ignored if an alternate is # provided os.environ.update( - DOCKER_HOST='tcp://192.168.59.103:2376', + DOCKER_HOST="tcp://192.168.59.103:2376", DOCKER_CERT_PATH=TEST_CERT_DIR, - DOCKER_TLS_VERIFY='' + DOCKER_TLS_VERIFY="", ) - kwargs = kwargs_from_env(environment={ - 'DOCKER_HOST': 'http://docker.gensokyo.jp:2581', - }) - assert 'http://docker.gensokyo.jp:2581' == kwargs['base_url'] - assert 'tls' not in kwargs + kwargs = kwargs_from_env( + environment={ + "DOCKER_HOST": "http://docker.gensokyo.jp:2581", + } + ) + assert "http://docker.gensokyo.jp:2581" == kwargs["base_url"] + assert "tls" not in kwargs class ConverVolumeBindsTest(unittest.TestCase): @@ -149,52 +168,36 @@ class ConverVolumeBindsTest(unittest.TestCase): assert convert_volume_binds([]) == [] def test_convert_volume_binds_list(self): - data = ['/a:/a:ro', '/b:/c:z'] + data = ["/a:/a:ro", "/b:/c:z"] assert convert_volume_binds(data) == data def test_convert_volume_binds_complete(self): - data = { - '/mnt/vol1': { - 'bind': '/data', - 'mode': 'ro' - } - } - assert convert_volume_binds(data) == ['/mnt/vol1:/data:ro'] + data = {"/mnt/vol1": {"bind": "/data", "mode": "ro"}} + assert convert_volume_binds(data) == ["/mnt/vol1:/data:ro"] def test_convert_volume_binds_compact(self): - data = { - '/mnt/vol1': '/data' - } - assert convert_volume_binds(data) == ['/mnt/vol1:/data:rw'] + data = {"/mnt/vol1": "/data"} + assert convert_volume_binds(data) == ["/mnt/vol1:/data:rw"] def test_convert_volume_binds_no_mode(self): - data = { - '/mnt/vol1': { - 'bind': '/data' - } - } - assert convert_volume_binds(data) == ['/mnt/vol1:/data:rw'] + data = {"/mnt/vol1": {"bind": "/data"}} + assert convert_volume_binds(data) == ["/mnt/vol1:/data:rw"] def test_convert_volume_binds_unicode_bytes_input(self): - expected = ['/mnt/지연:/unicode/박:rw'] + expected = ["/mnt/지연:/unicode/박:rw"] data = { - '/mnt/지연'.encode('utf-8'): { - 'bind': '/unicode/박'.encode('utf-8'), - 'mode': 'rw' + "/mnt/지연".encode("utf-8"): { + "bind": "/unicode/박".encode("utf-8"), + "mode": "rw", } } assert convert_volume_binds(data) == expected def test_convert_volume_binds_unicode_unicode_input(self): - expected = ['/mnt/지연:/unicode/박:rw'] + expected = ["/mnt/지연:/unicode/박:rw"] - data = { - '/mnt/지연': { - 'bind': '/unicode/박', - 'mode': 'rw' - } - } + data = {"/mnt/지연": {"bind": "/unicode/박", "mode": "rw"}} assert convert_volume_binds(data) == expected @@ -206,41 +209,36 @@ class ParseEnvFileTest(unittest.TestCase): Don't forget to unlink the file with os.unlink() after. """ local_tempfile = tempfile.NamedTemporaryFile(delete=False) - local_tempfile.write(file_content.encode('UTF-8')) + local_tempfile.write(file_content.encode("UTF-8")) local_tempfile.close() return local_tempfile.name def test_parse_env_file_proper(self): - env_file = self.generate_tempfile( - file_content='USER=jdoe\nPASS=secret') + env_file = self.generate_tempfile(file_content="USER=jdoe\nPASS=secret") get_parse_env_file = parse_env_file(env_file) - assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'secret'} + assert get_parse_env_file == {"USER": "jdoe", "PASS": "secret"} os.unlink(env_file) def test_parse_env_file_with_equals_character(self): - env_file = self.generate_tempfile( - file_content='USER=jdoe\nPASS=sec==ret') + env_file = self.generate_tempfile(file_content="USER=jdoe\nPASS=sec==ret") get_parse_env_file = parse_env_file(env_file) - assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'sec==ret'} + assert get_parse_env_file == {"USER": "jdoe", "PASS": "sec==ret"} os.unlink(env_file) def test_parse_env_file_commented_line(self): - env_file = self.generate_tempfile( - file_content='USER=jdoe\n#PASS=secret') + env_file = self.generate_tempfile(file_content="USER=jdoe\n#PASS=secret") get_parse_env_file = parse_env_file(env_file) - assert get_parse_env_file == {'USER': 'jdoe'} + assert get_parse_env_file == {"USER": "jdoe"} os.unlink(env_file) def test_parse_env_file_newline(self): - env_file = self.generate_tempfile( - file_content='\nUSER=jdoe\n\n\nPASS=secret') + env_file = self.generate_tempfile(file_content="\nUSER=jdoe\n\n\nPASS=secret") get_parse_env_file = parse_env_file(env_file) - assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'secret'} + assert get_parse_env_file == {"USER": "jdoe", "PASS": "secret"} os.unlink(env_file) def test_parse_env_file_invalid_line(self): - env_file = self.generate_tempfile( - file_content='USER jdoe') + env_file = self.generate_tempfile(file_content="USER jdoe") with pytest.raises(DockerException): parse_env_file(env_file) os.unlink(env_file) @@ -249,46 +247,44 @@ class ParseEnvFileTest(unittest.TestCase): class ParseHostTest(unittest.TestCase): def test_parse_host(self): invalid_hosts = [ - 'foo://0.0.0.0', - 'tcp://', - 'udp://127.0.0.1', - 'udp://127.0.0.1:2375', - 'ssh://:22/path', - 'tcp://netloc:3333/path?q=1', - 'unix:///sock/path#fragment', - 'https://netloc:3333/path;params', - 'ssh://:clearpassword@host:22', + "foo://0.0.0.0", + "tcp://", + "udp://127.0.0.1", + "udp://127.0.0.1:2375", + "ssh://:22/path", + "tcp://netloc:3333/path?q=1", + "unix:///sock/path#fragment", + "https://netloc:3333/path;params", + "ssh://:clearpassword@host:22", ] valid_hosts = { - '0.0.0.1:5555': 'http://0.0.0.1:5555', - ':6666': 'http://127.0.0.1:6666', - 'tcp://:7777': 'http://127.0.0.1:7777', - 'http://:7777': 'http://127.0.0.1:7777', - 'https://kokia.jp:2375': 'https://kokia.jp:2375', - 'unix:///var/run/docker.sock': 'http+unix:///var/run/docker.sock', - 'unix://': 'http+unix:///var/run/docker.sock', - '12.234.45.127:2375/docker/engine': ( - 'http://12.234.45.127:2375/docker/engine' + "0.0.0.1:5555": "http://0.0.0.1:5555", + ":6666": "http://127.0.0.1:6666", + "tcp://:7777": "http://127.0.0.1:7777", + "http://:7777": "http://127.0.0.1:7777", + "https://kokia.jp:2375": "https://kokia.jp:2375", + "unix:///var/run/docker.sock": "http+unix:///var/run/docker.sock", + "unix://": "http+unix:///var/run/docker.sock", + "12.234.45.127:2375/docker/engine": ( + "http://12.234.45.127:2375/docker/engine" ), - 'somehost.net:80/service/swarm': ( - 'http://somehost.net:80/service/swarm' + "somehost.net:80/service/swarm": ("http://somehost.net:80/service/swarm"), + "npipe:////./pipe/docker_engine": "npipe:////./pipe/docker_engine", + "[fd12::82d1]:2375": "http://[fd12::82d1]:2375", + "https://[fd12:5672::12aa]:1090": "https://[fd12:5672::12aa]:1090", + "[fd12::82d1]:2375/docker/engine": ( + "http://[fd12::82d1]:2375/docker/engine" ), - 'npipe:////./pipe/docker_engine': 'npipe:////./pipe/docker_engine', - '[fd12::82d1]:2375': 'http://[fd12::82d1]:2375', - 'https://[fd12:5672::12aa]:1090': 'https://[fd12:5672::12aa]:1090', - '[fd12::82d1]:2375/docker/engine': ( - 'http://[fd12::82d1]:2375/docker/engine' - ), - 'ssh://[fd12::82d1]': 'ssh://[fd12::82d1]:22', - 'ssh://user@[fd12::82d1]:8765': 'ssh://user@[fd12::82d1]:8765', - 'ssh://': 'ssh://127.0.0.1:22', - 'ssh://user@localhost:22': 'ssh://user@localhost:22', - 'ssh://user@remote': 'ssh://user@remote:22', + "ssh://[fd12::82d1]": "ssh://[fd12::82d1]:22", + "ssh://user@[fd12::82d1]:8765": "ssh://user@[fd12::82d1]:8765", + "ssh://": "ssh://127.0.0.1:22", + "ssh://user@localhost:22": "ssh://user@localhost:22", + "ssh://user@remote": "ssh://user@remote:22", } for host in invalid_hosts: - msg = f'Should have failed to parse invalid host: {host}' + msg = f"Should have failed to parse invalid host: {host}" with self.assertRaises(DockerException, msg=msg): parse_host(host, None) @@ -296,35 +292,35 @@ class ParseHostTest(unittest.TestCase): self.assertEqual( parse_host(host, None), expected, - msg=f'Failed to parse valid host: {host}', + msg=f"Failed to parse valid host: {host}", ) def test_parse_host_empty_value(self): - unix_socket = 'http+unix:///var/run/docker.sock' - npipe = 'npipe:////./pipe/docker_engine' + unix_socket = "http+unix:///var/run/docker.sock" + npipe = "npipe:////./pipe/docker_engine" - for val in [None, '']: + for val in [None, ""]: assert parse_host(val, is_win32=False) == unix_socket assert parse_host(val, is_win32=True) == npipe def test_parse_host_tls(self): - host_value = 'myhost.docker.net:3348' - expected_result = 'https://myhost.docker.net:3348' + host_value = "myhost.docker.net:3348" + expected_result = "https://myhost.docker.net:3348" assert parse_host(host_value, tls=True) == expected_result def test_parse_host_tls_tcp_proto(self): - host_value = 'tcp://myhost.docker.net:3348' - expected_result = 'https://myhost.docker.net:3348' + host_value = "tcp://myhost.docker.net:3348" + expected_result = "https://myhost.docker.net:3348" assert parse_host(host_value, tls=True) == expected_result def test_parse_host_trailing_slash(self): - host_value = 'tcp://myhost.docker.net:2376/' - expected_result = 'http://myhost.docker.net:2376' + host_value = "tcp://myhost.docker.net:2376/" + expected_result = "http://myhost.docker.net:2376" assert parse_host(host_value) == expected_result class ParseRepositoryTagTest(unittest.TestCase): - sha = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + sha = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" def test_index_image_no_tag(self): assert parse_repository_tag("root") == ("root", None) @@ -342,77 +338,83 @@ class ParseRepositoryTagTest(unittest.TestCase): assert parse_repository_tag("url:5000/repo") == ("url:5000/repo", None) def test_private_reg_image_tag(self): - assert parse_repository_tag("url:5000/repo:tag") == ( - "url:5000/repo", "tag" - ) + assert parse_repository_tag("url:5000/repo:tag") == ("url:5000/repo", "tag") def test_index_image_sha(self): assert parse_repository_tag(f"root@sha256:{self.sha}") == ( - "root", f"sha256:{self.sha}" + "root", + f"sha256:{self.sha}", ) def test_private_reg_image_sha(self): - assert parse_repository_tag( - f"url:5000/repo@sha256:{self.sha}" - ) == ("url:5000/repo", f"sha256:{self.sha}") + assert parse_repository_tag(f"url:5000/repo@sha256:{self.sha}") == ( + "url:5000/repo", + f"sha256:{self.sha}", + ) class ParseDeviceTest(unittest.TestCase): def test_dict(self): - devices = parse_devices([{ - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/mnt1', - 'CgroupPermissions': 'r' - }]) + devices = parse_devices( + [ + { + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/mnt1", + "CgroupPermissions": "r", + } + ] + ) assert devices[0] == { - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/mnt1', - 'CgroupPermissions': 'r' + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/mnt1", + "CgroupPermissions": "r", } def test_partial_string_definition(self): - devices = parse_devices(['/dev/sda1']) + devices = parse_devices(["/dev/sda1"]) assert devices[0] == { - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/sda1', - 'CgroupPermissions': 'rwm' + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/sda1", + "CgroupPermissions": "rwm", } def test_permissionless_string_definition(self): - devices = parse_devices(['/dev/sda1:/dev/mnt1']) + devices = parse_devices(["/dev/sda1:/dev/mnt1"]) assert devices[0] == { - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/mnt1', - 'CgroupPermissions': 'rwm' + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/mnt1", + "CgroupPermissions": "rwm", } def test_full_string_definition(self): - devices = parse_devices(['/dev/sda1:/dev/mnt1:r']) + devices = parse_devices(["/dev/sda1:/dev/mnt1:r"]) assert devices[0] == { - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/mnt1', - 'CgroupPermissions': 'r' + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/mnt1", + "CgroupPermissions": "r", } def test_hybrid_list(self): - devices = parse_devices([ - '/dev/sda1:/dev/mnt1:rw', - { - 'PathOnHost': '/dev/sda2', - 'PathInContainer': '/dev/mnt2', - 'CgroupPermissions': 'r' - } - ]) + devices = parse_devices( + [ + "/dev/sda1:/dev/mnt1:rw", + { + "PathOnHost": "/dev/sda2", + "PathInContainer": "/dev/mnt2", + "CgroupPermissions": "r", + }, + ] + ) assert devices[0] == { - 'PathOnHost': '/dev/sda1', - 'PathInContainer': '/dev/mnt1', - 'CgroupPermissions': 'rw' + "PathOnHost": "/dev/sda1", + "PathInContainer": "/dev/mnt1", + "CgroupPermissions": "rw", } assert devices[1] == { - 'PathOnHost': '/dev/sda2', - 'PathInContainer': '/dev/mnt2', - 'CgroupPermissions': 'r' + "PathOnHost": "/dev/sda2", + "PathInContainer": "/dev/mnt2", + "CgroupPermissions": "r", } @@ -439,37 +441,35 @@ class UtilsTest(unittest.TestCase): def test_convert_filters(self): tests = [ - ({'dangling': True}, '{"dangling": ["true"]}'), - ({'dangling': "true"}, '{"dangling": ["true"]}'), - ({'exited': 0}, '{"exited": ["0"]}'), - ({'exited': [0, 1]}, '{"exited": ["0", "1"]}'), + ({"dangling": True}, '{"dangling": ["true"]}'), + ({"dangling": "true"}, '{"dangling": ["true"]}'), + ({"exited": 0}, '{"exited": ["0"]}'), + ({"exited": [0, 1]}, '{"exited": ["0", "1"]}'), ] for filters, expected in tests: assert convert_filters(filters) == expected def test_decode_json_header(self): - obj = {'a': 'b', 'c': 1} - data = base64.urlsafe_b64encode(bytes(json.dumps(obj), 'utf-8')) + obj = {"a": "b", "c": 1} + data = base64.urlsafe_b64encode(bytes(json.dumps(obj), "utf-8")) decoded_data = decode_json_header(data) assert obj == decoded_data class SplitCommandTest(unittest.TestCase): def test_split_command_with_unicode(self): - assert split_command('echo μμ') == ['echo', 'μμ'] + assert split_command("echo μμ") == ["echo", "μμ"] class FormatEnvironmentTest(unittest.TestCase): def test_format_env_binary_unicode_value(self): - env_dict = { - 'ARTIST_NAME': b'\xec\x86\xa1\xec\xa7\x80\xec\x9d\x80' - } - assert format_environment(env_dict) == ['ARTIST_NAME=송지은'] + env_dict = {"ARTIST_NAME": b"\xec\x86\xa1\xec\xa7\x80\xec\x9d\x80"} + assert format_environment(env_dict) == ["ARTIST_NAME=송지은"] def test_format_env_no_value(self): env_dict = { - 'FOO': None, - 'BAR': '', + "FOO": None, + "BAR": "", } - assert sorted(format_environment(env_dict)) == ['BAR=', 'FOO'] + assert sorted(format_environment(env_dict)) == ["BAR=", "FOO"] diff --git a/tests/unit/plugins/module_utils/compose_v2_test_cases.py b/tests/unit/plugins/module_utils/compose_v2_test_cases.py index 4745fe34..f7eed495 100644 --- a/tests/unit/plugins/module_utils/compose_v2_test_cases.py +++ b/tests/unit/plugins/module_utils/compose_v2_test_cases.py @@ -2,7 +2,9 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( @@ -19,51 +21,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-absent', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-absent", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' Network ansible-docker-test-01234567-start-stop_default Removed\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " Network ansible-docker-test-01234567-start-stop_default Removing\n" + " Network ansible-docker-test-01234567-start-stop_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removed', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removed", None, ), ], @@ -72,52 +74,52 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Absent (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Absent (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-absent-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-absent-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', + "network", + "ansible-docker-test-01234567-start-stop_default", None, - 'Resource is still in use', + "Resource is still in use", ), ], [], @@ -127,51 +129,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-cleanup', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-cleanup", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n' - ' Network ansible-docker-test-01234567-pull_default Removing\n' - ' Network ansible-docker-test-01234567-pull_default Removed\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n" + " Network ansible-docker-test-01234567-pull_default Removing\n" + " Network ansible-docker-test-01234567-pull_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removing', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removed', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removing', + "network", + "ansible-docker-test-01234567-pull_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removed', + "network", + "ansible-docker-test-01234567-pull_default", + "Removed", None, ), ], @@ -180,65 +182,65 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present", + "2.18.1", False, False, - ' ansible-docker-test-01234567-container Pulling \n' - ' ansible-docker-test-01234567-container Pulled \n' - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " ansible-docker-test-01234567-container Pulling \n" + " ansible-docker-test-01234567-container Pulled \n" + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulling', + "service", + "ansible-docker-test-01234567-container", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulled', + "service", + "ansible-docker-test-01234567-container", + "Pulled", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -246,37 +248,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "80c52c26780_ansible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -284,37 +286,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "9121995872d_ansible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -323,37 +325,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present (changed)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present (changed)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(changed)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(changed)", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -361,65 +363,65 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-container Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-container Pulled \n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-container Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-container Pulled \n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulling', + "service", + "ansible-docker-test-01234567-container", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulled', + "service", + "ansible-docker-test-01234567-container", + "Pulled", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-7c5458ac-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -427,65 +429,65 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-container Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-container Pulled \n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-container Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-container Pulled \n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulling', + "service", + "ansible-docker-test-01234567-container", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-container', - 'Pulled', + "service", + "ansible-docker-test-01234567-container", + "Pulled", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-dba91fb6-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -496,16 +498,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(idempotent-check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(idempotent-check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -516,16 +518,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(idempotent)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-(idempotent)", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -534,37 +536,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present stopped" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present stopped" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-stopped', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-stopped", + "2.18.1", False, False, - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -573,37 +575,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present stopped (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present stopped (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-stopped-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-stopped-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -612,30 +614,30 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=always" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=always" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=always', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=always", + "2.18.1", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -643,51 +645,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "002a15404ac_ansible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -695,51 +697,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ec060c7b341_ansible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -748,51 +750,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=missing" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=missing" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing", + "2.18.1", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -800,51 +802,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -852,51 +854,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -907,16 +909,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -927,16 +929,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -945,51 +947,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.18.1", False, False, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -998,38 +1000,38 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.18.1", False, True, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - 'Error response from daemon: No such image: does-not-exist:latest\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + "Error response from daemon: No such image: does-not-exist:latest\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'unknown', - '', - 'Error', - 'Error response from daemon: No such image: does-not-exist:latest', + "unknown", + "", + "Error", + "Error response from daemon: No such image: does-not-exist:latest", ), ], [], @@ -1037,51 +1039,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-7c5458ac-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -1090,51 +1092,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-dba91fb6-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -1144,24 +1146,24 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present without explicit pull" on 2024-01-13 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2_pull: "Pull" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-without-explicit-pull', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-without-explicit-pull", + "2.18.1", False, True, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Error \n' + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Error \n" "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied\n", # noqa: E501 [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied", # noqa: E501 ), ], @@ -1170,25 +1172,25 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present without explicit pull (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Present without explicit pull (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)", + "2.18.1", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], @@ -1198,23 +1200,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-restarted', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-restarted", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1225,23 +1227,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-restarted-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-restarted-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1250,23 +1252,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Started" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Started" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-started', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-started", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1275,23 +1277,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Started (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Started (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-started-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-started-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1300,31 +1302,31 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Stopped" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Stopped" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-stopped', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-stopped", + "2.18.1", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -1333,31 +1335,31 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Stopped (check)" on 2024-01-07 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2: "Stopped (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-stopped-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-07-docker_compose_v2-stopped-(check)", + "2.18.1", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -1365,98 +1367,98 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-(check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-(check)", + "2.18.1", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.18.1", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -1464,23 +1466,23 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always (again)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again)", + "2.18.1", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -1489,23 +1491,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2_pull: "Pull with policy=always (again, check)" on 2024-01-13 in 2.12-ubuntu1804 # Duplicated in: docker_compose_v2_pull: "Pull with policy=always (check)" on 2024-01-13 in 2.12-ubuntu1804 ( - '2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again,-check)', - '2.18.1', + "2.18.1-2.12-ubuntu1804-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again,-check)", + "2.18.1", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -1559,51 +1561,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-absent', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-absent", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' Network ansible-docker-test-01234567-start-stop_default Removed\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " Network ansible-docker-test-01234567-start-stop_default Removing\n" + " Network ansible-docker-test-01234567-start-stop_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removed', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removed", None, ), ], @@ -1632,52 +1634,52 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Absent (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Absent (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-absent-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-absent-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', + "network", + "ansible-docker-test-01234567-start-stop_default", None, - 'Resource is still in use', + "Resource is still in use", ), ], [], @@ -1727,51 +1729,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-cleanup', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-cleanup", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n' - ' Network ansible-docker-test-01234567-pull_default Removing\n' - ' Network ansible-docker-test-01234567-pull_default Removed\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n" + " Network ansible-docker-test-01234567-pull_default Removing\n" + " Network ansible-docker-test-01234567-pull_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removing', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removed', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removing', + "network", + "ansible-docker-test-01234567-pull_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removed', + "network", + "ansible-docker-test-01234567-pull_default", + "Removed", None, ), ], @@ -1800,51 +1802,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present", + "2.21.0", False, False, - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1852,37 +1854,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "054e03eb5ea_ansible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1890,37 +1892,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "0a77f424a61_ansible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1928,37 +1930,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "0e165a36533_ansible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -1966,37 +1968,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "26bf8ff1675_ansible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2004,37 +2006,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "27c209a84a5_ansible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2042,37 +2044,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "4b568108657_ansible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2080,37 +2082,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "6dc8d091c94_ansible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2118,37 +2120,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "71b893893dc_ansible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2156,37 +2158,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "71e7a319c23_ansible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2194,37 +2196,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "78e827e6673_ansible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2232,37 +2234,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "7971b0c189d_ansible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2270,37 +2272,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "7b8d91093c9_ansible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2308,37 +2310,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "7bedd9b4513_ansible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2346,37 +2348,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container 87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container 87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container 87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container 87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - '87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - '87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "87873e68934_ansible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2384,37 +2386,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "a81dce9774b_ansible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2422,37 +2424,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'd28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'd28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "d28b7978587_ansible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2460,37 +2462,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "dea4aafe907_ansible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2498,37 +2500,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "e508faa8323_ansible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2536,37 +2538,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ef57cb7913f_ansible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2574,37 +2576,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "efe8857a191_ansible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2612,37 +2614,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "f0de40ba686_ansible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2650,37 +2652,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "f6416652e13_ansible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2709,37 +2711,37 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present (changed)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present (changed)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(changed)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(changed)", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2747,51 +2749,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-19ffba88-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2799,51 +2801,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-1ba2643a-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2851,51 +2853,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-1f1d0d58-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2903,51 +2905,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-2460e737-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -2955,51 +2957,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-4baa7139-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3007,51 +3009,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-4fcbaf1e-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3059,51 +3061,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-51914faa-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3111,51 +3113,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-5f3d2e16-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3163,51 +3165,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-601188b1-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3215,51 +3217,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-64d917f4-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3267,51 +3269,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-6aaaa304-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3319,51 +3321,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-834c1a9b-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3371,51 +3373,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-971ad57c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3423,51 +3425,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-ad622acd-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3475,51 +3477,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-b2745d99-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3527,51 +3529,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-ce1fa4d7-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3579,51 +3581,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-d1d30700-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3631,51 +3633,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-d2caf0c9-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3683,51 +3685,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-d616c3a5-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3735,51 +3737,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-d6ae094c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3787,51 +3789,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-e700ac20-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3839,51 +3841,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-ede01681-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -3934,16 +3936,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(idempotent-check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(idempotent-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -3994,16 +3996,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(idempotent)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-(idempotent)", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -4032,37 +4034,37 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present stopped" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present stopped" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-stopped', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-stopped", + "2.21.0", False, False, - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -4091,37 +4093,37 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present stopped (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present stopped (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-stopped-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-stopped-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -4150,30 +4152,30 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=always" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=always" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=always', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -4181,51 +4183,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "0b4286904e0_ansible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4233,51 +4235,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "0d5362bac93_ansible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4285,51 +4287,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "3d7b7be6dbe_ansible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4337,51 +4339,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "49ff7fef052_ansible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4389,51 +4391,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "5d30320650e_ansible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4441,51 +4443,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "611a044106b_ansible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4493,51 +4495,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "61802a08aa6_ansible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4545,51 +4547,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "61bd1b13d9c_ansible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4597,51 +4599,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "769aec9cd4d_ansible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4649,51 +4651,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "813ca227a6f_ansible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4701,51 +4703,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "8a586ed91d6_ansible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4753,51 +4755,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "8bec416e98d_ansible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4805,51 +4807,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "906450ba6e7_ansible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4857,51 +4859,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "a99ed30c7d6_ansible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4909,51 +4911,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "b78faf8a742_ansible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -4961,51 +4963,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "be1b2a9ca28_ansible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5013,51 +5015,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "c9d730c2613_ansible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5065,51 +5067,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "da16aa68f6f_ansible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5117,51 +5119,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "dff4b309c58_ansible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5169,51 +5171,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ecd243ea972_ansible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5221,51 +5223,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "f48d54a75fb_ansible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5273,51 +5275,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "fa8f62dfced_ansible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5346,51 +5348,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=missing" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=missing" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5398,51 +5400,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5450,51 +5452,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5502,51 +5504,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5554,51 +5556,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5606,51 +5608,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5658,51 +5660,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5710,51 +5712,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5762,51 +5764,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5814,51 +5816,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5866,51 +5868,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5918,51 +5920,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -5970,51 +5972,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6022,51 +6024,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6074,51 +6076,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6126,51 +6128,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6178,51 +6180,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6230,51 +6232,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6282,51 +6284,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6334,51 +6336,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6386,51 +6388,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6438,51 +6440,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6490,51 +6492,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6585,16 +6587,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -6645,16 +6647,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -6683,51 +6685,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.21.0", False, False, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6744,38 +6746,38 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-07 in devel-rhel9.3 # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.21.0", False, True, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - 'Error response from daemon: No such image: does-not-exist:latest\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + "Error response from daemon: No such image: does-not-exist:latest\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'unknown', - '', - 'Error', - 'Error response from daemon: No such image: does-not-exist:latest', + "unknown", + "", + "Error", + "Error response from daemon: No such image: does-not-exist:latest", ), ], [], @@ -6793,38 +6795,38 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.21.0", False, True, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - 'Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + "Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'unknown', - '', - 'Error', - 'Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest', + "unknown", + "", + "Error", + "Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest", ), ], [], @@ -6832,51 +6834,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-debian-bullseye # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-19ffba88-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6885,51 +6887,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.16-centos7 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-1ba2643a-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6938,51 +6940,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-1f1d0d58-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -6991,51 +6993,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-ubuntu2004 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-2460e737-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7044,51 +7046,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.14-rhel9.0 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-4baa7139-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7097,51 +7099,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.14-rhel9.0 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.14-rhel9.0 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-4fcbaf1e-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7150,51 +7152,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-debian-bookworm # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-51914faa-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7203,51 +7205,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-debian-bookworm # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-5f3d2e16-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7256,51 +7258,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-rhel7.9 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-601188b1-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7309,51 +7311,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-rhel9.3 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-64d917f4-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7362,51 +7364,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.16-centos7 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-6aaaa304-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7415,51 +7417,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-debian-bullseye # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-834c1a9b-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7468,51 +7470,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-centos7 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-971ad57c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7521,51 +7523,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-rhel7.9 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ad622acd-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7574,51 +7576,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.16-rhel9.2 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-b2745d99-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7627,51 +7629,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-centos7 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ce1fa4d7-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7680,51 +7682,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.16-rhel9.2 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d1d30700-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7733,51 +7735,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-rhel9.3 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-rhel9.3-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d2caf0c9-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7786,51 +7788,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-rhel9.1 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d616c3a5-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7839,51 +7841,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-rhel9.1 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-d6ae094c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7892,51 +7894,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-ubuntu2004 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-e700ac20-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -7945,51 +7947,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-ede01681-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -8029,24 +8031,24 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2_pull: "Pull" on 2024-01-13 in devel-ubuntu2004 # Duplicated in: docker_compose_v2_pull: "Pull" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-without-explicit-pull', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-without-explicit-pull", + "2.21.0", False, True, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Error \n' + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Error \n" "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied\n", # noqa: E501 [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied", # noqa: E501 ), ], @@ -8075,25 +8077,25 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present without explicit pull (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Present without explicit pull (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)", + "2.21.0", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], @@ -8143,23 +8145,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-restarted', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-restarted", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -8210,23 +8212,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-restarted-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-restarted-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -8255,23 +8257,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Started" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-started', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-started", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -8300,23 +8302,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Started (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-started-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-started-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -8345,31 +8347,31 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Stopped" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Stopped" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-stopped', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-stopped", + "2.21.0", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -8398,31 +8400,31 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Stopped (check)" on 2024-01-07 in devel-ubuntu2204 # Duplicated in: docker_compose_v2: "Stopped (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-stopped-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-07-docker_compose_v2-stopped-(check)", + "2.21.0", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -8440,98 +8442,98 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2_pull: "Pull (check)" on 2024-01-13 in devel-ubuntu2004 # Duplicated in: docker_compose_v2_pull: "Pull (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-(check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-(check)", + "2.21.0", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.15-rhel7.9 ( - '2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.15-rhel7.9-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 34.2kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 34.2kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8539,65 +8541,65 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.15-rhel9.1 ( - '2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.15-rhel9.1-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.12kB/2.207MB\n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.12kB/2.207MB\n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8605,72 +8607,72 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.16-rhel9.2 ( - '2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.16-rhel9.2-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.12kB/2.207MB\n' - ' 486039affc0a Downloading [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.12kB/2.207MB\n" + " 486039affc0a Downloading [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8679,72 +8681,72 @@ EVENT_TEST_CASES = [ # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.14-rhel9.0 # Duplicated in: docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-rhel9.3 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8752,79 +8754,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.16-centos7 ( - '2.21.0-2.16-centos7-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.16-centos7-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [================================================> ] 2.13MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [================================================> ] 2.13MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8832,79 +8834,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-devel-ubuntu2204-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [===============================================> ] 2.097MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [===============================================> ] 2.097MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8912,79 +8914,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-debian-bookworm ( - '2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-devel-debian-bookworm-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==============================================> ] 2.032MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==============================================> ] 2.032MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -8992,79 +8994,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-ubuntu2004 ( - '2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-devel-ubuntu2004-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [============================================> ] 1.966MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [============================================> ] 1.966MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -9072,79 +9074,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in 2.15-centos7 ( - '2.21.0-2.15-centos7-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-2.15-centos7-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [=======================================> ] 1.737MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [=======================================> ] 1.737MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -9152,79 +9154,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-debian-bullseye ( - '2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.21.0', + "2.21.0-devel-debian-bullseye-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [=====================================> ] 1.638MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [=====================================> ] 1.638MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -9242,23 +9244,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2_pull: "Pull with policy=always (again)" on 2024-01-13 in devel-ubuntu2004 # Duplicated in: docker_compose_v2_pull: "Pull with policy=always (again)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again)", + "2.21.0", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -9287,23 +9289,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2_pull: "Pull with policy=always (check)" on 2024-01-13 in devel-ubuntu2004 # Duplicated in: docker_compose_v2_pull: "Pull with policy=always (check)" on 2024-01-13 in devel-ubuntu2204 ( - '2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again,-check)', - '2.21.0', + "2.21.0-2.14-rhel9.0-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(again,-check)", + "2.21.0", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -9317,51 +9319,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Cleanup" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-absent', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-absent", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' Network ansible-docker-test-01234567-start-stop_default Removed\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " Network ansible-docker-test-01234567-start-stop_default Removing\n" + " Network ansible-docker-test-01234567-start-stop_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removed', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removed", None, ), ], @@ -9370,52 +9372,52 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Absent (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Absent (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-absent-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-absent-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removing\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Removed\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Removing\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Resource is still in use\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removing', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Removed', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Removing', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', + "network", + "ansible-docker-test-01234567-start-stop_default", None, - 'Resource is still in use', + "Resource is still in use", ), ], [], @@ -9425,51 +9427,51 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Stopping service" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-cleanup', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-cleanup", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n' - ' Network ansible-docker-test-01234567-pull_default Removing\n' - ' Network ansible-docker-test-01234567-pull_default Removed\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopping\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Stopped\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removing\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Removed\n" + " Network ansible-docker-test-01234567-pull_default Removing\n" + " Network ansible-docker-test-01234567-pull_default Removed\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Stopped", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removing', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removing", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Removed', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Removed", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removing', + "network", + "ansible-docker-test-01234567-pull_default", + "Removing", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Removed', + "network", + "ansible-docker-test-01234567-pull_default", + "Removed", None, ), ], @@ -9478,51 +9480,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present", + "2.23.3", False, False, - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9530,37 +9532,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-07 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(changed-check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(changed-check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "df477f7889c_ansible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9568,37 +9570,37 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (changed check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-(changed-check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-(changed-check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' DRY-RUN MODE - Container e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " DRY-RUN MODE - Container e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "e3161c3ca1e_ansible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9607,37 +9609,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present (changed)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present (changed)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(changed)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(changed)", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreate\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Recreated\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Recreated", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9645,51 +9647,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-26fefc5c-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9697,51 +9699,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present (check)" on 2024-01-07 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "nsible-docker-test-bc362ba-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -9752,16 +9754,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Started (idempotent check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(idempotent-check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(idempotent-check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -9772,16 +9774,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Started (idempotent)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(idempotent)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-(idempotent)", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), ], @@ -9790,37 +9792,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present stopped" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present stopped" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-stopped', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-stopped", + "2.23.3", False, False, - ' Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' Network ansible-docker-test-01234567-start-stop_default Created\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " Network ansible-docker-test-01234567-start-stop_default Creating\n" + " Network ansible-docker-test-01234567-start-stop_default Created\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -9829,37 +9831,37 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present stopped (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present stopped (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-stopped-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-stopped-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-start-stop_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Created\n", [ Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Creating', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-start-stop_default', - 'Created', + "network", + "ansible-docker-test-01234567-start-stop_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Creating', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Created', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Created", None, ), ], @@ -9868,30 +9870,30 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=always" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=always" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=always', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=always", + "2.23.3", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -9899,51 +9901,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-07 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=always-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=always-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container 9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container 9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container 9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container 9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - '9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - '9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "9f33f2ddb62_ansible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -9951,51 +9953,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=always (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=always-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=always-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n' - ' DRY-RUN MODE - Container e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreate\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Recreated\n" + " DRY-RUN MODE - Container e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreate', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreate", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Recreated', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Recreated", None, ), Event( - 'container', - 'e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "e6dd7e14964_ansible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10004,51 +10006,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=missing" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=missing" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing", + "2.23.3", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10056,51 +10058,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=missing-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10108,51 +10110,51 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2: "Present with pull=missing (check)" on 2024-01-07 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10163,16 +10165,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent)", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -10183,16 +10185,16 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never (idempotent, check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=missing-(idempotent,-check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Running\n", [ Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Running', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Running", None, ), ], @@ -10201,51 +10203,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.23.3", False, False, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10254,38 +10256,38 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never", + "2.23.3", False, True, - ' Network ansible-docker-test-01234567-pull_default Creating\n' - ' Network ansible-docker-test-01234567-pull_default Created\n' - ' Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - 'Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest\n', + " Network ansible-docker-test-01234567-pull_default Creating\n" + " Network ansible-docker-test-01234567-pull_default Created\n" + " Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + "Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'unknown', - '', - 'Error', - 'Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest', + "unknown", + "", + "Error", + "Error response from daemon: no such image: does-not-exist:latest: No such image: does-not-exist:latest", ), ], [], @@ -10293,51 +10295,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=never-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2-present-with-pull=never-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-26fefc5c-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10346,51 +10348,51 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present with pull=never (check)" on 2024-01-07 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-with-pull=never-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n' - ' DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n' - ' DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n', + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Creating\n" + " DRY-RUN MODE - Network ansible-docker-test-01234567-pull_default Created\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Creating\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1 Created\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Starting\n" + " DRY-RUN MODE - Container nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1 Started\n", [ Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Creating', + "network", + "ansible-docker-test-01234567-pull_default", + "Creating", None, ), Event( - 'network', - 'ansible-docker-test-01234567-pull_default', - 'Created', + "network", + "ansible-docker-test-01234567-pull_default", + "Created", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Creating', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Creating", None, ), Event( - 'container', - 'ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1', - 'Created', + "container", + "ansible-docker-test-01234567-pull-ansible-docker-test-01234567-cont-1", + "Created", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Starting', + "container", + "nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Starting", None, ), Event( - 'container', - 'nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1', - 'Started', + "container", + "nsible-docker-test-bc362ba-pull-ansible-docker-test-01234567-cont-1", + "Started", None, ), ], @@ -10400,24 +10402,24 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Present without explicit pull" on 2024-01-13 in devel-archlinux # Duplicated in: docker_compose_v2_pull: "Pull" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-without-explicit-pull', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-without-explicit-pull", + "2.23.3", False, True, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Error \n' + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Error \n" "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied\n", # noqa: E501 [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", "Error response from daemon: pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested access to the resource is denied", # noqa: E501 ), ], @@ -10426,25 +10428,25 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Present without explicit pull (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Present without explicit pull (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-present-without-explicit-pull-(check)", + "2.23.3", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], @@ -10454,23 +10456,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in devel-archlinux # Duplicated in: docker_compose_v2: "Restarted" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-restarted', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-restarted", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -10481,23 +10483,23 @@ EVENT_TEST_CASES = [ # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in devel-archlinux # Duplicated in: docker_compose_v2: "Restarted (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-restarted-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-restarted-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Restarting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Restarting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Restarting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -10506,23 +10508,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Started" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Started" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-started', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-started", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -10531,23 +10533,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Started (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Started (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-started-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-started-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Starting\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Started\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Starting', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Starting", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Started', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Started", None, ), ], @@ -10556,31 +10558,31 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Stopped" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Stopped" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-stopped', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-stopped", + "2.23.3", False, False, - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -10589,31 +10591,31 @@ EVENT_TEST_CASES = [ # docker_compose_v2: "Stopped (check)" on 2024-01-07 in devel-archlinux # Duplicated in: docker_compose_v2: "Stopped (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-stopped-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-07-docker_compose_v2-stopped-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n' - '\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n' - ' DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n', + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Running\n" + "\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopping\n" + " DRY-RUN MODE - Container ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1 Stopped\n", [ Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Running', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Running", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopping', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopping", None, ), Event( - 'container', - 'ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1', - 'Stopped', + "container", + "ansible-docker-test-01234567-start-stop-ansible-docker-test-01234567-container-1", + "Stopped", None, ), ], @@ -10621,49 +10623,49 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-(check)", + "2.23.3", True, True, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n' - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Error \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont - Pull error for image: does-not-exist:latest \n" + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed\n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Error', - 'pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed', + "unknown", + "ansible-docker-test-01234567-cont", + "Error", + "pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed", ), ], [], ), # docker_compose_v2_pull: "Pull with policy=always" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=always', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=always", + "2.23.3", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -10672,23 +10674,23 @@ EVENT_TEST_CASES = [ # docker_compose_v2_pull: "Pull with policy=always (check)" on 2024-01-13 in devel-archlinux # Duplicated in: docker_compose_v2_pull: "Pull with policy=missing (check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=always-(check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n' - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulling \n" + " DRY-RUN MODE - ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -10696,79 +10698,79 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=missing" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing", + "2.23.3", False, False, - ' ansible-docker-test-01234567-cont Pulling \n' - ' 486039affc0a Pulling fs layer \n' - ' 486039affc0a Downloading [> ] 32.16kB/2.207MB\n' - ' 486039affc0a Verifying Checksum \n' - ' 486039affc0a Download complete \n' - ' 486039affc0a Extracting [> ] 32.77kB/2.207MB\n' - ' 486039affc0a Extracting [==========================================> ] 1.868MB/2.207MB\n' - ' 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n' - ' 486039affc0a Pull complete \n' - ' ansible-docker-test-01234567-cont Pulled \n', + " ansible-docker-test-01234567-cont Pulling \n" + " 486039affc0a Pulling fs layer \n" + " 486039affc0a Downloading [> ] 32.16kB/2.207MB\n" + " 486039affc0a Verifying Checksum \n" + " 486039affc0a Download complete \n" + " 486039affc0a Extracting [> ] 32.77kB/2.207MB\n" + " 486039affc0a Extracting [==========================================> ] 1.868MB/2.207MB\n" + " 486039affc0a Extracting [==================================================>] 2.207MB/2.207MB\n" + " 486039affc0a Pull complete \n" + " ansible-docker-test-01234567-cont Pulled \n", [ Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulling', + "service", + "ansible-docker-test-01234567-cont", + "Pulling", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pulling fs layer', + "image-layer", + "486039affc0a", + "Pulling fs layer", None, ), Event( - 'image-layer', - '486039affc0a', - 'Downloading', + "image-layer", + "486039affc0a", + "Downloading", None, ), Event( - 'image-layer', - '486039affc0a', - 'Verifying Checksum', + "image-layer", + "486039affc0a", + "Verifying Checksum", None, ), Event( - 'image-layer', - '486039affc0a', - 'Download complete', + "image-layer", + "486039affc0a", + "Download complete", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Extracting', + "image-layer", + "486039affc0a", + "Extracting", None, ), Event( - 'image-layer', - '486039affc0a', - 'Pull complete', + "image-layer", + "486039affc0a", + "Pull complete", None, ), Event( - 'service', - 'ansible-docker-test-01234567-cont', - 'Pulled', + "service", + "ansible-docker-test-01234567-cont", + "Pulled", None, ), ], @@ -10776,34 +10778,34 @@ EVENT_TEST_CASES = [ ), # docker_compose_v2_pull: "Pull with policy=missing (idempotent)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing-(idempotent)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing-(idempotent)", + "2.23.3", False, False, - ' ansible-docker-test-01234567-cont Skipped - Image is already present locally \n', + " ansible-docker-test-01234567-cont Skipped - Image is already present locally \n", [ Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Skipped', - 'Image is already present locally', + "unknown", + "ansible-docker-test-01234567-cont", + "Skipped", + "Image is already present locally", ), ], [], ), # docker_compose_v2_pull: "Pull with policy=missing (idempotent, check)" on 2024-01-13 in devel-archlinux ( - '2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing-(idempotent,-check)', - '2.23.3', + "2.23.3-devel-archlinux-2024-01-13-docker_compose_v2_pull-pull-with-policy=missing-(idempotent,-check)", + "2.23.3", True, False, - ' DRY-RUN MODE - ansible-docker-test-01234567-cont Skipped - Image is already present locally \n', + " DRY-RUN MODE - ansible-docker-test-01234567-cont Skipped - Image is already present locally \n", [ Event( - 'unknown', - 'ansible-docker-test-01234567-cont', - 'Skipped', - 'Image is already present locally', + "unknown", + "ansible-docker-test-01234567-cont", + "Skipped", + "Image is already present locally", ), ], [], diff --git a/tests/unit/plugins/module_utils/test__logfmt.py b/tests/unit/plugins/module_utils/test__logfmt.py index efcc15ff..efb1fa67 100644 --- a/tests/unit/plugins/module_utils/test__logfmt.py +++ b/tests/unit/plugins/module_utils/test__logfmt.py @@ -2,11 +2,12 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible_collections.community.docker.plugins.module_utils._logfmt import ( InvalidLogFmt, parse_line, @@ -19,42 +20,42 @@ SUCCESS_TEST_CASES = [ ' created for project \\"influxdb\\".\\nSet `external: true` to use an existing network"', {}, { - 'time': '2024-02-02T08:14:10+01:00', - 'level': 'warning', - 'msg': 'a network with name influxNetwork exists but was not created for project "influxdb".\nSet `external: true` to use an existing network', + "time": "2024-02-02T08:14:10+01:00", + "level": "warning", + "msg": 'a network with name influxNetwork exists but was not created for project "influxdb".\nSet `external: true` to use an existing network', }, ), ( 'time="2024-02-02T08:14:10+01:00" level=warning msg="a network with name influxNetwork exists but was not' ' created for project \\"influxdb\\".\\nSet `external: true` to use an existing network"', - {'logrus_mode': True}, + {"logrus_mode": True}, { - 'time': '2024-02-02T08:14:10+01:00', - 'level': 'warning', - 'msg': 'a network with name influxNetwork exists but was not created for project "influxdb".\nSet `external: true` to use an existing network', + "time": "2024-02-02T08:14:10+01:00", + "level": "warning", + "msg": 'a network with name influxNetwork exists but was not created for project "influxdb".\nSet `external: true` to use an existing network', }, ), ( 'foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf', {}, { - 'foo': 'bar', - 'a': '14', - 'baz': 'hello kitty', - 'cool%story': 'bro', - 'f': None, - '%^asdf': None, + "foo": "bar", + "a": "14", + "baz": "hello kitty", + "cool%story": "bro", + "f": None, + "%^asdf": None, }, ), ( '{"foo":"bar"}', {}, { - '{': None, - 'foo': None, - ':': None, - 'bar': None, - '}': None, + "{": None, + "foo": None, + ":": None, + "bar": None, + "}": None, }, ), ] @@ -63,35 +64,35 @@ SUCCESS_TEST_CASES = [ FAILURE_TEST_CASES = [ ( 'foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf', - {'logrus_mode': True}, + {"logrus_mode": True}, 'Key must always be followed by "=" in logrus mode', ), ( - '{}', - {'logrus_mode': True}, + "{}", + {"logrus_mode": True}, 'Key must always be followed by "=" in logrus mode', ), ( - '[]', - {'logrus_mode': True}, + "[]", + {"logrus_mode": True}, 'Key must always be followed by "=" in logrus mode', ), ( '{"foo=bar": "baz=bam"}', - {'logrus_mode': True}, + {"logrus_mode": True}, 'Key must always be followed by "=" in logrus mode', ), ] -@pytest.mark.parametrize('line, kwargs, result', SUCCESS_TEST_CASES) +@pytest.mark.parametrize("line, kwargs, result", SUCCESS_TEST_CASES) def test_parse_line_success(line, kwargs, result): res = parse_line(line, **kwargs) print(repr(res)) assert res == result -@pytest.mark.parametrize('line, kwargs, message', FAILURE_TEST_CASES) +@pytest.mark.parametrize("line, kwargs, message", FAILURE_TEST_CASES) def test_parse_line_success(line, kwargs, message): with pytest.raises(InvalidLogFmt) as exc: parse_line(line, **kwargs) diff --git a/tests/unit/plugins/module_utils/test__scramble.py b/tests/unit/plugins/module_utils/test__scramble.py index f4a31a8c..93556cce 100644 --- a/tests/unit/plugins/module_utils/test__scramble.py +++ b/tests/unit/plugins/module_utils/test__scramble.py @@ -2,27 +2,31 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible_collections.community.docker.plugins.module_utils._scramble import ( scramble, unscramble, ) -@pytest.mark.parametrize('plaintext, key, scrambled', [ - ('', b'0', '=S='), - ('hello', b'\x00', '=S=aGVsbG8='), - ('hello', b'\x01', '=S=aWRtbW4='), -]) +@pytest.mark.parametrize( + "plaintext, key, scrambled", + [ + ("", b"0", "=S="), + ("hello", b"\x00", "=S=aGVsbG8="), + ("hello", b"\x01", "=S=aWRtbW4="), + ], +) def test_scramble_unscramble(plaintext, key, scrambled): scrambled_ = scramble(plaintext, key) - print(f'{scrambled_!r} == {scrambled!r}') + print(f"{scrambled_!r} == {scrambled!r}") assert scrambled_ == scrambled plaintext_ = unscramble(scrambled, key) - print(f'{plaintext_!r} == {plaintext!r}') + print(f"{plaintext_!r} == {plaintext!r}") assert plaintext_ == plaintext diff --git a/tests/unit/plugins/module_utils/test_compose_v2.py b/tests/unit/plugins/module_utils/test_compose_v2.py index 80999927..c6234e1c 100644 --- a/tests/unit/plugins/module_utils/test_compose_v2.py +++ b/tests/unit/plugins/module_utils/test_compose_v2.py @@ -2,11 +2,12 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible_collections.community.docker.plugins.module_utils.compose_v2 import ( Event, parse_events, @@ -17,60 +18,60 @@ from .compose_v2_test_cases import EVENT_TEST_CASES EXTRA_TEST_CASES = [ ( - '2.24.2-manual-build-dry-run', - '2.24.2', + "2.24.2-manual-build-dry-run", + "2.24.2", True, False, - ' DRY-RUN MODE - build service foobar \n' - ' DRY-RUN MODE - ==> ==> writing image dryRun-8843d7f92416211de9ebb963ff4ce28125932878 \n' - ' DRY-RUN MODE - ==> ==> naming to my-python \n' - ' DRY-RUN MODE - Network compose_default Creating\n' - ' DRY-RUN MODE - Network compose_default Created\n' - ' DRY-RUN MODE - Container compose-foobar-1 Creating\n' - ' DRY-RUN MODE - Container compose-foobar-1 Created\n' - ' DRY-RUN MODE - Container ompose-foobar-1 Starting\n' - ' DRY-RUN MODE - Container ompose-foobar-1 Started\n', + " DRY-RUN MODE - build service foobar \n" + " DRY-RUN MODE - ==> ==> writing image dryRun-8843d7f92416211de9ebb963ff4ce28125932878 \n" + " DRY-RUN MODE - ==> ==> naming to my-python \n" + " DRY-RUN MODE - Network compose_default Creating\n" + " DRY-RUN MODE - Network compose_default Created\n" + " DRY-RUN MODE - Container compose-foobar-1 Creating\n" + " DRY-RUN MODE - Container compose-foobar-1 Created\n" + " DRY-RUN MODE - Container ompose-foobar-1 Starting\n" + " DRY-RUN MODE - Container ompose-foobar-1 Started\n", [ Event( - 'service', - 'foobar', - 'Building', + "service", + "foobar", + "Building", None, ), Event( - 'network', - 'compose_default', - 'Creating', + "network", + "compose_default", + "Creating", None, ), Event( - 'network', - 'compose_default', - 'Created', + "network", + "compose_default", + "Created", None, ), Event( - 'container', - 'compose-foobar-1', - 'Creating', + "container", + "compose-foobar-1", + "Creating", None, ), Event( - 'container', - 'compose-foobar-1', - 'Created', + "container", + "compose-foobar-1", + "Created", None, ), Event( - 'container', - 'ompose-foobar-1', - 'Starting', + "container", + "ompose-foobar-1", + "Starting", None, ), Event( - 'container', - 'ompose-foobar-1', - 'Started', + "container", + "ompose-foobar-1", + "Started", None, ), ], @@ -78,100 +79,100 @@ EXTRA_TEST_CASES = [ ), ( # https://github.com/ansible-collections/community.docker/issues/785 - '2.20.0-manual-pull', - '2.20.0', + "2.20.0-manual-pull", + "2.20.0", False, False, - '4f4fb700ef54 Waiting\n' - '238022553356 Downloading 541B/541B\n' - '972e292d3a60 Downloading 106kB/10.43MB\n' - 'f2543dc9f0a9 Downloading 25.36kB/2.425MB\n' - '972e292d3a60 Downloading 5.925MB/10.43MB\n' - 'f2543dc9f0a9 Downloading 2.219MB/2.425MB\n' - 'f2543dc9f0a9 Extracting 32.77kB/2.425MB\n' - '4f4fb700ef54 Downloading 32B/32B\n' - 'f2543dc9f0a9 Extracting 2.425MB/2.425MB\n' - '972e292d3a60 Extracting 131.1kB/10.43MB\n' - '972e292d3a60 Extracting 10.43MB/10.43MB\n' - '238022553356 Extracting 541B/541B\n' - '4f4fb700ef54 Extracting 32B/32B\n', + "4f4fb700ef54 Waiting\n" + "238022553356 Downloading 541B/541B\n" + "972e292d3a60 Downloading 106kB/10.43MB\n" + "f2543dc9f0a9 Downloading 25.36kB/2.425MB\n" + "972e292d3a60 Downloading 5.925MB/10.43MB\n" + "f2543dc9f0a9 Downloading 2.219MB/2.425MB\n" + "f2543dc9f0a9 Extracting 32.77kB/2.425MB\n" + "4f4fb700ef54 Downloading 32B/32B\n" + "f2543dc9f0a9 Extracting 2.425MB/2.425MB\n" + "972e292d3a60 Extracting 131.1kB/10.43MB\n" + "972e292d3a60 Extracting 10.43MB/10.43MB\n" + "238022553356 Extracting 541B/541B\n" + "4f4fb700ef54 Extracting 32B/32B\n", [ Event( - 'image-layer', - '4f4fb700ef54', - 'Waiting', + "image-layer", + "4f4fb700ef54", + "Waiting", None, ), Event( - 'image-layer', - '238022553356', - 'Downloading', + "image-layer", + "238022553356", + "Downloading", None, ), Event( - 'image-layer', - '972e292d3a60', - 'Downloading', + "image-layer", + "972e292d3a60", + "Downloading", None, ), Event( - 'image-layer', - 'f2543dc9f0a9', - 'Downloading', + "image-layer", + "f2543dc9f0a9", + "Downloading", None, ), Event( - 'image-layer', - '972e292d3a60', - 'Downloading', + "image-layer", + "972e292d3a60", + "Downloading", None, ), Event( - 'image-layer', - 'f2543dc9f0a9', - 'Downloading', + "image-layer", + "f2543dc9f0a9", + "Downloading", None, ), Event( - 'image-layer', - 'f2543dc9f0a9', - 'Extracting', + "image-layer", + "f2543dc9f0a9", + "Extracting", None, ), Event( - 'image-layer', - '4f4fb700ef54', - 'Downloading', + "image-layer", + "4f4fb700ef54", + "Downloading", None, ), Event( - 'image-layer', - 'f2543dc9f0a9', - 'Extracting', + "image-layer", + "f2543dc9f0a9", + "Extracting", None, ), Event( - 'image-layer', - '972e292d3a60', - 'Extracting', + "image-layer", + "972e292d3a60", + "Extracting", None, ), Event( - 'image-layer', - '972e292d3a60', - 'Extracting', + "image-layer", + "972e292d3a60", + "Extracting", None, ), Event( - 'image-layer', - '238022553356', - 'Extracting', + "image-layer", + "238022553356", + "Extracting", None, ), Event( - 'image-layer', - '4f4fb700ef54', - 'Extracting', + "image-layer", + "4f4fb700ef54", + "Extracting", None, ), ], @@ -179,8 +180,8 @@ EXTRA_TEST_CASES = [ ), ( # https://github.com/ansible-collections/community.docker/issues/787 - '2.20.3-logrus-warn', - '2.20.3', + "2.20.3-logrus-warn", + "2.20.3", False, False, 'time="2024-02-02T08:14:10+01:00" level=warning msg="a network with name influxNetwork exists but was not' @@ -192,8 +193,8 @@ EXTRA_TEST_CASES = [ ), ( # https://github.com/ansible-collections/community.docker/issues/807 - '2.20.3-image-warning-error', - '2.20.3', + "2.20.3-image-warning-error", + "2.20.3", False, True, " dummy3 Warning \n" @@ -203,50 +204,48 @@ EXTRA_TEST_CASES = [ " dummy5 Error Bar baz bam \n", [ Event( - 'unknown', - 'dummy', - 'Error', + "unknown", + "dummy", + "Error", None, ), Event( - 'unknown', - 'dummy5', - 'Error', - 'Bar baz bam', + "unknown", + "dummy5", + "Error", + "Bar baz bam", ), ], [ - 'Unspecified warning for dummy3', - 'Unspecified warning for dummy2', - 'dummy4: Foo bar', + "Unspecified warning for dummy3", + "Unspecified warning for dummy2", + "dummy4: Foo bar", ], ), ( # https://github.com/ansible-collections/community.docker/issues/911 - '2.28.1-image-pull-skipped', - '2.28.1', + "2.28.1-image-pull-skipped", + "2.28.1", False, False, - " bash_1 Skipped \n" - " bash_2 Pulling \n" - " bash_2 Pulled \n", + " bash_1 Skipped \n" " bash_2 Pulling \n" " bash_2 Pulled \n", [ Event( - 'unknown', - 'bash_1', - 'Skipped', + "unknown", + "bash_1", + "Skipped", None, ), Event( - 'service', - 'bash_2', - 'Pulling', + "service", + "bash_2", + "Pulling", None, ), Event( - 'service', - 'bash_2', - 'Pulled', + "service", + "bash_2", + "Pulled", None, ), ], @@ -254,30 +253,30 @@ EXTRA_TEST_CASES = [ ), ( # https://github.com/ansible-collections/community.docker/issues/948 - '2.28.1-unknown', # TODO: find out actual version! - '2.28.1', # TODO: find out actual version! + "2.28.1-unknown", # TODO: find out actual version! + "2.28.1", # TODO: find out actual version! False, True, " prometheus Pulling \n" " prometheus Pulled \n" - "network internet-monitoring-front-tier was found but has incorrect label com.docker.compose.network set to \"internet-monitoring-front-tier\"\n", + 'network internet-monitoring-front-tier was found but has incorrect label com.docker.compose.network set to "internet-monitoring-front-tier"\n', [ Event( - 'service', - 'prometheus', - 'Pulling', + "service", + "prometheus", + "Pulling", None, ), Event( - 'service', - 'prometheus', - 'Pulled', + "service", + "prometheus", + "Pulled", None, ), Event( - 'unknown', - '', - 'Error', + "unknown", + "", + "Error", 'network internet-monitoring-front-tier was found but has incorrect label com.docker.compose.network set to "internet-monitoring-front-tier"', ), ], @@ -285,8 +284,8 @@ EXTRA_TEST_CASES = [ ), ( # https://github.com/ansible-collections/community.docker/issues/978 - '2.28.1-unknown', # TODO: find out actual version! - '2.28.1', # TODO: find out actual version! + "2.28.1-unknown", # TODO: find out actual version! + "2.28.1", # TODO: find out actual version! False, True, " Network create_users_db_default Creating\n" @@ -299,52 +298,52 @@ EXTRA_TEST_CASES = [ "container create_users_db-init exited (0)\n", [ Event( - 'network', - 'create_users_db_default', - 'Creating', + "network", + "create_users_db_default", + "Creating", None, ), Event( - 'network', - 'create_users_db_default', - 'Created', + "network", + "create_users_db_default", + "Created", None, ), Event( - 'container', - 'create_users_db-init', - 'Creating', + "container", + "create_users_db-init", + "Creating", None, ), Event( - 'container', - 'create_users_db-init', - 'Created', + "container", + "create_users_db-init", + "Created", None, ), Event( - 'container', - 'create_users_db-init', - 'Starting', + "container", + "create_users_db-init", + "Starting", None, ), Event( - 'container', - 'create_users_db-init', - 'Started', + "container", + "create_users_db-init", + "Started", None, ), Event( - 'container', - 'create_users_db-init', - 'Waiting', + "container", + "create_users_db-init", + "Waiting", None, ), Event( - 'unknown', - '', - 'Error', - 'container create_users_db-init exited (0)', + "unknown", + "", + "Error", + "container create_users_db-init exited (0)", ), ], [], @@ -355,17 +354,21 @@ _ALL_TEST_CASES = EVENT_TEST_CASES + EXTRA_TEST_CASES @pytest.mark.parametrize( - 'test_id, compose_version, dry_run, nonzero_rc, stderr, events, warnings', + "test_id, compose_version, dry_run, nonzero_rc, stderr, events, warnings", _ALL_TEST_CASES, ids=[tc[0] for tc in _ALL_TEST_CASES], ) -def test_parse_events(test_id, compose_version, dry_run, nonzero_rc, stderr, events, warnings): +def test_parse_events( + test_id, compose_version, dry_run, nonzero_rc, stderr, events, warnings +): collected_warnings = [] def collect_warning(msg): collected_warnings.append(msg) - collected_events = parse_events(stderr, dry_run=dry_run, warn_function=collect_warning, nonzero_rc=nonzero_rc) + collected_events = parse_events( + stderr, dry_run=dry_run, warn_function=collect_warning, nonzero_rc=nonzero_rc + ) print(collected_events) print(collected_warnings) diff --git a/tests/unit/plugins/module_utils/test_copy.py b/tests/unit/plugins/module_utils/test_copy.py index 9a826713..eb150b25 100644 --- a/tests/unit/plugins/module_utils/test_copy.py +++ b/tests/unit/plugins/module_utils/test_copy.py @@ -2,11 +2,12 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible_collections.community.docker.plugins.module_utils.copy import ( _stream_generator_to_fileobj, ) @@ -16,54 +17,57 @@ def _simple_generator(sequence): yield from sequence -@pytest.mark.parametrize('chunks, read_sizes', [ - ( - [ - (1, b'1'), - (1, b'2'), - (1, b'3'), - (1, b'4'), - ], - [ - 1, - 2, - 3, - ] - ), - ( - [ - (1, b'123'), - (1, b'456'), - (1, b'789'), - ], - [ - 1, - 4, - 2, - 2, - 2, - ] - ), - ( - [ - (10 * 1024 * 1024, b'0'), - (10 * 1024 * 1024, b'1'), - ], - [ - 1024 * 1024 - 5, - 5 * 1024 * 1024 - 3, - 10 * 1024 * 1024 - 2, - 2 * 1024 * 1024 - 1, - 2 * 1024 * 1024 + 5 + 3 + 2 + 1, - ] - ), -]) +@pytest.mark.parametrize( + "chunks, read_sizes", + [ + ( + [ + (1, b"1"), + (1, b"2"), + (1, b"3"), + (1, b"4"), + ], + [ + 1, + 2, + 3, + ], + ), + ( + [ + (1, b"123"), + (1, b"456"), + (1, b"789"), + ], + [ + 1, + 4, + 2, + 2, + 2, + ], + ), + ( + [ + (10 * 1024 * 1024, b"0"), + (10 * 1024 * 1024, b"1"), + ], + [ + 1024 * 1024 - 5, + 5 * 1024 * 1024 - 3, + 10 * 1024 * 1024 - 2, + 2 * 1024 * 1024 - 1, + 2 * 1024 * 1024 + 5 + 3 + 2 + 1, + ], + ), + ], +) def test__stream_generator_to_fileobj(chunks, read_sizes): chunks = [count * data for count, data in chunks] stream = _simple_generator(chunks) - expected = b''.join(chunks) + expected = b"".join(chunks) - buffer = b'' + buffer = b"" totally_read = 0 f = _stream_generator_to_fileobj(stream) for read_size in read_sizes: @@ -72,5 +76,5 @@ def test__stream_generator_to_fileobj(chunks, read_sizes): buffer += chunk totally_read += read_size - assert buffer == expected[:len(buffer)] + assert buffer == expected[: len(buffer)] assert min(totally_read, len(expected)) == len(buffer) diff --git a/tests/unit/plugins/module_utils/test_image_archive.py b/tests/unit/plugins/module_utils/test_image_archive.py index 76ff2006..7c161037 100644 --- a/tests/unit/plugins/module_utils/test_image_archive.py +++ b/tests/unit/plugins/module_utils/test_image_archive.py @@ -2,16 +2,18 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type -import pytest import tarfile +import pytest from ansible_collections.community.docker.plugins.module_utils.image_archive import ( + ImageArchiveInvalidException, api_image_id, archived_image_manifest, - ImageArchiveInvalidException ) from ..test_support.docker_image_archive_stubbing import ( @@ -23,18 +25,17 @@ from ..test_support.docker_image_archive_stubbing import ( @pytest.fixture def tar_file_name(tmpdir): - ''' + """ Return the name of a non-existing tar file in an existing temporary directory. - ''' + """ # Cast to str required by Python 2.x - return str(tmpdir.join('foo.tar')) + return str(tmpdir.join("foo.tar")) -@pytest.mark.parametrize('expected, value', [ - ('sha256:foo', 'foo'), - ('sha256:bar', 'bar') -]) +@pytest.mark.parametrize( + "expected, value", [("sha256:foo", "foo"), ("sha256:bar", "bar")] +) def test_api_image_id_from_archive_id(expected, value): assert api_image_id(value) == expected @@ -74,15 +75,11 @@ def test_archived_image_manifest_raises_when_tar_missing_manifest(tar_file_name) raise AssertionError() except ImageArchiveInvalidException as e: assert isinstance(e.__cause__, KeyError) - assert 'manifest.json' in str(e.__cause__) + assert "manifest.json" in str(e.__cause__) def test_archived_image_manifest_raises_when_manifest_missing_id(tar_file_name): - manifest = [ - { - 'foo': 'bar' - } - ] + manifest = [{"foo": "bar"}] write_imitation_archive_with_manifest(tar_file_name, manifest) @@ -91,4 +88,4 @@ def test_archived_image_manifest_raises_when_manifest_missing_id(tar_file_name): raise AssertionError() except ImageArchiveInvalidException as e: assert isinstance(e.__cause__, KeyError) - assert 'Config' in str(e.__cause__) + assert "Config" in str(e.__cause__) diff --git a/tests/unit/plugins/module_utils/test_util.py b/tests/unit/plugins/module_utils/test_util.py index c7d36212..ff3231c2 100644 --- a/tests/unit/plugins/module_utils/test_util.py +++ b/tests/unit/plugins/module_utils/test_util.py @@ -2,521 +2,452 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - from ansible_collections.community.docker.plugins.module_utils.util import ( compare_dict_allow_more_present, compare_generic, convert_duration_to_nanosecond, - parse_healthcheck + parse_healthcheck, ) + DICT_ALLOW_MORE_PRESENT = ( - { - 'av': {}, - 'bv': {'a': 1}, - 'result': True - }, - { - 'av': {'a': 1}, - 'bv': {'a': 1, 'b': 2}, - 'result': True - }, - { - 'av': {'a': 1}, - 'bv': {'b': 2}, - 'result': False - }, - { - 'av': {'a': 1}, - 'bv': {'a': None, 'b': 1}, - 'result': False - }, - { - 'av': {'a': None}, - 'bv': {'b': 1}, - 'result': False - }, + {"av": {}, "bv": {"a": 1}, "result": True}, + {"av": {"a": 1}, "bv": {"a": 1, "b": 2}, "result": True}, + {"av": {"a": 1}, "bv": {"b": 2}, "result": False}, + {"av": {"a": 1}, "bv": {"a": None, "b": 1}, "result": False}, + {"av": {"a": None}, "bv": {"b": 1}, "result": False}, ) COMPARE_GENERIC = [ ######################################################################################## # value - { - 'a': 1, - 'b': 2, - 'method': 'strict', - 'type': 'value', - 'result': False - }, - { - 'a': 'hello', - 'b': 'hello', - 'method': 'strict', - 'type': 'value', - 'result': True - }, - { - 'a': None, - 'b': 'hello', - 'method': 'strict', - 'type': 'value', - 'result': False - }, - { - 'a': None, - 'b': None, - 'method': 'strict', - 'type': 'value', - 'result': True - }, - { - 'a': 1, - 'b': 2, - 'method': 'ignore', - 'type': 'value', - 'result': True - }, - { - 'a': None, - 'b': 2, - 'method': 'ignore', - 'type': 'value', - 'result': True - }, + {"a": 1, "b": 2, "method": "strict", "type": "value", "result": False}, + {"a": "hello", "b": "hello", "method": "strict", "type": "value", "result": True}, + {"a": None, "b": "hello", "method": "strict", "type": "value", "result": False}, + {"a": None, "b": None, "method": "strict", "type": "value", "result": True}, + {"a": 1, "b": 2, "method": "ignore", "type": "value", "result": True}, + {"a": None, "b": 2, "method": "ignore", "type": "value", "result": True}, ######################################################################################## # list { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'y', + "b": [ + "y", ], - 'method': 'strict', - 'type': 'list', - 'result': False + "method": "strict", + "type": "list", + "result": False, }, { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'x', - 'x', + "b": [ + "x", + "x", ], - 'method': 'strict', - 'type': 'list', - 'result': False + "method": "strict", + "type": "list", + "result": False, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'strict', - 'type': 'list', - 'result': True + "method": "strict", + "type": "list", + "result": True, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'y', - 'x', + "b": [ + "y", + "x", ], - 'method': 'strict', - 'type': 'list', - 'result': False + "method": "strict", + "type": "list", + "result": False, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'x', + "b": [ + "x", ], - 'method': 'allow_more_present', - 'type': 'list', - 'result': False + "method": "allow_more_present", + "type": "list", + "result": False, }, { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'allow_more_present', - 'type': 'list', - 'result': True + "method": "allow_more_present", + "type": "list", + "result": True, }, { - 'a': [ - 'x', - 'x', - 'y', + "a": [ + "x", + "x", + "y", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'allow_more_present', - 'type': 'list', - 'result': False + "method": "allow_more_present", + "type": "list", + "result": False, }, { - 'a': [ - 'x', - 'z', + "a": [ + "x", + "z", ], - 'b': [ - 'x', - 'y', - 'x', - 'z', + "b": [ + "x", + "y", + "x", + "z", ], - 'method': 'allow_more_present', - 'type': 'list', - 'result': True + "method": "allow_more_present", + "type": "list", + "result": True, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'y', - 'x', + "b": [ + "y", + "x", ], - 'method': 'ignore', - 'type': 'list', - 'result': True + "method": "ignore", + "type": "list", + "result": True, }, ######################################################################################## # set { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'y', + "b": [ + "y", ], - 'method': 'strict', - 'type': 'set', - 'result': False + "method": "strict", + "type": "set", + "result": False, }, { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'x', - 'x', + "b": [ + "x", + "x", ], - 'method': 'strict', - 'type': 'set', - 'result': True + "method": "strict", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'strict', - 'type': 'set', - 'result': True + "method": "strict", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'y', - 'x', + "b": [ + "y", + "x", ], - 'method': 'strict', - 'type': 'set', - 'result': True + "method": "strict", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'y', + "a": [ + "x", + "y", ], - 'b': [ - 'x', + "b": [ + "x", ], - 'method': 'allow_more_present', - 'type': 'set', - 'result': False + "method": "allow_more_present", + "type": "set", + "result": False, }, { - 'a': [ - 'x', + "a": [ + "x", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'allow_more_present', - 'type': 'set', - 'result': True + "method": "allow_more_present", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'x', - 'y', + "a": [ + "x", + "x", + "y", ], - 'b': [ - 'x', - 'y', + "b": [ + "x", + "y", ], - 'method': 'allow_more_present', - 'type': 'set', - 'result': True + "method": "allow_more_present", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'z', + "a": [ + "x", + "z", ], - 'b': [ - 'x', - 'y', - 'x', - 'z', + "b": [ + "x", + "y", + "x", + "z", ], - 'method': 'allow_more_present', - 'type': 'set', - 'result': True + "method": "allow_more_present", + "type": "set", + "result": True, }, { - 'a': [ - 'x', - 'a', + "a": [ + "x", + "a", ], - 'b': [ - 'y', - 'z', + "b": [ + "y", + "z", ], - 'method': 'ignore', - 'type': 'set', - 'result': True + "method": "ignore", + "type": "set", + "result": True, }, ######################################################################################## # set(dict) { - 'a': [ - {'x': 1}, + "a": [ + {"x": 1}, ], - 'b': [ - {'y': 1}, + "b": [ + {"y": 1}, ], - 'method': 'strict', - 'type': 'set(dict)', - 'result': False + "method": "strict", + "type": "set(dict)", + "result": False, }, { - 'a': [ - {'x': 1}, + "a": [ + {"x": 1}, ], - 'b': [ - {'x': 1}, + "b": [ + {"x": 1}, ], - 'method': 'strict', - 'type': 'set(dict)', - 'result': True + "method": "strict", + "type": "set(dict)", + "result": True, }, { - 'a': [ - {'x': 1}, + "a": [ + {"x": 1}, ], - 'b': [ - {'x': 1, 'y': 2}, + "b": [ + {"x": 1, "y": 2}, ], - 'method': 'strict', - 'type': 'set(dict)', - 'result': True + "method": "strict", + "type": "set(dict)", + "result": True, }, { - 'a': [ - {'x': 1}, - {'x': 2, 'y': 3}, + "a": [ + {"x": 1}, + {"x": 2, "y": 3}, ], - 'b': [ - {'x': 1}, - {'x': 2, 'y': 3}, + "b": [ + {"x": 1}, + {"x": 2, "y": 3}, ], - 'method': 'strict', - 'type': 'set(dict)', - 'result': True + "method": "strict", + "type": "set(dict)", + "result": True, }, { - 'a': [ - {'x': 1}, + "a": [ + {"x": 1}, ], - 'b': [ - {'x': 1, 'z': 2}, - {'x': 2, 'y': 3}, + "b": [ + {"x": 1, "z": 2}, + {"x": 2, "y": 3}, ], - 'method': 'allow_more_present', - 'type': 'set(dict)', - 'result': True + "method": "allow_more_present", + "type": "set(dict)", + "result": True, }, { - 'a': [ - {'x': 1, 'y': 2}, + "a": [ + {"x": 1, "y": 2}, ], - 'b': [ - {'x': 1}, - {'x': 2, 'y': 3}, + "b": [ + {"x": 1}, + {"x": 2, "y": 3}, ], - 'method': 'allow_more_present', - 'type': 'set(dict)', - 'result': False + "method": "allow_more_present", + "type": "set(dict)", + "result": False, }, { - 'a': [ - {'x': 1, 'y': 3}, + "a": [ + {"x": 1, "y": 3}, ], - 'b': [ - {'x': 1}, - {'x': 1, 'y': 3, 'z': 4}, + "b": [ + {"x": 1}, + {"x": 1, "y": 3, "z": 4}, ], - 'method': 'allow_more_present', - 'type': 'set(dict)', - 'result': True + "method": "allow_more_present", + "type": "set(dict)", + "result": True, }, { - 'a': [ - {'x': 1}, - {'x': 2, 'y': 3}, + "a": [ + {"x": 1}, + {"x": 2, "y": 3}, ], - 'b': [ - {'x': 1}, + "b": [ + {"x": 1}, ], - 'method': 'ignore', - 'type': 'set(dict)', - 'result': True + "method": "ignore", + "type": "set(dict)", + "result": True, }, ######################################################################################## # dict + {"a": {"x": 1}, "b": {"y": 1}, "method": "strict", "type": "dict", "result": False}, { - 'a': {'x': 1}, - 'b': {'y': 1}, - 'method': 'strict', - 'type': 'dict', - 'result': False + "a": {"x": 1}, + "b": {"x": 1, "y": 2}, + "method": "strict", + "type": "dict", + "result": False, + }, + {"a": {"x": 1}, "b": {"x": 1}, "method": "strict", "type": "dict", "result": True}, + { + "a": {"x": 1, "z": 2}, + "b": {"x": 1, "y": 2}, + "method": "strict", + "type": "dict", + "result": False, }, { - 'a': {'x': 1}, - 'b': {'x': 1, 'y': 2}, - 'method': 'strict', - 'type': 'dict', - 'result': False + "a": {"x": 1, "z": 2}, + "b": {"x": 1, "y": 2}, + "method": "ignore", + "type": "dict", + "result": True, }, +] + [ { - 'a': {'x': 1}, - 'b': {'x': 1}, - 'method': 'strict', - 'type': 'dict', - 'result': True - }, - { - 'a': {'x': 1, 'z': 2}, - 'b': {'x': 1, 'y': 2}, - 'method': 'strict', - 'type': 'dict', - 'result': False - }, - { - 'a': {'x': 1, 'z': 2}, - 'b': {'x': 1, 'y': 2}, - 'method': 'ignore', - 'type': 'dict', - 'result': True - }, -] + [{ - 'a': entry['av'], - 'b': entry['bv'], - 'method': 'allow_more_present', - 'type': 'dict', - 'result': entry['result'] -} for entry in DICT_ALLOW_MORE_PRESENT] + "a": entry["av"], + "b": entry["bv"], + "method": "allow_more_present", + "type": "dict", + "result": entry["result"], + } + for entry in DICT_ALLOW_MORE_PRESENT +] @pytest.mark.parametrize("entry", DICT_ALLOW_MORE_PRESENT) def test_dict_allow_more_present(entry): - assert compare_dict_allow_more_present(entry['av'], entry['bv']) == entry['result'] + assert compare_dict_allow_more_present(entry["av"], entry["bv"]) == entry["result"] @pytest.mark.parametrize("entry", COMPARE_GENERIC) def test_compare_generic(entry): - assert compare_generic(entry['a'], entry['b'], entry['method'], entry['type']) == entry['result'] + assert ( + compare_generic(entry["a"], entry["b"], entry["method"], entry["type"]) + == entry["result"] + ) def test_convert_duration_to_nanosecond(): - nanoseconds = convert_duration_to_nanosecond('5s') + nanoseconds = convert_duration_to_nanosecond("5s") assert nanoseconds == 5000000000 - nanoseconds = convert_duration_to_nanosecond('1m5s') + nanoseconds = convert_duration_to_nanosecond("1m5s") assert nanoseconds == 65000000000 with pytest.raises(ValueError): convert_duration_to_nanosecond([1, 2, 3]) with pytest.raises(ValueError): - convert_duration_to_nanosecond('10x') + convert_duration_to_nanosecond("10x") def test_parse_healthcheck(): - result, disabled = parse_healthcheck({ - 'test': 'sleep 1', - 'interval': '1s', - }) + result, disabled = parse_healthcheck( + { + "test": "sleep 1", + "interval": "1s", + } + ) assert disabled is False - assert result == { - 'test': ['CMD-SHELL', 'sleep 1'], - 'interval': 1000000000 - } + assert result == {"test": ["CMD-SHELL", "sleep 1"], "interval": 1000000000} - result, disabled = parse_healthcheck({ - 'test': ['NONE'], - }) + result, disabled = parse_healthcheck( + { + "test": ["NONE"], + } + ) assert result is None assert disabled - result, disabled = parse_healthcheck({ - 'test': 'sleep 1', - 'interval': '1s423ms' - }) - assert result == { - 'test': ['CMD-SHELL', 'sleep 1'], - 'interval': 1423000000 - } + result, disabled = parse_healthcheck({"test": "sleep 1", "interval": "1s423ms"}) + assert result == {"test": ["CMD-SHELL", "sleep 1"], "interval": 1423000000} assert disabled is False - result, disabled = parse_healthcheck({ - 'test': 'sleep 1', - 'interval': '1h1m2s3ms4us' - }) - assert result == { - 'test': ['CMD-SHELL', 'sleep 1'], - 'interval': 3662003004000 - } + result, disabled = parse_healthcheck( + {"test": "sleep 1", "interval": "1h1m2s3ms4us"} + ) + assert result == {"test": ["CMD-SHELL", "sleep 1"], "interval": 3662003004000} assert disabled is False diff --git a/tests/unit/plugins/modules/test_docker_container_copy_into.py b/tests/unit/plugins/modules/test_docker_container_copy_into.py index 4a8292fd..3b93e84d 100644 --- a/tests/unit/plugins/modules/test_docker_container_copy_into.py +++ b/tests/unit/plugins/modules/test_docker_container_copy_into.py @@ -2,53 +2,66 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - -from ansible_collections.community.docker.plugins.modules.docker_container_copy_into import parse_modern, parse_octal_string_only +from ansible_collections.community.docker.plugins.modules.docker_container_copy_into import ( + parse_modern, + parse_octal_string_only, +) -@pytest.mark.parametrize("input, expected", [ - ('0777', 0o777), - ('777', 0o777), - ('0o777', 0o777), - ('0755', 0o755), - ('755', 0o755), - ('0o755', 0o755), - ('0644', 0o644), - ('644', 0o644), - ('0o644', 0o644), - (' 0644 ', 0o644), - (' 644 ', 0o644), - (' 0o644 ', 0o644), - ('-1', -1), -]) +@pytest.mark.parametrize( + "input, expected", + [ + ("0777", 0o777), + ("777", 0o777), + ("0o777", 0o777), + ("0755", 0o755), + ("755", 0o755), + ("0o755", 0o755), + ("0644", 0o644), + ("644", 0o644), + ("0o644", 0o644), + (" 0644 ", 0o644), + (" 644 ", 0o644), + (" 0o644 ", 0o644), + ("-1", -1), + ], +) def test_parse_string(input, expected): assert parse_modern(input) == expected assert parse_octal_string_only(input) == expected -@pytest.mark.parametrize("input", [ - 0o777, - 0o755, - 0o644, - 12345, - 123456789012345678901234567890123456789012345678901234567890, -]) +@pytest.mark.parametrize( + "input", + [ + 0o777, + 0o755, + 0o644, + 12345, + 123456789012345678901234567890123456789012345678901234567890, + ], +) def test_parse_int(input): assert parse_modern(input) == input with pytest.raises(TypeError, match=f"^must be an octal string, got {input}L?$"): parse_octal_string_only(input) -@pytest.mark.parametrize("input", [ - 1.0, - 755.5, - [], - {}, -]) +@pytest.mark.parametrize( + "input", + [ + 1.0, + 755.5, + [], + {}, + ], +) def test_parse_bad_type(input): with pytest.raises(TypeError, match="^must be an octal string or an integer, got "): parse_modern(input) @@ -56,11 +69,14 @@ def test_parse_bad_type(input): parse_octal_string_only(input) -@pytest.mark.parametrize("input", [ - "foo", - "8", - "9", -]) +@pytest.mark.parametrize( + "input", + [ + "foo", + "8", + "9", + ], +) def test_parse_bad_value(input): with pytest.raises(ValueError): parse_modern(input) diff --git a/tests/unit/plugins/modules/test_docker_image.py b/tests/unit/plugins/modules/test_docker_image.py index b408420a..b82188c6 100644 --- a/tests/unit/plugins/modules/test_docker_image.py +++ b/tests/unit/plugins/modules/test_docker_image.py @@ -2,14 +2,18 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - -from ansible_collections.community.docker.plugins.modules.docker_image import ImageManager - -from ansible_collections.community.docker.plugins.module_utils.image_archive import api_image_id +from ansible_collections.community.docker.plugins.module_utils.image_archive import ( + api_image_id, +) +from ansible_collections.community.docker.plugins.modules.docker_image import ( + ImageManager, +) from ..test_support.docker_image_archive_stubbing import ( write_imitation_archive, @@ -18,7 +22,7 @@ from ..test_support.docker_image_archive_stubbing import ( def assert_no_logging(msg): - raise AssertionError(f'Should not have logged anything but logged {msg}') + raise AssertionError(f"Should not have logged anything but logged {msg}") def capture_logging(messages): @@ -34,76 +38,81 @@ def tar_file_name(tmpdir): Return the name of a non-existing tar file in an existing temporary directory. """ - return tmpdir.join('foo.tar') + return tmpdir.join("foo.tar") def test_archived_image_action_when_missing(tar_file_name): - fake_name = 'a:latest' - fake_id = 'a1' + fake_name = "a:latest" + fake_id = "a1" - expected = f'Archived image {fake_name} to {tar_file_name}, since none present' + expected = f"Archived image {fake_name} to {tar_file_name}, since none present" - actual = ImageManager.archived_image_action(assert_no_logging, tar_file_name, fake_name, api_image_id(fake_id)) + actual = ImageManager.archived_image_action( + assert_no_logging, tar_file_name, fake_name, api_image_id(fake_id) + ) assert actual == expected def test_archived_image_action_when_current(tar_file_name): - fake_name = 'b:latest' - fake_id = 'b2' + fake_name = "b:latest" + fake_id = "b2" write_imitation_archive(tar_file_name, fake_id, [fake_name]) - actual = ImageManager.archived_image_action(assert_no_logging, tar_file_name, fake_name, api_image_id(fake_id)) + actual = ImageManager.archived_image_action( + assert_no_logging, tar_file_name, fake_name, api_image_id(fake_id) + ) assert actual is None def test_archived_image_action_when_invalid(tar_file_name): - fake_name = 'c:1.2.3' - fake_id = 'c3' + fake_name = "c:1.2.3" + fake_id = "c3" write_irrelevant_tar(tar_file_name) - expected = f'Archived image {fake_name} to {tar_file_name}, overwriting an unreadable archive file' + expected = f"Archived image {fake_name} to {tar_file_name}, overwriting an unreadable archive file" actual_log = [] actual = ImageManager.archived_image_action( - capture_logging(actual_log), - tar_file_name, - fake_name, - api_image_id(fake_id) + capture_logging(actual_log), tar_file_name, fake_name, api_image_id(fake_id) ) assert actual == expected assert len(actual_log) == 1 - assert actual_log[0].startswith('Unable to extract manifest summary from archive') + assert actual_log[0].startswith("Unable to extract manifest summary from archive") def test_archived_image_action_when_obsolete_by_id(tar_file_name): - fake_name = 'd:0.0.1' - old_id = 'e5' - new_id = 'd4' + fake_name = "d:0.0.1" + old_id = "e5" + new_id = "d4" write_imitation_archive(tar_file_name, old_id, [fake_name]) - expected = f'Archived image {fake_name} to {tar_file_name}, overwriting archive with image {old_id} named {fake_name}' - actual = ImageManager.archived_image_action(assert_no_logging, tar_file_name, fake_name, api_image_id(new_id)) + expected = f"Archived image {fake_name} to {tar_file_name}, overwriting archive with image {old_id} named {fake_name}" + actual = ImageManager.archived_image_action( + assert_no_logging, tar_file_name, fake_name, api_image_id(new_id) + ) assert actual == expected def test_archived_image_action_when_obsolete_by_name(tar_file_name): - old_name = 'hi' - new_name = 'd:0.0.1' - fake_id = 'd4' + old_name = "hi" + new_name = "d:0.0.1" + fake_id = "d4" write_imitation_archive(tar_file_name, fake_id, [old_name]) - expected = f'Archived image {new_name} to {tar_file_name}, overwriting archive with image {fake_id} named {old_name}' - actual = ImageManager.archived_image_action(assert_no_logging, tar_file_name, new_name, api_image_id(fake_id)) + expected = f"Archived image {new_name} to {tar_file_name}, overwriting archive with image {fake_id} named {old_name}" + actual = ImageManager.archived_image_action( + assert_no_logging, tar_file_name, new_name, api_image_id(fake_id) + ) - print(f'actual : {actual}') - print(f'expected : {expected}') + print(f"actual : {actual}") + print(f"expected : {expected}") assert actual == expected diff --git a/tests/unit/plugins/modules/test_docker_image_build.py b/tests/unit/plugins/modules/test_docker_image_build.py index 72555458..900112d6 100644 --- a/tests/unit/plugins/modules/test_docker_image_build.py +++ b/tests/unit/plugins/modules/test_docker_image_build.py @@ -2,20 +2,26 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - -from ansible_collections.community.docker.plugins.modules.docker_image_build import _quote_csv +from ansible_collections.community.docker.plugins.modules.docker_image_build import ( + _quote_csv, +) -@pytest.mark.parametrize("input, expected", [ - ('', ''), - (' ', '" "'), - (',', '","'), - ('"', '""""'), - ('\rhello, "hi" !\n', '"\rhello, ""hi"" !\n"'), -]) +@pytest.mark.parametrize( + "input, expected", + [ + ("", ""), + (" ", '" "'), + (",", '","'), + ('"', '""""'), + ('\rhello, "hi" !\n', '"\rhello, ""hi"" !\n"'), + ], +) def test__quote_csv(input, expected): assert _quote_csv(input) == expected diff --git a/tests/unit/plugins/modules/test_docker_network.py b/tests/unit/plugins/modules/test_docker_network.py index 62da8313..ce1a5ad8 100644 --- a/tests/unit/plugins/modules/test_docker_network.py +++ b/tests/unit/plugins/modules/test_docker_network.py @@ -4,31 +4,40 @@ """Unit tests for docker_network.""" -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest - -from ansible_collections.community.docker.plugins.modules.docker_network import validate_cidr +from ansible_collections.community.docker.plugins.modules.docker_network import ( + validate_cidr, +) -@pytest.mark.parametrize("cidr,expected", [ - ('192.168.0.1/16', 'ipv4'), - ('192.168.0.1/24', 'ipv4'), - ('192.168.0.1/32', 'ipv4'), - ('fdd1:ac8c:0557:7ce2::/64', 'ipv6'), - ('fdd1:ac8c:0557:7ce2::/128', 'ipv6'), -]) +@pytest.mark.parametrize( + "cidr,expected", + [ + ("192.168.0.1/16", "ipv4"), + ("192.168.0.1/24", "ipv4"), + ("192.168.0.1/32", "ipv4"), + ("fdd1:ac8c:0557:7ce2::/64", "ipv6"), + ("fdd1:ac8c:0557:7ce2::/128", "ipv6"), + ], +) def test_validate_cidr_positives(cidr, expected): assert validate_cidr(cidr) == expected -@pytest.mark.parametrize("cidr", [ - '192.168.0.1', - '192.168.0.1/34', - '192.168.0.1/asd', - 'fdd1:ac8c:0557:7ce2::', -]) +@pytest.mark.parametrize( + "cidr", + [ + "192.168.0.1", + "192.168.0.1/34", + "192.168.0.1/asd", + "fdd1:ac8c:0557:7ce2::", + ], +) def test_validate_cidr_negatives(cidr): with pytest.raises(ValueError) as e: validate_cidr(cidr) diff --git a/tests/unit/plugins/modules/test_docker_swarm_service.py b/tests/unit/plugins/modules/test_docker_swarm_service.py index 9f2b27f4..aba652fb 100644 --- a/tests/unit/plugins/modules/test_docker_swarm_service.py +++ b/tests/unit/plugins/modules/test_docker_swarm_service.py @@ -2,7 +2,9 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import pytest @@ -22,16 +24,18 @@ def docker_module_mock(mocker): docker_errors_module_mock = mocker.MagicMock() docker_errors_module_mock.APIError = APIErrorMock mock_modules = { - 'docker': docker_module_mock, - 'docker.utils': docker_utils_module_mock, - 'docker.errors': docker_errors_module_mock, + "docker": docker_module_mock, + "docker.utils": docker_utils_module_mock, + "docker.errors": docker_errors_module_mock, } - return mocker.patch.dict('sys.modules', **mock_modules) + return mocker.patch.dict("sys.modules", **mock_modules) @pytest.fixture(autouse=True) def docker_swarm_service(): - from ansible_collections.community.docker.plugins.modules import docker_swarm_service + from ansible_collections.community.docker.plugins.modules import ( + docker_swarm_service, + ) return docker_swarm_service @@ -39,9 +43,9 @@ def docker_swarm_service(): def test_retry_on_out_of_sequence_error(mocker, docker_swarm_service): run_mock = mocker.MagicMock( side_effect=APIErrorMock( - message='', + message="", response=None, - explanation='rpc error: code = Unknown desc = update out of sequence', + explanation="rpc error: code = Unknown desc = update out of sequence", ) ) manager = docker_swarm_service.DockerServiceManager(client=None) @@ -53,7 +57,7 @@ def test_retry_on_out_of_sequence_error(mocker, docker_swarm_service): def test_no_retry_on_general_api_error(mocker, docker_swarm_service): run_mock = mocker.MagicMock( - side_effect=APIErrorMock(message='', response=None, explanation='some error') + side_effect=APIErrorMock(message="", response=None, explanation="some error") ) manager = docker_swarm_service.DockerServiceManager(client=None) manager.run = run_mock @@ -63,62 +67,57 @@ def test_no_retry_on_general_api_error(mocker, docker_swarm_service): def test_get_docker_environment(mocker, docker_swarm_service): - env_file_result = {'TEST1': 'A', 'TEST2': 'B', 'TEST3': 'C'} - env_dict = {'TEST3': 'CC', 'TEST4': 'D'} + env_file_result = {"TEST1": "A", "TEST2": "B", "TEST3": "C"} + env_dict = {"TEST3": "CC", "TEST4": "D"} env_string = "TEST3=CC,TEST4=D" - env_list = ['TEST3=CC', 'TEST4=D'] - expected_result = sorted(['TEST1=A', 'TEST2=B', 'TEST3=CC', 'TEST4=D']) + env_list = ["TEST3=CC", "TEST4=D"] + expected_result = sorted(["TEST1=A", "TEST2=B", "TEST3=CC", "TEST4=D"]) mocker.patch.object( - docker_swarm_service, 'parse_env_file', return_value=env_file_result + docker_swarm_service, "parse_env_file", return_value=env_file_result ) mocker.patch.object( docker_swarm_service, - 'format_environment', - side_effect=lambda d: [f'{key}={value}' for key, value in d.items()], + "format_environment", + side_effect=lambda d: [f"{key}={value}" for key, value in d.items()], ) # Test with env dict and file result = docker_swarm_service.get_docker_environment( - env_dict, env_files=['dummypath'] + env_dict, env_files=["dummypath"] ) assert result == expected_result # Test with env list and file result = docker_swarm_service.get_docker_environment( - env_list, - env_files=['dummypath'] + env_list, env_files=["dummypath"] ) assert result == expected_result # Test with env string and file result = docker_swarm_service.get_docker_environment( - env_string, env_files=['dummypath'] + env_string, env_files=["dummypath"] ) assert result == expected_result assert result == expected_result # Test with empty env - result = docker_swarm_service.get_docker_environment( - [], env_files=None - ) + result = docker_swarm_service.get_docker_environment([], env_files=None) assert result == [] # Test with empty env_files - result = docker_swarm_service.get_docker_environment( - None, env_files=[] - ) + result = docker_swarm_service.get_docker_environment(None, env_files=[]) assert result == [] def test_get_nanoseconds_from_raw_option(docker_swarm_service): - value = docker_swarm_service.get_nanoseconds_from_raw_option('test', None) + value = docker_swarm_service.get_nanoseconds_from_raw_option("test", None) assert value is None - value = docker_swarm_service.get_nanoseconds_from_raw_option('test', '1m30s535ms') + value = docker_swarm_service.get_nanoseconds_from_raw_option("test", "1m30s535ms") assert value == 90535000000 - value = docker_swarm_service.get_nanoseconds_from_raw_option('test', 10000000000) + value = docker_swarm_service.get_nanoseconds_from_raw_option("test", 10000000000) assert value == 10000000000 with pytest.raises(ValueError): - docker_swarm_service.get_nanoseconds_from_raw_option('test', []) + docker_swarm_service.get_nanoseconds_from_raw_option("test", []) def test_has_dict_changed(docker_swarm_service): @@ -126,50 +125,17 @@ def test_has_dict_changed(docker_swarm_service): {"a": 1}, {"a": 1}, ) - assert not docker_swarm_service.has_dict_changed( - {"a": 1}, - {"a": 1, "b": 2} - ) - assert docker_swarm_service.has_dict_changed( - {"a": 1}, - {"a": 2, "b": 2} - ) - assert docker_swarm_service.has_dict_changed( - {"a": 1, "b": 1}, - {"a": 1} - ) - assert not docker_swarm_service.has_dict_changed( - None, - {"a": 2, "b": 2} - ) - assert docker_swarm_service.has_dict_changed( - {}, - {"a": 2, "b": 2} - ) - assert docker_swarm_service.has_dict_changed( - {"a": 1}, - {} - ) - assert docker_swarm_service.has_dict_changed( - {"a": 1}, - None - ) - assert not docker_swarm_service.has_dict_changed( - {}, - {} - ) - assert not docker_swarm_service.has_dict_changed( - None, - None - ) - assert not docker_swarm_service.has_dict_changed( - {}, - None - ) - assert not docker_swarm_service.has_dict_changed( - None, - {} - ) + assert not docker_swarm_service.has_dict_changed({"a": 1}, {"a": 1, "b": 2}) + assert docker_swarm_service.has_dict_changed({"a": 1}, {"a": 2, "b": 2}) + assert docker_swarm_service.has_dict_changed({"a": 1, "b": 1}, {"a": 1}) + assert not docker_swarm_service.has_dict_changed(None, {"a": 2, "b": 2}) + assert docker_swarm_service.has_dict_changed({}, {"a": 2, "b": 2}) + assert docker_swarm_service.has_dict_changed({"a": 1}, {}) + assert docker_swarm_service.has_dict_changed({"a": 1}, None) + assert not docker_swarm_service.has_dict_changed({}, {}) + assert not docker_swarm_service.has_dict_changed(None, None) + assert not docker_swarm_service.has_dict_changed({}, None) + assert not docker_swarm_service.has_dict_changed(None, {}) def test_has_list_changed(docker_swarm_service): @@ -192,65 +158,45 @@ def test_has_list_changed(docker_swarm_service): # Check list sorting assert not docker_swarm_service.has_list_changed([1, 2], [2, 1]) - assert docker_swarm_service.has_list_changed( - [1, 2], - [2, 1], - sort_lists=False - ) + assert docker_swarm_service.has_list_changed([1, 2], [2, 1], sort_lists=False) # Check type matching assert docker_swarm_service.has_list_changed([None, 1], [2, 1]) assert docker_swarm_service.has_list_changed([2, 1], [None, 1]) assert docker_swarm_service.has_list_changed( - "command --with args", - ['command', '--with', 'args'] + "command --with args", ["command", "--with", "args"] ) assert docker_swarm_service.has_list_changed( - ['sleep', '3400'], - ['sleep', '3600'], - sort_lists=False + ["sleep", "3400"], ["sleep", "3600"], sort_lists=False ) # List comparisons with dictionaries assert not docker_swarm_service.has_list_changed( - [{'a': 1}], - [{'a': 1}], - sort_key='a' + [{"a": 1}], [{"a": 1}], sort_key="a" ) assert not docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}], - [{'a': 1}, {'a': 2}], - sort_key='a' + [{"a": 1}, {"a": 2}], [{"a": 1}, {"a": 2}], sort_key="a" ) with pytest.raises(Exception): docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}], - [{'a': 1}, {'a': 2}] + [{"a": 1}, {"a": 2}], [{"a": 1}, {"a": 2}] ) # List sort checking with sort key assert not docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}], - [{'a': 2}, {'a': 1}], - sort_key='a' + [{"a": 1}, {"a": 2}], [{"a": 2}, {"a": 1}], sort_key="a" ) assert docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}], - [{'a': 2}, {'a': 1}], - sort_lists=False + [{"a": 1}, {"a": 2}], [{"a": 2}, {"a": 1}], sort_lists=False ) assert docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}, {'a': 3}], - [{'a': 2}, {'a': 1}], - sort_key='a' + [{"a": 1}, {"a": 2}, {"a": 3}], [{"a": 2}, {"a": 1}], sort_key="a" ) assert docker_swarm_service.has_list_changed( - [{'a': 1}, {'a': 2}], - [{'a': 1}, {'a': 2}, {'a': 3}], - sort_lists=False + [{"a": 1}, {"a": 2}], [{"a": 1}, {"a": 2}, {"a": 3}], sort_lists=False ) # Additional dictionary elements @@ -263,7 +209,7 @@ def test_has_list_changed(docker_swarm_service): {"src": 1, "dst": 2, "protocol": "tcp"}, {"src": 1, "dst": 2, "protocol": "udp"}, ], - sort_key='dst' + sort_key="dst", ) assert not docker_swarm_service.has_list_changed( [ @@ -274,7 +220,7 @@ def test_has_list_changed(docker_swarm_service): {"src": 1, "dst": 2, "protocol": "udp"}, {"src": 1, "dst": 3, "protocol": "tcp"}, ], - sort_key='dst' + sort_key="dst", ) assert docker_swarm_service.has_list_changed( [ @@ -287,7 +233,7 @@ def test_has_list_changed(docker_swarm_service): {"src": 1, "dst": 2, "protocol": "tcp"}, {"src": 3, "dst": 4, "protocol": "tcp"}, ], - sort_key='dst' + sort_key="dst", ) assert docker_swarm_service.has_list_changed( [ @@ -298,7 +244,7 @@ def test_has_list_changed(docker_swarm_service): {"src": 1, "dst": 2, "protocol": "tcp"}, {"src": 1, "dst": 2, "protocol": "udp"}, ], - sort_key='dst' + sort_key="dst", ) assert docker_swarm_service.has_list_changed( [ @@ -309,206 +255,148 @@ def test_has_list_changed(docker_swarm_service): {"src": 1, "dst": 2, "protocol": "udp"}, {"src": 1, "dst": 2, "protocol": "tcp"}, ], - sort_key='dst' + sort_key="dst", ) assert not docker_swarm_service.has_list_changed( - [{'id': '123', 'aliases': []}], - [{'id': '123'}], - sort_key='id' + [{"id": "123", "aliases": []}], [{"id": "123"}], sort_key="id" ) def test_have_networks_changed(docker_swarm_service): - assert not docker_swarm_service.have_networks_changed( - None, - None + assert not docker_swarm_service.have_networks_changed(None, None) + + assert not docker_swarm_service.have_networks_changed([], None) + + assert not docker_swarm_service.have_networks_changed([{"id": 1}], [{"id": 1}]) + + assert docker_swarm_service.have_networks_changed( + [{"id": 1}], [{"id": 1}, {"id": 2}] ) assert not docker_swarm_service.have_networks_changed( - [], - None + [{"id": 1}, {"id": 2}], [{"id": 1}, {"id": 2}] ) assert not docker_swarm_service.have_networks_changed( - [{'id': 1}], - [{'id': 1}] + [{"id": 1}, {"id": 2}], [{"id": 2}, {"id": 1}] + ) + + assert not docker_swarm_service.have_networks_changed( + [{"id": 1}, {"id": 2, "aliases": []}], [{"id": 1}, {"id": 2}] ) assert docker_swarm_service.have_networks_changed( - [{'id': 1}], - [{'id': 1}, {'id': 2}] + [{"id": 1}, {"id": 2, "aliases": ["alias1"]}], [{"id": 1}, {"id": 2}] + ) + + assert docker_swarm_service.have_networks_changed( + [{"id": 1}, {"id": 2, "aliases": ["alias1", "alias2"]}], + [{"id": 1}, {"id": 2, "aliases": ["alias1"]}], ) assert not docker_swarm_service.have_networks_changed( - [{'id': 1}, {'id': 2}], - [{'id': 1}, {'id': 2}] + [{"id": 1}, {"id": 2, "aliases": ["alias1", "alias2"]}], + [{"id": 1}, {"id": 2, "aliases": ["alias1", "alias2"]}], ) assert not docker_swarm_service.have_networks_changed( - [{'id': 1}, {'id': 2}], - [{'id': 2}, {'id': 1}] + [{"id": 1}, {"id": 2, "aliases": ["alias1", "alias2"]}], + [{"id": 1}, {"id": 2, "aliases": ["alias2", "alias1"]}], + ) + + assert not docker_swarm_service.have_networks_changed( + [{"id": 1, "options": {}}, {"id": 2, "aliases": ["alias1", "alias2"]}], + [{"id": 1}, {"id": 2, "aliases": ["alias2", "alias1"]}], ) assert not docker_swarm_service.have_networks_changed( [ - {'id': 1}, - {'id': 2, 'aliases': []} + {"id": 1, "options": {"option1": "value1"}}, + {"id": 2, "aliases": ["alias1", "alias2"]}, ], [ - {'id': 1}, - {'id': 2} - ] + {"id": 1, "options": {"option1": "value1"}}, + {"id": 2, "aliases": ["alias2", "alias1"]}, + ], ) assert docker_swarm_service.have_networks_changed( [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1']} + {"id": 1, "options": {"option1": "value1"}}, + {"id": 2, "aliases": ["alias1", "alias2"]}, ], [ - {'id': 1}, - {'id': 2} - ] - ) - - assert docker_swarm_service.have_networks_changed( - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1', 'alias2']} + {"id": 1, "options": {"option1": "value2"}}, + {"id": 2, "aliases": ["alias2", "alias1"]}, ], - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1']} - ] - ) - - assert not docker_swarm_service.have_networks_changed( - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1', 'alias2']} - ], - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1', 'alias2']} - ] - ) - - assert not docker_swarm_service.have_networks_changed( - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias1', 'alias2']} - ], - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias2', 'alias1']} - ] - ) - - assert not docker_swarm_service.have_networks_changed( - [ - {'id': 1, 'options': {}}, - {'id': 2, 'aliases': ['alias1', 'alias2']}], - [ - {'id': 1}, - {'id': 2, 'aliases': ['alias2', 'alias1']} - ] - ) - - assert not docker_swarm_service.have_networks_changed( - [ - {'id': 1, 'options': {'option1': 'value1'}}, - {'id': 2, 'aliases': ['alias1', 'alias2']}], - [ - {'id': 1, 'options': {'option1': 'value1'}}, - {'id': 2, 'aliases': ['alias2', 'alias1']} - ] - ) - - assert docker_swarm_service.have_networks_changed( - [ - {'id': 1, 'options': {'option1': 'value1'}}, - {'id': 2, 'aliases': ['alias1', 'alias2']}], - [ - {'id': 1, 'options': {'option1': 'value2'}}, - {'id': 2, 'aliases': ['alias2', 'alias1']} - ] ) def test_get_docker_networks(docker_swarm_service): network_names = [ - 'network_1', - 'network_2', - 'network_3', - 'network_4', + "network_1", + "network_2", + "network_3", + "network_4", ] networks = [ network_names[0], - {'name': network_names[1]}, - {'name': network_names[2], 'aliases': ['networkalias1']}, - {'name': network_names[3], 'aliases': ['networkalias2'], 'options': {'foo': 'bar'}}, + {"name": network_names[1]}, + {"name": network_names[2], "aliases": ["networkalias1"]}, + { + "name": network_names[3], + "aliases": ["networkalias2"], + "options": {"foo": "bar"}, + }, ] network_ids = { - network_names[0]: '1', - network_names[1]: '2', - network_names[2]: '3', - network_names[3]: '4', + network_names[0]: "1", + network_names[1]: "2", + network_names[2]: "3", + network_names[3]: "4", } - parsed_networks = docker_swarm_service.get_docker_networks( - networks, - network_ids - ) + parsed_networks = docker_swarm_service.get_docker_networks(networks, network_ids) assert len(parsed_networks) == 4 for i, network in enumerate(parsed_networks): - assert 'name' not in network - assert 'id' in network + assert "name" not in network + assert "id" in network expected_name = network_names[i] - assert network['id'] == network_ids[expected_name] + assert network["id"] == network_ids[expected_name] if i == 2: - assert network['aliases'] == ['networkalias1'] + assert network["aliases"] == ["networkalias1"] if i == 3: - assert network['aliases'] == ['networkalias2'] + assert network["aliases"] == ["networkalias2"] if i == 3: - assert 'foo' in network['options'] + assert "foo" in network["options"] # Test missing name with pytest.raises(TypeError): - docker_swarm_service.get_docker_networks([{'invalid': 'err'}], {'err': 1}) + docker_swarm_service.get_docker_networks([{"invalid": "err"}], {"err": 1}) # test for invalid aliases type with pytest.raises(TypeError): docker_swarm_service.get_docker_networks( - [{'name': 'test', 'aliases': 1}], - {'test': 1} + [{"name": "test", "aliases": 1}], {"test": 1} ) # Test invalid aliases elements with pytest.raises(TypeError): docker_swarm_service.get_docker_networks( - [{'name': 'test', 'aliases': [1]}], - {'test': 1} + [{"name": "test", "aliases": [1]}], {"test": 1} ) # Test for invalid options type with pytest.raises(TypeError): docker_swarm_service.get_docker_networks( - [{'name': 'test', 'options': 1}], - {'test': 1} + [{"name": "test", "options": 1}], {"test": 1} ) # Test for invalid networks type with pytest.raises(TypeError): - docker_swarm_service.get_docker_networks( - 1, - {'test': 1} - ) + docker_swarm_service.get_docker_networks(1, {"test": 1}) # Test for non existing networks with pytest.raises(ValueError): - docker_swarm_service.get_docker_networks( - [{'name': 'idontexist'}], - {'test': 1} - ) + docker_swarm_service.get_docker_networks([{"name": "idontexist"}], {"test": 1}) # Test empty values assert docker_swarm_service.get_docker_networks([], {}) == [] assert docker_swarm_service.get_docker_networks(None, {}) is None # Test invalid options with pytest.raises(TypeError): docker_swarm_service.get_docker_networks( - [{'name': 'test', 'nonexisting_option': 'foo'}], - {'test': '1'} + [{"name": "test", "nonexisting_option": "foo"}], {"test": "1"} ) diff --git a/tests/unit/plugins/plugin_utils/test_unsafe.py b/tests/unit/plugins/plugin_utils/test_unsafe.py index d9021ece..b79022ed 100644 --- a/tests/unit/plugins/plugin_utils/test_unsafe.py +++ b/tests/unit/plugins/plugin_utils/test_unsafe.py @@ -5,31 +5,34 @@ from __future__ import annotations - import pytest - -from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( - make_untrusted as _make_untrusted, - make_trusted as _make_trusted, - is_trusted as _is_trusted, - SUPPORTS_DATA_TAGGING, -) - from ansible_collections.community.docker.plugins.plugin_utils.unsafe import ( make_unsafe, ) +from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( + SUPPORTS_DATA_TAGGING, +) +from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( + is_trusted as _is_trusted, +) +from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( + make_trusted as _make_trusted, +) +from ansible_collections.community.internal_test_tools.tests.unit.utils.trust import ( + make_untrusted as _make_untrusted, +) TEST_MAKE_UNSAFE = [ ( - _make_trusted('text'), + _make_trusted("text"), [], [ (), ], ), ( - _make_trusted('{{text}}'), + _make_trusted("{{text}}"), [ (), ], @@ -37,59 +40,63 @@ TEST_MAKE_UNSAFE = [ ), ( { - _make_trusted('skey'): _make_trusted('value'), - _make_trusted('ukey'): _make_trusted('{{value}}'), + _make_trusted("skey"): _make_trusted("value"), + _make_trusted("ukey"): _make_trusted("{{value}}"), 1: [ - _make_trusted('value'), - _make_trusted('{{value}}'), + _make_trusted("value"), + _make_trusted("{{value}}"), { - 1.0: _make_trusted('{{value}}'), - 2.0: _make_trusted('value'), + 1.0: _make_trusted("{{value}}"), + 2.0: _make_trusted("value"), }, ], }, [ - ('ukey', ), + ("ukey",), (1, 1), (1, 2, 1.0), ], [ - ('skey', ), + ("skey",), (1, 0), (1, 2, 2.0), ], ), ( - [_make_trusted('value'), _make_trusted('{{value}}')], + [_make_trusted("value"), _make_trusted("{{value}}")], [ - (1, ), + (1,), ], [ - (0, ), + (0,), ], ), ] if not SUPPORTS_DATA_TAGGING: - TEST_MAKE_UNSAFE.extend([ - ( - _make_trusted(b"text"), - [], - [ - (), - ], - ), - ( - _make_trusted(b"{{text}}"), - [ - (), - ], - [], - ), - ]) + TEST_MAKE_UNSAFE.extend( + [ + ( + _make_trusted(b"text"), + [], + [ + (), + ], + ), + ( + _make_trusted(b"{{text}}"), + [ + (), + ], + [], + ), + ] + ) -@pytest.mark.parametrize("value, check_unsafe_paths, check_safe_paths", TEST_MAKE_UNSAFE) +@pytest.mark.parametrize( + "value, check_unsafe_paths, check_safe_paths", TEST_MAKE_UNSAFE +) def test_make_unsafe(value, check_unsafe_paths, check_safe_paths): unsafe_value = make_unsafe(value) assert unsafe_value == value @@ -108,16 +115,16 @@ def test_make_unsafe(value, check_unsafe_paths, check_safe_paths): def test_make_unsafe_idempotence(): assert make_unsafe(None) is None - unsafe_str = _make_untrusted('{{test}}') + unsafe_str = _make_untrusted("{{test}}") assert id(make_unsafe(unsafe_str)) == id(unsafe_str) - safe_str = _make_trusted('{{test}}') + safe_str = _make_trusted("{{test}}") assert id(make_unsafe(safe_str)) != id(safe_str) def test_make_unsafe_dict_key(): value = { - _make_trusted('test'): 2, + _make_trusted("test"): 2, } if not SUPPORTS_DATA_TAGGING: value[_make_trusted(b"test")] = 1 @@ -127,7 +134,7 @@ def test_make_unsafe_dict_key(): assert _is_trusted(obj) value = { - _make_trusted('{{test}}'): 2, + _make_trusted("{{test}}"): 2, } if not SUPPORTS_DATA_TAGGING: value[_make_trusted(b"{{test}}")] = 1 @@ -138,7 +145,7 @@ def test_make_unsafe_dict_key(): def test_make_unsafe_set(): - value = set([_make_trusted('test')]) + value = set([_make_trusted("test")]) if not SUPPORTS_DATA_TAGGING: value.add(_make_trusted(b"test")) unsafe_value = make_unsafe(value) @@ -146,7 +153,7 @@ def test_make_unsafe_set(): for obj in unsafe_value: assert _is_trusted(obj) - value = set([_make_trusted('{{test}}')]) + value = set([_make_trusted("{{test}}")]) if not SUPPORTS_DATA_TAGGING: value.add(_make_trusted(b"{{test}}")) unsafe_value = make_unsafe(value) diff --git a/tests/unit/plugins/test_support/docker_image_archive_stubbing.py b/tests/unit/plugins/test_support/docker_image_archive_stubbing.py index cbfee043..76d3d2ea 100644 --- a/tests/unit/plugins/test_support/docker_image_archive_stubbing.py +++ b/tests/unit/plugins/test_support/docker_image_archive_stubbing.py @@ -2,7 +2,9 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + + __metaclass__ = type import json @@ -11,7 +13,7 @@ from tempfile import TemporaryFile def write_imitation_archive(file_name, image_id, repo_tags): - ''' + """ Write a tar file meeting these requirements: * Has a file manifest.json @@ -24,24 +26,19 @@ def write_imitation_archive(file_name, image_id, repo_tags): :type image_id: str :param repo_tags: list of fake image:tag's :type repo_tags: list - ''' + """ - manifest = [ - { - 'Config': f'{image_id}.json', - 'RepoTags': repo_tags - } - ] + manifest = [{"Config": f"{image_id}.json", "RepoTags": repo_tags}] write_imitation_archive_with_manifest(file_name, manifest) def write_imitation_archive_with_manifest(file_name, manifest): - with tarfile.open(file_name, 'w') as tf: + with tarfile.open(file_name, "w") as tf: with TemporaryFile() as f: - f.write(json.dumps(manifest).encode('utf-8')) + f.write(json.dumps(manifest).encode("utf-8")) - ti = tarfile.TarInfo('manifest.json') + ti = tarfile.TarInfo("manifest.json") ti.size = f.tell() f.seek(0) @@ -49,19 +46,19 @@ def write_imitation_archive_with_manifest(file_name, manifest): def write_irrelevant_tar(file_name): - ''' + """ Create a tar file that does not match the spec for "docker image save" / "docker image load" commands. :param file_name: Name of tar file to create :type file_name: str - ''' + """ - tf = tarfile.open(file_name, 'w') + tf = tarfile.open(file_name, "w") try: with TemporaryFile() as f: - f.write('Hello, world.'.encode('utf-8')) + f.write("Hello, world.".encode("utf-8")) - ti = tarfile.TarInfo('hi.txt') + ti = tarfile.TarInfo("hi.txt") ti.size = f.tell() f.seek(0)