Hearth is the infrastructure home for the letemcook ecosystem. Ported from coherence-mcp/infra: - Terraform modules (VPC, EKS, IAM, NLB, S3, storage) - Kubernetes manifests (Forgejo, ingress, cert-manager, karpenter) - Deployment scripts (phased rollout) Status: Not deployed. EKS cluster needs to be provisioned. Next steps: 1. Bootstrap terraform backend 2. Deploy phase 1 (foundation) 3. Deploy phase 2 (core services including Forgejo) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
257 lines
7.2 KiB
Bash
Executable file
257 lines
7.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Phase 2 Validation: Core Services
|
|
#
|
|
# Validates that RFC 0040 components are deployed and healthy:
|
|
# - Vault (unsealed, HA mode)
|
|
# - Keycloak (realm exists, healthy)
|
|
# - Forgejo (SSO working)
|
|
#
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
PASSED=0
|
|
FAILED=0
|
|
WARNINGS=0
|
|
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_pass() {
|
|
echo -e "${GREEN}[PASS]${NC} $1"
|
|
((PASSED++))
|
|
}
|
|
|
|
log_fail() {
|
|
echo -e "${RED}[FAIL]${NC} $1"
|
|
((FAILED++))
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
((WARNINGS++))
|
|
}
|
|
|
|
check_vault() {
|
|
log_info "Checking Vault status..."
|
|
|
|
# Check if namespace exists
|
|
if ! kubectl get namespace vault &> /dev/null; then
|
|
log_fail "vault namespace does not exist"
|
|
return
|
|
fi
|
|
|
|
# Check pod count
|
|
local pod_count
|
|
pod_count=$(kubectl -n vault get pods -l app=vault -o json | jq '[.items[] | select(.status.phase == "Running")] | length')
|
|
|
|
if [ "$pod_count" -ge 1 ]; then
|
|
log_pass "Vault has $pod_count running pods"
|
|
else
|
|
log_fail "Vault has no running pods"
|
|
return
|
|
fi
|
|
|
|
# Check Vault seal status
|
|
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')
|
|
|
|
local sealed
|
|
sealed=$(echo "$vault_status" | jq -r '.sealed // true')
|
|
|
|
if [ "$initialized" = "true" ]; then
|
|
log_pass "Vault is initialized"
|
|
else
|
|
log_fail "Vault is not initialized"
|
|
fi
|
|
|
|
if [ "$sealed" = "false" ]; then
|
|
log_pass "Vault is unsealed"
|
|
else
|
|
log_fail "Vault is sealed - must be unsealed before services can access secrets"
|
|
fi
|
|
|
|
# Check HA status
|
|
local ha_enabled
|
|
ha_enabled=$(echo "$vault_status" | jq -r '.ha_enabled // false')
|
|
|
|
if [ "$ha_enabled" = "true" ]; then
|
|
log_pass "Vault HA mode is enabled"
|
|
else
|
|
log_warn "Vault HA mode is not enabled"
|
|
fi
|
|
|
|
# Check Vault health endpoint
|
|
local health_code
|
|
health_code=$(kubectl -n vault exec vault-0 -- wget -q -O /dev/null -S http://127.0.0.1:8200/v1/sys/health 2>&1 | grep "HTTP/" | awk '{print $2}' || echo "0")
|
|
|
|
if [ "$health_code" = "200" ] || [ "$health_code" = "429" ]; then
|
|
log_pass "Vault health endpoint responding (HTTP $health_code)"
|
|
else
|
|
log_warn "Vault health endpoint returned HTTP $health_code"
|
|
fi
|
|
}
|
|
|
|
check_keycloak() {
|
|
log_info "Checking Keycloak status..."
|
|
|
|
# Check if namespace exists
|
|
if ! kubectl get namespace keycloak &> /dev/null; then
|
|
log_fail "keycloak namespace does not exist"
|
|
return
|
|
fi
|
|
|
|
# Check pod count and health
|
|
local ready_pods
|
|
ready_pods=$(kubectl -n keycloak get pods -l app=keycloak -o json | jq '[.items[] | select(.status.phase == "Running") | select(.status.containerStatuses[]?.ready == true)] | length')
|
|
|
|
if [ "$ready_pods" -ge 1 ]; then
|
|
log_pass "Keycloak has $ready_pods ready pods"
|
|
else
|
|
log_fail "Keycloak has no ready pods"
|
|
return
|
|
fi
|
|
|
|
# Check Keycloak service
|
|
local svc_exists
|
|
svc_exists=$(kubectl -n keycloak get svc keycloak -o name 2>/dev/null || echo "")
|
|
|
|
if [ -n "$svc_exists" ]; then
|
|
log_pass "Keycloak service exists"
|
|
else
|
|
log_fail "Keycloak service does not exist"
|
|
fi
|
|
|
|
# Check for coherence realm (via API if accessible)
|
|
# This requires port-forward or ingress access
|
|
log_info "Note: Verify 'coherence' realm exists via Keycloak Web UI"
|
|
|
|
# Check ingress/route
|
|
local ingress_exists
|
|
ingress_exists=$(kubectl get ingress -A -o json 2>/dev/null | jq '[.items[] | select(.spec.rules[]?.host | contains("auth"))] | length')
|
|
|
|
if [ "$ingress_exists" -ge 1 ]; then
|
|
log_pass "Keycloak ingress configured"
|
|
else
|
|
log_warn "No Keycloak ingress found - may be using LoadBalancer service"
|
|
fi
|
|
}
|
|
|
|
check_forgejo() {
|
|
log_info "Checking Forgejo status..."
|
|
|
|
# Check if namespace exists
|
|
if ! kubectl get namespace forgejo &> /dev/null; then
|
|
log_fail "forgejo namespace does not exist"
|
|
return
|
|
fi
|
|
|
|
# Check pod health
|
|
local ready_pods
|
|
ready_pods=$(kubectl -n forgejo get pods -l app=forgejo -o json | jq '[.items[] | select(.status.phase == "Running") | select(.status.containerStatuses[]?.ready == true)] | length')
|
|
|
|
if [ "$ready_pods" -ge 1 ]; then
|
|
log_pass "Forgejo has $ready_pods ready pods"
|
|
else
|
|
log_fail "Forgejo has no ready pods"
|
|
return
|
|
fi
|
|
|
|
# Check Forgejo service
|
|
local svc_exists
|
|
svc_exists=$(kubectl -n forgejo get svc forgejo -o name 2>/dev/null || echo "")
|
|
|
|
if [ -n "$svc_exists" ]; then
|
|
log_pass "Forgejo service exists"
|
|
else
|
|
log_fail "Forgejo service does not exist"
|
|
fi
|
|
|
|
# Check PVC
|
|
local pvc_bound
|
|
pvc_bound=$(kubectl -n forgejo get pvc -o json | jq '[.items[] | select(.status.phase == "Bound")] | length')
|
|
|
|
if [ "$pvc_bound" -ge 1 ]; then
|
|
log_pass "Forgejo PVC is bound"
|
|
else
|
|
log_warn "No bound PVC found for Forgejo"
|
|
fi
|
|
|
|
# Check OAuth configuration
|
|
local oauth_config
|
|
oauth_config=$(kubectl -n forgejo get configmap -o json 2>/dev/null | jq '[.items[] | select(.metadata.name | contains("oauth"))] | length')
|
|
|
|
if [ "$oauth_config" -ge 1 ]; then
|
|
log_pass "Forgejo OAuth configuration found"
|
|
else
|
|
log_warn "No OAuth configuration found - SSO may not be configured"
|
|
fi
|
|
}
|
|
|
|
check_sso_integration() {
|
|
log_info "Checking SSO integration..."
|
|
|
|
# This is a placeholder for SSO checks
|
|
# In practice, you would test OAuth flows
|
|
log_info "Note: Manually verify SSO by logging into Forgejo with Keycloak"
|
|
log_info " 1. Navigate to Forgejo web UI"
|
|
log_info " 2. Click 'Sign in with Keycloak'"
|
|
log_info " 3. Verify redirect to Keycloak login"
|
|
log_info " 4. Verify successful login and redirect back"
|
|
}
|
|
|
|
print_summary() {
|
|
echo ""
|
|
echo "========================================"
|
|
echo "Phase 2 Validation Summary"
|
|
echo "========================================"
|
|
echo -e " ${GREEN}Passed:${NC} $PASSED"
|
|
echo -e " ${RED}Failed:${NC} $FAILED"
|
|
echo -e " ${YELLOW}Warnings:${NC} $WARNINGS"
|
|
echo "========================================"
|
|
|
|
if [ "$FAILED" -gt 0 ]; then
|
|
echo ""
|
|
echo -e "${RED}Phase 2 validation FAILED${NC}"
|
|
echo "Please fix the issues above before proceeding to Phase 3."
|
|
exit 1
|
|
elif [ "$WARNINGS" -gt 0 ]; then
|
|
echo ""
|
|
echo -e "${YELLOW}Phase 2 validation passed with warnings${NC}"
|
|
echo "Review warnings above. You may proceed to Phase 3."
|
|
exit 0
|
|
else
|
|
echo ""
|
|
echo -e "${GREEN}Phase 2 validation PASSED${NC}"
|
|
echo "You may proceed to Phase 3."
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
echo "========================================"
|
|
echo "Phase 2 Validation: Core Services"
|
|
echo "========================================"
|
|
echo ""
|
|
|
|
check_vault
|
|
check_keycloak
|
|
check_forgejo
|
|
check_sso_integration
|
|
|
|
print_summary
|
|
}
|
|
|
|
main "$@"
|