docker_swarm_service: Make secret_id and config_id optional (#58299)

* Lookup secret id by name if not set

* Lookup config id by name if not set

* Add changelog fragment

* Remove usage of secret/config_id in examples

* Python 2.6 compat

* Extend secrets and configs tests
This commit is contained in:
Hannes Ljungberg 2019-06-27 22:24:35 +02:00 committed by Felix Fontein
parent 4d5a3d3341
commit 1b90e10cf0
4 changed files with 143 additions and 29 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- "docker_swarm_service - Remove requirement of ``secret_id`` on ``secrets`` and ``config_id`` on ``configs``."

View file

@ -44,7 +44,6 @@ options:
description:
- Config's ID.
type: str
required: yes
config_name:
description:
- Config's name as defined at its creation.
@ -598,7 +597,6 @@ options:
description:
- Secret's ID.
type: str
required: yes
secret_name:
description:
- Secret's name as defined at its creation.
@ -993,8 +991,7 @@ EXAMPLES = '''
name: myservice
image: alpine:edge
configs:
- config_id: myconfig_id
config_name: myconfig_name
- config_name: myconfig_name
filename: "/tmp/config.txt"
- name: Set networks
@ -1009,8 +1006,7 @@ EXAMPLES = '''
name: myservice
image: alpine:edge
secrets:
- secret_id: mysecret_id
secret_name: mysecret_name
- secret_name: mysecret_name
filename: "/run/secrets/secret.txt"
- name: Start service with healthcheck
@ -1480,7 +1476,9 @@ class DockerService(DockerBaseClass):
}
@classmethod
def from_ansible_params(cls, ap, old_service, image_digest, can_update_networks):
def from_ansible_params(
cls, ap, old_service, image_digest, can_update_networks, secret_ids, config_ids
):
s = DockerService()
s.image = image_digest
s.can_update_networks = can_update_networks
@ -1616,9 +1614,10 @@ class DockerService(DockerBaseClass):
s.configs = []
for param_m in ap['configs']:
service_c = {}
service_c['config_id'] = param_m['config_id']
service_c['config_name'] = param_m['config_name']
service_c['filename'] = param_m['filename'] or service_c['config_name']
config_name = param_m['config_name']
service_c['config_id'] = param_m['config_id'] or config_ids[config_name]
service_c['config_name'] = config_name
service_c['filename'] = param_m['filename'] or config_name
service_c['uid'] = param_m['uid']
service_c['gid'] = param_m['gid']
service_c['mode'] = param_m['mode']
@ -1628,9 +1627,10 @@ class DockerService(DockerBaseClass):
s.secrets = []
for param_m in ap['secrets']:
service_s = {}
service_s['secret_id'] = param_m['secret_id']
service_s['secret_name'] = param_m['secret_name']
service_s['filename'] = param_m['filename'] or service_s['secret_name']
secret_name = param_m['secret_name']
service_s['secret_id'] = param_m['secret_id'] or secret_ids[secret_name]
service_s['secret_name'] = secret_name
service_s['filename'] = param_m['filename'] or secret_name
service_s['uid'] = param_m['uid']
service_s['gid'] = param_m['gid']
service_s['mode'] = param_m['mode']
@ -2326,6 +2326,54 @@ class DockerServiceManager(object):
self.client.docker_py_version >= LooseVersion('2.7')
)
def get_missing_secret_ids(self):
"""
Resolve missing secret ids by looking them up by name
"""
secret_names = [
secret['secret_name']
for secret in self.client.module.params.get('secrets') or []
if secret['secret_id'] is None
]
if not secret_names:
return {}
secrets = self.client.secrets(filters={'name': secret_names})
secrets = dict(
(secret['Spec']['Name'], secret['ID'])
for secret in secrets
if secret['Spec']['Name'] in secret_names
)
for secret_name in secret_names:
if secret_name not in secrets:
self.client.fail(
'Could not find a secret named "%s"' % secret_name
)
return secrets
def get_missing_config_ids(self):
"""
Resolve missing config ids by looking them up by name
"""
config_names = [
config['config_name']
for config in self.client.module.params.get('configs') or []
if config['config_id'] is None
]
if not config_names:
return {}
configs = self.client.configs(filters={'name': config_names})
configs = dict(
(config['Spec']['Name'], config['ID'])
for config in configs
if config['Spec']['Name'] in config_names
)
for config_name in config_names:
if config_name not in configs:
self.client.fail(
'Could not find a config named "%s"' % config_name
)
return configs
def run(self):
self.diff_tracker = DifferenceTracker()
module = self.client.module
@ -2351,11 +2399,15 @@ class DockerServiceManager(object):
)
try:
can_update_networks = self.can_update_networks()
secret_ids = self.get_missing_secret_ids()
config_ids = self.get_missing_config_ids()
new_service = DockerService.from_ansible_params(
module.params,
current_service,
image_digest,
can_update_networks
can_update_networks,
secret_ids,
config_ids
)
except Exception as e:
return self.client.fail(
@ -2375,7 +2427,9 @@ class DockerServiceManager(object):
msg = 'Service removed'
changed = True
else:
changed, differences, need_rebuild, force_update = new_service.compare(current_service)
changed, differences, need_rebuild, force_update = new_service.compare(
current_service
)
if changed:
self.diff_tracker.merge(differences)
if need_rebuild:
@ -2504,7 +2558,7 @@ def main():
tmpfs_mode=dict(type='int')
)),
configs=dict(type='list', elements='dict', options=dict(
config_id=dict(type='str', required=True),
config_id=dict(type='str'),
config_name=dict(type='str', required=True),
filename=dict(type='str'),
uid=dict(type='str'),
@ -2512,7 +2566,7 @@ def main():
mode=dict(type='int'),
)),
secrets=dict(type='list', elements='dict', options=dict(
secret_id=dict(type='str', required=True),
secret_id=dict(type='str'),
secret_name=dict(type='str', required=True),
filename=dict(type='str'),
uid=dict(type='str'),

View file

@ -48,8 +48,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs:
- config_id: "{{ config_result_1.config_id|default('') }}"
config_name: "{{ config_name_1 }}"
- config_name: "{{ config_name_1 }}"
filename: "/tmp/{{ config_name_1 }}.txt"
register: configs_2
ignore_errors: yes
@ -64,10 +63,38 @@
- config_id: "{{ config_result_1.config_id|default('') }}"
config_name: "{{ config_name_1 }}"
filename: "/tmp/{{ config_name_1 }}.txt"
- config_name: "{{ config_name_2 }}"
filename: "/tmp/{{ config_name_2 }}.txt"
register: configs_3
ignore_errors: yes
- name: configs (add idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs:
- config_name: "{{ config_name_1 }}"
filename: "/tmp/{{ config_name_1 }}.txt"
- config_id: "{{ config_result_2.config_id|default('') }}"
config_name: "{{ config_name_2 }}"
filename: "/tmp/{{ config_name_2 }}.txt"
register: configs_3
register: configs_4
ignore_errors: yes
- name: configs (add idempotency no id)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs:
- config_name: "{{ config_name_1 }}"
filename: "/tmp/{{ config_name_1 }}.txt"
- config_name: "{{ config_name_2 }}"
filename: "/tmp/{{ config_name_2 }}.txt"
register: configs_5
ignore_errors: yes
- name: configs (empty)
@ -77,7 +104,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs: []
register: configs_4
register: configs_6
ignore_errors: yes
- name: configs (empty idempotency)
@ -87,7 +114,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs: []
register: configs_5
register: configs_7
ignore_errors: yes
- name: cleanup
@ -101,8 +128,10 @@
- configs_1 is changed
- configs_2 is not changed
- configs_3 is changed
- configs_4 is changed
- configs_4 is not changed
- configs_5 is not changed
- configs_6 is changed
- configs_7 is not changed
when: docker_api_version is version('1.30', '>=') and docker_py_version is version('2.6.0', '>=')
- assert:

View file

@ -48,8 +48,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets:
- secret_id: "{{ secret_result_1.secret_id|default('') }}"
secret_name: "{{ secret_name_1 }}"
- secret_name: "{{ secret_name_1 }}"
filename: "/run/secrets/{{ secret_name_1 }}.txt"
register: secrets_2
ignore_errors: yes
@ -64,10 +63,38 @@
- secret_id: "{{ secret_result_1.secret_id|default('') }}"
secret_name: "{{ secret_name_1 }}"
filename: "/run/secrets/{{ secret_name_1 }}.txt"
- secret_name: "{{ secret_name_2 }}"
filename: "/run/secrets/{{ secret_name_2 }}.txt"
register: secrets_3
ignore_errors: yes
- name: secrets (add idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets:
- secret_name: "{{ secret_name_1 }}"
filename: "/run/secrets/{{ secret_name_1 }}.txt"
- secret_id: "{{ secret_result_2.secret_id|default('') }}"
secret_name: "{{ secret_name_2 }}"
filename: "/run/secrets/{{ secret_name_2 }}.txt"
register: secrets_3
register: secrets_4
ignore_errors: yes
- name: secrets (add idempotency no id)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets:
- secret_name: "{{ secret_name_1 }}"
filename: "/run/secrets/{{ secret_name_1 }}.txt"
- secret_name: "{{ secret_name_2 }}"
filename: "/run/secrets/{{ secret_name_2 }}.txt"
register: secrets_5
ignore_errors: yes
- name: secrets (empty)
@ -77,7 +104,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets: []
register: secrets_4
register: secrets_6
ignore_errors: yes
- name: secrets (empty idempotency)
@ -87,7 +114,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets: []
register: secrets_5
register: secrets_7
ignore_errors: yes
- name: cleanup
@ -101,8 +128,10 @@
- secrets_1 is changed
- secrets_2 is not changed
- secrets_3 is changed
- secrets_4 is changed
- secrets_4 is not changed
- secrets_5 is not changed
- secrets_6 is changed
- secrets_7 is not changed
when: docker_api_version is version('1.25', '>=') and docker_py_version is version('2.4.0', '>=')
- assert:
that: