Add compatibility for docker-py version 3 (#36973)

This commit is contained in:
skylerbunny 2018-03-06 04:14:31 -08:00 committed by Chris Houseknecht
commit d984afa5ba
4 changed files with 39 additions and 18 deletions

View file

@ -29,6 +29,7 @@ from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE, BOOLEANS_FA
HAS_DOCKER_PY = True HAS_DOCKER_PY = True
HAS_DOCKER_PY_2 = False HAS_DOCKER_PY_2 = False
HAS_DOCKER_PY_3 = False
HAS_DOCKER_ERROR = None HAS_DOCKER_ERROR = None
try: try:
@ -38,7 +39,12 @@ try:
from docker.tls import TLSConfig from docker.tls import TLSConfig
from docker.constants import DEFAULT_TIMEOUT_SECONDS, DEFAULT_DOCKER_API_VERSION from docker.constants import DEFAULT_TIMEOUT_SECONDS, DEFAULT_DOCKER_API_VERSION
from docker import auth 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 HAS_DOCKER_PY_2 = True
from docker import APIClient as Client from docker import APIClient as Client
from docker.types import Ulimit, LogConfig from docker.types import Ulimit, LogConfig

View file

@ -688,12 +688,12 @@ import re
import shlex import shlex
from ansible.module_utils.basic import human_to_bytes 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 from ansible.module_utils.six import string_types
try: try:
from docker import utils 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 from docker.types import Ulimit, LogConfig
else: else:
from docker.utils.types import Ulimit, LogConfig from docker.utils.types import Ulimit, LogConfig
@ -894,14 +894,16 @@ class TaskParameters(DockerBaseClass):
environment='env', environment='env',
name='name', name='name',
entrypoint='entrypoint', entrypoint='entrypoint',
cpu_shares='cpu_shares',
mac_address='mac_address', mac_address='mac_address',
labels='labels', labels='labels',
stop_signal='stop_signal', stop_signal='stop_signal',
volume_driver='volume_driver',
working_dir='working_dir', working_dir='working_dir',
) )
if not HAS_DOCKER_PY_3:
create_params['cpu_shares'] = 'cpu_shares'
create_params['volume_driver'] = 'volume_driver'
result = dict( result = dict(
host_config=self._host_config(), host_config=self._host_config(),
volumes=self._get_mounts(), volumes=self._get_mounts(),
@ -990,10 +992,15 @@ class TaskParameters(DockerBaseClass):
tmpfs='tmpfs' 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 # auto_remove is only supported in docker>=2
host_config_params['auto_remove'] = 'auto_remove' 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() params = dict()
for key, value in host_config_params.items(): for key, value in host_config_params.items():
if getattr(self, value, None) is not None: if getattr(self, value, None) is not None:
@ -1973,6 +1980,9 @@ class ContainerManager(DockerBaseClass):
self.fail("Error starting container %s: %s" % (container_id, str(exc))) self.fail("Error starting container %s: %s" % (container_id, str(exc)))
if not self.parameters.detach: if not self.parameters.detach:
if HAS_DOCKER_PY_3:
status = self.client.wait(container_id)['StatusCode']
else:
status = self.client.wait(container_id) status = self.client.wait(container_id)
config = self.client.inspect_container(container_id) config = self.client.inspect_container(container_id)
logging_driver = config['HostConfig']['LogConfig']['Type'] logging_driver = config['HostConfig']['LogConfig']['Type']
@ -2141,8 +2151,8 @@ def main():
supports_check_mode=True supports_check_mode=True
) )
if not HAS_DOCKER_PY_2 and client.module.params.get('auto_remove'): 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 docker-py, and requires the docker python module") 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) cm = ContainerManager(client)
client.module.exit_json(**cm.results) client.module.exit_json(**cm.results)

View file

@ -243,11 +243,11 @@ image:
import os import os
import re 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 from ansible.module_utils._text import to_native
try: try:
if HAS_DOCKER_PY_2: if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3:
from docker.auth import resolve_repository_name from docker.auth import resolve_repository_name
else: else:
from docker.auth.auth import resolve_repository_name from docker.auth.auth import resolve_repository_name
@ -399,6 +399,10 @@ class ImageManager(DockerBaseClass):
try: try:
with open(self.archive_path, 'w') as fd: with open(self.archive_path, 'w') as fd:
if HAS_DOCKER_PY_3:
for chunk in image:
fd.write(chunk)
else:
for chunk in image.stream(2048, decode_content=False): for chunk in image.stream(2048, decode_content=False):
fd.write(chunk) fd.write(chunk)
except Exception as exc: except Exception as exc:
@ -500,13 +504,14 @@ class ImageManager(DockerBaseClass):
tag=self.name, tag=self.name,
rm=self.rm, rm=self.rm,
nocache=self.nocache, nocache=self.nocache,
stream=True,
timeout=self.http_timeout, timeout=self.http_timeout,
pull=self.pull, pull=self.pull,
forcerm=self.rm, forcerm=self.rm,
dockerfile=self.dockerfile, dockerfile=self.dockerfile,
decode=True decode=True
) )
if not HAS_DOCKER_PY_3:
params['stream'] = True
build_output = [] build_output = []
if self.tag: if self.tag:
params['tag'] = "%s:%s" % (self.name, self.tag) params['tag'] = "%s:%s" % (self.name, self.tag)

View file

@ -151,11 +151,11 @@ facts:
sample: {} 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: try:
from docker import utils 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 from docker.types import IPAMPool, IPAMConfig
except: except:
# missing docker-py handled in ansible.module_utils.docker # missing docker-py handled in ansible.module_utils.docker
@ -269,12 +269,12 @@ class DockerNetworkManager(object):
if not self.existing_network: if not self.existing_network:
ipam_pools = [] ipam_pools = []
if self.parameters.ipam_options: 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)) ipam_pools.append(IPAMPool(**self.parameters.ipam_options))
else: else:
ipam_pools.append(utils.create_ipam_pool(**self.parameters.ipam_options)) 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, ipam_config = IPAMConfig(driver=self.parameters.ipam_driver,
pool_configs=ipam_pools) pool_configs=ipam_pools)
else: else: