[2.8] docker_container: fix idempotency for network IP addresses (#62959)

* docker_container: fix idempotency for network IP addresses (#62928)

* Specifying IP addresses needs API version 1.22 or newer.

* Simplify code.

* Use IPAMConfig.IPv*Address instead of IPAddress and GlobalIPv6Address.

* Add changelog.

* Fix syntax errors.

* Add integration test.

* Don't rely on netaddr.

* Normalize IPv6 addresses before comparison.

* Install netaddr, and use it.

(cherry picked from commit 62c0cae29a)

* Remove IPv6 tests, use intermediate state of tests from PR which don't need ipaddr filter.
pull/63339/head
Felix Fontein 6 years ago committed by Toshio Kuratomi
parent eeb829ccaa
commit ddd9f6081e

@ -0,0 +1,4 @@
bugfixes:
- "docker_container - fix idempotency for IP addresses for networks. The old implementation checked the effective
IP addresses assigned by the Docker daemon, and not the specified ones. This causes idempotency issues for
containers which are not running, since they have no effective IP addresses assigned."

@ -1999,7 +1999,8 @@ class Container(DockerBaseClass):
connected_networks = self.container['NetworkSettings']['Networks']
for network in self.parameters.networks:
if connected_networks.get(network['name'], None) is None:
network_info = connected_networks.get(network['name'])
if network_info is None:
different = True
differences.append(dict(
parameter=network,
@ -2007,18 +2008,19 @@ class Container(DockerBaseClass):
))
else:
diff = False
if network.get('ipv4_address') and network['ipv4_address'] != connected_networks[network['name']].get('IPAddress'):
network_info_ipam = network_info.get('IPAMConfig', {})
if network.get('ipv4_address') and network['ipv4_address'] != network_info_ipam.get('IPv4Address'):
diff = True
if network.get('ipv6_address') and network['ipv6_address'] != connected_networks[network['name']].get('GlobalIPv6Address'):
if network.get('ipv6_address') and network['ipv6_address'] != network_info_ipam.get('IPv6Address'):
diff = True
if network.get('aliases'):
if not compare_generic(network['aliases'], connected_networks[network['name']].get('Aliases'), 'allow_more_present', 'set'):
if not compare_generic(network['aliases'], network_info.get('Aliases'), 'allow_more_present', 'set'):
diff = True
if network.get('links'):
expected_links = []
for link, alias in network['links']:
expected_links.append("%s:%s" % (link, alias))
if not compare_generic(expected_links, connected_networks[network['name']].get('Links'), 'allow_more_present', 'set'):
if not compare_generic(expected_links, network_info.get('Links'), 'allow_more_present', 'set'):
diff = True
if diff:
different = True
@ -2026,10 +2028,10 @@ class Container(DockerBaseClass):
parameter=network,
container=dict(
name=network['name'],
ipv4_address=connected_networks[network['name']].get('IPAddress'),
ipv6_address=connected_networks[network['name']].get('GlobalIPv6Address'),
aliases=connected_networks[network['name']].get('Aliases'),
links=connected_networks[network['name']].get('Links')
ipv4_address=network_info_ipam.get('IPv4Address'),
ipv6_address=network_info_ipam.get('IPv6Address'),
aliases=network_info.get('Aliases'),
links=network_info.get('Links')
)
))
return different, differences
@ -2892,7 +2894,8 @@ class AnsibleDockerClientContainer(AnsibleDockerClient):
uts=dict(docker_py_version='3.5.0', docker_api_version='1.25'),
pids_limit=dict(docker_py_version='1.10.0', docker_api_version='1.23'),
# specials
ipvX_address_supported=dict(docker_py_version='1.9.0', detect_usage=detect_ipvX_address_usage,
ipvX_address_supported=dict(docker_py_version='1.9.0', docker_api_version='1.22',
detect_usage=detect_ipvX_address_usage,
usage_msg='ipv4_address or ipv6_address in networks'),
stop_timeout=dict(), # see _get_additional_minimal_versions()
)

@ -5,10 +5,11 @@
cname_h1: "{{ cname_prefix ~ '-network-h1' }}"
nname_1: "{{ cname_prefix ~ '-network-1' }}"
nname_2: "{{ cname_prefix ~ '-network-2' }}"
nname_3: "{{ cname_prefix ~ '-network-3' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname, cname_h1] }}"
dnetworks: "{{ dnetworks + [nname_1, nname_2] }}"
dnetworks: "{{ dnetworks + [nname_1, nname_2, nname_3] }}"
- name: Create networks
docker_network:
@ -21,6 +22,26 @@
loop_var: network_name
when: docker_py_version is version('1.10.0', '>=')
- set_fact:
subnet_ipv4_base: 192.168.{{ 64 + (192 | random) }}
- set_fact:
subnet_ipv4: "{{ subnet_ipv4_base }}.0/24"
nname_3_ipv4_2: "{{ subnet_ipv4_base }}.2"
nname_3_ipv4_3: "{{ subnet_ipv4_base }}.3"
nname_3_ipv4_4: "{{ subnet_ipv4_base }}.4"
- debug:
msg: "Chose random IPv4 subnet {{ subnet_ipv4 }}"
- name: Create network with fixed IPv4 subnet
docker_network:
name: "{{ nname_3 }}"
ipam_config:
- subnet: "{{ subnet_ipv4 }}"
state: present
when: docker_py_version is version('1.10.0', '>=')
####################################################################
## network_mode ####################################################
####################################################################
@ -535,6 +556,108 @@
when: docker_py_version is version('1.10.0', '>=')
####################################################################
## networks with IP address ########################################
####################################################################
- block:
- name: create container (stopped) with one network and fixed IP
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_2 }}"
networks_cli_compatible: yes
register: networks_1
- name: create container (stopped) with one network and fixed IP (idempotent)
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_2 }}"
networks_cli_compatible: yes
register: networks_2
- name: create container (stopped) with one network and fixed IP (different IPv4)
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_3 }}"
networks_cli_compatible: yes
register: networks_3
- name: create container (started) with one network and fixed IP
docker_container:
name: "{{ cname }}"
state: started
register: networks_5
- name: create container (started) with one network and fixed IP (different IPv4)
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_4 }}"
networks_cli_compatible: yes
force_kill: yes
register: networks_6
- name: create container (started) with one network and fixed IP (idempotent)
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_4 }}"
networks_cli_compatible: yes
register: networks_8
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: yes
diff: no
- assert:
that:
- networks_1 is changed
- networks_1.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_2
- networks_1.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_2 is not changed
- networks_2.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_2
- networks_2.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_3 is changed
- networks_3.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_3
- networks_3.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_5 is changed
- networks_5.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_3
- networks_5.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_3
- networks_6 is changed
- networks_6.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_4
- networks_6.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_4
- networks_8 is not changed
- networks_8.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_4
- networks_8.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_4
when: docker_py_version is version('1.10.0', '>=')
####################################################################
####################################################################
####################################################################
@ -547,6 +670,7 @@
loop:
- "{{ nname_1 }}"
- "{{ nname_2 }}"
- "{{ nname_3 }}"
loop_control:
loop_var: network_name
when: docker_py_version is version('1.10.0', '>=')

Loading…
Cancel
Save