prole/etc/init_cnpg_gke.sh
chrisfu 620bdd25eb feat: GKE production deployment — CNPG + GCS backups + Google Workspace SSO
Add complete GKE deployment stack:
- deploy/gcp/gke/: namespace, CloudNativePG cluster (premium-rwo, 100Gi),
  GCS barman-cloud backup with Workload Identity SA, ArgoCD OIDC ConfigMap,
  GitLab OmniAuth secret template, knoe-auth Google OIDC secret template
- etc/init_cnpg_gke.sh: provision GCS buckets, GCP SA, WI binding, CNPG operator
- etc/init_argocd.sh: apply argocd-oidc-cm.yaml when PLATFORM_DOMAIN/FRONTDOOR_HOST set
- etc/init_gitlab.sh: inject OmniAuth openid_connect block when FRONTDOOR_HOST set
- prole-auth-deployment.yaml: add GOOGLE_OIDC_ENABLED env vars (all optional)
- supabase/helm/prole-supabase/values-gke.yaml: GoTrue Google OAuth overlay
- knoe/core/prod_config.py: add backupProvider, workloadIdentitySA, googleClientId/Secret/HostedDomain

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 10:41:55 -07:00

139 lines
6.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# init_cnpg_gke.sh
# Provision CloudNativePG on GKE with GCS backup via Workload Identity.
#
# Usage:
# ./etc/init_cnpg_gke.sh [--project PROJECT_ID] [--region REGION] [--cluster CLUSTER_NAME]
#
# Env vars (override flags):
# GCP_PROJECT_ID, GCP_REGION, GKE_CLUSTER, GCS_BACKUP_BUCKET,
# CNPG_NAMESPACE, ARTIFACT_REGISTRY, CLOUDSDK_AUTH_ACCESS_TOKEN
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
GKE_MANIFEST_DIR="$REPO_ROOT/deploy/gcp/gke"
# ── Defaults ────────────────────────────────────────────────────────────────
GCP_PROJECT_ID="${GCP_PROJECT_ID:-}"
GCP_REGION="${GCP_REGION:-us-central1}"
GKE_CLUSTER="${GKE_CLUSTER:-knoe-dev-0}"
GCS_BACKUP_BUCKET="${GCS_BACKUP_BUCKET:-knoe-0-backups}"
GCS_WAL_BUCKET="${GCS_WAL_BUCKET:-knoe-0-wal}"
CNPG_NAMESPACE="${CNPG_NAMESPACE:-ecosystem-0}"
CNPG_BACKUP_SA="${CNPG_BACKUP_SA:-cnpg-backup}"
ARTIFACT_REGISTRY="${ARTIFACT_REGISTRY:-}"
# ── Argument parsing ─────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--project) GCP_PROJECT_ID="$2"; shift 2 ;;
--region) GCP_REGION="$2"; shift 2 ;;
--cluster) GKE_CLUSTER="$2"; shift 2 ;;
--bucket) GCS_BACKUP_BUCKET="$2"; shift 2 ;;
--namespace) CNPG_NAMESPACE="$2"; shift 2 ;;
--registry) ARTIFACT_REGISTRY="$2"; shift 2 ;;
*) echo "Unknown argument: $1" >&2; exit 1 ;;
esac
done
[[ -z "$GCP_PROJECT_ID" ]] && {
# Try to read from gcp.cfg
GCP_CFG="$REPO_ROOT/conf/prod/gcp.cfg"
if [[ -f "$GCP_CFG" ]]; then
GCP_PROJECT_ID=$(grep '^project_id' "$GCP_CFG" | sed 's/.*=\s*"\?\([^"]*\)"\?.*/\1/' | tr -d '[:space:]')
fi
}
[[ -z "$GCP_PROJECT_ID" ]] && { echo "ERROR: GCP_PROJECT_ID not set and conf/prod/gcp.cfg not found." >&2; exit 1; }
GCP_SA_EMAIL="${CNPG_BACKUP_SA}@${GCP_PROJECT_ID}.iam.gserviceaccount.com"
log() { echo "[init_cnpg_gke] $*"; }
# ── Tool checks ──────────────────────────────────────────────────────────────
for tool in gcloud kubectl gsutil; do
command -v "$tool" >/dev/null || { echo "ERROR: $tool not found in PATH." >&2; exit 1; }
done
# ── 1. Acquire kubeconfig ────────────────────────────────────────────────────
log "Fetching kubeconfig for $GKE_CLUSTER in $GCP_REGION ..."
gcloud container clusters get-credentials "$GKE_CLUSTER" \
--project "$GCP_PROJECT_ID" \
--region "$GCP_REGION"
# ── 2. Create GCS buckets ────────────────────────────────────────────────────
for bucket in "$GCS_BACKUP_BUCKET" "$GCS_WAL_BUCKET"; do
if gsutil ls -b "gs://$bucket" >/dev/null 2>&1; then
log "Bucket gs://$bucket already exists."
else
log "Creating bucket gs://$bucket ..."
gsutil mb -p "$GCP_PROJECT_ID" -l "$GCP_REGION" "gs://$bucket"
gsutil versioning set on "gs://$bucket"
gsutil lifecycle set /dev/stdin "gs://$bucket" <<EOF
{"rule":[{"action":{"type":"Delete"},"condition":{"age":30}}]}
EOF
fi
done
# ── 3. Create GCP service account ────────────────────────────────────────────
if gcloud iam service-accounts describe "$GCP_SA_EMAIL" --project "$GCP_PROJECT_ID" >/dev/null 2>&1; then
log "Service account $GCP_SA_EMAIL already exists."
else
log "Creating service account $GCP_SA_EMAIL ..."
gcloud iam service-accounts create "$CNPG_BACKUP_SA" \
--display-name="CNPG GCS Backup" \
--project="$GCP_PROJECT_ID"
fi
# ── 4. Grant storage access ───────────────────────────────────────────────────
log "Granting objectAdmin on backup buckets to $GCP_SA_EMAIL ..."
for bucket in "$GCS_BACKUP_BUCKET" "$GCS_WAL_BUCKET"; do
gsutil iam ch \
"serviceAccount:${GCP_SA_EMAIL}:objectAdmin" \
"gs://$bucket"
done
# ── 5. Bind Workload Identity ─────────────────────────────────────────────────
log "Binding Workload Identity ..."
gcloud iam service-accounts add-iam-policy-binding "$GCP_SA_EMAIL" \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:${GCP_PROJECT_ID}.svc.id.goog[${CNPG_NAMESPACE}/cnpg-backup-sa]" \
--project="$GCP_PROJECT_ID"
# ── 6. Apply namespace ────────────────────────────────────────────────────────
log "Applying namespace $CNPG_NAMESPACE ..."
kubectl apply -f "$GKE_MANIFEST_DIR/namespace.yaml"
# ── 7. Install CNPG operator (if not present) ─────────────────────────────────
if ! kubectl get crd clusters.postgresql.cnpg.io >/dev/null 2>&1; then
log "Installing CloudNativePG operator ..."
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.24.0.yaml
log "Waiting for CNPG operator to be ready ..."
kubectl rollout status deployment/cnpg-controller-manager -n cnpg-system --timeout=120s
else
log "CNPG operator already installed."
fi
# ── 8. Apply ServiceAccount + annotate with WI ───────────────────────────────
log "Applying CNPG backup ServiceAccount with Workload Identity annotation ..."
GCP_PROJECT_ID="$GCP_PROJECT_ID" envsubst < "$GKE_MANIFEST_DIR/knoe-db-backup-gcs.yaml" \
| kubectl apply -f -
kubectl annotate serviceaccount cnpg-backup-sa \
-n "$CNPG_NAMESPACE" \
"iam.gke.io/gcp-service-account=${GCP_SA_EMAIL}" \
--overwrite
# ── 9. Apply CNPG cluster ────────────────────────────────────────────────────
log "Applying knoe-db CNPG cluster ..."
ARTIFACT_REGISTRY="${ARTIFACT_REGISTRY}" envsubst < "$GKE_MANIFEST_DIR/knoe-db.yaml" \
| kubectl apply -f -
log ""
log "Done. Monitor with:"
log " kubectl -n $CNPG_NAMESPACE get cluster knoe-db"
log " kubectl -n $CNPG_NAMESPACE get pods -l cnpg.io/cluster=knoe-db"
log " kubectl -n $CNPG_NAMESPACE get scheduledbackup"