From 0643f543e4e5dcf96444b80074a3355a6205f252 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:10:13 +0700 Subject: [PATCH 1/6] fix(gsp182): align lab defaults and verify Locust rollout --- labs/GSP182/lab.sh | 69 +++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index f2390c6..f9b4081 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -3,6 +3,9 @@ set -euo pipefail export CLOUDSDK_CORE_DISABLE_PROMPTS=1 +# Lab GSP182 defaults. Respect an explicitly configured region/zone. +# App Engine region is immutable once the app has been created. + BLACK_TEXT=$'\033[0;90m' RED_TEXT=$'\033[0;91m' GREEN_TEXT=$'\033[0;92m' @@ -25,7 +28,7 @@ NO_COLOR=$'\033[0m' RESET_FORMAT=$'\033[0m' REVERSE_TEXT=$'\033[7m' -clear +if [[ -t 1 ]]; then clear; fi echo "${CYAN_TEXT}${BOLD_TEXT}==================================================================${RESET_FORMAT}" echo "${CYAN_TEXT}${BOLD_TEXT} ePlus.DEV - INITIATING EXECUTION... ${RESET_FORMAT}" @@ -55,24 +58,22 @@ if [ -z "${PROJECT_ID}" ]; then fi if [ -z "${PROJECT_ID}" ]; then - err "Không lấy được PROJECT_ID" + err "Unable to determine PROJECT_ID" exit 1 fi -REGION="$(gcloud config get-value compute/region 2>/dev/null || true)" -ZONE="$(gcloud config get-value compute/zone 2>/dev/null || true)" - -if [ -z "${REGION}" ]; then - REGION="$(gcloud compute project-info describe --format='value(commonInstanceMetadata.items[google-compute-default-region])' 2>/dev/null || true)" -fi - -if [ -z "${ZONE}" ]; then - ZONE="$(gcloud compute project-info describe --format='value(commonInstanceMetadata.items[google-compute-default-zone])' 2>/dev/null || true)" +REGION="${REGION:-$(gcloud config get-value compute/region 2>/dev/null || true)}" +ZONE="${ZONE:-$(gcloud config get-value compute/zone 2>/dev/null || true)}" +[[ "${REGION}" == "(unset)" ]] && REGION="" +[[ "${ZONE}" == "(unset)" ]] && ZONE="" +if [[ -z "${REGION}" && -n "${ZONE}" ]]; then REGION="${ZONE%-*}"; fi +if [[ -z "${REGION}" ]]; then REGION="us-east1"; fi +if [[ -z "${ZONE}" ]]; then ZONE="${REGION}-c"; fi +if [[ "${ZONE%-*}" != "${REGION}" ]]; then + err "Zone ${ZONE} is not in region ${REGION}; set REGION and ZONE consistently." + exit 1 fi -[ -z "${REGION}" ] && REGION="us-east4" -[ -z "${ZONE}" ] && ZONE="us-east4-b" - CLUSTER="gke-load-test" TARGET="${PROJECT_ID}.appspot.com" BASE_DIR="$HOME/distributed-load-testing-using-kubernetes" @@ -94,6 +95,7 @@ step "Step 2: Enabling required APIs" gcloud services enable \ cloudbuild.googleapis.com \ containerregistry.googleapis.com \ + artifactregistry.googleapis.com \ appengine.googleapis.com \ container.googleapis.com >/dev/null log "Required APIs enabled" @@ -103,30 +105,31 @@ cd "$HOME" if [ -d "${BASE_DIR}" ]; then warn "Directory already exists, skipping download" else - gsutil -m cp -r gs://spls/gsp182/distributed-load-testing-using-kubernetes . + gcloud storage cp -r gs://spls/gsp182/distributed-load-testing-using-kubernetes . fi +[[ -f "${BASE_DIR}/sample-webapp/app.yaml" && -d "${BASE_DIR}/kubernetes-config" && -d "${BASE_DIR}/docker-image" ]] || { err "Sample resources are incomplete"; exit 1; } log "Resources ready" step "Step 4: Configuring Sample Web Application" cd "${BASE_DIR}/sample-webapp" -sed -i 's/python37/python312/g' app.yaml || true -grep -q "python312" app.yaml && log "app.yaml updated to python312" || warn "python312 may already be set" +sed -i 's/python37/python312/g' app.yaml +grep -Eq "^runtime: *python312" app.yaml || { err "Expected Python 3.12 runtime in app.yaml"; exit 1; } +log "app.yaml uses Python 3.12" cd "${BASE_DIR}" step "Step 5: Building Locust Docker Image" gcloud builds submit --tag "gcr.io/${PROJECT_ID}/locust-tasks:latest" docker-image/. -if gcloud container images list --repository="gcr.io/${PROJECT_ID}" 2>/dev/null | grep -q "locust-tasks"; then - log "Docker image built and pushed successfully" -else - err "Không thấy image locust-tasks trong gcr.io/${PROJECT_ID}" - exit 1 -fi +log "Cloud Build completed successfully" step "Step 6: Creating App Engine App if needed" if gcloud app describe >/dev/null 2>&1; then - warn "App Engine app already exists" + warn "App Engine app already exists; keeping its immutable location" else + # App Engine does not support every GKE region. GSP182 specifies us-east1. + if [[ "${REGION}" != "us-east1" ]]; then + warn "App Engine may not support ${REGION}; GSP182 uses us-east1." + fi gcloud app create --region="${REGION}" log "App Engine app created" fi @@ -142,7 +145,7 @@ else gcloud container clusters create "${CLUSTER}" \ --zone "${ZONE}" \ --num-nodes=5 \ - --machine-type=e2-standard-4 + --machine-type=e2-medium log "GKE cluster created" fi @@ -161,7 +164,8 @@ log "Kubernetes config files updated" step "Step 11: Deploying Locust Master" kubectl apply -f kubernetes-config/locust-master-controller.yaml kubectl apply -f kubernetes-config/locust-master-service.yaml -log "Locust master deployed" +kubectl rollout status deployment/locust-master --timeout=10m +log "Locust master ready" step "Step 12: Checking Locust Master Service" kubectl get svc locust-master @@ -169,21 +173,28 @@ log "Locust master service listed" step "Step 13: Deploying Locust Workers" kubectl apply -f kubernetes-config/locust-worker-controller.yaml -log "Initial workers deployed" +kubectl rollout status deployment/locust-worker --timeout=10m +log "Initial workers ready" step "Step 14: Scaling Workers" kubectl scale deployment/locust-worker --replicas=20 -log "Scaled to 20 workers" +kubectl rollout status deployment/locust-worker --timeout=15m +READY="$(kubectl get deployment locust-worker -o jsonpath='{.status.readyReplicas}')" +[[ "${READY:-0}" -eq 20 ]] || { err "Expected 20 ready workers; found ${READY:-0}"; exit 1; } +log "All 20 workers ready" +kubectl get pods -l app=locust-worker +kubectl get svc locust-master echo echo "${CYAN_TEXT}${BOLD_TEXT}=======================================================${RESET_FORMAT}" -echo "${CYAN_TEXT}${BOLD_TEXT} LAB COMPLETED SUCCESSFULLY! ${RESET_FORMAT}" +echo "${CYAN_TEXT}${BOLD_TEXT} GSP182 DEPLOYMENT VERIFIED! ${RESET_FORMAT}" echo "${CYAN_TEXT}${BOLD_TEXT}=======================================================${RESET_FORMAT}" echo echo "${GREEN_TEXT}${BOLD_TEXT}Project : ${PROJECT_ID}${RESET_FORMAT}" echo "${GREEN_TEXT}${BOLD_TEXT}Region : ${REGION}${RESET_FORMAT}" echo "${GREEN_TEXT}${BOLD_TEXT}Zone : ${ZONE}${RESET_FORMAT}" echo "${GREEN_TEXT}${BOLD_TEXT}Target : ${TARGET}${RESET_FORMAT}" +echo "${GREEN_TEXT}${BOLD_TEXT}Locust UI: http://$(kubectl get svc locust-master -o jsonpath='{.status.loadBalancer.ingress[0].ip}'):8089${RESET_FORMAT}" echo echo "${RED_TEXT}${BOLD_TEXT}${UNDERLINE_TEXT}https://eplus.dev${RESET_FORMAT}" echo "${GREEN_TEXT}${BOLD_TEXT}Don't forget to Like, Share and Subscribe for more Videos${RESET_FORMAT}" \ No newline at end of file From 02e5ca8e3501bd6d0a1ff08c1a67c9f08952e2c1 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:18:12 +0700 Subject: [PATCH 2/6] fix(gsp182): add local Docker fallback for Cloud Build IAM failure --- labs/GSP182/lab.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index f9b4081..b116c67 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -118,9 +118,22 @@ log "app.yaml uses Python 3.12" cd "${BASE_DIR}" step "Step 5: Building Locust Docker Image" -gcloud builds submit --tag "gcr.io/${PROJECT_ID}/locust-tasks:latest" docker-image/. - -log "Cloud Build completed successfully" +IMAGE="gcr.io/${PROJECT_ID}/locust-tasks:latest" +if gcloud builds submit --tag "${IMAGE}" docker-image/.; then + log "Cloud Build completed successfully" +else + warn "Cloud Build failed (possibly missing iam.serviceAccounts.actAs on the build service account)." + warn "Trying Docker build/push directly from Cloud Shell; this requires registry write access." + if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + err "Docker daemon unavailable. Contact the lab administrator to fix Cloud Build IAM." + exit 1 + fi + gcloud auth configure-docker gcr.io --quiet + docker build -t "${IMAGE}" docker-image/. + docker push "${IMAGE}" + log "Docker image pushed without Cloud Build" + warn "The lab checker may specifically require a Cloud Build record; verify Task 2 progress." +fi step "Step 6: Creating App Engine App if needed" if gcloud app describe >/dev/null 2>&1; then From 47ff3506b5645f17cd2d85afe7e59063ae94e5b0 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:24:49 +0700 Subject: [PATCH 3/6] fix(gsp182): diagnose restricted GKE resource locations --- labs/GSP182/lab.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index b116c67..eb5c65a 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -152,13 +152,20 @@ gcloud app deploy "${BASE_DIR}/sample-webapp/app.yaml" --quiet log "Web application deployed" step "Step 8: Creating GKE Cluster" +echo "Checking effective resource-location policy (read-only; may be unavailable to lab users)..." +gcloud org-policies describe gcp.resourceLocations --project="${PROJECT_ID}" --effective --format="yaml(spec.rules)" 2>/dev/null || warn "Cannot read the effective location policy; use an authorized lab region/zone." if gcloud container clusters describe "${CLUSTER}" --zone "${ZONE}" >/dev/null 2>&1; then warn "Cluster ${CLUSTER} already exists" else - gcloud container clusters create "${CLUSTER}" \ + if ! gcloud container clusters create "${CLUSTER}" \ --zone "${ZONE}" \ --num-nodes=5 \ - --machine-type=e2-medium + --machine-type=e2-medium; then + err "GKE creation failed in ${ZONE}. If constraints/gcp.resourceLocations appears, the lab project forbids this location." + warn "Inspect the effective org policy above and rerun with REGION= ZONE= bash lab-gsp182.sh" + warn "Do not modify organization policies or guess arbitrary zones on a temporary lab project." + exit 1 + fi log "GKE cluster created" fi From de33274a710742e3f66bf4760f597dc17c95380b Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:27:06 +0700 Subject: [PATCH 4/6] fix(gsp182): use Qwiklabs-compatible us-central1 fallback --- labs/GSP182/lab.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index eb5c65a..267e875 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -3,7 +3,7 @@ set -euo pipefail export CLOUDSDK_CORE_DISABLE_PROMPTS=1 -# Lab GSP182 defaults. Respect an explicitly configured region/zone. +# GSP182 fallback for restricted Qwiklabs projects. Respect explicitly configured region/zone. # App Engine region is immutable once the app has been created. BLACK_TEXT=$'\033[0;90m' @@ -67,7 +67,7 @@ ZONE="${ZONE:-$(gcloud config get-value compute/zone 2>/dev/null || true)}" [[ "${REGION}" == "(unset)" ]] && REGION="" [[ "${ZONE}" == "(unset)" ]] && ZONE="" if [[ -z "${REGION}" && -n "${ZONE}" ]]; then REGION="${ZONE%-*}"; fi -if [[ -z "${REGION}" ]]; then REGION="us-east1"; fi +if [[ -z "${REGION}" ]]; then REGION="us-central1"; fi if [[ -z "${ZONE}" ]]; then ZONE="${REGION}-c"; fi if [[ "${ZONE%-*}" != "${REGION}" ]]; then err "Zone ${ZONE} is not in region ${REGION}; set REGION and ZONE consistently." @@ -139,10 +139,7 @@ step "Step 6: Creating App Engine App if needed" if gcloud app describe >/dev/null 2>&1; then warn "App Engine app already exists; keeping its immutable location" else - # App Engine does not support every GKE region. GSP182 specifies us-east1. - if [[ "${REGION}" != "us-east1" ]]; then - warn "App Engine may not support ${REGION}; GSP182 uses us-east1." - fi + # An existing App Engine application has an immutable region; otherwise use the configured supported region. gcloud app create --region="${REGION}" log "App Engine app created" fi From c501f45cbcf69ee5902c65d734316e1aecb27b84 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:31:17 +0700 Subject: [PATCH 5/6] fix(gsp182): explain GKE node service account IAM blocker --- labs/GSP182/lab.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index 267e875..9617d1e 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -158,9 +158,12 @@ else --zone "${ZONE}" \ --num-nodes=5 \ --machine-type=e2-medium; then - err "GKE creation failed in ${ZONE}. If constraints/gcp.resourceLocations appears, the lab project forbids this location." - warn "Inspect the effective org policy above and rerun with REGION= ZONE= bash lab-gsp182.sh" - warn "Do not modify organization policies or guess arbitrary zones on a temporary lab project." + err "GKE cluster creation failed in ${ZONE}." + warn "Common lab blockers: constraints/gcp.resourceLocations or missing iam.serviceAccounts.actAs on the node service account." + warn "If the error says 'does not have access to service account', a project owner/lab administrator must grant roles/iam.serviceAccountUser on the approved node service account." + warn "Read-only inspection: gcloud iam service-accounts list --project=${PROJECT_ID}" + warn "If the location is denied, inspect the effective location policy; rerun with an authorized REGION and ZONE." + warn "Do not change lab IAM/org policies, randomly probe zones, or run kubectl until cluster creation succeeds." exit 1 fi log "GKE cluster created" From c951e8b4ae7130ee56d2443be8ba2539c6c03b41 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 4 Nov 2026 14:32:53 +0700 Subject: [PATCH 6/6] fix(gsp182): support explicitly approved GKE node service account --- labs/GSP182/lab.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/labs/GSP182/lab.sh b/labs/GSP182/lab.sh index 9617d1e..b27be10 100644 --- a/labs/GSP182/lab.sh +++ b/labs/GSP182/lab.sh @@ -154,10 +154,18 @@ gcloud org-policies describe gcp.resourceLocations --project="${PROJECT_ID}" --e if gcloud container clusters describe "${CLUSTER}" --zone "${ZONE}" >/dev/null 2>&1; then warn "Cluster ${CLUSTER} already exists" else + # Optional: set GKE_NODE_SA to an existing lab-approved node service account. + # The active user still requires iam.serviceAccounts.actAs on that identity. + NODE_SA_ARGS=() + if [[ -n "${GKE_NODE_SA:-}" ]]; then + NODE_SA_ARGS=(--service-account="${GKE_NODE_SA}") + log "Using approved node service account: ${GKE_NODE_SA}" + fi if ! gcloud container clusters create "${CLUSTER}" \ --zone "${ZONE}" \ --num-nodes=5 \ - --machine-type=e2-medium; then + --machine-type=e2-medium \ + "${NODE_SA_ARGS[@]}"; then err "GKE cluster creation failed in ${ZONE}." warn "Common lab blockers: constraints/gcp.resourceLocations or missing iam.serviceAccounts.actAs on the node service account." warn "If the error says 'does not have access to service account', a project owner/lab administrator must grant roles/iam.serviceAccountUser on the approved node service account."