From 588e1d534edd3eaecb388ca5354a3a8aad70614e Mon Sep 17 00:00:00 2001 From: Aditya Shantanu Date: Wed, 2 Sep 2026 10:38:46 -0700 Subject: [PATCH 1/4] setup-gcp: enable artifactregistry.googleapis.com in bootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bootstrap grants roles/artifactregistry.reader to the GKE node service account and the atelet workload-identity principal, and ko pushes the control-plane images through gcr.io's Artifact Registry backing — but the API-enablement step never enabled artifactregistry.googleapis.com, so a fresh project failed at image push/pull instead of step 1. --- tools/setup-gcp/cmd/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/setup-gcp/cmd/api.go b/tools/setup-gcp/cmd/api.go index c80916fc89..586eca8f31 100644 --- a/tools/setup-gcp/cmd/api.go +++ b/tools/setup-gcp/cmd/api.go @@ -32,6 +32,7 @@ func enableRequiredAPIs(ctx context.Context, cfg *Config) error { defer suClient.Close() services := []string{ + "artifactregistry.googleapis.com", "cloudresourcemanager.googleapis.com", "container.googleapis.com", "networkconnectivity.googleapis.com", From b1f1c798ad8407488840e5b96733a0c0b2f5ba64 Mon Sep 17 00:00:00 2001 From: Aditya Shantanu Date: Wed, 2 Sep 2026 10:39:40 -0700 Subject: [PATCH 2/4] teardown.sh: cover everything bootstrap creates, without a dev-env file - Revoke atelet's project-level bindings (roles/storage.objectAdmin and roles/artifactregistry.reader for the workload-identity principal), which grant_atelet_permissions adds but nothing removed. - Delete the three Substrate monitoring dashboards, matched by the display names in tools/setup-gcp/dashboards/. - Accept configuration from the environment when .ate-dev-env.sh is absent, so installers and one-liners can drive the script, and fail with the specific missing variable instead of a blanket message. - Drop the kubectl cluster-admin precheck: every step talks to GCP, not the cluster, and the check blocked tearing down a cluster that was already gone or unreachable. - Make --all run in the true reverse of bootstrap's setup order. --- hack/teardown.sh | 67 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/hack/teardown.sh b/hack/teardown.sh index 2f0258de65..d472b7ccbb 100755 --- a/hack/teardown.sh +++ b/hack/teardown.sh @@ -16,29 +16,35 @@ set -o errexit -o nounset -o pipefail -# Source the environment variables +# Source the environment variables. The file is optional: an installer (or a +# user pasting a one-liner) can pass the same variables through the +# environment instead, which also makes teardown possible on a machine that +# never had a dev-env file. if [ -f .ate-dev-env.sh ]; then source .ate-dev-env.sh -else - echo "Please create .ate-dev-env.sh from the example file in hack" - exit 1 fi - -# Precheck for cluster-admin permissions -kubectl auth can-i delete crd 2>/dev/null | grep -q yes || { - echo "teardown requires cluster-admin on the GKE cluster" >&2 +for var in PROJECT_ID PROJECT_NUMBER CLUSTER_NAME CLUSTER_LOCATION BUCKET_NAME NODE_POOL_NAME; do + if [ -z "${!var:-}" ]; then + echo "${var} is not set; export it or create .ate-dev-env.sh from the example file in hack" >&2 exit 1 -} + fi +done + +# No cluster-admin precheck here: every step below talks to GCP, not to the +# cluster, and requiring a live kubectl context would block tearing down a +# cluster that is already half-gone. # --- Helper Functions --- function usage() { echo "Usage: $0 [options]" echo "Options:" echo " --revoke-gke-node-permissions Revoke GKE nodes permission to pull images" + echo " --revoke-atelet-permissions Revoke atelet's project-level IAM bindings" echo " --delete-iam-policy-bindings Delete IAM policy bindings for atelet" echo " --delete-snapshot-bucket Delete snapshot bucket" echo " --delete-gvisor-node-pool Delete gVisor node pool" echo " --delete-cluster Delete GKE cluster" + echo " --delete-dashboards Delete the Substrate monitoring dashboards" echo " --all Run all teardown steps (reverse order of setup)" exit 1 } @@ -60,6 +66,43 @@ revoke_gke_node_permissions() { --quiet || true } +# Revoke Atelet's project-level bindings (Reverse of grant_atelet_permissions) +revoke_atelet_permissions() { + echo "Revoking atelet project-level permissions..." + local member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/ate-system/sa/atelet" + gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \ + --member="${member}" \ + --role="roles/storage.objectAdmin" \ + --condition=None \ + --quiet || true + gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \ + --member="${member}" \ + --role="roles/artifactregistry.reader" \ + --condition=None \ + --quiet || true +} + +# Delete Monitoring Dashboards (Reverse of create_monitoring_dashboards) +delete_dashboards() { + echo "Deleting Substrate monitoring dashboards..." + # Matched by display name, since setup only records names in its JSON. + local names=( + "Substrate Snapshot Size & QPS" + "Substrate Routing & E2E Latency" + "Substrate gRPC Server — latency / QPS / errors" + ) + for display_name in "${names[@]}"; do + for dashboard in $(gcloud monitoring dashboards list \ + --project="${PROJECT_ID}" \ + --filter="displayName=\"${display_name}\"" \ + --format="value(name)" 2>/dev/null); do + gcloud monitoring dashboards delete "${dashboard}" \ + --project="${PROJECT_ID}" \ + --quiet || true + done + done +} + # Delete IAM Policy Bindings for Bucket (Reverse of create_iam_policy_bindings) delete_iam_policy_bindings() { echo "Deleting IAM policy bindings for bucket..." @@ -107,13 +150,17 @@ fi while [[ "$#" -gt 0 ]]; do case $1 in --revoke-gke-node-permissions) revoke_gke_node_permissions ;; + --revoke-atelet-permissions) revoke_atelet_permissions ;; --delete-iam-policy-bindings) delete_iam_policy_bindings ;; --delete-snapshot-bucket) delete_snapshot_bucket ;; --delete-gvisor-node-pool) delete_gvisor_node_pool ;; --delete-cluster) delete_cluster ;; + --delete-dashboards) delete_dashboards ;; --all) - revoke_gke_node_permissions + delete_dashboards delete_iam_policy_bindings + revoke_atelet_permissions + revoke_gke_node_permissions delete_snapshot_bucket delete_gvisor_node_pool delete_cluster From c5827d334240e7591bd5eb98ad074379792f9b47 Mon Sep 17 00:00:00 2001 From: Aditya Shantanu Date: Wed, 2 Sep 2026 10:39:58 -0700 Subject: [PATCH 3/4] commands.md: the hack/install-demo-*.sh scripts still exist The closing paragraph claimed the per-demo hack scripts are gone, but hack/install-ate.sh still sources hack/install-demo-*.sh and generates its --deploy-demo-NAME / --delete-demo-NAME flags from them. Describe the two installers as parallel instead of declaring one dead. --- cmd/ate-setup/commands.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ate-setup/commands.md b/cmd/ate-setup/commands.md index 0506efd3ac..99d1b26ed1 100644 --- a/cmd/ate-setup/commands.md +++ b/cmd/ate-setup/commands.md @@ -173,6 +173,7 @@ The demo list is not hard-coded here — it is built from the registry in `go run ./cmd/ate-setup deploy demo --help` is authoritative for both the list and the per-demo flags. -Each demo previously had its own `hack/install-demo-*.sh`, sourced by the -installer, which registered the flags above. Those scripts are gone; the demos -now live in [`internal/demos`](internal/demos). +The demos also each have a `hack/install-demo-*.sh`, sourced by +`hack/install-ate.sh`, which registers `--deploy-demo-NAME` / +`--delete-demo-NAME` flags on that installer. ate-setup does not use those +scripts; its demos live in [`internal/demos`](internal/demos). From 0e1ea50675a3dc42a3f73ef1bfb27f8392453021 Mon Sep 17 00:00:00 2001 From: Aditya Shantanu Date: Wed, 2 Sep 2026 11:18:38 -0700 Subject: [PATCH 4/4] teardown.sh: require only the variables the selected steps use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deleting a bucket must not demand a node pool name. Each step now declares its own requirements via a require helper, still failing up front with the specific missing variable, and --all skips the separate node-pool deletion when NODE_POOL_NAME is unset — deleting the cluster removes its pools — so a caller that does not track the pool name (an installer driving this script through the environment) can still run the full teardown. --- hack/teardown.sh | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/hack/teardown.sh b/hack/teardown.sh index d472b7ccbb..831a7793b6 100755 --- a/hack/teardown.sh +++ b/hack/teardown.sh @@ -23,17 +23,23 @@ set -o errexit -o nounset -o pipefail if [ -f .ate-dev-env.sh ]; then source .ate-dev-env.sh fi -for var in PROJECT_ID PROJECT_NUMBER CLUSTER_NAME CLUSTER_LOCATION BUCKET_NAME NODE_POOL_NAME; do - if [ -z "${!var:-}" ]; then - echo "${var} is not set; export it or create .ate-dev-env.sh from the example file in hack" >&2 - exit 1 - fi -done - # No cluster-admin precheck here: every step below talks to GCP, not to the # cluster, and requiring a live kubectl context would block tearing down a # cluster that is already half-gone. +# require checks that each named variable is set, so a step fails up front +# with the variable's name rather than mid-deletion with a gcloud error. Each +# step declares only what it uses: deleting a bucket must not demand a node +# pool name. +require() { + for var in "$@"; do + if [ -z "${!var:-}" ]; then + echo "${var} is not set; export it or create .ate-dev-env.sh from the example file in hack" >&2 + exit 1 + fi + done +} + # --- Helper Functions --- function usage() { echo "Usage: $0 [options]" @@ -53,6 +59,7 @@ function usage() { # Revoke GKE Node Permissions (Reverse of grant_gke_node_permissions) revoke_gke_node_permissions() { + require PROJECT_ID PROJECT_NUMBER echo "Revoking GKE node permissions..." gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \ @@ -68,6 +75,7 @@ revoke_gke_node_permissions() { # Revoke Atelet's project-level bindings (Reverse of grant_atelet_permissions) revoke_atelet_permissions() { + require PROJECT_ID PROJECT_NUMBER echo "Revoking atelet project-level permissions..." local member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/ate-system/sa/atelet" gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \ @@ -84,6 +92,7 @@ revoke_atelet_permissions() { # Delete Monitoring Dashboards (Reverse of create_monitoring_dashboards) delete_dashboards() { + require PROJECT_ID echo "Deleting Substrate monitoring dashboards..." # Matched by display name, since setup only records names in its JSON. local names=( @@ -105,6 +114,7 @@ delete_dashboards() { # Delete IAM Policy Bindings for Bucket (Reverse of create_iam_policy_bindings) delete_iam_policy_bindings() { + require PROJECT_ID PROJECT_NUMBER BUCKET_NAME echo "Deleting IAM policy bindings for bucket..." gcloud storage buckets remove-iam-policy-binding "gs://${BUCKET_NAME}" \ --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/ate-system/sa/atelet" \ @@ -118,6 +128,7 @@ delete_iam_policy_bindings() { # Delete Snapshot Bucket (Reverse of create_snapshot_bucket) delete_snapshot_bucket() { + require PROJECT_ID BUCKET_NAME echo "Deleting snapshot bucket..." gcloud storage rm --recursive "gs://${BUCKET_NAME}/**" --project="${PROJECT_ID}" --quiet || true gcloud storage buckets delete "gs://${BUCKET_NAME}" --project="${PROJECT_ID}" --quiet || true @@ -125,6 +136,7 @@ delete_snapshot_bucket() { # Delete gVisor Node Pool (Reverse of create_gvisor_node_pool) delete_gvisor_node_pool() { + require PROJECT_ID CLUSTER_NAME CLUSTER_LOCATION NODE_POOL_NAME echo "Deleting gVisor node pool..." gcloud container node-pools delete "${NODE_POOL_NAME}" \ --cluster="${CLUSTER_NAME}" \ @@ -135,6 +147,7 @@ delete_gvisor_node_pool() { # Delete Cluster (Reverse of create_cluster) delete_cluster() { + require PROJECT_ID CLUSTER_NAME CLUSTER_LOCATION echo "Deleting GKE cluster..." gcloud container clusters delete "${CLUSTER_NAME}" \ --location="${CLUSTER_LOCATION}" \ @@ -162,7 +175,13 @@ while [[ "$#" -gt 0 ]]; do revoke_atelet_permissions revoke_gke_node_permissions delete_snapshot_bucket - delete_gvisor_node_pool + # Deleting the cluster removes its node pools, so --all does not insist + # on a pool name a caller (e.g. an installer) may not track. + if [ -n "${NODE_POOL_NAME:-}" ]; then + delete_gvisor_node_pool + else + echo "NODE_POOL_NAME not set; skipping node pool deletion (the cluster deletion removes its pools)" + fi delete_cluster ;; *) usage ;;