From 5b31f170166520b8e4e23c4eeddaf5054d83f8a0 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 1 Nov 2022 20:48:58 +0100 Subject: [PATCH] Add image_name_mismatch option. (#488) --- .../488-docker_container-image-name.yml | 2 + plugins/module_utils/module_container/base.py | 4 + .../module_container/docker_api.py | 3 + .../module_utils/module_container/module.py | 7 + plugins/modules/docker_container.py | 14 ++ .../docker_container/tasks/tests/options.yml | 149 ++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 changelogs/fragments/488-docker_container-image-name.yml diff --git a/changelogs/fragments/488-docker_container-image-name.yml b/changelogs/fragments/488-docker_container-image-name.yml new file mode 100644 index 00000000..1e642b93 --- /dev/null +++ b/changelogs/fragments/488-docker_container-image-name.yml @@ -0,0 +1,2 @@ +minor_changes: + - "docker_container - added ``image_name_mismatch`` option which allows to control the behavior if the container uses the image specified, but the container's configuration uses a different name for the image than the one provided to the module (https://github.com/ansible-collections/community.docker/issues/485, https://github.com/ansible-collections/community.docker/pull/488)." diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index 71e92a2e..4c02d6c8 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -220,6 +220,10 @@ class EngineDriver(object): def get_image_from_container(self, container): pass + @abc.abstractmethod + def get_image_name_from_container(self, container): + pass + @abc.abstractmethod def is_container_removing(self, container): pass diff --git a/plugins/module_utils/module_container/docker_api.py b/plugins/module_utils/module_container/docker_api.py index 31fde528..a115292d 100644 --- a/plugins/module_utils/module_container/docker_api.py +++ b/plugins/module_utils/module_container/docker_api.py @@ -191,6 +191,9 @@ class DockerAPIEngineDriver(EngineDriver): def get_image_from_container(self, container): return container['Image'] + def get_image_name_from_container(self, container): + return container['Config'].get('Image') + def is_container_removing(self, container): if container.get('State'): return container['State'].get('Status') == 'removing' diff --git a/plugins/module_utils/module_container/module.py b/plugins/module_utils/module_container/module.py index 5c362324..230dbfb4 100644 --- a/plugins/module_utils/module_container/module.py +++ b/plugins/module_utils/module_container/module.py @@ -28,11 +28,13 @@ class Container(DockerBaseClass): self.raw = container self.id = None self.image = None + self.image_name = None self.container = container self.engine_driver = engine_driver if container: self.id = engine_driver.get_container_id(container) self.image = engine_driver.get_image_from_container(container) + self.image_name = engine_driver.get_image_name_from_container(container) self.log(self.container, pretty_print=True) @property @@ -68,6 +70,7 @@ class ContainerManager(DockerBaseClass): self.param_image = self.module.params['image'] self.param_image_comparison = self.module.params['image_comparison'] self.param_image_label_mismatch = self.module.params['image_label_mismatch'] + self.param_image_name_mismatch = self.module.params['image_name_mismatch'] self.param_keep_volumes = self.module.params['keep_volumes'] self.param_kill_signal = self.module.params['kill_signal'] self.param_name = self.module.params['name'] @@ -302,6 +305,9 @@ class ContainerManager(DockerBaseClass): image_different = False if self.all_options['image'].comparison == 'strict': image_different = self._image_is_different(image, container) + if self.param_image_name_mismatch == 'recreate' and self.param_image is not None and self.param_image != container.image_name: + different = True + self.diff_tracker.add('image_name', parameter=self.param_image, active=container.image_name) if image_different or different or self.param_recreate: self.diff_tracker.merge(differences) self.diff['differences'] = differences.get_legacy_docker_container_diffs() @@ -810,6 +816,7 @@ def run_module(engine_driver): image=dict(type='str'), image_comparison=dict(type='str', choices=['desired-image', 'current-image'], default='desired-image'), image_label_mismatch=dict(type='str', choices=['ignore', 'fail'], default='ignore'), + image_name_mismatch=dict(type='str', choices=['ignore', 'recreate'], default='ignore'), keep_volumes=dict(type='bool', default=True), kill_signal=dict(type='str'), name=dict(type='str', required=True), diff --git a/plugins/modules/docker_container.py b/plugins/modules/docker_container.py index 13d257e1..5d7a13c6 100644 --- a/plugins/modules/docker_container.py +++ b/plugins/modules/docker_container.py @@ -436,6 +436,20 @@ options: - 'fail' default: ignore version_added: 2.6.0 + image_name_mismatch: + description: + - Determines what the module does if the image matches, but the image name in the container's configuration + does not match the image name provided to the module. + - "This is ignored if C(image: ignore) is set in I(comparisons)." + - If set to C(recreate) the container will be recreated. + - If set to C(ignore) the container will not be recreated because of this. It might still get recreated for other reasons. + This has been the default behavior of the module for a long time, but might not be what users expect. + type: str + choices: + - recreate + - ignore + default: ignore + version_added: 3.2.0 init: description: - Run an init inside the container that forwards signals and reaps processes. diff --git a/tests/integration/targets/docker_container/tasks/tests/options.yml b/tests/integration/targets/docker_container/tasks/tests/options.yml index ab15b8f1..f55dfd2d 100644 --- a/tests/integration/targets/docker_container/tasks/tests/options.yml +++ b/tests/integration/targets/docker_container/tasks/tests/options.yml @@ -2423,6 +2423,155 @@ - image_label_mismatch_10 is not changed - image_label_mismatch_11 is changed +#################################################################### +## image_name_mismatch ############################################# +#################################################################### + +- name: Pull images to make sure ignore_image test succeeds + # If the image isn't there, it will pull it and return 'changed'. + docker_image: + name: "{{ item }}" + source: pull + loop: + - "{{ docker_test_image_hello_world }}" + - "{{ docker_test_image_registry_nginx }}" + +- name: image + docker_container: + image: "{{ docker_test_image_alpine }}" + command: '/bin/sh -c "sleep 10m"' + name: "{{ cname }}" + state: started + register: image_1 + +- name: image (idempotency) + docker_container: + image: "{{ docker_test_image_alpine }}" + command: '/bin/sh -c "sleep 10m"' + name: "{{ cname }}" + state: started + register: image_2 + diff: true + +- name: ignore_image + docker_container: + image: "{{ docker_test_image_hello_world }}" + ignore_image: yes + name: "{{ cname }}" + state: started + register: ignore_image + diff: true + +- name: ignore_image (labels and env differ in image, image_comparison=current-image) + docker_container: + image: "{{ docker_test_image_registry_nginx }}" + ignore_image: yes + image_comparison: current-image + name: "{{ cname }}" + state: started + register: ignore_image_2 + diff: true + +- name: ignore_image (labels and env differ in image, image_comparison=desired-image) + docker_container: + image: "{{ docker_test_image_registry_nginx }}" + ignore_image: yes + image_comparison: desired-image + name: "{{ cname }}" + state: started + force_kill: yes + register: ignore_image_3 + diff: true + +- name: image change + docker_container: + image: "{{ docker_test_image_hello_world }}" + name: "{{ cname }}" + state: started + force_kill: yes + register: image_change + diff: true + +- name: cleanup + docker_container: + name: "{{ cname }}" + state: absent + force_kill: yes + diff: no + +- assert: + that: + - image_1 is changed + - image_2 is not changed + - ignore_image is not changed + - ignore_image_2 is not changed + - ignore_image_3 is changed + - image_change is changed + +#################################################################### +## image_name_mismatch ############################################# +#################################################################### + +- name: Registering image name + set_fact: + iname_name_mismatch: "{{ cname_prefix ~ '-image-name' }}" +- name: Registering image name + set_fact: + inames: "{{ inames + [iname_name_mismatch] }}" + +- name: Tag hello world image (pulled earlier) with new name + docker_image: + name: "{{ docker_test_image_registry_nginx }}" + source: local + repository: "{{ iname_name_mismatch }}:latest" + +- name: image_name_mismatch + docker_container: + image: "{{ docker_test_image_registry_nginx }}" + command: '/bin/sh -c "sleep 10m"' + name: "{{ cname }}" + state: started + register: image_name_mismatch_1 + +- name: image_name_mismatch (ignore) + docker_container: + image: "{{ iname_name_mismatch }}:latest" + command: '/bin/sh -c "sleep 10m"' + name: "{{ cname }}" + image_name_mismatch: ignore + state: started + register: image_name_mismatch_2 + +- name: image_name_mismatch (recreate) + docker_container: + image: "{{ iname_name_mismatch }}:latest" + command: '/bin/sh -c "sleep 10m"' + name: "{{ cname }}" + image_name_mismatch: recreate + state: started + force_kill: yes + register: image_name_mismatch_3 + +- name: Cleanup container + docker_container: + name: "{{ cname }}" + state: absent + force_kill: yes + diff: no + +- name: Cleanup image + docker_image: + name: "{{ iname_name_mismatch }}" + state: absent + diff: no + +- assert: + that: + - image_name_mismatch_1 is changed + - image_name_mismatch_2 is not changed + - image_name_mismatch_3 is changed + - image_name_mismatch_3.container.Image == image_name_mismatch_2.container.Image + #################################################################### ## ipc_mode ######################################################## ####################################################################