diff --git a/ci/deploy-k8s-aws/scripts/cleanup-orphaned.sh b/ci/deploy-k8s-aws/scripts/cleanup-orphaned.sh index 78158ae85..d4057b4eb 100644 --- a/ci/deploy-k8s-aws/scripts/cleanup-orphaned.sh +++ b/ci/deploy-k8s-aws/scripts/cleanup-orphaned.sh @@ -9,10 +9,14 @@ # Orphaned resources are those that: # - Have the label 'managed-by=github-action' # - Do NOT have corresponding services in docker-compose.yml +# - Are NOT in the exclusion list (special deployments managed separately) +# +# Excluded deployments: +# - uni-resolver-frontend: Web UI deployed separately, not in docker-compose.yml # # The script removes: -# - Orphaned Deployments -# - Orphaned Services +# - Orphaned Deployments (not in docker-compose.yml and not excluded) +# - Orphaned Services (not in docker-compose.yml AND no matching deployment) # - Service-specific ConfigMaps (not the global app-config) # # Prerequisites: @@ -41,6 +45,10 @@ echo "====================================================================" echo "Checking for orphaned deployments in namespace: $NAMESPACE" echo "====================================================================" +# Deployments that are not in docker-compose.yml but should be kept +# These are special deployments managed separately +EXCLUDED_DEPLOYMENTS="uni-resolver-frontend" + # Get all deployments managed by this GitHub Action kubectl get deployments -n "$NAMESPACE" -l managed-by=github-action -o json 2>/dev/null | \ jq -r '.items[].metadata.name' > managed_deployments.txt || touch managed_deployments.txt @@ -69,6 +77,13 @@ orphans_found=0 # Find and remove orphaned deployments while read -r deployment; do + # Check if this deployment is in the exclusion list + if echo "$EXCLUDED_DEPLOYMENTS" | grep -qw "$deployment"; then + echo "" + echo "ℹ Skipping deployment: $deployment (excluded from cleanup)" + continue + fi + # Check if this deployment exists in docker-compose.yml if ! grep -q "^${deployment}$" compose_services.txt; then echo "" @@ -113,3 +128,80 @@ else echo "✓ Cleanup completed: $orphans_found orphaned deployment(s) removed" fi echo "====================================================================" + +echo "" +echo "====================================================================" +echo "Checking for orphaned services in namespace: $NAMESPACE" +echo "====================================================================" + +# Get all services managed by this GitHub Action +kubectl get services -n "$NAMESPACE" -l managed-by=github-action -o json 2>/dev/null | \ + jq -r '.items[].metadata.name' > managed_services.txt || touch managed_services.txt + +# Check if there are any managed services +if [ ! -s managed_services.txt ]; then + echo "No managed services found in namespace" + echo "====================================================================" + exit 0 +fi + +echo "Managed services in cluster:" +cat managed_services.txt + +echo "" +echo "Services in docker-compose.yml:" +cat compose_services.txt + +echo "" +echo "Comparing services with docker-compose.yml and deployments..." + +# Track orphaned services +orphaned_services=0 + +# Find and remove orphaned services +while read -r service; do + # Check if this service is in the exclusion list + if echo "$EXCLUDED_DEPLOYMENTS" | grep -qw "$service"; then + echo "" + echo "ℹ Skipping service: $service (excluded from cleanup)" + continue + fi + + # Check if service exists in docker-compose.yml OR has a matching deployment + service_in_compose=false + deployment_exists=false + + if grep -q "^${service}$" compose_services.txt; then + service_in_compose=true + fi + + if kubectl get deployment "$service" -n "$NAMESPACE" &>/dev/null; then + deployment_exists=true + fi + + # Service is orphaned if it's not in compose AND has no matching deployment + if [ "$service_in_compose" = false ] && [ "$deployment_exists" = false ]; then + echo "" + echo "⚠ Found orphaned service: $service" + echo " This service is not defined in docker-compose.yml and has no matching deployment" + echo " Removing service..." + + orphaned_services=$((orphaned_services + 1)) + + if kubectl delete service "$service" -n "$NAMESPACE" --ignore-not-found=true; then + echo " ✓ Service '$service' deleted" + else + echo " ✗ Failed to delete service '$service'" + fi + fi +done < managed_services.txt + +echo "" +echo "====================================================================" +if [ $orphaned_services -eq 0 ]; then + echo "✓ No orphaned services found" + echo " All managed services match docker-compose.yml or have deployments" +else + echo "✓ Cleanup completed: $orphaned_services orphaned service(s) removed" +fi +echo "====================================================================" diff --git a/ci/deploy-k8s-aws/scripts/deploy-frontend.sh b/ci/deploy-k8s-aws/scripts/deploy-frontend.sh new file mode 100755 index 000000000..2c0d95848 --- /dev/null +++ b/ci/deploy-k8s-aws/scripts/deploy-frontend.sh @@ -0,0 +1,170 @@ +#!/bin/bash + +################################################################################ +# Deploy Universal Resolver Frontend +# +# This script deploys the uni-resolver-frontend application which provides +# the web UI for the Universal Resolver. This is separate from the services +# defined in docker-compose.yml. +# +# Components deployed: +# - ConfigMap: uni-resolver-frontend (contains backend URL) +# - Deployment: uni-resolver-frontend (web UI container) +# - Service: uni-resolver-frontend (NodePort service) +# +# Prerequisites: +# - kubectl must be configured and authenticated +# - NAMESPACE environment variable must be set +# +# Usage: +# NAMESPACE=uni-resolver ./deploy-frontend.sh +################################################################################ + +set -e + +# Validate prerequisites +if [ -z "$NAMESPACE" ]; then + echo "Error: NAMESPACE environment variable is not set" + exit 1 +fi + +# Configuration +BACKEND_URL="https://dev.uniresolver.io/" + +echo "====================================================================" +echo "Deploying Universal Resolver Frontend" +echo "====================================================================" +echo "" + +################################################################################ +# Step 1: Deploy ConfigMap +################################################################################ + +echo "Step 1: Creating/updating ConfigMap..." + +cat > configmap-uni-resolver-frontend.yaml << EOF +apiVersion: v1 +kind: ConfigMap +metadata: + name: uni-resolver-frontend + namespace: ${NAMESPACE} + labels: + app: uni-resolver-frontend + managed-by: github-action +data: + backend_url: ${BACKEND_URL} +EOF + +if kubectl apply -f configmap-uni-resolver-frontend.yaml; then + echo "✓ ConfigMap created/updated" +else + echo "✗ Failed to create ConfigMap" + exit 1 +fi + +echo "" + +################################################################################ +# Step 2: Deploy Frontend Application +################################################################################ + +echo "Step 2: Creating/updating Deployment and Service..." + +cat > deployment-uni-resolver-frontend.yaml << EOF +apiVersion: apps/v1 +kind: Deployment +metadata: + name: uni-resolver-frontend + namespace: ${NAMESPACE} + labels: + app: uni-resolver-frontend + type: frontend + managed-by: github-action +spec: + replicas: 1 + selector: + matchLabels: + app: uni-resolver-frontend + template: + metadata: + labels: + app: uni-resolver-frontend + spec: + containers: + - name: uni-resolver-frontend + image: universalresolver/universal-resolver-frontend + imagePullPolicy: Always + ports: + - containerPort: 7081 + env: + - name: BACKEND_URL + valueFrom: + configMapKeyRef: + name: uni-resolver-frontend + key: backend_url +--- +apiVersion: v1 +kind: Service +metadata: + name: uni-resolver-frontend + namespace: ${NAMESPACE} + labels: + app: uni-resolver-frontend + managed-by: github-action +spec: + type: NodePort + selector: + app: uni-resolver-frontend + ports: + - protocol: TCP + port: 7081 + targetPort: 7081 +EOF + +if kubectl apply -f deployment-uni-resolver-frontend.yaml; then + echo "✓ Deployment and Service created/updated" +else + echo "✗ Failed to create Deployment and Service" + exit 1 +fi + +echo "" + +################################################################################ +# Step 3: Wait for Deployment to be Ready +################################################################################ + +echo "Step 3: Waiting for deployment to become ready..." + +if kubectl rollout status deployment/uni-resolver-frontend -n "$NAMESPACE" --timeout=300s; then + echo "✓ Frontend deployment is ready" +else + echo "⚠ Frontend deployment rollout timed out" + echo " Continuing with deployment process..." +fi + +echo "" + +################################################################################ +# Summary +################################################################################ + +echo "====================================================================" +echo "Frontend Deployment Summary" +echo "====================================================================" +echo "" + +# Show deployment status +echo "Deployment:" +kubectl get deployment uni-resolver-frontend -n "$NAMESPACE" -o wide + +echo "" +echo "Service:" +kubectl get service uni-resolver-frontend -n "$NAMESPACE" -o wide + +echo "" +echo "Pods:" +kubectl get pods -n "$NAMESPACE" -l app=uni-resolver-frontend -o wide + +echo "" +echo "✓ Frontend deployment complete" diff --git a/ci/deploy-k8s-aws/scripts/deploy-ingress.sh b/ci/deploy-k8s-aws/scripts/deploy-ingress.sh new file mode 100755 index 000000000..bc6295b78 --- /dev/null +++ b/ci/deploy-k8s-aws/scripts/deploy-ingress.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +################################################################################ +# Deploy Ingress +# +# This script ensures the uni-resolver-ingress exists in the cluster. +# If it doesn't exist, it creates it from scratch. +# +# The Ingress configures AWS ALB to route traffic to: +# - uni-resolver-web service (API endpoints at /1.0/*) +# - uni-resolver-frontend service (UI at /*) +# +# Prerequisites: +# - kubectl must be configured and authenticated +# - NAMESPACE environment variable must be set +# - AWS Load Balancer Controller must be installed in the cluster +# +# Usage: +# NAMESPACE=uni-resolver ./deploy-ingress.sh +################################################################################ + +set -e + +# Validate prerequisites +if [ -z "$NAMESPACE" ]; then + echo "Error: NAMESPACE environment variable is not set" + exit 1 +fi + +# Constants +INGRESS_NAME="uni-resolver-ingress" +DEV_DOMAIN_NAME="dev.uniresolver.io" +PROD_DOMAIN_NAME="resolver.identity.foundation" +CERTIFICATE_ARNS="arn:aws:acm:us-east-2:332553390353:certificate/925fce37-d446-4af3-828e-f803b3746af0,arn:aws:acm:us-east-2:332553390353:certificate/59fa30ca-de05-4024-8f80-fea9ab9ab8bf" + +echo "====================================================================" +echo "Checking Ingress: $INGRESS_NAME" +echo "====================================================================" + +# First, ensure required services exist and are of type NodePort +echo "Checking required services..." +echo "" + +for service in uni-resolver-web uni-resolver-frontend; do + if kubectl get service "$service" -n "$NAMESPACE" &>/dev/null; then + service_type=$(kubectl get service "$service" -n "$NAMESPACE" -o jsonpath='{.spec.type}') + echo " Service '$service' exists (type: $service_type)" + + if [ "$service_type" != "NodePort" ] && [ "$service_type" != "LoadBalancer" ]; then + echo " ⚠ Service type is '$service_type', patching to 'NodePort'..." + kubectl patch service "$service" -n "$NAMESPACE" -p '{"spec":{"type":"NodePort"}}' + echo " ✓ Service patched to NodePort" + fi + else + echo " ⚠ Warning: Service '$service' does not exist" + echo " The Ingress will be created but may not work until the service is deployed" + fi +done + +echo "" + +# Check if ingress exists +if kubectl get ingress "$INGRESS_NAME" -n "$NAMESPACE" &>/dev/null; then + echo "✓ Ingress '$INGRESS_NAME' already exists" + echo "" + echo "Current Ingress configuration:" + kubectl get ingress "$INGRESS_NAME" -n "$NAMESPACE" -o wide + echo "" + echo "No action needed" + exit 0 +fi + +echo "⚠ Ingress '$INGRESS_NAME' does not exist" +echo "Creating Ingress..." +echo "" + +# Generate Ingress manifest +cat > ingress.yaml << EOF +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${INGRESS_NAME} + namespace: ${NAMESPACE} + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/certificate-arn: "${CERTIFICATE_ARNS}" + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/ssl-redirect: '443' + labels: + app: uni-resolver-web + managed-by: github-action +spec: + ingressClassName: alb + rules: + - host: ${DEV_DOMAIN_NAME} + http: + paths: + - path: /1.0/* + pathType: ImplementationSpecific + backend: + service: + name: uni-resolver-web + port: + number: 8080 + - path: /* + pathType: ImplementationSpecific + backend: + service: + name: uni-resolver-frontend + port: + number: 7081 + - host: ${PROD_DOMAIN_NAME} + http: + paths: + - path: /1.0/* + pathType: ImplementationSpecific + backend: + service: + name: uni-resolver-web + port: + number: 8080 + - path: /* + pathType: ImplementationSpecific + backend: + service: + name: uni-resolver-frontend + port: + number: 7081 +EOF + +echo "Generated Ingress manifest:" +cat ingress.yaml +echo "" + +# Apply Ingress +if kubectl apply -f ingress.yaml; then + echo "✓ Ingress created successfully" +else + echo "✗ Failed to create Ingress" + exit 1 +fi + +echo "" +echo "Waiting for ALB to provision (this may take 2-3 minutes)..." +echo "You can check the status with: kubectl get ingress $INGRESS_NAME -n $NAMESPACE -w" +echo "" + +# Show the created ingress +kubectl get ingress "$INGRESS_NAME" -n "$NAMESPACE" -o wide + +echo "" +echo "✓ Ingress deployment complete" +echo "" +echo "Note: The AWS Load Balancer Controller will provision an ALB." +echo "DNS records for ${DEV_DOMAIN_NAME} and ${PROD_DOMAIN_NAME}" +echo "must point to the ALB address shown above." diff --git a/ci/deploy-k8s-aws/scripts/deploy-services.sh b/ci/deploy-k8s-aws/scripts/deploy-services.sh index 058f78a22..4dd9700ed 100644 --- a/ci/deploy-k8s-aws/scripts/deploy-services.sh +++ b/ci/deploy-k8s-aws/scripts/deploy-services.sh @@ -140,29 +140,33 @@ EOF done fi - # Add environment variables from ConfigMap - if [ "$env_file" != "null" ] || [ "$env_vars" != "null" ]; then - echo " envFrom:" >> "deployment-${service_name}.yaml" - echo " - configMapRef:" >> "deployment-${service_name}.yaml" - echo " name: app-config" >> "deployment-${service_name}.yaml" - fi + # Skip environment variables for uni-resolver-web (uses defaults from application.yml) + if [ "$service_name" = "uni-resolver-web" ]; then + echo " Skipping environment variables for uni-resolver-web (uses container defaults)" + else + # Add environment variables from ConfigMap + if [ "$env_file" != "null" ] || [ "$env_vars" != "null" ]; then + echo " envFrom:" >> "deployment-${service_name}.yaml" + echo " - configMapRef:" >> "deployment-${service_name}.yaml" + echo " name: app-config" >> "deployment-${service_name}.yaml" + fi - # Add specific environment variables if defined in docker-compose - if [ "$env_vars" != "null" ] && [ "$env_vars" != "{}" ]; then - echo " env:" >> "deployment-${service_name}.yaml" + # Add specific environment variables if defined in docker-compose + if [ "$env_vars" != "null" ] && [ "$env_vars" != "{}" ]; then + echo " env:" >> "deployment-${service_name}.yaml" - # Process each environment variable - echo "$env_vars" | jq -r 'to_entries | .[]' | jq -c '.' | while read -r entry; do - key=$(echo "$entry" | jq -r '.key') - value=$(echo "$entry" | jq -r '.value // ""') + # Process each environment variable + echo "$env_vars" | jq -r 'to_entries | .[]' | jq -c '.' | while read -r entry; do + key=$(echo "$entry" | jq -r '.key') + value=$(echo "$entry" | jq -r '.value // ""') - # Check if value is a variable reference like ${VAR_NAME} - if [[ "$value" =~ ^\$\{([^}]+)\}$ ]]; then - # Extract the ConfigMap key name (remove ${ and }) - configmap_key="${BASH_REMATCH[1]}" + # Check if value is a variable reference like ${VAR_NAME} + if [[ "$value" =~ ^\$\{([^}]+)\}$ ]]; then + # Extract the ConfigMap key name (remove ${ and }) + configmap_key="${BASH_REMATCH[1]}" - # Use valueFrom to reference the ConfigMap key - cat >> "deployment-${service_name}.yaml" << EOF + # Use valueFrom to reference the ConfigMap key + cat >> "deployment-${service_name}.yaml" << EOF - name: $key valueFrom: configMapKeyRef: @@ -170,12 +174,13 @@ EOF key: $configmap_key optional: true EOF - else - # Use literal value (for non-variable-reference values) - echo " - name: $key" >> "deployment-${service_name}.yaml" - echo " value: \"$value\"" >> "deployment-${service_name}.yaml" - fi - done + else + # Use literal value (for non-variable-reference values) + echo " - name: $key" >> "deployment-${service_name}.yaml" + echo " value: \"$value\"" >> "deployment-${service_name}.yaml" + fi + done + fi fi echo "✓ Deployment manifest created: deployment-${service_name}.yaml" @@ -214,6 +219,7 @@ metadata: app: ${service_name} managed-by: github-action spec: + type: NodePort selector: app: ${service_name} ports: @@ -221,17 +227,19 @@ EOF # Add port mappings # Handles both "host:container" and "container" port formats + # In Kubernetes, Service should expose the container port, not the docker-compose host port echo "$ports" | jq -r '.[] | gsub("^\""; "") | gsub("\"$"; "")' | while read -r port_mapping; do if [[ "$port_mapping" == *:* ]]; then - # Format: "host:container" - host_port=$(echo "$port_mapping" | cut -d: -f1) + # Format: "host:container" (e.g., "8083:8081") + # Use container port for both service port and targetPort container_port=$(echo "$port_mapping" | cut -d: -f2) + service_port="$container_port" else # Format: "container" only - host_port="$port_mapping" + service_port="$port_mapping" container_port="$port_mapping" fi - echo " - port: $host_port" >> "service-${service_name}.yaml" + echo " - port: $service_port" >> "service-${service_name}.yaml" echo " targetPort: $container_port" >> "service-${service_name}.yaml" echo " protocol: TCP" >> "service-${service_name}.yaml" done @@ -301,6 +309,18 @@ cat services.json | jq -c '.' | while read -r service; do else echo "✓ Image is up to date, no action needed" fi + + # Ensure service exists if ports are defined (may be missing from previous deployments) + if [ ! -z "$ports" ] && [ "$ports" != "null" ]; then + if kubectl get service "$name" -n "$NAMESPACE" &>/dev/null; then + echo " ✓ Service exists" + else + echo " ⚠ Service missing, creating..." + create_service_yaml "$name" "$ports" + kubectl apply -f "service-${name}.yaml" + echo " ✓ Service created" + fi + fi else echo "⚡ Deployment '$name' does not exist, creating new deployment..." diff --git a/ci/deploy-k8s-aws/scripts/entrypoint.sh b/ci/deploy-k8s-aws/scripts/entrypoint.sh index 9be5c3f07..14926937b 100755 --- a/ci/deploy-k8s-aws/scripts/entrypoint.sh +++ b/ci/deploy-k8s-aws/scripts/entrypoint.sh @@ -12,8 +12,10 @@ # 3. Process environment variables and create ConfigMaps # 4. Deploy or update services (only changes are applied) # 5. Handle special cases (e.g., driver-did-btcr secrets) -# 6. Clean up orphaned resources -# 7. Verify all deployments are healthy +# 6. Deploy frontend application (uni-resolver-frontend) +# 7. Deploy Ingress (AWS ALB for routing) +# 8. Clean up orphaned resources +# 9. Verify all deployments are healthy # # Required Environment Variables: # - KUBE_CONFIG_DATA: Base64-encoded Kubernetes config @@ -257,11 +259,47 @@ fi echo "" ################################################################################ -# Step 6: Clean Up Orphaned Resources +# Step 6: Deploy Frontend Application ################################################################################ echo "====================================================================" -echo "Step 6: Cleaning up orphaned resources" +echo "Step 6: Deploying frontend application" +echo "====================================================================" + +# Execute frontend deployment script +if /scripts/deploy-frontend.sh; then + echo "✓ Frontend deployment completed" +else + echo "⚠ Warning: Frontend deployment encountered issues" + echo "Continuing with deployment..." +fi + +echo "" + +################################################################################ +# Step 7: Deploy Ingress +################################################################################ + +echo "====================================================================" +echo "Step 7: Deploying Ingress" +echo "====================================================================" + +# Execute ingress deployment script +if /scripts/deploy-ingress.sh; then + echo "✓ Ingress deployment completed" +else + echo "⚠ Warning: Ingress deployment encountered issues" + echo "Continuing with deployment..." +fi + +echo "" + +################################################################################ +# Step 8: Clean Up Orphaned Resources +################################################################################ + +echo "====================================================================" +echo "Step 8: Cleaning up orphaned resources" echo "====================================================================" # Execute cleanup script @@ -275,11 +313,11 @@ fi echo "" ################################################################################ -# Step 7: Verify Deployment Status +# Step 9: Verify Deployment Status ################################################################################ echo "====================================================================" -echo "Step 7: Verifying deployment status" +echo "Step 9: Verifying deployment status" echo "====================================================================" # Execute verification script