From 20e78a92e0b6f90018fd17e249c580f134e8a6e2 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 31 Dec 2023 09:51:42 +0100 Subject: [PATCH] Add docker_image_pull module (#728) * Add docker_image_pull module. * Support platform during idempotency check. * Add diff mode, extend tests. * Add image ID tests. --- README.md | 1 + meta/runtime.yml | 1 + plugins/modules/docker_image.py | 5 + plugins/modules/docker_image_pull.py | 215 ++++++++++++++++++ .../targets/docker_image_pull/aliases | 6 + .../targets/docker_image_pull/meta/main.yml | 8 + .../targets/docker_image_pull/tasks/main.yml | 13 ++ .../docker_image_pull/tasks/run-test.yml | 7 + .../targets/docker_image_pull/tasks/test.yml | 38 ++++ .../docker_image_pull/tasks/tests/basic.yml | 198 ++++++++++++++++ .../tasks/tests/image-ids.yml | 91 ++++++++ .../tasks/tests/registry.yml | 125 ++++++++++ .../tasks/tests/registry.yml | 3 +- .../targets/setup_docker/vars/main.yml | 2 + 14 files changed, 711 insertions(+), 2 deletions(-) create mode 100644 plugins/modules/docker_image_pull.py create mode 100644 tests/integration/targets/docker_image_pull/aliases create mode 100644 tests/integration/targets/docker_image_pull/meta/main.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/main.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/run-test.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/test.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/tests/basic.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/tests/image-ids.yml create mode 100644 tests/integration/targets/docker_image_pull/tasks/tests/registry.yml diff --git a/README.md b/README.md index 21a10379..25ea364d 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ If you use the Ansible package and do not update collections independently, use - community.docker.docker_image: manage Docker images - community.docker.docker_image_info: retrieve information on Docker images - community.docker.docker_image_load: load Docker images from archives + - community.docker.docker_image_pull: pull Docker images from registries - community.docker.docker_image_push: push Docker images to registries - community.docker.docker_login: log in and out to/from registries - community.docker.docker_network: manage Docker networks diff --git a/meta/runtime.yml b/meta/runtime.yml index a4eb75ac..bc5aca47 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -16,6 +16,7 @@ action_groups: - docker_image - docker_image_info - docker_image_load + - docker_image_pull - docker_image_push - docker_login - docker_network diff --git a/plugins/modules/docker_image.py b/plugins/modules/docker_image.py index 613e8ea4..becb27d5 100644 --- a/plugins/modules/docker_image.py +++ b/plugins/modules/docker_image.py @@ -250,6 +250,11 @@ author: - Chris Houseknecht (@chouseknecht) - Sorin Sbarnea (@ssbarnea) +seealso: + - module: community.docker.docker_image_info + - module: community.docker.docker_image_load + - module: community.docker.docker_image_pull + - module: community.docker.docker_image_push ''' EXAMPLES = ''' diff --git a/plugins/modules/docker_image_pull.py b/plugins/modules/docker_image_pull.py new file mode 100644 index 00000000..ef6eea87 --- /dev/null +++ b/plugins/modules/docker_image_pull.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +# +# Copyright (c) 2023, Felix Fontein +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: docker_image_pull +version_added: 3.6.0 +short_description: Pull Docker images from registries +description: + - Pulls a Docker image from a registry. + +extends_documentation_fragment: + - community.docker.docker.api_documentation + - community.docker.attributes + - community.docker.attributes.actiongroup_docker + +attributes: + check_mode: + support: partial + details: + - When trying to pull an image with O(pull=always), the module assumes this is always changed in check mode. + - When check mode is combined with diff mode, the pulled image's ID is always shown as V(unknown) in the diff. + diff_mode: + support: full + +options: + name: + description: + - "Image name. Name format must be one of V(name), V(repository/name), or V(registry_server:port/name). + - The name can optionally include the tag by appending V(:tag_name), or it can contain a digest by appending V(@hash:digest)." + type: str + required: true + tag: + description: + - Used to select an image when pulling. Defaults to V(latest). + - If O(name) parameter format is C(name:tag) or C(image@hash:digest), then O(tag) will be ignored. + type: str + default: latest + platform: + description: + - Ask for this specific platform when pulling. + type: str + pull: + description: + - Determines when to pull an image. + - If V(always), will always pull the image. + - If V(not_present), will only pull the image if no image of the name exists on the current Docker daemon, + or if O(platform) does not match. + type: str + choices: + - always + - not_present + default: always + +requirements: + - "Docker API >= 1.25" + +author: + - Felix Fontein (@felixfontein) +''' + +EXAMPLES = ''' +- name: Pull an image + community.docker.docker_image_pull: + name: pacur/centos-7 + # Select platform for pulling. If not specified, will pull whatever docker prefers. + platform: amd64 +''' + +RETURN = ''' +image: + description: Image inspection results for the affected image. + returned: success + type: dict + sample: {} +''' + +import traceback + +from ansible.module_utils.common.text.converters import to_native + +from ansible_collections.community.docker.plugins.module_utils.common_api import ( + AnsibleDockerClient, + RequestException, +) + +from ansible_collections.community.docker.plugins.module_utils.util import ( + DockerBaseClass, + is_image_name_id, + is_valid_tag, +) + +from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException +from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( + parse_repository_tag, +) + +from ansible_collections.community.docker.plugins.module_utils._platform import ( + normalize_platform_string, + compare_platform_strings, + compose_platform_string, +) + + +def image_info(image): + result = {} + if image: + result['id'] = image['Id'] + else: + result['exists'] = False + return result + + +class ImagePuller(DockerBaseClass): + def __init__(self, client): + super(ImagePuller, self).__init__() + + self.client = client + self.check_mode = self.client.check_mode + + parameters = self.client.module.params + self.name = parameters['name'] + self.tag = parameters['tag'] + self.platform = parameters['platform'] + self.pull_mode = parameters['pull'] + + if is_image_name_id(self.name): + self.client.fail("Cannot pull an image by ID") + if not is_valid_tag(self.tag, allow_empty=True): + self.client.fail('"{0}" is not a valid docker tag!'.format(self.tag)) + + # If name contains a tag, it takes precedence over tag parameter. + repo, repo_tag = parse_repository_tag(self.name) + if repo_tag: + self.name = repo + self.tag = repo_tag + + def pull(self): + image = self.client.find_image(name=self.name, tag=self.tag) + results = dict( + changed=False, + actions=[], + image=image or {}, + diff=dict(before=image_info(image), after=image_info(image)), + ) + + if image and self.pull_mode == 'not_present': + if self.platform is None: + return results + host_info = self.client.info() + wanted_platform = normalize_platform_string( + self.platform, + daemon_os=host_info.get('OSType'), + daemon_arch=host_info.get('Architecture'), + ) + image_platform = compose_platform_string( + os=image.get('Os'), + arch=image.get('Architecture'), + variant=image.get('Variant'), + daemon_os=host_info.get('OSType'), + daemon_arch=host_info.get('Architecture'), + ) + if compare_platform_strings(wanted_platform, image_platform): + return results + + results['actions'].append('Pulled image %s:%s' % (self.name, self.tag)) + if self.check_mode: + results['changed'] = True + results['diff']['after'] = image_info(dict(Id='unknown')) + else: + results['image'], not_changed = self.client.pull_image(self.name, tag=self.tag, platform=self.platform) + results['changed'] = not not_changed + results['diff']['after'] = image_info(results['image']) + + return results + + +def main(): + argument_spec = dict( + name=dict(type='str', required=True), + tag=dict(type='str', default='latest'), + platform=dict(type='str'), + pull=dict(type='str', choices=['always', 'not_present'], default='always'), + ) + + option_minimal_versions = dict( + platform=dict(docker_api_version='1.32'), + ) + + client = AnsibleDockerClient( + argument_spec=argument_spec, + supports_check_mode=True, + option_minimal_versions=option_minimal_versions, + ) + + try: + results = ImagePuller(client).pull() + client.module.exit_json(**results) + except DockerException as e: + client.fail('An unexpected Docker error occurred: {0}'.format(to_native(e)), exception=traceback.format_exc()) + except RequestException as e: + client.fail( + 'An unexpected requests error occurred when trying to talk to the Docker daemon: {0}'.format(to_native(e)), + exception=traceback.format_exc()) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/docker_image_pull/aliases b/tests/integration/targets/docker_image_pull/aliases new file mode 100644 index 00000000..2e1acc0a --- /dev/null +++ b/tests/integration/targets/docker_image_pull/aliases @@ -0,0 +1,6 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/4 +destructive diff --git a/tests/integration/targets/docker_image_pull/meta/main.yml b/tests/integration/targets/docker_image_pull/meta/main.yml new file mode 100644 index 00000000..ff316450 --- /dev/null +++ b/tests/integration/targets/docker_image_pull/meta/main.yml @@ -0,0 +1,8 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +dependencies: + - setup_docker_registry + - setup_docker_python_deps diff --git a/tests/integration/targets/docker_image_pull/tasks/main.yml b/tests/integration/targets/docker_image_pull/tasks/main.yml new file mode 100644 index 00000000..88b23cfe --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/main.yml @@ -0,0 +1,13 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +- when: ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['CentOS6', 'RedHat6'] + include_tasks: + file: test.yml diff --git a/tests/integration/targets/docker_image_pull/tasks/run-test.yml b/tests/integration/targets/docker_image_pull/tasks/run-test.yml new file mode 100644 index 00000000..65853ddd --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/run-test.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: "Loading tasks from {{ item }}" + include_tasks: "{{ item }}" diff --git a/tests/integration/targets/docker_image_pull/tasks/test.yml b/tests/integration/targets/docker_image_pull/tasks/test.yml new file mode 100644 index 00000000..a56c9530 --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/test.yml @@ -0,0 +1,38 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Create random name prefix + set_fact: + name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}" +- name: Create image and container list + set_fact: + inames: [] + cnames: [] + +- debug: + msg: "Using name prefix {{ name_prefix }}" + +- block: + - include_tasks: run-test.yml + with_fileglob: + - "tests/*.yml" + + always: + - name: "Make sure all images are removed" + docker_image: + name: "{{ item }}" + state: absent + with_items: "{{ inames }}" + - name: "Make sure all containers are removed" + docker_container: + name: "{{ item }}" + state: absent + force_kill: true + with_items: "{{ cnames }}" + + when: docker_api_version is version('1.25', '>=') + +- fail: msg="Too old docker / docker-py version to run docker_image tests!" + when: not(docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6) diff --git a/tests/integration/targets/docker_image_pull/tasks/tests/basic.yml b/tests/integration/targets/docker_image_pull/tasks/tests/basic.yml new file mode 100644 index 00000000..ad1e1738 --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/tests/basic.yml @@ -0,0 +1,198 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Basic pull tests + vars: + image_name: "{{ docker_test_image_hello_world_platform }}" + block: + - name: Make sure image is not there + docker_image: + name: "{{ image_name }}" + state: absent + force_absent: true + + - name: Pull image (check mode) + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + register: present_1_check + check_mode: true + + - debug: + var: present_1_check.diff + + - name: Pull image + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + register: present_1 + + - debug: + var: present_1.diff + + - name: Pull image (idempotent 1, check mode) + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + pull: always + register: present_2_check + check_mode: true + + - debug: + var: present_2_check.diff + + - name: Pull image (idempotent 1) + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + pull: always + register: present_2 + + - debug: + var: present_2.diff + + - name: Pull image (change, check mode) + docker_image_pull: + name: "{{ image_name }}" + platform: arm64 + pull: always + register: present_3_check + check_mode: true + + - debug: + var: present_3_check.diff + + - name: Pull image (change) + docker_image_pull: + name: "{{ image_name }}" + platform: arm64 + pull: always + register: present_3 + + - debug: + var: present_3.diff + + - name: Pull image (idempotent 2, check mode) + docker_image_pull: + name: "{{ image_name }}" + platform: arm64 + pull: not_present + register: present_4_check + check_mode: true + + - debug: + var: present_4_check.diff + + - name: Pull image (idempotent 2) + docker_image_pull: + name: "{{ image_name }}" + platform: arm64 + pull: not_present + register: present_4 + + - debug: + var: present_4.diff + + - name: Pull image (change, check mode) + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + pull: not_present + register: present_5_check + check_mode: true + + - debug: + var: present_5_check.diff + + - name: Pull image (change) + docker_image_pull: + name: "{{ image_name }}" + platform: amd64 + pull: not_present + register: present_5 + + - debug: + var: present_5.diff + + - assert: + that: + - present_1_check is changed + - present_1_check.actions | length == 1 + - present_1_check.actions[0] == 'Pulled image ' ~ image_name + - present_1_check.diff.before.exists is false + - present_1_check.diff.after.id == 'unknown' + - present_1 is changed + - present_1.actions | length == 1 + - present_1.actions[0] == 'Pulled image ' ~ image_name + - present_1.diff.before.exists is false + - present_1.diff.after.id is string + - present_2_check is changed + - present_2_check.actions | length == 1 + - present_2_check.actions[0] == 'Pulled image ' ~ image_name + - present_2_check.diff.before.id == present_1.diff.after.id + - present_2_check.diff.after.id == 'unknown' + - present_2 is not changed + - present_2.actions | length == 1 + - present_2.actions[0] == 'Pulled image ' ~ image_name + - present_2.diff.before.id == present_1.diff.after.id + - present_2.diff.after.id == present_1.diff.after.id + - present_3_check is changed + - present_3_check.actions | length == 1 + - present_3_check.actions[0] == 'Pulled image ' ~ image_name + - present_3_check.diff.before.id == present_1.diff.after.id + - present_3_check.diff.after.id == 'unknown' + - present_3 is changed + - present_3.actions | length == 1 + - present_3.actions[0] == 'Pulled image ' ~ image_name + - present_3.diff.before.id == present_1.diff.after.id + - present_3.diff.after.id != present_1.diff.after.id + - present_3.diff.after.id is string + - present_4_check is not changed + - present_4_check.actions | length == 0 + - present_4_check.diff.before.id == present_3.diff.after.id + - present_4_check.diff.after.id == present_3.diff.after.id + - present_4 is not changed + - present_4.actions | length == 0 + - present_4.diff.before.id == present_3.diff.after.id + - present_4.diff.after.id == present_3.diff.after.id + - present_5_check is changed + - present_5_check.actions | length == 1 + - present_5_check.actions[0] == 'Pulled image ' ~ image_name + - present_5_check.diff.before.id == present_3.diff.after.id + - present_5_check.diff.after.id == 'unknown' + - present_5 is changed + - present_5.actions | length == 1 + - present_5.actions[0] == 'Pulled image ' ~ image_name + - present_5.diff.before.id == present_3.diff.after.id + - present_5.diff.after.id == present_1.diff.after.id + + always: + - name: cleanup + docker_image: + name: "{{ image_name }}" + state: absent + force_absent: true + +- name: Pull image ID (must fail) + docker_image_pull: + name: "{{ present_1.image.Id }}" + register: fail_1 + ignore_errors: true + +- name: Pull invalid tag (must fail) + docker_image_pull: + name: "{{ docker_test_image_hello_world }}" + tag: foo/bar + register: fail_2 + ignore_errors: true + +- assert: + that: + - fail_1 is failed + - >- + 'Cannot pull an image by ID' == fail_1.msg + - fail_2 is failed + - >- + '"foo/bar" is not a valid docker tag!' == fail_2.msg diff --git a/tests/integration/targets/docker_image_pull/tasks/tests/image-ids.yml b/tests/integration/targets/docker_image_pull/tasks/tests/image-ids.yml new file mode 100644 index 00000000..91a07e87 --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/tests/image-ids.yml @@ -0,0 +1,91 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Image ID pull tests + block: + - name: Make sure images are not there + docker_image: + name: "{{ item }}" + state: absent + force_absent: true + loop: + - "sha256:{{ docker_test_image_digest_v1_image_id }}" + - "sha256:{{ docker_test_image_digest_v2_image_id }}" + + - name: Pull image 1 + docker_image_pull: + name: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}" + register: present_1 + diff: true + + - name: Pull image 1 (idempotent, do pull) + docker_image_pull: + name: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}" + pull: always + register: present_2 + diff: true + + - name: Pull image 1 (idempotent, do not pull) + docker_image_pull: + name: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}" + pull: not_present + register: present_3 + diff: true + + - assert: + that: + - present_1 is changed + - present_1.actions | length == 1 + - present_1.actions[0] == 'Pulled image ' ~ docker_test_image_digest_base ~ ':sha256:' ~ docker_test_image_digest_v1 + - present_1.diff.before.exists is false + - present_1.diff.after.id is string + - present_2 is not changed + - present_2.actions | length == 1 + - present_2.actions[0] == 'Pulled image ' ~ docker_test_image_digest_base ~ ':sha256:' ~ docker_test_image_digest_v1 + - present_2.diff.before.id == present_1.diff.after.id + - present_2.diff.after.id == present_1.diff.after.id + - present_3 is not changed + - present_3.actions | length == 0 + - present_3.diff.before.id == present_1.diff.after.id + - present_3.diff.after.id == present_1.diff.after.id + + - name: Pull image 2 (check mode) + docker_image_pull: + name: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v2 }}" + pull: always + register: present_4 + diff: true + check_mode: true + + - name: Pull image 2 + docker_image_pull: + name: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v2 }}" + pull: always + register: present_5 + diff: true + + - assert: + that: + - present_4 is changed + - present_4.actions | length == 1 + - present_4.actions[0] == 'Pulled image ' ~ docker_test_image_digest_base ~ ':sha256:' ~ docker_test_image_digest_v2 + - present_4.diff.before.exists is false + - present_4.diff.after.id == 'unknown' + - present_5 is changed + - present_5.actions | length == 1 + - present_5.actions[0] == 'Pulled image ' ~ docker_test_image_digest_base ~ ':sha256:' ~ docker_test_image_digest_v2 + - present_5.diff.before.exists is false + - present_5.diff.after.id != present_1.diff.after.id + - present_5.diff.after.id is string + + always: + - name: cleanup + docker_image: + name: "{{ item }}" + state: absent + force_absent: true + loop: + - "sha256:{{ docker_test_image_digest_v1_image_id }}" + - "sha256:{{ docker_test_image_digest_v2_image_id }}" diff --git a/tests/integration/targets/docker_image_pull/tasks/tests/registry.yml b/tests/integration/targets/docker_image_pull/tasks/tests/registry.yml new file mode 100644 index 00000000..76ca57b9 --- /dev/null +++ b/tests/integration/targets/docker_image_pull/tasks/tests/registry.yml @@ -0,0 +1,125 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Run registry tests only when registry is present + when: registry_address is defined + block: + - name: Registering image name + set_fact: + iname: "{{ name_prefix ~ '-options' }}" + + - name: Determining pushed image names + set_fact: + hello_world_image_base: "{{ registry_address }}/test/hello-world" + + - name: Registering image name + set_fact: + inames: "{{ inames + [iname, hello_world_image_base ~ ':latest'] }}" + + - name: Make sure image is not there + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: absent + force_absent: true + + - name: Make sure we have {{ docker_test_image_hello_world }} + docker_image_pull: + name: "{{ docker_test_image_hello_world }}" + diff: true + + - name: Push image to test registry + docker_image: + name: "{{ docker_test_image_hello_world }}" + repository: "{{ hello_world_image_base }}:latest" + push: true + source: local + + - name: Get facts of local image + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_1 + + - name: Make sure image is not there + docker_image: + name: "{{ hello_world_image_base }}:latest" + state: absent + force_absent: true + + - name: Get facts of local image (not there) + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_2 + + - name: Pull image from test registry (check mode) + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + register: pull_1_check + diff: true + check_mode: true + + - name: Pull image from test registry + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + register: pull_1 + diff: true + + - name: Pull image from test registry (idempotency, not pulling, check mode) + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + pull: not_present + register: pull_2_check + diff: true + check_mode: true + + - name: Pull image from test registry (idempotency, not pulling) + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + pull: not_present + register: pull_2 + diff: true + + - name: Pull image from test registry (idempotency, pulling, check mode) + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + pull: always + register: pull_3_check + diff: true + check_mode: true + + - name: Pull image from test registry (idempotency, pulling) + docker_image_pull: + name: "{{ hello_world_image_base }}:latest" + pull: always + register: pull_3 + diff: true + + - name: Get facts of local image (present) + docker_image_info: + name: "{{ hello_world_image_base }}:latest" + register: facts_3 + + - assert: + that: + - pull_1_check is changed + - pull_1_check.diff.before.exists is false + - pull_1_check.diff.after.id == 'unknown' + - pull_1 is changed + - pull_1.diff.before.exists is false + - pull_1.diff.after.id == facts_1.images[0].Id + - pull_2_check is not changed + - pull_2_check.diff.before.id == facts_1.images[0].Id + - pull_2_check.diff.before == pull_2.diff.after + - pull_2 is not changed + - pull_2.diff.before.id == facts_1.images[0].Id + - pull_2.diff.before == pull_2.diff.after + - pull_3_check is changed + - pull_3_check.diff.before.id == facts_1.images[0].Id + - pull_3_check.diff.after.id == 'unknown' + - pull_3 is not changed + - pull_3.diff.before.id == facts_1.images[0].Id + - pull_3.diff.before == pull_2.diff.after + - facts_1.images | length == 1 + - facts_2.images | length == 0 + - facts_3.images | length == 1 diff --git a/tests/integration/targets/docker_image_push/tasks/tests/registry.yml b/tests/integration/targets/docker_image_push/tasks/tests/registry.yml index ddf9addc..01834dcb 100644 --- a/tests/integration/targets/docker_image_push/tasks/tests/registry.yml +++ b/tests/integration/targets/docker_image_push/tasks/tests/registry.yml @@ -7,9 +7,8 @@ when: registry_address is defined block: - name: Pull images - docker_image: + docker_image_pull: name: "{{ item }}" - source: pull loop: - "{{ docker_test_image_hello_world }}" - "{{ docker_test_image_alpine }}" diff --git a/tests/integration/targets/setup_docker/vars/main.yml b/tests/integration/targets/setup_docker/vars/main.yml index 6e43e5a2..328add81 100644 --- a/tests/integration/targets/setup_docker/vars/main.yml +++ b/tests/integration/targets/setup_docker/vars/main.yml @@ -6,9 +6,11 @@ docker_test_image_digest_v1: e004c2cc521c95383aebb1fb5893719aa7a8eae2e7a71f316a4410784edb00a9 docker_test_image_digest_v1_image_id: 758ec7f3a1ee85f8f08399b55641bfb13e8c1109287ddc5e22b68c3d653152ee docker_test_image_digest_v2: ee44b399df993016003bf5466bd3eeb221305e9d0fa831606bc7902d149c775b +docker_test_image_digest_v2_image_id: dc3bacd8b5ea796cea5d6070c8f145df9076f26a6bc1c8981fd5b176d37de843 docker_test_image_digest_base: quay.io/ansible/docker-test-containers docker_test_image_hello_world: quay.io/ansible/docker-test-containers:hello-world docker_test_image_hello_world_base: quay.io/ansible/docker-test-containers +docker_test_image_hello_world_platform: docker.io/library/hello-world:latest docker_test_image_busybox: quay.io/ansible/docker-test-containers:busybox docker_test_image_alpine: quay.io/ansible/docker-test-containers:alpine3.8 docker_test_image_alpine_different: quay.io/ansible/docker-test-containers:alpine3.7