Repository navigation
ci: pull Docker Hub test images through the mirror.gcr.io cache - #808
Conversation
The integration and e2e jobs pulled every Docker Hub image anonymously (ClickHouse, Redis, Valkey, NATS, dynamodb-local and testcontainers' Ryuk) from hosted runners that share IP addresses, so a job could fail with toomanyrequests before any test ran. Each container job now runs scripts/ci/docker-hub-mirror.sh first. It adds mirror.gcr.io to the runner daemon's registry-mirrors, keeping the keys already there, and reloads dockerd with SIGHUP. dockerd tries the mirror first and falls back to Docker Hub, so no image name changes, no secret is needed, and the step is continue-on-error: a failure leaves the daemon pulling from Docker Hub as before. Rewriting image names to the mirror was rejected: mirror.gcr.io serves official images only under library/ (mirror.gcr.io/nats:2.14.6-alpine is not found), and a prefixed pull has no fallback when the mirror's upstream quota refuses a miss. The Actions cache was rejected at about 560 MiB compressed for the six images. Closes #805. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📜 Recent review details
📝 Summary
Merge Risk: ⚪ Minimal · up to The integration and e2e jobs attempt mirror setup, while the documentation notes that setup does not guarantee where an image is served from. Coverage does not pull container images. No actionable merge-blocking risk is established; the hosted runner’s exact Docker Engine version is not pinned. Pre-merge checks |
|
|
📚 Docs preview is live → https://6d963dfd-wavehouse-docs.wave-rf.workers.dev
|
|
@coderabbitai review |
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: ASSERTIVE
- Plan: Advanced
- Run ID:
b95bef4c-acff-4e99-aad2-ec57fbb4ef90
📒 Files selected for processing (5)
.github/workflows/README.md.github/workflows/ci.ymlCHANGELOG.mddocs/src/content/docs/development.mdscripts/ci/docker-hub-mirror.sh
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
📜 Review details
🧰 Additional context used
📚 Code guidelines (3)
.github/workflows/README.md — auto-discovered
AGENTS.md — auto-discovered
docs/src/content/docs/development.md — auto-discovered
📓 Path-based instructions (3)
Source excerpt: How a `merge_group` run flows through the DAG: the classifier treats it like a push (full suite — the queue never skips code checks; `docs` still gates docs-build), while `title` (already validated on the PR), `docs-preview`...
📄 CodeRabbit inference engine (.github/workflows/README.md)
Files:
.github/workflows/ci.yml
Source excerpt: **WH001 applies to every tracked Markdown file, with no carve-out** — `AGENTS.md`, `CHANGELOG.md`, `.github/` CI docs and `.claude/` agent prompts included.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
docs/src/content/docs/development.mdCHANGELOG.md
Source excerpt: Run `make fix` to apply them.
📄 CodeRabbit inference engine (docs/src/content/docs/development.md)
Files:
docs/src/content/docs/development.mdCHANGELOG.md
🧠 Learnings (1)
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.
Applied to files:
docs/src/content/docs/development.mdCHANGELOG.md
🪛 ast-grep (0.45.3)
scripts/ci/docker-hub-mirror.sh
[error] 26-26: sudo is invoked with a user-controlled variable as the command to run (e.g. sudo $cmd, sudo "$cmd", sudo $(...)) or via a shell evaluating a variable (sudo bash -c "$var", sudo sh -c "$cmd"). Because sudo runs as root, any attacker influence over that value (arguments, environment, file contents, network output) becomes root-level command injection. Never build the privileged command from dynamic data: run a fixed, hard-coded program with the variable passed as a quoted argument (sudo /usr/bin/prog -- "$arg"), use an array for the argument list, or restrict the input to a validated allowlist before escalating privileges.
Context: sudo "$@"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(sudo-unquoted-variable-bash)
🪛 LanguageTool
docs/src/content/docs/development.md
[uncategorized] ~370-~370: The official name of this software platform is spelled with a capital “H”.
Context: ...ker Hub as the fallback (invariant 8 of [.github/workflows/README.md](https://github.co...
(GITHUB)
.github/workflows/README.md
[style] ~57-~57: Since ownership is already implied, this phrasing may be redundant.
Context: ...tainers' own Ryuk; Dragonfly comes from its own registry), so jobs failed with `toomany...
(PRP_OWN)
CHANGELOG.md
[typographical] ~136-~136: Consider using an em dash in dialogues and enumerations.
Context: - **CI's container jobs pull their Docker...
(DASH_RULE)
[uncategorized] ~136-~136: The official name of this software platform is spelled with a capital “H”.
Context: ...scripts/ci/docker-hub-mirror.sh(new),.github/workflows/{ci.yml,README.md}, docs/sr...
(GITHUB)
🔇 Additional comments (4)
scripts/ci/docker-hub-mirror.sh (1)
20-46: LGTM!.github/workflows/ci.yml (1)
255-257: LGTM!Also applies to: 307-309
.github/workflows/README.md (1)
57-58: LGTM!docs/src/content/docs/development.md (1)
370-370: LGTM!
…claim A passing mirror step shows the daemon was configured, not where a pull was served from, and pulls that fall back to Docker Hub still count against its anonymous limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@coderabbitai review |
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
CI's container jobs no longer pull Docker Hub images under Docker Hub's anonymous limit, and no repository secret is needed. The
integrationande2ejobs now runscripts/ci/docker-hub-mirror.shright after checkout. The script addshttps://mirror.gcr.io, Google's cache of Docker Hub, to the runner daemon'sregistry-mirrorsin/etc/docker/daemon.jsonand keeps the keys already there. Then it sends dockerd aSIGHUP, becauseregistry-mirrorsis one of the settings dockerd reloads without a restart. From then on, dockerd tries the mirror first for every Docker Hub pull on that runner and falls back to Docker Hub when the mirror lacks an image or refuses the pull. That covers testcontainers (Ryuk included), the orchestrator and the ClickHouse prefetch.continue-on-error: if it fails, the daemon keeps pulling from Docker Hub, as before.docker login, after a localtoomanyrequests.Images the suites pull
clickhouse/clickhouse-server:26.6.3.62tests/integration,scripts/orchestrator, CI prefetchredis:8.10.2-alpineinternal/cache,tests/integration/shared_cache_test.go,scripts/orchestratorvalkey/valkey:8.1.10-alpineinternal/cachenats:2.14.6-alpinetests/integration(startNATS)amazon/dynamodb-local:3.3.1tests/integrationtestcontainers/ryuk:0.14.0internal/config.ReaperDefaultImagein v0.44.0), once per test processdocker.dragonflydb.io/dragonflydb/dragonfly:v2.0.0internal/cacheThe compose files (
deployments/compose/*.yaml) backmake dev, not the tests, andscripts/otel/*.shrun only on a developer's machine.Why this option (measured 2026-10-10 unless labelled)
TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX, or a helper of ours): rejected.mirror.gcr.ioserves official images only underlibrary/.mirror.gcr.io/library/nats:2.14.6-alpineresolves, butmirror.gcr.io/nats:2.14.6-alpineisno such manifest, and testcontainers' prefix is a plainpath.Join(prefix, image).mirror.gcr.iocan refuse a miss on its own upstream quota. The first lookup ofmirror.gcr.io/clickhouse/clickhouse-server:26.6.3.62returnedtoomanyrequests: Quota exceeded for quota metric 'Read requests to external upstream hosts per organization per host in the US multi-region', and a retry minutes later succeeded.registry-mirrors, dockerd normalizeslibrary/itself and falls back to Docker Hub on such an error. moby'scontinueOnErrorfalls back for any registry error code from a mirror endpoint (from the source, not tested).public.ecr.aws/docker/library/<image>): rejected.redisandnatsresolve there, but ClickHouse, Valkey, dynamodb-local and Ryuk don't exist underpublic.ecr.aws/docker/, so each image would need its own path mapping.docker/library/<image>, notlibrary/<image>, and dockerd puts a mirror URL's path before/v2/, so no mirror URL maps one onto the other (inferred)..github/workflows/README.mdalready budgets past the 10 GB cap in its worst case, and the first fill would still pull from Docker Hub.docker loginwith a token: not built, because it needs a repository secret, which is the maintainer's call. It would raise the fallback path from 100 to 200 pulls per six hours, and it composes with the mirror.Documented limits:
mirror.gcr.io: Google documents no quota. "Pulling cached images does not count against Docker Hub rate limits", and it caches only frequently requested images. The upstream quota above is what a miss met.Not covered
publish-dev.yml,release.ymlandgoreleaser-validate.ymlbuild images whose stages start fromalpine:3andgolang:1.26.9-alpineon Docker Hub. They build through BuildKit'sdocker-containerdriver, which doesn't read the daemon'sregistry-mirrors(inferred). That is about one pull per push tomain, and it hasn't been seen failing. Moving those base images is a change to the shipped build recipe, so it is left for a follow-up if wanted.Overlap with #712
#712 moves the ClickHouse pin into
internal/chversionand bumps it to 26.8.15.10 (both versions exist on the mirror). This PR changes no image reference. Against #712's head,git merge-treeconflicts in the same files (.github/actions/setup-env/action.yml,.github/workflows/{README.md,ci.yml}) and with the same number of conflict regions as #712 already has againstmain; one line of this PR's README addition sits inside one of those README regions, so whichever lands second resolves it there. None of this PR'sci.ymllines fall inside a conflict region.Test plan
docker:28.0.4-dind, the same engine version as theubuntu-24.04runner image (20261004.327.1), on overlay2.sudoas uid 1000.daemon.json(exec-opts,max-concurrent-downloads) kept, and a second run is a no-op./etc/dockercreated when it is missing.docker infothen listshttps://mirror.gcr.io/.-tags integration(no-race) all pass.internal/cacheTestRedis_Conformance(Redis, Valkey, Dragonfly, Redis Cluster).tests/integration:TestCoordNATS_ReplicasShareTheShards,TestSharedCache_RedisDownQueriesBypass,TestIngest_FlowsToClickHouseWithoutDLQandTestDynamoDBDedupe_TwoInstancesShareSeenIDs, withTestMain's ClickHouse and dynamodb-local.Trying to pull <image> from https://mirror.gcr.io/for each of the six Docker Hub images, with noAttempting next endpointand noregistry-1.docker.io. Dragonfly came straight fromdocker.dragonflydb.io.Attempting next endpoint for pull after errorand pullednats:2.14.6-alpinefromregistry-1.docker.io.make lint-sh lint-gha lint-md lint-prosemake cidocker-hub-mirror: Docker Hub pulls try https://mirror.gcr.io first, andtoomanyrequestsstops appearing in the integration and e2e logs.Related Issues
Closes #805. Part of #740.
🤖 Generated with Claude Code