From d984afa5baede3f532a870647f601a39551321d5 Mon Sep 17 00:00:00 2001 From: skylerbunny Date: Tue, 6 Mar 2018 04:14:31 -0800 Subject: [PATCH] Add compatibility for docker-py version 3 (#36973) --- lib/ansible/module_utils/docker_common.py | 8 +++++- .../modules/cloud/docker/docker_container.py | 26 +++++++++++++------ .../modules/cloud/docker/docker_image.py | 15 +++++++---- .../modules/cloud/docker/docker_network.py | 8 +++--- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/ansible/module_utils/docker_common.py b/lib/ansible/module_utils/docker_common.py index 37e1e0fbfaf..76a07a22c01 100644 --- a/lib/ansible/module_utils/docker_common.py +++ b/lib/ansible/module_utils/docker_common.py @@ -29,6 +29,7 @@ from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE, BOOLEANS_FA HAS_DOCKER_PY = True HAS_DOCKER_PY_2 = False +HAS_DOCKER_PY_3 = False HAS_DOCKER_ERROR = None try: @@ -38,7 +39,12 @@ try: from docker.tls import TLSConfig from docker.constants import DEFAULT_TIMEOUT_SECONDS, DEFAULT_DOCKER_API_VERSION from docker import auth - if LooseVersion(docker_version) >= LooseVersion('2.0.0'): + + if LooseVersion(docker_version) >= LooseVersion('3.0.0'): + HAS_DOCKER_PY_3 = True + from docker import APIClient as Client + from docker.types import Ulimit, LogConfig + elif LooseVersion(docker_version) >= LooseVersion('2.0.0'): HAS_DOCKER_PY_2 = True from docker import APIClient as Client from docker.types import Ulimit, LogConfig diff --git a/lib/ansible/modules/cloud/docker/docker_container.py b/lib/ansible/modules/cloud/docker/docker_container.py index 20ad63ca0ad..f9c7f32044c 100644 --- a/lib/ansible/modules/cloud/docker/docker_container.py +++ b/lib/ansible/modules/cloud/docker/docker_container.py @@ -688,12 +688,12 @@ import re import shlex from ansible.module_utils.basic import human_to_bytes -from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, AnsibleDockerClient, DockerBaseClass +from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, HAS_DOCKER_PY_3, AnsibleDockerClient, DockerBaseClass from ansible.module_utils.six import string_types try: from docker import utils - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: from docker.types import Ulimit, LogConfig else: from docker.utils.types import Ulimit, LogConfig @@ -894,14 +894,16 @@ class TaskParameters(DockerBaseClass): environment='env', name='name', entrypoint='entrypoint', - cpu_shares='cpu_shares', mac_address='mac_address', labels='labels', stop_signal='stop_signal', - volume_driver='volume_driver', working_dir='working_dir', ) + if not HAS_DOCKER_PY_3: + create_params['cpu_shares'] = 'cpu_shares' + create_params['volume_driver'] = 'volume_driver' + result = dict( host_config=self._host_config(), volumes=self._get_mounts(), @@ -990,10 +992,15 @@ class TaskParameters(DockerBaseClass): tmpfs='tmpfs' ) - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: # auto_remove is only supported in docker>=2 host_config_params['auto_remove'] = 'auto_remove' + if HAS_DOCKER_PY_3: + # cpu_shares and volume_driver moved to create_host_config in > 3 + host_config_params['cpu_shares'] = 'cpu_shares' + host_config_params['volume_driver'] = 'volume_driver' + params = dict() for key, value in host_config_params.items(): if getattr(self, value, None) is not None: @@ -1973,7 +1980,10 @@ class ContainerManager(DockerBaseClass): self.fail("Error starting container %s: %s" % (container_id, str(exc))) if not self.parameters.detach: - status = self.client.wait(container_id) + if HAS_DOCKER_PY_3: + status = self.client.wait(container_id)['StatusCode'] + else: + status = self.client.wait(container_id) config = self.client.inspect_container(container_id) logging_driver = config['HostConfig']['LogConfig']['Type'] @@ -2141,8 +2151,8 @@ def main(): supports_check_mode=True ) - if not HAS_DOCKER_PY_2 and client.module.params.get('auto_remove'): - client.module.fail_json(msg="'auto_remove' is not compatible with docker-py, and requires the docker python module") + if (not (HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3)) and client.module.params.get('auto_remove'): + client.module.fail_json(msg="'auto_remove' is not compatible with the 'docker-py' Python package. It requires the newer 'docker' Python package.") cm = ContainerManager(client) client.module.exit_json(**cm.results) diff --git a/lib/ansible/modules/cloud/docker/docker_image.py b/lib/ansible/modules/cloud/docker/docker_image.py index c2e2d78cd13..19f4574399c 100644 --- a/lib/ansible/modules/cloud/docker/docker_image.py +++ b/lib/ansible/modules/cloud/docker/docker_image.py @@ -243,11 +243,11 @@ image: import os import re -from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, AnsibleDockerClient, DockerBaseClass +from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, HAS_DOCKER_PY_3, AnsibleDockerClient, DockerBaseClass from ansible.module_utils._text import to_native try: - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: from docker.auth import resolve_repository_name else: from docker.auth.auth import resolve_repository_name @@ -399,8 +399,12 @@ class ImageManager(DockerBaseClass): try: with open(self.archive_path, 'w') as fd: - for chunk in image.stream(2048, decode_content=False): - fd.write(chunk) + if HAS_DOCKER_PY_3: + for chunk in image: + fd.write(chunk) + else: + for chunk in image.stream(2048, decode_content=False): + fd.write(chunk) except Exception as exc: self.fail("Error writing image archive %s - %s" % (self.archive_path, str(exc))) @@ -500,13 +504,14 @@ class ImageManager(DockerBaseClass): tag=self.name, rm=self.rm, nocache=self.nocache, - stream=True, timeout=self.http_timeout, pull=self.pull, forcerm=self.rm, dockerfile=self.dockerfile, decode=True ) + if not HAS_DOCKER_PY_3: + params['stream'] = True build_output = [] if self.tag: params['tag'] = "%s:%s" % (self.name, self.tag) diff --git a/lib/ansible/modules/cloud/docker/docker_network.py b/lib/ansible/modules/cloud/docker/docker_network.py index e4a99b45dbc..ca0d63e872a 100644 --- a/lib/ansible/modules/cloud/docker/docker_network.py +++ b/lib/ansible/modules/cloud/docker/docker_network.py @@ -151,11 +151,11 @@ facts: sample: {} ''' -from ansible.module_utils.docker_common import AnsibleDockerClient, DockerBaseClass, HAS_DOCKER_PY_2 +from ansible.module_utils.docker_common import AnsibleDockerClient, DockerBaseClass, HAS_DOCKER_PY_2, HAS_DOCKER_PY_3 try: from docker import utils - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: from docker.types import IPAMPool, IPAMConfig except: # missing docker-py handled in ansible.module_utils.docker @@ -269,12 +269,12 @@ class DockerNetworkManager(object): if not self.existing_network: ipam_pools = [] if self.parameters.ipam_options: - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: ipam_pools.append(IPAMPool(**self.parameters.ipam_options)) else: ipam_pools.append(utils.create_ipam_pool(**self.parameters.ipam_options)) - if HAS_DOCKER_PY_2: + if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: ipam_config = IPAMConfig(driver=self.parameters.ipam_driver, pool_configs=ipam_pools) else: