Skip to content

protect service accounts for ongoing Cloud Builds and queued Kueue jobs - #6253

Open
aslam-quad wants to merge 4 commits into
GoogleCloudPlatform:developfrom
aslam-quad:protect-active-cloud-builds-SA-kueue
Open

protect service accounts for ongoing Cloud Builds and queued Kueue jobs#6253
aslam-quad wants to merge 4 commits into
GoogleCloudPlatform:developfrom
aslam-quad:protect-active-cloud-builds-SA-kueue

Conversation

@aslam-quad

@aslam-quad aslam-quad commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes a bug where the cleanup script accidentally deleted Service Accounts for tests that were still running or waiting in the queue.

Changes:

Cloud Build Protection: Finds ongoing Cloud Builds and extracts their 6-character ID, adding it to the script's EXCLUSION_MAP to protect their Service Accounts.

Kueue Job Protection: Safely checks clusters using custom-columns to find jobs that haven't succeeded or failed yet (catching both Running and Queued tests), and adds them to the EXCLUSION_MAP.

Submission Checklist

NOTE: Community submissions can take up to 2 weeks to be reviewed.

Please take the following actions before submitting this pull request.

  • Fork your PR branch from the Toolkit "develop" branch (not main)
  • Test all changes with pre-commit in a local branch #
  • Confirm that "make tests" passes all tests
  • Add or modify unit tests to cover code changes
  • Ensure that unit test coverage remains above 80%
  • Update all applicable documentation
  • Follow Cluster Toolkit Contribution guidelines #

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a safety mechanism to the cleanup process by identifying and protecting service accounts currently in use by active Cloud Build builds and GKE Kueue jobs. By querying active builds and inspecting GKE clusters for running or queued jobs, the script ensures that critical service accounts are not prematurely removed during automated cleanup tasks.

Highlights

  • Service Account Protection: Added a new function to prevent the accidental deletion of service accounts associated with ongoing Cloud Build builds and active GKE Kueue jobs.
  • Exclusion Logic Integration: Integrated the new protection logic into the main execution flow to ensure active resources are correctly identified and excluded before cleanup operations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the external PR from external contributor label Sep 2, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new function populate_active_build_exclusions in tools/cleanup.sh to protect service accounts associated with ongoing Cloud Build builds and active GKE Kueue batch jobs from being cleaned up. Feedback on this change highlights issues with inconsistent indentation (spaces instead of tabs) and a potential resource leak where the temporary KUBECONFIG file is not cleaned up if the script is interrupted, suggesting the use of a trap on EXIT for robust cleanup.

Comment thread tools/cleanup.sh
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from 27ceb2e to 22e3d60 Compare September 2, 2026 10:54
@aslam-quad

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new function populate_active_build_exclusions in tools/cleanup.sh to protect service accounts associated with active Cloud Build builds and GKE Kueue batch jobs from being cleaned up. The review feedback highlights two important issues: first, the gcloud builds list command uses an unsupported --ongoing flag which will cause it to fail, and second, a failure in mktemp is not handled, which could inadvertently lead to overwriting the user's default kubeconfig file.

Comment thread tools/cleanup.sh Outdated
Comment thread tools/cleanup.sh Outdated
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from 22e3d60 to 39b1109 Compare September 2, 2026 16:17
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from 39b1109 to caa0088 Compare September 2, 2026 16:19
@aslam-quad

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new function populate_active_build_exclusions in tools/cleanup.sh to protect service accounts associated with active Cloud Build builds and GKE Kueue batch jobs from being deleted during cleanup. The review feedback highlights a potential race condition in Kueue job filtering where provisioning jobs could be missed, suggesting a robust filtering approach that avoids masking kubectl errors. Additionally, the feedback recommends preserving and restoring the user's original KUBECONFIG environment variable instead of unconditionally unsetting it to prevent unintended side effects.

Comment thread tools/cleanup.sh Outdated
Comment thread tools/cleanup.sh Outdated
Comment thread tools/cleanup.sh Outdated
@aslam-quad aslam-quad self-assigned this Sep 3, 2026
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from c1269be to 5dfb560 Compare September 3, 2026 09:48
@aslam-quad
aslam-quad marked this pull request as ready for review September 3, 2026 10:09
@aslam-quad
aslam-quad requested a review from a team as a code owner September 3, 2026 10:09
@aslam-quad
aslam-quad requested review from AdarshK15, Neelabh94, ishitachail and rahimkhan19 and removed request for AdarshK15 and ishitachail September 3, 2026 10:09
@aslam-quad aslam-quad assigned Neelabh94 and unassigned aslam-quad Sep 3, 2026
@aslam-quad aslam-quad added the release-chore To not include into release notes label Sep 3, 2026
@aslam-quad aslam-quad assigned rahimkhan19 and unassigned Neelabh94 Sep 3, 2026
Comment thread tools/cleanup.sh
if [[ "$succeeded" =~ ^(<none>|0)$ && "$failed" =~ ^(<none>|0)$ ]]; then
if [[ -z "${EXCLUSION_MAP[${job_name}]:-}" ]]; then
log "INFO" "Protecting active/queued Kueue job: ${job_name} (cluster: ${cluster_name})"
EXCLUSION_MAP["${job_name}"]=1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a critical mismatch here that will cause service accounts for active/queued Kueue jobs to still be deleted.

The Problem:

process_service_accounts() checks if any key in EXCLUSION_MAP is a substring of the service account email ("$email" == *"$protected_name"*).

Adding the raw Kubernetes job name EXCLUSION_MAP["${job_name}"]=1 does not match the actual service account email because many integration tests use a DEPLOYMENT_NAME (and thus service account name) that diverges from the Kubernetes Job name:

  • vm-storage.yaml: Job name is vm-storage-a1b2c3, but deployment name is vmstorage-a1b2c3 (no hyphen) $\rightarrow$ SA email is vmstorage-a1b2c3-compute@....
  • ml-a3-ultragpu-onspot-jbvms.yaml: Job name is ml-a3-ultragpu-onspot-jbvms-a1b2c3, but deployment name is a3u-spot-jbvms-a1b2c3 $\rightarrow$ SA email is a3u-spot-jbvms-a1b2c3-sa@....
  • slurm-gcp-v6-ubuntu.yaml: Job name is slurm-gcp-v6-ubuntu-a1b2c3, but deployment name is ubun-v6-a1b2c3 $\rightarrow$ SA email is ubun-v6-a1b2c3-sa@....
  • gke-managed-hyperdisk.yaml: Job name is gke-managed-hyperdisk-a1b2c3, but deployment name is gke-hyperdisk-a1b2c3.
  • packer.yaml: Job name is packer-a1b2c3, but deployment name is pkrv6a1b2c3.

Because "vmstorage-a1b2c3-compute@..." does not contain "vm-storage-a1b2c3", the substring match fails and the service account is deleted during cleanup.

Solution:

In addition to the job name, extract the 6-character short build ID from the job (from the job name suffix ${job_name##*-} or the build-id label) and add that prefix to EXCLUSION_MAP, matching how Part A works:

Suggested change
EXCLUSION_MAP["${job_name}"]=1
# If a job hasn't succeeded and hasn't failed, it MUST be Running or Queued!
if [[ "$succeeded" =~ ^(<none>|0)$ && "$failed" =~ ^(<none>|0)$ ]]; then
EXCLUSION_MAP["${job_name}"]=1
# Extract short build ID prefix to protect SAs derived from DEPLOYMENT_NAME
local short_id="${job_name##*-}"
if [[ -n "$short_id" && -z "${EXCLUSION_MAP[${short_id}]:-}" ]]; then
log "INFO" "Protecting active/queued Kueue job prefix: ${short_id} (job: ${job_name}, cluster: ${cluster_name})"
EXCLUSION_MAP["${short_id}"]=1
fi
fi

Comment thread tools/cleanup.sh
fi

# --- Part B: Protect service accounts tied to active GKE Kueue batch jobs ---
if ! command -v kubectl &>/dev/null; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In tools/cloud-build/project-cleanup.yaml, the scheduled cleanup step executes inside gcr.io/cloud-builders/gcloud:

- name: gcr.io/cloud-builders/gcloud
  entrypoint: /bin/bash
  ...
  /workspace/tools/cleanup.sh

gcr.io/cloud-builders/gcloud does not have kubectl pre-installed. In production CI, command -v kubectl will evaluate to false, log a warning, and silently skip Part B on every scheduled run.

To ensure Part B actually protects Kueue jobs in CI, we should update project-cleanup.yaml in this PR to install kubectl and gke-gcloud-auth-plugin before calling cleanup.sh, e.g.:

  - |
    set -euo pipefail
    gcloud components install kubectl gke-gcloud-auth-plugin --quiet || true
    ...
    /workspace/tools/cleanup.sh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the cleanup step to use the pre-built Cloud SDK image (gcr.io/google.com/cloudsdktool/cloud-sdk) and installed kubectl and gke-gcloud-auth-plugin before running cleanup.sh. This ensures the required tools are available in the CI environment.

@Neelabh94 Neelabh94 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes to address empty key poisoning, TOCTOU race condition, and kubeconfig trap safety.

Comment thread tools/cleanup.sh Outdated
fi
local old_kubeconfig="${KUBECONFIG:-}"
export KUBECONFIG="$temp_kubeconfig"
trap 'rm -f "$temp_kubeconfig"' EXIT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifying global KUBECONFIG and setting trap 'rm -f "$temp_kubeconfig"' EXIT inside a helper function clobbers any script-level EXIT handlers, and trap - EXIT at line 180 permanently unsets all exit handlers for the remainder of the script.

Both gcloud container clusters get-credentials and kubectl accept --kubeconfig="$temp_kubeconfig" directly as a flag. Using the flag eliminates export KUBECONFIG, trap EXIT, and variable restoring entirely:

				if ! gcloud container clusters get-credentials "$cluster_name" \
					--location="$cluster_location" --project="$PROJECT_ID" \
					--kubeconfig="$temp_kubeconfig" &>/dev/null; then
					log "WARNING" "Failed to authenticate kubectl against cluster ${cluster_name}; skipping this cluster for Kueue job protection."
					continue
				fi

				local raw_jobs
				if ! raw_jobs=$(kubectl get jobs -A -l "kueue.x-k8s.io/queue-name" \
					--kubeconfig="$temp_kubeconfig" \
					--request-timeout=10s \
					-o custom-columns="NAME:.metadata.name,SUCCEEDED:.status.succeeded,FAILED:.status.failed" \
					--no-headers 2>/dev/null); then

Comment thread tools/cleanup.sh

# In kubectl, empty numbers show up as "<none>"
# If a job hasn't succeeded and hasn't failed, it MUST be Running or Queued!
if [[ "$succeeded" =~ ^(<none>|0)$ && "$failed" =~ ^(<none>|0)$ ]]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two critical bugs in this block:

  1. Empty Key Project-Wide Poisoning: If line contains leading whitespace or empty fields, job_name can be empty, setting EXCLUSION_MAP[""]=1. Downstream in process_service_accounts(), [[ "$email" == *"$protected_name"* ]] matches every service account in the GCP project when protected_name="", causing the script to silently abort deleting all orphaned service accounts project-wide. Please guard with [[ -z "$job_name" ]] && continue.

  2. Kueue Job vs. Service Account Name Mismatch: Matching on full job_name fails because integration test jobs derive service account emails from DEPLOYMENT_NAME (e.g. vmstorage-a1b2c3-compute@...), which diverges from the k8s Job name (vm-storage-a1b2c3). In addition to job_name, extract the short build ID suffix (${job_name##*-}) and add that prefix to EXCLUSION_MAP.

						[[ -z "$job_name" ]] && continue

						if [[ "$succeeded" =~ ^(<none>|0)$ && "$failed" =~ ^(<none>|0)$ ]]; then
							if [[ -z "${EXCLUSION_MAP[${job_name}]:-}" ]]; then
								log "INFO" "Protecting active/queued Kueue job: ${job_name} (cluster: ${cluster_name})"
								EXCLUSION_MAP["${job_name}"]=1
							fi
							local short_id="${job_name##*-}"
							if [[ -n "$short_id" && -z "${EXCLUSION_MAP[${short_id}]:-}" ]]; then
								log "INFO" "Protecting service accounts matching Kueue build prefix: ${short_id} (job: ${job_name})"
								EXCLUSION_MAP["${short_id}"]=1
							fi
						fi

Comment thread tools/cleanup.sh Outdated

check_dependencies
load_exclusions
populate_active_build_exclusions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a 15–20 minute TOCTOU (Time-of-Check to Time-of-Use) race window here. populate_active_build_exclusions is called at script start in Phase 1, but process_service_accounts() runs in Phase 5 after synchronous deletion of clusters, Filestores, disks, and networks. Any integration test triggered while earlier phases are executing will have its newly created service accounts deleted in Phase 5 because EXCLUSION_MAP was populated 20 minutes earlier.

Please move populate_active_build_exclusions to execute immediately before process_service_accounts in Phase 5.

@aslam-quad
aslam-quad marked this pull request as draft September 3, 2026 12:29
@rahimkhan19 rahimkhan19 removed their assignment Sep 3, 2026
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from 0d8621d to 6139189 Compare September 4, 2026 05:41
@aslam-quad
aslam-quad force-pushed the protect-active-cloud-builds-SA-kueue branch from 6139189 to b19db43 Compare September 4, 2026 06:02
@aslam-quad
aslam-quad marked this pull request as ready for review September 4, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external PR from external contributor release-chore To not include into release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants