136 lines
5.4 KiB
Bash
136 lines
5.4 KiB
Bash
#!/usr/bin/env bash
|
|
# deploy/onboarding/onboard-tenant.sh
|
|
#
|
|
# Provisions a new pilot tenant in Veylant IA:
|
|
# 1. Creates the tenant admin user
|
|
# 2. Seeds default routing policies (hr, finance, engineering)
|
|
# 3. Configures default rate limits
|
|
# 4. Verifies the setup
|
|
#
|
|
# Usage:
|
|
# export VEYLANT_URL=http://localhost:8090
|
|
# export VEYLANT_ADMIN_TOKEN=<super-admin-jwt>
|
|
# export TENANT_NAME="Acme Corp"
|
|
# export TENANT_ADMIN_EMAIL=admin@acme.example
|
|
# ./onboard-tenant.sh
|
|
#
|
|
# Required env vars:
|
|
# VEYLANT_URL - base URL of the proxy (no trailing slash)
|
|
# VEYLANT_ADMIN_TOKEN - JWT with admin role for the platform tenant
|
|
# TENANT_ADMIN_EMAIL - email of the new tenant's first admin
|
|
#
|
|
# Optional env vars:
|
|
# TENANT_ADMIN_FIRST - first name (default: Admin)
|
|
# TENANT_ADMIN_LAST - last name (default: User)
|
|
# RPM - requests per minute (default: 1000)
|
|
# BURST - burst size (default: 200)
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Config ────────────────────────────────────────────────────────────────────
|
|
|
|
VEYLANT_URL="${VEYLANT_URL:?VEYLANT_URL is required}"
|
|
VEYLANT_ADMIN_TOKEN="${VEYLANT_ADMIN_TOKEN:?VEYLANT_ADMIN_TOKEN is required}"
|
|
TENANT_ADMIN_EMAIL="${TENANT_ADMIN_EMAIL:?TENANT_ADMIN_EMAIL is required}"
|
|
TENANT_ADMIN_FIRST="${TENANT_ADMIN_FIRST:-Admin}"
|
|
TENANT_ADMIN_LAST="${TENANT_ADMIN_LAST:-User}"
|
|
RPM="${RPM:-1000}"
|
|
BURST="${BURST:-200}"
|
|
|
|
API="${VEYLANT_URL}/v1/admin"
|
|
AUTH="Authorization: Bearer ${VEYLANT_ADMIN_TOKEN}"
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
log() { echo "[onboard] $*"; }
|
|
die() { echo "[onboard] ERROR: $*" >&2; exit 1; }
|
|
|
|
api_post() {
|
|
local path="$1"
|
|
local body="$2"
|
|
curl -sf -X POST "${API}${path}" \
|
|
-H "${AUTH}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${body}"
|
|
}
|
|
|
|
api_put() {
|
|
local path="$1"
|
|
local body="$2"
|
|
curl -sf -X PUT "${API}${path}" \
|
|
-H "${AUTH}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${body}"
|
|
}
|
|
|
|
api_get() {
|
|
local path="$1"
|
|
curl -sf -X GET "${API}${path}" \
|
|
-H "${AUTH}"
|
|
}
|
|
|
|
# ── Step 1: Health check ──────────────────────────────────────────────────────
|
|
|
|
log "Checking proxy health…"
|
|
status=$(curl -sf "${VEYLANT_URL}/healthz" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status',''))")
|
|
[[ "$status" == "ok" ]] || die "Proxy health check failed (got: $status)"
|
|
log "Proxy is healthy."
|
|
|
|
# ── Step 2: Create tenant admin user ─────────────────────────────────────────
|
|
|
|
log "Creating tenant admin user: ${TENANT_ADMIN_EMAIL}…"
|
|
user_resp=$(api_post "/users" "{
|
|
\"email\": \"${TENANT_ADMIN_EMAIL}\",
|
|
\"first_name\": \"${TENANT_ADMIN_FIRST}\",
|
|
\"last_name\": \"${TENANT_ADMIN_LAST}\",
|
|
\"role\": \"admin\"
|
|
}")
|
|
user_id=$(echo "$user_resp" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))")
|
|
[[ -n "$user_id" ]] || die "Failed to create admin user"
|
|
log "Admin user created: id=${user_id}"
|
|
|
|
# ── Step 3: Seed default routing policies ─────────────────────────────────────
|
|
|
|
for tmpl in hr finance engineering catchall; do
|
|
log "Seeding routing template: ${tmpl}…"
|
|
api_post "/policies/seed/${tmpl}" "{}" > /dev/null
|
|
log " → ${tmpl} policy seeded."
|
|
done
|
|
|
|
# ── Step 4: Configure rate limits ─────────────────────────────────────────────
|
|
|
|
# Extract tenant_id from the JWT (middle base64 segment).
|
|
TENANT_ID=$(echo "$VEYLANT_ADMIN_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null \
|
|
| python3 -c "import sys,json; print(json.load(sys.stdin).get('tenant_id',''))" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$TENANT_ID" ]]; then
|
|
log "Configuring rate limits for tenant ${TENANT_ID}: ${RPM} RPM, burst ${BURST}…"
|
|
api_put "/rate-limits/${TENANT_ID}" "{
|
|
\"requests_per_min\": ${RPM},
|
|
\"burst_size\": ${BURST},
|
|
\"user_rpm\": $((RPM / 5)),
|
|
\"user_burst\": $((BURST / 5)),
|
|
\"is_enabled\": true
|
|
}" > /dev/null
|
|
log "Rate limits configured."
|
|
else
|
|
log "Warning: could not decode tenant_id from JWT — skipping rate-limit setup."
|
|
fi
|
|
|
|
# ── Step 5: Verify ────────────────────────────────────────────────────────────
|
|
|
|
log "Verifying setup…"
|
|
policies=$(api_get "/policies" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('data', [])))")
|
|
log " → ${policies} routing policies active."
|
|
|
|
users=$(api_get "/users" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('data', [])))")
|
|
log " → ${users} user(s) in the tenant."
|
|
|
|
log ""
|
|
log "✓ Tenant onboarding complete."
|
|
log " Admin: ${TENANT_ADMIN_EMAIL}"
|
|
log " Policies seeded: hr, finance, engineering, catchall"
|
|
log " Rate limit: ${RPM} RPM / ${BURST} burst"
|
|
log ""
|
|
log "Next step: issue a Keycloak JWT for ${TENANT_ADMIN_EMAIL} and share it with the admin."
|