prod deployment

This commit is contained in:
2026-04-01 18:34:08 -04:00
parent ca44e68f64
commit eab581f8cb
62 changed files with 1922 additions and 286 deletions

View File

@@ -23,6 +23,7 @@ usage() {
echo " relay-config - ZMQ relay configuration"
echo " ingestor-config - CCXT ingestor configuration"
echo " flink-config - Flink job configuration"
echo " gateway-config - Gateway configuration (prod only; uses op inject)"
echo ""
echo "Examples:"
echo " $0 # Update all dev configs"
@@ -49,16 +50,18 @@ if [ ! -d "$CONFIG_DIR" ]; then
exit 1
fi
# Get kubectl context
# Set kubectl command and warn for prod
if [[ "$ENV" == "prod" ]]; then
CONTEXT=$(kubectl config current-context)
KUBECTL="kubectl --context=prod"
echo -e "${YELLOW}⚠️ WARNING: Updating PRODUCTION configs!${NC}"
echo -e "${YELLOW}Current kubectl context: $CONTEXT${NC}"
echo -e "${YELLOW}kubectl context: prod${NC}"
read -p "Are you sure you want to continue? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
echo "Aborted."
exit 0
fi
else
KUBECTL="kubectl"
fi
apply_config() {
@@ -71,9 +74,15 @@ apply_config() {
fi
echo -e "${GREEN}→${NC} Creating/updating ConfigMap $config_name..."
kubectl create configmap "$config_name" \
--from-file=config.yaml="$config_file" \
--dry-run=client -o yaml | kubectl apply -f -
if [[ "$ENV" == "prod" && "$config_name" == "gateway-config" ]]; then
# gateway-config contains op:// references — resolve via op inject
op inject -i "$config_file" | $KUBECTL apply -f -
else
$KUBECTL create configmap "$config_name" \
--from-file=config.yaml="$config_file" \
--dry-run=client -o yaml | $KUBECTL apply -f -
fi
echo -e "${GREEN}✓${NC} $config_name updated"
# Optionally restart pods that use this config
@@ -88,11 +97,14 @@ apply_config() {
flink-config)
restart_pods="deployment/flink-jobmanager deployment/flink-taskmanager"
;;
gateway-config)
restart_pods="deployment/gateway"
;;
esac
if [ -n "$restart_pods" ]; then
echo -e "${YELLOW} Restarting pods...${NC}"
kubectl rollout restart $restart_pods 2>/dev/null || echo -e "${YELLOW} (No pods found to restart)${NC}"
$KUBECTL rollout restart $restart_pods 2>/dev/null || echo -e "${YELLOW} (No pods found to restart)${NC}"
fi
}
@@ -105,11 +117,20 @@ else
echo -e "${GREEN}Updating all $ENV configs...${NC}"
echo ""
CONFIGS=(
"relay-config"
"ingestor-config"
"flink-config"
)
if [[ "$ENV" == "prod" ]]; then
CONFIGS=(
"relay-config"
"ingestor-config"
"flink-config"
"gateway-config"
)
else
CONFIGS=(
"relay-config"
"ingestor-config"
"flink-config"
)
fi
FAILED=0
for config in "${CONFIGS[@]}"; do

View File

@@ -28,6 +28,15 @@ if [ "$1" == "dev" ]; then
DEV=1
fi
if [ "$1" == "prod" ]; then
shift
ENV=prod
KUBECTL="kubectl --context=prod"
else
ENV=dev
KUBECTL="kubectl"
fi
if [ "$PROJECT" == "dev" ]; then
DEV=1
# NO_CACHE=--no-cache
@@ -183,7 +192,28 @@ echo "$TAG" >&2
if [ "$DEPLOY" == "1" ]; then
docker push $REMOTE/ai-$PROJECT:$TAG
YAML=$(sed "s#image: dexorder/ai-$PROJECT*#image: $REMOTE/ai-$PROJECT:$TAG#" deploy/k8s/$KUBERNETES.yaml)
echo "$YAML" | kubectl apply -f - || echo "$YAML" "\nkubectl apply failed" && exit 1
echo deployed $KUBERNETES.yaml $REMOTE/ai-$PROJECT:$TAG
docker push $REMOTE/ai-$PROJECT:latest
if [ "$ENV" == "prod" ]; then
case "$PROJECT" in
flink)
$KUBECTL set image deployment/flink-jobmanager flink-jobmanager=$REMOTE/ai-flink:$TAG
$KUBECTL set image deployment/flink-taskmanager flink-taskmanager=$REMOTE/ai-flink:$TAG
;;
web)
$KUBECTL set image deployment/ai-web ai-web=$REMOTE/ai-web:$TAG
;;
sandbox|lifecycle-sidecar)
echo "Image pushed. New sandboxes will use $REMOTE/ai-$PROJECT:$TAG"
;;
*)
$KUBECTL set image deployment/$PROJECT $PROJECT=$REMOTE/ai-$PROJECT:$TAG
;;
esac
echo "deployed $PROJECT $REMOTE/ai-$PROJECT:$TAG"
else
YAML=$(sed "s#image: dexorder/ai-$PROJECT*#image: $REMOTE/ai-$PROJECT:$TAG#" deploy/k8s/$KUBERNETES.yaml)
echo "$YAML" | kubectl apply -f - || { echo "$YAML"; echo "kubectl apply failed"; exit 1; }
echo deployed $KUBERNETES.yaml $REMOTE/ai-$PROJECT:$TAG
fi
fi

63
bin/dev
View File

@@ -249,7 +249,7 @@ EOF
echo -e "${GREEN}→${NC} Applying Kubernetes manifests..."
kubectl apply -k .
# Apply sandbox-namespace secrets (must be after kustomize creates the dexorder-sandboxes namespace)
# Apply sandbox-namespace secrets (must be after kustomize creates the sandbox namespace)
echo -e "${GREEN}→${NC} Applying sandbox secrets..."
if [ -f "$ROOT_DIR/deploy/k8s/dev/secrets/sandbox-secrets.yaml" ]; then
kubectl apply -f "$ROOT_DIR/deploy/k8s/dev/secrets/sandbox-secrets.yaml"
@@ -274,53 +274,8 @@ EOF
deployment/flink-taskmanager \
2>/dev/null || echo -e "${YELLOW}(Some deployments not ready yet)${NC}"
# Initialize gateway database schema
echo -e "${BLUE}Initializing gateway database schema...${NC}"
echo -e "${GREEN}→${NC} Waiting for postgres..."
kubectl wait --for=condition=ready --timeout=120s pod -l app=postgres 2>/dev/null || {
echo -e "${YELLOW}⚠️ Postgres not ready yet${NC}"
}
pg_pod=$(kubectl get pods -l app=postgres -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ -n "$pg_pod" ]; then
# Wait for postgres to actually be ready to accept connections
echo -e "${GREEN}→${NC} Verifying postgres is ready to accept connections..."
for i in {1..30}; do
if kubectl exec "$pg_pod" -- psql -U postgres -d iceberg -c "SELECT 1;" > /dev/null 2>&1; then
echo -e "${GREEN}✓ Postgres ready${NC}"
break
fi
if [ $i -eq 30 ]; then
echo -e "${RED}✗ Postgres not ready after 30 seconds${NC}"
exit 1
fi
sleep 1
done
table_count=$(kubectl exec "$pg_pod" -- psql -U postgres -d iceberg -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user';" 2>/dev/null | tr -d ' ')
if [ "$table_count" = "1" ]; then
echo -e "${GREEN}✓ Gateway schema already exists${NC}"
else
echo -e "${GREEN}→${NC} Applying gateway schema..."
if kubectl exec -i "$pg_pod" -- psql -U postgres -d iceberg < "$ROOT_DIR/gateway/schema.sql" > /dev/null 2>&1; then
# Verify schema was actually created
sleep 1
table_count=$(kubectl exec "$pg_pod" -- psql -U postgres -d iceberg -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user';" 2>/dev/null | tr -d ' ')
if [ "$table_count" = "1" ]; then
echo -e "${GREEN}✓ Gateway schema initialized${NC}"
else
echo -e "${RED}✗ Failed to verify schema creation${NC}"
exit 1
fi
else
echo -e "${RED}✗ Failed to initialize gateway schema${NC}"
exit 1
fi
fi
# Create dev user (refactored into reusable function)
create_dev_user
fi
# Initialize schema and dev user
"$SCRIPT_DIR/init" dev
echo ""
echo -e "${GREEN}✓ Dev environment ready!${NC}"
@@ -555,7 +510,7 @@ deep_restart() {
kubectl delete pod -l app=iceberg-catalog 2>/dev/null || true
# Remove all sandbox deployments and services to free quota
echo -e "${GREEN}→${NC} Removing all sandbox deployments and services..."
kubectl delete deployments,services --all -n dexorder-sandboxes 2>/dev/null || true
kubectl delete deployments,services --all -n sandbox 2>/dev/null || true
;;
*)
echo -e "${RED}Error: Unknown service '$service'${NC}"
@@ -699,9 +654,9 @@ case "$COMMAND" in
kubectl wait --for=delete pod -l app=qdrant --timeout=60s 2>/dev/null || true
# Now delete PVCs
delete_pvcs all
# Delete dexorder-sandboxes namespace
echo -e "${GREEN}→${NC} Deleting dexorder-sandboxes namespace..."
kubectl delete namespace dexorder-sandboxes 2>/dev/null || true
# Delete sandbox namespace
echo -e "${GREEN}→${NC} Deleting sandbox namespace..."
kubectl delete namespace sandbox 2>/dev/null || true
minikube stop
echo -e "${GREEN}✓ Minikube stopped and PVCs deleted${NC}"
echo -e "${YELLOW}Tip: Use 'bin/dev stop --keep-data' to preserve PVCs${NC}"
@@ -779,8 +734,8 @@ case "$COMMAND" in
# Handle sandbox separately
if [ "$sandbox_requested" == "1" ]; then
echo -e "${GREEN}→${NC} Deleting user container deployments in dexorder-sandboxes namespace..."
kubectl delete deployments --all -n dexorder-sandboxes 2>/dev/null || true
echo -e "${GREEN}→${NC} Deleting user container deployments in sandbox namespace..."
kubectl delete deployments --all -n sandbox 2>/dev/null || true
echo -e "${GREEN}✓ User containers will be recreated by gateway on next login${NC}"
fi
fi

194
bin/init Executable file
View File

@@ -0,0 +1,194 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
usage() {
echo "Usage: $0 [dev|prod]"
echo ""
echo "Initialize database schema and create admin user."
echo ""
echo " dev - Initialize dev environment (minikube, default namespace)"
echo " prod - Initialize prod environment (requires confirmation)"
exit 1
}
ENV="${1:-dev}"
if [[ "$ENV" != "dev" && "$ENV" != "prod" ]]; then
echo -e "${RED}Error: Environment must be 'dev' or 'prod'${NC}"
usage
fi
if [[ "$ENV" == "prod" ]]; then
KUBECTL="kubectl --context=prod"
BASE_URL="https://dexorder.ai"
MCP_URL="https://dexorder.ai/mcp"
echo -e "${YELLOW}⚠️ WARNING: Initializing PRODUCTION environment!${NC}"
echo -e "${YELLOW}kubectl context: prod${NC}"
read -p "Are you sure you want to continue? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
echo "Aborted."
exit 0
fi
else
KUBECTL="kubectl"
BASE_URL="http://dexorder.local"
MCP_URL="http://localhost:8080/mcp"
fi
# ---------- Schema Initialization ----------
echo ""
echo -e "${BLUE}=== Schema Initialization ===${NC}"
echo ""
echo -e "${BLUE}Waiting for postgres pod...${NC}"
$KUBECTL wait --for=condition=ready --timeout=180s pod -l app=postgres 2>/dev/null || {
echo -e "${RED}Postgres not ready after 180s${NC}"
exit 1
}
PG_POD=$($KUBECTL get pods -l app=postgres -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ -z "$PG_POD" ]; then
echo -e "${RED}No postgres pod found${NC}"
exit 1
fi
echo -e "${GREEN}Found postgres pod: $PG_POD${NC}"
# Wait for postgres to accept connections
echo -e "${BLUE}Waiting for postgres to accept connections...${NC}"
for i in $(seq 1 30); do
if $KUBECTL exec "$PG_POD" -- psql -U postgres -d iceberg -c "SELECT 1;" > /dev/null 2>&1; then
echo -e "${GREEN}Postgres ready${NC}"
break
fi
if [[ $i -eq 30 ]]; then
echo -e "${RED}Postgres not accepting connections after 60s${NC}"
exit 1
fi
sleep 2
done
# Check if schema exists
TABLE_COUNT=$($KUBECTL exec "$PG_POD" -- psql -U postgres -d iceberg -t \
-c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user';" \
2>/dev/null | tr -d ' \n')
if [[ "$TABLE_COUNT" == "1" ]]; then
echo -e "${GREEN}✓ Schema already initialized${NC}"
else
echo -e "${BLUE}Applying gateway schema...${NC}"
$KUBECTL exec -i "$PG_POD" -- psql -U postgres -d iceberg < "$ROOT_DIR/gateway/schema.sql"
echo -e "${GREEN}✓ Schema applied${NC}"
fi
# ---------- Admin User Creation ----------
echo ""
echo -e "${BLUE}=== Admin User Setup ===${NC}"
echo ""
if [[ "$ENV" == "dev" ]]; then
# Dev: use fixed credentials
USER_EMAIL="tim@dexorder.ai"
USER_PASSWORD="test1234"
USER_NAME="Tim"
LICENSE_TYPE="pro"
echo -e "${BLUE}Using dev defaults: $USER_EMAIL / $USER_PASSWORD ($LICENSE_TYPE)${NC}"
else
# Prod: prompt for credentials
read -p "Admin email: " USER_EMAIL
read -s -p "Admin password (min 8 chars): " USER_PASSWORD
echo ""
read -p "Admin display name: " USER_NAME
read -p "License type [free|pro|enterprise] (default: pro): " LICENSE_TYPE
LICENSE_TYPE="${LICENSE_TYPE:-pro}"
fi
# Check if user already exists
EXISTING_ID=$($KUBECTL exec "$PG_POD" -- psql -U postgres -d iceberg -t \
-c "SELECT id FROM \"user\" WHERE email = '$USER_EMAIL';" \
2>/dev/null | tr -d ' \n')
if [ -n "$EXISTING_ID" ]; then
echo -e "${GREEN}✓ User already exists in database ($USER_EMAIL)${NC}"
USER_ID="$EXISTING_ID"
else
# Register via API
echo -e "${BLUE}Waiting for gateway...${NC}"
$KUBECTL wait --for=condition=available --timeout=120s deployment/gateway 2>/dev/null || {
echo -e "${YELLOW}⚠️ Gateway not ready after 120s, trying anyway${NC}"
}
sleep 3
echo -e "${GREEN}→${NC} Registering user via API..."
HTTP_CODE=$(curl -s -o /tmp/dexorder-init-response.json -w "%{http_code}" \
-X POST "$BASE_URL/api/auth/register" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$USER_EMAIL\",\"password\":\"$USER_PASSWORD\",\"name\":\"$USER_NAME\"}")
if [[ "$HTTP_CODE" == "200" || "$HTTP_CODE" == "201" ]]; then
echo -e "${GREEN}✓ User registered via API${NC}"
elif [[ "$HTTP_CODE" == "400" ]]; then
echo -e "${YELLOW}⚠️ API returned 400 (user may already exist), continuing...${NC}"
else
echo -e "${YELLOW}⚠️ API returned HTTP $HTTP_CODE${NC}"
cat /tmp/dexorder-init-response.json 2>/dev/null || true
fi
rm -f /tmp/dexorder-init-response.json
sleep 2
USER_ID=$($KUBECTL exec "$PG_POD" -- psql -U postgres -d iceberg -t \
-c "SELECT id FROM \"user\" WHERE email = '$USER_EMAIL';" \
2>/dev/null | tr -d ' \n')
fi
if [ -z "$USER_ID" ]; then
echo -e "${RED}User not found in database after registration. Is the gateway running?${NC}"
exit 1
fi
echo -e "${GREEN}User ID: $USER_ID${NC}"
# Build license JSON based on type
case "$LICENSE_TYPE" in
enterprise)
LICENSE_JSON='{"licenseType":"enterprise","features":{"maxIndicators":200,"maxStrategies":100,"maxBacktestDays":1825,"realtimeData":true,"customExecutors":true,"apiAccess":true},"resourceLimits":{"maxConcurrentSessions":20,"maxMessagesPerDay":10000,"maxTokensPerMessage":32768,"rateLimitPerMinute":300},"k8sResources":{"memoryRequest":"1Gi","memoryLimit":"4Gi","cpuRequest":"500m","cpuLimit":"4000m","storage":"50Gi","tmpSizeLimit":"1Gi","enableIdleShutdown":true,"idleTimeoutMinutes":120},"preferredModel":{"provider":"anthropic","model":"claude-opus-4-6","temperature":0.7}}'
;;
free)
LICENSE_JSON='{"licenseType":"free","features":{"maxIndicators":10,"maxStrategies":3,"maxBacktestDays":30,"realtimeData":false,"customExecutors":false,"apiAccess":false},"resourceLimits":{"maxConcurrentSessions":1,"maxMessagesPerDay":100,"maxTokensPerMessage":4096,"rateLimitPerMinute":20},"k8sResources":{"memoryRequest":"256Mi","memoryLimit":"512Mi","cpuRequest":"100m","cpuLimit":"500m","storage":"2Gi","tmpSizeLimit":"128Mi","enableIdleShutdown":true,"idleTimeoutMinutes":30},"preferredModel":{"provider":"anthropic","model":"claude-haiku-4-5-20251001","temperature":0.7}}'
;;
pro|*)
LICENSE_JSON='{"licenseType":"pro","features":{"maxIndicators":50,"maxStrategies":20,"maxBacktestDays":365,"realtimeData":true,"customExecutors":true,"apiAccess":true},"resourceLimits":{"maxConcurrentSessions":5,"maxMessagesPerDay":1000,"maxTokensPerMessage":8192,"rateLimitPerMinute":60},"k8sResources":{"memoryRequest":"512Mi","memoryLimit":"2Gi","cpuRequest":"250m","cpuLimit":"2000m","storage":"10Gi","tmpSizeLimit":"256Mi","enableIdleShutdown":true,"idleTimeoutMinutes":60},"preferredModel":{"provider":"anthropic","model":"claude-sonnet-4-6","temperature":0.7}}'
;;
esac
echo -e "${GREEN}→${NC} Creating $LICENSE_TYPE license..."
$KUBECTL exec "$PG_POD" -- psql -U postgres -d iceberg -c "
INSERT INTO user_licenses (user_id, email, license, mcp_server_url)
VALUES (
'$USER_ID',
'$USER_EMAIL',
'$LICENSE_JSON',
'$MCP_URL'
)
ON CONFLICT (user_id) DO UPDATE SET
license = EXCLUDED.license,
updated_at = NOW();
" > /dev/null
echo -e "${GREEN}✓ User ready: $USER_EMAIL ($LICENSE_TYPE)${NC}"
echo ""
echo -e "${BLUE}Initialization complete.${NC}"
if [[ "$ENV" == "dev" ]]; then
echo -e "${BLUE}Login at http://dexorder.local with $USER_EMAIL / $USER_PASSWORD${NC}"
fi

153
bin/op-setup Executable file
View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
# Create the "AI Prod" 1Password vault and all required items with placeholder values.
# Run this once on a fresh setup, then edit each item in 1Password with real values.
#
# Usage:
# bin/op-setup # Create vault and all items
# bin/op-setup --dry-run # Print what would be created without doing it
set -e
VAULT="AI Prod"
DRY_RUN=false
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
echo -e "${YELLOW}Dry run mode — no changes will be made${NC}"
fi
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
run() {
if $DRY_RUN; then
echo -e " ${BLUE}[dry-run]${NC} $*"
else
"$@"
fi
}
item_exists() {
local title="$1"
op item get "$title" --vault "$VAULT" &>/dev/null
}
create_item() {
local title="$1"
shift
if item_exists "$title"; then
echo -e " ${YELLOW}↩${NC} $title — already exists, skipping"
else
echo -e " ${GREEN}+${NC} Creating: $title"
run op item create \
--vault "$VAULT" \
--category "Login" \
--title "$title" \
"$@"
fi
}
# ---------------------------------------------------------------------------
# Step 1: Ensure vault exists
# ---------------------------------------------------------------------------
echo ""
echo -e "${BLUE}=== 1Password Vault ===${NC}"
echo ""
if op vault get "$VAULT" &>/dev/null; then
echo -e "${GREEN}✓${NC} Vault '$VAULT' already exists"
else
echo -e "${GREEN}+${NC} Creating vault: $VAULT"
run op vault create "$VAULT"
fi
# ---------------------------------------------------------------------------
# Step 2: Create items
# ---------------------------------------------------------------------------
echo ""
echo -e "${BLUE}=== Creating Items in '$VAULT' ===${NC}"
echo ""
# --- PostgreSQL ---
# Used by: gateway (DB connection), minio-init job (postgres metadata)
create_item "PostgreSQL" \
"password[password]=REPLACE_WITH_STRONG_PASSWORD"
# --- MinIO ---
# Used by: minio StatefulSet, flink-secrets, gateway-secrets (iceberg S3), sandbox-secrets
# access_key = MinIO root user (equivalent to AWS_ACCESS_KEY_ID)
# secret_key = MinIO root password (equivalent to AWS_SECRET_ACCESS_KEY)
create_item "MinIO" \
"access_key[text]=minio-admin" \
"secret_key[password]=REPLACE_WITH_STRONG_SECRET_KEY"
# --- Gateway ---
# Used by: ai-secrets (anthropic_api_key), gateway-secrets (all LLM keys + jwt_secret)
# jwt_secret: used to sign user sessions — generate with: openssl rand -base64 48
# anthropic_api_key: Anthropic Console → API Keys (https://console.anthropic.com)
# openai_api_key: OpenAI Platform → API Keys (https://platform.openai.com)
# google_api_key: Google AI Studio (https://aistudio.google.com)
# openrouter_api_key: OpenRouter (https://openrouter.ai)
create_item "Gateway" \
"anthropic_api_key[password]=sk-ant-REPLACE_ME" \
"jwt_secret[password]=REPLACE_WITH_RANDOM_64_CHAR_SECRET" \
"openai_api_key[password]=sk-REPLACE_ME" \
"google_api_key[password]=REPLACE_ME" \
"openrouter_api_key[password]=sk-or-REPLACE_ME"
# --- Telegram ---
# Used by: gateway-secrets (optional Telegram bot integration)
# bot_token: BotFather → /newbot (https://t.me/BotFather)
# Leave as placeholder if Telegram integration is not needed.
create_item "Telegram" \
"bot_token[password]=REPLACE_ME_OR_LEAVE_EMPTY"
# --- Ingestor ---
# Used by: ingestor-secrets (exchange API keys for CCXT market data)
# Keys with empty/placeholder values will cause the ingestor to skip that exchange.
# Binance: https://www.binance.com/en/my/settings/api-management
# Coinbase: https://portal.cdp.coinbase.com/
# Kraken: https://www.kraken.com/u/security/api
create_item "Ingestor" \
"binance_api_key[text]=REPLACE_ME" \
"binance_api_secret[password]=REPLACE_ME" \
"coinbase_api_key[text]=REPLACE_ME" \
"coinbase_api_secret[password]=REPLACE_ME" \
"kraken_api_key[text]=REPLACE_ME" \
"kraken_api_secret[password]=REPLACE_ME"
# ---------------------------------------------------------------------------
# Done
# ---------------------------------------------------------------------------
echo ""
if $DRY_RUN; then
echo -e "${YELLOW}Dry run complete — no items were created.${NC}"
else
echo -e "${GREEN}✓ Setup complete.${NC}"
echo ""
echo -e "Next steps:"
echo -e " 1. Open 1Password and update each item in the '${VAULT}' vault with real values:"
echo -e " • PostgreSQL → set a strong random password"
echo -e " • MinIO → set a strong secret_key (access_key can stay as-is)"
echo -e " • Gateway → add real API keys and a random jwt_secret"
echo -e " • Ingestor → add real exchange API keys"
echo -e " • Telegram → add bot token (or leave placeholder if unused)"
echo ""
echo -e " 2. Verify op:// references resolve correctly:"
echo -e " op inject -i deploy/k8s/prod/secrets/gateway-secrets.tpl.yaml | head -20"
echo ""
echo -e " 3. Continue with cluster setup:"
echo -e " bin/secret-update prod"
fi
echo ""

View File

@@ -50,19 +50,21 @@ if [ ! -d "$SECRETS_DIR" ]; then
exit 1
fi
# Get kubectl context
# Set kubectl command and warn for prod
if [[ "$ENV" == "prod" ]]; then
CONTEXT=$(kubectl config current-context)
KUBECTL="kubectl --context=prod"
echo -e "${YELLOW}⚠️ WARNING: Updating PRODUCTION secrets!${NC}"
echo -e "${YELLOW}Current kubectl context: $CONTEXT${NC}"
echo -e "${YELLOW}kubectl context: prod${NC}"
read -p "Are you sure you want to continue? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
echo "Aborted."
exit 0
fi
else
KUBECTL="kubectl"
fi
apply_secret() {
apply_secret_dev() {
local secret_file="$1"
local secret_basename=$(basename "$secret_file" .yaml)
@@ -73,45 +75,71 @@ apply_secret() {
fi
echo -e "${GREEN}→${NC} Applying $secret_basename..."
kubectl apply -f "$secret_file"
$KUBECTL apply -f "$secret_file"
echo -e "${GREEN}✓${NC} $secret_basename updated"
}
apply_secret_prod() {
local tpl_file="$1"
local secret_basename=$(basename "$tpl_file" .tpl.yaml)
if [ ! -f "$tpl_file" ]; then
echo -e "${RED}✗ Template file not found: $tpl_file${NC}"
return 1
fi
echo -e "${GREEN}→${NC} Applying $secret_basename (via op inject)..."
op inject -i "$tpl_file" | $KUBECTL apply -f -
echo -e "${GREEN}✓${NC} $secret_basename updated"
}
SECRETS=(
"ai-secrets"
"postgres-secret"
"minio-secret"
"ingestor-secrets"
"flink-secrets"
"gateway-secrets"
"sandbox-secrets"
)
# Update specific secret or all secrets
if [ -n "$SECRET_NAME" ]; then
# Update single secret
SECRET_FILE="$SECRETS_DIR/$SECRET_NAME.yaml"
apply_secret "$SECRET_FILE"
if [[ "$ENV" == "prod" ]]; then
apply_secret_prod "$SECRETS_DIR/$SECRET_NAME.tpl.yaml"
else
apply_secret_dev "$SECRETS_DIR/$SECRET_NAME.yaml"
fi
else
# Update all secrets
echo -e "${GREEN}Updating all $ENV secrets...${NC}"
echo ""
SECRETS=(
"ai-secrets"
"postgres-secret"
"minio-secret"
"ingestor-secrets"
"flink-secrets"
"gateway-secrets"
"sandbox-secrets"
)
FAILED=0
for secret in "${SECRETS[@]}"; do
SECRET_FILE="$SECRETS_DIR/$secret.yaml"
if ! apply_secret "$SECRET_FILE"; then
FAILED=$((FAILED + 1))
if [[ "$ENV" == "prod" ]]; then
if ! apply_secret_prod "$SECRETS_DIR/$secret.tpl.yaml"; then
FAILED=$((FAILED + 1))
fi
else
if ! apply_secret_dev "$SECRETS_DIR/$secret.yaml"; then
FAILED=$((FAILED + 1))
fi
fi
done
echo ""
if [ $FAILED -gt 0 ]; then
echo -e "${YELLOW}⚠️ $FAILED secret(s) failed to apply${NC}"
echo -e "${YELLOW}Create missing secret files by copying from .example templates:${NC}"
echo -e "${YELLOW} cd $SECRETS_DIR${NC}"
echo -e "${YELLOW} cp SECRET_NAME.yaml.example SECRET_NAME.yaml${NC}"
echo -e "${YELLOW} # Edit SECRET_NAME.yaml with actual values${NC}"
if [[ "$ENV" == "prod" ]]; then
echo -e "${YELLOW}⚠️ $FAILED secret(s) failed to apply${NC}"
echo -e "${YELLOW}Ensure 1Password CLI is authenticated: op signin${NC}"
echo -e "${YELLOW}Ensure 'AI Prod' vault items exist (see deploy/k8s/prod/secrets/*.tpl.yaml)${NC}"
else
echo -e "${YELLOW}⚠️ $FAILED secret(s) failed to apply${NC}"
echo -e "${YELLOW}Create missing secret files by copying from .example templates:${NC}"
echo -e "${YELLOW} cd $SECRETS_DIR${NC}"
echo -e "${YELLOW} cp SECRET_NAME.yaml.example SECRET_NAME.yaml${NC}"
echo -e "${YELLOW} # Edit SECRET_NAME.yaml with actual values${NC}"
fi
exit 1
else
echo -e "${GREEN}✓ All secrets updated successfully${NC}"