docker_swarm: Return UnlockKey (#54490)

* Return UnlockKey

* Add changelog fragment

* Add method to check if a parameter exists in diffs

* Add method to get swarm unlock key

* Add option unlock_key

* Only return unlock key when created or changed

* Rename difference check

* Extend unlock key example

* Assert that unlock_key is a string

* Fix docker_swarm_info authors

* Don’t silence APIErrors

* Test unlock_key on unlocked swarm

* Catch APIError when retrieving unlock key

* Better return value description

* Lint

* Fix UnlockKey return value documentation

Co-Authored-By: hannseman <hannes@5monkeys.se>

* Get unlock key safely

Co-Authored-By: hannseman <hannes@5monkeys.se>

* Return None on empty UnlockKey

* Assert swarm_unlock_key is undefined if unqueried

* Add documentation about swarm_info unlock_key

* Add change log fragment for unlock_key option

* Revert "Add change log fragment for unlock_key option"

This reverts commit e3cb2325b5.

* Use generator expression instead

* Restart docker more decisively

* Use systemctl kill

Co-Authored-By: hannseman <hannes@5monkeys.se>

* Try to restart docker daemon
pull/54675/head
Hannes Ljungberg 6 years ago committed by John R Barker
parent 21c8650180
commit e58f23b73e

@ -0,0 +1,2 @@
minor_changes:
- "docker_swarm - ``UnlockKey`` will now be returned when ``autolock_managers`` is ``true``."

@ -846,6 +846,12 @@ class DifferenceTracker(object):
after[item['name']] = item['parameter'] after[item['name']] = item['parameter']
return before, after return before, after
def has_difference_for(self, name):
'''
Returns a boolean if a difference exists for name
'''
return any(diff for diff in self._diff if diff['name'] == name)
def get_legacy_docker_container_diffs(self): def get_legacy_docker_container_diffs(self):
''' '''
Return differences in the docker_container legacy format. Return differences in the docker_container legacy format.

@ -13,7 +13,10 @@ except ImportError:
pass pass
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
from ansible.module_utils.docker.common import AnsibleDockerClient from ansible.module_utils.docker.common import (
AnsibleDockerClient,
LooseVersion,
)
class AnsibleDockerSwarmClient(AnsibleDockerClient): class AnsibleDockerSwarmClient(AnsibleDockerClient):
@ -241,3 +244,8 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient):
def get_node_name_by_id(self, nodeid): def get_node_name_by_id(self, nodeid):
return self.get_node_inspect(nodeid)['Description']['Hostname'] return self.get_node_inspect(nodeid)['Description']['Hostname']
def get_unlock_key(self):
if self.docker_py_version < LooseVersion('2.7.0'):
return None
return super(AnsibleDockerSwarmClient, self).get_unlock_key()

@ -171,6 +171,7 @@ options:
description: description:
- If set, generate a key and use it to lock data stored on the managers. - If set, generate a key and use it to lock data stored on the managers.
- Docker default value is C(no). - Docker default value is C(no).
- M(docker_swarm_info) can be used to retrieve the unlock key.
type: bool type: bool
rotate_worker_token: rotate_worker_token:
description: Rotate the worker join token. description: Rotate the worker join token.
@ -250,6 +251,13 @@ swarm_facts:
returned: success returned: success
type: str type: str
example: SWMTKN-1--xxxxx example: SWMTKN-1--xxxxx
UnlockKey:
description: The swarm unlock-key if I(autolock_managers) is C(true).
returned: on success if I(autolock_managers) is C(true)
and swarm is initialised, or if I(autolock_managers) has changed.
type: str
example: SWMKEY-1-xxx
actions: actions:
description: Provides the actions done on the swarm. description: Provides the actions done on the swarm.
returned: when action failed. returned: when action failed.
@ -269,6 +277,7 @@ except ImportError:
from ansible.module_utils.docker.common import ( from ansible.module_utils.docker.common import (
DockerBaseClass, DockerBaseClass,
DifferenceTracker, DifferenceTracker,
LooseVersion,
) )
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
@ -424,6 +433,8 @@ class SwarmManager(DockerBaseClass):
self.differences = DifferenceTracker() self.differences = DifferenceTracker()
self.parameters = TaskParameters.from_ansible_params(client) self.parameters = TaskParameters.from_ansible_params(client)
self.created = False
def __call__(self): def __call__(self):
choice_map = { choice_map = {
"present": self.init_swarm, "present": self.init_swarm,
@ -450,11 +461,29 @@ class SwarmManager(DockerBaseClass):
data = self.client.inspect_swarm() data = self.client.inspect_swarm()
json_str = json.dumps(data, ensure_ascii=False) json_str = json.dumps(data, ensure_ascii=False)
self.swarm_info = json.loads(json_str) self.swarm_info = json.loads(json_str)
self.results['changed'] = False self.results['changed'] = False
self.results['swarm_facts'] = self.swarm_info self.results['swarm_facts'] = self.swarm_info
unlock_key = self.get_unlock_key()
self.swarm_info.update(unlock_key)
except APIError: except APIError:
return return
def get_unlock_key(self):
default = {'UnlockKey': None}
if not self.has_swarm_lock_changed():
return default
try:
return self.client.get_unlock_key() or default
except APIError:
return default
def has_swarm_lock_changed(self):
return self.parameters.autolock_managers and (
self.created or self.differences.has_difference_for('autolock_managers')
)
def init_swarm(self): def init_swarm(self):
if not self.force and self.client.check_if_swarm_manager(): if not self.force and self.client.check_if_swarm_manager():
self.__update_swarm() self.__update_swarm()
@ -479,11 +508,16 @@ class SwarmManager(DockerBaseClass):
if not self.client.check_if_swarm_manager(): if not self.client.check_if_swarm_manager():
if not self.check_mode: if not self.check_mode:
self.client.fail("Swarm not created or other error!") self.client.fail("Swarm not created or other error!")
self.created = True
self.inspect_swarm() self.inspect_swarm()
self.results['actions'].append("New Swarm cluster created: %s" % (self.swarm_info.get('ID'))) self.results['actions'].append("New Swarm cluster created: %s" % (self.swarm_info.get('ID')))
self.differences.add('state', parameter='present', active='absent') self.differences.add('state', parameter='present', active='absent')
self.results['changed'] = True self.results['changed'] = True
self.results['swarm_facts'] = {u'JoinTokens': self.swarm_info.get('JoinTokens')} self.results['swarm_facts'] = {
'JoinTokens': self.swarm_info.get('JoinTokens'),
'UnlockKey': self.swarm_info.get('UnlockKey')
}
def __update_swarm(self): def __update_swarm(self):
try: try:

@ -29,7 +29,7 @@ description:
version_added: "2.8" version_added: "2.8"
author: author:
- Piotr Wojciechowski (@wojciechowskipiotr) - Piotr Wojciechowski (@WojciechowskiPiotr)
options: options:
nodes: nodes:
@ -68,6 +68,11 @@ options:
- See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/service_ps/#filtering) - See L(the docker documentation,https://docs.docker.com/engine/reference/commandline/service_ps/#filtering)
for more information on possible filters. for more information on possible filters.
type: dict type: dict
unlock_key:
description:
- Whether to retrieve the swarm unlock key.
type: bool
default: no
verbose_output: verbose_output:
description: description:
- When set to C(yes) and I(nodes), I(services) or I(tasks) is set to C(yes) - When set to C(yes) and I(nodes), I(services) or I(tasks) is set to C(yes)
@ -121,6 +126,15 @@ EXAMPLES = '''
- debug: - debug:
var: result.swarm_facts var: result.swarm_facts
- name: Get the swarm unlock key
docker_swarm_info:
unlock_key: yes
register: result
- debug:
var: result.swarm_unlock_key
''' '''
RETURN = ''' RETURN = '''
@ -143,13 +157,17 @@ docker_swarm_manager:
- Only if this one is C(true), the module will not fail. - Only if this one is C(true), the module will not fail.
returned: both on success and on error returned: both on success and on error
type: bool type: bool
swarm_facts: swarm_facts:
description: description:
- Facts representing the basic state of the docker Swarm cluster. - Facts representing the basic state of the docker Swarm cluster.
- Contains tokens to connect to the Swarm - Contains tokens to connect to the Swarm
returned: always returned: always
type: dict type: dict
swarm_unlock_key:
description:
- Contains the key needed to unlock the swarm.
returned: When I(unlock_key) is C(true).
type: str
nodes: nodes:
description: description:
- List of dict objects containing the basic information about each volume. - List of dict objects containing the basic information about each volume.
@ -208,6 +226,8 @@ class DockerSwarmManager(DockerBaseClass):
filter_name = docker_object + "_filters" filter_name = docker_object + "_filters"
filters = clean_dict_booleans_for_docker_api(client.module.params.get(filter_name)) filters = clean_dict_booleans_for_docker_api(client.module.params.get(filter_name))
self.results[returned_name] = self.get_docker_items_list(docker_object, filters) self.results[returned_name] = self.get_docker_items_list(docker_object, filters)
if self.client.module.params['unlock_key']:
self.results['swarm_unlock_key'] = self.get_docker_swarm_unlock_key()
def get_docker_swarm_facts(self): def get_docker_swarm_facts(self):
try: try:
@ -305,6 +325,10 @@ class DockerSwarmManager(DockerBaseClass):
return object_essentials return object_essentials
def get_docker_swarm_unlock_key(self):
unlock_key = self.client.get_unlock_key() or {}
return unlock_key.get('UnlockKey') or None
def main(): def main():
argument_spec = dict( argument_spec = dict(
@ -314,14 +338,19 @@ def main():
tasks_filters=dict(type='dict'), tasks_filters=dict(type='dict'),
services=dict(type='bool', default=False), services=dict(type='bool', default=False),
services_filters=dict(type='dict'), services_filters=dict(type='dict'),
unlock_key=dict(type='bool', default=False),
verbose_output=dict(type='bool', default=False), verbose_output=dict(type='bool', default=False),
) )
option_minimal_versions = dict(
unlock_key=dict(docker_py_version='2.7.0', docker_api_version='1.25'),
)
client = AnsibleDockerSwarmClient( client = AnsibleDockerSwarmClient(
argument_spec=argument_spec, argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
min_docker_version='1.10.0', min_docker_version='1.10.0',
min_docker_api_version='1.24', min_docker_api_version='1.24',
option_minimal_versions=option_minimal_versions,
fail_results=dict( fail_results=dict(
can_talk_to_docker=False, can_talk_to_docker=False,
docker_swarm_active=False, docker_swarm_active=False,

@ -13,11 +13,16 @@
diff: no diff: no
ignore_errors: yes ignore_errors: yes
- name: Kill docker daemon
command: systemctl kill -s 9 docker
become: yes
- name: Restart docker daemon - name: Restart docker daemon
service: service:
name: docker name: docker
state: restarted state: restarted
become: yes become: yes
- name: Wait for docker daemon to be fully restarted - name: Wait for docker daemon to be fully restarted
command: docker ps command: docker ps

@ -61,6 +61,15 @@
register: output_6 register: output_6
ignore_errors: yes ignore_errors: yes
- name: autolock_managers (force new swarm)
docker_swarm:
state: present
force: yes
autolock_managers: yes
diff: yes
register: output_7
ignore_errors: yes
- name: assert autolock_managers changes - name: assert autolock_managers changes
assert: assert:
that: that:
@ -89,6 +98,16 @@
- 'output_6.diff.before is defined' - 'output_6.diff.before is defined'
- 'output_6.diff.after is defined' - 'output_6.diff.after is defined'
when: docker_py_version is version('2.6.0', '>=') when: docker_py_version is version('2.6.0', '>=')
- name: assert UnlockKey in swarm_facts
assert:
that:
- 'output_2.swarm_facts.UnlockKey'
- 'output_3.swarm_facts.UnlockKey is none'
- 'output_6.swarm_facts.UnlockKey is none'
- 'output_7.swarm_facts.UnlockKey'
when: docker_py_version is version('2.7.0', '>=')
- assert: - assert:
that: that:
- output_1 is failed - output_1 is failed

@ -18,6 +18,7 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == false' - 'output.docker_swarm_active == false'
- 'output.docker_swarm_manager == false' - 'output.docker_swarm_manager == false'
- 'output.swarm_unlock_key is not defined'
- name: Create a Swarm cluster - name: Create a Swarm cluster
docker_swarm: docker_swarm:
@ -45,6 +46,7 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true' - 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true' - 'output.docker_swarm_manager == true'
- 'output.swarm_unlock_key is not defined'
- name: Try to get docker_swarm_info and list of nodes when docker is running in swarm mode and as manager - name: Try to get docker_swarm_info and list of nodes when docker is running in swarm mode and as manager
docker_swarm_info: docker_swarm_info:
@ -61,6 +63,7 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true' - 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true' - 'output.docker_swarm_manager == true'
- 'output.swarm_unlock_key is not defined'
- name: Get local docker node name - name: Get local docker node name
set_fact: set_fact:
@ -84,6 +87,7 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true' - 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true' - 'output.docker_swarm_manager == true'
- 'output.swarm_unlock_key is not defined'
- name: Try to get docker_swarm_info and list of nodes with filters providing existing node name - name: Try to get docker_swarm_info and list of nodes with filters providing existing node name
docker_swarm_info: docker_swarm_info:
@ -102,6 +106,7 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true' - 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true' - 'output.docker_swarm_manager == true'
- 'output.swarm_unlock_key is not defined'
- name: Create random name - name: Create random name
set_fact: set_fact:
@ -124,6 +129,40 @@
- 'output.can_talk_to_docker == true' - 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true' - 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true' - 'output.docker_swarm_manager == true'
- 'output.swarm_unlock_key is not defined'
- name: Try to get docker_swarm_info and swarm_unlock_key on non a unlocked swarm
docker_swarm_info:
unlock_key: yes
register: output
- name: assert reading swarm facts and non existing swarm unlock key
assert:
that:
- 'output.swarm_unlock_key is none'
- 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true'
- name: Update swarm cluster to be locked
docker_swarm:
state: present
autolock_managers: true
register: autolock_managers_update_output
- name: Try to get docker_swarm_info and swarm_unlock_key
docker_swarm_info:
unlock_key: yes
register: output
- name: assert reading swarm facts and swarm unlock key
assert:
that:
- 'output.swarm_unlock_key is string'
- 'output.swarm_unlock_key == autolock_managers_update_output.swarm_facts.UnlockKey'
- 'output.can_talk_to_docker == true'
- 'output.docker_swarm_active == true'
- 'output.docker_swarm_manager == true'
always: always:
- name: Cleanup - name: Cleanup

Loading…
Cancel
Save