local-up.py is a stdlib-only Python rewrite of local-up.sh + local-seed-secrets.sh. Key improvements: - Correctly parses limactl list --json NDJSON output (json.load() choked on NDJSON, causing spurious VM creation attempts) - Handles all Lima VM states: none, Running, Stopped, Broken, etc. - Inlines seed secrets (no separate local-seed-secrets.sh subprocess) - Partial runs: --seed, --apply, --restart flags - Consistent idempotency: every step checks state before acting - Adds people-backend/celery to restart list; find to PG users list local-up.sh patched: yq in prereqs, NDJSON-safe VM detection, --server-side for Linkerd apply, people in restart list, Mail URL.
192 lines
8.1 KiB
Bash
Executable File
192 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Seed all secrets for the local dev stack.
|
|
# - Initializes OpenBao (if needed) and stores root token + unseal key
|
|
# - Sets postgres user passwords
|
|
# - Creates K8s secrets consumed by each service
|
|
# - Stores all secrets in OpenBao as source of truth
|
|
#
|
|
# Idempotent: safe to run multiple times.
|
|
set -euo pipefail
|
|
|
|
CTX="--context=sunbeam"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Deterministic local-dev passwords (simple, memorable, not for production)
|
|
DB_PASSWORD="localdev"
|
|
S3_ACCESS_KEY="minioadmin"
|
|
S3_SECRET_KEY="minioadmin"
|
|
HYDRA_SYSTEM_SECRET="local-hydra-system-secret-at-least-16"
|
|
HYDRA_COOKIE_SECRET="local-hydra-cookie-secret-at-least-16"
|
|
HYDRA_PAIRWISE_SALT="local-hydra-pairwise-salt-value-1"
|
|
LIVEKIT_API_KEY="devkey"
|
|
LIVEKIT_API_SECRET="secret-placeholder"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper
|
|
# ---------------------------------------------------------------------------
|
|
ensure_ns() {
|
|
kubectl $CTX create namespace "$1" --dry-run=client -o yaml | kubectl $CTX apply -f - 2>/dev/null
|
|
}
|
|
|
|
create_secret() {
|
|
local ns="$1"; shift
|
|
local name="$1"; shift
|
|
# remaining args are --from-literal=key=value
|
|
kubectl $CTX create secret generic "$name" -n "$ns" "$@" \
|
|
--dry-run=client -o yaml | kubectl $CTX apply -f -
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Wait for postgres to be ready
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> Waiting for postgres cluster..."
|
|
for i in $(seq 1 60); do
|
|
PHASE=$(kubectl $CTX -n data get cluster postgres -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
|
|
if [[ "$PHASE" == "Cluster in healthy state" ]]; then
|
|
echo " Postgres is ready."
|
|
break
|
|
fi
|
|
if [[ $i -eq 60 ]]; then
|
|
echo "WARN: Postgres not ready after 5 min, continuing anyway..."
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Set postgres user passwords
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> Setting postgres user passwords..."
|
|
PG_POD=$(kubectl $CTX -n data get pods -l cnpg.io/cluster=postgres,role=primary -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
|
if [[ -n "$PG_POD" ]]; then
|
|
for user in kratos hydra gitea hive docs meet drive messages conversations people find; do
|
|
kubectl $CTX -n data exec "$PG_POD" -c postgres -- \
|
|
psql -U postgres -c "ALTER USER $user WITH PASSWORD '$DB_PASSWORD';" 2>/dev/null || true
|
|
done
|
|
echo " Done."
|
|
else
|
|
echo "WARN: No postgres primary pod found, skipping password setup."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Create K8s secrets for each service
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> Creating K8s secrets..."
|
|
|
|
# Ory namespace
|
|
ensure_ns ory
|
|
# Secret name must match chart release name (secret.enabled: false means chart uses release name)
|
|
create_secret ory hydra \
|
|
--from-literal=dsn="postgresql://hydra:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hydra_db?sslmode=disable" \
|
|
--from-literal=secretsSystem="$HYDRA_SYSTEM_SECRET" \
|
|
--from-literal=secretsCookie="$HYDRA_COOKIE_SECRET" \
|
|
--from-literal=pairwise-salt="$HYDRA_PAIRWISE_SALT"
|
|
|
|
# Kratos chart (secret.enabled: true, nameOverride: kratos-secrets) creates kratos-secrets
|
|
# from Helm values — DSN is in kratos-values.yaml, random secrets generated by chart.
|
|
# This create is a no-op placeholder; chart apply overwrites with Helm-generated values.
|
|
|
|
# Devtools namespace
|
|
ensure_ns devtools
|
|
create_secret devtools gitea-db-credentials \
|
|
--from-literal=password="$DB_PASSWORD"
|
|
|
|
create_secret devtools gitea-s3-credentials \
|
|
--from-literal=access-key="$S3_ACCESS_KEY" \
|
|
--from-literal=secret-key="$S3_SECRET_KEY"
|
|
|
|
# Storage namespace
|
|
ensure_ns storage
|
|
create_secret storage seaweedfs-s3-credentials \
|
|
--from-literal=S3_ACCESS_KEY="$S3_ACCESS_KEY" \
|
|
--from-literal=S3_SECRET_KEY="$S3_SECRET_KEY"
|
|
|
|
# La Suite namespace
|
|
ensure_ns lasuite
|
|
create_secret lasuite seaweedfs-s3-credentials \
|
|
--from-literal=S3_ACCESS_KEY="$S3_ACCESS_KEY" \
|
|
--from-literal=S3_SECRET_KEY="$S3_SECRET_KEY"
|
|
|
|
create_secret lasuite hive-db-url \
|
|
--from-literal=url="postgresql://hive:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hive_db"
|
|
|
|
create_secret lasuite hive-oidc \
|
|
--from-literal=client-id="hive-local" \
|
|
--from-literal=client-secret="hive-local-secret"
|
|
|
|
# People (desk)
|
|
create_secret lasuite people-db-credentials \
|
|
--from-literal=password="$DB_PASSWORD"
|
|
|
|
create_secret lasuite people-django-secret \
|
|
--from-literal=DJANGO_SECRET_KEY="local-dev-people-django-secret-key-not-for-production"
|
|
|
|
# Media namespace
|
|
ensure_ns media
|
|
|
|
echo " Done."
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Initialize and unseal OpenBao (if deployed)
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> Checking OpenBao..."
|
|
OB_POD=$(kubectl $CTX -n data get pods -l app.kubernetes.io/name=openbao,component=server -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
|
if [[ -z "$OB_POD" ]]; then
|
|
echo " OpenBao pod not found, skipping."
|
|
else
|
|
# Wait for pod to be running (not necessarily ready — it won't be ready until unsealed)
|
|
kubectl $CTX -n data wait pod "$OB_POD" --for=jsonpath='{.status.phase}'=Running --timeout=120s 2>/dev/null || true
|
|
|
|
# Check if initialized
|
|
INIT_STATUS=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao status -format=json 2>/dev/null | grep '"initialized"' | grep -c 'true' || echo "0")
|
|
|
|
if [[ "$INIT_STATUS" != "1" ]]; then
|
|
echo "==> Initializing OpenBao..."
|
|
INIT_OUTPUT=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao operator init -key-shares=1 -key-threshold=1 -format=json 2>/dev/null)
|
|
UNSEAL_KEY=$(echo "$INIT_OUTPUT" | jq -r '.unseal_keys_b64[0]')
|
|
ROOT_TOKEN=$(echo "$INIT_OUTPUT" | jq -r '.root_token')
|
|
|
|
# Store keys in K8s secret
|
|
create_secret data openbao-keys \
|
|
--from-literal=key="$UNSEAL_KEY" \
|
|
--from-literal=root-token="$ROOT_TOKEN"
|
|
echo " Initialized. Keys stored in secret/openbao-keys."
|
|
else
|
|
echo " Already initialized."
|
|
# Read unseal key from existing secret
|
|
UNSEAL_KEY=$(kubectl $CTX -n data get secret openbao-keys -o jsonpath='{.data.key}' 2>/dev/null | base64 -d || echo "")
|
|
ROOT_TOKEN=$(kubectl $CTX -n data get secret openbao-keys -o jsonpath='{.data.root-token}' 2>/dev/null | base64 -d || echo "")
|
|
fi
|
|
|
|
# Unseal if sealed
|
|
SEALED=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao status -format=json 2>/dev/null | grep '"sealed"' | grep -c 'true' || echo "0")
|
|
if [[ "$SEALED" == "1" && -n "$UNSEAL_KEY" ]]; then
|
|
echo "==> Unsealing OpenBao..."
|
|
kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao operator unseal "$UNSEAL_KEY"
|
|
echo " Unsealed."
|
|
fi
|
|
|
|
# Seed secrets into OpenBao
|
|
if [[ -n "$ROOT_TOKEN" ]]; then
|
|
echo "==> Seeding secrets into OpenBao..."
|
|
kubectl $CTX -n data exec "$OB_POD" -c openbao -- sh -c "
|
|
export BAO_ADDR=http://127.0.0.1:8200
|
|
export BAO_TOKEN='$ROOT_TOKEN'
|
|
|
|
bao secrets enable -path=secret -version=2 kv 2>/dev/null || true
|
|
|
|
bao kv put secret/postgres password='$DB_PASSWORD'
|
|
bao kv put secret/hydra db-password='$DB_PASSWORD' system-secret='$HYDRA_SYSTEM_SECRET' cookie-secret='$HYDRA_COOKIE_SECRET' pairwise-salt='$HYDRA_PAIRWISE_SALT'
|
|
bao kv put secret/kratos db-password='$DB_PASSWORD'
|
|
bao kv put secret/gitea db-password='$DB_PASSWORD' s3-access-key='$S3_ACCESS_KEY' s3-secret-key='$S3_SECRET_KEY'
|
|
bao kv put secret/seaweedfs access-key='$S3_ACCESS_KEY' secret-key='$S3_SECRET_KEY'
|
|
bao kv put secret/hive db-url='postgresql://hive:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hive_db' oidc-client-id='hive-local' oidc-client-secret='hive-local-secret'
|
|
bao kv put secret/people db-password='$DB_PASSWORD' django-secret-key='local-dev-people-django-secret-key-not-for-production'
|
|
bao kv put secret/livekit api-key='$LIVEKIT_API_KEY' api-secret='$LIVEKIT_API_SECRET'
|
|
" 2>/dev/null
|
|
echo " Done."
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "==> All secrets seeded."
|