docker_swarm: support older docker-py versions (#53129)

* Decreasing docker_swarm requirements.

* Fixing docker-py / docker API version requirements, and some comments.

* Add changelog.

* Only send parameters specified by user to docker daemon.

* Extend labels test: not specifying == keep labels.

* Bump minimally required docker-py version for docker_node and docker_node_facts to 2.4.0.

* Prevent crashing when publish or healthcheck is not provided.

* Similarly to docker_swarm tests, only execute docker_node tests on real VMs and restart docker daemon when tests are done.

(cherry picked from commit 8e26c2dfbe)
This commit is contained in:
Felix Fontein 2019-03-04 12:10:09 +01:00 committed by Toshio Kuratomi
parent 122e73d062
commit 5aabb5ea02
7 changed files with 197 additions and 56 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- "docker_swarm - now supports docker-py 1.10.0 and newer, instead only docker 2.6.0 and newer."

View file

@ -125,20 +125,26 @@ options:
- User-defined key/value metadata.
- Label operations in this module apply to the docker swarm cluster.
Use M(docker_node) module to add/modify/remove swarm node labels.
- Requires API version >= 1.32.
type: dict
signing_ca_cert:
description:
- The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format.
type: path
- This must not be a path to a certificate, but the contents of the certificate.
- Requires API version >= 1.30.
type: str
signing_ca_key:
description:
- The desired signing CA key for all swarm node TLS leaf certificates, in PEM format.
type: path
- This must not be a path to a key, but the contents of the key.
- Requires API version >= 1.30.
type: str
ca_force_rotate:
description:
- An integer whose purpose is to force swarm to generate a new signing CA certificate and key,
if none have been specified.
- Docker default value is C(0).
- Requires API version >= 1.30.
type: int
autolock_managers:
description:
@ -157,11 +163,15 @@ extends_documentation_fragment:
- docker
requirements:
- python >= 2.7
- "docker-py >= 2.6.0"
- "docker-py >= 1.10.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."
For Python 2.6, C(docker-py) must be used. Otherwise, it is recommended to
install the C(docker) Python module. Note that both modules should I(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."
- Docker API >= 1.25
author:
- Thierry Bouvet (@tbouvet)
@ -268,7 +278,6 @@ class TaskParameters(DockerBaseClass):
self.election_tick = None
self.dispatcher_heartbeat_period = None
self.node_cert_expiry = None
self.external_cas = None
self.name = None
self.labels = None
self.log_driver = None
@ -286,8 +295,6 @@ class TaskParameters(DockerBaseClass):
if key in result.__dict__:
setattr(result, key, value)
result.labels = result.labels or {}
result.update_parameters(client)
return result
@ -334,34 +341,43 @@ class TaskParameters(DockerBaseClass):
self.log_driver = spec['TaskDefaults']['LogDriver']
def update_parameters(self, client):
params = dict(
snapshot_interval=self.snapshot_interval,
task_history_retention_limit=self.task_history_retention_limit,
keep_old_snapshots=self.keep_old_snapshots,
log_entries_for_slow_followers=self.log_entries_for_slow_followers,
heartbeat_tick=self.heartbeat_tick,
election_tick=self.election_tick,
dispatcher_heartbeat_period=self.dispatcher_heartbeat_period,
node_cert_expiry=self.node_cert_expiry,
name=self.name,
signing_ca_cert=self.signing_ca_cert,
signing_ca_key=self.signing_ca_key,
ca_force_rotate=self.ca_force_rotate,
autolock_managers=self.autolock_managers,
log_driver=self.log_driver,
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',
)
if self.labels:
params['labels'] = self.labels
params = dict()
for dest, source in assign.items():
if not client.option_minimal_versions[source]['supported']:
continue
value = getattr(self, source)
if value is not None:
params[dest] = value
self.spec = client.create_swarm_spec(**params)
def compare_to_active(self, other):
def compare_to_active(self, other, client):
for k in self.__dict__:
if k in ('advertise_addr', 'listen_addr', 'remote_addrs', 'join_token',
'rotate_worker_token', 'rotate_manager_token', 'spec'):
continue
if self.__dict__[k] is None:
if not client.option_minimal_versions[k]['supported']:
continue
if self.__dict__[k] != other.__dict__[k]:
value = getattr(self, k)
if value is None:
continue
if value != getattr(other, k):
return False
if self.rotate_worker_token:
return False
@ -441,14 +457,15 @@ class SwarmManager(DockerBaseClass):
self.parameters.update_from_swarm_info(self.swarm_info)
old_parameters = TaskParameters()
old_parameters.update_from_swarm_info(self.swarm_info)
if self.parameters.compare_to_active(old_parameters):
if self.parameters.compare_to_active(old_parameters, self.client):
self.results['actions'].append("No modification")
self.results['changed'] = False
return
self.parameters.update_parameters(self.client)
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=self.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)
except APIError as exc:
@ -567,17 +584,19 @@ def main():
]
option_minimal_versions = dict(
labels=dict(docker_api_version='1.32'),
signing_ca_cert=dict(docker_api_version='1.30'),
signing_ca_key=dict(docker_api_version='1.30'),
ca_force_rotate=dict(docker_api_version='1.30'),
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'),
)
client = AnsibleDockerClient(
argument_spec=argument_spec,
supports_check_mode=True,
required_if=required_if,
min_docker_version='2.6.0',
min_docker_version='1.10.0',
min_docker_api_version='1.25',
option_minimal_versions=option_minimal_versions,
)

View file

@ -1,6 +1,5 @@
- include_tasks: test_secrets.yml
# Maximum of 2.1.0 (docker-py version for docker_secrets) and 2.6.0 (docker-py version for docker_swarm) is 2.6.0
when: docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.25', '>=')
when: docker_py_version is version('2.1.0', '>=') and docker_api_version is version('1.25', '>=')
- fail: msg="Too old docker / docker-py version to run docker_secrets tests!"
when: not(docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)
when: not(docker_py_version is version('2.1.0', '>=') and docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

View file

@ -25,7 +25,7 @@
state: absent
force: true
when: docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.25', '>=')
when: docker_py_version is version('1.10.0', '>=') and docker_api_version is version('1.25', '>=')
- fail: msg="Too old docker / docker-py version to run docker_swarm tests!"
when: not(docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)
when: not(docker_py_version is version('1.10.0', '>=') and docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

View file

@ -60,14 +60,14 @@
- name: Create a Swarm cluster (force re-create)
docker_swarm:
state: present
advertise_addr: "{{ansible_default_ipv4.address}}"
advertise_addr: "{{ansible_default_ipv4.address | default('127.0.0.1')}}"
force: yes
register: output_5
- name: Create a Swarm cluster (force re-create, check mode)
docker_swarm:
state: present
advertise_addr: "{{ansible_default_ipv4.address}}"
advertise_addr: "{{ansible_default_ipv4.address | default('127.0.0.1')}}"
force: yes
check_mode: yes
register: output_6

View file

@ -49,6 +49,7 @@
timeout: 120
check_mode: yes
register: output_1
ignore_errors: yes
- name: signing_ca_cert and signing_ca_key
docker_swarm:
@ -58,6 +59,7 @@
signing_ca_key: "{{ lookup('file', role_path ~ '/' ~ output_dir ~ '/ansible_key1.key') }}"
timeout: 120
register: output_2
ignore_errors: yes
- name: Private key
debug: msg="{{ lookup('file', role_path ~ '/' ~ output_dir ~ '/ansible_key1.key') }}"
@ -73,6 +75,7 @@
# signing_ca_key: "{{ lookup('file', role_path ~ '/' ~ output_dir ~ '/ansible_key1.key') }}"
# timeout: 120
# register: output_3
# ignore_errors: yes
#- name: signing_ca_cert and signing_ca_key (idempotent, check mode)
# docker_swarm:
@ -82,6 +85,7 @@
# timeout: 120
# check_mode: yes
# register: output_4
# ignore_errors: yes
- name: signing_ca_cert and signing_ca_key (change, check mode)
docker_swarm:
@ -91,6 +95,7 @@
timeout: 120
check_mode: yes
register: output_5
ignore_errors: yes
- name: signing_ca_cert and signing_ca_key (change)
docker_swarm:
@ -99,6 +104,7 @@
signing_ca_key: "{{ lookup('file', role_path ~ '/' ~ output_dir ~ '/ansible_key2.key') }}"
timeout: 120
register: output_6
ignore_errors: yes
- name: assert signing_ca_cert and signing_ca_key
assert:
@ -115,6 +121,12 @@
- 'output_5.actions[0] == "Swarm cluster updated"'
- 'output_6 is changed'
- 'output_6.actions[0] == "Swarm cluster updated"'
when: docker_py_version is version('2.6.0', '>=')
- assert:
that:
- output_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.6.0') in output_1.msg"
when: docker_py_version is version('2.6.0', '<')
# https://github.com/ansible/ansible/issues/34054: openssl_certificate unusable on RHEL 7
when: pyopenssl_version.stdout is version('0.15', '>=')

View file

@ -15,18 +15,21 @@
autolock_managers: yes
check_mode: yes
register: output_1
ignore_errors: yes
- name: autolock_managers
docker_swarm:
state: present
autolock_managers: yes
register: output_2
ignore_errors: yes
- name: autolock_managers (idempotent)
docker_swarm:
state: present
autolock_managers: yes
register: output_3
ignore_errors: yes
- name: autolock_managers (idempotent, check mode)
docker_swarm:
@ -34,6 +37,7 @@
autolock_managers: yes
check_mode: yes
register: output_4
ignore_errors: yes
- name: autolock_managers (change, check mode)
docker_swarm:
@ -41,14 +45,16 @@
autolock_managers: no
check_mode: yes
register: output_5
ignore_errors: yes
- name: autolock_managers (change)
docker_swarm:
state: present
autolock_managers: no
register: output_6
ignore_errors: yes
- name: assert changed when remove a swarm cluster
- name: assert autolock_managers changes
assert:
that:
- 'output_1 is changed'
@ -63,6 +69,12 @@
- 'output_5.actions[0] == "Swarm cluster updated"'
- 'output_6 is changed'
- 'output_6.actions[0] == "Swarm cluster updated"'
when: docker_py_version is version('2.6.0', '>=')
- assert:
that:
- output_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.6.0') in output_1.msg"
when: docker_py_version is version('2.6.0', '<')
####################################################################
## ca_force_rotate #################################################
@ -74,18 +86,21 @@
ca_force_rotate: 1
check_mode: yes
register: output_1
ignore_errors: yes
- name: ca_force_rotate
docker_swarm:
state: present
ca_force_rotate: 1
register: output_2
ignore_errors: yes
- name: ca_force_rotate (idempotent)
docker_swarm:
state: present
ca_force_rotate: 1
register: output_3
ignore_errors: yes
- name: ca_force_rotate (idempotent, check mode)
docker_swarm:
@ -93,6 +108,7 @@
ca_force_rotate: 1
check_mode: yes
register: output_4
ignore_errors: yes
- name: ca_force_rotate (change, check mode)
docker_swarm:
@ -100,14 +116,16 @@
ca_force_rotate: 0
check_mode: yes
register: output_5
ignore_errors: yes
- name: ca_force_rotate (change)
docker_swarm:
state: present
ca_force_rotate: 0
register: output_6
ignore_errors: yes
- name: assert changed when remove a swarm cluster
- name: assert ca_force_rotate changes
assert:
that:
- 'output_1 is changed'
@ -122,6 +140,12 @@
- 'output_5.actions[0] == "Swarm cluster updated"'
- 'output_6 is changed'
- 'output_6.actions[0] == "Swarm cluster updated"'
when: docker_py_version is version('2.6.0', '>=')
- assert:
that:
- output_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.6.0') in output_1.msg"
when: docker_py_version is version('2.6.0', '<')
####################################################################
## dispatcher_heartbeat_period #####################################
@ -166,7 +190,7 @@
dispatcher_heartbeat_period: 23
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert dispatcher_heartbeat_period changes
assert:
that:
- 'output_1 is changed'
@ -225,7 +249,7 @@
election_tick: 5
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert election_tick changes
assert:
that:
- 'output_1 is changed'
@ -284,7 +308,7 @@
heartbeat_tick: 3
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert heartbeat_tick changes
assert:
that:
- 'output_1 is changed'
@ -342,7 +366,7 @@
keep_old_snapshots: 2
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert keep_old_snapshots changes
assert:
that:
- 'output_1 is changed'
@ -369,6 +393,7 @@
b: v2
check_mode: yes
register: output_1
ignore_errors: yes
- name: labels
docker_swarm:
@ -377,6 +402,7 @@
a: v1
b: v2
register: output_2
ignore_errors: yes
- name: labels (idempotent)
docker_swarm:
@ -385,6 +411,7 @@
a: v1
b: v2
register: output_3
ignore_errors: yes
- name: labels (idempotent, check mode)
docker_swarm:
@ -394,6 +421,7 @@
b: v2
check_mode: yes
register: output_4
ignore_errors: yes
- name: labels (change, check mode)
docker_swarm:
@ -403,6 +431,7 @@
c: v3
check_mode: yes
register: output_5
ignore_errors: yes
- name: labels (change)
docker_swarm:
@ -411,8 +440,68 @@
a: v1
c: v3
register: output_6
ignore_errors: yes
- name: assert changed when remove a swarm cluster
- name: labels (not specifying, check mode)
docker_swarm:
state: present
check_mode: yes
diff: yes
register: output_7
ignore_errors: yes
- name: labels (not specifying)
docker_swarm:
state: present
diff: yes
register: output_8
ignore_errors: yes
- name: labels (idempotency, check that labels are still there)
docker_swarm:
state: present
labels:
a: v1
c: v3
diff: yes
register: output_9
ignore_errors: yes
- name: labels (empty, check mode)
docker_swarm:
state: present
labels: {}
check_mode: yes
diff: yes
register: output_10
ignore_errors: yes
- name: labels (empty)
docker_swarm:
state: present
labels: {}
diff: yes
register: output_11
ignore_errors: yes
- name: labels (empty, idempotent, check mode)
docker_swarm:
state: present
labels: {}
check_mode: yes
diff: yes
register: output_12
ignore_errors: yes
- name: labels (empty, idempotent)
docker_swarm:
state: present
labels: {}
diff: yes
register: output_13
ignore_errors: yes
- name: assert labels changes
assert:
that:
- 'output_1 is changed'
@ -427,6 +516,26 @@
- 'output_5.actions[0] == "Swarm cluster updated"'
- 'output_6 is changed'
- 'output_6.actions[0] == "Swarm cluster updated"'
- 'output_7 is not changed'
- 'output_7.actions[0] == "No modification"'
- 'output_8 is not changed'
- 'output_8.actions[0] == "No modification"'
- 'output_9 is not changed'
- 'output_9.actions[0] == "No modification"'
- 'output_10 is changed'
- 'output_10.actions[0] == "Swarm cluster updated"'
- 'output_11 is changed'
- 'output_11.actions[0] == "Swarm cluster updated"'
- 'output_12 is not changed'
- 'output_12.actions[0] == "No modification"'
- 'output_13 is not changed'
- 'output_13.actions[0] == "No modification"'
when: docker_py_version is version('2.6.0', '>=')
- assert:
that:
- output_1 is failed
- "('version is ' ~ docker_py_version ~'. Minimum version required is 2.6.0') in output_1.msg"
when: docker_py_version is version('2.6.0', '<')
####################################################################
## log_entries_for_slow_followers ##################################
@ -470,7 +579,7 @@
log_entries_for_slow_followers: 23
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert log_entries_for_slow_followers changes
assert:
that:
- 'output_1 is changed'
@ -512,7 +621,7 @@
register: output_3
ignore_errors: yes
- name: assert changed when remove a swarm cluster
- name: assert name changes
assert:
that:
- 'output_1 is not changed'
@ -563,7 +672,7 @@
node_cert_expiry: 8766000000000000
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert node_cert_expiry changes
assert:
that:
- 'output_1 is changed'
@ -608,7 +717,7 @@
check_mode: yes
register: output_4
- name: assert changed when remove a swarm cluster
- name: assert rotate_manager_token changes
assert:
that:
- 'output_1 is changed'
@ -649,7 +758,7 @@
check_mode: yes
register: output_4
- name: assert changed when remove a swarm cluster
- name: assert rotate_worker_token changes
assert:
that:
- 'output_1 is changed'
@ -703,7 +812,7 @@
snapshot_interval: 54321
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert snapshot_interval changes
assert:
that:
- 'output_1 is changed'
@ -761,7 +870,7 @@
task_history_retention_limit: 7
register: output_6
- name: assert changed when remove a swarm cluster
- name: assert task_history_retention_limit changes
assert:
that:
- 'output_1 is changed'