fix: move single-tenant DB record creation from bootstrap to installer
All checks were successful
CI / build (push) Successful in 1m11s
CI / docker (push) Successful in 17s

The bootstrap script runs before the SaaS app starts, but the tenants
table only exists after Flyway migrations run in the SaaS app. This
circular dependency caused Phase 12b's psql commands to fail under
set -e, crashing the Logto container on first install in single-tenant
mode.

Now the bootstrap only handles Logto-side setup (org, user roles, OIDC
redirect URIs), and the installer creates the tenant DB record after
verify_health confirms the SaaS app is up. Also makes docker_compose_up
tolerant of transient startup errors since verify_health is the real
health gate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-13 19:31:23 +02:00
parent bfb26d9aa5
commit 17d8d98d5f
2 changed files with 60 additions and 16 deletions

View File

@@ -730,20 +730,8 @@ elif [ -n "$TENANT_ORG_NAME" ]; then
fi fi
fi fi
# Insert tenant record into cameleer_saas database # NOTE: Tenant DB record is created by the installer after Flyway migrations
pgpass # have run (the tenants table doesn't exist yet at bootstrap time).
EXISTING_TENANT=$(psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB_SAAS" -t -A -c \
"SELECT id FROM tenants WHERE slug = '$TENANT_SLUG';" 2>/dev/null)
if [ -z "$EXISTING_TENANT" ]; then
TENANT_UUID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c "import uuid; print(uuid.uuid4())" 2>/dev/null)
psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB_SAAS" -c \
"INSERT INTO tenants (id, name, slug, tier, status, logto_org_id, created_at, updated_at)
VALUES ('$TENANT_UUID', '$TENANT_ORG_NAME', '$TENANT_SLUG', 'STANDARD', 'PROVISIONING', '$TENANT_ORG_ID', NOW(), NOW());" >/dev/null 2>&1
log "Created tenant record: $TENANT_SLUG (status: PROVISIONING)"
log " The SaaS app will provision the tenant's server on next restart or via the UI."
else
log "Tenant record already exists for slug: $TENANT_SLUG"
fi
fi fi
log "Single-tenant setup complete." log "Single-tenant setup complete."

View File

@@ -930,8 +930,8 @@ docker_compose_pull() {
docker_compose_up() { docker_compose_up() {
log_info "Starting Cameleer SaaS platform..." log_info "Starting Cameleer SaaS platform..."
(cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" up -d) (cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" up -d) || true
log_info "Containers started." log_info "Containers started — verifying health next."
} }
docker_compose_down() { docker_compose_down() {
@@ -1018,6 +1018,58 @@ verify_health() {
log_success "All services healthy." log_success "All services healthy."
} }
# --- Single-tenant DB record ---
setup_single_tenant_record() {
[ -z "$TENANT_ORG_NAME" ] && return 0
local slug
slug=$(echo "$TENANT_ORG_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g; s/--*/-/g; s/^-//; s/-$//')
log_info "Creating single-tenant record for '$TENANT_ORG_NAME' (slug: $slug)..."
# Check if tenant already exists
local existing
existing=$(cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" exec -T postgres \
psql -U "${POSTGRES_USER}" -d cameleer_saas -t -A -c \
"SELECT id FROM tenants WHERE slug = '$slug';" 2>/dev/null) || true
if [ -n "$existing" ]; then
printf " ${GREEN}[ok]${NC} Tenant record already exists: %s\n" "$slug"
return 0
fi
# Get Logto org ID from the logto database
local org_id
org_id=$(cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" exec -T postgres \
psql -U "${POSTGRES_USER}" -d logto -t -A -c \
"SELECT id FROM organizations WHERE name = '$TENANT_ORG_NAME' AND tenant_id = 'default';" 2>/dev/null) || true
if [ -z "$org_id" ]; then
log_warn "Could not find Logto organization for '$TENANT_ORG_NAME' — tenant record not created."
log_warn "Create the tenant manually via the vendor console."
return 0
fi
# Generate UUID and insert
local uuid
uuid=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c "import uuid; print(uuid.uuid4())" 2>/dev/null || true)
if [ -z "$uuid" ]; then
log_warn "Could not generate UUID — tenant record not created."
return 0
fi
if cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" exec -T postgres \
psql -U "${POSTGRES_USER}" -d cameleer_saas -c \
"INSERT INTO tenants (id, name, slug, tier, status, logto_org_id, created_at, updated_at)
VALUES ('$uuid', '$TENANT_ORG_NAME', '$slug', 'STANDARD', 'PROVISIONING', '$org_id', NOW(), NOW());" >/dev/null 2>&1; then
printf " ${GREEN}[ok]${NC} Tenant record created: %s (status: PROVISIONING)\n" "$slug"
log_info "The SaaS app will provision the tenant's server automatically."
else
log_warn "Failed to create tenant record — create it manually via the vendor console."
fi
}
# --- Output file generation --- # --- Output file generation ---
write_config_file() { write_config_file() {
@@ -1330,6 +1382,7 @@ handle_rerun() {
docker_compose_down docker_compose_down
docker_compose_up docker_compose_up
verify_health verify_health
setup_single_tenant_record
generate_install_doc generate_install_doc
print_summary print_summary
exit 0 exit 0
@@ -1423,6 +1476,9 @@ main() {
# Verify health # Verify health
verify_health verify_health
# Create single-tenant record (after Flyway migrations have run)
setup_single_tenant_record
# Generate output files # Generate output files
generate_credentials_file generate_credentials_file
generate_install_doc generate_install_doc