From 32e8251f86d53e244c38b8725e5dab164aecd196 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 18 Oct 2018 11:51:58 +0200 Subject: [PATCH] docker_* modules: unify docker module version checks (#47046) * Unify docker module version checks. * Improve messages depending on required docker-py versions. * Linting. --- lib/ansible/module_utils/docker_common.py | 33 +++++++++++++++---- .../modules/cloud/docker/docker_container.py | 3 +- .../modules/cloud/docker/docker_image.py | 1 + .../cloud/docker/docker_image_facts.py | 3 +- .../modules/cloud/docker/docker_login.py | 3 +- .../modules/cloud/docker/docker_network.py | 3 +- .../modules/cloud/docker/docker_secret.py | 4 ++- .../modules/cloud/docker/docker_service.py | 3 +- .../modules/cloud/docker/docker_swarm.py | 4 ++- .../cloud/docker/docker_swarm_service.py | 23 +++++-------- .../modules/cloud/docker/docker_volume.py | 4 ++- 11 files changed, 55 insertions(+), 29 deletions(-) diff --git a/lib/ansible/module_utils/docker_common.py b/lib/ansible/module_utils/docker_common.py index 6a4bd63e04a..5a83d678a0c 100644 --- a/lib/ansible/module_utils/docker_common.py +++ b/lib/ansible/module_utils/docker_common.py @@ -157,7 +157,8 @@ class DockerBaseClass(object): class AnsibleDockerClient(Client): def __init__(self, argument_spec=None, supports_check_mode=False, mutually_exclusive=None, - required_together=None, required_if=None): + required_together=None, required_if=None, min_docker_version=MIN_DOCKER_VERSION, + min_docker_api_version=None): merged_arg_spec = dict() merged_arg_spec.update(DOCKER_COMMON_ARGS) @@ -182,6 +183,8 @@ class AnsibleDockerClient(Client): required_together=required_together_params, required_if=required_if) + NEEDS_DOCKER_PY2 = (LooseVersion(min_docker_version) < LooseVersion('2.0')) + if HAS_DOCKER_MODELS and HAS_DOCKER_SSLADAPTER: self.fail("Cannot have both the docker-py and docker python modules 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 python " @@ -189,11 +192,24 @@ class AnsibleDockerClient(Client): "Please note that simply uninstalling one of the modules can leave the other module in a broken state.") if not HAS_DOCKER_PY: - self.fail("Failed to import docker or docker-py - %s. Try `pip install docker` or `pip install docker-py` (Python 2.6)" % HAS_DOCKER_ERROR) - - if LooseVersion(docker_version) < LooseVersion(MIN_DOCKER_VERSION): - self.fail("Error: docker / docker-py version is %s. Minimum version required is %s." % (docker_version, - MIN_DOCKER_VERSION)) + if NEEDS_DOCKER_PY2: + msg = "Failed to import docker - %s. Try `pip install docker`" + else: + msg = "Failed to import docker or docker-py - %s. Try `pip install docker` or `pip install docker-py` (Python 2.6)" + self.fail(msg % HAS_DOCKER_ERROR) + + if LooseVersion(docker_version) < LooseVersion(min_docker_version): + if NEEDS_DOCKER_PY2: + if docker_version < LooseVersion('2.0'): + msg = "Error: docker-py version is %s, while this module requires docker %s. Try `pip uninstall docker-py` and then `pip install docker`" + else: + msg = "Error: docker version is %s. Minimum version required is %s. Use `pip install --upgrade docker` to upgrade." + else: + # The minimal required version is < 2.0 (and the current version as well). + # Advertise docker (instead of docker-py) for non-Python-2.6 users. + msg = ("Error: docker / docker-py version is %s. Minimum version required is %s. " + "Hint: if you do not need Python 2.6 support, try `pip uninstall docker-py` followed by `pip install docker`") + self.fail(msg % (docker_version, min_docker_version)) self.debug = self.module.params.get('debug') self.check_mode = self.module.check_mode @@ -206,6 +222,11 @@ class AnsibleDockerClient(Client): except Exception as exc: self.fail("Error connecting: %s" % exc) + if min_docker_api_version is not None: + docker_api_version = self.version()['ApiVersion'] + if LooseVersion(docker_api_version) < LooseVersion(min_docker_api_version): + self.fail('docker API version is %s. Minimum version required is %s.' % (docker_api_version, min_docker_api_version)) + def log(self, msg, pretty_print=False): pass # if self.debug: diff --git a/lib/ansible/modules/cloud/docker/docker_container.py b/lib/ansible/modules/cloud/docker/docker_container.py index 2fb2c948f6d..4898f50bd42 100644 --- a/lib/ansible/modules/cloud/docker/docker_container.py +++ b/lib/ansible/modules/cloud/docker/docker_container.py @@ -2522,7 +2522,8 @@ def main(): client = AnsibleDockerClientContainer( argument_spec=argument_spec, required_if=required_if, - supports_check_mode=True + supports_check_mode=True, + min_docker_api_version='1.20', ) cm = ContainerManager(client) diff --git a/lib/ansible/modules/cloud/docker/docker_image.py b/lib/ansible/modules/cloud/docker/docker_image.py index 24a4bb9f7fb..be9943d5be8 100644 --- a/lib/ansible/modules/cloud/docker/docker_image.py +++ b/lib/ansible/modules/cloud/docker/docker_image.py @@ -598,6 +598,7 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True, + min_docker_api_version='1.20', ) results = dict( diff --git a/lib/ansible/modules/cloud/docker/docker_image_facts.py b/lib/ansible/modules/cloud/docker/docker_image_facts.py index bc6a32e89bb..d12b79f213a 100644 --- a/lib/ansible/modules/cloud/docker/docker_image_facts.py +++ b/lib/ansible/modules/cloud/docker/docker_image_facts.py @@ -232,7 +232,8 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, - supports_check_mode=True + supports_check_mode=True, + min_docker_api_version='1.20', ) results = dict( diff --git a/lib/ansible/modules/cloud/docker/docker_login.py b/lib/ansible/modules/cloud/docker/docker_login.py index b668b5adbf0..9df3969a2b8 100644 --- a/lib/ansible/modules/cloud/docker/docker_login.py +++ b/lib/ansible/modules/cloud/docker/docker_login.py @@ -311,7 +311,8 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True, - required_if=required_if + required_if=required_if, + min_docker_api_version='1.20', ) results = dict( diff --git a/lib/ansible/modules/cloud/docker/docker_network.py b/lib/ansible/modules/cloud/docker/docker_network.py index c3ace43fa70..069bfc03ed1 100644 --- a/lib/ansible/modules/cloud/docker/docker_network.py +++ b/lib/ansible/modules/cloud/docker/docker_network.py @@ -163,7 +163,7 @@ try: from docker import utils if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3: from docker.types import IPAMPool, IPAMConfig -except: +except Exception as dummy: # missing docker-py handled in ansible.module_utils.docker_common pass @@ -384,6 +384,7 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True + # "The docker server >= 1.9.0" ) cm = DockerNetworkManager(client) diff --git a/lib/ansible/modules/cloud/docker/docker_secret.py b/lib/ansible/modules/cloud/docker/docker_secret.py index 5656a4130ae..1e307a242b2 100644 --- a/lib/ansible/modules/cloud/docker/docker_secret.py +++ b/lib/ansible/modules/cloud/docker/docker_secret.py @@ -272,7 +272,9 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True, - required_if=required_if + required_if=required_if, + min_docker_version='2.1.0', + min_docker_api_version='1.25', ) results = dict( diff --git a/lib/ansible/modules/cloud/docker/docker_service.py b/lib/ansible/modules/cloud/docker/docker_service.py index 7c92bcccb35..902d72da365 100644 --- a/lib/ansible/modules/cloud/docker/docker_service.py +++ b/lib/ansible/modules/cloud/docker/docker_service.py @@ -1061,7 +1061,8 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, - supports_check_mode=True + supports_check_mode=True, + min_docker_api_version='1.20', ) result = ContainerManager(client).exec_module() diff --git a/lib/ansible/modules/cloud/docker/docker_swarm.py b/lib/ansible/modules/cloud/docker/docker_swarm.py index 9fbb1a2bd65..ec2f8ef2377 100644 --- a/lib/ansible/modules/cloud/docker/docker_swarm.py +++ b/lib/ansible/modules/cloud/docker/docker_swarm.py @@ -508,7 +508,9 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True, - required_if=required_if + required_if=required_if, + min_docker_version='2.6.0', + min_docker_api_version='1.35', ) results = dict( diff --git a/lib/ansible/modules/cloud/docker/docker_swarm_service.py b/lib/ansible/modules/cloud/docker/docker_swarm_service.py index f3d01f095ad..b3eeb50aef0 100644 --- a/lib/ansible/modules/cloud/docker/docker_swarm_service.py +++ b/lib/ansible/modules/cloud/docker/docker_swarm_service.py @@ -277,6 +277,10 @@ extends_documentation_fragment: - docker requirements: - "docker-py >= 2.0" +- "Please note that the L(docker-py,https://pypi.org/project/docker-py/) 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). + Version 2.1.0 or newer is only available with the C(docker) module." ''' RETURN = ''' @@ -462,13 +466,7 @@ try: from distutils.version import LooseVersion from docker import utils from docker import types - from docker import __version__ as docker_version - if LooseVersion(docker_version) >= LooseVersion('2.0.0'): - from docker.types import Ulimit, LogConfig - HAS_DOCKER_PY_2 = True - else: - from docker.utils.types import Ulimit, LogConfig -except: +except Exception as dummy: # missing docker-py handled in ansible.module_utils.docker pass @@ -839,7 +837,7 @@ class DockerService(DockerBaseClass): network_id = None try: network_id = list(filter(lambda n: n['name'] == network_name, docker_networks))[0]['id'] - except: + except Exception as dummy: pass if network_id: networks.append({'Target': network_id}) @@ -1156,15 +1154,10 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, required_if=required_if, - supports_check_mode=True + supports_check_mode=True, + min_docker_version='2.0.0', ) - if not HAS_DOCKER_PY_2: - client.module.fail_json( - msg=("docker python library version is %s. " + - "this module requires version 2.0.0 or greater") - % docker_version) - dsm = DockerServiceManager(client) msg, changed, rebuilt, changes, facts = dsm.run() diff --git a/lib/ansible/modules/cloud/docker/docker_volume.py b/lib/ansible/modules/cloud/docker/docker_volume.py index 9b56370b353..3403ec085ef 100644 --- a/lib/ansible/modules/cloud/docker/docker_volume.py +++ b/lib/ansible/modules/cloud/docker/docker_volume.py @@ -254,7 +254,9 @@ def main(): client = AnsibleDockerClient( argument_spec=argument_spec, - supports_check_mode=True + supports_check_mode=True, + min_docker_version='1.10.0', + # "The docker server >= 1.9.0" ) cm = DockerVolumeManager(client)