Arcade Adventure: Google Cloud ML APIs | GSP125

Solution for Arcade Adventure: Google Cloud ML APIs | GSP125. 1 lab: GSP125. Fast copy-paste commands for Google Cloud.

GSP125 — Speaking with a Webpage - Streaming Speech Transcripts

Estimated time: 15 minutes

# 🚀 Speaking with a Webpage - Streaming Speech Transcripts | GSP125 > ⚠️ **Disclaimer:** This is an independent, community-made walkthrough created for educational purposes, hands-on practice, and Google Cloud certification preparation. It helps learners explore streaming speech transcripts and configure the required virtual machine, software, firewall access, and web application components. Always attempt the lab yourself first and follow Google Cloud Skills Boost / Qwiklabs Terms of Service.

clear
RESET=

\033[0m'; BOLD=

\033[1m'; CYAN=

\033[1;36m'; BLUE=

\033[1;34m'; GREEN=

\033[1;32m'; YELLOW=

\033[1;33m'; RED=

\033[1;31m'; MAGENTA=

\033[1;35m'; WHITE=

\033[1;37m'; ORANGE=

\033[38;5;208m'
set +H

echo "${CYAN}${BOLD}"
echo "   ____       _     _ _            __   ___             "
echo "  / __ \     | |   (_) |          / _| / _ \            "
echo " | |  | |_ __| |__  _| |_   ___  | |_ | | | |_ __  ___  "
echo " | |  | | '__| '_ \| | __| / _ \ |  _|| | | | '_ \/ __| "
echo " | |__| | |  | |_) | | |_ | (_) || |  | |_| | |_) \__ \ "
echo "  \____/|_|  |_.__/|_|\__| \___/ |_|   \___/| .__/|___/ "
echo "                                            | |         "
echo "                                            |_|         "
echo "${RESET}"

BAR=$(printf '═%.0s' {1..60})
echo "${MAGENTA}${BOLD}╔${BAR}╗${RESET}"
printf "${MAGENTA}${BOLD}║${RESET}${ORANGE}${BOLD} %-58s ${RESET}${MAGENTA}${BOLD}║${RESET}\n" "BROUGHT TO YOU BY ORBIT OF OPS"
printf "${MAGENTA}${BOLD}║${RESET}${GREEN}${BOLD} %-58s ${RESET}${MAGENTA}${BOLD}║${RESET}\n" "STAGE 1 OF 4 : CREATE THE VM (TASK 1)"
printf "${MAGENTA}${BOLD}║${RESET}${CYAN}${BOLD} %-58s ${RESET}${MAGENTA}${BOLD}║${RESET}\n" "GSP125 | Speaking with a Webpage"
echo "${MAGENTA}${BOLD}╚${BAR}╝${RESET}"

o_step() { echo; echo "${CYAN}${BOLD}▶ $*${RESET}"; }
o_ok()   { echo "  ${GREEN}${BOLD}[ OK ]${RESET} ${GREEN}$*${RESET}"; }
o_info() { echo "  ${BLUE}${BOLD}[INFO]${RESET} ${WHITE}$*${RESET}"; }
o_warn() { echo "  ${YELLOW}${BOLD}[WARN]${RESET} ${YELLOW}$*${RESET}"; }
o_bad()  { ORBIT_FAILS=$((ORBIT_FAILS+1)); echo "  ${RED}${BOLD}[FAIL]${RESET} ${RED}$*${RESET}"; }
o_run()  { local L="$1"; shift; if "$@" >/tmp/orbit_run.log 2>&1; then o_ok "$L"; return 0; fi; if grep -qi "already exists" /tmp/orbit_run.log; then o_info "$L (already exists, kept)"; return 0; fi; o_bad "$L"; tail -n 4 /tmp/orbit_run.log | sed 's/^/        /'; return 1; }
ORBIT_FAILS=0

orbit_ask() {
  local L=$1 D=$2 i A
  ANSWER=""
  for i in 1 2 3 4 5 6; do
    if [ -n "$D" ]; then read -r -p "${WHITE}${BOLD}${L} [Enter = ${D}]: ${RESET}" A; A=${A:-$D}; else read -r -p "${WHITE}${BOLD}${L}: ${RESET}" A; fi
    A=$(echo "$A" | tr -d '[:space:]' | tr 'A-Z' 'a-z')
    [ -z "$A" ] && continue
    if ! echo "$A" | grep -qE '^[a-z]+-[a-z]+[0-9]+-[a-z]

; then o_warn "'$A' is not a zone name (example: us-east1-b)"; continue; fi
    if [ "$(gcloud compute zones list --project=$PROJECT_ID --filter="name=$A AND status=UP" --format="value(name)" 2>/dev/null)" != "$A" ]; then o_warn "'$A' is not an available zone in this project"; continue; fi
    ANSWER=$A; return 0
  done
  return 1
}

orbit_vm() {
  local CUR
  CUR=$(gcloud compute instances list --project=$PROJECT_ID --filter="name=speaking-with-a-webpage" --format="value(zone.basename())" 2>/dev/null | head -n 1)
  if [ "$CUR" = "$ZONE" ]; then o_info "speaking-with-a-webpage already exists in $ZONE, kept"; return 0; fi
  if [ -n "$CUR" ]; then o_warn "VM exists in $CUR but you entered $ZONE, recreating"; gcloud compute instances delete speaking-with-a-webpage --zone=$CUR --project=$PROJECT_ID --quiet >/dev/null 2>&1; fi
  if gcloud compute instances create speaking-with-a-webpage --zone=$ZONE --machine-type=e2-medium --image-family=debian-12 --image-project=debian-cloud --tags=http-server,https-server --scopes=cloud-platform --project=$PROJECT_ID --quiet >/tmp/orbit_run.log 2>&1; then o_ok "speaking-with-a-webpage created in $ZONE"; else o_bad "VM could not be created"; tail -n 4 /tmp/orbit_run.log | sed 's/^/        /'; return 1; fi
}

orbit_stage1() {
  PROJECT_ID=$(gcloud config get-value project 2>/dev/null); [ -z "$PROJECT_ID" ] && PROJECT_ID=$DEVSHELL_PROJECT_ID
  DEF=$(gcloud compute project-info describe --project=$PROJECT_ID --format="value(commonInstanceMetadata.items.filter(key:google-compute-default-zone).extract(value).flatten())" 2>/dev/null | head -n 1)

  o_step "Enter the zone from your lab panel"
  o_info "The lab calls it ZONE. Type it exactly as the lab panel shows it."
  orbit_ask "Zone for speaking-with-a-webpage" "$DEF" || { o_bad "No valid zone entered."; return 1; }; ZONE=$ANSWER

  o_step "Plan"
  o_info "VM speaking-with-a-webpage | zone $ZONE | e2-medium | Debian 12 | tags http-server,https-server | scope cloud-platform"
  o_warn "A VM with this name in a different zone is deleted and recreated."
  read -r -p "${WHITE}${BOLD}Type y to create the VM (anything else cancels): ${RESET}" GO
  [ "$GO" = "y" ] || { o_warn "Cancelled. Paste this block again to start over."; return 1; }

  gcloud config set compute/zone $ZONE >/dev/null 2>&1
  gcloud config set compute/region ${ZONE%-*} >/dev/null 2>&1

  o_step "Firewall rules behind 'Allow HTTP traffic' and 'Allow HTTPS traffic'"
  o_run "default-allow-http (tcp:80, tag http-server)" gcloud compute firewall-rules create default-allow-http --network=default --allow=tcp:80 --source-ranges=0.0.0.0/0 --target-tags=http-server --quiet
  o_run "default-allow-https (tcp:443, tag https-server)" gcloud compute firewall-rules create default-allow-https --network=default --allow=tcp:443 --source-ranges=0.0.0.0/0 --target-tags=https-server --quiet
  sleep 10

  o_step "Task 1: creating speaking-with-a-webpage"
  orbit_vm || return 1
  sleep 15

  o_step "Final audit"
  ORBIT_FAILS=0
  V=$(gcloud compute instances describe speaking-with-a-webpage --zone=$ZONE --project=$PROJECT_ID --format="value(status,machineType.basename())" 2>/dev/null | tr '\t' ' ')
  G=$(gcloud compute instances describe speaking-with-a-webpage --zone=$ZONE --project=$PROJECT_ID --format="value(tags.items.join(sep=','))" 2>/dev/null)
  S=$(gcloud compute instances describe speaking-with-a-webpage --zone=$ZONE --project=$PROJECT_ID --format="value(serviceAccounts[0].scopes.join(sep=','))" 2>/dev/null)
  IM=$(gcloud compute instances describe speaking-with-a-webpage --zone=$ZONE --project=$PROJECT_ID --format="value(disks[0].licenses[0].basename())" 2>/dev/null)
  EXTIP=$(gcloud compute instances describe speaking-with-a-webpage --zone=$ZONE --project=$PROJECT_ID --format="value(networkInterfaces[0].accessConfigs[0].natIP)" 2>/dev/null)
  [ "${V%% *}" = "RUNNING" ] && o_ok "Status and type: $V" || o_bad "Status and type: '${V:-nothing}'"
  [ "$G" = "http-server,https-server" ] && o_ok "Tags: $G" || o_bad "Tags are '$G' (expected http-server,https-server)"
  echo "$S" | grep -q "auth/cloud-platform" && o_ok "Access scope: full access to all Cloud APIs" || o_bad "Scopes are '$S' (expected cloud-platform)"
  echo "$IM" | grep -q "debian-12" && o_ok "Boot disk image: $IM" || o_warn "Boot disk image is '$IM' (the lab installs openjdk-17-jdk, which needs Debian 12)"
  [ -n "$EXTIP" ] && o_ok "External IP: $EXTIP (later: https://$EXTIP:8443)" || o_bad "No external IP found"
  echo
  if [ $ORBIT_FAILS -eq 0 ]; then echo "${MAGENTA}${BOLD}>> Click 'Check my progress' for 'Create a virtual machine', then run Command 2 of 4.${RESET}"; else echo "${RED}${BOLD}>> $ORBIT_FAILS check(s) failed. Fix the FAIL lines above (or send them to me).${RESET}"; fi
}
orbit_stage1
RESET=

\033[0m'; BOLD=

\033[1m'; CYAN=

\033[1;36m'; BLUE=

\033[1;34m'; GREEN=

\033[1;32m'; YELLOW=

\033[1;33m'; RED=

\033[1;31m'; MAGENTA=

\033[1;35m'; WHITE=

\033[1;37m'; ORANGE=

\033[38;5;208m'
set +H
echo "${MAGENTA}${BOLD}[ ORBIT OF OPS ]${RESET} ${ORANGE}${BOLD}Stage 2 of 4${RESET} ${WHITE}::${RESET} ${GREEN}${BOLD}Software, Repo and Firewall (Task 2)${RESET}"

o_step() { echo; echo "${CYAN}${BOLD}▶ $*${RESET}"; }
o_ok()   { echo "  ${GREEN}${BOLD}[ OK ]${RESET} ${GREEN}$*${RESET}"; }
o_info() { echo "  ${BLUE}${BOLD}[INFO]${RESET} ${WHITE}$*${RESET}"; }
o_warn() { echo "  ${YELLOW}${BOLD}[WARN]${RESET} ${YELLOW}$*${RESET}"; }
o_bad()  { echo "  ${RED}${BOLD}[FAIL]${RESET} ${RED}$*${RESET}"; }
o_run()  { local L="$1"; shift; if "$@" >/tmp/orbit_run.log 2>&1; then o_ok "$L"; return 0; fi; if grep -qi "already exists" /tmp/orbit_run.log; then o_info "$L (already exists, kept)"; return 0; fi; o_bad "$L"; tail -n 4 /tmp/orbit_run.log | sed 's/^/        /'; return 1; }

orbit_load() {
  PROJECT_ID=$(gcloud config get-value project 2>/dev/null); [ -z "$PROJECT_ID" ] && PROJECT_ID=$DEVSHELL_PROJECT_ID
  INSTANCE=speaking-with-a-webpage
  ZONE=$(gcloud compute instances list --project=$PROJECT_ID --filter="name=$INSTANCE" --format="value(zone.basename())" 2>/dev/null | head -n 1)
  EXTIP=$(gcloud compute instances describe $INSTANCE --zone=$ZONE --project=$PROJECT_ID --format="value(networkInterfaces[0].accessConfigs[0].natIP)" 2>/dev/null)
  [ -n "$ZONE" ] && [ -n "$EXTIP" ]
}

o_ssh() {
  local C=$1 T=${2:-300} i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout $T gcloud compute ssh $INSTANCE --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X --command="$C" >/tmp/orbit_ssh.log 2>&1 && return 0
    o_warn "SSH attempt $i of 4 failed, retrying"
    [ $i -eq 2 ] && gcloud compute firewall-rules create fw-allow-iap-ssh-orbit --network=default --allow=tcp:22 --source-ranges=35.235.240.0/20 --quiet >/dev/null 2>&1
    sleep 15
  done
  return 1
}

o_scp() {
  local F=$1 i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout 180 gcloud compute scp $F $INSTANCE:~ --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X >/tmp/orbit_scp.log 2>&1 && return 0
    sleep 15
  done
  return 1
}

orbit_stage2() {
  o_step "Loading the VM from Command 1"
  orbit_load || { o_bad "speaking-with-a-webpage not found. Run Command 1 first."; return 1; }
  o_info "VM $INSTANCE | zone $ZONE | external IP $EXTIP"

  o_step "Task 2: firewall rule dev-ports (tcp:8443 from 0.0.0.0/0)"
  o_run "dev-ports" gcloud compute firewall-rules create dev-ports --allow=tcp:8443 --source-ranges=0.0.0.0/0 --quiet
  P=$(gcloud compute firewall-rules describe dev-ports --format="value(allowed[0].ports.join(sep=','))" 2>/dev/null)
  R=$(gcloud compute firewall-rules describe dev-ports --format="value(sourceRanges.join(sep=','))" 2>/dev/null)
  if [ "$P" = "8443" ] && [ "$R" = "0.0.0.0/0" ]; then o_ok "dev-ports allows tcp:$P from $R"; else o_bad "dev-ports shows ports '$P' and ranges '$R'"; return 1; fi
  sleep 10

cat << 'EOF' > orbit_setup.sh
#!/bin/bash
for i in 1 2 3 4 5; do
  sudo DEBIAN_FRONTEND=noninteractive apt-get -y -qq update >/dev/null 2>&1
  sudo DEBIAN_FRONTEND=noninteractive apt-get -y -qq install git >/tmp/orbit_apt1.log 2>&1 && break
  sleep 15
done
for i in 1 2 3 4 5; do
  sudo DEBIAN_FRONTEND=noninteractive apt-get -y -qq install maven openjdk-17-jdk >/tmp/orbit_apt2.log 2>&1 && break
  sleep 15
done
cd ~ && [ -d speaking-with-a-webpage ] || git clone -q https://github.com/googlecodelabs/speaking-with-a-webpage.git
echo "VER_GIT $(git --version 2>&1)"
echo "VER_JAVA $(java -version 2>&1 | head -n 1)"
echo "VER_MVN $(mvn -version 2>&1 | head -n 1)"
echo "VER_REPO $(ls ~/speaking-with-a-webpage 2>/dev/null | tr '\n' ' ')"
EOF

  o_step "Installing git, Maven and OpenJDK 17 on the VM and cloning the repo (about 2-4 min)"
  o_scp orbit_setup.sh || { o_bad "Could not copy orbit_setup.sh to the VM"; tail -n 3 /tmp/orbit_scp.log | sed 's/^/        /'; return 1; }
  o_ssh "bash orbit_setup.sh" 900 || { o_bad "Setup did not complete"; tail -n 3 /tmp/orbit_ssh.log | sed 's/^/        /'; return 1; }
  grep "^VER_" /tmp/orbit_ssh.log | while read -r L; do o_info "$L"; done

  grep -q '^VER_GIT git version' /tmp/orbit_ssh.log && o_ok "git installed" || { o_bad "git missing"; return 1; }
  grep -q '^VER_JAVA.*"17' /tmp/orbit_ssh.log && o_ok "Java 17 is the active JDK" || { o_bad "Java 17 is not the active JDK"; return 1; }
  grep -q '^VER_MVN Apache Maven' /tmp/orbit_ssh.log && o_ok "Maven installed" || { o_bad "Maven missing"; return 1; }
  if grep -q '^VER_REPO.*01-hello-https' /tmp/orbit_ssh.log && grep -q '^VER_REPO.*02-webaudio' /tmp/orbit_ssh.log; then o_ok "Repo cloned (01-hello-https and 02-webaudio present)"; else o_bad "Repo folders missing"; return 1; fi
  sleep 10
  echo
  echo "${MAGENTA}${BOLD}>> Click 'Check my progress' for 'Install necessary software and create a firewall rule', then run Command 3 of 4.${RESET}"
}
orbit_stage2
RESET=

\033[0m'; BOLD=

\033[1m'; CYAN=

\033[1;36m'; BLUE=

\033[1;34m'; GREEN=

\033[1;32m'; YELLOW=

\033[1;33m'; RED=

\033[1;31m'; MAGENTA=

\033[1;35m'; WHITE=

\033[1;37m'; ORANGE=

\033[38;5;208m'
set +H
echo "${MAGENTA}${BOLD}[ ORBIT OF OPS ]${RESET} ${ORANGE}${BOLD}Stage 3 of 4${RESET} ${WHITE}::${RESET} ${GREEN}${BOLD}Run 01-hello-https (Task 3)${RESET}"

o_step() { echo; echo "${CYAN}${BOLD}▶ $*${RESET}"; }
o_ok()   { echo "  ${GREEN}${BOLD}[ OK ]${RESET} ${GREEN}$*${RESET}"; }
o_info() { echo "  ${BLUE}${BOLD}[INFO]${RESET} ${WHITE}$*${RESET}"; }
o_warn() { echo "  ${YELLOW}${BOLD}[WARN]${RESET} ${YELLOW}$*${RESET}"; }
o_bad()  { echo "  ${RED}${BOLD}[FAIL]${RESET} ${RED}$*${RESET}"; }

orbit_load() {
  PROJECT_ID=$(gcloud config get-value project 2>/dev/null); [ -z "$PROJECT_ID" ] && PROJECT_ID=$DEVSHELL_PROJECT_ID
  INSTANCE=speaking-with-a-webpage
  ZONE=$(gcloud compute instances list --project=$PROJECT_ID --filter="name=$INSTANCE" --format="value(zone.basename())" 2>/dev/null | head -n 1)
  EXTIP=$(gcloud compute instances describe $INSTANCE --zone=$ZONE --project=$PROJECT_ID --format="value(networkInterfaces[0].accessConfigs[0].natIP)" 2>/dev/null)
  [ -n "$ZONE" ] && [ -n "$EXTIP" ]
}

o_ssh() {
  local C=$1 T=${2:-300} i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout $T gcloud compute ssh $INSTANCE --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X --command="$C" >/tmp/orbit_ssh.log 2>&1 && return 0
    o_warn "SSH attempt $i of 4 failed, retrying"
    [ $i -eq 2 ] && gcloud compute firewall-rules create fw-allow-iap-ssh-orbit --network=default --allow=tcp:22 --source-ranges=35.235.240.0/20 --quiet >/dev/null 2>&1
    sleep 15
  done
  return 1
}

o_scp() {
  local F=$1 i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout 180 gcloud compute scp $F $INSTANCE:~ --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X >/tmp/orbit_scp.log 2>&1 && return 0
    sleep 15
  done
  return 1
}

orbit_serve() {
  local D=$1 i C F UP=0 L
  o_step "Copying the helper scripts to the VM"
  o_scp "orbit_stop.sh orbit_start.sh orbit_probe.sh" || { o_bad "Could not copy the helper scripts"; return 1; }
  o_ok "Helper scripts copied"

  o_step "Stopping any servlet that is already running"
  o_ssh "bash orbit_stop.sh" 120 || { o_bad "Could not run orbit_stop.sh"; return 1; }
  grep -q ORBIT_PORT_FREE /tmp/orbit_ssh.log && o_ok "Port 8443 is free" || { o_bad "Port 8443 is still busy"; return 1; }

  o_step "Starting $D (first build downloads dependencies, about 3-8 min)"
  o_ssh "bash orbit_start.sh $D" 120 || { o_bad "Could not start the servlet"; return 1; }
  grep -q ORBIT_STARTED /tmp/orbit_ssh.log && o_ok "mvn clean jetty:run started in the background" || { o_bad "Start script reported a problem"; tail -n 3 /tmp/orbit_ssh.log | sed 's/^/        /'; return 1; }

  o_step "Waiting for https://localhost:8443 on the VM (max about 15 min)"
  for i in $(seq 1 60); do
    sleep 10
    o_ssh "bash orbit_probe.sh $D" 120 || { o_warn "poll $i failed, retrying"; continue; }
    L=$(grep -o 'ORBIT_PROBE.*' /tmp/orbit_ssh.log | tail -n 1)
    C=$(echo "$L" | sed -n 's/.*code=\([0-9]*\).*/\1/p'); F=$(echo "$L" | sed -n 's/.*fail=\([0-9]*\).*/\1/p')
    if [ "${F:-0}" -ge 1 ]; then o_bad "Maven reported BUILD FAILURE"; o_ssh "tail -n 15 ~/jetty_$D.log; true" 120; tail -n 15 /tmp/orbit_ssh.log | sed 's/^/        /'; return 1; fi
    if [ -n "$C" ] && [ "$C" != "000" ]; then UP=1; o_ok "Servlet answers on 8443 (HTTP $C)"; break; fi
    o_info "[$i of 60] still building or starting (HTTP ${C:-?})"
    sleep 5
  done
  [ $UP -eq 1 ] || { o_bad "Servlet did not come up in time. Re-run this block, or send me ~/jetty_$D.log"; return 1; }

  o_step "Checking the public path (dev-ports firewall rule)"
  C=000
  for i in 1 2 3 4 5 6; do C=$(curl -k -s -m 10 -o /dev/null -w "%{http_code}" https://$EXTIP:8443/); [ "$C" != "000" ] && break; o_info "[$i of 6] no answer from https://$EXTIP:8443 yet"; sleep 10; done
  if [ "$C" != "000" ]; then o_ok "https://$EXTIP:8443 answers from outside (HTTP $C)"; else o_bad "https://$EXTIP:8443 is not reachable. Check the dev-ports rule from Command 2."; return 1; fi
}

orbit_stage3() {
  o_step "Loading the VM"
  orbit_load || { o_bad "speaking-with-a-webpage not found. Run Command 1 first."; return 1; }
  o_info "VM $INSTANCE | zone $ZONE | external IP $EXTIP"

cat << 'EOF' > orbit_stop.sh
#!/bin/bash
pkill -f '[j]etty:run'
sleep 3
ss -ltn 2>/dev/null | grep -q ':8443 ' && echo ORBIT_PORT_BUSY || echo ORBIT_PORT_FREE
EOF
cat << 'EOF' > orbit_start.sh
#!/bin/bash
cd ~/speaking-with-a-webpage/$1 || { echo ORBIT_NO_DIR; exit 1; }
: > ~/jetty_$1.log
setsid nohup mvn -B clean jetty:run > ~/jetty_$1.log 2>&1 < /dev/null &
sleep 2
echo ORBIT_STARTED
EOF
cat << 'EOF' > orbit_probe.sh
#!/bin/bash
CODE=$(curl -k -s -m 10 -o /dev/null -w '%{http_code}' https://localhost:8443/)
FAIL=$(grep -c 'BUILD FAILURE' ~/jetty_$1.log 2>/dev/null)
echo "ORBIT_PROBE code=$CODE fail=${FAIL:-0}"
EOF

  orbit_serve 01-hello-https || return 1
  sleep 5
  echo
  echo "${MAGENTA}${BOLD}>> The servlet is running. Click 'Check my progress' for 'Run the sample solution (hello-https)', then run Command 4 of 4.${RESET}"
}
orbit_stage3
RESET=

\033[0m'; BOLD=

\033[1m'; CYAN=

\033[1;36m'; BLUE=

\033[1;34m'; GREEN=

\033[1;32m'; YELLOW=

\033[1;33m'; RED=

\033[1;31m'; MAGENTA=

\033[1;35m'; WHITE=

\033[1;37m'; ORANGE=

\033[38;5;208m'
set +H
echo "${MAGENTA}${BOLD}[ ORBIT OF OPS ]${RESET} ${ORANGE}${BOLD}Stage 4 of 4${RESET} ${WHITE}::${RESET} ${GREEN}${BOLD}Run 02-webaudio (Task 4)${RESET}"

o_step() { echo; echo "${CYAN}${BOLD}▶ $*${RESET}"; }
o_ok()   { echo "  ${GREEN}${BOLD}[ OK ]${RESET} ${GREEN}$*${RESET}"; }
o_info() { echo "  ${BLUE}${BOLD}[INFO]${RESET} ${WHITE}$*${RESET}"; }
o_warn() { echo "  ${YELLOW}${BOLD}[WARN]${RESET} ${YELLOW}$*${RESET}"; }
o_bad()  { echo "  ${RED}${BOLD}[FAIL]${RESET} ${RED}$*${RESET}"; }

orbit_load() {
  PROJECT_ID=$(gcloud config get-value project 2>/dev/null); [ -z "$PROJECT_ID" ] && PROJECT_ID=$DEVSHELL_PROJECT_ID
  INSTANCE=speaking-with-a-webpage
  ZONE=$(gcloud compute instances list --project=$PROJECT_ID --filter="name=$INSTANCE" --format="value(zone.basename())" 2>/dev/null | head -n 1)
  EXTIP=$(gcloud compute instances describe $INSTANCE --zone=$ZONE --project=$PROJECT_ID --format="value(networkInterfaces[0].accessConfigs[0].natIP)" 2>/dev/null)
  [ -n "$ZONE" ] && [ -n "$EXTIP" ]
}

o_ssh() {
  local C=$1 T=${2:-300} i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout $T gcloud compute ssh $INSTANCE --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X --command="$C" >/tmp/orbit_ssh.log 2>&1 && return 0
    o_warn "SSH attempt $i of 4 failed, retrying"
    [ $i -eq 2 ] && gcloud compute firewall-rules create fw-allow-iap-ssh-orbit --network=default --allow=tcp:22 --source-ranges=35.235.240.0/20 --quiet >/dev/null 2>&1
    sleep 15
  done
  return 1
}

o_scp() {
  local F=$1 i X=""
  for i in 1 2 3 4; do
    [ $i -ge 3 ] && X="--tunnel-through-iap"
    timeout 180 gcloud compute scp $F $INSTANCE:~ --zone=$ZONE --project=$PROJECT_ID --quiet --strict-host-key-checking=no $X >/tmp/orbit_scp.log 2>&1 && return 0
    sleep 15
  done
  return 1
}

orbit_serve() {
  local D=$1 i C F UP=0 L
  o_step "Copying the helper scripts to the VM"
  o_scp "orbit_stop.sh orbit_start.sh orbit_probe.sh" || { o_bad "Could not copy the helper scripts"; return 1; }
  o_ok "Helper scripts copied"

  o_step "Stopping the 01-hello-https servlet"
  o_ssh "bash orbit_stop.sh" 120 || { o_bad "Could not run orbit_stop.sh"; return 1; }
  grep -q ORBIT_PORT_FREE /tmp/orbit_ssh.log && o_ok "Port 8443 is free" || { o_bad "Port 8443 is still busy"; return 1; }

  o_step "Starting $D (about 1-3 min, dependencies are already downloaded)"
  o_ssh "bash orbit_start.sh $D" 120 || { o_bad "Could not start the servlet"; return 1; }
  grep -q ORBIT_STARTED /tmp/orbit_ssh.log && o_ok "mvn clean jetty:run started in the background" || { o_bad "Start script reported a problem"; tail -n 3 /tmp/orbit_ssh.log | sed 's/^/        /'; return 1; }

  o_step "Waiting for https://localhost:8443 on the VM (max about 15 min)"
  for i in $(seq 1 60); do
    sleep 10
    o_ssh "bash orbit_probe.sh $D" 120 || { o_warn "poll $i failed, retrying"; continue; }
    L=$(grep -o 'ORBIT_PROBE.*' /tmp/orbit_ssh.log | tail -n 1)
    C=$(echo "$L" | sed -n 's/.*code=\([0-9]*\).*/\1/p'); F=$(echo "$L" | sed -n 's/.*fail=\([0-9]*\).*/\1/p')
    if [ "${F:-0}" -ge 1 ]; then o_bad "Maven reported BUILD FAILURE"; o_ssh "tail -n 15 ~/jetty_$D.log; true" 120; tail -n 15 /tmp/orbit_ssh.log | sed 's/^/        /'; return 1; fi
    if [ -n "$C" ] && [ "$C" != "000" ]; then UP=1; o_ok "Servlet answers on 8443 (HTTP $C)"; break; fi
    o_info "[$i of 60] still building or starting (HTTP ${C:-?})"
    sleep 5
  done
  [ $UP -eq 1 ] || { o_bad "Servlet did not come up in time. Re-run this block, or send me ~/jetty_$D.log"; return 1; }

  o_step "Checking the public path (dev-ports firewall rule)"
  C=000
  for i in 1 2 3 4 5 6; do C=$(curl -k -s -m 10 -o /dev/null -w "%{http_code}" https://$EXTIP:8443/); [ "$C" != "000" ] && break; o_info "[$i of 6] no answer from https://$EXTIP:8443 yet"; sleep 10; done
  if [ "$C" != "000" ]; then o_ok "https://$EXTIP:8443 answers from outside (HTTP $C)"; else o_bad "https://$EXTIP:8443 is not reachable. Check the dev-ports rule from Command 2."; return 1; fi
}

orbit_stage4() {
  o_step "Loading the VM"
  orbit_load || { o_bad "speaking-with-a-webpage not found. Run Command 1 first."; return 1; }
  o_info "VM $INSTANCE | zone $ZONE | external IP $EXTIP"

cat << 'EOF' > orbit_stop.sh
#!/bin/bash
pkill -f '[j]etty:run'
sleep 3
ss -ltn 2>/dev/null | grep -q ':8443 ' && echo ORBIT_PORT_BUSY || echo ORBIT_PORT_FREE
EOF
cat << 'EOF' > orbit_start.sh
#!/bin/bash
cd ~/speaking-with-a-webpage/$1 || { echo ORBIT_NO_DIR; exit 1; }
: > ~/jetty_$1.log
setsid nohup mvn -B clean jetty:run > ~/jetty_$1.log 2>&1 < /dev/null &
sleep 2
echo ORBIT_STARTED
EOF
cat << 'EOF' > orbit_probe.sh
#!/bin/bash
CODE=$(curl -k -s -m 10 -o /dev/null -w '%{http_code}' https://localhost:8443/)
FAIL=$(grep -c 'BUILD FAILURE' ~/jetty_$1.log 2>/dev/null)
echo "ORBIT_PROBE code=$CODE fail=${FAIL:-0}"
EOF

  orbit_serve 02-webaudio || return 1
  sleep 5
  echo
  echo "${MAGENTA}${BOLD}>> The servlet is running. Click 'Check my progress' for 'Run the sample solution to capture audio on webpage'.${RESET}"
  echo "${ORANGE}${BOLD}Congratulations. GSP125 is complete - brought to you by Orbit of Ops.${RESET}"
}
orbit_stage4

# Subscribe to Orbit of Ops https://www.youtube.com/@orbitofops/videos