From ebe2fd0647e4e761a5be1dbae1ea8462621a5357 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Fri, 19 Jul 2024 10:26:52 +0200 Subject: [PATCH 1/2] Pass networks to Daemon on container creation. --- .../933-docker_container-networks.yml | 8 ++++++++ plugins/module_utils/module_container/base.py | 2 +- .../module_container/docker_api.py | 19 ++++++++++++++++--- .../module_utils/module_container/module.py | 11 +++++++++-- plugins/modules/docker_container.py | 3 +++ 5 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/933-docker_container-networks.yml diff --git a/changelogs/fragments/933-docker_container-networks.yml b/changelogs/fragments/933-docker_container-networks.yml new file mode 100644 index 000000000..e07dc0d69 --- /dev/null +++ b/changelogs/fragments/933-docker_container-networks.yml @@ -0,0 +1,8 @@ +minor_changes: + - "docker_container - when creating a network, directly pass the networks to connect to to the Docker Daemon. + This makes creation more efficient and works around a bug in Docker Daemon that does not use the specified + MAC address in at least some cases, though only for creation (https://github.com/ansible-collections/community.docker/pull/933)." +known_issues: + - "docker_container - when specifying a MAC address for a container's network, and the network is attached + after container creation (for example, due to idempotency checks), the MAC address is at least in some + cases ignored by the Docker Daemon (https://github.com/ansible-collections/community.docker/pull/933)." diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index e10acea84..3d1708c87 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -300,7 +300,7 @@ def connect_container_to_network(self, client, container_id, network_id, paramet pass @abc.abstractmethod - def create_container(self, client, container_name, create_parameters): + def create_container(self, client, container_name, create_parameters, networks=None): pass @abc.abstractmethod diff --git a/plugins/module_utils/module_container/docker_api.py b/plugins/module_utils/module_container/docker_api.py index b130e8c7d..009f91daa 100644 --- a/plugins/module_utils/module_container/docker_api.py +++ b/plugins/module_utils/module_container/docker_api.py @@ -244,8 +244,8 @@ def unpause_container(self, client, container_id): def disconnect_container_from_network(self, client, container_id, network_id): client.post_json('/networks/{0}/disconnect', network_id, data={'Container': container_id}) - def connect_container_to_network(self, client, container_id, network_id, parameters=None): - parameters = (parameters or {}).copy() + def _create_endpoint_config(self, parameters): + parameters = parameters.copy() params = {} for para, dest_para in { 'ipv4_address': 'IPv4Address', @@ -268,16 +268,29 @@ def connect_container_to_network(self, client, container_id, network_id, paramet ipam_config[param] = params.pop(param) if ipam_config: params['IPAMConfig'] = ipam_config + return params + + def connect_container_to_network(self, client, container_id, network_id, parameters=None): + parameters = (parameters or {}).copy() + params = self._create_endpoint_config(parameters or {}) data = { 'Container': container_id, 'EndpointConfig': params, } client.post_json('/networks/{0}/connect', network_id, data=data) - def create_container(self, client, container_name, create_parameters): + def create_container(self, client, container_name, create_parameters, networks=None): params = {'name': container_name} if 'platform' in create_parameters: params['platform'] = create_parameters.pop('platform') + if networks is not None: + create_parameters = create_parameters.copy() + create_parameters['NetworkingConfig'] = { + 'EndpointsConfig': { + network: self._create_endpoint_config(network_params) + for network, network_params in networks.items() + } + } new_container = client.post_json_to_json('/containers/create', data=create_parameters, params=params) client.report_warnings(new_container) return new_container['Id'] diff --git a/plugins/module_utils/module_container/module.py b/plugins/module_utils/module_container/module.py index b58ad351a..d780535b7 100644 --- a/plugins/module_utils/module_container/module.py +++ b/plugins/module_utils/module_container/module.py @@ -766,12 +766,19 @@ def container_create(self, image): self.log("create container") self.log("image: %s parameters:" % image) self.log(create_parameters, pretty_print=True) - self.results['actions'].append(dict(created="Created container", create_parameters=create_parameters)) + networks = {} + if self.module.params['networks']: + for network in self.module.params['networks']: + networks[network['name']] = { + key: value for key, value in network.items() + if key not in ('name', 'id') + } + self.results['actions'].append(dict(created="Created container", create_parameters=create_parameters, networks=networks)) self.results['changed'] = True new_container = None if not self.check_mode: try: - container_id = self.engine_driver.create_container(self.client, self.param_name, create_parameters) + container_id = self.engine_driver.create_container(self.client, self.param_name, create_parameters, networks=networks) except Exception as exc: self.fail("Error creating container: %s" % to_native(exc)) return self._get_container(container_id) diff --git a/plugins/modules/docker_container.py b/plugins/modules/docker_container.py index bdc92e570..832a2cf46 100644 --- a/plugins/modules/docker_container.py +++ b/plugins/modules/docker_container.py @@ -722,6 +722,9 @@ description: - Endpoint MAC address (for example, V(92:d0:c6:0a:29:33)). - This is only available for Docker API version 1.44 and later. + - Please note that when a container is attached to a network after creation, + this is currently ignored by the Docker Daemon at least in some cases. + When passed on creation, this seems to work better. type: str version_added: 3.6.0 networks_cli_compatible: From 1761cbfa720e28a3afad3f9ca3c96b1da99395ec Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 20 Jul 2024 00:50:03 +0200 Subject: [PATCH 2/2] Restore old behavior, and only provide all networks on creation for API 1.44+. --- changelogs/fragments/933-docker_container-networks.yml | 10 +++++++--- plugins/module_utils/module_container/base.py | 3 +++ plugins/module_utils/module_container/docker_api.py | 9 ++++++--- plugins/module_utils/module_container/module.py | 7 +++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/changelogs/fragments/933-docker_container-networks.yml b/changelogs/fragments/933-docker_container-networks.yml index e07dc0d69..5484fe613 100644 --- a/changelogs/fragments/933-docker_container-networks.yml +++ b/changelogs/fragments/933-docker_container-networks.yml @@ -1,7 +1,11 @@ minor_changes: - - "docker_container - when creating a network, directly pass the networks to connect to to the Docker Daemon. - This makes creation more efficient and works around a bug in Docker Daemon that does not use the specified - MAC address in at least some cases, though only for creation (https://github.com/ansible-collections/community.docker/pull/933)." + - "docker_container - when creating a container, directly pass all networks to connect to to the Docker Daemon + for API version 1.44 and newer. This makes creation more efficient and works around a bug in Docker Daemon that + does not use the specified MAC address in at least some cases, though only for creation + (https://github.com/ansible-collections/community.docker/pull/933)." +bugfixes: + - "docker_container - restore behavior of the module from community.docker 2.x.y that passes the first network + to the Docker Deamon while creating the container (https://github.com/ansible-collections/community.docker/pull/933)." known_issues: - "docker_container - when specifying a MAC address for a container's network, and the network is attached after container creation (for example, due to idempotency checks), the MAC address is at least in some diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index 3d1708c87..3953177f6 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -299,6 +299,9 @@ def disconnect_container_from_network(self, client, container_id, network_id): def connect_container_to_network(self, client, container_id, network_id, parameters=None): pass + def create_container_supports_more_than_one_network(self, client): + return False + @abc.abstractmethod def create_container(self, client, container_name, create_parameters, networks=None): pass diff --git a/plugins/module_utils/module_container/docker_api.py b/plugins/module_utils/module_container/docker_api.py index 009f91daa..4e5b891cb 100644 --- a/plugins/module_utils/module_container/docker_api.py +++ b/plugins/module_utils/module_container/docker_api.py @@ -279,6 +279,9 @@ def connect_container_to_network(self, client, container_id, network_id, paramet } client.post_json('/networks/{0}/connect', network_id, data=data) + def create_container_supports_more_than_one_network(self, client): + return client.docker_api_version >= LooseVersion('1.44') + def create_container(self, client, container_name, create_parameters, networks=None): params = {'name': container_name} if 'platform' in create_parameters: @@ -286,10 +289,10 @@ def create_container(self, client, container_name, create_parameters, networks=N if networks is not None: create_parameters = create_parameters.copy() create_parameters['NetworkingConfig'] = { - 'EndpointsConfig': { - network: self._create_endpoint_config(network_params) + 'EndpointsConfig': dict( + (network, self._create_endpoint_config(network_params)) for network, network_params in networks.items() - } + ) } new_container = client.post_json_to_json('/containers/create', data=create_parameters, params=params) client.report_warnings(new_container) diff --git a/plugins/module_utils/module_container/module.py b/plugins/module_utils/module_container/module.py index d780535b7..858f4537a 100644 --- a/plugins/module_utils/module_container/module.py +++ b/plugins/module_utils/module_container/module.py @@ -767,8 +767,11 @@ def container_create(self, image): self.log("image: %s parameters:" % image) self.log(create_parameters, pretty_print=True) networks = {} - if self.module.params['networks']: - for network in self.module.params['networks']: + if self.param_networks_cli_compatible and self.module.params['networks']: + network_list = self.module.params['networks'] + if not self.engine_driver.create_container_supports_more_than_one_network(self.client): + network_list = network_list[:1] + for network in network_list: networks[network['name']] = { key: value for key, value in network.items() if key not in ('name', 'id')