#!/usr/bin/env bash # Teslamate Bootstrap - Auto-configure PostgreSQL with ESO set -euo pipefail echo "=== Teslamate Bootstrap - Fully Automated ===" # Change to the apps directory for relative paths cd "$(dirname "$0")/../apps" || exit 1 NS=teslamate CLUSTER=pg-teslamate # 1) Ensure namespace kubectl get ns "${NS}" >/dev/null 2>&1 || kubectl create ns "${NS}" # 2) Apply CNPG cluster (auto-generates DB password) echo "Applying Teslamate PostgreSQL cluster..." kubectl apply -f teslamate/cnpg-cluster.yaml # 3) Wait for CNPG cluster echo "Waiting for CNPG cluster ${CLUSTER}..." kubectl -n "${NS}" wait --for=condition=Ready "cluster/${CLUSTER}" --timeout=300s # 3.5) Set up PostgreSQL extensions and grant superuser echo "Setting up PostgreSQL extensions for Teslamate..." PRIMARY_POD=$(kubectl get pod -n "${NS}" -l cnpg.io/cluster="${CLUSTER}",role=primary -o jsonpath='{.items[0].metadata.name}') if [ -n "$PRIMARY_POD" ]; then echo "Granting superuser to teslamate user..." kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -c "ALTER USER teslamate WITH SUPERUSER;" 2>/dev/null || echo " (user may already have superuser)" echo "Creating required PostgreSQL extensions..." kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -d teslamate -c "CREATE EXTENSION IF NOT EXISTS cube;" 2>/dev/null || echo " (cube extension may already exist)" kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -d teslamate -c "CREATE EXTENSION IF NOT EXISTS earthdistance;" 2>/dev/null || echo " (earthdistance extension may already exist)" echo "✅ PostgreSQL extensions configured" else echo "⚠️ Warning: Could not find primary pod, skipping extension setup" fi # 4) Apply ESO secrets (auto-rotate DB passwords) echo "Applying Teslamate ESO secrets (auto-generates passwords)..." kubectl apply -f teslamate/cnpg-secrets.yaml # 5) Wait for password to be generated echo "Waiting for ESO to generate database password..." for i in {1..30}; do if kubectl -n "${NS}" get secret pg-teslamate-app >/dev/null 2>&1; then echo "✅ Database password generated" break fi echo " waiting for secret... (attempt $i/30)" sleep 2 done # 6) Initialize encryption key in Vault if it doesn't exist echo "Initializing encryption key in Vault..." # Get Vault root token from secret VAULT_ROOT_TOKEN=$(kubectl -n vault get secret vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' 2>/dev/null | base64 -d || echo "") if [ -z "$VAULT_ROOT_TOKEN" ]; then echo "⚠️ Warning: Could not retrieve Vault root token from secret vault-init-keys" echo "Skipping encryption key creation. You'll need to create it manually." echo "" echo "To create the encryption key manually:" echo " ENCRYPTION_KEY=\$(openssl rand -base64 32)" echo " kubectl exec -n vault vault-0 -- vault kv put secret/teslamate/config encryption_key=\"\${ENCRYPTION_KEY}\"" echo "" # Continue anyway - the ExternalSecret will show an error if the key doesn't exist else # Check if the secret already exists in Vault if kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv get secret/teslamate/config >/dev/null 2>&1; then echo "✅ Encryption key already exists in Vault" else echo "Generating new encryption key..." ENCRYPTION_KEY=$(openssl rand -base64 32) # Write to Vault using root token if kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv put secret/teslamate/config encryption_key="${ENCRYPTION_KEY}"; then echo "✅ Encryption key stored in Vault" else echo "❌ Failed to write to Vault" echo "Please create the encryption key manually:" echo " kubectl exec -n vault vault-0 -- env VAULT_TOKEN=\"${VAULT_ROOT_TOKEN}\" vault kv put secret/teslamate/config encryption_key=\"${ENCRYPTION_KEY}\"" exit 1 fi fi fi # 7) Apply Teslamate config ExternalSecret echo "Applying Teslamate config ExternalSecret..." kubectl apply -f teslamate/external-secret.yaml # 8) Wait for teslamate-config-secret to be created echo "Waiting for teslamate-config-secret to be synced from Vault..." SECRET_SYNCED=false for i in {1..30}; do if kubectl -n "${NS}" get secret teslamate-config-secret >/dev/null 2>&1; then echo "✅ teslamate-config-secret synced successfully" SECRET_SYNCED=true break fi echo " waiting for secret... (attempt $i/30)" sleep 2 done # 9) If ESO failed to sync, create secret manually as fallback if [ "$SECRET_SYNCED" = "false" ]; then echo "⚠️ ExternalSecret failed to sync (ClusterSecretStore issue)" echo "Creating teslamate-config-secret manually from Vault..." if [ -n "$VAULT_ROOT_TOKEN" ]; then ENCRYPTION_KEY=$(kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv get -field=encryption_key secret/teslamate/config 2>/dev/null || echo "") if [ -n "$ENCRYPTION_KEY" ]; then kubectl create secret generic teslamate-config-secret -n "${NS}" \ --from-literal=encryption-key="${ENCRYPTION_KEY}" \ --dry-run=client -o yaml | kubectl apply -f - echo "✅ Created teslamate-config-secret manually" else echo "❌ Failed to retrieve encryption key from Vault" exit 1 fi else echo "❌ Cannot create secret manually - no Vault token" exit 1 fi fi echo "✅ Teslamate bootstrap complete - fully automated!"