docker_swarm_service: Allow passing dicts in networks (#58961)
* Add support for passing networks as dicts * Add function to compare a list of different objects * Handle comparing falsy values to missing values * Pass docker versions to Service * Move can_update_networks to Service class * Pass Networks in TaskTemplate when supported * Remove weird __str__ * Add networks integration tests * Add unit tests * Add example * Add changelog fragment * Make sure that network options are clean Co-Authored-By: Felix Fontein <felix@fontein.de> * Set networks elements as raw in arg spec Co-Authored-By: Felix Fontein <felix@fontein.de> * Fix wrong variable naming * Check for network options that are not valid * Only check for None options * Validate that aliases is a list
This commit is contained in:
parent
aaaa4f1809
commit
13364fc530
5 changed files with 701 additions and 236 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- "docker_swarm_service - Support passing dictionaries in ``networks`` to allow setting ``aliases`` and ``options``."
|
|
@ -358,7 +358,9 @@ options:
|
||||||
required: yes
|
required: yes
|
||||||
networks:
|
networks:
|
||||||
description:
|
description:
|
||||||
- List of the service networks names.
|
- List of the service networks names or dictionaries.
|
||||||
|
- When passed dictionaries valid sub-options are C(name) which is required and
|
||||||
|
C(aliases) and C(options).
|
||||||
- Prior to API version 1.29, updating and removing networks is not supported.
|
- Prior to API version 1.29, updating and removing networks is not supported.
|
||||||
If changes are made the service will then be removed and recreated.
|
If changes are made the service will then be removed and recreated.
|
||||||
- Corresponds to the C(--network) option of C(docker service create).
|
- Corresponds to the C(--network) option of C(docker service create).
|
||||||
|
@ -997,6 +999,17 @@ EXAMPLES = '''
|
||||||
networks:
|
networks:
|
||||||
- mynetwork
|
- mynetwork
|
||||||
|
|
||||||
|
- name: Set networks as a dictionary
|
||||||
|
docker_swarm_service:
|
||||||
|
name: myservice
|
||||||
|
image: alpine:edge
|
||||||
|
networks:
|
||||||
|
- name: "mynetwork"
|
||||||
|
aliases:
|
||||||
|
- "mynetwork_alias"
|
||||||
|
options:
|
||||||
|
foo: bar
|
||||||
|
|
||||||
- name: Set secrets
|
- name: Set secrets
|
||||||
docker_swarm_service:
|
docker_swarm_service:
|
||||||
name: myservice
|
name: myservice
|
||||||
|
@ -1048,6 +1061,7 @@ from ansible.module_utils.docker.common import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
convert_duration_to_nanosecond,
|
convert_duration_to_nanosecond,
|
||||||
parse_healthcheck,
|
parse_healthcheck,
|
||||||
|
clean_dict_booleans_for_docker_api,
|
||||||
RequestException,
|
RequestException,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1116,6 +1130,58 @@ def get_docker_environment(env, env_files):
|
||||||
return sorted(env_list)
|
return sorted(env_list)
|
||||||
|
|
||||||
|
|
||||||
|
def get_docker_networks(networks, network_ids):
|
||||||
|
"""
|
||||||
|
Validate a list of network names or a list of network dictionaries.
|
||||||
|
Network names will be resolved to ids by using the network_ids mapping.
|
||||||
|
"""
|
||||||
|
if networks is None:
|
||||||
|
return None
|
||||||
|
parsed_networks = []
|
||||||
|
for network in networks:
|
||||||
|
if isinstance(network, string_types):
|
||||||
|
parsed_network = {'name': network}
|
||||||
|
elif isinstance(network, dict):
|
||||||
|
if 'name' not in network:
|
||||||
|
raise TypeError(
|
||||||
|
'"name" is required when networks are passed as dictionaries.'
|
||||||
|
)
|
||||||
|
name = network.pop('name')
|
||||||
|
parsed_network = {'name': name}
|
||||||
|
aliases = network.pop('aliases', None)
|
||||||
|
if aliases is not None:
|
||||||
|
if not isinstance(aliases, list):
|
||||||
|
raise TypeError('"aliases" network option is only allowed as a list')
|
||||||
|
if not all(
|
||||||
|
isinstance(alias, string_types) for alias in aliases
|
||||||
|
):
|
||||||
|
raise TypeError('Only strings are allowed as network aliases.')
|
||||||
|
parsed_network['aliases'] = aliases
|
||||||
|
options = network.pop('options', None)
|
||||||
|
if options is not None:
|
||||||
|
if not isinstance(options, dict):
|
||||||
|
raise TypeError('Only dict is allowed as network options.')
|
||||||
|
parsed_network['options'] = clean_dict_booleans_for_docker_api(options)
|
||||||
|
# Check if any invalid keys left
|
||||||
|
if network:
|
||||||
|
invalid_keys = ', '.join(network.keys())
|
||||||
|
raise TypeError(
|
||||||
|
'%s are not valid keys for the networks option' % invalid_keys
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
'Only a list of strings or dictionaries are allowed to be passed as networks.'
|
||||||
|
)
|
||||||
|
network_name = parsed_network.pop('name')
|
||||||
|
try:
|
||||||
|
parsed_network['id'] = network_ids[network_name]
|
||||||
|
except KeyError as e:
|
||||||
|
raise ValueError('Could not find a network named: %s.' % e)
|
||||||
|
parsed_networks.append(parsed_network)
|
||||||
|
return parsed_networks or []
|
||||||
|
|
||||||
|
|
||||||
def get_nanoseconds_from_raw_option(name, value):
|
def get_nanoseconds_from_raw_option(name, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
|
@ -1154,14 +1220,17 @@ def has_dict_changed(new_dict, old_dict):
|
||||||
if value is not None
|
if value is not None
|
||||||
)
|
)
|
||||||
for option, value in defined_options.items():
|
for option, value in defined_options.items():
|
||||||
if value != old_dict.get(option):
|
old_value = old_dict.get(option)
|
||||||
|
if not value and not old_value:
|
||||||
|
continue
|
||||||
|
if value != old_value:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def has_list_of_dicts_changed(new_list, old_list):
|
def has_list_changed(new_list, old_list):
|
||||||
"""
|
"""
|
||||||
Check two lists of dicts has differences.
|
Check two lists has differences.
|
||||||
"""
|
"""
|
||||||
if new_list is None:
|
if new_list is None:
|
||||||
return False
|
return False
|
||||||
|
@ -1169,13 +1238,20 @@ def has_list_of_dicts_changed(new_list, old_list):
|
||||||
if len(new_list) != len(old_list):
|
if len(new_list) != len(old_list):
|
||||||
return True
|
return True
|
||||||
for new_item, old_item in zip(new_list, old_list):
|
for new_item, old_item in zip(new_list, old_list):
|
||||||
if has_dict_changed(new_item, old_item):
|
is_same_type = type(new_item) == type(old_item)
|
||||||
|
if not is_same_type:
|
||||||
return True
|
return True
|
||||||
|
if isinstance(new_item, dict):
|
||||||
|
if has_dict_changed(new_item, old_item):
|
||||||
|
return True
|
||||||
|
elif new_item != old_item:
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class DockerService(DockerBaseClass):
|
class DockerService(DockerBaseClass):
|
||||||
def __init__(self):
|
def __init__(self, docker_api_version, docker_py_version):
|
||||||
super(DockerService, self).__init__()
|
super(DockerService, self).__init__()
|
||||||
self.image = ""
|
self.image = ""
|
||||||
self.command = None
|
self.command = None
|
||||||
|
@ -1227,7 +1303,9 @@ class DockerService(DockerBaseClass):
|
||||||
self.update_max_failure_ratio = None
|
self.update_max_failure_ratio = None
|
||||||
self.update_order = None
|
self.update_order = None
|
||||||
self.working_dir = None
|
self.working_dir = None
|
||||||
self.can_update_networks = None
|
|
||||||
|
self.docker_api_version = docker_api_version
|
||||||
|
self.docker_py_version = docker_py_version
|
||||||
|
|
||||||
def get_facts(self):
|
def get_facts(self):
|
||||||
return {
|
return {
|
||||||
|
@ -1281,6 +1359,22 @@ class DockerService(DockerBaseClass):
|
||||||
'working_dir': self.working_dir,
|
'working_dir': self.working_dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_update_networks(self):
|
||||||
|
# Before Docker API 1.29 adding/removing networks was not supported
|
||||||
|
return (
|
||||||
|
self.docker_api_version >= LooseVersion('1.29') and
|
||||||
|
self.docker_py_version >= LooseVersion('2.7')
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_use_task_template_networks(self):
|
||||||
|
# In Docker API 1.25 attaching networks to TaskTemplate is preferred over Spec
|
||||||
|
return (
|
||||||
|
self.docker_api_version >= LooseVersion('1.25') and
|
||||||
|
self.docker_py_version >= LooseVersion('2.7')
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_restart_config_from_ansible_params(params):
|
def get_restart_config_from_ansible_params(params):
|
||||||
restart_config = params['restart_config'] or {}
|
restart_config = params['restart_config'] or {}
|
||||||
|
@ -1474,11 +1568,18 @@ class DockerService(DockerBaseClass):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_ansible_params(
|
def from_ansible_params(
|
||||||
cls, ap, old_service, image_digest, can_update_networks, secret_ids, config_ids
|
cls,
|
||||||
|
ap,
|
||||||
|
old_service,
|
||||||
|
image_digest,
|
||||||
|
secret_ids,
|
||||||
|
config_ids,
|
||||||
|
network_ids,
|
||||||
|
docker_api_version,
|
||||||
|
docker_py_version,
|
||||||
):
|
):
|
||||||
s = DockerService()
|
s = DockerService(docker_api_version, docker_py_version)
|
||||||
s.image = image_digest
|
s.image = image_digest
|
||||||
s.can_update_networks = can_update_networks
|
|
||||||
s.args = ap['args']
|
s.args = ap['args']
|
||||||
s.endpoint_mode = ap['endpoint_mode']
|
s.endpoint_mode = ap['endpoint_mode']
|
||||||
s.dns = ap['dns']
|
s.dns = ap['dns']
|
||||||
|
@ -1491,12 +1592,13 @@ class DockerService(DockerBaseClass):
|
||||||
s.labels = ap['labels']
|
s.labels = ap['labels']
|
||||||
s.container_labels = ap['container_labels']
|
s.container_labels = ap['container_labels']
|
||||||
s.mode = ap['mode']
|
s.mode = ap['mode']
|
||||||
s.networks = ap['networks']
|
|
||||||
s.stop_signal = ap['stop_signal']
|
s.stop_signal = ap['stop_signal']
|
||||||
s.user = ap['user']
|
s.user = ap['user']
|
||||||
s.working_dir = ap['working_dir']
|
s.working_dir = ap['working_dir']
|
||||||
s.read_only = ap['read_only']
|
s.read_only = ap['read_only']
|
||||||
|
|
||||||
|
s.networks = get_docker_networks(ap['networks'], network_ids)
|
||||||
|
|
||||||
s.command = ap['command']
|
s.command = ap['command']
|
||||||
if isinstance(s.command, string_types):
|
if isinstance(s.command, string_types):
|
||||||
s.command = shlex.split(s.command)
|
s.command = shlex.split(s.command)
|
||||||
|
@ -1650,13 +1752,13 @@ class DockerService(DockerBaseClass):
|
||||||
if self.mode != os.mode:
|
if self.mode != os.mode:
|
||||||
needs_rebuild = True
|
needs_rebuild = True
|
||||||
differences.add('mode', parameter=self.mode, active=os.mode)
|
differences.add('mode', parameter=self.mode, active=os.mode)
|
||||||
if has_list_of_dicts_changed(self.mounts, os.mounts):
|
if has_list_changed(self.mounts, os.mounts):
|
||||||
differences.add('mounts', parameter=self.mounts, active=os.mounts)
|
differences.add('mounts', parameter=self.mounts, active=os.mounts)
|
||||||
if has_list_of_dicts_changed(self.configs, os.configs):
|
if has_list_changed(self.configs, os.configs):
|
||||||
differences.add('configs', parameter=self.configs, active=os.configs)
|
differences.add('configs', parameter=self.configs, active=os.configs)
|
||||||
if has_list_of_dicts_changed(self.secrets, os.secrets):
|
if has_list_changed(self.secrets, os.secrets):
|
||||||
differences.add('secrets', parameter=self.secrets, active=os.secrets)
|
differences.add('secrets', parameter=self.secrets, active=os.secrets)
|
||||||
if self.networks is not None and self.networks != (os.networks or []):
|
if has_list_changed(self.networks, os.networks):
|
||||||
differences.add('networks', parameter=self.networks, active=os.networks)
|
differences.add('networks', parameter=self.networks, active=os.networks)
|
||||||
needs_rebuild = not self.can_update_networks
|
needs_rebuild = not self.can_update_networks
|
||||||
if self.replicas != os.replicas:
|
if self.replicas != os.replicas:
|
||||||
|
@ -1774,18 +1876,6 @@ class DockerService(DockerBaseClass):
|
||||||
old_image = old_image.split('@')[0]
|
old_image = old_image.split('@')[0]
|
||||||
return self.image != old_image, old_image
|
return self.image != old_image, old_image
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str({
|
|
||||||
'mode': self.mode,
|
|
||||||
'env': self.env,
|
|
||||||
'endpoint_mode': self.endpoint_mode,
|
|
||||||
'mounts': self.mounts,
|
|
||||||
'configs': self.configs,
|
|
||||||
'secrets': self.secrets,
|
|
||||||
'networks': self.networks,
|
|
||||||
'replicas': self.replicas
|
|
||||||
})
|
|
||||||
|
|
||||||
def build_container_spec(self):
|
def build_container_spec(self):
|
||||||
mounts = None
|
mounts = None
|
||||||
if self.mounts is not None:
|
if self.mounts is not None:
|
||||||
|
@ -2000,6 +2090,10 @@ class DockerService(DockerBaseClass):
|
||||||
task_template_args['resources'] = resources
|
task_template_args['resources'] = resources
|
||||||
if self.force_update:
|
if self.force_update:
|
||||||
task_template_args['force_update'] = self.force_update
|
task_template_args['force_update'] = self.force_update
|
||||||
|
if self.can_use_task_template_networks:
|
||||||
|
networks = self.build_networks()
|
||||||
|
if networks:
|
||||||
|
task_template_args['networks'] = networks
|
||||||
return types.TaskTemplate(container_spec=container_spec, **task_template_args)
|
return types.TaskTemplate(container_spec=container_spec, **task_template_args)
|
||||||
|
|
||||||
def build_service_mode(self):
|
def build_service_mode(self):
|
||||||
|
@ -2007,22 +2101,17 @@ class DockerService(DockerBaseClass):
|
||||||
self.replicas = None
|
self.replicas = None
|
||||||
return types.ServiceMode(self.mode, replicas=self.replicas)
|
return types.ServiceMode(self.mode, replicas=self.replicas)
|
||||||
|
|
||||||
def build_networks(self, docker_networks):
|
def build_networks(self):
|
||||||
networks = None
|
networks = None
|
||||||
if self.networks is not None:
|
if self.networks is not None:
|
||||||
networks = []
|
networks = []
|
||||||
for network_name in self.networks:
|
for network in self.networks:
|
||||||
network_id = None
|
docker_network = {'Target': network['id']}
|
||||||
try:
|
if 'aliases' in network:
|
||||||
network_id = list(
|
docker_network['Aliases'] = network['aliases']
|
||||||
filter(lambda n: n['name'] == network_name, docker_networks)
|
if 'options' in network:
|
||||||
)[0]['id']
|
docker_network['DriverOpts'] = network['options']
|
||||||
except (IndexError, KeyError):
|
networks.append(docker_network)
|
||||||
pass
|
|
||||||
if network_id:
|
|
||||||
networks.append({'Target': network_id})
|
|
||||||
else:
|
|
||||||
raise Exception('no docker networks named: %s' % network_name)
|
|
||||||
return networks
|
return networks
|
||||||
|
|
||||||
def build_endpoint_spec(self):
|
def build_endpoint_spec(self):
|
||||||
|
@ -2043,7 +2132,7 @@ class DockerService(DockerBaseClass):
|
||||||
endpoint_spec_args['mode'] = self.endpoint_mode
|
endpoint_spec_args['mode'] = self.endpoint_mode
|
||||||
return types.EndpointSpec(**endpoint_spec_args) if endpoint_spec_args else None
|
return types.EndpointSpec(**endpoint_spec_args) if endpoint_spec_args else None
|
||||||
|
|
||||||
def build_docker_service(self, docker_networks):
|
def build_docker_service(self):
|
||||||
container_spec = self.build_container_spec()
|
container_spec = self.build_container_spec()
|
||||||
placement = self.build_placement()
|
placement = self.build_placement()
|
||||||
task_template = self.build_task_template(container_spec, placement)
|
task_template = self.build_task_template(container_spec, placement)
|
||||||
|
@ -2051,7 +2140,6 @@ class DockerService(DockerBaseClass):
|
||||||
update_config = self.build_update_config()
|
update_config = self.build_update_config()
|
||||||
rollback_config = self.build_rollback_config()
|
rollback_config = self.build_rollback_config()
|
||||||
service_mode = self.build_service_mode()
|
service_mode = self.build_service_mode()
|
||||||
networks = self.build_networks(docker_networks)
|
|
||||||
endpoint_spec = self.build_endpoint_spec()
|
endpoint_spec = self.build_endpoint_spec()
|
||||||
|
|
||||||
service = {'task_template': task_template, 'mode': service_mode}
|
service = {'task_template': task_template, 'mode': service_mode}
|
||||||
|
@ -2059,12 +2147,14 @@ class DockerService(DockerBaseClass):
|
||||||
service['update_config'] = update_config
|
service['update_config'] = update_config
|
||||||
if rollback_config:
|
if rollback_config:
|
||||||
service['rollback_config'] = rollback_config
|
service['rollback_config'] = rollback_config
|
||||||
if networks:
|
|
||||||
service['networks'] = networks
|
|
||||||
if endpoint_spec:
|
if endpoint_spec:
|
||||||
service['endpoint_spec'] = endpoint_spec
|
service['endpoint_spec'] = endpoint_spec
|
||||||
if self.labels:
|
if self.labels:
|
||||||
service['labels'] = self.labels
|
service['labels'] = self.labels
|
||||||
|
if not self.can_use_task_template_networks:
|
||||||
|
networks = self.build_networks()
|
||||||
|
if networks:
|
||||||
|
service['networks'] = networks
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
@ -2075,15 +2165,12 @@ class DockerServiceManager(object):
|
||||||
self.retries = 2
|
self.retries = 2
|
||||||
self.diff_tracker = None
|
self.diff_tracker = None
|
||||||
|
|
||||||
def get_networks_names_ids(self):
|
|
||||||
return [{'name': n['Name'], 'id': n['Id']} for n in self.client.networks()]
|
|
||||||
|
|
||||||
def get_service(self, name):
|
def get_service(self, name):
|
||||||
try:
|
try:
|
||||||
raw_data = self.client.inspect_service(name)
|
raw_data = self.client.inspect_service(name)
|
||||||
except NotFound:
|
except NotFound:
|
||||||
return None
|
return None
|
||||||
ds = DockerService()
|
ds = DockerService(self.client.docker_api_version, self.client.docker_py_version)
|
||||||
|
|
||||||
task_template_data = raw_data['Spec']['TaskTemplate']
|
task_template_data = raw_data['Spec']['TaskTemplate']
|
||||||
ds.image = task_template_data['ContainerSpec']['Image']
|
ds.image = task_template_data['ContainerSpec']['Image']
|
||||||
|
@ -2263,24 +2350,22 @@ class DockerServiceManager(object):
|
||||||
'mode': secret_data['File'].get('Mode')
|
'mode': secret_data['File'].get('Mode')
|
||||||
})
|
})
|
||||||
|
|
||||||
networks_names_ids = self.get_networks_names_ids()
|
|
||||||
raw_networks_data = task_template_data.get('Networks', raw_data['Spec'].get('Networks'))
|
raw_networks_data = task_template_data.get('Networks', raw_data['Spec'].get('Networks'))
|
||||||
if raw_networks_data:
|
if raw_networks_data:
|
||||||
ds.networks = []
|
ds.networks = []
|
||||||
for network_data in raw_networks_data:
|
for network_data in raw_networks_data:
|
||||||
network_name = [network_name_id['name'] for network_name_id in networks_names_ids if
|
network = {'id': network_data['Target']}
|
||||||
network_name_id['id'] == network_data['Target']]
|
if 'Aliases' in network_data:
|
||||||
if len(network_name) == 0:
|
network['aliases'] = network_data['Aliases']
|
||||||
ds.networks.append(network_data['Target'])
|
if 'DriverOpts' in network_data:
|
||||||
else:
|
network['options'] = network_data['DriverOpts']
|
||||||
ds.networks.append(network_name[0])
|
ds.networks.append(network)
|
||||||
|
|
||||||
ds.service_version = raw_data['Version']['Index']
|
ds.service_version = raw_data['Version']['Index']
|
||||||
ds.service_id = raw_data['ID']
|
ds.service_id = raw_data['ID']
|
||||||
return ds
|
return ds
|
||||||
|
|
||||||
def update_service(self, name, old_service, new_service):
|
def update_service(self, name, old_service, new_service):
|
||||||
service_data = new_service.build_docker_service(self.get_networks_names_ids())
|
service_data = new_service.build_docker_service()
|
||||||
result = self.client.update_service(
|
result = self.client.update_service(
|
||||||
old_service.service_id,
|
old_service.service_id,
|
||||||
old_service.service_version,
|
old_service.service_version,
|
||||||
|
@ -2292,7 +2377,7 @@ class DockerServiceManager(object):
|
||||||
self.client.report_warnings(result, ['Warning'])
|
self.client.report_warnings(result, ['Warning'])
|
||||||
|
|
||||||
def create_service(self, name, service):
|
def create_service(self, name, service):
|
||||||
service_data = service.build_docker_service(self.get_networks_names_ids())
|
service_data = service.build_docker_service()
|
||||||
result = self.client.create_service(name=name, **service_data)
|
result = self.client.create_service(name=name, **service_data)
|
||||||
self.client.report_warnings(result, ['Warning'])
|
self.client.report_warnings(result, ['Warning'])
|
||||||
|
|
||||||
|
@ -2313,11 +2398,9 @@ class DockerServiceManager(object):
|
||||||
digest = distribution_data['Descriptor']['digest']
|
digest = distribution_data['Descriptor']['digest']
|
||||||
return '%s@%s' % (name, digest)
|
return '%s@%s' % (name, digest)
|
||||||
|
|
||||||
def can_update_networks(self):
|
def get_networks_names_ids(self):
|
||||||
# Before Docker API 1.29 adding/removing networks was not supported
|
return dict(
|
||||||
return (
|
(network['Name'], network['Id']) for network in self.client.networks()
|
||||||
self.client.docker_api_version >= LooseVersion('1.29') and
|
|
||||||
self.client.docker_py_version >= LooseVersion('2.7')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_missing_secret_ids(self):
|
def get_missing_secret_ids(self):
|
||||||
|
@ -2392,16 +2475,18 @@ class DockerServiceManager(object):
|
||||||
% (module.params['name'], e)
|
% (module.params['name'], e)
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
can_update_networks = self.can_update_networks()
|
|
||||||
secret_ids = self.get_missing_secret_ids()
|
secret_ids = self.get_missing_secret_ids()
|
||||||
config_ids = self.get_missing_config_ids()
|
config_ids = self.get_missing_config_ids()
|
||||||
|
network_ids = self.get_networks_names_ids()
|
||||||
new_service = DockerService.from_ansible_params(
|
new_service = DockerService.from_ansible_params(
|
||||||
module.params,
|
module.params,
|
||||||
current_service,
|
current_service,
|
||||||
image_digest,
|
image_digest,
|
||||||
can_update_networks,
|
|
||||||
secret_ids,
|
secret_ids,
|
||||||
config_ids
|
config_ids,
|
||||||
|
network_ids,
|
||||||
|
self.client.docker_api_version,
|
||||||
|
self.client.docker_py_version
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.client.fail(
|
return self.client.fail(
|
||||||
|
@ -2567,7 +2652,7 @@ def main():
|
||||||
gid=dict(type='str'),
|
gid=dict(type='str'),
|
||||||
mode=dict(type='int'),
|
mode=dict(type='int'),
|
||||||
)),
|
)),
|
||||||
networks=dict(type='list', elements='str'),
|
networks=dict(type='list', elements='raw'),
|
||||||
command=dict(type='raw'),
|
command=dict(type='raw'),
|
||||||
args=dict(type='list', elements='str'),
|
args=dict(type='list', elements='str'),
|
||||||
env=dict(type='raw'),
|
env=dict(type='raw'),
|
||||||
|
|
|
@ -0,0 +1,448 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Registering service name
|
||||||
|
set_fact:
|
||||||
|
service_name: "{{ name_prefix ~ '-networks' }}"
|
||||||
|
network_name_1: "{{ name_prefix ~ '-network-1' }}"
|
||||||
|
network_name_2: "{{ name_prefix ~ '-network-2' }}"
|
||||||
|
|
||||||
|
- name: Registering service name
|
||||||
|
set_fact:
|
||||||
|
service_names: "{{ service_names + [service_name] }}"
|
||||||
|
network_names: "{{ network_names + [network_name_1, network_name_2] }}"
|
||||||
|
|
||||||
|
- docker_network:
|
||||||
|
name: "{{ network_name }}"
|
||||||
|
driver: "overlay"
|
||||||
|
state: present
|
||||||
|
loop:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: network_name
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
## networks #########################################################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
- name: networks
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
register: networks_1
|
||||||
|
|
||||||
|
- name: networks (idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
register: networks_2
|
||||||
|
|
||||||
|
- name: networks (dict idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
register: networks_3
|
||||||
|
|
||||||
|
- name: networks (change more)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
register: networks_4
|
||||||
|
|
||||||
|
- name: networks (change more idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
register: networks_5
|
||||||
|
|
||||||
|
- name: networks (change more dict idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
- name: "{{ network_name_2 }}"
|
||||||
|
register: networks_6
|
||||||
|
|
||||||
|
- name: networks (change more mixed idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
register: networks_7
|
||||||
|
|
||||||
|
- name: networks (change mixed order)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
register: networks_8
|
||||||
|
|
||||||
|
- name: networks (change mixed order idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
register: networks_9
|
||||||
|
|
||||||
|
- name: networks (change less)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
register: networks_10
|
||||||
|
|
||||||
|
- name: networks (change less idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
register: networks_11
|
||||||
|
|
||||||
|
- name: networks (empty)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks: []
|
||||||
|
register: networks_12
|
||||||
|
|
||||||
|
- name: networks (empty idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks: []
|
||||||
|
register: networks_13
|
||||||
|
|
||||||
|
- name: networks (unknown network)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- "idonotexist"
|
||||||
|
register: networks_14
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: networks (missing dict key name)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- foo: "bar"
|
||||||
|
register: networks_15
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: networks (invalid list type)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- [1, 2, 3]
|
||||||
|
register: networks_16
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: cleanup
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
state: absent
|
||||||
|
diff: no
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- networks_1 is changed
|
||||||
|
- networks_2 is not changed
|
||||||
|
- networks_3 is not changed
|
||||||
|
- networks_4 is changed
|
||||||
|
- networks_5 is not changed
|
||||||
|
- networks_6 is not changed
|
||||||
|
- networks_7 is not changed
|
||||||
|
- networks_8 is changed
|
||||||
|
- networks_9 is not changed
|
||||||
|
- networks_10 is changed
|
||||||
|
- networks_11 is not changed
|
||||||
|
- networks_12 is changed
|
||||||
|
- networks_13 is not changed
|
||||||
|
- networks_14 is failed
|
||||||
|
- '"Could not find a network named: ''idonotexist''" in networks_14.msg'
|
||||||
|
- networks_15 is failed
|
||||||
|
- "'\"name\" is required when networks are passed as dictionaries.' in networks_15.msg"
|
||||||
|
- networks_16 is failed
|
||||||
|
- "'Only a list of strings or dictionaries are allowed to be passed as networks' in networks_16.msg"
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- networks_4.rebuilt == false
|
||||||
|
- networks_7.rebuilt == false
|
||||||
|
when: docker_api_version is version('1.29', '>=') and docker_py_version is version('2.7.0', '>=')
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- networks_4.rebuilt == true
|
||||||
|
- networks_7.rebuilt == true
|
||||||
|
when: docker_api_version is version('1.29', '<') or docker_py_version is version('2.7.0', '<')
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
## networks.aliases ################################################
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
- name: networks.aliases
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases:
|
||||||
|
- "alias1"
|
||||||
|
- "alias2"
|
||||||
|
register: networks_aliases_1
|
||||||
|
|
||||||
|
- name: networks.aliases (idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases:
|
||||||
|
- "alias1"
|
||||||
|
- "alias2"
|
||||||
|
register: networks_aliases_2
|
||||||
|
|
||||||
|
- name: networks.aliases (change)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases:
|
||||||
|
- "alias1"
|
||||||
|
register: networks_aliases_3
|
||||||
|
|
||||||
|
- name: networks.aliases (empty)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases: []
|
||||||
|
register: networks_aliases_4
|
||||||
|
|
||||||
|
- name: networks.aliases (empty idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases: []
|
||||||
|
register: networks_aliases_5
|
||||||
|
|
||||||
|
- name: networks.aliases (invalid type)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
aliases:
|
||||||
|
- [1, 2, 3]
|
||||||
|
register: networks_aliases_6
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: cleanup
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
state: absent
|
||||||
|
diff: no
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- networks_aliases_1 is changed
|
||||||
|
- networks_aliases_2 is not changed
|
||||||
|
- networks_aliases_3 is changed
|
||||||
|
- networks_aliases_4 is changed
|
||||||
|
- networks_aliases_5 is not changed
|
||||||
|
- networks_aliases_6 is failed
|
||||||
|
- "'Only strings are allowed as network aliases' in networks_aliases_6.msg"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
## networks.options ################################################
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
- name: networks.options
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options:
|
||||||
|
foo: bar
|
||||||
|
test: hello
|
||||||
|
register: networks_options_1
|
||||||
|
|
||||||
|
- name: networks.options (idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options:
|
||||||
|
foo: bar
|
||||||
|
test: hello
|
||||||
|
register: networks_options_2
|
||||||
|
|
||||||
|
- name: networks.options (change)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options:
|
||||||
|
foo: bar
|
||||||
|
test: hej
|
||||||
|
register: networks_options_3
|
||||||
|
|
||||||
|
- name: networks.options (change less)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options:
|
||||||
|
foo: bar
|
||||||
|
register: networks_options_4
|
||||||
|
|
||||||
|
- name: networks.options (invalid type)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options: [1, 2, 3]
|
||||||
|
register: networks_options_5
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: networks.options (empty)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options: {}
|
||||||
|
register: networks_options_6
|
||||||
|
|
||||||
|
- name: networks.options (empty idempotency)
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
image: alpine:3.8
|
||||||
|
resolve_image: no
|
||||||
|
command: '/bin/sh -v -c "sleep 10m"'
|
||||||
|
networks:
|
||||||
|
- name: "{{ network_name_1 }}"
|
||||||
|
options: {}
|
||||||
|
register: networks_options_7
|
||||||
|
|
||||||
|
- name: cleanup
|
||||||
|
docker_swarm_service:
|
||||||
|
name: "{{ service_name }}"
|
||||||
|
state: absent
|
||||||
|
diff: no
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- networks_options_1 is changed
|
||||||
|
- networks_options_2 is not changed
|
||||||
|
- networks_options_3 is changed
|
||||||
|
- networks_options_4 is changed
|
||||||
|
- networks_options_5 is failed
|
||||||
|
- "'Only dict is allowed as network options' in networks_options_5.msg"
|
||||||
|
- networks_options_6 is changed
|
||||||
|
- networks_options_7 is not changed
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
####################################################################
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
- name: Delete networks
|
||||||
|
docker_network:
|
||||||
|
name: "{{ network_name }}"
|
||||||
|
state: absent
|
||||||
|
force: yes
|
||||||
|
loop:
|
||||||
|
- "{{ network_name_1 }}"
|
||||||
|
- "{{ network_name_2 }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: network_name
|
||||||
|
ignore_errors: yes
|
|
@ -1,25 +1,12 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: Registering container name
|
- name: Registering service name
|
||||||
set_fact:
|
set_fact:
|
||||||
service_name: "{{ name_prefix ~ '-options' }}"
|
service_name: "{{ name_prefix ~ '-options' }}"
|
||||||
network_name_1: "{{ name_prefix ~ '-network-1' }}"
|
|
||||||
network_name_2: "{{ name_prefix ~ '-network-2' }}"
|
|
||||||
|
|
||||||
- name: Registering container name
|
- name: Registering service name
|
||||||
set_fact:
|
set_fact:
|
||||||
service_names: "{{ service_names + [service_name] }}"
|
service_names: "{{ service_names + [service_name] }}"
|
||||||
network_names: "{{ network_names + [network_name_1, network_name_2] }}"
|
|
||||||
|
|
||||||
- docker_network:
|
|
||||||
name: "{{ network_name }}"
|
|
||||||
driver: "overlay"
|
|
||||||
state: present
|
|
||||||
loop:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: network_name
|
|
||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
## args ############################################################
|
## args ############################################################
|
||||||
|
@ -1275,119 +1262,6 @@
|
||||||
- mode_2 is not changed
|
- mode_2 is not changed
|
||||||
- mode_3 is changed
|
- mode_3 is changed
|
||||||
|
|
||||||
####################################################################
|
|
||||||
## networks ########################################################
|
|
||||||
####################################################################
|
|
||||||
|
|
||||||
- name: networks
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
register: networks_1
|
|
||||||
|
|
||||||
- name: networks (idempotency)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
register: networks_2
|
|
||||||
|
|
||||||
- name: networks (change more)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
register: networks_3
|
|
||||||
|
|
||||||
- name: networks (change more idempotency)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
register: networks_4
|
|
||||||
|
|
||||||
- name: networks (change less)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
register: networks_5
|
|
||||||
|
|
||||||
- name: networks (change less idempotency)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks:
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
register: networks_6
|
|
||||||
|
|
||||||
- name: networks (empty)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks: []
|
|
||||||
register: networks_7
|
|
||||||
|
|
||||||
- name: networks (empty idempotency)
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
image: alpine:3.8
|
|
||||||
resolve_image: no
|
|
||||||
command: '/bin/sh -v -c "sleep 10m"'
|
|
||||||
networks: []
|
|
||||||
register: networks_8
|
|
||||||
|
|
||||||
- name: cleanup
|
|
||||||
docker_swarm_service:
|
|
||||||
name: "{{ service_name }}"
|
|
||||||
state: absent
|
|
||||||
diff: no
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- networks_1 is changed
|
|
||||||
- networks_2 is not changed
|
|
||||||
- networks_3 is changed
|
|
||||||
- networks_4 is not changed
|
|
||||||
- networks_5 is changed
|
|
||||||
- networks_6 is not changed
|
|
||||||
- networks_7 is changed
|
|
||||||
- networks_8 is not changed
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- networks_3.rebuilt == false
|
|
||||||
- networks_5.rebuilt == false
|
|
||||||
when: docker_api_version is version('1.29', '>=') and docker_py_version is version('2.7.0', '>=')
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- networks_3.rebuilt == true
|
|
||||||
- networks_5.rebuilt == true
|
|
||||||
when: docker_api_version is version('1.29', '<') or docker_py_version is version('2.7.0', '<')
|
|
||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
## stop_grace_period ###############################################
|
## stop_grace_period ###############################################
|
||||||
####################################################################
|
####################################################################
|
||||||
|
@ -1915,30 +1789,3 @@
|
||||||
- working_dir_1 is changed
|
- working_dir_1 is changed
|
||||||
- working_dir_2 is not changed
|
- working_dir_2 is not changed
|
||||||
- working_dir_3 is changed
|
- working_dir_3 is changed
|
||||||
|
|
||||||
####################################################################
|
|
||||||
####################################################################
|
|
||||||
####################################################################
|
|
||||||
|
|
||||||
- name: Delete networks
|
|
||||||
docker_network:
|
|
||||||
name: "{{ network_name }}"
|
|
||||||
state: absent
|
|
||||||
force: yes
|
|
||||||
loop:
|
|
||||||
- "{{ network_name_1 }}"
|
|
||||||
- "{{ network_name_2 }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: network_name
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- name: Delete volumes
|
|
||||||
docker_volume:
|
|
||||||
name: "{{ volume_name }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- "{{ volume_name_1 }}"
|
|
||||||
- "{{ volume_name_2 }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: volume_name
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
|
@ -168,8 +168,8 @@ def test_has_dict_changed(docker_swarm_service):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_has_list_of_dicts_changed(docker_swarm_service):
|
def test_has_list_changed(docker_swarm_service):
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"a": 1},
|
{"a": 1},
|
||||||
{"b": 1}
|
{"b": 1}
|
||||||
|
@ -178,7 +178,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"a": 1}
|
{"a": 1}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"a": 1},
|
{"a": 1},
|
||||||
],
|
],
|
||||||
|
@ -187,7 +187,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"b": 1},
|
{"b": 1},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"a": 1},
|
{"a": 1},
|
||||||
{"b": 1},
|
{"b": 1},
|
||||||
|
@ -197,33 +197,33 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"b": 1}
|
{"b": 1}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
{"b": 1},
|
{"b": 1},
|
||||||
{"a": 1}
|
{"a": 1}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[],
|
[],
|
||||||
[
|
[
|
||||||
{"b": 1},
|
{"b": 1},
|
||||||
{"a": 1}
|
{"a": 1}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
None,
|
None,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
[],
|
[],
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
None,
|
None,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"src": 1, "dst": 2},
|
{"src": 1, "dst": 2},
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
|
@ -233,7 +233,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert not docker_swarm_service.has_list_of_dicts_changed(
|
assert not docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
{"src": 1, "dst": 3, "protocol": "tcp"},
|
{"src": 1, "dst": 3, "protocol": "tcp"},
|
||||||
|
@ -243,7 +243,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"src": 1, "dst": 3, "protocol": "tcp"},
|
{"src": 1, "dst": 3, "protocol": "tcp"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
{"src": 1, "dst": 2},
|
{"src": 1, "dst": 2},
|
||||||
|
@ -255,7 +255,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"src": 3, "dst": 4, "protocol": "tcp"},
|
{"src": 3, "dst": 4, "protocol": "tcp"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"src": 1, "dst": 3, "protocol": "tcp"},
|
{"src": 1, "dst": 3, "protocol": "tcp"},
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
|
@ -265,7 +265,7 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert docker_swarm_service.has_list_of_dicts_changed(
|
assert docker_swarm_service.has_list_changed(
|
||||||
[
|
[
|
||||||
{"src": 1, "dst": 2, "protocol": "udp"},
|
{"src": 1, "dst": 2, "protocol": "udp"},
|
||||||
{"src": 1, "dst": 2, "protocol": "tcp", "extra": {"test": "foo"}},
|
{"src": 1, "dst": 2, "protocol": "tcp", "extra": {"test": "foo"}},
|
||||||
|
@ -275,3 +275,86 @@ def test_has_list_of_dicts_changed(docker_swarm_service):
|
||||||
{"src": 1, "dst": 2, "protocol": "tcp"},
|
{"src": 1, "dst": 2, "protocol": "tcp"},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
assert not docker_swarm_service.has_list_changed(
|
||||||
|
[{'id': '123', 'aliases': []}],
|
||||||
|
[{'id': '123'}]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_docker_networks(docker_swarm_service):
|
||||||
|
network_names = [
|
||||||
|
'network_1',
|
||||||
|
'network_2',
|
||||||
|
'network_3',
|
||||||
|
'network_4',
|
||||||
|
]
|
||||||
|
networks = [
|
||||||
|
network_names[0],
|
||||||
|
{'name': network_names[1]},
|
||||||
|
{'name': network_names[2], 'aliases': ['networkalias1']},
|
||||||
|
{'name': network_names[3], 'aliases': ['networkalias2'], 'options': {'foo': 'bar'}},
|
||||||
|
]
|
||||||
|
network_ids = {
|
||||||
|
network_names[0]: '1',
|
||||||
|
network_names[1]: '2',
|
||||||
|
network_names[2]: '3',
|
||||||
|
network_names[3]: '4',
|
||||||
|
}
|
||||||
|
parsed_networks = docker_swarm_service.get_docker_networks(
|
||||||
|
networks,
|
||||||
|
network_ids
|
||||||
|
)
|
||||||
|
assert len(parsed_networks) == 4
|
||||||
|
for i, network in enumerate(parsed_networks):
|
||||||
|
assert 'name' not in network
|
||||||
|
assert 'id' in network
|
||||||
|
expected_name = network_names[i]
|
||||||
|
assert network['id'] == network_ids[expected_name]
|
||||||
|
if i == 2:
|
||||||
|
assert network['aliases'] == ['networkalias1']
|
||||||
|
if i == 3:
|
||||||
|
assert network['aliases'] == ['networkalias2']
|
||||||
|
if i == 3:
|
||||||
|
assert 'foo' in network['options']
|
||||||
|
# Test missing name
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks([{'invalid': 'err'}], {'err': 1})
|
||||||
|
# test for invalid aliases type
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
[{'name': 'test', 'aliases': 1}],
|
||||||
|
{'test': 1}
|
||||||
|
)
|
||||||
|
# Test invalid aliases elements
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
[{'name': 'test', 'aliases': [1]}],
|
||||||
|
{'test': 1}
|
||||||
|
)
|
||||||
|
# Test for invalid options type
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
[{'name': 'test', 'options': 1}],
|
||||||
|
{'test': 1}
|
||||||
|
)
|
||||||
|
# Test for invalid networks type
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
1,
|
||||||
|
{'test': 1}
|
||||||
|
)
|
||||||
|
# Test for non existing networks
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
[{'name': 'idontexist'}],
|
||||||
|
{'test': 1}
|
||||||
|
)
|
||||||
|
# Test empty values
|
||||||
|
assert docker_swarm_service.get_docker_networks([], {}) == []
|
||||||
|
assert docker_swarm_service.get_docker_networks(None, {}) is None
|
||||||
|
# Test invalid options
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
docker_swarm_service.get_docker_networks(
|
||||||
|
[{'name': 'test', 'nonexisting_option': 'foo'}],
|
||||||
|
{'test': '1'}
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue