docker_swarm_service: Sort lists when checking for changes (#63887)

* docker_swarm_service: Sort lists when checking for changes

When two lists are checked for changes in this module, the lists are 
reported changed when the order of the items is different. This PR 
resolves this issue.

* docker_swarm_service: Minor typo fix

* docker_swarm_service: Another minor typo

* docker_swarm_service: Should use sorted(), not sort()

* docker_swarm_service: Sort lists of dictionaries

* docker_swarm_service: Fix style issues in tests

* docker_swarm_service: Updates to integration tests

* docker_swarm_service: Casting string types within lists when comparing

* docker_swarm_service: Special handling of unordered networks with ordered aliases

* docker_swarm_service: Sorting network lists

* docker_swarm_serivce: Better unit test code coverage for lists and networks

* docker_swarm_service: Fixed coding style for sanity tests

* docker_swarm_service: More coding style fixes

* docker_swarm_service: Ignoring test for Python < 3

* docker_swarm_service: Update to version info check for backwards compatibility

* docker_swarm_service: Added change fragment #63887

* docker_swarm_service: Better handling of missing sort key for dictionary of lists

* docker_swarm_service: Preventing sorts from modifying in-place

Co-Authored-By: Felix Fontein <felix@fontein.de>

* docker_swarm_service: Removed spurious import in test

* docker_swarm_service: Preventing sorts from modifying more data in-place

Co-Authored-By: Felix Fontein <felix@fontein.de>
This commit is contained in:
Michael Cassaniti 2019-11-24 06:31:35 +11:00 committed by Felix Fontein
parent 136dc27572
commit a096cd08c5
9 changed files with 468 additions and 133 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- "docker_swarm_service - Sort lists when checking for changes."

View file

@ -1244,18 +1244,58 @@ def has_dict_changed(new_dict, old_dict):
return False
def has_list_changed(new_list, old_list):
def has_list_changed(new_list, old_list, sort_lists=True, sort_key=None):
"""
Check two lists has differences.
Check two lists have differences. Sort lists by default.
"""
def sort_list(unsorted_list):
"""
Sort a given list.
The list may contain dictionaries, so use the sort key to handle them.
"""
if unsorted_list and isinstance(unsorted_list[0], dict):
if not sort_key:
raise Exception(
'A sort key was not specified when sorting list'
)
else:
return sorted(unsorted_list, key=lambda k: k[sort_key])
# Either the list is empty or does not contain dictionaries
try:
return sorted(unsorted_list)
except TypeError:
return unsorted_list
if new_list is None:
return False
old_list = old_list or []
if len(new_list) != len(old_list):
return True
for new_item, old_item in zip(new_list, old_list):
if sort_lists:
zip_data = zip(sort_list(new_list), sort_list(old_list))
else:
zip_data = zip(new_list, old_list)
for new_item, old_item in zip_data:
is_same_type = type(new_item) == type(old_item)
if not is_same_type:
if isinstance(new_item, string_types) and isinstance(old_item, string_types):
# Even though the types are different between these items,
# they are both strings. Try matching on the same string type.
try:
new_item_type = type(new_item)
old_item_casted = new_item_type(old_item)
if new_item != old_item_casted:
return True
else:
continue
except UnicodeEncodeError:
# Fallback to assuming the strings are different
return True
else:
return True
if isinstance(new_item, dict):
if has_dict_changed(new_item, old_item):
@ -1266,6 +1306,35 @@ def has_list_changed(new_list, old_list):
return False
def have_networks_changed(new_networks, old_networks):
"""Special case list checking for networks to sort aliases"""
if new_networks is None:
return False
old_networks = old_networks or []
if len(new_networks) != len(old_networks):
return True
zip_data = zip(
sorted(new_networks, key=lambda k: k['id']),
sorted(old_networks, key=lambda k: k['id'])
)
for new_item, old_item in zip_data:
new_item = dict(new_item)
old_item = dict(old_item)
# Sort the aliases
if 'aliases' in new_item:
new_item['aliases'] = sorted(new_item['aliases'] or [])
if 'aliases' in old_item:
old_item['aliases'] = sorted(old_item['aliases'] or [])
if has_dict_changed(new_item, old_item):
return True
return False
class DockerService(DockerBaseClass):
def __init__(self, docker_api_version, docker_py_version):
super(DockerService, self).__init__()
@ -1761,7 +1830,7 @@ class DockerService(DockerBaseClass):
force_update = False
if self.endpoint_mode is not None and self.endpoint_mode != os.endpoint_mode:
differences.add('endpoint_mode', parameter=self.endpoint_mode, active=os.endpoint_mode)
if self.env is not None and self.env != (os.env or []):
if has_list_changed(self.env, os.env):
differences.add('env', parameter=self.env, active=os.env)
if self.log_driver is not None and self.log_driver != os.log_driver:
differences.add('log_driver', parameter=self.log_driver, active=os.log_driver)
@ -1770,26 +1839,26 @@ class DockerService(DockerBaseClass):
if self.mode != os.mode:
needs_rebuild = True
differences.add('mode', parameter=self.mode, active=os.mode)
if has_list_changed(self.mounts, os.mounts):
if has_list_changed(self.mounts, os.mounts, sort_key='target'):
differences.add('mounts', parameter=self.mounts, active=os.mounts)
if has_list_changed(self.configs, os.configs):
if has_list_changed(self.configs, os.configs, sort_key='config_name'):
differences.add('configs', parameter=self.configs, active=os.configs)
if has_list_changed(self.secrets, os.secrets):
if has_list_changed(self.secrets, os.secrets, sort_key='secret_name'):
differences.add('secrets', parameter=self.secrets, active=os.secrets)
if has_list_changed(self.networks, os.networks):
if have_networks_changed(self.networks, os.networks):
differences.add('networks', parameter=self.networks, active=os.networks)
needs_rebuild = not self.can_update_networks
if self.replicas != os.replicas:
differences.add('replicas', parameter=self.replicas, active=os.replicas)
if self.command is not None and self.command != (os.command or []):
if has_list_changed(self.command, os.command, sort_lists=False):
differences.add('command', parameter=self.command, active=os.command)
if self.args is not None and self.args != (os.args or []):
if has_list_changed(self.args, os.args, sort_lists=False):
differences.add('args', parameter=self.args, active=os.args)
if self.constraints is not None and self.constraints != (os.constraints or []):
if has_list_changed(self.constraints, os.constraints):
differences.add('constraints', parameter=self.constraints, active=os.constraints)
if self.placement_preferences is not None and self.placement_preferences != (os.placement_preferences or []):
if has_list_changed(self.placement_preferences, os.placement_preferences, sort_lists=False):
differences.add('placement_preferences', parameter=self.placement_preferences, active=os.placement_preferences)
if self.groups is not None and self.groups != (os.groups or []):
if has_list_changed(self.groups, os.groups):
differences.add('groups', parameter=self.groups, active=os.groups)
if self.labels is not None and self.labels != (os.labels or {}):
differences.add('labels', parameter=self.labels, active=os.labels)
@ -1838,11 +1907,11 @@ class DockerService(DockerBaseClass):
differences.add('image', parameter=self.image, active=change)
if self.user and self.user != os.user:
differences.add('user', parameter=self.user, active=os.user)
if self.dns is not None and self.dns != (os.dns or []):
if has_list_changed(self.dns, os.dns, sort_lists=False):
differences.add('dns', parameter=self.dns, active=os.dns)
if self.dns_search is not None and self.dns_search != (os.dns_search or []):
if has_list_changed(self.dns_search, os.dns_search, sort_lists=False):
differences.add('dns_search', parameter=self.dns_search, active=os.dns_search)
if self.dns_options is not None and self.dns_options != (os.dns_options or []):
if has_list_changed(self.dns_options, os.dns_options):
differences.add('dns_options', parameter=self.dns_options, active=os.dns_options)
if self.has_healthcheck_changed(os):
differences.add('healthcheck', parameter=self.healthcheck, active=os.healthcheck)

View file

@ -97,6 +97,20 @@
register: configs_5
ignore_errors: yes
- name: configs (add idempotency no id and re-ordered)
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_2 }}"
filename: "/tmp/{{ config_name_2 }}.txt"
- config_name: "{{ config_name_1 }}"
filename: "/tmp/{{ config_name_1 }}.txt"
register: configs_6
ignore_errors: yes
- name: configs (empty)
docker_swarm_service:
name: "{{ service_name }}"
@ -104,7 +118,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs: []
register: configs_6
register: configs_7
ignore_errors: yes
- name: configs (empty idempotency)
@ -114,7 +128,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
configs: []
register: configs_7
register: configs_8
ignore_errors: yes
- name: cleanup
@ -130,8 +144,9 @@
- configs_3 is changed
- configs_4 is not changed
- configs_5 is not changed
- configs_6 is changed
- configs_7 is not changed
- configs_6 is not changed
- configs_7 is changed
- configs_8 is not changed
when: docker_api_version is version('1.30', '>=') and docker_py_version is version('2.6.0', '>=')
- assert:

View file

@ -61,6 +61,21 @@
type: "bind"
register: mounts_3
- name: mounts (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
mounts:
- source: "/tmp/"
target: "/tmp/{{ volume_name_2 }}"
type: "bind"
- source: "{{ volume_name_1 }}"
target: "/tmp/{{ volume_name_1 }}"
type: "volume"
register: mounts_4
- name: mounts (empty)
docker_swarm_service:
name: "{{ service_name }}"
@ -68,7 +83,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
mounts: []
register: mounts_4
register: mounts_5
- name: mounts (empty idempotency)
docker_swarm_service:
@ -77,7 +92,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
mounts: []
register: mounts_5
register: mounts_6
- name: cleanup
docker_swarm_service:
@ -90,8 +105,9 @@
- mounts_1 is changed
- mounts_2 is not changed
- mounts_3 is changed
- mounts_4 is changed
- mounts_5 is not changed
- mounts_4 is not changed
- mounts_5 is changed
- mounts_6 is not changed
####################################################################
## mounts.readonly #################################################

View file

@ -99,7 +99,7 @@
- "{{ network_name_2 }}"
register: networks_7
- name: networks (change mixed order)
- name: networks (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
@ -110,17 +110,6 @@
- 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 }}"
@ -129,7 +118,7 @@
command: '/bin/sh -v -c "sleep 10m"'
networks:
- "{{ network_name_2 }}"
register: networks_10
register: networks_9
- name: networks (change less idempotency)
docker_swarm_service:
@ -139,7 +128,7 @@
command: '/bin/sh -v -c "sleep 10m"'
networks:
- "{{ network_name_2 }}"
register: networks_11
register: networks_10
- name: networks (empty)
docker_swarm_service:
@ -148,7 +137,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
networks: []
register: networks_12
register: networks_11
- name: networks (empty idempotency)
docker_swarm_service:
@ -157,7 +146,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
networks: []
register: networks_13
register: networks_12
- name: networks (unknown network)
docker_swarm_service:
@ -167,7 +156,7 @@
command: '/bin/sh -v -c "sleep 10m"'
networks:
- "idonotexist"
register: networks_14
register: networks_13
ignore_errors: yes
- name: networks (missing dict key name)
@ -178,7 +167,7 @@
command: '/bin/sh -v -c "sleep 10m"'
networks:
- foo: "bar"
register: networks_15
register: networks_14
ignore_errors: yes
- name: networks (invalid list type)
@ -189,7 +178,7 @@
command: '/bin/sh -v -c "sleep 10m"'
networks:
- [1, 2, 3]
register: networks_16
register: networks_15
ignore_errors: yes
- name: cleanup
@ -207,18 +196,17 @@
- 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_8 is not changed
- networks_9 is changed
- networks_10 is not changed
- networks_11 is changed
- networks_12 is not changed
- networks_13 is failed
- '"Could not find a network named: ''idonotexist''" in networks_13.msg'
- networks_14 is failed
- '"Could not find a network named: ''idonotexist''" in networks_14.msg'
- "'\"name\" is required when networks are passed as dictionaries.' 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"
- "'Only a list of strings or dictionaries are allowed to be passed as networks' in networks_15.msg"
- assert:
that:
@ -262,6 +250,19 @@
- "alias2"
register: networks_aliases_2
- name: networks.aliases (order 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:
- "alias2"
- "alias1"
register: networks_aliases_3
- name: networks.aliases (change)
docker_swarm_service:
name: "{{ service_name }}"
@ -272,7 +273,7 @@
- name: "{{ network_name_1 }}"
aliases:
- "alias1"
register: networks_aliases_3
register: networks_aliases_4
- name: networks.aliases (empty)
docker_swarm_service:
@ -283,7 +284,7 @@
networks:
- name: "{{ network_name_1 }}"
aliases: []
register: networks_aliases_4
register: networks_aliases_5
- name: networks.aliases (empty idempotency)
docker_swarm_service:
@ -294,7 +295,7 @@
networks:
- name: "{{ network_name_1 }}"
aliases: []
register: networks_aliases_5
register: networks_aliases_6
- name: networks.aliases (invalid type)
docker_swarm_service:
@ -306,7 +307,7 @@
- name: "{{ network_name_1 }}"
aliases:
- [1, 2, 3]
register: networks_aliases_6
register: networks_aliases_7
ignore_errors: yes
- name: cleanup
@ -319,11 +320,12 @@
that:
- networks_aliases_1 is changed
- networks_aliases_2 is not changed
- networks_aliases_3 is changed
- networks_aliases_3 is not 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_aliases_5 is changed
- networks_aliases_6 is not changed
- networks_aliases_7 is failed
- "'Only strings are allowed as network aliases' in networks_aliases_7.msg"
####################################################################
## networks.options ################################################

View file

@ -366,6 +366,18 @@
register: dns_options_3
ignore_errors: yes
- name: dns_options (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
dns_options:
- no-check-names
- "timeout:10"
register: dns_options_4
ignore_errors: yes
- name: dns_options (empty)
docker_swarm_service:
name: "{{ service_name }}"
@ -373,7 +385,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
dns_options: []
register: dns_options_4
register: dns_options_5
ignore_errors: yes
- name: dns_options (empty idempotency)
@ -383,7 +395,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
dns_options: []
register: dns_options_5
register: dns_options_6
ignore_errors: yes
- name: cleanup
@ -397,8 +409,9 @@
- dns_options_1 is changed
- dns_options_2 is not changed
- dns_options_3 is changed
- dns_options_4 is changed
- dns_options_5 is not changed
- dns_options_4 is not changed
- dns_options_5 is changed
- dns_options_6 is not changed
when: docker_api_version is version('1.25', '>=') and docker_py_version is version('2.6.0', '>=')
- assert:
that:
@ -588,16 +601,18 @@
- "TEST2=val3"
register: env_3
- name: env (empty)
- name: env (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
env: []
env:
- "TEST2=val3"
- "TEST1=val1"
register: env_4
- name: env (empty idempotency)
- name: env (empty)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
@ -606,6 +621,15 @@
env: []
register: env_5
- name: env (empty idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
env: []
register: env_6
- name: env (fail unwrapped values)
docker_swarm_service:
name: "{{ service_name }}"
@ -613,7 +637,7 @@
resolve_image: no
env:
TEST1: true
register: env_6
register: env_7
ignore_errors: yes
- name: env (fail invalid formatted string)
@ -624,7 +648,7 @@
env:
- "TEST1=val3"
- "TEST2"
register: env_7
register: env_8
ignore_errors: yes
- name: cleanup
@ -638,10 +662,11 @@
- env_1 is changed
- env_2 is not changed
- env_3 is changed
- env_4 is changed
- env_5 is not changed
- env_6 is failed
- env_4 is not changed
- env_5 is changed
- env_6 is not changed
- env_7 is failed
- env_8 is failed
####################################################################
## env_files #######################################################
@ -802,6 +827,18 @@
register: groups_2
ignore_errors: yes
- name: groups (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
groups:
- "5678"
- "1234"
register: groups_3
ignore_errors: yes
- name: groups (change)
docker_swarm_service:
name: "{{ service_name }}"
@ -810,7 +847,7 @@
command: '/bin/sh -v -c "sleep 10m"'
groups:
- "1234"
register: groups_3
register: groups_4
ignore_errors: yes
- name: groups (empty)
@ -820,7 +857,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
groups: []
register: groups_4
register: groups_5
ignore_errors: yes
- name: groups (empty idempotency)
@ -830,7 +867,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
groups: []
register: groups_5
register: groups_6
ignore_errors: yes
- name: cleanup
@ -843,9 +880,10 @@
that:
- groups_1 is changed
- groups_2 is not changed
- groups_3 is changed
- groups_3 is not changed
- groups_4 is changed
- groups_5 is not changed
- groups_5 is changed
- groups_6 is not changed
when: docker_api_version is version('1.25', '>=') and docker_py_version is version('2.6.0', '>=')
- assert:
that:

View file

@ -142,6 +142,32 @@
register: constraints_3
ignore_errors: yes
- name: placement.constraints (add)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
placement:
constraints:
- "node.role == worker"
- "node.label != non_existent_label"
register: constraints_4
ignore_errors: yes
- name: placement.constraints (order idempotency)
docker_swarm_service:
name: "{{ service_name }}"
image: alpine:3.8
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
placement:
constraints:
- "node.label != non_existent_label"
- "node.role == worker"
register: constraints_5
ignore_errors: yes
- name: placement.constraints (empty)
docker_swarm_service:
name: "{{ service_name }}"
@ -150,7 +176,7 @@
command: '/bin/sh -v -c "sleep 10m"'
placement:
constraints: []
register: constraints_4
register: constraints_6
ignore_errors: yes
- name: placement.constraints (empty idempotency)
@ -161,7 +187,7 @@
command: '/bin/sh -v -c "sleep 10m"'
placement:
constraints: []
register: constraints_5
register: constraints_7
ignore_errors: yes
- name: cleanup
@ -178,6 +204,8 @@
- constraints_3 is changed
- constraints_4 is changed
- constraints_5 is not changed
- constraints_6 is changed
- constraints_7 is not changed
when: docker_api_version is version('1.27', '>=') and docker_py_version is version('2.4.0', '>=')
- assert:
that:

View file

@ -97,6 +97,20 @@
register: secrets_5
ignore_errors: yes
- name: secrets (order 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_2 }}"
filename: "/run/secrets/{{ secret_name_2 }}.txt"
- secret_name: "{{ secret_name_1 }}"
filename: "/run/secrets/{{ secret_name_1 }}.txt"
register: secrets_6
ignore_errors: yes
- name: secrets (empty)
docker_swarm_service:
name: "{{ service_name }}"
@ -104,7 +118,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets: []
register: secrets_6
register: secrets_7
ignore_errors: yes
- name: secrets (empty idempotency)
@ -114,7 +128,7 @@
resolve_image: no
command: '/bin/sh -v -c "sleep 10m"'
secrets: []
register: secrets_7
register: secrets_8
ignore_errors: yes
- name: cleanup
@ -130,8 +144,9 @@
- secrets_3 is changed
- secrets_4 is not changed
- secrets_5 is not changed
- secrets_6 is changed
- secrets_7 is not changed
- secrets_6 is not changed
- secrets_7 is changed
- secrets_8 is not changed
when: docker_api_version is version('1.25', '>=') and docker_py_version is version('2.4.0', '>=')
- assert:
that:

View file

@ -169,60 +169,87 @@ def test_has_dict_changed(docker_swarm_service):
def test_has_list_changed(docker_swarm_service):
# List comparisons without dictionaries
# I could improve the indenting, but pycodestyle wants this instead
assert not docker_swarm_service.has_list_changed(None, None)
assert not docker_swarm_service.has_list_changed(None, [])
assert not docker_swarm_service.has_list_changed(None, [1, 2])
assert not docker_swarm_service.has_list_changed([], None)
assert not docker_swarm_service.has_list_changed([], [])
assert docker_swarm_service.has_list_changed([], [1, 2])
assert docker_swarm_service.has_list_changed([1, 2], None)
assert docker_swarm_service.has_list_changed([1, 2], [])
assert docker_swarm_service.has_list_changed([1, 2, 3], [1, 2])
assert docker_swarm_service.has_list_changed([1, 2], [1, 2, 3])
# Check list sorting
assert not docker_swarm_service.has_list_changed([1, 2], [2, 1])
assert docker_swarm_service.has_list_changed(
[
{"a": 1},
{"b": 1}
],
[
{"a": 1}
]
[1, 2],
[2, 1],
sort_lists=False
)
# Check type matching
assert docker_swarm_service.has_list_changed([None, 1], [2, 1])
assert docker_swarm_service.has_list_changed([2, 1], [None, 1])
assert docker_swarm_service.has_list_changed(
"command --with args",
['command', '--with', 'args']
)
assert docker_swarm_service.has_list_changed(
[
{"a": 1},
],
[
{"a": 1},
{"b": 1},
]
['sleep', '3400'],
[u'sleep', u'3600'],
sort_lists=False
)
# List comparisons with dictionaries
assert not docker_swarm_service.has_list_changed(
[
{"a": 1},
{"b": 1},
],
[
{"a": 1},
{"b": 1}
]
[{'a': 1}],
[{'a': 1}],
sort_key='a'
)
assert not docker_swarm_service.has_list_changed(
None,
[
{"b": 1},
{"a": 1}
]
[{'a': 1}, {'a': 2}],
[{'a': 1}, {'a': 2}],
sort_key='a'
)
with pytest.raises(Exception):
docker_swarm_service.has_list_changed(
[{'a': 1}, {'a': 2}],
[{'a': 1}, {'a': 2}]
)
# List sort checking with sort key
assert not docker_swarm_service.has_list_changed(
[{'a': 1}, {'a': 2}],
[{'a': 2}, {'a': 1}],
sort_key='a'
)
assert docker_swarm_service.has_list_changed(
[],
[
{"b": 1},
{"a": 1}
]
[{'a': 1}, {'a': 2}],
[{'a': 2}, {'a': 1}],
sort_lists=False
)
assert not docker_swarm_service.has_list_changed(
None,
None
assert docker_swarm_service.has_list_changed(
[{'a': 1}, {'a': 2}, {'a': 3}],
[{'a': 2}, {'a': 1}],
sort_key='a'
)
assert not docker_swarm_service.has_list_changed(
[],
None
)
assert not docker_swarm_service.has_list_changed(
None,
[]
assert docker_swarm_service.has_list_changed(
[{'a': 1}, {'a': 2}],
[{'a': 1}, {'a': 2}, {'a': 3}],
sort_lists=False
)
# Additional dictionary elements
assert not docker_swarm_service.has_list_changed(
[
{"src": 1, "dst": 2},
@ -231,7 +258,8 @@ def test_has_list_changed(docker_swarm_service):
[
{"src": 1, "dst": 2, "protocol": "tcp"},
{"src": 1, "dst": 2, "protocol": "udp"},
]
],
sort_key='dst'
)
assert not docker_swarm_service.has_list_changed(
[
@ -241,7 +269,8 @@ def test_has_list_changed(docker_swarm_service):
[
{"src": 1, "dst": 2, "protocol": "udp"},
{"src": 1, "dst": 3, "protocol": "tcp"},
]
],
sort_key='dst'
)
assert docker_swarm_service.has_list_changed(
[
@ -253,7 +282,8 @@ def test_has_list_changed(docker_swarm_service):
{"src": 1, "dst": 3, "protocol": "udp"},
{"src": 1, "dst": 2, "protocol": "tcp"},
{"src": 3, "dst": 4, "protocol": "tcp"},
]
],
sort_key='dst'
)
assert docker_swarm_service.has_list_changed(
[
@ -263,7 +293,8 @@ def test_has_list_changed(docker_swarm_service):
[
{"src": 1, "dst": 2, "protocol": "tcp"},
{"src": 1, "dst": 2, "protocol": "udp"},
]
],
sort_key='dst'
)
assert docker_swarm_service.has_list_changed(
[
@ -273,11 +304,130 @@ def test_has_list_changed(docker_swarm_service):
[
{"src": 1, "dst": 2, "protocol": "udp"},
{"src": 1, "dst": 2, "protocol": "tcp"},
]
],
sort_key='dst'
)
assert not docker_swarm_service.has_list_changed(
[{'id': '123', 'aliases': []}],
[{'id': '123'}]
[{'id': '123'}],
sort_key='id'
)
def test_have_networks_changed(docker_swarm_service):
assert not docker_swarm_service.have_networks_changed(
None,
None
)
assert not docker_swarm_service.have_networks_changed(
[],
None
)
assert not docker_swarm_service.have_networks_changed(
[{'id': 1}],
[{'id': 1}]
)
assert docker_swarm_service.have_networks_changed(
[{'id': 1}],
[{'id': 1}, {'id': 2}]
)
assert not docker_swarm_service.have_networks_changed(
[{'id': 1}, {'id': 2}],
[{'id': 1}, {'id': 2}]
)
assert not docker_swarm_service.have_networks_changed(
[{'id': 1}, {'id': 2}],
[{'id': 2}, {'id': 1}]
)
assert not docker_swarm_service.have_networks_changed(
[
{'id': 1},
{'id': 2, 'aliases': []}
],
[
{'id': 1},
{'id': 2}
]
)
assert docker_swarm_service.have_networks_changed(
[
{'id': 1},
{'id': 2, 'aliases': ['alias1']}
],
[
{'id': 1},
{'id': 2}
]
)
assert docker_swarm_service.have_networks_changed(
[
{'id': 1},
{'id': 2, 'aliases': ['alias1', 'alias2']}
],
[
{'id': 1},
{'id': 2, 'aliases': ['alias1']}
]
)
assert not docker_swarm_service.have_networks_changed(
[
{'id': 1},
{'id': 2, 'aliases': ['alias1', 'alias2']}
],
[
{'id': 1},
{'id': 2, 'aliases': ['alias1', 'alias2']}
]
)
assert not docker_swarm_service.have_networks_changed(
[
{'id': 1},
{'id': 2, 'aliases': ['alias1', 'alias2']}
],
[
{'id': 1},
{'id': 2, 'aliases': ['alias2', 'alias1']}
]
)
assert not docker_swarm_service.have_networks_changed(
[
{'id': 1, 'options': {}},
{'id': 2, 'aliases': ['alias1', 'alias2']}],
[
{'id': 1},
{'id': 2, 'aliases': ['alias2', 'alias1']}
]
)
assert not docker_swarm_service.have_networks_changed(
[
{'id': 1, 'options': {'option1': 'value1'}},
{'id': 2, 'aliases': ['alias1', 'alias2']}],
[
{'id': 1, 'options': {'option1': 'value1'}},
{'id': 2, 'aliases': ['alias2', 'alias1']}
]
)
assert docker_swarm_service.have_networks_changed(
[
{'id': 1, 'options': {'option1': 'value1'}},
{'id': 2, 'aliases': ['alias1', 'alias2']}],
[
{'id': 1, 'options': {'option1': 'value2'}},
{'id': 2, 'aliases': ['alias2', 'alias1']}
]
)