diff --git a/roles/download/tasks/check_pull_required.yml b/roles/download/tasks/check_pull_required.yml deleted file mode 100644 index 58501312572..00000000000 --- a/roles/download/tasks/check_pull_required.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -# The image_info_command depends on the Container Runtime and will output something like the following: -# nginx:1.15,gcr.io/google-containers/kube-proxy:v1.14.1,gcr.io/google-containers/kube-proxy@sha256:44af2833c6cbd9a7fc2e9d2f5244a39dfd2e31ad91bf9d4b7d810678db738ee9,gcr.io/google-containers/kube-apiserver:v1.14.1,etc... -- name: Check_pull_required | Generate a list of information about the images on a node # noqa command-instead-of-shell - image_info_command contains a pipe, therefore requiring shell - shell: "{{ image_info_command }}" - register: docker_images - changed_when: false - check_mode: false - when: not download_always_pull - -- name: Check_pull_required | Set pull_required if the desired image is not yet loaded - set_fact: - pull_required: >- - {%- if image_reponame | regex_replace('^docker\.io/(library/)?', '') in docker_images.stdout.split(',') %}false{%- else -%}true{%- endif -%} - when: not download_always_pull - -- name: Check_pull_required | Check that the local digest sha256 corresponds to the given image tag - assert: - that: "{{ download.repo }}:{{ download.tag }} in docker_images.stdout.split(',')" - when: - - not download_always_pull - - not pull_required - - pull_by_digest - tags: - - asserts diff --git a/roles/download/tasks/download_container.yml b/roles/download/tasks/download_container.yml index 5e67fe8c550..1f2a070ae20 100644 --- a/roles/download/tasks/download_container.yml +++ b/roles/download/tasks/download_container.yml @@ -15,11 +15,6 @@ tags: - facts - - name: Download_container | Prepare container download - include_tasks: check_pull_required.yml - when: - - not download_always_pull - - debug: # noqa name[missing] msg: "Pull {{ image_reponame }} required is: {{ pull_required }}" diff --git a/roles/download/tasks/main.yml b/roles/download/tasks/main.yml index 93f8bb55acb..575578cfc75 100644 --- a/roles/download/tasks/main.yml +++ b/roles/download/tasks/main.yml @@ -16,6 +16,39 @@ - download - upload +- name: Download | Check if image command tool exists + command: "{{ image_command_tool }} --version" + register: image_command_tool_exists + changed_when: false + failed_when: false + +- name: Download | Check if image command tool exists + stat: + path: "{{ bin_dir }}/{{ image_command_tool }}" + get_attributes: false + get_checksum: false + get_mime: false + register: image_command_tool_exists + changed_when: false + +- name: Download | Process and download required files and images + when: + - not (download_always_pull or skip_downloads) | default(false) + - image_command_tool_exists.stat.exists + block: + - name: Download | Get image list from node + command: "{{ image_info_command }}" + register: node_images_raw + changed_when: false + check_mode: false + - name: Download | Set node_images + include_tasks: "{{ include_file }}" + vars: + include_file: "set_node_facts{% if image_command_tool == 'crictl' %}_crictl{% endif %}.yml" + - name: Download | Show node images + debug: + msg: "{{ node_images }}" + - name: Download | Download files / images include_tasks: "{{ include_file }}" loop: "{{ downloads | combine(kubeadm_images) | dict2items }}" @@ -26,5 +59,29 @@ - not skip_downloads | default(false) - download.enabled - item.value.enabled - - (not (item.value.container | default(false))) or (item.value.container and download_container) + - > + ( + not ( + item.value.container | default(false) + ) + ) + or ( + item.value.container and + download_container and + ( + not ( + ( + node_images | selectattr('Repository', 'equalto', (item.value.repo | regex_replace('^docker\.io/(library/)?', ''))) | selectattr('Tag', 'equalto', item.value.tag) | list | length > 0 + ) + or ( + node_images | selectattr('Digest', 'equalto', 'sha256:' ~ (item.value.sha256 | default(None))) | list | length > 0 + ) + ) + or download_always_pull + ) + ) - (download_run_once and inventory_hostname == download_delegate) or (group_names | intersect(download.groups) | length) + +- name: Download | Show downloads + debug: + msg: "{{ downloads }}" diff --git a/roles/download/tasks/set_node_facts.yml b/roles/download/tasks/set_node_facts.yml new file mode 100644 index 00000000000..7afbacebbe3 --- /dev/null +++ b/roles/download/tasks/set_node_facts.yml @@ -0,0 +1,7 @@ +--- +# In case of nerdctl or docker, the output is already in the expected format +# {"Digest":"sha256:847423221ed040798e47df36450edce5581ed642cd087ccb210532764da38b23","Repository":"quay.io/coreos/etcd","Tag":"v3.5.12"} +# {"Digest":"sha256:34fc87c4a60c0b3ba3b3608871f4494de8072c02808a4151953068f2d7c87743","Repository":"flannel/flannel","Tag":"latest"} +- name: Download | Set node_images (nerdctl, docker) + set_fact: + node_images: "{{ node_images_raw.stdout_lines | map('from_json') | list }}" diff --git a/roles/download/tasks/set_node_facts_crictl.yml b/roles/download/tasks/set_node_facts_crictl.yml new file mode 100644 index 00000000000..2609c7bde80 --- /dev/null +++ b/roles/download/tasks/set_node_facts_crictl.yml @@ -0,0 +1,9 @@ +--- +- name: Download | Set node_images (crictl) + set_fact: + node_images: >- + {{ + (node_images | default([])) + + [{'Digest': (item.repoDigests[0] | split('@'))[1], 'Repository': ((item.repoTags[0] | split(':'))[0] | regex_replace('^docker\.io/(library/)?', '')), 'Tag': (item.repoTags[0] | split(':'))[1]}] + }} + with_items: "{{ (node_images_raw.stdout | from_json).images }}" diff --git a/roles/download/vars/main.yml b/roles/download/vars/main.yml new file mode 100644 index 00000000000..40f32977de9 --- /dev/null +++ b/roles/download/vars/main.yml @@ -0,0 +1,18 @@ +--- +node_images: [] + +docker_image_pull_command: "{{ docker_bin_dir }}/docker pull" +docker_image_info_command: "{{ docker_bin_dir }}/docker images --format='{\"Digest\":\"{{ '{{' }}.Digest{{ '}}' }}\",\"Repository\":\"{{ '{{' }}.Repository{{ '}}' }}\",\"Tag\":\"{{ '{{' }}.Tag{{ '}}' }}\"}'" +nerdctl_image_info_command: "{{ bin_dir }}/nerdctl images --format='{\"Digest\":\"{{ '{{' }}.Digest{{ '}}' }}\",\"Repository\":\"{{ '{{' }}.Repository{{ '}}' }}\",\"Tag\":\"{{ '{{' }}.Tag{{ '}}' }}\"}'" +# Using the ctr instead of nerdctl to workdaround the https://github.com/kubernetes-sigs/kubespray/issues/10670 +nerdctl_image_pull_command: "{{ bin_dir }}/ctr -n k8s.io images pull{% if containerd_registries_mirrors is defined %} --hosts-dir {{ containerd_cfg_dir }}/certs.d{%- endif -%}" +crictl_image_info_command: "{{ bin_dir }}/crictl images --output json" +crictl_image_pull_command: "{{ bin_dir }}/crictl pull" + +image_command_tool: "{%- if container_manager == 'containerd' -%}nerdctl{%- elif container_manager == 'crio' -%}crictl{%- else -%}{{ container_manager }}{%- endif -%}" +image_command_tool_on_localhost: "{{ image_command_tool }}" + +image_pull_command: "{{ lookup('vars', image_command_tool + '_image_pull_command') }}" +image_info_command: "{{ lookup('vars', image_command_tool + '_image_info_command') }}" +image_pull_command_on_localhost: "{{ lookup('vars', image_command_tool_on_localhost + '_image_pull_command') }}" +image_info_command_on_localhost: "{{ lookup('vars', image_command_tool_on_localhost + '_image_info_command') }}" diff --git a/roles/kubespray-defaults/defaults/main/download.yml b/roles/kubespray-defaults/defaults/main/download.yml index 8c3954c80e6..6e8cf7ba461 100644 --- a/roles/kubespray-defaults/defaults/main/download.yml +++ b/roles/kubespray-defaults/defaults/main/download.yml @@ -53,23 +53,6 @@ download_delegate: "{% if download_localhost %}localhost{% else %}{{ groups['kub # Allow control the times of download retries for files and containers download_retries: 4 -# The docker_image_info_command might seems weird but we are using raw/endraw and `{{ `{{` }}` to manage the double jinja2 processing -docker_image_pull_command: "{{ docker_bin_dir }}/docker pull" -docker_image_info_command: "{{ docker_bin_dir }}/docker images -q | xargs -i {{ '{{' }} docker_bin_dir }}/docker inspect -f {% raw %}'{{ '{{' }} if .RepoTags }}{{ '{{' }} join .RepoTags \",\" }}{{ '{{' }} end }}{{ '{{' }} if .RepoDigests }},{{ '{{' }} join .RepoDigests \",\" }}{{ '{{' }} end }}' {% endraw %} {} | tr '\n' ','" -nerdctl_image_info_command: "{{ bin_dir }}/nerdctl -n k8s.io images --format '{% raw %}{{ .Repository }}:{{ .Tag }}{% endraw %}' 2>/dev/null | grep -v ^:$ | tr '\n' ','" -# Using the ctr instead of nerdctl to workdaround the https://github.com/kubernetes-sigs/kubespray/issues/10670 -nerdctl_image_pull_command: "{{ bin_dir }}/ctr -n k8s.io images pull{% if containerd_registries_mirrors is defined %} --hosts-dir {{ containerd_cfg_dir }}/certs.d{%- endif -%}" -crictl_image_info_command: "{{ bin_dir }}/crictl images --verbose | awk -F ': ' '/RepoTags|RepoDigests/ {print $2}' | tr '\n' ','" -crictl_image_pull_command: "{{ bin_dir }}/crictl pull" - -image_command_tool: "{%- if container_manager == 'containerd' -%}nerdctl{%- elif container_manager == 'crio' -%}crictl{%- else -%}{{ container_manager }}{%- endif -%}" -image_command_tool_on_localhost: "{{ image_command_tool }}" - -image_pull_command: "{{ lookup('vars', image_command_tool + '_image_pull_command') }}" -image_info_command: "{{ lookup('vars', image_command_tool + '_image_info_command') }}" -image_pull_command_on_localhost: "{{ lookup('vars', image_command_tool_on_localhost + '_image_pull_command') }}" -image_info_command_on_localhost: "{{ lookup('vars', image_command_tool_on_localhost + '_image_info_command') }}" - # Arch of Docker images and needed packages image_arch: "{{ host_architecture | default('amd64') }}"