Add ability to push image indexes with pusher

What does this MR do?

Adds support to scripts/pusher for pushing multi-arch helper images. With the -gen-indexes flag, it generates a reasonable list of image indexes to push, after the component images themselves have been pushed. These image indexes are pushed during the release stage, as part of the development docker images, bleeding docker images, and stable docker images jobs. The -gen-indexes flag is only needed when pushing helper-images.json, because the archives referenced in runner-images.json are already multi-arch image indexes.

Why was this MR needed?

I've long wanted the helper images to have an image index wrapper that allows someone interested to simply use e.g. gitlab-runner-helper:latest, and have it pull in the default alpine for the current platform, rather than needing to specify gitlab-runner-helper:arm64-latest. This does not remove the architecture-specific tags, simply knits an image index together from the already-pushed architecture-specific tags for convenience.

What's the best way to test this MR?

Download artifacts from a CI build, and extract each at the root of the project (creates out/helper-images/*.zip):

  1. helper images: [alpine alpine-pwsh ubuntu ubuntu-pwsh ubi-fips]
  2. helper images: [windows-nanoserver-ltsc2019 windows-servercore-ltsc2019]
  3. helper images: [windows-nanoserver-ltsc2022 windows-servercore-ltsc2022]

Run the test:

cd scripts/pusher
go test
go build
./pusher helper-images.json registry.example.com/gitlab/gitlab-runner/index-test foo bar

Validate results, e.g. skopeo inspect --raw docker://registry.example.com/gitlab/gitlab-runner/index-test:foo | jq -r '.'

Implementation Overview

I've rewritten much of the logic in pusher to attempt to make it more readable after the inclusion of the index configuration generation and index pushes. As a high level overview, here's how pusher functions:

  1. Executed as ./pusher [-gen-indexes] {config.json} {repo} {tagFragment1} [tagFragment2...]
  2. Config file is read. If the -gen-indexes flag was passed, a default set of indexes is generated by inspecting the tag templates for the component images.
  3. This component/index config is combined with the repo and tag fragments from the command line, to create a final RuntimeConfig.
  4. Component images are pushed, by extracting the archives created earlier in the CI/CD pipeline. The digest-based references of these component images are stored in the RuntimeConfig.
  5. Image indexes are pushed by fetching the metadata for the component images from the destination repo using the digest-based references stored in the previous step, and combining them according to the RuntimeConfig.
  6. Print a completion message and exit

Step 5 could be further updated to remove the need to fetch metadata from the destination repo, if we were to rework the lifetime of the extracted component archives, or simply extract the expected data into a more concrete representation before the extracted archive was deleted.

The index config generation in step 2 is contained in indexes.go. I added unit tests in indexes_test.go. I also added a -print-indexes option, so ./pusher -print-indexes helper-images.json will print the generated index config to the command line for manual inspection.

Following conversations on #25869, I've added special handling for tag templates % and %-pwsh. The % tag template (e.g. final tag v18.10.0) will refer to the default alpine images for all architectures, as well as all architectures/versions of Windows servercore images. The %-pwsh tag template (e.g. final tag v18.10.0-pwsh) will include alpine images with pwsh, as well as Windows nanoserver images.

What are the relevant issue numbers?

Would replace !5147 (closed) and !4778 (closed), which replaced !4680 (closed) and !4116 (closed) and possibly others before.

Edited by Anthony Juckel

Merge request reports

Loading