#!/usr/bin/env bash # # Phase 2: Core Services Deployment (RFC 0040) # # This script deploys core services including: # - HashiCorp Vault for secrets management # - Keycloak for identity and SSO # - Forgejo for Git hosting # # Usage: # ./deploy-phase2-core-services.sh [--dry-run] [--skip-vault] [--skip-keycloak] [--skip-forgejo] # # Prerequisites: # - Phase 1 (Foundation) must be deployed and validated # - kubectl configured for EKS cluster # - Helm 3.x installed # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" INFRA_DIR="$(dirname "$SCRIPT_DIR")" K8S_DIR="$INFRA_DIR/kubernetes" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Flags DRY_RUN=false SKIP_VAULT=false SKIP_KEYCLOAK=false SKIP_FORGEJO=false # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --skip-vault) SKIP_VAULT=true shift ;; --skip-keycloak) SKIP_KEYCLOAK=true shift ;; --skip-forgejo) SKIP_FORGEJO=true shift ;; -h|--help) echo "Usage: $0 [--dry-run] [--skip-vault] [--skip-keycloak] [--skip-forgejo]" exit 0 ;; *) echo -e "${RED}Unknown option: $1${NC}" exit 1 ;; esac done log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } run_cmd() { if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}[DRY-RUN]${NC} Would run: $*" else "$@" fi } check_prerequisites() { log_info "Checking prerequisites..." # Check kubectl connectivity if ! kubectl cluster-info &> /dev/null; then log_error "kubectl not connected to cluster. Run Phase 1 first." exit 1 fi # Check cert-manager is running if ! kubectl -n cert-manager get pods -l app.kubernetes.io/instance=cert-manager -o jsonpath='{.items[0].status.phase}' 2>/dev/null | grep -q Running; then log_error "cert-manager not running. Ensure Phase 1 is complete." exit 1 fi # Check CockroachDB is running if ! kubectl -n cockroachdb get pods -l app=cockroachdb -o jsonpath='{.items[0].status.phase}' 2>/dev/null | grep -q Running; then log_error "CockroachDB not running. Ensure Phase 1 is complete." exit 1 fi log_success "All prerequisites met" } deploy_vault() { if [ "$SKIP_VAULT" = true ]; then log_warn "Skipping Vault deployment" return fi log_info "Deploying HashiCorp Vault..." # Deploy Vault run_cmd kubectl apply -k "$K8S_DIR/vault/" # Wait for Vault pods if [ "$DRY_RUN" = false ]; then log_info "Waiting for Vault pods to be ready..." kubectl -n vault wait --for=condition=ready pod -l app=vault --timeout=300s || true fi # Check if Vault needs initialization if [ "$DRY_RUN" = false ]; then local vault_status vault_status=$(kubectl -n vault exec vault-0 -- vault status -format=json 2>/dev/null || echo '{}') local initialized initialized=$(echo "$vault_status" | jq -r '.initialized // false') if [ "$initialized" = "false" ]; then log_warn "Vault needs initialization!" echo "" echo "Run the following commands to initialize Vault:" echo " kubectl -n vault exec -it vault-0 -- vault operator init" echo "" echo "IMPORTANT: Save the unseal keys and root token securely!" echo "" echo "Then unseal each Vault pod:" echo " kubectl -n vault exec -it vault-0 -- vault operator unseal " echo " kubectl -n vault exec -it vault-0 -- vault operator unseal " echo " kubectl -n vault exec -it vault-0 -- vault operator unseal " echo "" read -p "Press Enter after Vault is initialized and unsealed..." fi # Verify Vault is unsealed vault_status=$(kubectl -n vault exec vault-0 -- vault status -format=json 2>/dev/null || echo '{}') local sealed sealed=$(echo "$vault_status" | jq -r '.sealed // true') if [ "$sealed" = "true" ]; then log_error "Vault is still sealed. Please unseal before continuing." exit 1 fi fi log_success "Vault deployment complete" } deploy_keycloak() { if [ "$SKIP_KEYCLOAK" = true ]; then log_warn "Skipping Keycloak deployment" return fi log_info "Deploying Keycloak..." # Deploy Keycloak run_cmd kubectl apply -k "$K8S_DIR/keycloak/" # Wait for Keycloak pods if [ "$DRY_RUN" = false ]; then log_info "Waiting for Keycloak pods to be ready..." kubectl -n keycloak wait --for=condition=ready pod -l app=keycloak --timeout=300s fi # Import realm configuration if available if [ -f "$K8S_DIR/keycloak/realm-config.yaml" ]; then log_info "Applying realm configuration..." run_cmd kubectl apply -f "$K8S_DIR/keycloak/realm-config.yaml" fi log_success "Keycloak deployment complete" } deploy_forgejo() { if [ "$SKIP_FORGEJO" = true ]; then log_warn "Skipping Forgejo deployment" return fi log_info "Deploying Forgejo..." # Deploy Forgejo run_cmd kubectl apply -k "$K8S_DIR/forgejo/" # Wait for Forgejo pods if [ "$DRY_RUN" = false ]; then log_info "Waiting for Forgejo pods to be ready..." kubectl -n forgejo wait --for=condition=ready pod -l app=forgejo --timeout=300s fi log_success "Forgejo deployment complete" } validate_phase2() { log_info "Running Phase 2 validation..." local validation_script="$SCRIPT_DIR/validate-phase2.sh" if [ -x "$validation_script" ]; then if [ "$DRY_RUN" = true ]; then log_info "Would run validation script: $validation_script" else "$validation_script" fi else log_warn "Validation script not found or not executable: $validation_script" fi } main() { echo "========================================" echo "Phase 2: Core Services" echo "RFC 0040 Deployment" echo "========================================" echo "" if [ "$DRY_RUN" = true ]; then log_warn "Running in DRY-RUN mode - no changes will be made" echo "" fi check_prerequisites deploy_vault deploy_keycloak deploy_forgejo validate_phase2 echo "" echo "========================================" log_success "Phase 2 deployment complete!" echo "========================================" echo "" echo "Next steps:" echo " 1. Run validate-phase2.sh to verify deployment" echo " 2. Configure Keycloak realm and clients via Web UI" echo " 3. Tag this deployment: git tag -a v0.2.0-phase2 -m 'Phase 2: Core Services'" echo " 4. Proceed to Phase 3: ./deploy-phase3-dns-email.sh" } main "$@"