Skip to content

Refactor download (file) - #12299

Open
VannTen wants to merge 22 commits into
kubernetes-sigs:masterfrom
VannTen:cleanup/sane_download
Open

Refactor download (file)#12299
VannTen wants to merge 22 commits into
kubernetes-sigs:masterfrom
VannTen:cleanup/sane_download

Conversation

@VannTen

@VannTen VannTen commented Jun 12, 2025

Copy link
Copy Markdown
Contributor

What type of PR is this?
/kind bug
/kind feature

What this PR does / why we need it:
This should fix multi-arch cluster download, and be much more scalable. (rough testing of the file part only:
-> around 1 minutes for 1000 fake hosts with download_delegate == localhost)
This is only the file part, I plan to handle containers images in a separate PR.

This is a full rewrite of the file part of the download role.
This massively improves the runtime of the playbooks (our CI jobs appears to go from 9 to 6 minutes), and should also fix or at least improve a lot the situation with multi-architecture clusters (but that will need the follow-up handling container for that usecase), because the downloads are now calculated per host, before deduplication to the host handling the download if there is delegation.

The improvement mostly comes from dropping the include_tasks: download_file.yml with a loop on download, dropping unnecessary tasks and pre-processing the loop (so, there should not be any skipped tasks, because they are removed of the list before being passed to loop).

The uniqueness of binaries rely on the filename, so using the correct variable inside the downloads variable (architecture / os_version / etc) should suffice.

This also removes all of the include used in various roles to reuse part of the download role. Now only the downloads role is responsible for getting artefacts, which should help with skipping with --tags and similar.

Which issue(s) this PR fixes:
Fixes #12403
Fixes #13086

Special notes for your reviewer:
There will be a follow-up to handle the container part as well, with the same ideas

After this is merged, and I'll also handle the container download part (which should be easier as a good chunk of the logic around delegation will be reused), I plan to default download_delegate to localhost.
This will be a breaking change, moreso than the existing one in the release-note, because it will change behavior, not just interface.

I wanted to keep it separate for that reason.

Much of the complexity of the new role is for handling download_delegate != inventory_hostname || 'localhost' which is the current default (delegate to first control plane).

NOTE: The release-note also take into account #12937 which will need to be merge for fully removing the listed variables.

I tried to keep this not too big, but unfortunately it touched on a lot of assumptions / weird stuff in Kubespray. If you see something which could be split out do not hesitate to point it out, I'll try to extract it in a new PR (or if you have the energy feel free to do it directly ^)

Does this PR introduce a user-facing change?:

action required
`download_run_once`, `download_localhost`, `download_force_cache` are removed.
Only `download_delegate` is used (this can be a different values for each host, though this is not recommended.
Binaries are only put on hosts at their actual place or if inventory_hostname == download_delegate.
Using limits now require to include `localhost` in the limit if download/file is not skipped (by tags).

@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@k8s-ci-robot k8s-ci-robot added release-note-action-required Denotes a PR that introduces potentially breaking changes that require user action. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/bug Categorizes issue or PR as related to a bug. kind/feature Categorizes issue or PR as related to a new feature. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jun 12, 2025
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: VannTen

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 12, 2025
@k8s-ci-robot
k8s-ci-robot requested review from ErikJiang and mzaian June 12, 2025 14:42
@k8s-ci-robot k8s-ci-robot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jun 12, 2025
@VannTen

VannTen commented Jun 12, 2025 via email

Copy link
Copy Markdown
Contributor Author

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 17, 2025
@VannTen
VannTen force-pushed the cleanup/sane_download branch from 631adb1 to 984a759 Compare July 8, 2025 09:44
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 8, 2025
@VannTen
VannTen force-pushed the cleanup/sane_download branch from cace245 to 7241144 Compare July 8, 2025 14:27
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 19, 2025
@VannTen
VannTen force-pushed the cleanup/sane_download branch from f82c321 to 86be1af Compare September 1, 2025 14:54
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 1, 2025
@VannTen
VannTen force-pushed the cleanup/sane_download branch 5 times, most recently from 90b55a0 to 9dc42ee Compare September 2, 2025 11:55
@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 3, 2025
@VannTen
VannTen force-pushed the cleanup/sane_download branch from edc1223 to 4ceb0be Compare November 3, 2025 15:52
@tico88612

tico88612 commented Jul 6, 2026

Copy link
Copy Markdown
Member

These failing maybe are related with docker in conformance test.

@VannTen

VannTen commented Jul 6, 2026 via email

Copy link
Copy Markdown
Contributor Author

VannTen added 22 commits July 23, 2026 11:09
This is the same logic as system_packages: we simply let jinja expand
our variables to a list of boolean and filter on this.
- Remove the various variable concerning download delegation to keep only
    `download_delegate`.
- Only use one task with a loop to download all artefacts, computing the
  loop from hostvars[*]['downloads'] (aka, we compute the set of
  all needed artefacts across the nodes delegating to a particular host)
  The implementation is optimized towards the `download_delegate ==
  'localhost'` case, but retains backwards compatibility for other value
  of `download_delegate`.
Small efficiency gains when downloading, around 2-3s worse when
everything is already there.
- The download/file role needs to run on localhost as well because it does
  not use delegate_to directly, each host scans hostvars to determine what
  downloads are delegated to it by other hosts.
- We also needs to collect facts for localhost to have the downloads
  correctly processed here (machine architecture etc)
local_release_dir has insufficient nuances, as it needs to handle
several cases,
- where to put downloaded artifacts
- where to search for when installing artifacts
- where to search for when using artifacts (passing them via lookup to
  kubectl stdin for instance)

Furthermore, depending on how the user set up download delegation, the
value we want can be on the host the tasks is executing, on the
delegated hosts, etc.

Define variables on top of local_release_dir for differents contexts,
handling which host context to use depending on `download_delegate`.

Mostly, this boils down to choosing the correct `host` for
hostvars[host]['local_release_dir'].

download: use a dedicated variable as source when installing artefacts

Avoid problem if local_release_dir differs between localhost and other
hosts.
The current way of installing ArgoCD from downloaded manifests does not
work well with the new download/file role.
Additionally, usage of our internal `kube` Ansible module is deprecated.

Rewrite ArgoCD install to use kubectl_apply_stdin directly without
pushing manifests to the control plane node first.

ansible-lint: the refactor apparently newly exposes the password hash
filter to ansible and this needs passlib, so add it to the pre-commit
dependencies.
bb757be06 (Convert argocd to kubectl apply stdin, 2025-07-08) removes
the only task using yq on host, so remove it completely.
- Use only one task with loop instead of dedicated roles
- don't copy manifest to control plane, use kubectl_apply_stdin instead
- delete duplicates defaults from gateway_api (exists in
  kubespray_defaults)
- Add version to prometheus-operator and gateway-api crds manifests
  (allows uniqueness)
- avoids the needs to create the directory.
- convert to new download/file role.
The objective is to have 3 tasks only (usually two):
- fetching the artifact on download_delegate (can be same host)
- (optionnal) fetch back to localhost (this is only needed when
  download_delegate is not the same host, and not localhost)
- install directly (no need for a local staging directory on nodes)

The third item in particular avoid needless copy on the managed nodes on
potentially space constrained filesystems.

crio: group the 3 tasks handling artifacts installation (binaries,
service file, default policy) together for clarity.
This is needed for being able to filter on "not container" cleanly.
Move out kubeadm install out of the legacy download role and into its
own role.
skopeo is required to load image from archive anywhere cri-o is used
(since the latter lacks a way to do this natively, since load/save
commands are not part of the Kubernetes CRI interface)

Since we now rely on the downloads variable instead of manually
downloading skopeo, fix the inconsistency.
670d858 (download: rewrite file downloads, 2025-06-09) removed the
code using that data, so remove the fields.
Previously, kubespray created local_release_dir as root unnecessarily,
so we need to fix the permissions. The simplest way it to provide a way
for users to remove the directory entirely.

Since the default value is /tmp/releases and in most Linux distribution
/tmp should be cleaned after some days by systemd-tmpfiles, in most
cases this should not be needed.
The file artefacts are handled by the new download/file role
The molecule_run.sh isn't really needed since the switch to gitlab-ci
matrix runs.
@Payback159

Copy link
Copy Markdown
Contributor

I test-merged this against #13397 and ran a full install + upgrade on 6 VMs. Two things I hit
while doing it, both about the control node becoming a provisioning target.

1. vagrant up hangs forever

playbooks/internal_facts.yml:12 gains localhost:

-  hosts: k8s_cluster:etcd:calico_rr
+  hosts: k8s_cluster:etcd:calico_rr:localhost

The shipped Vagrantfile runs Ansible with a global become (ansible.become = true, line 329)
and -e ansible_become_pass=vagrant (line 334). So the setup tasks now run on the control
node
under sudo, with the VM's password. On a workstation whose sudo needs a password this
blocks indefinitely — no error, no timeout. I sat at

TASK [network_facts : Gather node IPs]

for 14 minutes twice before spotting

sudo -H -S -p "[sudo via ansible, key=…] password:" -u root /bin/sh -c
  '… /home/<me>/.ansible/tmp/ansible-tmp-…/AnsiballZ_setup.py'

(the controller's ~/.ansible/tmp and venv interpreter identify it as localhost, not a VM).

Setting ansible_become: false for localhost fixes it and the task drops to 0.94s.
Gathering facts about the control node doesn't need root, so become: false on that play
might be the cleaner fix.

CI wouldn't catch this — a control node running as root or with passwordless sudo just works.

2. tools_setup writes into /usr/local/bin on the control node

With become off, the next thing is:

TASK [tools_setup : Install tools | kubeadm]
fatal: [localhost]: FAILED! => {"msg": "Destination /usr/local/bin not writable"}

Which is by design — the README says the role sets up tools "on both the Ansible control node
and the managed node", and roles/download/container then calls {{ bin_dir }}/skopeo on
localhost. But bin_dir defaults to /usr/local/bin, so this is a new requirement worth
calling out: kubespray now needs root on the control node, and it will overwrite an existing
/usr/local/bin/kubeadm there. Neither is mentioned in the docs.

A separate control-node bin_dir (or documenting that it must be set) would avoid both.

Both were easy to work around locally (ansible_become: false + a writable bin_dir in
host_vars/localhost.yml), after which install and upgrade completed with failed=0.

@VannTen

VannTen commented Jul 28, 2026 via email

Copy link
Copy Markdown
Contributor Author

@tico88612

Copy link
Copy Markdown
Member

By the way, how should I test this download refactor PR? It looks like some parts still need to be adjusted.

@kubernetes-prow

Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. ci-extended Run additional tests cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/bug Categorizes issue or PR as related to a bug. kind/feature Categorizes issue or PR as related to a new feature. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. release-note-action-required Denotes a PR that introduces potentially breaking changes that require user action. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. tide/merge-method-merge Denotes a PR that should use a standard merge by tide when it merges.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gateway API CRDs failure / kubectl does not use server_side_apply var Downloaded tarballs don't need root ownership and execute bit

6 participants