docker_container: simplify minimal required version per option handling (#47711)

* Store parsed docker-py / docker API versions in client.

* Began refactoring 'minimal required version' for docker_container options.

* Removing some fake defaults.

* Added changelog.

* Improve tests (check older docker versions).

* Fix comparison. The breaking point is not docker-py 2.0.0, but 1.10.0.

(Verified by testing with these versions.)

* Move docker-py/API version detection to setup_docker.

* Add YAML document starter.

* docker_network requirement for docker-py was bumped to 1.10.0 in #47492.

(cherry picked from commit 3cca4185be)
This commit is contained in:
Felix Fontein 2018-11-05 01:25:11 +01:00 committed by Toshio Kuratomi
parent 5153286719
commit fb551bf62c
6 changed files with 335 additions and 176 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- "docker_container - refactored minimal docker-py/API version handling, and fixing such handling of some options."

View file

@ -188,6 +188,8 @@ class AnsibleDockerClient(Client):
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 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 "
@ -201,7 +203,7 @@ class AnsibleDockerClient(Client):
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 self.docker_py_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`"
@ -226,9 +228,10 @@ class AnsibleDockerClient(Client):
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))
self.docker_api_version_str = self.version()['ApiVersion']
self.docker_api_version = LooseVersion(self.docker_api_version_str)
if self.docker_api_version < LooseVersion(min_docker_api_version):
self.fail('docker API version is %s. Minimum version required is %s.' % (self.docker_api_version_str, min_docker_api_version))
def log(self, msg, pretty_print=False):
pass

View file

@ -55,11 +55,9 @@ options:
cpu_period:
description:
- Limit CPU CFS (Completely Fair Scheduler) period
default: 0
cpu_quota:
description:
- Limit CPU CFS (Completely Fair Scheduler) quota
default: 0
cpuset_cpus:
description:
- CPUs in which to allow execution C(1,3) or C(1-3).
@ -172,7 +170,6 @@ options:
Unit can be C(B) (byte), C(K) (kibibyte, 1024B), C(M) (mebibyte), C(G) (gibibyte),
C(T) (tebibyte), or C(P) (pebibyte). Minimum is C(4M)."
- Omitting the unit defaults to bytes.
default: 0
labels:
description:
- Dictionary of key value pairs.
@ -207,14 +204,12 @@ options:
Unit can be C(B) (byte), C(K) (kibibyte, 1024B), C(M) (mebibyte), C(G) (gibibyte),
C(T) (tebibyte), or C(P) (pebibyte)."
- Omitting the unit defaults to bytes.
default: 0
memory_swap:
description:
- "Total memory limit (memory + swap, format: C(<number>[<unit>])).
Number is a positive integer. Unit can be C(B) (byte), C(K) (kibibyte, 1024B),
C(M) (mebibyte), C(G) (gibibyte), C(T) (tebibyte), or C(P) (pebibyte)."
- Omitting the unit defaults to bytes.
default: 0
memory_swappiness:
description:
- Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
@ -247,11 +242,9 @@ options:
description:
- Whether or not to disable OOM Killer for the container.
type: bool
default: 'no'
oom_score_adj:
description:
- An integer value containing the score given to the container in order to tune OOM killer preferences.
default: 0
version_added: "2.2"
output_logs:
description:
@ -327,7 +320,6 @@ options:
restart_retries:
description:
- Use with restart policy to control maximum number of restart attempts.
default: 0
shm_size:
description:
- "Size of C(/dev/shm) (format: C(<number>[<unit>])). Number is positive integer.
@ -640,17 +632,17 @@ import shlex
from distutils.version import LooseVersion
from ansible.module_utils.basic import human_to_bytes
from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, HAS_DOCKER_PY_3, AnsibleDockerClient, DockerBaseClass, sanitize_result
from ansible.module_utils.docker_common import AnsibleDockerClient, DockerBaseClass, sanitize_result
from ansible.module_utils.six import string_types
try:
from docker import utils
if HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3:
from ansible.module_utils.docker_common import docker_version
if LooseVersion(docker_version) >= LooseVersion('1.10.0'):
from docker.types import Ulimit, LogConfig
else:
from docker.utils.types import Ulimit, LogConfig
from ansible.module_utils.docker_common import docker_version
except:
except Exception as dummy:
# missing docker-py handled in ansible.module_utils.docker
pass
@ -835,27 +827,22 @@ class TaskParameters(DockerBaseClass):
'''
update_parameters = dict(
blkio_weight='blkio_weight',
cpu_period='cpu_period',
cpu_quota='cpu_quota',
cpu_shares='cpu_shares',
cpuset_cpus='cpuset_cpus',
cpuset_mems='cpuset_mems',
mem_limit='memory',
mem_reservation='memory_reservation',
memswap_limit='memory_swap',
kernel_memory='kernel_memory',
)
if self.client.HAS_BLKIO_WEIGHT_OPT:
# blkio_weight is only supported in docker>=1.9
update_parameters['blkio_weight'] = 'blkio_weight'
if self.client.HAS_CPUSET_MEMS_OPT:
# cpuset_mems is only supported in docker>=2.3
update_parameters['cpuset_mems'] = 'cpuset_mems'
result = dict()
for key, value in update_parameters.items():
if getattr(self, value, None) is not None:
if self.client.option_minimal_versions[value]['supported']:
result[key] = getattr(self, value)
return result
@ -882,7 +869,8 @@ class TaskParameters(DockerBaseClass):
working_dir='working_dir',
)
if not HAS_DOCKER_PY_3:
if self.client.docker_py_version < LooseVersion('3.0'):
# cpu_shares and volume_driver moved to create_host_config in > 3
create_params['cpu_shares'] = 'cpu_shares'
create_params['volume_driver'] = 'volume_driver'
@ -893,6 +881,7 @@ class TaskParameters(DockerBaseClass):
for key, value in create_params.items():
if getattr(self, value, None) is not None:
if self.client.option_minimal_versions[value]['supported']:
result[key] = getattr(self, value)
return result
@ -974,30 +963,25 @@ class TaskParameters(DockerBaseClass):
devices='devices',
pid_mode='pid_mode',
tmpfs='tmpfs',
init='init',
uts_mode='uts',
auto_remove='auto_remove',
)
if self.client.HAS_AUTO_REMOVE_OPT:
# auto_remove is only supported in docker>=2
host_config_params['auto_remove'] = 'auto_remove'
if self.client.HAS_BLKIO_WEIGHT_OPT:
# blkio_weight is only supported in docker>=1.9
if self.client.docker_py_version >= LooseVersion('1.9') and self.client.docker_api_version >= LooseVersion('1.22'):
# blkio_weight can always be updated, but can only be set on creation
# when docker-py and docker API are new enough
host_config_params['blkio_weight'] = 'blkio_weight'
if HAS_DOCKER_PY_3:
if self.client.docker_py_version >= LooseVersion('3.0'):
# 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'
if self.client.HAS_INIT_OPT:
host_config_params['init'] = 'init'
if self.client.HAS_UTS_MODE_OPT:
host_config_params['uts_mode'] = 'uts'
params = dict()
for key, value in host_config_params.items():
if getattr(self, value, None) is not None:
if self.client.option_minimal_versions[value]['supported']:
params[key] = getattr(self, value)
if self.restart_policy:
@ -1455,23 +1439,40 @@ class Container(DockerBaseClass):
uts=host_config.get('UTSMode'),
expected_volumes=config.get('Volumes'),
expected_binds=host_config.get('Binds'),
volume_driver=host_config.get('VolumeDriver'),
volumes_from=host_config.get('VolumesFrom'),
working_dir=config.get('WorkingDir'),
publish_all_ports=host_config.get('PublishAllPorts'),
)
# Options which don't make sense without their accompanying option
if self.parameters.restart_policy:
config_mapping['restart_retries'] = restart_policy.get('MaximumRetryCount')
if self.parameters.log_driver:
config_mapping['log_driver'] = log_config.get('Type')
config_mapping['log_options'] = log_config.get('Config')
if self.parameters.client.HAS_AUTO_REMOVE_OPT:
# auto_remove is only supported in docker>=2
if self.parameters.client.option_minimal_versions['auto_remove']['supported']:
# auto_remove is only supported in docker>=2; unfortunately it has a default
# value, that's why we have to jump through the hoops here
config_mapping['auto_remove'] = host_config.get('AutoRemove')
if HAS_DOCKER_PY_3:
# volume_driver moved to create_host_config in > 3
config_mapping['volume_driver'] = host_config.get('VolumeDriver')
if self.parameters.client.docker_api_version < LooseVersion('1.22'):
# For docker API < 1.22, update_container() is not supported. Thus
# we need to handle all limits which are usually handled by
# update_container() as configuration changes which require a container
# restart.
config_mapping.update(dict(
blkio_weight=host_config.get('BlkioWeight'),
cpu_period=host_config.get('CpuPeriod'),
cpu_quota=host_config.get('CpuQuota'),
cpu_shares=host_config.get('CpuShares'),
cpuset_cpus=host_config.get('CpusetCpus'),
cpuset_mems=host_config.get('CpusetMems'),
kernel_memory=host_config.get("KernelMemory"),
memory=host_config.get('Memory'),
memory_reservation=host_config.get('MemoryReservation'),
memory_swap=host_config.get('MemorySwap'),
))
differences = []
for key, value in config_mapping.items():
@ -1498,33 +1499,25 @@ class Container(DockerBaseClass):
'''
if not self.container.get('HostConfig'):
self.fail("limits_differ_from_container: Error parsing container properties. HostConfig missing.")
if self.parameters.client.docker_api_version < LooseVersion('1.22'):
# update_container() call not supported
return False, []
host_config = self.container['HostConfig']
config_mapping = dict(
blkio_weight=host_config.get('BlkioWeight'),
cpu_period=host_config.get('CpuPeriod'),
cpu_quota=host_config.get('CpuQuota'),
cpu_shares=host_config.get('CpuShares'),
cpuset_cpus=host_config.get('CpusetCpus'),
cpuset_mems=host_config.get('CpusetMems'),
kernel_memory=host_config.get("KernelMemory"),
memory=host_config.get('Memory'),
memory_reservation=host_config.get('MemoryReservation'),
memory_swap=host_config.get('MemorySwap'),
oom_score_adj=host_config.get('OomScoreAdj'),
oom_killer=host_config.get('OomKillDisable'),
)
if self.parameters.client.HAS_BLKIO_WEIGHT_OPT:
# blkio_weight is only supported in docker>=1.9
config_mapping['blkio_weight'] = host_config.get('BlkioWeight')
if self.parameters.client.HAS_CPUSET_MEMS_OPT:
# cpuset_mems is only supported in docker>=2.3
config_mapping['cpuset_mems'] = host_config.get('CpusetMems')
if HAS_DOCKER_PY_3:
# cpu_shares moved to create_host_config in > 3
config_mapping['cpu_shares'] = host_config.get('CpuShares')
differences = []
for key, value in config_mapping.items():
if getattr(self.parameters, key, None):
@ -1844,7 +1837,7 @@ class ContainerManager(DockerBaseClass):
if client.module.params.get('log_options') and not client.module.params.get('log_driver'):
client.module.warn('log_options is ignored when log_driver is not specified')
if client.module.params.get('restart_retries') and not client.module.params.get('restart_policy'):
if client.module.params.get('restart_retries') is not None and not client.module.params.get('restart_policy'):
client.module.warn('restart_retries is ignored when restart_policy is not specified')
self.client = client
@ -2068,7 +2061,7 @@ class ContainerManager(DockerBaseClass):
self.fail("Error starting container %s: %s" % (container_id, str(exc)))
if not self.parameters.detach:
if HAS_DOCKER_PY_3:
if self.client.docker_py_version >= LooseVersion('3.0'):
status = self.client.wait(container_id)['StatusCode']
else:
status = self.client.wait(container_id)
@ -2159,6 +2152,14 @@ class ContainerManager(DockerBaseClass):
class AnsibleDockerClientContainer(AnsibleDockerClient):
# A list of module options which are not docker container properties
__NON_CONTAINER_PROPERTY_OPTIONS = (
'docker_host', 'tls_hostname', 'api_version', 'timeout', 'cacert_path', 'cert_path',
'key_path', 'ssl_version', 'tls', 'tls_verify', 'debug', 'env_file', 'force_kill',
'keep_volumes', 'ignore_image', 'name', 'pull', 'purge_networks', 'recreate',
'restart', 'state', 'stop_timeout', 'trust_image_content', 'networks', 'cleanup',
'kill_signal', 'output_logs', 'paused'
)
def _setup_comparisons(self):
comparisons = {}
@ -2176,10 +2177,7 @@ class AnsibleDockerClientContainer(AnsibleDockerClient):
)
for option, data in self.module.argument_spec.items():
# Ignore options which aren't used as container properties
if option in ('docker_host', 'tls_hostname', 'api_version', 'timeout', 'cacert_path', 'cert_path',
'key_path', 'ssl_version', 'tls', 'tls_verify', 'debug', 'env_file', 'force_kill',
'keep_volumes', 'ignore_image', 'name', 'pull', 'purge_networks', 'recreate',
'restart', 'state', 'stop_timeout', 'trust_image_content', 'networks'):
if option in self.__NON_CONTAINER_PROPERTY_OPTIONS:
continue
# Determine option type
if option in explicit_types:
@ -2208,51 +2206,94 @@ class AnsibleDockerClientContainer(AnsibleDockerClient):
comparisons['expected_ports'] = dict(type='dict', comparison=comparisons['published_ports']['comparison'], name='expected_ports')
self.comparisons = comparisons
def _get_minimal_versions(self):
# Helper function to detect whether any specified network uses ipv4_address or ipv6_address
def detect_ipvX_address_usage():
for network in self.module.params.get("networks") or []:
if 'ipv4_address' in network or 'ipv6_address' in network:
return True
return False
self.option_minimal_versions = dict(
# internal options
log_config=dict(),
publish_all_ports=dict(),
ports=dict(),
volume_binds=dict(),
name=dict(),
)
for option, data in self.module.argument_spec.items():
if option in self.__NON_CONTAINER_PROPERTY_OPTIONS:
continue
self.option_minimal_versions[option] = dict()
self.option_minimal_versions.update(dict(
dns_opts=dict(docker_api_version='1.21', docker_py_version='1.10.0'),
ipc_mode=dict(docker_api_version='1.25'),
mac_address=dict(docker_api_version='1.25'),
oom_killer=dict(docker_py_version='2.0.0'),
oom_score_adj=dict(docker_api_version='1.22', docker_py_version='2.0.0'),
shm_size=dict(docker_api_version='1.22'),
stop_signal=dict(docker_api_version='1.21'),
tmpfs=dict(docker_api_version='1.22'),
volume_driver=dict(docker_api_version='1.21'),
memory_reservation=dict(docker_api_version='1.21'),
kernel_memory=dict(docker_api_version='1.21'),
auto_remove=dict(docker_py_version='2.1.0', docker_api_version='1.25'),
init=dict(docker_py_version='2.2.0', docker_api_version='1.25'),
sysctls=dict(docker_py_version='1.10.0', docker_api_version='1.24'),
userns_mode=dict(docker_py_version='1.10.0', docker_api_version='1.23'),
uts=dict(docker_py_version='3.5.0', docker_api_version='1.25'),
# specials
ipvX_address_supported=dict(docker_py_version='1.9.0', detect_usage=detect_ipvX_address_usage,
usage_msg='ipv4_address or ipv6_address in networks'),
))
for option, data in self.option_minimal_versions.items():
# 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
# Fail if option is not supported but used
if not data['supported']:
# Test whether option is specified
if 'detect_usage' in data:
used = data['detect_usage']()
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:
# If the option is used, compose error message.
if 'usage_msg' in data:
usg = data['usage_msg']
else:
usg = 'set %s option' % (option, )
if not support_docker_api:
msg = 'docker API version is %s. Minimum version required is %s to %s.'
msg = msg % (self.docker_api_version_str, data['docker_api_version'], usg)
elif not support_docker_py:
if LooseVersion(data['docker_py_version']) < LooseVersion('2.0.0'):
msg = ("docker-py version is %s. Minimum version required is %s to %s. "
"Consider switching to the 'docker' package if you do not require Python 2.6 support.")
elif self.docker_py_version < LooseVersion('2.0.0'):
msg = ("docker-py version is %s. Minimum version required is %s to %s. "
"You have to switch to the Python 'docker' package. First uninstall 'docker-py' before "
"installing 'docker' to avoid a broken installation.")
else:
msg = "docker version is %s. Minimum version required is %s to %s."
msg = msg % (docker_version, data['docker_py_version'], usg)
else:
# should not happen
msg = 'Cannot %s with your configuration.' % (usg, )
self.fail(msg)
def __init__(self, **kwargs):
super(AnsibleDockerClientContainer, self).__init__(**kwargs)
docker_api_version = self.version()['ApiVersion']
init_supported = LooseVersion(docker_api_version) >= LooseVersion('1.25')
if self.module.params.get("init") and not init_supported:
self.fail('docker API version is %s. Minimum version required is 1.25 to set init option.' % (docker_api_version,))
init_supported = init_supported and LooseVersion(docker_version) >= LooseVersion('2.2')
if self.module.params.get("init") and not init_supported:
self.fail("docker or docker-py version is %s. Minimum version required is 2.2 to set init option. "
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
uts_mode_supported = LooseVersion(docker_version) >= LooseVersion('3.5')
if self.module.params.get("uts") is not None and not uts_mode_supported:
self.fail("docker or docker-py version is %s. Minimum version required is 3.5 to set uts option. "
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
blkio_weight_supported = LooseVersion(docker_version) >= LooseVersion('1.9')
if self.module.params.get("blkio_weight") is not None and not blkio_weight_supported:
self.fail("docker or docker-py version is %s. Minimum version required is 1.9 to set blkio_weight option.")
cpuset_mems_supported = LooseVersion(docker_version) >= LooseVersion('2.3')
if self.module.params.get("cpuset_mems") is not None and not cpuset_mems_supported:
self.fail("docker or docker-py version is %s. Minimum version required is 2.3 to set cpuset_mems option. "
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
ipvX_address_supported = LooseVersion(docker_version) >= LooseVersion('1.9')
if not ipvX_address_supported:
ipvX_address_used = False
for network in self.module.params.get("networks", []):
if 'ipv4_address' in network or 'ipv6_address' in network:
ipvX_address_used = True
if ipvX_address_used:
self.fail("docker or docker-py version is %s. Minimum version required is 1.9 to use "
"ipv4_address or ipv6_address in networks." % (docker_version,))
self.HAS_INIT_OPT = init_supported
self.HAS_UTS_MODE_OPT = uts_mode_supported
self.HAS_BLKIO_WEIGHT_OPT = blkio_weight_supported
self.HAS_CPUSET_MEMS_OPT = cpuset_mems_supported
self.HAS_AUTO_REMOVE_OPT = HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3
if self.module.params.get('auto_remove') and not self.HAS_AUTO_REMOVE_OPT:
self.fail("'auto_remove' is not compatible with the 'docker-py' Python package. It requires the newer 'docker' Python package.")
self._get_minimal_versions()
self._setup_comparisons()

View file

@ -1,4 +1,5 @@
---
# Create random name prefix (for containers, networks, ...)
- name: Create random container name prefix
set_fact:
cname_prefix: "{{ 'ansible-test-%0x' % ((2**32) | random) }}"
@ -8,6 +9,7 @@
- debug:
msg: "Using container name prefix {{ cname_prefix }}"
# Run the tests
- block:
- include_tasks: run-test.yml
with_fileglob:
@ -26,6 +28,6 @@
state: absent
force: yes
with_items: "{{ dnetworks }}"
when: docker_py_version is version('1.10.0', '>=')
# Skip for CentOS 6
when: ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6
when: docker_py_version is version('1.8.0', '>=') and docker_api_version is version('1.20', '>=')

View file

@ -21,6 +21,7 @@
- "{{ nname_2 }}"
loop_control:
loop_var: network_name
when: docker_py_version is version('1.10.0', '>=')
####################################################################
## auto_remove #####################################################
@ -34,6 +35,7 @@
state: started
auto_remove: yes
register: auto_remove_1
ignore_errors: yes
- name: Give container 1 second to be sure it terminated
pause:
@ -44,11 +46,18 @@
name: "{{ cname }}"
state: absent
register: auto_remove_2
ignore_errors: yes
- assert:
that:
- auto_remove_1 is changed
- auto_remove_2 is not changed
when: docker_py_version is version('2.1.0', '>=')
- assert:
that:
- auto_remove_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.1.0') in auto_remove_1.msg"
when: docker_py_version is version('2.1.0', '<')
####################################################################
## blkio_weight ####################################################
@ -515,6 +524,7 @@
auto_remove: yes
cleanup: yes
register: detach_auto_remove
ignore_errors: yes
- name: cleanup (unnecessary)
docker_container:
@ -528,8 +538,11 @@
- detach_no_cleanup_cleanup is changed
- "'Hello from Docker!' in detach_cleanup.ansible_facts.docker_container.Output"
- detach_cleanup_cleanup is not changed
- assert:
that:
- "'Cannot retrieve result as auto_remove is enabled' == detach_auto_remove.ansible_facts.docker_container.Output"
- detach_auto_remove_cleanup is not changed
when: docker_py_version is version('2.1.0', '>=')
####################################################################
## devices #########################################################
@ -606,6 +619,7 @@
- "timeout:10"
- rotate
register: dns_opts_1
ignore_errors: yes
- name: dns_opts (idempotency)
docker_container:
@ -617,6 +631,7 @@
- rotate
- "timeout:10"
register: dns_opts_2
ignore_errors: yes
- name: dns_opts (less resolv.conf options)
docker_container:
@ -627,6 +642,7 @@
dns_opts:
- "timeout:10"
register: dns_opts_3
ignore_errors: yes
- name: dns_opts (more resolv.conf options)
docker_container:
@ -639,6 +655,7 @@
- no-check-names
stop_timeout: 1
register: dns_opts_4
ignore_errors: yes
- name: cleanup
docker_container:
@ -652,6 +669,12 @@
- dns_opts_2 is not changed
- dns_opts_3 is not changed
- dns_opts_4 is changed
when: docker_py_version is version('1.10.0', '>=')
- assert:
that:
- dns_opts_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 1.10.0') in dns_opts_1.msg"
when: docker_py_version is version('1.10.0', '<')
####################################################################
## dns_search_domains ##############################################
@ -1238,6 +1261,7 @@
init: yes
state: started
register: init_1
ignore_errors: yes
- name: init (idempotency)
docker_container:
@ -1247,6 +1271,7 @@
init: yes
state: started
register: init_2
ignore_errors: yes
- name: init (change)
docker_container:
@ -1257,6 +1282,7 @@
state: started
stop_timeout: 1
register: init_3
ignore_errors: yes
- name: cleanup
docker_container:
@ -1269,6 +1295,12 @@
- init_1 is changed
- init_2 is not changed
- init_3 is changed
when: docker_py_version is version('2.2.0', '>=')
- assert:
that:
- init_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.2.0') in init_1.msg"
when: docker_py_version is version('2.2.0', '<')
####################################################################
## interactive #####################################################
@ -2018,6 +2050,7 @@
## networks, purge_networks ########################################
####################################################################
- block:
- name: networks, purge_networks
docker_container:
image: alpine:3.8
@ -2090,6 +2123,8 @@
- networks_4 is changed
- networks_5 is changed
when: docker_py_version is version('1.10.0', '>=')
####################################################################
## oom_killer ######################################################
####################################################################
@ -2102,6 +2137,7 @@
oom_killer: yes
state: started
register: oom_killer_1
ignore_errors: yes
- name: oom_killer (idempotency)
docker_container:
@ -2111,6 +2147,7 @@
oom_killer: yes
state: started
register: oom_killer_2
ignore_errors: yes
- name: oom_killer (change)
docker_container:
@ -2121,6 +2158,7 @@
state: started
stop_timeout: 1
register: oom_killer_3
ignore_errors: yes
- name: cleanup
docker_container:
@ -2133,6 +2171,12 @@
- oom_killer_1 is changed
- oom_killer_2 is not changed
- oom_killer_3 is changed
when: docker_py_version is version('2.0.0', '>=')
- assert:
that:
- oom_killer_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.0.0') in oom_killer_1.msg"
when: docker_py_version is version('2.0.0', '<')
####################################################################
## oom_score_adj ###################################################
@ -2146,6 +2190,7 @@
oom_score_adj: 5
state: started
register: oom_score_adj_1
ignore_errors: yes
- name: oom_score_adj (idempotency)
docker_container:
@ -2155,6 +2200,7 @@
oom_score_adj: 5
state: started
register: oom_score_adj_2
ignore_errors: yes
- name: oom_score_adj (change)
docker_container:
@ -2165,6 +2211,7 @@
state: started
stop_timeout: 1
register: oom_score_adj_3
ignore_errors: yes
- name: cleanup
docker_container:
@ -2177,6 +2224,12 @@
- oom_score_adj_1 is changed
- oom_score_adj_2 is not changed
- oom_score_adj_3 is changed
when: docker_py_version is version('2.0.0', '>=')
- assert:
that:
- oom_score_adj_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.0.0') in oom_score_adj_1.msg"
when: docker_py_version is version('2.0.0', '<')
####################################################################
## output_logs #####################################################
@ -2210,6 +2263,8 @@
state: started
pid_mode: "container:{{ pid_mode_helper.ansible_facts.docker_container.Id }}"
register: pid_mode_1
ignore_errors: yes
# docker-py < 2.0 does not support "arbitrary" pid_mode values
- name: pid_mode (idempotency)
docker_container:
@ -2219,6 +2274,8 @@
state: started
pid_mode: "container:{{ cname_h1 }}"
register: pid_mode_2
ignore_errors: yes
# docker-py < 2.0 does not support "arbitrary" pid_mode values
- name: pid_mode (change)
docker_container:
@ -2246,6 +2303,13 @@
- pid_mode_1 is changed
- pid_mode_2 is not changed
- pid_mode_3 is changed
when: docker_py_version is version('2.0.0', '>=')
- assert:
that:
- pid_mode_1 is failed
- pid_mode_2 is failed
- pid_mode_3 is changed
when: docker_py_version is version('2.0.0', '<')
####################################################################
## privileged ######################################################
@ -2700,6 +2764,7 @@
net.ipv4.icmp_echo_ignore_all: 1
net.ipv4.ip_forward: 1
register: sysctls_1
ignore_errors: yes
- name: sysctls (idempotency)
docker_container:
@ -2711,6 +2776,7 @@
net.ipv4.ip_forward: 1
net.ipv4.icmp_echo_ignore_all: 1
register: sysctls_2
ignore_errors: yes
- name: sysctls (less sysctls)
docker_container:
@ -2721,6 +2787,7 @@
sysctls:
net.ipv4.icmp_echo_ignore_all: 1
register: sysctls_3
ignore_errors: yes
- name: sysctls (more sysctls)
docker_container:
@ -2733,6 +2800,7 @@
net.ipv6.conf.default.accept_redirects: 0
stop_timeout: 1
register: sysctls_4
ignore_errors: yes
- name: cleanup
docker_container:
@ -2746,6 +2814,12 @@
- sysctls_2 is not changed
- sysctls_3 is not changed
- sysctls_4 is changed
when: docker_py_version is version('1.10.0', '>=')
- assert:
that:
- sysctls_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 1.10.0') in sysctls_1.msg"
when: docker_py_version is version('1.10.0', '<')
####################################################################
## timeout #########################################################
@ -2981,6 +3055,7 @@
userns_mode: host
state: started
register: userns_mode_1
ignore_errors: yes
- name: userns_mode (idempotency)
docker_container:
@ -2990,6 +3065,7 @@
userns_mode: host
state: started
register: userns_mode_2
ignore_errors: yes
- name: userns_mode (change)
docker_container:
@ -3000,6 +3076,7 @@
state: started
stop_timeout: 1
register: userns_mode_3
ignore_errors: yes
- name: cleanup
docker_container:
@ -3012,6 +3089,12 @@
- userns_mode_1 is changed
- userns_mode_2 is not changed
- userns_mode_3 is changed
when: docker_py_version is version('1.10.0', '>=')
- assert:
that:
- userns_mode_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 1.10.0') in userns_mode_1.msg"
when: docker_py_version is version('1.10.0', '<')
####################################################################
## uts #############################################################
@ -3025,6 +3108,7 @@
uts: host
state: started
register: uts_1
ignore_errors: yes
- name: uts (idempotency)
docker_container:
@ -3034,6 +3118,7 @@
uts: host
state: started
register: uts_2
ignore_errors: yes
- name: uts (change)
docker_container:
@ -3044,6 +3129,7 @@
state: started
stop_timeout: 1
register: uts_3
ignore_errors: yes
- name: cleanup
docker_container:
@ -3056,6 +3142,12 @@
- uts_1 is changed
- uts_2 is not changed
- uts_3 is changed
when: docker_py_version is version('3.5.0', '>=')
- assert:
that:
- uts_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 3.5.0') in uts_1.msg"
when: docker_py_version is version('3.5.0', '<')
####################################################################
## keep_volumes ####################################################
@ -3290,3 +3382,4 @@
- "{{ nname_2 }}"
loop_control:
loop_var: network_name
when: docker_py_version is version('1.10.0', '>=')

View file

@ -17,3 +17,21 @@
state: present
name: 'docker{{ extra_packages }}'
extra_args: "-c {{ role_path }}/../../../runner/requirements/constraints.txt"
# Detect docker API and docker-py versions
- name: Check Docker API version
command: "{{ ansible_python.executable }} -c 'import docker; print(docker.from_env().version()[\"ApiVersion\"])'"
register: docker_api_version_stdout
ignore_errors: yes
- name: Check docker-py API version
command: "{{ ansible_python.executable }} -c 'import docker; print(docker.__version__)'"
register: docker_py_version_stdout
ignore_errors: yes
- set_fact:
docker_api_version: "{{ docker_api_version_stdout.stdout or '0.0' }}"
docker_py_version: "{{ docker_py_version_stdout.stdout or '0.0' }}"
- debug:
msg: "Docker API version: {{ docker_api_version }}; docker-py library version: {{ docker_py_version }}"