From 7731bee96568913b2cd71d4d09f3d8c65c467b33 Mon Sep 17 00:00:00 2001 From: Sahil Batra Date: Sat, 8 Aug 2026 23:42:55 -0400 Subject: [PATCH] Fix silent no-op when destroying the Vertex AI Feature Store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit destroy_resources.sh guarded its deletes with `if gcloud ai feature-online-stores describe ... >/dev/null 2>&1`. That command group is beta-only and absent from a stock gcloud install, so gcloud exits 2 with "Invalid choice" — which the guard misread as "resource doesn't exist". The script printed "not found — skipping" and "Feature Store torn down" while ml_online_store kept running a Bigtable node and billing. Talk to the Vertex AI REST API directly instead, matching how the store is created (feature_store/setup.py uses the Python SDK, not gcloud). Deletes now poll the returned LRO to completion rather than returning before the resource is actually gone. Also delete every feature view in the store instead of only the hardcoded $FEATURE_VIEW_ID — any leftover view blocks the store delete with FAILED_PRECONDITION. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/destroy_resources.sh | 91 +++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/scripts/destroy_resources.sh b/scripts/destroy_resources.sh index 4bf23e3..62c80e9 100755 --- a/scripts/destroy_resources.sh +++ b/scripts/destroy_resources.sh @@ -76,12 +76,66 @@ run() { fi } +# --- Vertex AI Feature Store REST helpers --- +# `gcloud ai feature-online-stores` is beta-only and absent from a stock gcloud +# install, so it can't be used here: it exits 2 ("Invalid choice") and a +# `if gcloud ... 2>/dev/null` guard silently misreads that as "doesn't exist", +# leaving a Bigtable node billing forever. Creation already goes straight to the +# REST API via the Python SDK (feature_store/setup.py), so deletion does too. +FS_API="https://${FS_REGION}-aiplatform.googleapis.com/v1" +FS_PARENT="projects/${PROJECT_ID}/locations/${FS_REGION}" + +fs_curl() { # fs_curl -> body on stdout, HTTP status as exit-code proxy + curl -sS -X "$1" \ + -H "Authorization: Bearer $(gcloud auth print-access-token --project="$PROJECT_ID")" \ + -H "Content-Type: application/json" \ + -w '\n%{http_code}' "$2" +} + +# fs_get — succeeds only on HTTP 200; prints the body. +fs_get() { + local out status + out=$(fs_curl GET "$1") || return 1 + status=$(printf '%s' "$out" | tail -n1) + [ "$status" = "200" ] || return 1 + printf '%s' "$out" | sed '$d' +} + +# fs_delete