Draft: migrate gitlab-toolbox from gsutil to gcloud storage
What does this MR do?
Moves the GCS backend in the gitlab-toolbox image (Debian and UBI) from the legacy gsutil CLI to gcloud storage, as proposed in #2483.
- Removes
gsutil==5.37andcrcmodfrom the pip install. - Installs the Google Cloud CLI 590.0.1 from Google's versioned Linux tarball, with an SHA256 checksum pinned per architecture.
- Changes
backup-utilityandobject_storage_backup.rbtogcloud storage. Only thegcscode paths change; S3 and Azure are untouched.
Changes
gitlab-toolbox/Dockerfile(Debian):- Drops
ARG GSUTIL_VERSIONandgsutil crcmodfrom pip. awscli, s3cmd, pyOpenSSL and cryptography are unchanged. - Adds a
gcloudbuild stage modeled on theazcopystage. It downloadsgoogle-cloud-cli-<version>-linux-<arch>.tar.gz, verifies it withsha256sum -c, and runsinstall.sh --usage-reporting false. - The stage sets installation properties to disable update checks and survey prompts. It removes the
bqcomponent and thegsutilcopy that ships inside the Cloud CLI, and strips caches and__pycache__. - The final image copies
/usr/local/google-cloud-sdkand linksgcloudinto/usr/local/bin.
- Drops
gitlab-toolbox/Dockerfile.build.ubi: same pip removal and the same install, verify and trim steps in thebuildstage. Adds/usr/local/google-cloud-sdkand thegcloudsymlink to the/assetscopy.ci_files/variables.yml: addsGCLOUD_VERSION,GCLOUD_CHECKSUM_SHA256_AMD64andGCLOUD_CHECKSUM_SHA256_ARM64..gitlab/ci/common.gitlab-ci.yml: passes the three pins as--build-argto the toolbox asset and final builds (EE and CE), following the CFSSL pattern.scripts/renovate/update-gcloud-checksum.sh(new): Renovate post-upgrade script, mirroringupdate-gomplate-checksum.sh. It recomputes both checksums in all three pin locations.gitlab-toolbox/scripts/bin/backup-utilityandgitlab-toolbox/scripts/lib/object_storage_backup.rb: gcs branches only, see the mapping below. Incleanup, the empty-bucket guard now looks for listedgs://URLs.run_cmdmerges stderr into the output, so a gcloud warning would have defeated the oldoutput.length == 0check.- Tests and docs:
development/tests/gsutil/test.shbecomesdevelopment/tests/gcloud-storage/test.sh. It authenticates withgcloud auth activate-service-account, the same command the chart runs, and keeps the wildcardlsthat backup pruning depends on.- The gsutil test plan becomes
upgrade-gcloud-storage-test-plan.md, and the test plan index is updated.
Command mapping
| Caller | gsutil | gcloud storage |
|---|---|---|
backup-utility |
cp gs://… dst |
cp gs://… dst |
cp -n |
cp --no-clobber |
|
cp -s CLASS -n |
cp --storage-class="CLASS" --no-clobber |
|
ls gs://B/<wildcard> (pruning) |
ls gs://B/<same wildcard>, still LC_ALL=C sort |
|
rm URL |
rm URL |
|
object_storage_backup.rb |
ls gs://B (bucket check) |
ls gs://B |
-m rsync -d -x REGEX -r SRC DST |
rsync --recursive --delete-unmatched-destination-objects --exclude=REGEX SRC DST |
|
-m rsync -r (upload, bucket to bucket) |
rsync --recursive |
|
ls gs://B/ (cleanup) |
ls gs://B/ |
|
rm -f -r gs://B/* |
rm --recursive --continue-on-error gs://B/* |
-m is dropped everywhere because gcloud storage runs in parallel by default.
Authentication
No CNG change is needed. The chart's configure-gcloud-storage script runs before both the toolbox Deployment and the backup CronJob.
- When
gcloudis onPATH, it runsgcloud auth activate-service-account --key-file="$GOOGLE_APPLICATION_CREDENTIALS". - When no key is configured, it falls back to Application Default Credentials, which covers GKE Workload Identity.
Version and checksums
The checksums were computed from the downloaded tarballs; Google does not publish per-version SHA256 files.
| Arch | Tarball | SHA256 |
|---|---|---|
| amd64 | google-cloud-cli-590.0.1-linux-x86_64.tar.gz |
d1ae7be7e12c4e7d8286fab4774f5441020aca89dc7793ddacbace8d11d20f06 |
| arm64 | google-cloud-cli-590.0.1-linux-arm.tar.gz |
fd760d8b1c2c4c484d7b659964994b07aaefddc6ac4350e681940477dbdbb22c |
Image size
Compared with master, built locally for arm64 from the same base images:
| Image | master | this MR | Change |
|---|---|---|---|
| Debian | 3099.6 MB | 3417.8 MB | +318 MB |
| UBI | 5164.7 MB | 5482.9 MB | +318 MB |
The installed SDK is about 431 MB. About 101 MB of that is the Cloud CLI's bundled Python, which has to stay (see Notes for reviewers).
Test plan
Local verification:
- Debian amd64 and arm64, UBI arm64 (full image) and UBI amd64 (build stage) build successfully. In each image:
gcloud --versionreportsGoogle Cloud SDK 590.0.1.gsutil,bqandcrcmodare absent.s3cmd,awsandazcopyare unchanged.
bash -n backup-utilityandruby -c object_storage_backup.rbpass. shellcheck reports no new warnings: 104 existing warnings on bothmasterand this branch.- Functional run against fake-gcs-server, using the real
get_existing_backupsandremove_backupfunctions frombackup-utilityand the realobject-storage-backupandobject-storage-restoreentrypoints:
| Check | Result |
|---|---|
Wildcard ls returns full gs:// URLs, sorted, and remove_backup uses them unchanged |
|
Empty bucket gives zero backups without aborting backup-utility |
|
cp --no-clobber does not overwrite; cp --storage-class sets the class; restore download works |
|
Backup rsync: --exclude drops tmp/builds; --delete-unmatched-destination-objects removes stale local files |
|
Restore rsyncs: bucket to tmp/<name>.<ts>/, and local to bucket |
|
Restore into an empty bucket: cleanup guard skips rm |
|
gcloud storage rm: objects are deleted, but the command exits 1 |
DELETE with 200 null, while GCS returns 204 with an empty body |
Still to do:
- Run
development/tests/gcloud-storage/test.shagainst a real GCS bucket, mainly to confirmgcloud storage rmexit codes. - Back up and restore with
--backend gcson a review app or test cluster. CI's chartbackups_spec.rbuses the default S3 backend (MinIO), so it does not exercise the gcs paths. - Play the
fips_pipelinebridge job (FIPS and UBI).
Notes for reviewers
- Bundled Python and FIPS: the image's Python 3.12 is built without
sqlite3(gitlab-python/Dockerfiledeletes_sqlite3), and gcloud requires it. The Cloud CLI therefore keeps its bundled Python 3.14.8.- That Python links its own OpenSSL 3.5.9, so it does not use the system FIPS-validated module in FIPS images. This needs documenting, or
sqlite3needs to be restored ingitlab-python.
- That Python links its own OpenSSL 3.5.9, so it does not use the system FIPS-validated module in FIPS images. This needs documenting, or
- Symlinks:
gcloud storage rsyncskips symlinks by default, while gsutil followed them. The data being synced always comes from object storage, which has no symlinks, so the default is kept.--no-ignore-symlinkswould match gsutil exactly. rm --recursivealso deletes noncurrent object versions. gsutil's-rdid the same.- Renovate: needs a follow-up MR in renovate-gitlab-bot (
renovate/distribution/cng.config.js):- Replace the
GoogleCloudPlatform/gsutilrule with one forGCLOUD_VERSION. A possible source is the Docker datasourcegcr.io/google.com/cloudsdktool/google-cloud-cliwithX.Y.Ztags. - Add a
postUpgradeTasksrule that runs./scripts/renovate/update-gcloud-checksum.sh, modeled on gomplate.
- Replace the
- Overlap with !3221 (AWS CLI v2): both MRs edit the same pip lines in the toolbox Dockerfiles, the same CI build-arg blocks, and neighboring lines in
variables.yml. Whichever merges second needs a rebase. - Unblocks hardened-builds#49 (GCS backups in the nix toolbox).
Signed-off-by: psingh29 psingh@gitlab.com
Related issues
Closes #2483
Acceptance criteria from #2483
- No
gsutilcall remains ingitlab-toolbox/scripts, and the images no longer install gsutil, including the copy bundled in the Cloud CLI. -
backup-utility --backend gcsbacks up, restores and prunes old backups, andobject-storage-backupandobject-storage-restorework with GCS, on Debian and UBI. Verified against an emulator; a real GCS run is pending (see Test plan). - The image size change is recorded: +318 MB on Debian and UBI.
Checklist
See Definition of done.
For anything in this list which will not be completed, please provide a reason in the MR discussion
Required
- Merge Request Title, and Description are up to date, accurate, and descriptive
- MR targeting the appropriate branch
- MR has a green pipeline on GitLab.com
- When ready for review, MR is labeled "~workflow::ready for review" per the Distribution MR workflow
Expected (please provide an explanation if not completing)
- Test plan indicating conditions for success has been posted and passes
- Documentation created/updated
- Integration tests added to GitLab QA
- The impact any change in container size has should be evaluated
- New dependencies are managed with GitLab forked renovatebot