Loading
Gate gitlab-rails-* on the upstream assets image
What
Adds the wait-for-assets-{ce,ee} gate jobs (stage prepare on both Debian and UBI, needs: [], using a plain runner without dind/buildx). gitlab-rails-{ce,ee} get an optional needs: on the gate, so they wait for it to clear before starting. The gate polls the registry for the upstream gitlab-assets-{ce,ee}:${GITLAB_ASSETS_TAG} image via a new wait_for_assets helper.
The rails jobs now call CONTAINER_VERSION=$(get_rails_version) fetch_assets, so fetch_assets sees the rails version while build_asset_image does not.
Related to team-tasks#274 and the incident gitlab-com/gl-infra/production#23186 (The 19.5 Release was blocked for approximately 7hrs).
Why
Findings from the investigation in team-tasks#274:
- No ordering guarantee: The assets image is built by the upstream GitLab pipeline (
cache:assets-hash→compile-production-assets→build-assets-image). Upstream-triggered CNG pipelines are gated onbuild-assets-imagein the Upstream Rails CI, but CNG-initiated pipelines (master pushes, MRs, their ubi/fips downstreams) are not.fetch_assetsis polled insidegitlab-rails-*as afetch-sleeploop. - The poll ran on the most expensive runner:
gitlab-rails-*uses the${HIGH_CAPACITY_RUNNER_TAG}, which starts thedind/buildxbuilder, then polls. With.job-basehavingretry: 1, that means two jobs per pipeline, and every phase-six image blocked behind them.fetch_assetsnow has a 45 min limit (!3179 (merged), on master), which bounds this but still spends that time on the high-capacity runner. - The rails jobs wait for an image that is not yet in the registry:
fetch_assetspollsskopeo inspectfor the upstreamgitlab-assets-{ce,ee}image, and in team-tasks#274 that image had not been built yet whengitlab-rails-ee/gitlab-rails-cestarted. It is a transient condition, but it has failed pipelines during releases, so this is intended as a stopgap rather than outright changing how CNG sources its assets image. This is a small gate on a plain runner that checks whether the assets image has been built beforegitlab-rails-*claims its runner, saving that runner time and cost.
How it works
- Skips the wait if
COMPILE_ASSETS=true. - Probes the repository once via curl (tags-list,
n=1); a wrong image path (404NAME_UNKNOWN) fails immediately with exit 1, which is not retried. - Polls only while skopeo reports a missing manifest.
- Transient errors are retried
ASSETS_WAIT_TRANSIENT_RETRIEStimes (default 3) with exponential backoff (interval * 2^(failures-1), i.e. 30s, 60s, 120s). Transient means transport errors (DNS, connection, TLS, timeouts), registry 5xx (received unexpected HTTP status: 500/502/503/504/429). The 5xx wording is inferred from skopeo's error format, not captured from a real failure; the patterns are one list inassets_image_last_error_is_transientso they are easy to extend. - Any other error is passed through as skopeo reported it, with skopeo's exit status intact, and is not retried.
- If the assets are still missing after
ASSETS_WAIT_TIMEOUTthe gate returns exit code 75, and the job retries only on exit code 75 (one retry).
Timeouts and retry arithmetic
- Hosted GitLab.com runners stop jobs at 3h regardless of the job
timeout, so the gate hastimeout: 3handASSETS_WAIT_TIMEOUTdefaults to 9900s (2h45m), leaving headroom for start-up and the final poll so the gate, not the runner, reports the failure. - 2h45m per attempt, one retry on exit 75: about 5.5h in total before the pipeline fails.
ASSETS_WAIT_TIMEOUTis not set at job level on purpose: a job-levelvariables:entry would shadow project/pipeline variables, so it could no longer be tuned without a code change. The default lives inwait_for_assets.
Testing
bats --print-output-on-failure build-scripts/tests: all 107 tests pass locally, including 19 inwait_for_assets.batswith stubbedskopeoandcurl.- Covered: COMPILE_ASSETS skip, repository probe (unknown repo, inconclusive probe, no job token, registry port), poll loop, job token credential, transient retries (network, 503 that recovers,
toomanyrequests), exponential backoff (5/10/20s with a stubbedsleep) vs. flat polling for a missing manifest, error pass-through, exit 75 at the deadline.
- Covered: COMPILE_ASSETS skip, repository probe (unknown repo, inconclusive probe, no job token, registry port), poll loop, job token credential, transient retries (network, 503 that recovers,
- shellcheck on the test stubs and
wait_for_assets.batsis clean; thebuild.shshellcheck warning count is unchanged from master. - Please play the manual
fips_pipelinebridge to validate UBI/FIPS.
Follow-ups
- Move
GITLAB_ASSETS_TAGoff the floating ref slug onto the immutableassets-hash-<sha>tag for branch pipelines (same hash across .com, dev and the security mirror; tag pipelines keep the tag name). - Check the upstream registry cleanup policies keep
masterand*-stable-ee. - Optionally fall back to
COMPILE_ASSETS=trueafter the deadline for a deterministic build.
Edited by Adam Chu