From 4b0a134bc9e0683d6f46b87e827e66dde5d8f302 Mon Sep 17 00:00:00 2001
From: Max Gautier <mg@max.gautier.name>
Date: Thu, 8 Feb 2024 10:14:45 +0000
Subject: [PATCH] Only download kubeadm images where needed (#10899)

* Refactor of kubeadm images listing

Instead of setting multiples facts, we directly create the dict we need from
kubeadm output.

* Remove useless 'default' filters in roles/download

* Only download kubeadm images where needed
---
 galaxy.yml                                   |  1 +
 roles/download/tasks/main.yml                | 42 ++++++++++++++++++--
 roles/download/tasks/prep_kubeadm_images.yml | 33 ++-------------
 3 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/galaxy.yml b/galaxy.yml
index 33b259d5f..37f9b6b97 100644
--- a/galaxy.yml
+++ b/galaxy.yml
@@ -11,6 +11,7 @@ tags:
 repository: https://github.com/kubernetes-sigs/kubespray
 dependencies:
   ansible.utils: '>=2.5.0'
+  community.general: '>=3.0.0'
 build_ignore:
   - .github
   - '*.tar.gz'
diff --git a/roles/download/tasks/main.yml b/roles/download/tasks/main.yml
index 3309ab88e..fe83e6c8d 100644
--- a/roles/download/tasks/main.yml
+++ b/roles/download/tasks/main.yml
@@ -2,7 +2,7 @@
 - name: Download | Prepare working directories and variables
   import_tasks: prep_download.yml
   when:
-    - not skip_downloads | default(false)
+    - not skip_downloads
   tags:
     - download
     - upload
@@ -10,7 +10,7 @@
 - name: Download | Get kubeadm binary and list of required images
   include_tasks: prep_kubeadm_images.yml
   when:
-    - not skip_downloads | default(false)
+    - not skip_downloads
     - inventory_hostname in groups['kube_control_plane']
   tags:
     - download
@@ -22,8 +22,44 @@
   vars:
     download: "{{ download_defaults | combine(item.value) }}"
     include_file: "download_{% if download.container %}container{% else %}file{% endif %}.yml"
+    kubeadm_images: "{{ skip_kubeadm_images | ternary({}, _kubeadm_images) }}"
+    # The trick (converting list of tuples to list of dicts) below come from
+    # https://docs.ansible.com/ansible/latest/collections/community/general/dict_filter.html#examples
+    _kubeadm_images: "{{ dict(names | map('regex_replace', '^(.*)', 'kubeadm_\\1') |
+      zip( repos | zip(_tags, _groups) |
+      map('zip', keys) | map('map', 'reverse') | map('community.general.dict') |
+      map('combine', defaults))) |
+      dict2items | rejectattr('key', 'in', excluded) | items2dict }}"
+    keys:
+      - repo
+      - tag
+      - groups
+    images: "{{ kubeadm_images_raw.stdout_lines | map('split', ':') }}"
+    _tags: "{{ images | map(attribute=1) }}"
+    repos: "{{ images | map(attribute=0) }}"
+    names: "{{ repos | map('split', '/') | map(attribute=-1) }}"
+    _groups: "{{ names | map('extract', images_groups) }}"
+    defaults:
+      enabled: true
+      container: true
+    excluded:
+      - kubeadm_coredns
+      - kubeadm_pause
+    images_groups:
+      coredns: []
+      pause: []
+      kube-proxy:
+        - k8s_cluster
+      etcd:
+        - etcd
+      kube-scheduler:
+        - kube_control_plane
+      kube-controller-manager:
+        - kube_control_plane
+      kube-apiserver:
+        - kube_control_plane
   when:
-    - not skip_downloads | default(false)
+    - not skip_downloads
     - download.enabled
     - item.value.enabled
     - (not (item.value.container | default(false))) or (item.value.container and download_container)
diff --git a/roles/download/tasks/prep_kubeadm_images.yml b/roles/download/tasks/prep_kubeadm_images.yml
index fdfed1d08..67ac2f721 100644
--- a/roles/download/tasks/prep_kubeadm_images.yml
+++ b/roles/download/tasks/prep_kubeadm_images.yml
@@ -20,7 +20,7 @@
     dest: "{{ kube_config_dir }}/kubeadm-images.yaml"
     mode: 0644
   when:
-    - not skip_kubeadm_images | default(false)
+    - not skip_kubeadm_images
 
 - name: Prep_kubeadm_images | Copy kubeadm binary from download dir to system path
   copy:
@@ -36,36 +36,9 @@
     state: file
 
 - name: Prep_kubeadm_images | Generate list of required images
-  shell: "set -o pipefail && {{ bin_dir }}/kubeadm config images list --config={{ kube_config_dir }}/kubeadm-images.yaml | grep -Ev 'coredns|pause'"
-  args:
-    executable: /bin/bash
+  command: "{{ bin_dir }}/kubeadm config images list --config={{ kube_config_dir }}/kubeadm-images.yaml"
   register: kubeadm_images_raw
   run_once: true
   changed_when: false
   when:
-    - not skip_kubeadm_images | default(false)
-
-- name: Prep_kubeadm_images | Parse list of images
-  vars:
-    kubeadm_images_list: "{{ kubeadm_images_raw.stdout_lines }}"
-  set_fact:
-    kubeadm_image:
-      key: "kubeadm_{{ (item | regex_replace('^(?:.*\\/)*', '')).split(':')[0] }}"
-      value:
-        enabled: true
-        container: true
-        repo: "{{ item | regex_replace('^(.*):.*$', '\\1') }}"
-        tag: "{{ item | regex_replace('^.*:(.*)$', '\\1') }}"
-        groups: k8s_cluster
-  loop: "{{ kubeadm_images_list | flatten(levels=1) }}"
-  register: kubeadm_images_cooked
-  run_once: true
-  when:
-    - not skip_kubeadm_images | default(false)
-
-- name: Prep_kubeadm_images | Convert list of images to dict for later use
-  set_fact:
-    kubeadm_images: "{{ kubeadm_images_cooked.results | map(attribute='ansible_facts.kubeadm_image') | list | items2dict }}"
-  run_once: true
-  when:
-    - not skip_kubeadm_images | default(false)
+    - not skip_kubeadm_images
-- 
GitLab