Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docker_container: allow pull=never, and make check mode behavior configurable #797

Merged
merged 3 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/797-docker_container-pull.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- "docker_container - the ``pull_check_mode_behavior`` option now allows to control the module's behavior in check mode when ``pull=always`` (https://github.com/ansible-collections/community.docker/issues/792, https://github.com/ansible-collections/community.docker/pull/797)."
- "docker_container - the ``pull`` option now accepts the three values ``never``, ``missing_image`` (default), and ``never``, next to the previously valid values ``true`` (equivalent to ``always``) and ``false`` (equivalent to ``missing_image``). This allows the equivalent to ``--pull=never`` from the Docker command line (https://github.com/ansible-collections/community.docker/issues/783, https://github.com/ansible-collections/community.docker/pull/797)."
27 changes: 20 additions & 7 deletions plugins/module_utils/module_container/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ def __init__(self, module, engine_driver, client, active_options):
self.param_output_logs = self.module.params['output_logs']
self.param_paused = self.module.params['paused']
self.param_pull = self.module.params['pull']
if self.param_pull is True:
self.param_pull = 'always'
if self.param_pull is False:
self.param_pull = 'missing'
self.param_pull_check_mode_behavior = self.module.params['pull_check_mode_behavior']
self.param_recreate = self.module.params['recreate']
self.param_removal_wait_timeout = self.module.params['removal_wait_timeout']
self.param_restart = self.module.params['restart']
Expand Down Expand Up @@ -444,21 +449,28 @@ def _get_image(self, container, needs_container_image=False):
if not tag:
tag = "latest"
image = self.engine_driver.inspect_image_by_name(self.client, repository, tag)
if not image or self.param_pull:
if not image and self.param_pull == "never":
self.client.fail("Cannot find image with name %s:%s, and pull=never" % (repository, tag))
if not image or self.param_pull == "always":
if not self.check_mode:
self.log("Pull the image.")
image, alreadyToLatest = self.engine_driver.pull_image(
self.client, repository, tag, platform=self.module.params['platform'])
if alreadyToLatest:
self.results['changed'] = False
self.results['actions'].append(dict(pulled_image="%s:%s" % (repository, tag), changed=False))
else:
self.results['changed'] = True
self.results['actions'].append(dict(pulled_image="%s:%s" % (repository, tag)))
elif not image:
# If the image isn't there, claim we'll pull.
# (Implicitly: if the image is there, claim it already was latest.)
self.results['actions'].append(dict(pulled_image="%s:%s" % (repository, tag), changed=True))
elif not image or self.param_pull_check_mode_behavior == 'always':
# If the image isn't there, or pull_check_mode_behavior == 'always', claim we'll
# pull. (Implicitly: if the image is there, claim it already was latest unless
# pull_check_mode_behavior == 'always'.)
self.results['changed'] = True
self.results['actions'].append(dict(pulled_image="%s:%s" % (repository, tag)))
action = dict(pulled_image="%s:%s" % (repository, tag))
if not image:
action['changed'] = True
self.results['actions'].append(action)

self.log("image")
self.log(image, pretty_print=True)
Expand Down Expand Up @@ -860,7 +872,8 @@ def run_module(engine_driver):
networks_cli_compatible=dict(type='bool', default=True),
output_logs=dict(type='bool', default=False),
paused=dict(type='bool'),
pull=dict(type='bool', default=False),
pull=dict(type='raw', choices=['never', 'missing', 'always', True, False], default='missing'),
pull_check_mode_behavior=dict(type='str', choices=['image_not_present', 'always'], default='image_not_present'),
purge_networks=dict(type='bool', default=False, removed_in_version='4.0.0', removed_from_collection='community.docker'),
recreate=dict(type='bool', default=False),
removal_wait_timeout=dict(type='float'),
Expand Down
38 changes: 32 additions & 6 deletions plugins/modules/docker_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
check_mode:
support: partial
details:
- When trying to pull an image, the module assumes this is always changed in check mode.
- When trying to pull an image, the module assumes this is never changed in check mode except when the image is not present on the Docker daemon.
- This behavior can be configured with O(pull_check_mode_behavior).
diff_mode:
support: full

Expand Down Expand Up @@ -788,12 +789,37 @@
- ports
pull:
description:
- If true, always pull the latest version of an image. Otherwise, will only pull an image
when missing.
- If set to V(never), will never try to pull an image. Will fail if the image is not available
on the Docker daemon.
- If set to V(missing) or V(false), only pull the image if it is not available on the Docker
daemon. This is the default behavior.
- If set to V(always) or V(true), always try to pull the latest version of the image.
- "B(Note:) images are only pulled when specified by name. If the image is specified
as a image ID (hash), it cannot be pulled."
type: bool
default: false
as a image ID (hash), it cannot be pulled, and this option is ignored."
- "B(Note:) the values V(never), V(missing), and V(always) are only available since
community.docker 3.8.0. Earlier versions only support V(true) and V(false)."
type: raw
choices:
- never
- missing
- always
- true
- false
default: missing
pull_check_mode_behavior:
description:
- Allows to adjust the behavior when O(pull=always) or O(pull=true) in check mode.
- Since the Docker daemon does not expose any functionality to test whether a pull will result
in a changed image, the module by default acts like O(pull=always) only results in a change when
the image is not present.
- If set to V(image_not_present) (default), only report changes in check mode when the image is not present.
- If set to V(always), always report changes in check mode.
type: str
default: image_not_present
choices:
- image_not_present
- always
version_added: 3.8.0
purge_networks:
description:
- Remove the container from ALL networks not included in O(networks) parameter.
Expand Down
143 changes: 143 additions & 0 deletions tests/integration/targets/docker_container/tasks/tests/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3642,6 +3642,149 @@ avoid such warnings, please quote the value.' in (log_options_2.warnings | defau
('API version is ' ~ docker_api_version ~ '.') in platform_1.msg and 'Minimum version required is 1.41 ' in platform_1.msg
when: docker_api_version is version('1.41', '<')

####################################################################
## pull / pull_check_mode_behavior #################################
####################################################################

- name: Remove hello-world image
docker_image_remove:
name: "{{ docker_test_image_hello_world }}"

- name: pull (pull=never)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: never
debug: true
register: pull_1
ignore_errors: true

- name: pull (pull=missing, check mode)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: missing
debug: true
register: pull_2
check_mode: true
ignore_errors: true

- name: pull (pull=missing)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: missing
debug: true
register: pull_3
ignore_errors: true

- name: pull (pull=missing, idempotent, check mode)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: missing
debug: true
register: pull_4
check_mode: true
ignore_errors: true

- name: pull (pull=missing, idempotent)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: missing
debug: true
register: pull_5
ignore_errors: true

- name: pull (pull=always, check mode, pull_check_mode_behavior=image_not_present)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: always
pull_check_mode_behavior: image_not_present
debug: true
register: pull_6
check_mode: true
ignore_errors: true

- name: pull (pull=always, check mode, pull_check_mode_behavior=always)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: always
pull_check_mode_behavior: always
debug: true
register: pull_7
check_mode: true
ignore_errors: true

- name: pull (pull=always)
docker_container:
image: "{{ docker_test_image_hello_world }}"
name: "{{ cname }}"
state: present
pull: always
debug: true
register: pull_8
ignore_errors: true

- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false

- assert:
that:
- pull_1 is failed
- pull_1.msg == ("Cannot find image with name " ~ docker_test_image_hello_world ~ ", and pull=never")
- pull_2 is changed
- pulled_image_action not in pull_2.actions
- pulled_image_action_changed in pull_2.actions
- pulled_image_action_unchanged not in pull_2.actions
- pull_3 is changed
- pulled_image_action not in pull_3.actions
- pulled_image_action_changed in pull_3.actions
- pulled_image_action_unchanged not in pull_3.actions
- pull_4 is not changed
- pulled_image_action not in pull_4.actions
- pulled_image_action_changed not in pull_4.actions
- pulled_image_action_unchanged not in pull_4.actions
- pull_5 is not changed
- pulled_image_action not in pull_5.actions
- pulled_image_action_changed not in pull_5.actions
- pulled_image_action_unchanged not in pull_5.actions
- pull_6 is not changed
- pulled_image_action not in pull_6.actions
- pulled_image_action_changed not in pull_6.actions
- pulled_image_action_unchanged not in pull_6.actions
- pull_7 is changed
- pulled_image_action in pull_7.actions
- pulled_image_action_changed not in pull_7.actions
- pulled_image_action_unchanged not in pull_7.actions
- pull_8 is not changed
- pulled_image_action not in pull_8.actions
- pulled_image_action_changed not in pull_8.actions
- pulled_image_action_unchanged in pull_8.actions
vars:
pulled_image_action:
pulled_image: "{{ docker_test_image_hello_world }}"
pulled_image_action_changed:
pulled_image: "{{ docker_test_image_hello_world }}"
changed: true
pulled_image_action_unchanged:
pulled_image: "{{ docker_test_image_hello_world }}"
changed: false

####################################################################
## privileged ######################################################
####################################################################
Expand Down
Loading