#!/usr/bin/env bash
set -euo pipefail

if [[ $# != 1 ]]; then
  printf 'Usage: bash run-lab.sh KUBE_CONTEXT\nUse a disposable Kubernetes cluster.\n' >&2
  exit 2
fi

context=$1
namespace=brotli-rollout-lab
old_image=node:24.22.0-alpine
fixed_image=node:24.22.1-alpine
k=(kubectl --context "$context" -n "$namespace")

print_command() {
  printf '$'
  printf ' %q' "$@"
  printf '\n'
}

run() {
  print_command "$@"
  "$@"
}

run kubectl --context "$context" version
run kubectl --context "$context" create namespace "$namespace"

deployment() {
  cat <<EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: $1
spec:
  replicas: $2
  selector:
    matchLabels:
      app: api
      track: $3
  template:
    metadata:
      labels:
        app: api
        track: $3
    spec:
      containers:
        - name: api
          image: $old_image
          command: ["node", "-e"]
          args:
            - |
              const http = require('node:http');
              const os = require('node:os');
              http.createServer((req, res) => {
                if (req.url !== '/health') {
                  res.writeHead(404).end();
                  return;
                }
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({pod: os.hostname(), node: process.version}));
              }).listen(3000, '0.0.0.0');
          ports:
            - containerPort: 3000
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            periodSeconds: 1
EOF
}

{
  deployment api 5 main
  deployment api-legacy 1 legacy
  cat <<'EOF'
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 3000
      targetPort: 3000
EOF
} | "${k[@]}" apply -f -

run "${k[@]}" rollout status deployment/api --timeout=180s
run "${k[@]}" rollout status deployment/api-legacy --timeout=180s
run "${k[@]}" set image deployment/api "api=$fixed_image"
run "${k[@]}" rollout status deployment/api --timeout=180s

endpoint_format='{{range .items}}{{range .endpoints}}{{if .conditions.ready}}{{.targetRef.name}}{{"\t"}}{{index .addresses 0}}{{"\n"}}{{end}}{{end}}{{end}}'
pod_columns='POD:.metadata.name,TRACK:.metadata.labels.track,IMAGE:.spec.containers[0].image,IMAGE_ID:.status.containerStatuses[0].imageID'
probe_js='const [ip, expectedPod] = process.argv.slice(1); fetch("http://" + ip + ":3000/health", {signal: AbortSignal.timeout(5000)}).then(async r => { const body = await r.json(); if (!r.ok || body.pod !== expectedPod) throw Error("unexpected endpoint response"); console.log(body.pod + " " + r.status + " " + body.node); }).catch(e => { console.error(e); process.exitCode = 1; });'

observe() {
  local probe pod ip ready row
  local -a endpoints
  local -a list_ready=("${k[@]}" get endpointslice -l kubernetes.io/service-name=api -o "go-template=$endpoint_format")
  run "${k[@]}" get pods -l app=api -o "custom-columns=$pod_columns" --sort-by=.metadata.name || return 1
  print_command "${list_ready[@]}"
  ready=$("${list_ready[@]}") || return 1
  mapfile -t endpoints < <(printf '%s\n' "$ready" | sort)
  [[ ${#endpoints[@]} == 6 ]] || { printf 'Expected six ready endpoints\n' >&2; return 1; }
  probe=${endpoints[0]%%$'\t'*}
  printf 'READY_ENDPOINT HTTP NODE\n'
  for row in "${endpoints[@]}"; do
    IFS=$'\t' read -r pod ip <<<"$row"
    run "${k[@]}" exec "$probe" -- node -e "$probe_js" "$ip" "$pod" || return 1
  done
}

printf '\n=== MAIN ROLLOUT COMPLETE, LEGACY ENDPOINT STILL SELECTED ===\n'
before=$(observe)
printf '%s\n' "$before"
[[ $(grep -cE '^api-legacy-.* 200 v24\.22\.0$' <<<"$before") == 1 ]]
[[ $(grep -cE '^api-.* 200 v24\.22\.1$' <<<"$before") == 5 ]]

printf '\n=== UPDATE THE LEGACY DEPLOYMENT ===\n'
run "${k[@]}" set image deployment/api-legacy "api=$fixed_image"
run "${k[@]}" rollout status deployment/api-legacy --timeout=180s
after=$(observe)
printf '%s\n' "$after"
[[ $(grep -cE '^api-.* 200 v24\.22\.1$' <<<"$after") == 6 ]]
[[ $(grep -cE '^api-legacy-.* 200 v24\.22\.1$' <<<"$after") == 1 ]]
printf 'Verified six ready Service endpoints with HTTP 200 and Node.js v24.22.1.\n'
