From 0ac70fb8d9bae95a13fa7d91acab99e8769c038d Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 20 May 2026 13:04:44 +0000 Subject: [PATCH 1/4] Cache pre-pulled container images in CI workflows Use skopeo to query remote digests and key the GHA cache on actual image content. On cache hit, podman load replaces network pulls. Each image is saved as a separate tarball to preserve image-volume layer structure. Assisted-by: Claude Opus 4.6 (1M context) --- .github/workflows/integration-tests.yml | 57 +++++++++++++++++++--- .github/workflows/test-container-image.yml | 50 +++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index aa4b18a..b58edaa 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -10,6 +10,16 @@ concurrency: group: integration-${{ github.head_ref || github.ref }} cancel-in-progress: true +env: + BINK_IMAGES: >- + ghcr.io/alicefr/bink/cluster:latest + ghcr.io/alicefr/bink/node:v1.35-fedora-44-disk + ghcr.io/alicefr/bink/dns:latest + EXTERNAL_IMAGES: >- + docker.io/library/registry:2 + docker.io/library/haproxy:lts-alpine + quay.io/libpod/busybox:latest + jobs: integration-tests: runs-on: ubuntu-latest @@ -80,14 +90,49 @@ jobs: df -h / free -h + - name: Get image digests + id: digests + run: | + ALL_DIGESTS="" + for img in $BINK_IMAGES $EXTERNAL_IMAGES; do + digest=$(skopeo inspect --no-creds "docker://${img}" --format '{{.Digest}}') + echo "${img}: ${digest}" + ALL_DIGESTS="${ALL_DIGESTS}${digest}" + done + echo "hash=$(echo -n "${ALL_DIGESTS}" | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + + - name: Restore cached images + id: image-cache + uses: actions/cache/restore@v4 + with: + path: /tmp/podman-image-cache + key: podman-images-v2-integration-${{ steps.digests.outputs.hash }} + + - name: Load cached images + if: steps.image-cache.outputs.cache-hit == 'true' + run: | + for f in /tmp/podman-image-cache/*.tar; do + sudo podman load -i "$f" + done + sudo podman images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" + - name: Pre-pull container images + if: steps.image-cache.outputs.cache-hit != 'true' run: | - sudo podman pull ghcr.io/alicefr/bink/cluster:latest - sudo podman pull ghcr.io/alicefr/bink/node:v1.35-fedora-44-disk - sudo podman pull ghcr.io/alicefr/bink/dns:latest - sudo podman pull docker.io/library/registry:2 - sudo podman pull docker.io/library/haproxy:lts-alpine - sudo podman pull quay.io/libpod/busybox:latest + mkdir -p /tmp/podman-image-cache + for img in $BINK_IMAGES $EXTERNAL_IMAGES; do + sudo podman pull "$img" + name=$(echo "$img" | sed 's|[/:]|_|g') + sudo podman save -o "/tmp/podman-image-cache/${name}.tar" "$img" + done + sudo chown -R $(id -u):$(id -g) /tmp/podman-image-cache + + - name: Save image cache + if: steps.image-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: /tmp/podman-image-cache + key: podman-images-v2-integration-${{ steps.digests.outputs.hash }} - name: Run integration tests run: sudo make test-integration TEST_PROCS=3 diff --git a/.github/workflows/test-container-image.yml b/.github/workflows/test-container-image.yml index 7dd013a..0525759 100644 --- a/.github/workflows/test-container-image.yml +++ b/.github/workflows/test-container-image.yml @@ -15,6 +15,12 @@ concurrency: group: container-image-test-${{ github.head_ref || github.ref }} cancel-in-progress: true +env: + BINK_RUNTIME_IMAGES: >- + ghcr.io/alicefr/bink/cluster:latest + ghcr.io/alicefr/bink/node:v1.35-fedora-44-disk + ghcr.io/alicefr/bink/dns:latest + jobs: test-container-image: runs-on: ubuntu-latest @@ -52,6 +58,50 @@ jobs: sudo systemctl start podman.socket sudo podman info --format '{{.Store.GraphRoot}}' + - name: Get runtime image digests + id: digests + run: | + ALL_DIGESTS="" + for img in $BINK_RUNTIME_IMAGES; do + digest=$(skopeo inspect --no-creds "docker://${img}" --format '{{.Digest}}') + echo "${img}: ${digest}" + ALL_DIGESTS="${ALL_DIGESTS}${digest}" + done + echo "hash=$(echo -n "${ALL_DIGESTS}" | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + + - name: Restore cached images + id: image-cache + uses: actions/cache/restore@v4 + with: + path: /tmp/podman-image-cache + key: podman-images-v2-container-test-${{ steps.digests.outputs.hash }} + + - name: Load cached images + if: steps.image-cache.outputs.cache-hit == 'true' + run: | + for f in /tmp/podman-image-cache/*.tar; do + sudo podman load -i "$f" + done + sudo podman images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" + + - name: Pre-pull runtime images + if: steps.image-cache.outputs.cache-hit != 'true' + run: | + mkdir -p /tmp/podman-image-cache + for img in $BINK_RUNTIME_IMAGES; do + sudo podman pull "$img" + name=$(echo "$img" | sed 's|[/:]|_|g') + sudo podman save -o "/tmp/podman-image-cache/${name}.tar" "$img" + done + sudo chown -R $(id -u):$(id -g) /tmp/podman-image-cache + + - name: Save image cache + if: steps.image-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: /tmp/podman-image-cache + key: podman-images-v2-container-test-${{ steps.digests.outputs.hash }} + - name: Build bink image run: sudo make build-bink-image From 88f5b9c341fc13c23c893de3a8b68165135c34d2 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 20 May 2026 13:42:36 +0000 Subject: [PATCH 2/4] Load cached images into nested podman environment Mount the host image cache directory into the nested container and load tarballs into its podman storage before running tests. The cache path is configurable via IMAGE_CACHE_DIR env var. Assisted-by: Claude Opus 4.6 (1M context) --- .github/workflows/test-container-image.yml | 2 ++ hack/test-container-image.sh | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/.github/workflows/test-container-image.yml b/.github/workflows/test-container-image.yml index 0525759..322824d 100644 --- a/.github/workflows/test-container-image.yml +++ b/.github/workflows/test-container-image.yml @@ -108,6 +108,8 @@ jobs: - name: Test nested mode run: sudo hack/test-container-image.sh nested timeout-minutes: 40 + env: + IMAGE_CACHE_DIR: /tmp/podman-image-cache - name: Test socket mode run: sudo hack/test-container-image.sh socket diff --git a/hack/test-container-image.sh b/hack/test-container-image.sh index 424e3fd..f3cdf7d 100755 --- a/hack/test-container-image.sh +++ b/hack/test-container-image.sh @@ -2,6 +2,7 @@ set -euo pipefail BINK_IMAGE="${BINK_IMAGE:-ghcr.io/alicefr/bink/bink:latest}" +IMAGE_CACHE_DIR="${IMAGE_CACHE_DIR:-}" if [ -n "${CONTAINER_HOST:-}" ]; then PODMAN_SOCK="${CONTAINER_HOST#unix://}" elif [ -S "/run/podman/podman.sock" ]; then @@ -38,11 +39,16 @@ run_test() { ;; nested) nested_container="bink-nested-${cluster_name}" + cache_mount=() + if [ -n "${IMAGE_CACHE_DIR}" ] && [ -d "${IMAGE_CACHE_DIR}" ]; then + cache_mount=(-v "${IMAGE_CACHE_DIR}:/cache:ro") + fi echo "Starting bink daemon container: ${nested_container}" podman run -d --name "${nested_container}" --privileged \ --device /dev/kvm \ --ulimit core=-1:-1 \ -v "bink-test-storage:/var/lib/containers" \ + "${cache_mount[@]}" \ -v "$(pwd):/output" \ "${BINK_IMAGE}" echo "Waiting for podman service inside container..." @@ -56,6 +62,14 @@ run_test() { # unreachable from inside nested podman networks. Override it so inner aardvark-dns # forwards queries to a public resolver instead. podman exec "${nested_container}" bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' + if podman exec "${nested_container}" test -d /cache; then + echo "Loading cached images into nested podman..." + for f in $(podman exec "${nested_container}" ls /cache/*.tar 2>/dev/null); do + podman exec "${nested_container}" podman load -i "$f" + done + echo "Images available in nested podman:" + podman exec "${nested_container}" podman images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" + fi bink_args=(podman exec "${nested_container}" bink) ;; *) From 221c3161f95e8d383412d1e26e2557f5d3af32aa Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 20 May 2026 13:58:36 +0000 Subject: [PATCH 3/4] Use sudo -E to preserve IMAGE_CACHE_DIR in nested test Assisted-by: Claude Opus 4.6 (1M context) --- .github/workflows/test-container-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-container-image.yml b/.github/workflows/test-container-image.yml index 322824d..5ac0bf0 100644 --- a/.github/workflows/test-container-image.yml +++ b/.github/workflows/test-container-image.yml @@ -106,7 +106,7 @@ jobs: run: sudo make build-bink-image - name: Test nested mode - run: sudo hack/test-container-image.sh nested + run: sudo -E hack/test-container-image.sh nested timeout-minutes: 40 env: IMAGE_CACHE_DIR: /tmp/podman-image-cache From 349d31bd1d84c97a23425b20daa1cc25bd6d7e55 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 20 May 2026 14:07:09 +0000 Subject: [PATCH 4/4] Fix image cache loading glob in nested container Run the glob expansion and podman load inside a single bash -c in the container so *.tar resolves correctly. Assisted-by: Claude Opus 4.6 (1M context) --- hack/test-container-image.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hack/test-container-image.sh b/hack/test-container-image.sh index f3cdf7d..288415a 100755 --- a/hack/test-container-image.sh +++ b/hack/test-container-image.sh @@ -64,9 +64,7 @@ run_test() { podman exec "${nested_container}" bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' if podman exec "${nested_container}" test -d /cache; then echo "Loading cached images into nested podman..." - for f in $(podman exec "${nested_container}" ls /cache/*.tar 2>/dev/null); do - podman exec "${nested_container}" podman load -i "$f" - done + podman exec "${nested_container}" bash -c 'for f in /cache/*.tar; do [ -f "$f" ] && podman load -i "$f"; done' echo "Images available in nested podman:" podman exec "${nested_container}" podman images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" fi