Initial commit - production deployment
This commit is contained in:
392
infrastructure/scripts/deployment/deploy-signoz.sh
Executable file
392
infrastructure/scripts/deployment/deploy-signoz.sh
Executable file
@@ -0,0 +1,392 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# SigNoz Deployment Script for Bakery IA
|
||||
# ============================================================================
|
||||
# This script deploys SigNoz monitoring stack using Helm
|
||||
# Supports both development and production environments
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to display help
|
||||
show_help() {
|
||||
echo "Usage: $0 [OPTIONS] ENVIRONMENT"
|
||||
echo ""
|
||||
echo "Deploy SigNoz monitoring stack for Bakery IA"
|
||||
echo ""
|
||||
echo "Arguments:
|
||||
ENVIRONMENT Environment to deploy to (dev|prod)"
|
||||
echo ""
|
||||
echo "Options:
|
||||
-h, --help Show this help message
|
||||
-d, --dry-run Dry run - show what would be done without actually deploying
|
||||
-u, --upgrade Upgrade existing deployment
|
||||
-r, --remove Remove/Uninstall SigNoz deployment
|
||||
-n, --namespace NAMESPACE Specify namespace (default: bakery-ia)"
|
||||
echo ""
|
||||
echo "Examples:
|
||||
$0 dev # Deploy to development
|
||||
$0 prod # Deploy to production
|
||||
$0 --upgrade prod # Upgrade production deployment
|
||||
$0 --remove dev # Remove development deployment"
|
||||
echo ""
|
||||
echo "Docker Hub Authentication:"
|
||||
echo " This script automatically creates a Docker Hub secret for image pulls."
|
||||
echo " Provide credentials via environment variables (recommended):"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-personal-access-token'"
|
||||
echo " Or ensure you're logged in with Docker CLI:"
|
||||
echo " docker login"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
DRY_RUN=false
|
||||
UPGRADE=false
|
||||
REMOVE=false
|
||||
NAMESPACE="bakery-ia"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-u|--upgrade)
|
||||
UPGRADE=true
|
||||
shift
|
||||
;;
|
||||
-r|--remove)
|
||||
REMOVE=true
|
||||
shift
|
||||
;;
|
||||
-n|--namespace)
|
||||
NAMESPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
dev|prod)
|
||||
ENVIRONMENT="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate environment
|
||||
if [[ -z "$ENVIRONMENT" ]]; then
|
||||
echo "Error: Environment not specified. Use 'dev' or 'prod'."
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ENVIRONMENT" != "dev" && "$ENVIRONMENT" != "prod" ]]; then
|
||||
echo "Error: Invalid environment. Use 'dev' or 'prod'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to check if Helm is installed
|
||||
check_helm() {
|
||||
if ! command -v helm &> /dev/null; then
|
||||
echo "${RED}Error: Helm is not installed. Please install Helm first.${NC}"
|
||||
echo "Installation instructions: https://helm.sh/docs/intro/install/"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check if kubectl is configured
|
||||
check_kubectl() {
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo "${RED}Error: kubectl is not configured or cannot connect to cluster.${NC}"
|
||||
echo "Please ensure you have access to a Kubernetes cluster."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check if namespace exists, create if not
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
|
||||
echo "${BLUE}Creating namespace $NAMESPACE...${NC}"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " (dry-run) Would create namespace $NAMESPACE"
|
||||
else
|
||||
kubectl create namespace "$NAMESPACE"
|
||||
echo "${GREEN}Namespace $NAMESPACE created.${NC}"
|
||||
fi
|
||||
else
|
||||
echo "${BLUE}Namespace $NAMESPACE already exists.${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create Docker Hub secret for image pulls
|
||||
create_dockerhub_secret() {
|
||||
echo "${BLUE}Setting up Docker Hub image pull secret...${NC}"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " (dry-run) Would create Docker Hub secret in namespace $NAMESPACE"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if secret already exists
|
||||
if kubectl get secret dockerhub-creds -n "$NAMESPACE" &> /dev/null; then
|
||||
echo "${GREEN}Docker Hub secret already exists in namespace $NAMESPACE.${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if Docker Hub credentials are available
|
||||
if [[ -n "$DOCKERHUB_USERNAME" ]] && [[ -n "$DOCKERHUB_PASSWORD" ]]; then
|
||||
echo "${BLUE}Found DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD environment variables${NC}"
|
||||
|
||||
kubectl create secret docker-registry dockerhub-creds \
|
||||
--docker-server=https://index.docker.io/v1/ \
|
||||
--docker-username="$DOCKERHUB_USERNAME" \
|
||||
--docker-password="$DOCKERHUB_PASSWORD" \
|
||||
--docker-email="${DOCKERHUB_EMAIL:-noreply@bakery-ia.local}" \
|
||||
-n "$NAMESPACE"
|
||||
|
||||
echo "${GREEN}Docker Hub secret created successfully.${NC}"
|
||||
|
||||
elif [[ -f "$HOME/.docker/config.json" ]]; then
|
||||
echo "${BLUE}Attempting to use Docker CLI credentials...${NC}"
|
||||
|
||||
# Try to extract credentials from Docker config
|
||||
if grep -q "credsStore" "$HOME/.docker/config.json"; then
|
||||
echo "${YELLOW}Docker is using a credential store. Please set environment variables:${NC}"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo "${YELLOW}Continuing without Docker Hub authentication...${NC}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Try to extract from base64 encoded auth
|
||||
AUTH=$(cat "$HOME/.docker/config.json" | jq -r '.auths["https://index.docker.io/v1/"].auth // empty' 2>/dev/null)
|
||||
if [[ -n "$AUTH" ]]; then
|
||||
echo "${GREEN}Found Docker Hub credentials in Docker config${NC}"
|
||||
local DOCKER_USERNAME=$(echo "$AUTH" | base64 -d | cut -d: -f1)
|
||||
local DOCKER_PASSWORD=$(echo "$AUTH" | base64 -d | cut -d: -f2-)
|
||||
|
||||
kubectl create secret docker-registry dockerhub-creds \
|
||||
--docker-server=https://index.docker.io/v1/ \
|
||||
--docker-username="$DOCKER_USERNAME" \
|
||||
--docker-password="$DOCKER_PASSWORD" \
|
||||
--docker-email="${DOCKERHUB_EMAIL:-noreply@bakery-ia.local}" \
|
||||
-n "$NAMESPACE"
|
||||
|
||||
echo "${GREEN}Docker Hub secret created successfully.${NC}"
|
||||
else
|
||||
echo "${YELLOW}Could not find Docker Hub credentials${NC}"
|
||||
echo "${YELLOW}To enable automatic Docker Hub authentication:${NC}"
|
||||
echo " 1. Run 'docker login', OR"
|
||||
echo " 2. Set environment variables:"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo "${YELLOW}Continuing without Docker Hub authentication...${NC}"
|
||||
fi
|
||||
else
|
||||
echo "${YELLOW}Docker Hub credentials not found${NC}"
|
||||
echo "${YELLOW}To enable automatic Docker Hub authentication:${NC}"
|
||||
echo " 1. Run 'docker login', OR"
|
||||
echo " 2. Set environment variables:"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo "${YELLOW}Continuing without Docker Hub authentication...${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to add and update Helm repository
|
||||
setup_helm_repo() {
|
||||
echo "${BLUE}Setting up SigNoz Helm repository...${NC}"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " (dry-run) Would add SigNoz Helm repository"
|
||||
return
|
||||
fi
|
||||
|
||||
# Add SigNoz Helm repository
|
||||
if helm repo list | grep -q "^signoz"; then
|
||||
echo "${BLUE}SigNoz repository already added, updating...${NC}"
|
||||
helm repo update signoz
|
||||
else
|
||||
echo "${BLUE}Adding SigNoz Helm repository...${NC}"
|
||||
helm repo add signoz https://charts.signoz.io
|
||||
helm repo update
|
||||
fi
|
||||
|
||||
echo "${GREEN}Helm repository ready.${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to deploy SigNoz
|
||||
deploy_signoz() {
|
||||
local values_file="infrastructure/helm/signoz-values-$ENVIRONMENT.yaml"
|
||||
|
||||
if [[ ! -f "$values_file" ]]; then
|
||||
echo "${RED}Error: Values file $values_file not found.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${BLUE}Deploying SigNoz to $ENVIRONMENT environment...${NC}"
|
||||
echo " Using values file: $values_file"
|
||||
echo " Target namespace: $NAMESPACE"
|
||||
echo " Chart version: Latest from signoz/signoz"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " (dry-run) Would deploy SigNoz with:"
|
||||
echo " helm upgrade --install signoz signoz/signoz -n $NAMESPACE -f $values_file --wait --timeout 15m"
|
||||
return
|
||||
fi
|
||||
|
||||
# Use upgrade --install to handle both new installations and upgrades
|
||||
echo "${BLUE}Installing/Upgrading SigNoz...${NC}"
|
||||
echo "This may take 10-15 minutes..."
|
||||
|
||||
helm upgrade --install signoz signoz/signoz \
|
||||
-n "$NAMESPACE" \
|
||||
-f "$values_file" \
|
||||
--wait \
|
||||
--timeout 15m \
|
||||
--create-namespace
|
||||
|
||||
echo "${GREEN}SigNoz deployment completed.${NC}"
|
||||
echo ""
|
||||
|
||||
# Show deployment status
|
||||
show_deployment_status
|
||||
}
|
||||
|
||||
# Function to remove SigNoz
|
||||
remove_signoz() {
|
||||
echo "${BLUE}Removing SigNoz deployment from namespace $NAMESPACE...${NC}"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo " (dry-run) Would remove SigNoz deployment"
|
||||
return
|
||||
fi
|
||||
|
||||
if helm list -n "$NAMESPACE" | grep -q signoz; then
|
||||
helm uninstall signoz -n "$NAMESPACE" --wait
|
||||
echo "${GREEN}SigNoz deployment removed.${NC}"
|
||||
|
||||
# Optionally remove PVCs (commented out by default for safety)
|
||||
echo ""
|
||||
echo "${YELLOW}Note: Persistent Volume Claims (PVCs) were NOT deleted.${NC}"
|
||||
echo "To delete PVCs and all data, run:"
|
||||
echo " kubectl delete pvc -n $NAMESPACE -l app.kubernetes.io/instance=signoz"
|
||||
else
|
||||
echo "${YELLOW}No SigNoz deployment found in namespace $NAMESPACE.${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to show deployment status
|
||||
show_deployment_status() {
|
||||
echo ""
|
||||
echo "${BLUE}=== SigNoz Deployment Status ===${NC}"
|
||||
echo ""
|
||||
|
||||
# Get pods
|
||||
echo "Pods:"
|
||||
kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
echo ""
|
||||
|
||||
# Get services
|
||||
echo "Services:"
|
||||
kubectl get svc -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
echo ""
|
||||
|
||||
# Get ingress
|
||||
echo "Ingress:"
|
||||
kubectl get ingress -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
echo ""
|
||||
|
||||
# Show access information
|
||||
show_access_info
|
||||
}
|
||||
|
||||
# Function to show access information
|
||||
show_access_info() {
|
||||
echo "${BLUE}=== Access Information ===${NC}"
|
||||
|
||||
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||||
echo "SigNoz UI: http://monitoring.bakery-ia.local"
|
||||
echo ""
|
||||
echo "OpenTelemetry Collector Endpoints (from within cluster):"
|
||||
echo " gRPC: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4317"
|
||||
echo " HTTP: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4318"
|
||||
echo ""
|
||||
echo "Port-forward for local access:"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz 8080:8080"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz-otel-collector 4317:4317"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz-otel-collector 4318:4318"
|
||||
else
|
||||
echo "SigNoz UI: https://monitoring.bakewise.ai"
|
||||
echo ""
|
||||
echo "OpenTelemetry Collector Endpoints (from within cluster):"
|
||||
echo " gRPC: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4317"
|
||||
echo " HTTP: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4318"
|
||||
echo ""
|
||||
echo "External endpoints (if exposed):"
|
||||
echo " Check ingress configuration for external OTLP endpoints"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Default credentials:"
|
||||
echo " Username: admin@example.com"
|
||||
echo " Password: admin"
|
||||
echo ""
|
||||
echo "Note: Change default password after first login!"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo "${BLUE}"
|
||||
echo "=========================================="
|
||||
echo "🚀 SigNoz Deployment for Bakery IA"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
|
||||
# Check prerequisites
|
||||
check_helm
|
||||
check_kubectl
|
||||
|
||||
# Ensure namespace
|
||||
ensure_namespace
|
||||
|
||||
if [[ "$REMOVE" == true ]]; then
|
||||
remove_signoz
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Setup Helm repository
|
||||
setup_helm_repo
|
||||
|
||||
# Create Docker Hub secret for image pulls
|
||||
create_dockerhub_secret
|
||||
|
||||
# Deploy SigNoz
|
||||
deploy_signoz
|
||||
|
||||
echo "${GREEN}"
|
||||
echo "=========================================="
|
||||
echo "✅ SigNoz deployment completed!"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
168
infrastructure/scripts/maintenance/apply-security-changes.sh
Executable file
168
infrastructure/scripts/maintenance/apply-security-changes.sh
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Apply all database security changes to Kubernetes cluster
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="bakery-ia"
|
||||
|
||||
echo "======================================"
|
||||
echo "Bakery IA Database Security Deployment"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "This script will apply all security changes to the cluster:"
|
||||
echo " 1. Updated passwords"
|
||||
echo " 2. TLS certificates for PostgreSQL and Redis"
|
||||
echo " 3. Updated database deployments with TLS and PVCs"
|
||||
echo " 4. PostgreSQL logging configuration"
|
||||
echo " 5. pgcrypto extension"
|
||||
echo ""
|
||||
read -p "Press Enter to continue or Ctrl+C to cancel..."
|
||||
echo ""
|
||||
|
||||
# ===== 1. Apply Secrets =====
|
||||
echo "Step 1: Applying updated secrets..."
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets/postgres-tls-secret.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets/redis-tls-secret.yaml
|
||||
echo "✓ Secrets applied"
|
||||
echo ""
|
||||
|
||||
# ===== 2. Apply ConfigMaps =====
|
||||
echo "Step 2: Applying ConfigMaps..."
|
||||
kubectl apply -f infrastructure/kubernetes/base/configs/postgres-init-config.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/configmaps/postgres-logging-config.yaml
|
||||
echo "✓ ConfigMaps applied"
|
||||
echo ""
|
||||
|
||||
# ===== 3. Apply Database Deployments =====
|
||||
echo "Step 3: Applying database deployments..."
|
||||
kubectl apply -f infrastructure/kubernetes/base/components/databases/
|
||||
echo "✓ Database deployments applied"
|
||||
echo ""
|
||||
|
||||
# ===== 4. Wait for Rollout =====
|
||||
echo "Step 4: Waiting for database pods to be ready..."
|
||||
|
||||
DBS=(
|
||||
"auth-db"
|
||||
"tenant-db"
|
||||
"training-db"
|
||||
"forecasting-db"
|
||||
"sales-db"
|
||||
"external-db"
|
||||
"notification-db"
|
||||
"inventory-db"
|
||||
"recipes-db"
|
||||
"suppliers-db"
|
||||
"pos-db"
|
||||
"orders-db"
|
||||
"production-db"
|
||||
"alert-processor-db"
|
||||
"redis"
|
||||
)
|
||||
|
||||
for db in "${DBS[@]}"; do
|
||||
echo " Waiting for $db..."
|
||||
kubectl rollout status deployment/$db -n $NAMESPACE --timeout=5m || echo " ⚠️ Warning: $db rollout may have issues"
|
||||
done
|
||||
|
||||
echo "✓ All deployments rolled out"
|
||||
echo ""
|
||||
|
||||
# ===== 5. Verify PVCs =====
|
||||
echo "Step 5: Verifying PersistentVolumeClaims..."
|
||||
kubectl get pvc -n $NAMESPACE
|
||||
echo ""
|
||||
|
||||
# ===== 6. Test Database Connections =====
|
||||
echo "Step 6: Testing database connectivity..."
|
||||
|
||||
# Test PostgreSQL with TLS
|
||||
echo " Testing PostgreSQL (auth-db) with TLS..."
|
||||
AUTH_POD=$(kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=auth-db -o jsonpath='{.items[0].metadata.name}')
|
||||
if [ -n "$AUTH_POD" ]; then
|
||||
kubectl exec -n $NAMESPACE "$AUTH_POD" -- \
|
||||
sh -c 'psql -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT version();"' > /dev/null 2>&1 && \
|
||||
echo " ✓ PostgreSQL connection successful" || \
|
||||
echo " ⚠️ PostgreSQL connection test failed"
|
||||
else
|
||||
echo " ⚠️ auth-db pod not found"
|
||||
fi
|
||||
|
||||
# Test Redis with TLS
|
||||
echo " Testing Redis with TLS..."
|
||||
REDIS_POD=$(kubectl get pods -n $NAMESPACE -l app.kubernetes.io/name=redis -o jsonpath='{.items[0].metadata.name}')
|
||||
if [ -n "$REDIS_POD" ]; then
|
||||
kubectl exec -n $NAMESPACE "$REDIS_POD" -- \
|
||||
redis-cli -a $(kubectl get secret redis-secrets -n $NAMESPACE -o jsonpath='{.data.REDIS_PASSWORD}' | base64 -d) \
|
||||
--tls --cert /tls/redis-cert.pem --key /tls/redis-key.pem --cacert /tls/ca-cert.pem \
|
||||
PING > /dev/null 2>&1 && \
|
||||
echo " ✓ Redis TLS connection successful" || \
|
||||
echo " ⚠️ Redis TLS connection test failed (may need to restart services)"
|
||||
else
|
||||
echo " ⚠️ Redis pod not found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ===== 7. Verify TLS Certificates =====
|
||||
echo "Step 7: Verifying TLS certificates are mounted..."
|
||||
|
||||
echo " Checking PostgreSQL TLS certs..."
|
||||
if [ -n "$AUTH_POD" ]; then
|
||||
kubectl exec -n $NAMESPACE "$AUTH_POD" -- ls -la /tls/ 2>/dev/null && \
|
||||
echo " ✓ PostgreSQL TLS certificates mounted" || \
|
||||
echo " ⚠️ PostgreSQL TLS certificates not found"
|
||||
fi
|
||||
|
||||
echo " Checking Redis TLS certs..."
|
||||
if [ -n "$REDIS_POD" ]; then
|
||||
kubectl exec -n $NAMESPACE "$REDIS_POD" -- ls -la /tls/ 2>/dev/null && \
|
||||
echo " ✓ Redis TLS certificates mounted" || \
|
||||
echo " ⚠️ Redis TLS certificates not found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ===== 8. Display Summary =====
|
||||
echo "======================================"
|
||||
echo "Deployment Summary"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Database Pods:"
|
||||
kubectl get pods -n $NAMESPACE -l app.kubernetes.io/component=database
|
||||
echo ""
|
||||
echo "PersistentVolumeClaims:"
|
||||
kubectl get pvc -n $NAMESPACE | grep -E "NAME|db-pvc"
|
||||
echo ""
|
||||
echo "Secrets:"
|
||||
kubectl get secrets -n $NAMESPACE | grep -E "NAME|database-secrets|redis-secrets|postgres-tls|redis-tls"
|
||||
echo ""
|
||||
|
||||
echo "======================================"
|
||||
echo "✓ Security Deployment Complete!"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Security improvements applied:"
|
||||
echo " ✅ Strong 32-character passwords for all databases"
|
||||
echo " ✅ TLS encryption for PostgreSQL connections"
|
||||
echo " ✅ TLS encryption for Redis connections"
|
||||
echo " ✅ Persistent storage (PVCs) for all databases"
|
||||
echo " ✅ pgcrypto extension enabled for column-level encryption"
|
||||
echo " ✅ PostgreSQL audit logging configured"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Restart all services to pick up new database URLs with TLS"
|
||||
echo " 2. Monitor logs for any connection issues"
|
||||
echo " 3. Test application functionality end-to-end"
|
||||
echo " 4. Review PostgreSQL logs: kubectl logs -n $NAMESPACE <db-pod>"
|
||||
echo ""
|
||||
echo "To create encrypted backups, run:"
|
||||
echo " ./scripts/encrypted-backup.sh"
|
||||
echo ""
|
||||
echo "To enable Kubernetes secrets encryption (requires cluster recreate):"
|
||||
echo " kind delete cluster --name bakery-ia-local"
|
||||
echo " kind create cluster --config kind-config.yaml"
|
||||
echo " kubectl apply -f infrastructure/kubernetes/base/namespace.yaml"
|
||||
echo " ./scripts/apply-security-changes.sh"
|
||||
161
infrastructure/scripts/maintenance/backup-databases.sh
Executable file
161
infrastructure/scripts/maintenance/backup-databases.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Database Backup Script for Bakery IA
|
||||
# This script backs up all PostgreSQL databases in the Kubernetes cluster
|
||||
# Designed to run on the VPS via cron
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
BACKUP_ROOT="/backups"
|
||||
NAMESPACE="bakery-ia"
|
||||
RETENTION_DAYS=7
|
||||
DATE=$(date +%Y-%m-%d_%H-%M-%S)
|
||||
BACKUP_DIR="${BACKUP_ROOT}/${DATE}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Logging
|
||||
log() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] SUCCESS: $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
log "Starting database backup to $BACKUP_DIR"
|
||||
|
||||
# Get all database pods
|
||||
DB_PODS=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/component=database -o jsonpath='{.items[*].metadata.name}')
|
||||
|
||||
if [ -z "$DB_PODS" ]; then
|
||||
log_error "No database pods found in namespace $NAMESPACE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Found database pods: $DB_PODS"
|
||||
|
||||
# Backup counter
|
||||
SUCCESS_COUNT=0
|
||||
FAILED_COUNT=0
|
||||
FAILED_DBS=()
|
||||
|
||||
# Backup each database
|
||||
for pod in $DB_PODS; do
|
||||
log "Backing up database: $pod"
|
||||
|
||||
# Get database name from pod labels
|
||||
DB_NAME=$(kubectl get pod "$pod" -n "$NAMESPACE" -o jsonpath='{.metadata.labels.app\.kubernetes\.io/name}')
|
||||
|
||||
if [ -z "$DB_NAME" ]; then
|
||||
DB_NAME=$pod
|
||||
fi
|
||||
|
||||
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}.sql"
|
||||
|
||||
# Perform backup
|
||||
if kubectl exec -n "$NAMESPACE" "$pod" -- pg_dumpall -U postgres > "$BACKUP_FILE" 2>/dev/null; then
|
||||
FILE_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
log_success "Backed up $DB_NAME ($FILE_SIZE)"
|
||||
((SUCCESS_COUNT++))
|
||||
else
|
||||
log_error "Failed to backup $DB_NAME"
|
||||
FAILED_DBS+=("$DB_NAME")
|
||||
((FAILED_COUNT++))
|
||||
rm -f "$BACKUP_FILE" # Remove partial backup
|
||||
fi
|
||||
done
|
||||
|
||||
# Also backup Redis if present
|
||||
REDIS_POD=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/name=redis -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$REDIS_POD" ]; then
|
||||
log "Backing up Redis: $REDIS_POD"
|
||||
REDIS_BACKUP="${BACKUP_DIR}/redis.rdb"
|
||||
|
||||
if kubectl exec -n "$NAMESPACE" "$REDIS_POD" -- redis-cli --rdb /tmp/dump.rdb SAVE > /dev/null 2>&1 && \
|
||||
kubectl cp "$NAMESPACE/$REDIS_POD:/tmp/dump.rdb" "$REDIS_BACKUP" > /dev/null 2>&1; then
|
||||
FILE_SIZE=$(du -h "$REDIS_BACKUP" | cut -f1)
|
||||
log_success "Backed up Redis ($FILE_SIZE)"
|
||||
((SUCCESS_COUNT++))
|
||||
else
|
||||
log_warning "Failed to backup Redis (non-critical)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create backup metadata
|
||||
cat > "${BACKUP_DIR}/backup-info.txt" <<EOF
|
||||
Backup Date: $(date)
|
||||
Namespace: $NAMESPACE
|
||||
Success Count: $SUCCESS_COUNT
|
||||
Failed Count: $FAILED_COUNT
|
||||
Failed Databases: ${FAILED_DBS[@]:-none}
|
||||
Kubernetes Cluster: $(kubectl config current-context)
|
||||
EOF
|
||||
|
||||
# Compress backup
|
||||
log "Compressing backup..."
|
||||
COMPRESSED_FILE="${BACKUP_ROOT}/backup-${DATE}.tar.gz"
|
||||
|
||||
if tar -czf "$COMPRESSED_FILE" -C "$BACKUP_ROOT" "$(basename "$BACKUP_DIR")"; then
|
||||
COMPRESSED_SIZE=$(du -h "$COMPRESSED_FILE" | cut -f1)
|
||||
log_success "Backup compressed: $COMPRESSED_FILE ($COMPRESSED_SIZE)"
|
||||
|
||||
# Remove uncompressed backup
|
||||
rm -rf "$BACKUP_DIR"
|
||||
else
|
||||
log_error "Failed to compress backup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup old backups
|
||||
log "Cleaning up backups older than $RETENTION_DAYS days..."
|
||||
DELETED_COUNT=$(find "$BACKUP_ROOT" -name "backup-*.tar.gz" -mtime "+$RETENTION_DAYS" -delete -print | wc -l)
|
||||
|
||||
if [ "$DELETED_COUNT" -gt 0 ]; then
|
||||
log "Deleted $DELETED_COUNT old backup(s)"
|
||||
fi
|
||||
|
||||
# Calculate total backup size
|
||||
TOTAL_SIZE=$(du -sh "$BACKUP_ROOT" | cut -f1)
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log "========================================="
|
||||
log "Backup Summary"
|
||||
log "========================================="
|
||||
log "Successful backups: $SUCCESS_COUNT"
|
||||
log "Failed backups: $FAILED_COUNT"
|
||||
log "Backup location: $COMPRESSED_FILE"
|
||||
log "Backup size: $COMPRESSED_SIZE"
|
||||
log "Total backup storage: $TOTAL_SIZE"
|
||||
log "========================================="
|
||||
|
||||
if [ $FAILED_COUNT -gt 0 ]; then
|
||||
log_error "Some backups failed: ${FAILED_DBS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Backup completed successfully!"
|
||||
|
||||
# Optional: Send notification
|
||||
# Uncomment and configure if you want email/slack notifications
|
||||
# send_notification "Bakery IA Backup Completed" "Successfully backed up $SUCCESS_COUNT databases. Size: $COMPRESSED_SIZE"
|
||||
|
||||
exit 0
|
||||
82
infrastructure/scripts/maintenance/cleanup-docker.sh
Executable file
82
infrastructure/scripts/maintenance/cleanup-docker.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Docker Cleanup Script for Local Kubernetes Development
|
||||
# This script helps prevent disk space issues by cleaning up unused Docker resources
|
||||
|
||||
set -e
|
||||
|
||||
echo "🧹 Docker Cleanup Script for Bakery-IA Local Development"
|
||||
echo "========================================================="
|
||||
echo ""
|
||||
|
||||
# Check if we should run automatically or ask for confirmation
|
||||
AUTO_MODE=${1:-""}
|
||||
|
||||
# Show current disk usage
|
||||
echo "📊 Current Docker Disk Usage:"
|
||||
docker system df
|
||||
echo ""
|
||||
|
||||
# Check Kind node disk usage if cluster is running
|
||||
if docker ps | grep -q "bakery-ia-local-control-plane"; then
|
||||
echo "📊 Kind Node Disk Usage:"
|
||||
docker exec bakery-ia-local-control-plane df -h / /var | grep -E "(Filesystem|overlay|/dev/vdb1)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Calculate reclaimable space
|
||||
RECLAIMABLE=$(docker system df | grep "Images" | awk '{print $4}')
|
||||
echo "💾 Estimated reclaimable space: $RECLAIMABLE"
|
||||
echo ""
|
||||
|
||||
# Ask for confirmation unless in auto mode
|
||||
if [ "$AUTO_MODE" != "--auto" ]; then
|
||||
read -p "Do you want to proceed with cleanup? (y/n) " -n 1 -r
|
||||
echo ""
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "❌ Cleanup cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🚀 Starting cleanup..."
|
||||
echo ""
|
||||
|
||||
# Remove unused images (keep images from last 24 hours)
|
||||
echo "1️⃣ Removing unused Docker images..."
|
||||
docker image prune -af --filter "until=24h" || true
|
||||
echo ""
|
||||
|
||||
# Remove unused volumes
|
||||
echo "2️⃣ Removing unused Docker volumes..."
|
||||
docker volume prune -f || true
|
||||
echo ""
|
||||
|
||||
# Remove build cache
|
||||
echo "3️⃣ Removing build cache..."
|
||||
docker builder prune -af || true
|
||||
echo ""
|
||||
|
||||
# Show results
|
||||
echo "✅ Cleanup completed!"
|
||||
echo ""
|
||||
echo "📊 Final Docker Disk Usage:"
|
||||
docker system df
|
||||
echo ""
|
||||
|
||||
# Check Kind node disk usage if cluster is running
|
||||
if docker ps | grep -q "bakery-ia-local-control-plane"; then
|
||||
echo "📊 Kind Node Disk Usage After Cleanup:"
|
||||
docker exec bakery-ia-local-control-plane df -h / /var | grep -E "(Filesystem|overlay|/dev/vdb1)"
|
||||
echo ""
|
||||
|
||||
# Warn if still above 80%
|
||||
USAGE=$(docker exec bakery-ia-local-control-plane df -h /var | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||
if [ "$USAGE" -gt 80 ]; then
|
||||
echo "⚠️ Warning: Disk usage is still above 80%. Consider:"
|
||||
echo " - Deleting and recreating the Kind cluster"
|
||||
echo " - Increasing Docker's disk allocation"
|
||||
echo " - Running: docker system prune -a --volumes -f"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🎉 All done!"
|
||||
203
infrastructure/scripts/maintenance/cleanup_databases_k8s.sh
Executable file
203
infrastructure/scripts/maintenance/cleanup_databases_k8s.sh
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Helper script to manually clean all service databases in Kubernetes
|
||||
# This ensures databases are in a clean state before running migration generation
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="${KUBE_NAMESPACE:-bakery-ia}"
|
||||
|
||||
# Parse command line arguments
|
||||
CONFIRM=false
|
||||
SPECIFIC_SERVICE=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--namespace) NAMESPACE="$2"; shift 2 ;;
|
||||
--service) SPECIFIC_SERVICE="$2"; shift 2 ;;
|
||||
--yes) CONFIRM=true; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --namespace NAME Use specific Kubernetes namespace (default: bakery-ia)"
|
||||
echo " --service NAME Clean only specific service database"
|
||||
echo " --yes Skip confirmation prompt"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Clean all databases (with confirmation)"
|
||||
echo " $0 --service auth --yes # Clean only auth database without confirmation"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1"; echo "Use --help for usage information"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# List of all services
|
||||
SERVICES=(
|
||||
"pos" "sales" "recipes" "training" "auth" "orders" "inventory"
|
||||
"suppliers" "tenant" "notification" "alert-processor" "forecasting"
|
||||
"external" "production"
|
||||
)
|
||||
|
||||
# If specific service is provided, use only that
|
||||
if [ -n "$SPECIFIC_SERVICE" ]; then
|
||||
SERVICES=("$SPECIFIC_SERVICE")
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Database Cleanup Script (K8s)${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${RED}⚠ WARNING: This will DROP ALL TABLES in the following databases:${NC}"
|
||||
for service in "${SERVICES[@]}"; do
|
||||
echo -e " - ${service}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [ "$CONFIRM" = false ]; then
|
||||
read -p "Are you sure you want to continue? (yes/no) " -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^yes$ ]]; then
|
||||
echo -e "${YELLOW}Aborted.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
SUCCESS_COUNT=0
|
||||
FAILED_COUNT=0
|
||||
FAILED_SERVICES=()
|
||||
|
||||
# Function to get a running pod for a service
|
||||
get_running_pod() {
|
||||
local service=$1
|
||||
local pod_name=""
|
||||
local selectors=(
|
||||
"app.kubernetes.io/name=${service}-service,app.kubernetes.io/component=microservice"
|
||||
"app.kubernetes.io/name=${service}-service,app.kubernetes.io/component=worker"
|
||||
"app.kubernetes.io/name=${service}-service"
|
||||
)
|
||||
|
||||
for selector in "${selectors[@]}"; do
|
||||
pod_name=$(kubectl get pods -n "$NAMESPACE" -l "$selector" --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
|
||||
if [ -n "$pod_name" ]; then
|
||||
echo "$pod_name"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Starting database cleanup...${NC}"
|
||||
echo ""
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
echo -e "${BLUE}----------------------------------------${NC}"
|
||||
echo -e "${BLUE}Cleaning: $service${NC}"
|
||||
echo -e "${BLUE}----------------------------------------${NC}"
|
||||
|
||||
# Find pod
|
||||
POD_NAME=$(get_running_pod "$service")
|
||||
if [ -z "$POD_NAME" ]; then
|
||||
echo -e "${RED}✗ No running pod found, skipping...${NC}"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (pod not found)")
|
||||
continue
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Found pod: $POD_NAME${NC}"
|
||||
|
||||
# Get database URL environment variable name
|
||||
db_env_var=$(echo "$service" | tr '[:lower:]-' '[:upper:]_')_DATABASE_URL
|
||||
CONTAINER="${service}-service"
|
||||
|
||||
# Drop all tables
|
||||
CLEANUP_RESULT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 << 'EOFPYTHON'
|
||||
import asyncio
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
async def cleanup_database():
|
||||
try:
|
||||
engine = create_async_engine(os.getenv('$db_env_var'))
|
||||
|
||||
# Get list of tables before cleanup
|
||||
async with engine.connect() as conn:
|
||||
result = await conn.execute(text(\"\"\"
|
||||
SELECT COUNT(*)
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
\"\"\"))
|
||||
table_count_before = result.scalar()
|
||||
|
||||
# Drop and recreate public schema
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text('DROP SCHEMA IF EXISTS public CASCADE'))
|
||||
await conn.execute(text('CREATE SCHEMA public'))
|
||||
await conn.execute(text('GRANT ALL ON SCHEMA public TO PUBLIC'))
|
||||
|
||||
# Verify cleanup
|
||||
async with engine.connect() as conn:
|
||||
result = await conn.execute(text(\"\"\"
|
||||
SELECT COUNT(*)
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
\"\"\"))
|
||||
table_count_after = result.scalar()
|
||||
|
||||
await engine.dispose()
|
||||
print(f'SUCCESS: Dropped {table_count_before} tables, {table_count_after} remaining')
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f'ERROR: {str(e)}')
|
||||
return 1
|
||||
|
||||
exit(asyncio.run(cleanup_database()))
|
||||
EOFPYTHON
|
||||
" 2>&1)
|
||||
|
||||
if echo "$CLEANUP_RESULT" | grep -q "SUCCESS"; then
|
||||
echo -e "${GREEN}✓ Database cleaned successfully${NC}"
|
||||
echo -e "${BLUE} $CLEANUP_RESULT${NC}"
|
||||
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
|
||||
else
|
||||
echo -e "${RED}✗ Database cleanup failed${NC}"
|
||||
echo -e "${YELLOW}Error details: $CLEANUP_RESULT${NC}"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (cleanup failed)")
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Summary${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${GREEN}✓ Successfully cleaned: $SUCCESS_COUNT databases${NC}"
|
||||
echo -e "${RED}✗ Failed: $FAILED_COUNT databases${NC}"
|
||||
|
||||
if [ "$FAILED_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo -e "${RED}Failed services:${NC}"
|
||||
for failed_service in "${FAILED_SERVICES[@]}"; do
|
||||
echo -e "${RED} - $failed_service${NC}"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||||
echo -e "${GREEN}Databases are now clean and ready for migration generation!${NC}"
|
||||
echo -e "${YELLOW}Next step: Run ./regenerate_migrations_k8s.sh${NC}"
|
||||
fi
|
||||
190
infrastructure/scripts/maintenance/deploy-production.sh
Executable file
190
infrastructure/scripts/maintenance/deploy-production.sh
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Production Deployment Script for MicroK8s
|
||||
# This script helps deploy Bakery IA to a MicroK8s cluster
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}Bakery IA - Production Deployment${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="bakery-ia"
|
||||
KUSTOMIZE_PATH="infrastructure/environments/prod/k8s-manifests"
|
||||
|
||||
# Check if kubectl is available
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}Error: kubectl not found. Please install kubectl or setup microk8s alias.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to check if cluster is accessible
|
||||
check_cluster() {
|
||||
echo -e "${YELLOW}Checking cluster connectivity...${NC}"
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}Error: Cannot connect to Kubernetes cluster.${NC}"
|
||||
echo "Please ensure your kubeconfig is set correctly."
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Cluster connection successful${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to check required addons
|
||||
check_addons() {
|
||||
echo -e "${YELLOW}Checking required MicroK8s addons...${NC}"
|
||||
|
||||
# Check if this is MicroK8s
|
||||
if command -v microk8s &> /dev/null; then
|
||||
REQUIRED_ADDONS=("dns" "hostpath-storage" "ingress" "cert-manager" "metrics-server")
|
||||
|
||||
for addon in "${REQUIRED_ADDONS[@]}"; do
|
||||
if microk8s status | grep -q "$addon: enabled"; then
|
||||
echo -e "${GREEN}✓ $addon enabled${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $addon not enabled${NC}"
|
||||
echo -e "${YELLOW}Enable with: microk8s enable $addon${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${YELLOW}Not running on MicroK8s. Skipping addon check.${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to create namespace
|
||||
create_namespace() {
|
||||
echo -e "${YELLOW}Creating namespace...${NC}"
|
||||
if kubectl get namespace $NAMESPACE &> /dev/null; then
|
||||
echo -e "${GREEN}✓ Namespace $NAMESPACE already exists${NC}"
|
||||
else
|
||||
kubectl create namespace $NAMESPACE
|
||||
echo -e "${GREEN}✓ Namespace $NAMESPACE created${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to apply secrets
|
||||
apply_secrets() {
|
||||
echo -e "${YELLOW}Applying secrets...${NC}"
|
||||
echo -e "${RED}WARNING: Ensure production secrets are updated before deployment!${NC}"
|
||||
read -p "Have you updated production secrets? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo -e "${RED}Deployment cancelled. Please update secrets first.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets/postgres-tls-secret.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets/redis-tls-secret.yaml
|
||||
kubectl apply -f infrastructure/kubernetes/base/secrets/demo-internal-api-key-secret.yaml
|
||||
echo -e "${GREEN}✓ Secrets applied${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to apply kustomization
|
||||
deploy_application() {
|
||||
echo -e "${YELLOW}Deploying application...${NC}"
|
||||
kubectl apply -k $KUSTOMIZE_PATH
|
||||
echo -e "${GREEN}✓ Application deployed${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to wait for deployments
|
||||
wait_for_deployments() {
|
||||
echo -e "${YELLOW}Waiting for deployments to be ready...${NC}"
|
||||
echo "This may take several minutes..."
|
||||
|
||||
# Wait for all deployments
|
||||
kubectl wait --for=condition=available --timeout=600s \
|
||||
deployment --all -n $NAMESPACE
|
||||
|
||||
echo -e "${GREEN}✓ All deployments are ready${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to check deployment status
|
||||
check_status() {
|
||||
echo -e "${YELLOW}Deployment Status:${NC}"
|
||||
echo ""
|
||||
|
||||
echo "Pods:"
|
||||
kubectl get pods -n $NAMESPACE
|
||||
echo ""
|
||||
|
||||
echo "Services:"
|
||||
kubectl get svc -n $NAMESPACE
|
||||
echo ""
|
||||
|
||||
echo "Ingress:"
|
||||
kubectl get ingress -n $NAMESPACE
|
||||
echo ""
|
||||
|
||||
echo "Persistent Volume Claims:"
|
||||
kubectl get pvc -n $NAMESPACE
|
||||
echo ""
|
||||
|
||||
echo "Certificates:"
|
||||
kubectl get certificate -n $NAMESPACE
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to show access information
|
||||
show_access_info() {
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}Deployment Complete!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo "Access your application at:"
|
||||
|
||||
# Get ingress hosts
|
||||
HOSTS=$(kubectl get ingress bakery-ingress-prod -n $NAMESPACE -o jsonpath='{.spec.rules[*].host}' 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$HOSTS" ]; then
|
||||
for host in $HOSTS; do
|
||||
echo " https://$host"
|
||||
done
|
||||
else
|
||||
echo " Configure your domain in prod-ingress.yaml"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " View logs: kubectl logs -f deployment/gateway -n $NAMESPACE"
|
||||
echo " Check pods: kubectl get pods -n $NAMESPACE"
|
||||
echo " Check events: kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp'"
|
||||
echo " Scale: kubectl scale deployment/gateway --replicas=5 -n $NAMESPACE"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main deployment flow
|
||||
main() {
|
||||
check_cluster
|
||||
check_addons
|
||||
create_namespace
|
||||
apply_secrets
|
||||
deploy_application
|
||||
|
||||
echo -e "${YELLOW}Do you want to wait for deployments to be ready? (yes/no):${NC}"
|
||||
read -p "> " wait_confirm
|
||||
|
||||
if [ "$wait_confirm" = "yes" ]; then
|
||||
wait_for_deployments
|
||||
fi
|
||||
|
||||
check_status
|
||||
show_access_info
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
82
infrastructure/scripts/maintenance/encrypted-backup.sh
Executable file
82
infrastructure/scripts/maintenance/encrypted-backup.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Encrypted PostgreSQL Backup Script
|
||||
# Creates GPG-encrypted backups of all databases
|
||||
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-/backups}"
|
||||
BACKUP_DATE=$(date +%Y%m%d-%H%M%S)
|
||||
GPG_RECIPIENT="${GPG_RECIPIENT:-backup@bakery-ia.com}"
|
||||
NAMESPACE="${NAMESPACE:-bakery-ia}"
|
||||
|
||||
# Database list
|
||||
DATABASES=(
|
||||
"auth-db"
|
||||
"tenant-db"
|
||||
"training-db"
|
||||
"forecasting-db"
|
||||
"sales-db"
|
||||
"external-db"
|
||||
"notification-db"
|
||||
"inventory-db"
|
||||
"recipes-db"
|
||||
"suppliers-db"
|
||||
"pos-db"
|
||||
"orders-db"
|
||||
"production-db"
|
||||
"alert-processor-db"
|
||||
)
|
||||
|
||||
echo "Starting encrypted backup process..."
|
||||
echo "Backup date: $BACKUP_DATE"
|
||||
echo "Backup directory: $BACKUP_DIR"
|
||||
echo "Namespace: $NAMESPACE"
|
||||
echo ""
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
for db in "${DATABASES[@]}"; do
|
||||
echo "Backing up $db..."
|
||||
|
||||
# Get pod name
|
||||
POD=$(kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=$db" -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
if [ -z "$POD" ]; then
|
||||
echo " ⚠️ Warning: Pod not found for $db, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract database name from environment
|
||||
DB_NAME=$(kubectl exec -n "$NAMESPACE" "$POD" -- sh -c 'echo $POSTGRES_DB')
|
||||
DB_USER=$(kubectl exec -n "$NAMESPACE" "$POD" -- sh -c 'echo $POSTGRES_USER')
|
||||
|
||||
# Create backup file name
|
||||
BACKUP_FILE="$BACKUP_DIR/${db}_${DB_NAME}_${BACKUP_DATE}.sql.gz.gpg"
|
||||
|
||||
# Perform backup with pg_dump, compress with gzip, encrypt with GPG
|
||||
kubectl exec -n "$NAMESPACE" "$POD" -- \
|
||||
sh -c "pg_dump -U $DB_USER -d $DB_NAME" | \
|
||||
gzip | \
|
||||
gpg --encrypt --recipient "$GPG_RECIPIENT" --trust-model always > "$BACKUP_FILE"
|
||||
|
||||
# Get file size
|
||||
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
|
||||
echo " ✓ Backup complete: $BACKUP_FILE ($SIZE)"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "===================="
|
||||
echo "✓ Backup process completed!"
|
||||
echo ""
|
||||
echo "Total backups created: ${#DATABASES[@]}"
|
||||
echo "Backup location: $BACKUP_DIR"
|
||||
echo "Backup date: $BACKUP_DATE"
|
||||
echo ""
|
||||
echo "To decrypt a backup:"
|
||||
echo " gpg --decrypt backup_file.sql.gz.gpg | gunzip > backup.sql"
|
||||
echo ""
|
||||
echo "To restore a backup:"
|
||||
echo " gpg --decrypt backup_file.sql.gz.gpg | gunzip | psql -U user -d database"
|
||||
60
infrastructure/scripts/maintenance/fix-otel-endpoints.sh
Executable file
60
infrastructure/scripts/maintenance/fix-otel-endpoints.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fix OTEL endpoint configuration in all service manifests
|
||||
# This script replaces hardcoded OTEL_EXPORTER_OTLP_ENDPOINT values
|
||||
# with references to the central bakery-config ConfigMap
|
||||
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}Fixing OTEL endpoint configuration in all services...${NC}"
|
||||
echo ""
|
||||
|
||||
# Find all service YAML files
|
||||
SERVICE_FILES=$(find infrastructure/kubernetes/base/components -name "*-service.yaml")
|
||||
|
||||
for file in $SERVICE_FILES; do
|
||||
# Check if file contains hardcoded OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
if grep -q "name: OTEL_EXPORTER_OTLP_ENDPOINT" "$file"; then
|
||||
# Check if it's already using configMapKeyRef
|
||||
if grep -A 3 "name: OTEL_EXPORTER_OTLP_ENDPOINT" "$file" | grep -q "configMapKeyRef"; then
|
||||
echo -e "${GREEN}✓ $file already using ConfigMap${NC}"
|
||||
else
|
||||
echo -e "${BLUE}→ Fixing $file${NC}"
|
||||
|
||||
# Create a temporary file
|
||||
tmp_file=$(mktemp)
|
||||
|
||||
# Process the file
|
||||
awk '
|
||||
/name: OTEL_EXPORTER_OTLP_ENDPOINT/ {
|
||||
print $0
|
||||
# Read and skip the next line (value line)
|
||||
getline
|
||||
# Output the configMapKeyRef instead
|
||||
print " valueFrom:"
|
||||
print " configMapKeyRef:"
|
||||
print " name: bakery-config"
|
||||
print " key: OTEL_EXPORTER_OTLP_ENDPOINT"
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$file" > "$tmp_file"
|
||||
|
||||
# Replace original file
|
||||
mv "$tmp_file" "$file"
|
||||
echo -e "${GREEN} ✓ Fixed${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ All service files processed!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Review changes: git diff infrastructure/kubernetes/base/components"
|
||||
echo "2. Apply changes: kubectl apply -k infrastructure/environments/dev/k8s-manifests"
|
||||
echo "3. Restart services: kubectl rollout restart deployment -n bakery-ia --all"
|
||||
58
infrastructure/scripts/maintenance/generate-passwords.sh
Executable file
58
infrastructure/scripts/maintenance/generate-passwords.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Script to generate cryptographically secure passwords for all databases
|
||||
# Generates 32-character random passwords using openssl
|
||||
|
||||
set -e
|
||||
|
||||
echo "Generating secure passwords for all databases..."
|
||||
echo ""
|
||||
|
||||
# Generate password function
|
||||
generate_password() {
|
||||
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||
}
|
||||
|
||||
# Generate passwords for all services
|
||||
SERVICES=(
|
||||
"AUTH_DB_PASSWORD"
|
||||
"TRAINING_DB_PASSWORD"
|
||||
"FORECASTING_DB_PASSWORD"
|
||||
"SALES_DB_PASSWORD"
|
||||
"EXTERNAL_DB_PASSWORD"
|
||||
"TENANT_DB_PASSWORD"
|
||||
"NOTIFICATION_DB_PASSWORD"
|
||||
"ALERT_PROCESSOR_DB_PASSWORD"
|
||||
"INVENTORY_DB_PASSWORD"
|
||||
"RECIPES_DB_PASSWORD"
|
||||
"SUPPLIERS_DB_PASSWORD"
|
||||
"POS_DB_PASSWORD"
|
||||
"ORDERS_DB_PASSWORD"
|
||||
"PRODUCTION_DB_PASSWORD"
|
||||
"REDIS_PASSWORD"
|
||||
)
|
||||
|
||||
echo "Generated Passwords:"
|
||||
echo "===================="
|
||||
echo ""
|
||||
|
||||
count=0
|
||||
for service in "${SERVICES[@]}"; do
|
||||
password=$(generate_password)
|
||||
echo "$service=$password"
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "===================="
|
||||
echo ""
|
||||
echo "Passwords generated successfully!"
|
||||
echo "Total: $count passwords"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Update .env file with these passwords"
|
||||
echo "2. Update infrastructure/kubernetes/base/secrets.yaml with base64-encoded passwords"
|
||||
echo "3. Apply new secrets to Kubernetes cluster"
|
||||
echo ""
|
||||
echo "To base64 encode a password:"
|
||||
echo " echo -n 'password' | base64"
|
||||
141
infrastructure/scripts/maintenance/generate-test-traffic.sh
Executable file
141
infrastructure/scripts/maintenance/generate-test-traffic.sh
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Generate Test Traffic to Services
|
||||
# This script generates API calls to verify telemetry data collection
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="bakery-ia"
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE} Generating Test Traffic for SigNoz Verification${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if ingress is accessible
|
||||
echo -e "${BLUE}Step 1: Verifying Gateway Access${NC}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
GATEWAY_POD=$(kubectl get pods -n $NAMESPACE -l app=gateway --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
|
||||
if [[ -z "$GATEWAY_POD" ]]; then
|
||||
echo -e "${YELLOW}⚠ Gateway pod not running. Starting port-forward...${NC}"
|
||||
# Port forward in background
|
||||
kubectl port-forward -n $NAMESPACE svc/gateway-service 8000:8000 &
|
||||
PORT_FORWARD_PID=$!
|
||||
sleep 3
|
||||
API_URL="http://localhost:8000"
|
||||
else
|
||||
echo -e "${GREEN}✓ Gateway is running: $GATEWAY_POD${NC}"
|
||||
# Use internal service
|
||||
API_URL="http://gateway-service.$NAMESPACE.svc.cluster.local:8000"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Function to make API call from inside cluster
|
||||
make_request() {
|
||||
local endpoint=$1
|
||||
local description=$2
|
||||
|
||||
echo -e "${BLUE}→ Testing: $description${NC}"
|
||||
echo " Endpoint: $endpoint"
|
||||
|
||||
if [[ -n "$GATEWAY_POD" ]]; then
|
||||
# Make request from inside the gateway pod
|
||||
RESPONSE=$(kubectl exec -n $NAMESPACE $GATEWAY_POD -- curl -s -w "\nHTTP_CODE:%{http_code}" "$API_URL$endpoint" 2>/dev/null || echo "FAILED")
|
||||
else
|
||||
# Make request from localhost
|
||||
RESPONSE=$(curl -s -w "\nHTTP_CODE:%{http_code}" "$API_URL$endpoint" 2>/dev/null || echo "FAILED")
|
||||
fi
|
||||
|
||||
if [[ "$RESPONSE" == "FAILED" ]]; then
|
||||
echo -e " ${YELLOW}⚠ Request failed${NC}"
|
||||
else
|
||||
HTTP_CODE=$(echo "$RESPONSE" | grep "HTTP_CODE" | cut -d: -f2)
|
||||
if [[ "$HTTP_CODE" == "200" ]] || [[ "$HTTP_CODE" == "401" ]] || [[ "$HTTP_CODE" == "404" ]]; then
|
||||
echo -e " ${GREEN}✓ Response received (HTTP $HTTP_CODE)${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠ Unexpected response (HTTP $HTTP_CODE)${NC}"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
sleep 1
|
||||
}
|
||||
|
||||
# Generate traffic to various endpoints
|
||||
echo -e "${BLUE}Step 2: Generating Traffic to Services${NC}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Health checks (should generate traces)
|
||||
make_request "/health" "Gateway Health Check"
|
||||
make_request "/api/health" "API Health Check"
|
||||
|
||||
# Auth service endpoints
|
||||
make_request "/api/auth/health" "Auth Service Health"
|
||||
|
||||
# Tenant service endpoints
|
||||
make_request "/api/tenants/health" "Tenant Service Health"
|
||||
|
||||
# Inventory service endpoints
|
||||
make_request "/api/inventory/health" "Inventory Service Health"
|
||||
|
||||
# Orders service endpoints
|
||||
make_request "/api/orders/health" "Orders Service Health"
|
||||
|
||||
# Forecasting service endpoints
|
||||
make_request "/api/forecasting/health" "Forecasting Service Health"
|
||||
|
||||
echo -e "${BLUE}Step 3: Checking Service Logs for Telemetry${NC}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Check a few service pods for tracing logs
|
||||
SERVICES=("auth-service" "inventory-service" "gateway")
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
POD=$(kubectl get pods -n $NAMESPACE -l app=$service --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
|
||||
if [[ -n "$POD" ]]; then
|
||||
echo -e "${BLUE}Checking $service ($POD)...${NC}"
|
||||
TRACING_LOG=$(kubectl logs -n $NAMESPACE $POD --tail=100 2>/dev/null | grep -i "tracing\|otel" | head -n 2 || echo "")
|
||||
if [[ -n "$TRACING_LOG" ]]; then
|
||||
echo -e "${GREEN}✓ Tracing configured:${NC}"
|
||||
echo "$TRACING_LOG" | sed 's/^/ /'
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No tracing logs found${NC}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for data to be processed
|
||||
echo -e "${BLUE}Step 4: Waiting for Data Processing${NC}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Waiting 30 seconds for telemetry data to be processed..."
|
||||
for i in {30..1}; do
|
||||
echo -ne "\r ${i} seconds remaining..."
|
||||
sleep 1
|
||||
done
|
||||
echo -e "\n"
|
||||
|
||||
# Cleanup port-forward if started
|
||||
if [[ -n "$PORT_FORWARD_PID" ]]; then
|
||||
kill $PORT_FORWARD_PID 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Test traffic generation complete!${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Next Steps:${NC}"
|
||||
echo "1. Run the verification script to check for collected data:"
|
||||
echo " ./infrastructure/helm/verify-signoz-telemetry.sh"
|
||||
echo ""
|
||||
echo "2. Access SigNoz UI to visualize the data:"
|
||||
echo " https://monitoring.bakery-ia.local"
|
||||
echo " or"
|
||||
echo " kubectl port-forward -n bakery-ia svc/signoz 3301:8080"
|
||||
echo " Then go to: http://localhost:3301"
|
||||
echo ""
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
129
infrastructure/scripts/maintenance/generate_subscription_test_report.sh
Executable file
129
infrastructure/scripts/maintenance/generate_subscription_test_report.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to generate a comprehensive test report for the subscription creation flow
|
||||
# This script checks all components and generates a detailed report
|
||||
|
||||
echo "📊 Generating Subscription Creation Flow Test Report"
|
||||
echo "===================================================="
|
||||
echo "Report generated on: $(date)"
|
||||
echo ""
|
||||
|
||||
# Test 1: Check if database migration was applied
|
||||
echo "🔍 Test 1: Database Migration Check"
|
||||
echo "-----------------------------------"
|
||||
POD_NAME=$(kubectl get pods -n bakery-ia -l app=auth-service -o jsonpath='{.items[0].metadata.name}')
|
||||
MIGRATION_STATUS=$(kubectl exec -n bakery-ia $POD_NAME -- psql -U auth_user -d auth_db -c "SELECT version_num FROM alembic_version" -t -A)
|
||||
|
||||
if [[ "$MIGRATION_STATUS" == "20260113_add_payment_columns" ]]; then
|
||||
echo "✅ PASS: Database migration '20260113_add_payment_columns' is applied"
|
||||
else
|
||||
echo "❌ FAIL: Database migration not found. Current version: $MIGRATION_STATUS"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: Check if payment columns exist in users table
|
||||
echo "🔍 Test 2: Payment Columns in Users Table"
|
||||
echo "------------------------------------------"
|
||||
COLUMNS=$(kubectl exec -n bakery-ia $POD_NAME -- psql -U auth_user -d auth_db -c "\d users" -t -A | grep -E "payment_customer_id|default_payment_method_id")
|
||||
|
||||
if [[ -n "$COLUMNS" ]]; then
|
||||
echo "✅ PASS: Payment columns found in users table"
|
||||
echo " Found columns:"
|
||||
echo " $COLUMNS" | sed 's/^/ /'
|
||||
else
|
||||
echo "❌ FAIL: Payment columns not found in users table"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Check if gateway route exists
|
||||
echo "🔍 Test 3: Gateway Route Configuration"
|
||||
echo "--------------------------------------"
|
||||
GATEWAY_POD=$(kubectl get pods -n bakery-ia -l app=gateway -o jsonpath='{.items[0].metadata.name}')
|
||||
ROUTE_CHECK=$(kubectl exec -n bakery-ia $GATEWAY_POD -- grep -c "create-for-registration" /app/app/routes/subscription.py)
|
||||
|
||||
if [[ "$ROUTE_CHECK" -gt 0 ]]; then
|
||||
echo "✅ PASS: Gateway route for 'create-for-registration' is configured"
|
||||
else
|
||||
echo "❌ FAIL: Gateway route for 'create-for-registration' not found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Check if tenant service endpoint exists
|
||||
echo "🔍 Test 4: Tenant Service Endpoint"
|
||||
echo "-----------------------------------"
|
||||
TENANT_POD=$(kubectl get pods -n bakery-ia -l app=tenant-service -o jsonpath='{.items[0].metadata.name}')
|
||||
ENDPOINT_CHECK=$(kubectl exec -n bakery-ia $TENANT_POD -- grep -c "create-for-registration" /app/app/api/subscription.py)
|
||||
|
||||
if [[ "$ENDPOINT_CHECK" -gt 0 ]]; then
|
||||
echo "✅ PASS: Tenant service endpoint 'create-for-registration' is configured"
|
||||
else
|
||||
echo "❌ FAIL: Tenant service endpoint 'create-for-registration' not found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: Test user registration (create a test user)
|
||||
echo "🔍 Test 5: User Registration Test"
|
||||
echo "--------------------------------"
|
||||
TEST_EMAIL="test_$(date +%Y%m%d%H%M%S)@example.com"
|
||||
REGISTRATION_RESPONSE=$(curl -X POST "https://bakery-ia.local/api/v1/auth/register-with-subscription" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-d "{\"email\":\"$TEST_EMAIL\",\"password\":\"SecurePassword123!\",\"full_name\":\"Test User\",\"subscription_plan\":\"basic\",\"payment_method_id\":\"pm_test123\"}" \
|
||||
-k -s)
|
||||
|
||||
if echo "$REGISTRATION_RESPONSE" | grep -q "access_token"; then
|
||||
echo "✅ PASS: User registration successful"
|
||||
USER_ID=$(echo "$REGISTRATION_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin)['user']['id'])")
|
||||
echo " Created user ID: $USER_ID"
|
||||
else
|
||||
echo "❌ FAIL: User registration failed"
|
||||
echo " Response: $REGISTRATION_RESPONSE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 6: Check if user has payment fields
|
||||
echo "🔍 Test 6: User Payment Fields"
|
||||
echo "------------------------------"
|
||||
if [[ -n "$USER_ID" ]]; then
|
||||
USER_DATA=$(kubectl exec -n bakery-ia $POD_NAME -- psql -U auth_user -d auth_db -c "SELECT payment_customer_id, default_payment_method_id FROM users WHERE id = '$USER_ID'" -t -A)
|
||||
|
||||
if [[ -n "$USER_DATA" ]]; then
|
||||
echo "✅ PASS: User has payment fields in database"
|
||||
echo " Payment data: $USER_DATA"
|
||||
else
|
||||
echo "❌ FAIL: User payment fields not found"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ SKIP: User ID not available from previous test"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 7: Check subscription creation in onboarding progress
|
||||
echo "🔍 Test 7: Subscription in Onboarding Progress"
|
||||
echo "---------------------------------------------"
|
||||
if [[ -n "$USER_ID" ]]; then
|
||||
# This would require authentication, so we'll skip for now
|
||||
echo "⚠️ SKIP: Requires authentication (would need to implement token handling)"
|
||||
else
|
||||
echo "⚠️ SKIP: User ID not available from previous test"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "📋 Test Summary"
|
||||
echo "==============="
|
||||
echo "The subscription creation flow test report has been generated."
|
||||
echo ""
|
||||
echo "Components tested:"
|
||||
echo " 1. Database migration"
|
||||
echo " 2. Payment columns in users table"
|
||||
echo " 3. Gateway route configuration"
|
||||
echo " 4. Tenant service endpoint"
|
||||
echo " 5. User registration"
|
||||
echo " 6. User payment fields"
|
||||
echo " 7. Subscription in onboarding progress"
|
||||
echo ""
|
||||
echo "For a complete integration test, run:"
|
||||
echo " ./scripts/run_subscription_integration_test.sh"
|
||||
echo ""
|
||||
echo "🎉 Report generation completed!"
|
||||
369
infrastructure/scripts/maintenance/kubernetes_restart.sh
Executable file
369
infrastructure/scripts/maintenance/kubernetes_restart.sh
Executable file
@@ -0,0 +1,369 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to wait for pods with retry logic
|
||||
wait_for_pods() {
|
||||
local namespace=$1
|
||||
local selector=$2
|
||||
local timeout=$3
|
||||
local max_retries=30
|
||||
local retry_count=0
|
||||
|
||||
print_status "Waiting for pods with selector '$selector' in namespace '$namespace'..."
|
||||
|
||||
while [ $retry_count -lt $max_retries ]; do
|
||||
# Check if any pods exist first
|
||||
if kubectl get pods -n "$namespace" --selector="$selector" 2>/dev/null | grep -v "No resources found" | grep -v "NAME" > /dev/null; then
|
||||
# Pods exist, now wait for them to be ready
|
||||
if kubectl wait --namespace "$namespace" \
|
||||
--for=condition=ready pod \
|
||||
--selector="$selector" \
|
||||
--timeout="${timeout}s" 2>/dev/null; then
|
||||
print_success "Pods are ready"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
retry_count=$((retry_count + 1))
|
||||
print_status "Waiting for pods to be created... (attempt $retry_count/$max_retries)"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
print_error "Timed out waiting for pods after $((max_retries * 5)) seconds"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to handle cleanup
|
||||
cleanup() {
|
||||
print_status "Starting cleanup process..."
|
||||
|
||||
# Delete Kubernetes namespace with timeout
|
||||
print_status "Deleting namespace bakery-ia..."
|
||||
if kubectl get namespace bakery-ia &>/dev/null; then
|
||||
kubectl delete namespace bakery-ia 2>/dev/null &
|
||||
PID=$!
|
||||
sleep 2
|
||||
if ps -p $PID &>/dev/null; then
|
||||
print_warning "kubectl delete namespace command taking too long, forcing termination..."
|
||||
kill $PID 2>/dev/null
|
||||
fi
|
||||
print_success "Namespace deletion attempted"
|
||||
else
|
||||
print_status "Namespace bakery-ia not found"
|
||||
fi
|
||||
|
||||
# Delete Kind cluster
|
||||
print_status "Deleting Kind cluster..."
|
||||
if kind get clusters | grep -q "bakery-ia-local"; then
|
||||
kind delete cluster --name bakery-ia-local
|
||||
print_success "Kind cluster deleted"
|
||||
else
|
||||
print_status "Kind cluster bakery-ia-local not found"
|
||||
fi
|
||||
|
||||
# Stop local registry
|
||||
print_status "Stopping local registry..."
|
||||
if docker ps -a | grep -q "kind-registry"; then
|
||||
docker stop kind-registry 2>/dev/null || true
|
||||
docker rm kind-registry 2>/dev/null || true
|
||||
print_success "Local registry removed"
|
||||
else
|
||||
print_status "Local registry not found"
|
||||
fi
|
||||
|
||||
# Stop Colima
|
||||
print_status "Stopping Colima..."
|
||||
if colima list | grep -q "k8s-local"; then
|
||||
colima stop --profile k8s-local
|
||||
print_success "Colima stopped"
|
||||
else
|
||||
print_status "Colima profile k8s-local not found"
|
||||
fi
|
||||
|
||||
print_success "Cleanup completed!"
|
||||
echo "----------------------------------------"
|
||||
}
|
||||
|
||||
# Function to check for required configuration files
|
||||
check_config_files() {
|
||||
print_status "Checking for required configuration files..."
|
||||
|
||||
# Check for kind-config.yaml
|
||||
if [ ! -f kind-config.yaml ]; then
|
||||
print_error "kind-config.yaml not found in current directory!"
|
||||
print_error "Please ensure kind-config.yaml exists with your cluster configuration."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for encryption directory if referenced in config
|
||||
if grep -q "infrastructure/kubernetes/encryption" kind-config.yaml; then
|
||||
if [ ! -d "./infrastructure/kubernetes/encryption" ]; then
|
||||
print_warning "Encryption directory './infrastructure/kubernetes/encryption' not found"
|
||||
print_warning "Some encryption configurations may not work properly"
|
||||
fi
|
||||
fi
|
||||
|
||||
print_success "Configuration files check completed"
|
||||
}
|
||||
|
||||
# Function to create local registry
|
||||
create_local_registry() {
|
||||
local reg_name='kind-registry'
|
||||
local reg_port='5001'
|
||||
|
||||
print_status "Setting up local Docker registry..."
|
||||
|
||||
# Create registry container unless it already exists
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
|
||||
print_status "Creating registry container on port ${reg_port}..."
|
||||
docker run \
|
||||
-d --restart=always \
|
||||
-p "127.0.0.1:${reg_port}:5000" \
|
||||
--name "${reg_name}" \
|
||||
registry:2
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Local registry created at localhost:${reg_port}"
|
||||
else
|
||||
print_error "Failed to create local registry"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_success "Local registry already running at localhost:${reg_port}"
|
||||
fi
|
||||
|
||||
# Store registry info for later use
|
||||
echo "${reg_name}:${reg_port}"
|
||||
}
|
||||
|
||||
# Function to connect registry to Kind
|
||||
connect_registry_to_kind() {
|
||||
local reg_name='kind-registry'
|
||||
local reg_port='5001'
|
||||
|
||||
print_status "Connecting registry to Kind network..."
|
||||
|
||||
# Connect the registry to the cluster network if not already connected
|
||||
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
|
||||
docker network connect "kind" "${reg_name}"
|
||||
print_success "Registry connected to Kind network"
|
||||
else
|
||||
print_success "Registry already connected to Kind network"
|
||||
fi
|
||||
|
||||
# Configure containerd in the Kind node to use the registry
|
||||
print_status "Configuring containerd to use local registry..."
|
||||
|
||||
# Create the registry config directory
|
||||
docker exec bakery-ia-local-control-plane mkdir -p /etc/containerd/certs.d/localhost:${reg_port}
|
||||
|
||||
# Add registry configuration
|
||||
docker exec bakery-ia-local-control-plane sh -c "cat > /etc/containerd/certs.d/localhost:${reg_port}/hosts.toml <<EOF
|
||||
server = \"http://localhost:${reg_port}\"
|
||||
|
||||
[host.\"http://${reg_name}:5000\"]
|
||||
capabilities = [\"pull\", \"resolve\", \"push\"]
|
||||
skip_verify = true
|
||||
EOF"
|
||||
|
||||
# Restart containerd to pick up new configuration
|
||||
docker exec bakery-ia-local-control-plane systemctl restart containerd
|
||||
|
||||
print_success "Containerd configured for local registry"
|
||||
|
||||
# Document the local registry
|
||||
print_status "Documenting local registry in cluster..."
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: local-registry-hosting
|
||||
namespace: kube-public
|
||||
data:
|
||||
localRegistryHosting.v1: |
|
||||
host: "localhost:${reg_port}"
|
||||
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Registry documented in cluster"
|
||||
else
|
||||
print_warning "Failed to document registry (non-critical)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to handle setup
|
||||
setup() {
|
||||
print_status "Starting setup process..."
|
||||
|
||||
# Check for required config files
|
||||
check_config_files
|
||||
|
||||
# 1. Start Colima with adequate resources for SigNoz
|
||||
print_status "Starting Colima with 8 CPU, 16GB memory, 120GB disk..."
|
||||
colima start --cpu 8 --memory 16 --disk 120 --runtime docker --profile k8s-local
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Colima started successfully"
|
||||
|
||||
# Increase inotify limits for Colima to prevent "too many open files" errors
|
||||
print_status "Increasing inotify limits in Colima VM..."
|
||||
colima ssh --profile k8s-local "sudo sysctl -w fs.inotify.max_user_watches=524288"
|
||||
colima ssh --profile k8s-local "sudo sysctl -w fs.inotify.max_user_instances=512"
|
||||
print_success "Inotify limits increased"
|
||||
else
|
||||
print_error "Failed to start Colima"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Create local registry before Kind cluster
|
||||
create_local_registry
|
||||
|
||||
# 3. Create Kind cluster using existing configuration with registry support
|
||||
print_status "Creating Kind cluster with registry configuration..."
|
||||
|
||||
if [ -f kind-config.yaml ]; then
|
||||
print_status "Using kind-config.yaml with local registry support"
|
||||
|
||||
# Extract cluster name from config for verification
|
||||
CLUSTER_NAME=$(grep -E "name:\s*" kind-config.yaml | head -1 | sed 's/name:\s*//' | tr -d '[:space:]' || echo "bakery-ia-local")
|
||||
|
||||
print_status "Creating cluster: $CLUSTER_NAME"
|
||||
kind create cluster --config kind-config.yaml
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Kind cluster created successfully"
|
||||
else
|
||||
print_error "Failed to create Kind cluster"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_error "kind-config.yaml file not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. Connect registry to Kind network
|
||||
connect_registry_to_kind
|
||||
|
||||
# 5. Install NGINX Ingress Controller
|
||||
print_status "Installing NGINX Ingress Controller..."
|
||||
|
||||
# Apply the ingress-nginx manifest
|
||||
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "NGINX Ingress Controller manifest applied"
|
||||
else
|
||||
print_error "Failed to apply NGINX Ingress Controller manifest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for ingress-nginx pods to be ready with retry logic
|
||||
wait_for_pods "ingress-nginx" "app.kubernetes.io/component=controller" 300
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
print_error "NGINX Ingress Controller failed to become ready"
|
||||
print_status "Checking pod status for debugging..."
|
||||
kubectl get pods -n ingress-nginx
|
||||
kubectl describe pods -n ingress-nginx
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "NGINX Ingress Controller ready (using Kind's built-in NodePort configuration)"
|
||||
|
||||
# 6. Verify port mappings from kind-config.yaml
|
||||
print_status "Verifying port mappings from configuration..."
|
||||
|
||||
# Extract ports from kind-config.yaml
|
||||
HTTP_HOST_PORT=$(grep -A1 "containerPort: 30080" kind-config.yaml | grep "hostPort:" | awk '{print $2}' || echo "80")
|
||||
HTTPS_HOST_PORT=$(grep -A1 "containerPort: 30443" kind-config.yaml | grep "hostPort:" | awk '{print $2}' || echo "443")
|
||||
|
||||
# Print cluster info
|
||||
echo ""
|
||||
print_success "Setup completed successfully!"
|
||||
echo "----------------------------------------"
|
||||
print_status "Cluster Information:"
|
||||
echo " - Colima profile: k8s-local"
|
||||
echo " - Kind cluster: $CLUSTER_NAME"
|
||||
echo " - Local registry: localhost:5001"
|
||||
echo ""
|
||||
print_status "Port Mappings (configured in kind-config.yaml):"
|
||||
echo " - HTTP Ingress: localhost:${HTTP_HOST_PORT} -> Kind NodePort 30080"
|
||||
echo " - HTTPS Ingress: localhost:${HTTPS_HOST_PORT} -> Kind NodePort 30443"
|
||||
echo " - Frontend Direct: localhost:3000 -> container:30300"
|
||||
echo " - Gateway Direct: localhost:8000 -> container:30800"
|
||||
echo ""
|
||||
print_status "How to access your application:"
|
||||
echo " 1. Start Tilt: tilt up"
|
||||
echo " 2. Access via:"
|
||||
echo " - Ingress: http://localhost (or https://localhost)"
|
||||
echo " - Direct: http://localhost:3000 (frontend), http://localhost:8000 (gateway)"
|
||||
echo " - Tilt UI: http://localhost:10350"
|
||||
echo "----------------------------------------"
|
||||
print_status "Local Registry Information:"
|
||||
echo " - Registry URL: localhost:5001"
|
||||
echo " - Images pushed to: localhost:5001/bakery/<service>"
|
||||
echo " - Tiltfile already configured: default_registry('localhost:5001')"
|
||||
echo "----------------------------------------"
|
||||
}
|
||||
|
||||
# Function to show usage
|
||||
usage() {
|
||||
echo "Usage: $0 [option]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " cleanup Clean up all resources (namespace, cluster, colima)"
|
||||
echo " setup Set up the complete environment"
|
||||
echo " full Clean up first, then set up (default)"
|
||||
echo " help Show this help message"
|
||||
echo ""
|
||||
echo "Requirements:"
|
||||
echo " - kind-config.yaml must exist in current directory"
|
||||
echo " - For encryption: ./infrastructure/kubernetes/encryption directory"
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
case "${1:-full}" in
|
||||
"cleanup")
|
||||
cleanup
|
||||
;;
|
||||
"setup")
|
||||
setup
|
||||
;;
|
||||
"full")
|
||||
cleanup
|
||||
setup
|
||||
;;
|
||||
"help"|"-h"|"--help")
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
print_warning "Unknown option: $1"
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
166
infrastructure/scripts/maintenance/regenerate_all_migrations.sh
Executable file
166
infrastructure/scripts/maintenance/regenerate_all_migrations.sh
Executable file
@@ -0,0 +1,166 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Convenience script: Clean databases and regenerate all migrations in one command
|
||||
# This wraps the two-step process into a single workflow
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="${KUBE_NAMESPACE:-bakery-ia}"
|
||||
SKIP_CONFIRMATION=false
|
||||
APPLY_MIGRATIONS=false
|
||||
VERBOSE=false
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--namespace) NAMESPACE="$2"; shift 2 ;;
|
||||
--yes) SKIP_CONFIRMATION=true; shift ;;
|
||||
--apply) APPLY_MIGRATIONS=true; shift ;;
|
||||
--verbose) VERBOSE=true; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "This script performs the complete migration regeneration workflow:"
|
||||
echo " 1. Clean all service databases"
|
||||
echo " 2. Generate new migrations from models"
|
||||
echo " 3. Optionally apply migrations"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --namespace NAME Use specific Kubernetes namespace (default: bakery-ia)"
|
||||
echo " --yes Skip all confirmation prompts"
|
||||
echo " --apply Apply migrations after generation"
|
||||
echo " --verbose Enable detailed logging"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Interactive mode (with confirmations)"
|
||||
echo " $0 --yes --verbose # Automated mode with detailed output"
|
||||
echo " $0 --apply # Generate and apply migrations"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1"; echo "Use --help for usage information"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Complete Migration Regeneration Workflow${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}This script will:${NC}"
|
||||
echo -e "${YELLOW} 1. Clean all service databases (DROP all tables)${NC}"
|
||||
echo -e "${YELLOW} 2. Generate new migrations from models${NC}"
|
||||
if [ "$APPLY_MIGRATIONS" = true ]; then
|
||||
echo -e "${YELLOW} 3. Apply migrations to databases${NC}"
|
||||
fi
|
||||
echo ""
|
||||
echo -e "${YELLOW}Namespace: $NAMESPACE${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$SKIP_CONFIRMATION" = false ]; then
|
||||
echo -e "${RED}⚠ WARNING: This will DROP ALL TABLES in all service databases!${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}Aborted.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Step 1: Cleaning Databases${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Build cleanup command
|
||||
CLEANUP_CMD="./cleanup_databases_k8s.sh --namespace $NAMESPACE"
|
||||
if [ "$SKIP_CONFIRMATION" = true ]; then
|
||||
CLEANUP_CMD="$CLEANUP_CMD --yes"
|
||||
fi
|
||||
|
||||
# Run cleanup
|
||||
if ! $CLEANUP_CMD; then
|
||||
echo -e "${RED}✗ Database cleanup failed!${NC}"
|
||||
echo -e "${YELLOW}Cannot proceed with migration generation.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Database cleanup completed${NC}"
|
||||
echo ""
|
||||
|
||||
# Wait a moment for database connections to settle
|
||||
sleep 2
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Step 2: Generating Migrations${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Build migration command
|
||||
MIGRATION_CMD="./regenerate_migrations_k8s.sh --namespace $NAMESPACE"
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
MIGRATION_CMD="$MIGRATION_CMD --verbose"
|
||||
fi
|
||||
if [ "$APPLY_MIGRATIONS" = true ]; then
|
||||
MIGRATION_CMD="$MIGRATION_CMD --apply"
|
||||
fi
|
||||
|
||||
# Run migration generation (with automatic 'y' response)
|
||||
if [ "$SKIP_CONFIRMATION" = true ]; then
|
||||
echo "y" | $MIGRATION_CMD
|
||||
else
|
||||
$MIGRATION_CMD
|
||||
fi
|
||||
|
||||
MIGRATION_EXIT_CODE=$?
|
||||
|
||||
echo ""
|
||||
if [ $MIGRATION_EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}✓ Workflow Completed Successfully!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Summary:${NC}"
|
||||
echo -e " ${GREEN}✓${NC} Databases cleaned"
|
||||
echo -e " ${GREEN}✓${NC} Migrations generated"
|
||||
if [ "$APPLY_MIGRATIONS" = true ]; then
|
||||
echo -e " ${GREEN}✓${NC} Migrations applied"
|
||||
fi
|
||||
echo ""
|
||||
echo -e "${YELLOW}Generated migration files:${NC}"
|
||||
find services/*/migrations/versions/ -name "*.py" -type f -mmin -5 2>/dev/null | while read file; do
|
||||
size=$(wc -c < "$file" | tr -d ' ')
|
||||
echo -e " ${GREEN}✓${NC} $file ($size bytes)"
|
||||
done
|
||||
echo ""
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo -e " 1. Review the generated migrations above"
|
||||
echo -e " 2. Verify migrations contain schema operations (not just 'pass')"
|
||||
if [ "$APPLY_MIGRATIONS" = false ]; then
|
||||
echo -e " 3. Apply migrations: ./regenerate_migrations_k8s.sh --apply"
|
||||
echo -e " 4. Commit migrations: git add services/*/migrations/versions/*.py"
|
||||
else
|
||||
echo -e " 3. Commit migrations: git add services/*/migrations/versions/*.py"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}========================================${NC}"
|
||||
echo -e "${RED}✗ Migration Generation Failed${NC}"
|
||||
echo -e "${RED}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Check the log file for details.${NC}"
|
||||
echo -e "${YELLOW}Common issues:${NC}"
|
||||
echo -e " - Pod not running for some services"
|
||||
echo -e " - Database connectivity issues"
|
||||
echo -e " - Model import errors"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
796
infrastructure/scripts/maintenance/regenerate_migrations_k8s.sh
Executable file
796
infrastructure/scripts/maintenance/regenerate_migrations_k8s.sh
Executable file
@@ -0,0 +1,796 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to regenerate Alembic migrations using Kubernetes local dev environment
|
||||
# This script backs up existing migrations and generates new ones based on current models
|
||||
|
||||
set -euo pipefail # Exit on error, undefined variables, and pipe failures
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="${KUBE_NAMESPACE:-bakery-ia}"
|
||||
LOG_FILE="migration_script_$(date +%Y%m%d_%H%M%S).log"
|
||||
BACKUP_RETENTION_DAYS=7
|
||||
CONTAINER_SUFFIX="service" # Default container name suffix (e.g., pos-service)
|
||||
|
||||
# Parse command line arguments
|
||||
DRY_RUN=false
|
||||
SKIP_BACKUP=false
|
||||
APPLY_MIGRATIONS=false
|
||||
CHECK_EXISTING=false
|
||||
VERBOSE=false
|
||||
SKIP_DB_CHECK=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--skip-backup) SKIP_BACKUP=true; shift ;;
|
||||
--apply) APPLY_MIGRATIONS=true; shift ;;
|
||||
--check-existing) CHECK_EXISTING=true; shift ;;
|
||||
--verbose) VERBOSE=true; shift ;;
|
||||
--skip-db-check) SKIP_DB_CHECK=true; shift ;;
|
||||
--namespace) NAMESPACE="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --dry-run Show what would be done without making changes"
|
||||
echo " --skip-backup Skip backing up existing migrations"
|
||||
echo " --apply Automatically apply migrations after generation"
|
||||
echo " --check-existing Check for and copy existing migrations from pods first"
|
||||
echo " --verbose Enable detailed logging"
|
||||
echo " --skip-db-check Skip database connectivity check"
|
||||
echo " --namespace NAME Use specific Kubernetes namespace (default: bakery-ia)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 --namespace dev --dry-run # Simulate migration regeneration"
|
||||
echo " $0 --apply --verbose # Generate and apply migrations with detailed logs"
|
||||
echo " $0 --skip-db-check # Skip database connectivity check"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1"; echo "Use --help for usage information"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# List of all services
|
||||
SERVICES=(
|
||||
"pos" "sales" "recipes" "training" "auth" "orders" "inventory"
|
||||
"suppliers" "tenant" "notification" "alert-processor" "forecasting"
|
||||
"external" "production" "demo-session"
|
||||
)
|
||||
|
||||
# Backup directory
|
||||
BACKUP_DIR="migrations_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Initialize log file
|
||||
touch "$LOG_FILE"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting migration regeneration" >> "$LOG_FILE"
|
||||
|
||||
# Function to perform pre-flight checks
|
||||
preflight_checks() {
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Pre-flight Checks${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
local checks_passed=true
|
||||
|
||||
# Check kubectl
|
||||
echo -e "${YELLOW}Checking kubectl...${NC}"
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}✗ kubectl not found${NC}"
|
||||
log_message "ERROR" "kubectl not found"
|
||||
checks_passed=false
|
||||
else
|
||||
KUBECTL_VERSION=$(kubectl version --client --short 2>/dev/null | grep -oP 'v\d+\.\d+\.\d+' || echo "unknown")
|
||||
echo -e "${GREEN}✓ kubectl found (version: $KUBECTL_VERSION)${NC}"
|
||||
fi
|
||||
|
||||
# Check cluster connectivity
|
||||
echo -e "${YELLOW}Checking Kubernetes cluster connectivity...${NC}"
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}✗ Cannot connect to Kubernetes cluster${NC}"
|
||||
log_message "ERROR" "Cannot connect to Kubernetes cluster"
|
||||
checks_passed=false
|
||||
else
|
||||
CLUSTER_NAME=$(kubectl config current-context 2>/dev/null || echo "unknown")
|
||||
echo -e "${GREEN}✓ Connected to cluster: $CLUSTER_NAME${NC}"
|
||||
fi
|
||||
|
||||
# Check namespace exists
|
||||
echo -e "${YELLOW}Checking namespace '$NAMESPACE'...${NC}"
|
||||
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
|
||||
echo -e "${RED}✗ Namespace '$NAMESPACE' not found${NC}"
|
||||
log_message "ERROR" "Namespace '$NAMESPACE' not found"
|
||||
checks_passed=false
|
||||
else
|
||||
echo -e "${GREEN}✓ Namespace exists${NC}"
|
||||
fi
|
||||
|
||||
# Check if all service pods are running
|
||||
echo -e "${YELLOW}Checking service pods...${NC}"
|
||||
local pods_found=0
|
||||
local pods_running=0
|
||||
for service in "${SERVICES[@]}"; do
|
||||
local pod_name=$(kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=${service}-service" --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
||||
if [ -n "$pod_name" ]; then
|
||||
pods_found=$((pods_found + 1))
|
||||
pods_running=$((pods_running + 1))
|
||||
fi
|
||||
done
|
||||
echo -e "${GREEN}✓ Found $pods_running/${#SERVICES[@]} service pods running${NC}"
|
||||
|
||||
if [ $pods_running -lt ${#SERVICES[@]} ]; then
|
||||
echo -e "${YELLOW}⚠ Not all service pods are running${NC}"
|
||||
echo -e "${YELLOW} Missing services will be skipped${NC}"
|
||||
fi
|
||||
|
||||
# Check database connectivity for running services
|
||||
echo -e "${YELLOW}Checking database connectivity (sample)...${NC}"
|
||||
local sample_service="auth"
|
||||
local sample_pod=$(kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=${sample_service}-service" --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$sample_pod" ]; then
|
||||
local db_check=$(kubectl exec -n "$NAMESPACE" "$sample_pod" -c "${sample_service}-service" -- sh -c "python3 -c 'import asyncpg; print(\"OK\")' 2>/dev/null" || echo "FAIL")
|
||||
if [ "$db_check" = "OK" ]; then
|
||||
echo -e "${GREEN}✓ Database drivers available (asyncpg)${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Database driver check failed${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Cannot check database connectivity (no sample pod running)${NC}"
|
||||
fi
|
||||
|
||||
# Check local directory structure
|
||||
echo -e "${YELLOW}Checking local directory structure...${NC}"
|
||||
local dirs_found=0
|
||||
for service in "${SERVICES[@]}"; do
|
||||
local service_dir=$(echo "$service" | tr '-' '_')
|
||||
if [ -d "services/$service_dir/migrations" ]; then
|
||||
dirs_found=$((dirs_found + 1))
|
||||
fi
|
||||
done
|
||||
echo -e "${GREEN}✓ Found $dirs_found/${#SERVICES[@]} service migration directories${NC}"
|
||||
|
||||
# Check disk space
|
||||
echo -e "${YELLOW}Checking disk space...${NC}"
|
||||
local available_space=$(df -h . | tail -1 | awk '{print $4}')
|
||||
echo -e "${GREEN}✓ Available disk space: $available_space${NC}"
|
||||
|
||||
echo ""
|
||||
if [ "$checks_passed" = false ]; then
|
||||
echo -e "${RED}========================================${NC}"
|
||||
echo -e "${RED}Pre-flight checks failed!${NC}"
|
||||
echo -e "${RED}========================================${NC}"
|
||||
echo ""
|
||||
read -p "Continue anyway? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Aborted.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}All pre-flight checks passed!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run pre-flight checks
|
||||
preflight_checks
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Migration Regeneration Script (K8s)${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo -e "${YELLOW}🔍 DRY RUN MODE - No changes will be made${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}This script will:${NC}"
|
||||
if [ "$CHECK_EXISTING" = true ]; then
|
||||
echo -e "${YELLOW}1. Check for existing migrations in pods and copy them${NC}"
|
||||
fi
|
||||
if [ "$SKIP_BACKUP" = false ]; then
|
||||
echo -e "${YELLOW}2. Backup existing migration files${NC}"
|
||||
fi
|
||||
echo -e "${YELLOW}3. Generate new migrations in Kubernetes pods${NC}"
|
||||
echo -e "${YELLOW}4. Copy generated files back to local machine${NC}"
|
||||
if [ "$APPLY_MIGRATIONS" = true ]; then
|
||||
echo -e "${YELLOW}5. Apply migrations to databases${NC}"
|
||||
fi
|
||||
if [ "$SKIP_BACKUP" = false ]; then
|
||||
echo -e "${YELLOW}6. Keep the backup in: $BACKUP_DIR${NC}"
|
||||
fi
|
||||
echo ""
|
||||
echo -e "${YELLOW}Using Kubernetes namespace: $NAMESPACE${NC}"
|
||||
echo -e "${YELLOW}Logs will be saved to: $LOG_FILE${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p "Continue? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Aborted.${NC}"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Aborted by user" >> "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Kubernetes setup already verified in pre-flight checks
|
||||
|
||||
# Function to get a running pod for a service
|
||||
get_running_pod() {
|
||||
local service=$1
|
||||
local pod_name=""
|
||||
local selectors=(
|
||||
"app.kubernetes.io/name=${service}-service,app.kubernetes.io/component=microservice"
|
||||
"app.kubernetes.io/name=${service}-service,app.kubernetes.io/component=worker"
|
||||
"app.kubernetes.io/name=${service}-service"
|
||||
"app=${service}-service,component=${service}" # Fallback for demo-session
|
||||
"app=${service}-service" # Additional fallback
|
||||
)
|
||||
|
||||
for selector in "${selectors[@]}"; do
|
||||
pod_name=$(kubectl get pods -n "$NAMESPACE" -l "$selector" --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
|
||||
if [ -n "$pod_name" ]; then
|
||||
echo "$pod_name"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to log messages
|
||||
log_message() {
|
||||
local level=$1
|
||||
local message=$2
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $level: $message" >> "$LOG_FILE"
|
||||
if [ "$VERBOSE" = true ] || [ "$level" = "ERROR" ]; then
|
||||
echo -e "${YELLOW}$message${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for existing migrations in pods if requested
|
||||
if [ "$CHECK_EXISTING" = true ]; then
|
||||
echo -e "${BLUE}Step 1.5: Checking for existing migrations in pods...${NC}"
|
||||
echo ""
|
||||
|
||||
FOUND_COUNT=0
|
||||
COPIED_COUNT=0
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
service_dir=$(echo "$service" | tr '-' '_')
|
||||
echo -e "${YELLOW}Checking $service...${NC}"
|
||||
|
||||
# Find a running pod
|
||||
POD_NAME=$(get_running_pod "$service")
|
||||
if [ -z "$POD_NAME" ]; then
|
||||
echo -e "${YELLOW}⚠ Pod not found, skipping${NC}"
|
||||
log_message "WARNING" "No running pod found for $service"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check container availability
|
||||
CONTAINER="${service}-${CONTAINER_SUFFIX}"
|
||||
if ! kubectl get pod -n "$NAMESPACE" "$POD_NAME" -o jsonpath='{.spec.containers[*].name}' | grep -qw "$CONTAINER"; then
|
||||
echo -e "${RED}✗ Container $CONTAINER not found in pod $POD_NAME, skipping${NC}"
|
||||
log_message "ERROR" "Container $CONTAINER not found in pod $POD_NAME for $service"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if migration files exist in the pod
|
||||
EXISTING_FILES=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "ls /app/migrations/versions/*.py 2>/dev/null | grep -v __pycache__ | grep -v __init__.py" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$EXISTING_FILES" ]; then
|
||||
FILE_COUNT=$(echo "$EXISTING_FILES" | wc -l | tr -d ' ')
|
||||
echo -e "${GREEN}✓ Found $FILE_COUNT migration file(s) in pod${NC}"
|
||||
FOUND_COUNT=$((FOUND_COUNT + 1))
|
||||
|
||||
# Create local versions directory
|
||||
mkdir -p "services/$service_dir/migrations/versions"
|
||||
|
||||
# Copy each file
|
||||
for pod_file in $EXISTING_FILES; do
|
||||
filename=$(basename "$pod_file")
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo -e "${BLUE}[DRY RUN] Would copy: $filename${NC}"
|
||||
log_message "INFO" "[DRY RUN] Would copy $filename for $service"
|
||||
else
|
||||
if kubectl cp -n "$NAMESPACE" "$POD_NAME:$pod_file" "services/$service_dir/migrations/versions/$filename" -c "$CONTAINER" 2>>"$LOG_FILE"; then
|
||||
echo -e "${GREEN}✓ Copied: $filename${NC}"
|
||||
COPIED_COUNT=$((COPIED_COUNT + 1))
|
||||
log_message "INFO" "Copied $filename for $service"
|
||||
# Display brief summary
|
||||
echo -e "${BLUE}Preview:${NC}"
|
||||
grep "def upgrade" "services/$service_dir/migrations/versions/$filename" | head -1
|
||||
grep "op\." "services/$service_dir/migrations/versions/$filename" | head -3 | sed 's/^/ /'
|
||||
else
|
||||
echo -e "${RED}✗ Failed to copy: $filename${NC}"
|
||||
log_message "ERROR" "Failed to copy $filename for $service"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No migration files found in pod${NC}"
|
||||
log_message "WARNING" "No migration files found in pod for $service"
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Existing Migrations Check Summary${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${GREEN}Services with migrations: $FOUND_COUNT${NC}"
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
echo -e "${GREEN}Files copied: $COPIED_COUNT${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [ "$FOUND_COUNT" = 0 ] && [ "$DRY_RUN" = false ]; then
|
||||
read -p "Do you want to continue with regeneration? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}Stopping. Existing migrations have been copied.${NC}"
|
||||
log_message "INFO" "Stopped after copying existing migrations"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Backup existing migrations
|
||||
if [ "$SKIP_BACKUP" = false ] && [ "$DRY_RUN" = false ]; then
|
||||
echo -e "${BLUE}Step 2: Backing up existing migrations...${NC}"
|
||||
BACKUP_COUNT=0
|
||||
for service in "${SERVICES[@]}"; do
|
||||
service_dir=$(echo "$service" | tr '-' '_')
|
||||
if [ -d "services/$service_dir/migrations/versions" ] && [ -n "$(ls services/$service_dir/migrations/versions/*.py 2>/dev/null)" ]; then
|
||||
echo -e "${YELLOW}Backing up $service migrations...${NC}"
|
||||
mkdir -p "$BACKUP_DIR/$service_dir/versions"
|
||||
cp -r "services/$service_dir/migrations/versions/"*.py "$BACKUP_DIR/$service_dir/versions/" 2>>"$LOG_FILE"
|
||||
BACKUP_COUNT=$((BACKUP_COUNT + 1))
|
||||
log_message "INFO" "Backed up migrations for $service to $BACKUP_DIR/$service_dir/versions"
|
||||
else
|
||||
echo -e "${YELLOW}No migration files to backup for $service${NC}"
|
||||
fi
|
||||
done
|
||||
if [ "$BACKUP_COUNT" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓ Backup complete: $BACKUP_DIR ($BACKUP_COUNT services)${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}No migrations backed up (no migration files found)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
elif [ "$SKIP_BACKUP" = true ]; then
|
||||
echo -e "${YELLOW}Skipping backup step (--skip-backup flag)${NC}"
|
||||
log_message "INFO" "Backup skipped due to --skip-backup flag"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Clean up old backups
|
||||
find . -maxdepth 1 -type d -name 'migrations_backup_*' -mtime +"$BACKUP_RETENTION_DAYS" -exec rm -rf {} \; 2>/dev/null || true
|
||||
log_message "INFO" "Cleaned up backups older than $BACKUP_RETENTION_DAYS days"
|
||||
|
||||
echo -e "${BLUE}Step 3: Generating new migrations in Kubernetes...${NC}"
|
||||
echo ""
|
||||
|
||||
SUCCESS_COUNT=0
|
||||
FAILED_COUNT=0
|
||||
FAILED_SERVICES=()
|
||||
|
||||
# Function to process a single service
|
||||
process_service() {
|
||||
local service=$1
|
||||
local service_dir=$(echo "$service" | tr '-' '_')
|
||||
local db_env_var=$(echo "$service" | tr '[:lower:]-' '[:upper:]_')_DATABASE_URL # e.g., pos -> POS_DATABASE_URL, alert-processor -> ALERT_PROCESSOR_DATABASE_URL
|
||||
|
||||
echo -e "${BLUE}----------------------------------------${NC}"
|
||||
echo -e "${BLUE}Processing: $service${NC}"
|
||||
echo -e "${BLUE}----------------------------------------${NC}"
|
||||
log_message "INFO" "Starting migration generation for $service"
|
||||
|
||||
# Skip if no local migrations directory and --check-existing is not set
|
||||
if [ ! -d "services/$service_dir/migrations/versions" ] && [ "$CHECK_EXISTING" = false ]; then
|
||||
echo -e "${YELLOW}⚠ No local migrations/versions directory for $service, skipping...${NC}"
|
||||
log_message "WARNING" "No local migrations/versions directory for $service"
|
||||
return
|
||||
fi
|
||||
|
||||
# Find a running pod
|
||||
echo -e "${YELLOW}Finding $service pod in namespace $NAMESPACE...${NC}"
|
||||
POD_NAME=$(get_running_pod "$service")
|
||||
if [ -z "$POD_NAME" ]; then
|
||||
echo -e "${RED}✗ No running pod found for $service. Skipping...${NC}"
|
||||
log_message "ERROR" "No running pod found for $service"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (pod not found)")
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Found pod: $POD_NAME${NC}"
|
||||
log_message "INFO" "Found pod $POD_NAME for $service"
|
||||
|
||||
# Check container availability
|
||||
CONTAINER="${service}-${CONTAINER_SUFFIX}"
|
||||
if ! kubectl get pod -n "$NAMESPACE" "$POD_NAME" -o jsonpath='{.spec.containers[*].name}' | grep -qw "$CONTAINER"; then
|
||||
echo -e "${RED}✗ Container $CONTAINER not found in pod $POD_NAME, skipping${NC}"
|
||||
log_message "ERROR" "Container $CONTAINER not found in pod $POD_NAME for $service"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (container not found)")
|
||||
return
|
||||
fi
|
||||
|
||||
# Verify database connectivity
|
||||
if [ "$SKIP_DB_CHECK" = false ]; then
|
||||
echo -e "${YELLOW}Verifying database connectivity using $db_env_var...${NC}"
|
||||
# Check if asyncpg is installed
|
||||
ASYNCPG_CHECK=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "python3 -c \"import asyncpg; print('asyncpg OK')\" 2>/dev/null" || echo "asyncpg MISSING")
|
||||
if [[ "$ASYNCPG_CHECK" != "asyncpg OK" ]]; then
|
||||
echo -e "${YELLOW}Installing asyncpg...${NC}"
|
||||
kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "python3 -m pip install --quiet asyncpg" 2>>"$LOG_FILE"
|
||||
fi
|
||||
|
||||
# Check for database URL
|
||||
DB_URL_CHECK=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "env | grep $db_env_var" 2>/dev/null || echo "")
|
||||
if [ -z "$DB_URL_CHECK" ]; then
|
||||
echo -e "${RED}✗ Environment variable $db_env_var not found in pod $POD_NAME${NC}"
|
||||
echo -e "${YELLOW}Available environment variables:${NC}"
|
||||
kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "env" 2>>"$LOG_FILE" | grep -i "database" || echo "No database-related variables found"
|
||||
log_message "ERROR" "Environment variable $db_env_var not found for $service in pod $POD_NAME"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (missing $db_env_var)")
|
||||
return
|
||||
fi
|
||||
|
||||
# Log redacted database URL for debugging
|
||||
DB_URL=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "echo \$"$db_env_var"" 2>/dev/null | sed 's/\(password=\)[^@]*/\1[REDACTED]/')
|
||||
log_message "INFO" "Using database URL for $service: $DB_URL"
|
||||
|
||||
# Perform async database connectivity check
|
||||
DB_CHECK_OUTPUT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 -c \"import asyncio; from sqlalchemy.ext.asyncio import create_async_engine; async def check_db(): engine = create_async_engine(os.getenv('$db_env_var')); async with engine.connect() as conn: pass; await engine.dispose(); print('DB OK'); asyncio.run(check_db())\" 2>&1" || echo "DB ERROR")
|
||||
if [[ "$DB_CHECK_OUTPUT" == *"DB OK"* ]]; then
|
||||
echo -e "${GREEN}✓ Database connection verified${NC}"
|
||||
log_message "INFO" "Database connection verified for $service"
|
||||
else
|
||||
echo -e "${RED}✗ Database connection failed for $service${NC}"
|
||||
echo -e "${YELLOW}Error details: $DB_CHECK_OUTPUT${NC}"
|
||||
log_message "ERROR" "Database connection failed for $service: $DB_CHECK_OUTPUT"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (database connection failed)")
|
||||
return
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}Skipping database connectivity check (--skip-db-check)${NC}"
|
||||
log_message "INFO" "Skipped database connectivity check for $service"
|
||||
fi
|
||||
|
||||
# Reset alembic version tracking
|
||||
echo -e "${YELLOW}Resetting alembic version tracking...${NC}"
|
||||
kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH alembic downgrade base" 2>&1 | tee -a "$LOG_FILE" | grep -v "^INFO" || true
|
||||
log_message "INFO" "Attempted alembic downgrade for $service"
|
||||
|
||||
# Option 1: Complete database schema reset using CASCADE
|
||||
echo -e "${YELLOW}Performing complete database schema reset...${NC}"
|
||||
SCHEMA_DROP_RESULT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 << 'EOFPYTHON'
|
||||
import asyncio
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
async def reset_database():
|
||||
try:
|
||||
engine = create_async_engine(os.getenv('$db_env_var'))
|
||||
async with engine.begin() as conn:
|
||||
# Drop and recreate public schema - cleanest approach
|
||||
await conn.execute(text('DROP SCHEMA IF EXISTS public CASCADE'))
|
||||
await conn.execute(text('CREATE SCHEMA public'))
|
||||
await conn.execute(text('GRANT ALL ON SCHEMA public TO PUBLIC'))
|
||||
await engine.dispose()
|
||||
print('SUCCESS: Database schema reset complete')
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f'ERROR: {str(e)}')
|
||||
return 1
|
||||
|
||||
exit(asyncio.run(reset_database()))
|
||||
EOFPYTHON
|
||||
" 2>&1)
|
||||
|
||||
echo "$SCHEMA_DROP_RESULT" >> "$LOG_FILE"
|
||||
|
||||
if echo "$SCHEMA_DROP_RESULT" | grep -q "SUCCESS"; then
|
||||
echo -e "${GREEN}✓ Database schema reset successfully${NC}"
|
||||
log_message "INFO" "Database schema reset for $service"
|
||||
else
|
||||
echo -e "${RED}✗ Database schema reset failed${NC}"
|
||||
echo -e "${YELLOW}Error details:${NC}"
|
||||
echo "$SCHEMA_DROP_RESULT"
|
||||
log_message "ERROR" "Database schema reset failed for $service: $SCHEMA_DROP_RESULT"
|
||||
|
||||
# Try alternative approach: Drop individual tables from database (not just models)
|
||||
echo -e "${YELLOW}Attempting alternative: dropping all existing tables individually...${NC}"
|
||||
TABLE_DROP_RESULT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 << 'EOFPYTHON'
|
||||
import asyncio
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
async def drop_all_tables():
|
||||
try:
|
||||
engine = create_async_engine(os.getenv('$db_env_var'))
|
||||
async with engine.begin() as conn:
|
||||
# Get all tables from database
|
||||
result = await conn.execute(text(\"\"\"
|
||||
SELECT tablename
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
\"\"\"))
|
||||
tables = [row[0] for row in result]
|
||||
|
||||
# Drop each table
|
||||
for table in tables:
|
||||
await conn.execute(text(f'DROP TABLE IF EXISTS \"{table}\" CASCADE'))
|
||||
|
||||
print(f'SUCCESS: Dropped {len(tables)} tables: {tables}')
|
||||
await engine.dispose()
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f'ERROR: {str(e)}')
|
||||
return 1
|
||||
|
||||
exit(asyncio.run(drop_all_tables()))
|
||||
EOFPYTHON
|
||||
" 2>&1)
|
||||
|
||||
echo "$TABLE_DROP_RESULT" >> "$LOG_FILE"
|
||||
|
||||
if echo "$TABLE_DROP_RESULT" | grep -q "SUCCESS"; then
|
||||
echo -e "${GREEN}✓ All tables dropped successfully${NC}"
|
||||
log_message "INFO" "All tables dropped for $service"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to drop tables${NC}"
|
||||
echo -e "${YELLOW}Error details:${NC}"
|
||||
echo "$TABLE_DROP_RESULT"
|
||||
log_message "ERROR" "Failed to drop tables for $service: $TABLE_DROP_RESULT"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (database cleanup failed)")
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify database is empty
|
||||
echo -e "${YELLOW}Verifying database is clean...${NC}"
|
||||
VERIFY_RESULT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 << 'EOFPYTHON'
|
||||
import asyncio
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
async def verify_empty():
|
||||
engine = create_async_engine(os.getenv('$db_env_var'))
|
||||
async with engine.connect() as conn:
|
||||
result = await conn.execute(text(\"\"\"
|
||||
SELECT COUNT(*)
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
\"\"\"))
|
||||
count = result.scalar()
|
||||
print(f'Tables remaining: {count}')
|
||||
await engine.dispose()
|
||||
return count
|
||||
|
||||
exit(asyncio.run(verify_empty()))
|
||||
EOFPYTHON
|
||||
" 2>&1)
|
||||
|
||||
echo "$VERIFY_RESULT" >> "$LOG_FILE"
|
||||
echo -e "${BLUE}$VERIFY_RESULT${NC}"
|
||||
|
||||
# Initialize alembic version table after schema reset
|
||||
echo -e "${YELLOW}Initializing alembic version tracking...${NC}"
|
||||
ALEMBIC_INIT_OUTPUT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH alembic stamp base" 2>&1)
|
||||
ALEMBIC_INIT_EXIT_CODE=$?
|
||||
|
||||
echo "$ALEMBIC_INIT_OUTPUT" >> "$LOG_FILE"
|
||||
|
||||
if [ $ALEMBIC_INIT_EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Alembic version tracking initialized${NC}"
|
||||
log_message "INFO" "Alembic version tracking initialized for $service"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Alembic initialization warning (may be normal)${NC}"
|
||||
log_message "WARNING" "Alembic initialization for $service: $ALEMBIC_INIT_OUTPUT"
|
||||
fi
|
||||
|
||||
# Remove old migration files in pod
|
||||
echo -e "${YELLOW}Removing old migration files in pod...${NC}"
|
||||
kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "rm -rf /app/migrations/versions/*.py /app/migrations/versions/__pycache__" 2>>"$LOG_FILE" || log_message "WARNING" "Failed to remove old migration files for $service"
|
||||
|
||||
# Ensure dependencies
|
||||
echo -e "${YELLOW}Ensuring python-dateutil and asyncpg are installed...${NC}"
|
||||
kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "python3 -m pip install --quiet python-dateutil asyncpg" 2>>"$LOG_FILE"
|
||||
|
||||
# Generate migration
|
||||
echo -e "${YELLOW}Running alembic autogenerate in pod...${NC}"
|
||||
MIGRATION_TIMESTAMP=$(date +%Y%m%d_%H%M)
|
||||
MIGRATION_OUTPUT=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH python3 -m alembic revision --autogenerate -m \"initial_schema_$MIGRATION_TIMESTAMP\"" 2>&1)
|
||||
MIGRATION_EXIT_CODE=$?
|
||||
|
||||
echo "$MIGRATION_OUTPUT" >> "$LOG_FILE"
|
||||
|
||||
if [ $MIGRATION_EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Migration generated in pod${NC}"
|
||||
log_message "INFO" "Migration generated for $service"
|
||||
|
||||
# Copy migration file
|
||||
MIGRATION_FILE=$(kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "ls -t /app/migrations/versions/*.py 2>/dev/null | head -1" || echo "")
|
||||
if [ -z "$MIGRATION_FILE" ]; then
|
||||
echo -e "${RED}✗ No migration file found in pod${NC}"
|
||||
log_message "ERROR" "No migration file generated for $service"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (no file generated)")
|
||||
return
|
||||
fi
|
||||
|
||||
MIGRATION_FILENAME=$(basename "$MIGRATION_FILE")
|
||||
mkdir -p "services/$service_dir/migrations/versions"
|
||||
|
||||
# Copy file with better error handling
|
||||
echo -e "${YELLOW}Copying migration file from pod...${NC}"
|
||||
CP_OUTPUT=$(kubectl cp -n "$NAMESPACE" "$POD_NAME:$MIGRATION_FILE" "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" -c "$CONTAINER" 2>&1)
|
||||
CP_EXIT_CODE=$?
|
||||
|
||||
echo "$CP_OUTPUT" >> "$LOG_FILE"
|
||||
|
||||
# Verify the file was actually copied
|
||||
if [ $CP_EXIT_CODE -eq 0 ] && [ -f "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" ]; then
|
||||
LOCAL_FILE_SIZE=$(wc -c < "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" | tr -d ' ')
|
||||
|
||||
if [ "$LOCAL_FILE_SIZE" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓ Migration file copied: $MIGRATION_FILENAME ($LOCAL_FILE_SIZE bytes)${NC}"
|
||||
log_message "INFO" "Copied $MIGRATION_FILENAME for $service ($LOCAL_FILE_SIZE bytes)"
|
||||
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
|
||||
|
||||
# Validate migration content
|
||||
echo -e "${YELLOW}Validating migration content...${NC}"
|
||||
if grep -E "op\.(create_table|add_column|create_index|alter_column|drop_table|drop_column|create_foreign_key)" "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" >/dev/null; then
|
||||
echo -e "${GREEN}✓ Migration contains schema operations${NC}"
|
||||
log_message "INFO" "Migration contains schema operations for $service"
|
||||
elif grep -q "pass" "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" && grep -q "def upgrade()" "services/$service_dir/migrations/versions/$MIGRATION_FILENAME"; then
|
||||
echo -e "${YELLOW}⚠ WARNING: Migration is empty (no schema changes detected)${NC}"
|
||||
echo -e "${YELLOW}⚠ This usually means tables already exist in database matching the models${NC}"
|
||||
log_message "WARNING" "Empty migration generated for $service - possible database cleanup issue"
|
||||
else
|
||||
echo -e "${GREEN}✓ Migration file created${NC}"
|
||||
fi
|
||||
|
||||
# Display summary
|
||||
echo -e "${BLUE}Migration summary:${NC}"
|
||||
grep -E "^def (upgrade|downgrade)" "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" | head -2
|
||||
echo -e "${BLUE}Operations:${NC}"
|
||||
grep "op\." "services/$service_dir/migrations/versions/$MIGRATION_FILENAME" | head -5 || echo " (none found)"
|
||||
else
|
||||
echo -e "${RED}✗ Migration file is empty (0 bytes)${NC}"
|
||||
log_message "ERROR" "Migration file is empty for $service"
|
||||
rm -f "services/$service_dir/migrations/versions/$MIGRATION_FILENAME"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (empty file)")
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Failed to copy migration file${NC}"
|
||||
echo -e "${YELLOW}kubectl cp exit code: $CP_EXIT_CODE${NC}"
|
||||
echo -e "${YELLOW}kubectl cp output: $CP_OUTPUT${NC}"
|
||||
log_message "ERROR" "Failed to copy migration file for $service: $CP_OUTPUT"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (copy failed)")
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Failed to generate migration${NC}"
|
||||
log_message "ERROR" "Failed to generate migration for $service"
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
FAILED_SERVICES+=("$service (generation failed)")
|
||||
fi
|
||||
}
|
||||
|
||||
# Process services sequentially
|
||||
for service in "${SERVICES[@]}"; do
|
||||
process_service "$service"
|
||||
done
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Summary${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${GREEN}✓ Successful: $SUCCESS_COUNT services${NC}"
|
||||
echo -e "${RED}✗ Failed: $FAILED_COUNT services${NC}"
|
||||
|
||||
if [ "$FAILED_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo -e "${RED}Failed services:${NC}"
|
||||
for failed_service in "${FAILED_SERVICES[@]}"; do
|
||||
echo -e "${RED} - $failed_service${NC}"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Backup location: $BACKUP_DIR${NC}"
|
||||
echo -e "${YELLOW}Log file: $LOG_FILE${NC}"
|
||||
echo ""
|
||||
|
||||
# Apply migrations if requested
|
||||
if [ "$APPLY_MIGRATIONS" = true ] && [ "$DRY_RUN" = false ] && [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Applying Migrations${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
APPLIED_COUNT=0
|
||||
APPLY_FAILED_COUNT=0
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
service_dir=$(echo "$service" | tr '-' '_')
|
||||
local db_env_var=$(echo "$service" | tr '[:lower:]-' '[:upper:]_')_DATABASE_URL
|
||||
if [ ! -d "services/$service_dir/migrations/versions" ] || [ -z "$(ls services/$service_dir/migrations/versions/*.py 2>/dev/null)" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Applying migrations for: $service${NC}"
|
||||
POD_NAME=$(get_running_pod "$service")
|
||||
if [ -z "$POD_NAME" ]; then
|
||||
echo -e "${YELLOW}⚠ Pod not found for $service, skipping...${NC}"
|
||||
log_message "WARNING" "No running pod found for $service during migration application"
|
||||
continue
|
||||
fi
|
||||
|
||||
CONTAINER="${service}-${CONTAINER_SUFFIX}"
|
||||
if ! kubectl get pod -n "$NAMESPACE" "$POD_NAME" -o jsonpath='{.spec.containers[*].name}' | grep -qw "$CONTAINER"; then
|
||||
echo -e "${RED}✗ Container $CONTAINER not found in pod $POD_NAME, skipping${NC}"
|
||||
log_message "ERROR" "Container $CONTAINER not found in pod $POD_NAME for $service"
|
||||
continue
|
||||
fi
|
||||
|
||||
if kubectl exec -n "$NAMESPACE" "$POD_NAME" -c "$CONTAINER" -- sh -c "cd /app && PYTHONPATH=/app:/app/shared:\$PYTHONPATH alembic upgrade head" 2>>"$LOG_FILE"; then
|
||||
echo -e "${GREEN}✓ Migrations applied successfully for $service${NC}"
|
||||
log_message "INFO" "Migrations applied for $service"
|
||||
APPLIED_COUNT=$((APPLIED_COUNT + 1))
|
||||
else
|
||||
echo -e "${RED}✗ Failed to apply migrations for $service${NC}"
|
||||
log_message "ERROR" "Failed to apply migrations for $service"
|
||||
APPLY_FAILED_COUNT=$((APPLY_FAILED_COUNT + 1))
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "${BLUE}Migration Application Summary:${NC}"
|
||||
echo -e "${GREEN}✓ Applied: $APPLIED_COUNT services${NC}"
|
||||
echo -e "${RED}✗ Failed: $APPLY_FAILED_COUNT services${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Clean up temporary files
|
||||
rm -f /tmp/*_migration.log /tmp/*_downgrade.log /tmp/*_apply.log 2>/dev/null || true
|
||||
log_message "INFO" "Cleaned up temporary files"
|
||||
|
||||
echo -e "${BLUE}Next steps:${NC}"
|
||||
echo -e "${YELLOW}1. Review the generated migrations in services/*/migrations/versions/${NC}"
|
||||
echo -e "${YELLOW}2. Compare with the backup in $BACKUP_DIR${NC}"
|
||||
echo -e "${YELLOW}3. Check logs in $LOG_FILE for details${NC}"
|
||||
echo -e "${YELLOW}4. Test migrations by applying them:${NC}"
|
||||
echo -e " ${GREEN}kubectl exec -n $NAMESPACE -it <pod-name> -c <service>-${CONTAINER_SUFFIX} -- alembic upgrade head${NC}"
|
||||
echo -e "${YELLOW}5. Verify tables were created:${NC}"
|
||||
echo -e " ${GREEN}kubectl exec -n $NAMESPACE -it <pod-name> -c <service>-${CONTAINER_SUFFIX} -- python3 -c \"${NC}"
|
||||
echo -e " ${GREEN}import asyncio; from sqlalchemy.ext.asyncio import create_async_engine; from sqlalchemy import inspect; async def check_tables(): engine = create_async_engine(os.getenv('<SERVICE>_DATABASE_URL')); async with engine.connect() as conn: print(inspect(conn).get_table_names()); await engine.dispose(); asyncio.run(check_tables())${NC}"
|
||||
echo -e " ${GREEN}\"${NC}"
|
||||
echo -e "${YELLOW}6. If issues occur, restore from backup:${NC}"
|
||||
echo -e " ${GREEN}cp -r $BACKUP_DIR/*/versions/* services/*/migrations/versions/${NC}"
|
||||
echo ""
|
||||
23
infrastructure/scripts/maintenance/remove-imagepullsecrets.sh
Executable file
23
infrastructure/scripts/maintenance/remove-imagepullsecrets.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# Script to remove imagePullSecrets from all Kubernetes manifests
|
||||
# Run this from the repository root: ./infrastructure/kubernetes/remove-imagepullsecrets.sh
|
||||
|
||||
echo "Removing imagePullSecrets from all Kubernetes manifests..."
|
||||
|
||||
# Find all YAML files in base directory and remove imagePullSecrets
|
||||
find infrastructure/kubernetes/base -name "*.yaml" -type f | while read file; do
|
||||
# Create backup
|
||||
cp "$file" "$file.bak"
|
||||
|
||||
# Remove imagePullSecrets and the following line (name: dockerhub-creds)
|
||||
sed -i '/imagePullSecrets:/,+1d' "$file"
|
||||
|
||||
echo "Processed: $file"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ Done! Removed imagePullSecrets from all manifests"
|
||||
echo "Backup files created with .bak extension"
|
||||
echo ""
|
||||
echo "Verify removal:"
|
||||
grep -r "imagePullSecrets" infrastructure/kubernetes/base/ && echo "⚠️ WARNING: Some files still contain imagePullSecrets" || echo "✅ All imagePullSecrets removed successfully"
|
||||
145
infrastructure/scripts/maintenance/run_subscription_integration_test.sh
Executable file
145
infrastructure/scripts/maintenance/run_subscription_integration_test.sh
Executable file
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to run the subscription creation integration test inside Kubernetes
|
||||
# This script creates a test pod that runs the integration test
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting subscription creation integration test..."
|
||||
|
||||
# Check if there's already a test pod running
|
||||
EXISTING_POD=$(kubectl get pod subscription-integration-test -n bakery-ia 2>/dev/null || echo "")
|
||||
if [ -n "$EXISTING_POD" ]; then
|
||||
echo "🧹 Cleaning up existing test pod..."
|
||||
kubectl delete pod subscription-integration-test -n bakery-ia --wait=true
|
||||
echo "✅ Existing pod cleaned up"
|
||||
fi
|
||||
|
||||
# Determine the correct image to use by checking the existing tenant service deployment
|
||||
IMAGE=$(kubectl get deployment tenant-service -n bakery-ia -o jsonpath='{.spec.template.spec.containers[0].image}')
|
||||
|
||||
if [ -z "$IMAGE" ]; then
|
||||
echo "❌ Could not determine tenant service image. Is the tenant service deployed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Using image: $IMAGE"
|
||||
|
||||
# Create a test pod that runs the integration test with a simple command
|
||||
echo "🔧 Creating test pod..."
|
||||
kubectl run subscription-integration-test \
|
||||
--image="$IMAGE" \
|
||||
--namespace=bakery-ia \
|
||||
--restart=Never \
|
||||
--env="GATEWAY_URL=http://gateway-service:8000" \
|
||||
--env="STRIPE_SECRET_KEY=$(kubectl get secret payment-secrets -n bakery-ia -o jsonpath='{.data.STRIPE_SECRET_KEY}' | base64 -d)" \
|
||||
--command -- /bin/sh -c "
|
||||
set -e
|
||||
echo '🧪 Setting up test environment...' &&
|
||||
cd /app &&
|
||||
echo '📋 Installing test dependencies...' &&
|
||||
pip install pytest pytest-asyncio httpx stripe --quiet &&
|
||||
echo '✅ Dependencies installed' &&
|
||||
echo '' &&
|
||||
echo '🔧 Configuring test to use internal gateway service URL...' &&
|
||||
# Backup original file before modification
|
||||
cp tests/integration/test_subscription_creation_flow.py tests/integration/test_subscription_creation_flow.py.bak &&
|
||||
# Update the test file to use the internal gateway service URL
|
||||
sed -i 's|self.base_url = \"https://bakery-ia.local\"|self.base_url = \"http://gateway-service:8000\"|g' tests/integration/test_subscription_creation_flow.py &&
|
||||
echo '✅ Test configured for internal Kubernetes networking' &&
|
||||
echo '' &&
|
||||
echo '🧪 Running subscription creation integration test...' &&
|
||||
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' &&
|
||||
python -m pytest tests/integration/test_subscription_creation_flow.py -v --tb=short -s --color=yes &&
|
||||
TEST_RESULT=\$? &&
|
||||
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' &&
|
||||
echo '' &&
|
||||
echo '📋 Restoring original test file...' &&
|
||||
mv tests/integration/test_subscription_creation_flow.py.bak tests/integration/test_subscription_creation_flow.py &&
|
||||
echo '✅ Original test file restored' &&
|
||||
echo '' &&
|
||||
if [ \$TEST_RESULT -eq 0 ]; then
|
||||
echo '🎉 Integration test PASSED!'
|
||||
else
|
||||
echo '❌ Integration test FAILED!'
|
||||
fi &&
|
||||
exit \$TEST_RESULT
|
||||
"
|
||||
|
||||
# Wait for the test pod to start
|
||||
echo "⏳ Waiting for test pod to start..."
|
||||
sleep 5
|
||||
|
||||
# Follow the logs in real-time
|
||||
echo "📋 Following test execution logs..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Stream logs while the pod is running
|
||||
kubectl logs -f subscription-integration-test -n bakery-ia 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Wait for the pod to complete with a timeout
|
||||
echo "⏳ Waiting for test pod to complete..."
|
||||
TIMEOUT=600 # 10 minutes timeout
|
||||
COUNTER=0
|
||||
while [ $COUNTER -lt $TIMEOUT ]; do
|
||||
POD_STATUS=$(kubectl get pod subscription-integration-test -n bakery-ia -o jsonpath='{.status.phase}' 2>/dev/null)
|
||||
|
||||
if [ "$POD_STATUS" == "Succeeded" ] || [ "$POD_STATUS" == "Failed" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
COUNTER=$((COUNTER + 2))
|
||||
done
|
||||
|
||||
if [ $COUNTER -ge $TIMEOUT ]; then
|
||||
echo "⏰ Timeout waiting for test to complete after $TIMEOUT seconds"
|
||||
echo "📋 Fetching final logs before cleanup..."
|
||||
kubectl logs subscription-integration-test -n bakery-ia --tail=100
|
||||
echo "🧹 Cleaning up test pod due to timeout..."
|
||||
kubectl delete pod subscription-integration-test -n bakery-ia --wait=false
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the final status
|
||||
POD_STATUS=$(kubectl get pod subscription-integration-test -n bakery-ia -o jsonpath='{.status.phase}')
|
||||
CONTAINER_EXIT_CODE=$(kubectl get pod subscription-integration-test -n bakery-ia -o jsonpath='{.status.containerStatuses[0].state.terminated.exitCode}' 2>/dev/null || echo "unknown")
|
||||
|
||||
echo ""
|
||||
echo "📊 Test Results:"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Pod Status: $POD_STATUS"
|
||||
echo "Exit Code: $CONTAINER_EXIT_CODE"
|
||||
|
||||
# Determine if the test passed
|
||||
if [ "$POD_STATUS" == "Succeeded" ] && [ "$CONTAINER_EXIT_CODE" == "0" ]; then
|
||||
echo ""
|
||||
echo "✅ Integration test PASSED!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
RESULT=0
|
||||
else
|
||||
echo ""
|
||||
echo "❌ Integration test FAILED!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Show additional logs if failed
|
||||
if [ "$POD_STATUS" == "Failed" ]; then
|
||||
echo ""
|
||||
echo "📋 Last 50 lines of logs:"
|
||||
kubectl logs subscription-integration-test -n bakery-ia --tail=50
|
||||
fi
|
||||
|
||||
RESULT=1
|
||||
fi
|
||||
|
||||
# Clean up the test pod
|
||||
echo ""
|
||||
echo "🧹 Cleaning up test pod..."
|
||||
kubectl delete pod subscription-integration-test -n bakery-ia --wait=false
|
||||
|
||||
echo "🏁 Integration test process completed!"
|
||||
exit $RESULT
|
||||
649
infrastructure/scripts/maintenance/setup-https.sh
Executable file
649
infrastructure/scripts/maintenance/setup-https.sh
Executable file
@@ -0,0 +1,649 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Bakery IA HTTPS Setup Script
|
||||
# This script sets up HTTPS with cert-manager and Let's Encrypt for local development
|
||||
|
||||
# Remove -e to handle errors more gracefully
|
||||
set -u
|
||||
|
||||
echo "🔒 Setting up HTTPS for Bakery IA with cert-manager and Let's Encrypt"
|
||||
echo "==============================================================="
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
print_status "Checking prerequisites..."
|
||||
|
||||
# Check required tools
|
||||
local missing_tools=()
|
||||
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
missing_tools+=("kubectl")
|
||||
fi
|
||||
|
||||
if ! command -v kind &> /dev/null; then
|
||||
missing_tools+=("kind")
|
||||
fi
|
||||
|
||||
if ! command -v skaffold &> /dev/null; then
|
||||
missing_tools+=("skaffold")
|
||||
fi
|
||||
|
||||
if ! command -v colima &> /dev/null; then
|
||||
missing_tools+=("colima")
|
||||
fi
|
||||
|
||||
# Report missing tools
|
||||
if [ ${#missing_tools[@]} -ne 0 ]; then
|
||||
print_error "Missing required tools: ${missing_tools[*]}"
|
||||
print_error "Please install them with: brew install ${missing_tools[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if Colima is running
|
||||
if ! colima status --profile k8s-local &> /dev/null; then
|
||||
print_warning "Colima is not running. Starting Colima..."
|
||||
colima start --cpu 8 --memory 16 --disk 100 --runtime docker --profile k8s-local
|
||||
if [ $? -ne 0 ]; then
|
||||
print_error "Failed to start Colima. Please check your Docker installation."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Colima started successfully"
|
||||
fi
|
||||
|
||||
# Check if cluster is running or exists
|
||||
local cluster_exists=false
|
||||
local cluster_running=false
|
||||
|
||||
# Check if Kind cluster exists
|
||||
if kind get clusters | grep -q "bakery-ia-local"; then
|
||||
cluster_exists=true
|
||||
print_status "Kind cluster 'bakery-ia-local' already exists"
|
||||
|
||||
# Check if kubectl can connect to it
|
||||
if kubectl cluster-info --context kind-bakery-ia-local &> /dev/null; then
|
||||
cluster_running=true
|
||||
print_success "Kubernetes cluster is running and accessible"
|
||||
else
|
||||
print_warning "Kind cluster exists but is not accessible via kubectl"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Handle cluster creation/recreation
|
||||
if [ "$cluster_exists" = true ] && [ "$cluster_running" = false ]; then
|
||||
print_warning "Kind cluster exists but is not running. Recreating..."
|
||||
kind delete cluster --name bakery-ia-local || true
|
||||
cluster_exists=false
|
||||
fi
|
||||
|
||||
if [ "$cluster_exists" = false ]; then
|
||||
print_warning "Creating new Kind cluster..."
|
||||
if [ ! -f "kind-config.yaml" ]; then
|
||||
print_error "kind-config.yaml not found. Please ensure you're running this script from the project root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if kind create cluster --config kind-config.yaml; then
|
||||
print_success "Kind cluster created successfully"
|
||||
else
|
||||
print_error "Failed to create Kind cluster. Please check your Kind installation."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure we're using the correct kubectl context
|
||||
kubectl config use-context kind-bakery-ia-local || {
|
||||
print_error "Failed to set kubectl context to kind-bakery-ia-local"
|
||||
exit 1
|
||||
}
|
||||
|
||||
print_success "Prerequisites check passed"
|
||||
}
|
||||
|
||||
# Install cert-manager
|
||||
install_cert_manager() {
|
||||
print_status "Installing cert-manager..."
|
||||
|
||||
# Check if cert-manager is already installed
|
||||
if kubectl get namespace cert-manager &> /dev/null; then
|
||||
print_warning "cert-manager namespace already exists. Checking if installation is complete..."
|
||||
|
||||
# Check if pods are running
|
||||
if kubectl get pods -n cert-manager | grep -q "Running"; then
|
||||
print_success "cert-manager is already installed and running"
|
||||
return 0
|
||||
else
|
||||
print_status "cert-manager exists but pods are not ready. Waiting..."
|
||||
fi
|
||||
else
|
||||
# Install cert-manager
|
||||
print_status "Installing cert-manager from official release..."
|
||||
if kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml; then
|
||||
print_success "cert-manager installation started"
|
||||
else
|
||||
print_error "Failed to install cert-manager. Please check your internet connection and try again."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Wait for cert-manager namespace to be created
|
||||
print_status "Waiting for cert-manager namespace..."
|
||||
for i in {1..30}; do
|
||||
if kubectl get namespace cert-manager &> /dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Wait for cert-manager pods to be created
|
||||
print_status "Waiting for cert-manager pods to be created..."
|
||||
for i in {1..60}; do
|
||||
if kubectl get pods -n cert-manager &> /dev/null && [ $(kubectl get pods -n cert-manager --no-headers | wc -l) -ge 3 ]; then
|
||||
print_success "cert-manager pods created"
|
||||
break
|
||||
fi
|
||||
print_status "Waiting for cert-manager pods... (attempt $i/60)"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# Wait for cert-manager pods to be ready
|
||||
print_status "Waiting for cert-manager pods to be ready..."
|
||||
|
||||
# Use more reliable selectors for cert-manager components
|
||||
local components=(
|
||||
"app.kubernetes.io/name=cert-manager"
|
||||
"app.kubernetes.io/name=cainjector"
|
||||
"app.kubernetes.io/name=webhook"
|
||||
)
|
||||
local component_names=("cert-manager" "cert-manager-cainjector" "cert-manager-webhook")
|
||||
|
||||
for i in "${!components[@]}"; do
|
||||
local selector="${components[$i]}"
|
||||
local name="${component_names[$i]}"
|
||||
|
||||
print_status "Waiting for $name to be ready..."
|
||||
|
||||
# First check if pods exist with this selector
|
||||
local pod_count=0
|
||||
for attempt in {1..30}; do
|
||||
pod_count=$(kubectl get pods -n cert-manager -l "$selector" --no-headers 2>/dev/null | wc -l)
|
||||
if [ "$pod_count" -gt 0 ]; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "$pod_count" -eq 0 ]; then
|
||||
print_warning "No pods found for $name with selector $selector, trying alternative approach..."
|
||||
# Fallback: wait for any pods containing the component name
|
||||
if kubectl wait --for=condition=ready pod -n cert-manager --all --timeout=300s 2>/dev/null; then
|
||||
print_success "All cert-manager pods are ready"
|
||||
break
|
||||
else
|
||||
print_warning "$name pods not found, but continuing..."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Wait for the specific component to be ready
|
||||
if kubectl wait --for=condition=ready pod -l "$selector" -n cert-manager --timeout=300s 2>/dev/null; then
|
||||
print_success "$name is ready"
|
||||
else
|
||||
print_warning "$name is taking longer than expected. Checking status..."
|
||||
kubectl get pods -n cert-manager -l "$selector" 2>/dev/null || true
|
||||
|
||||
# Continue anyway, sometimes it works despite timeout
|
||||
print_warning "Continuing with setup. $name may still be starting..."
|
||||
fi
|
||||
done
|
||||
|
||||
# Final verification
|
||||
if kubectl get pods -n cert-manager | grep -q "Running"; then
|
||||
print_success "cert-manager installed successfully"
|
||||
else
|
||||
print_warning "cert-manager installation may not be complete. Current status:"
|
||||
kubectl get pods -n cert-manager
|
||||
print_status "Continuing with setup anyway..."
|
||||
fi
|
||||
}
|
||||
|
||||
# Install NGINX Ingress Controller
|
||||
install_nginx_ingress() {
|
||||
print_status "Installing NGINX Ingress Controller for Kind..."
|
||||
|
||||
# Check if NGINX Ingress is already installed
|
||||
if kubectl get namespace ingress-nginx &> /dev/null; then
|
||||
print_warning "NGINX Ingress Controller namespace already exists. Checking status..."
|
||||
|
||||
# Check if controller is running
|
||||
if kubectl get pods -n ingress-nginx -l app.kubernetes.io/component=controller | grep -q "Running"; then
|
||||
print_success "NGINX Ingress Controller is already running"
|
||||
else
|
||||
print_status "NGINX Ingress Controller exists but not ready. Waiting..."
|
||||
kubectl wait --namespace ingress-nginx \
|
||||
--for=condition=ready pod \
|
||||
--selector=app.kubernetes.io/component=controller \
|
||||
--timeout=300s 2>/dev/null || {
|
||||
print_warning "Ingress controller taking longer than expected, but continuing..."
|
||||
}
|
||||
fi
|
||||
else
|
||||
# Install NGINX Ingress Controller for Kind (updated URL)
|
||||
print_status "Installing NGINX Ingress Controller for Kind..."
|
||||
if kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml; then
|
||||
print_success "NGINX Ingress Controller installation started"
|
||||
|
||||
# Wait for ingress controller to be ready
|
||||
print_status "Waiting for NGINX Ingress Controller to be ready..."
|
||||
kubectl wait --namespace ingress-nginx \
|
||||
--for=condition=ready pod \
|
||||
--selector=app.kubernetes.io/component=controller \
|
||||
--timeout=300s 2>/dev/null || {
|
||||
print_warning "Ingress controller taking longer than expected, but continuing..."
|
||||
}
|
||||
else
|
||||
print_error "Failed to install NGINX Ingress Controller"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Configure ingress for permanent localhost access
|
||||
print_status "Configuring permanent localhost access..."
|
||||
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p '{"spec":{"type":"NodePort","ports":[{"name":"http","port":80,"targetPort":"http","nodePort":30080},{"name":"https","port":443,"targetPort":"https","nodePort":30443}]}}' || true
|
||||
|
||||
print_success "NGINX Ingress Controller configured successfully"
|
||||
}
|
||||
|
||||
# Setup cluster issuers
|
||||
setup_cluster_issuers() {
|
||||
print_status "Setting up cluster issuers..."
|
||||
|
||||
# Check if cert-manager components exist
|
||||
if [ ! -f "infrastructure/platform/cert-manager/cluster-issuer-staging.yaml" ]; then
|
||||
print_error "cert-manager component files not found. Please ensure you're running this script from the project root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply cluster issuers
|
||||
print_status "Applying cluster issuers..."
|
||||
|
||||
local issuer_files=(
|
||||
"infrastructure/platform/cert-manager/cluster-issuer-staging.yaml"
|
||||
"infrastructure/platform/cert-manager/local-ca-issuer.yaml"
|
||||
"infrastructure/platform/cert-manager/cluster-issuer-production.yaml"
|
||||
)
|
||||
|
||||
for issuer_file in "${issuer_files[@]}"; do
|
||||
if [ -f "$issuer_file" ]; then
|
||||
print_status "Applying $issuer_file..."
|
||||
kubectl apply -f "$issuer_file" || {
|
||||
print_warning "Failed to apply $issuer_file, but continuing..."
|
||||
}
|
||||
else
|
||||
print_warning "$issuer_file not found, skipping..."
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for the issuers to be created
|
||||
print_status "Waiting for cluster issuers to be ready..."
|
||||
sleep 15
|
||||
|
||||
# Check if issuers are ready
|
||||
print_status "Checking cluster issuer status..."
|
||||
kubectl get clusterissuers 2>/dev/null || print_warning "No cluster issuers found yet"
|
||||
|
||||
# Verify that the local CA issuer is ready (if it exists)
|
||||
if kubectl get clusterissuer local-ca-issuer &> /dev/null; then
|
||||
for i in {1..10}; do
|
||||
local issuer_ready=$(kubectl get clusterissuer local-ca-issuer -o jsonpath='{.status.conditions[0].type}' 2>/dev/null || echo "")
|
||||
if [[ "$issuer_ready" == "Ready" ]]; then
|
||||
print_success "Local CA issuer is ready"
|
||||
break
|
||||
fi
|
||||
print_status "Waiting for local CA issuer to be ready... (attempt $i/10)"
|
||||
sleep 10
|
||||
done
|
||||
else
|
||||
print_warning "Local CA issuer not found, skipping readiness check"
|
||||
fi
|
||||
|
||||
print_success "Cluster issuers configured successfully"
|
||||
}
|
||||
|
||||
# Deploy the application with HTTPS using Skaffold
|
||||
deploy_with_https() {
|
||||
print_status "Deploying Bakery IA with HTTPS support using Skaffold..."
|
||||
|
||||
# Check if Skaffold is available
|
||||
if ! command -v skaffold &> /dev/null; then
|
||||
print_error "Skaffold is not installed. Please install skaffold first:"
|
||||
print_error "brew install skaffold"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if skaffold.yaml exists
|
||||
if [ ! -f "skaffold.yaml" ]; then
|
||||
print_error "skaffold.yaml not found. Please ensure you're running this script from the project root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Deploy with Skaffold (builds and deploys automatically with HTTPS support)
|
||||
print_status "Building and deploying with Skaffold (dev profile includes HTTPS)..."
|
||||
if skaffold run --profile=dev; then
|
||||
print_success "Skaffold deployment started"
|
||||
else
|
||||
print_warning "Skaffold deployment had issues, but continuing..."
|
||||
fi
|
||||
|
||||
# Wait for namespace to be created
|
||||
print_status "Waiting for bakery-ia namespace..."
|
||||
for i in {1..30}; do
|
||||
if kubectl get namespace bakery-ia &> /dev/null; then
|
||||
print_success "bakery-ia namespace found"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Check if namespace was created
|
||||
if ! kubectl get namespace bakery-ia &> /dev/null; then
|
||||
print_warning "bakery-ia namespace not found. Deployment may have failed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait for deployments to be ready
|
||||
print_status "Waiting for deployments to be ready..."
|
||||
if kubectl wait --for=condition=available --timeout=600s deployment --all -n bakery-ia 2>/dev/null; then
|
||||
print_success "All deployments are ready"
|
||||
else
|
||||
print_warning "Some deployments are taking longer than expected, but continuing..."
|
||||
fi
|
||||
|
||||
# Verify ingress exists
|
||||
if kubectl get ingress bakery-ingress -n bakery-ia &> /dev/null; then
|
||||
print_success "HTTPS ingress configured successfully"
|
||||
else
|
||||
print_warning "Ingress not found, but continuing with setup..."
|
||||
fi
|
||||
|
||||
print_success "Application deployed with HTTPS support using Skaffold"
|
||||
}
|
||||
|
||||
# Check certificate status
|
||||
check_certificates() {
|
||||
print_status "Checking certificate status..."
|
||||
|
||||
# Wait for certificate to be issued
|
||||
print_status "Waiting for certificates to be issued..."
|
||||
|
||||
# Check if certificate exists
|
||||
for i in {1..12}; do
|
||||
if kubectl get certificate bakery-ia-tls-cert -n bakery-ia &> /dev/null; then
|
||||
print_success "Certificate found"
|
||||
break
|
||||
fi
|
||||
print_status "Waiting for certificate to be created... (attempt $i/12)"
|
||||
sleep 10
|
||||
done
|
||||
|
||||
# Wait for certificate to be ready
|
||||
for i in {1..20}; do
|
||||
if kubectl get certificate bakery-ia-tls-cert -n bakery-ia -o jsonpath='{.status.conditions[0].type}' 2>/dev/null | grep -q "Ready"; then
|
||||
print_success "Certificate is ready"
|
||||
break
|
||||
fi
|
||||
print_status "Waiting for certificate to be ready... (attempt $i/20)"
|
||||
sleep 15
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📋 Certificate status:"
|
||||
kubectl get certificates -n bakery-ia 2>/dev/null || print_warning "No certificates found"
|
||||
|
||||
echo ""
|
||||
echo "🔍 Certificate details:"
|
||||
kubectl describe certificate bakery-ia-tls-cert -n bakery-ia 2>/dev/null || print_warning "Certificate not found"
|
||||
|
||||
echo ""
|
||||
echo "🔐 TLS secret status:"
|
||||
kubectl get secret bakery-ia-tls-cert -n bakery-ia 2>/dev/null || print_warning "TLS secret not found"
|
||||
}
|
||||
|
||||
# Update hosts file
|
||||
update_hosts_file() {
|
||||
print_status "Checking hosts file configuration..."
|
||||
|
||||
# Get the external IP for Kind
|
||||
EXTERNAL_IP="127.0.0.1"
|
||||
|
||||
# Check if entries exist in hosts file
|
||||
if ! grep -q "bakery-ia.local" /etc/hosts 2>/dev/null; then
|
||||
print_warning "Adding entries to /etc/hosts file for named host access..."
|
||||
|
||||
# Ask for user permission
|
||||
read -p "Do you want to add entries to /etc/hosts for named host access? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Add hosts entries with proper error handling
|
||||
{
|
||||
echo "$EXTERNAL_IP bakery-ia.local"
|
||||
echo "$EXTERNAL_IP api.bakery-ia.local"
|
||||
echo "$EXTERNAL_IP monitoring.bakery-ia.local"
|
||||
} | sudo tee -a /etc/hosts > /dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Hosts file entries added successfully"
|
||||
else
|
||||
print_error "Failed to update hosts file. You may need to add entries manually."
|
||||
fi
|
||||
else
|
||||
print_warning "Skipping hosts file update. You can still access via https://localhost"
|
||||
fi
|
||||
else
|
||||
print_success "Hosts file entries already exist"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_status "Available access methods:"
|
||||
echo " 🌐 Primary: https://localhost (no hosts file needed)"
|
||||
echo " 🏷️ Named: https://bakery-ia.local (requires hosts file)"
|
||||
echo " 🔗 API: https://localhost/api or https://api.bakery-ia.local"
|
||||
}
|
||||
|
||||
# Export CA certificate for browser trust
|
||||
export_ca_certificate() {
|
||||
print_status "Exporting CA certificate for browser trust..."
|
||||
|
||||
# Wait for CA certificate to be created
|
||||
for i in {1..10}; do
|
||||
if kubectl get secret local-ca-key-pair -n cert-manager &> /dev/null; then
|
||||
print_success "CA certificate secret found"
|
||||
break
|
||||
fi
|
||||
print_status "Waiting for CA certificate secret... (attempt $i/10)"
|
||||
sleep 10
|
||||
done
|
||||
|
||||
# Extract the CA certificate
|
||||
if kubectl get secret local-ca-key-pair -n cert-manager &> /dev/null; then
|
||||
if kubectl get secret local-ca-key-pair -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 -d > bakery-ia-ca.crt 2>/dev/null; then
|
||||
print_success "CA certificate exported as 'bakery-ia-ca.crt'"
|
||||
|
||||
# Make the certificate file readable
|
||||
chmod 644 bakery-ia-ca.crt
|
||||
else
|
||||
print_warning "Failed to extract CA certificate from secret"
|
||||
fi
|
||||
|
||||
print_warning "To trust this certificate and remove browser warnings:"
|
||||
echo ""
|
||||
echo "📱 macOS:"
|
||||
echo " 1. Double-click 'bakery-ia-ca.crt' to open Keychain Access"
|
||||
echo " 2. Find 'bakery-ia-local-ca' in the certificates list"
|
||||
echo " 3. Double-click it and set to 'Always Trust'"
|
||||
echo ""
|
||||
echo "🐧 Linux:"
|
||||
echo " sudo cp bakery-ia-ca.crt /usr/local/share/ca-certificates/"
|
||||
echo " sudo update-ca-certificates"
|
||||
echo ""
|
||||
echo "🪟 Windows:"
|
||||
echo " 1. Double-click 'bakery-ia-ca.crt'"
|
||||
echo " 2. Click 'Install Certificate'"
|
||||
echo " 3. Choose 'Trusted Root Certification Authorities'"
|
||||
echo ""
|
||||
else
|
||||
print_warning "CA certificate secret not found. HTTPS will work but with browser warnings."
|
||||
print_warning "You can still access the application at https://localhost"
|
||||
fi
|
||||
}
|
||||
|
||||
# Display access information
|
||||
display_access_info() {
|
||||
print_success "🎉 HTTPS setup completed!"
|
||||
echo ""
|
||||
echo "🌐 Access your application at:"
|
||||
echo " Primary: https://localhost"
|
||||
echo " API: https://localhost/api"
|
||||
echo " Named Host: https://bakery-ia.local (if hosts file updated)"
|
||||
echo " API Named: https://api.bakery-ia.local (if hosts file updated)"
|
||||
echo ""
|
||||
echo "🛠️ Useful commands:"
|
||||
echo " 📋 Check status: kubectl get all -n bakery-ia"
|
||||
echo " 🔍 Check ingress: kubectl get ingress -n bakery-ia"
|
||||
echo " 📜 Check certificates: kubectl get certificates -n bakery-ia"
|
||||
echo " 📝 View service logs: kubectl logs -f deployment/<service-name> -n bakery-ia"
|
||||
echo " 🚀 Development mode: skaffold dev --profile=dev"
|
||||
echo " 🧹 Clean up: skaffold delete --profile=dev"
|
||||
echo " 🔄 Restart service: kubectl rollout restart deployment/<service-name> -n bakery-ia"
|
||||
echo ""
|
||||
echo "🔧 Troubleshooting:"
|
||||
echo " 🩺 Get events: kubectl get events -n bakery-ia --sort-by='.firstTimestamp'"
|
||||
echo " 🔍 Describe pod: kubectl describe pod <pod-name> -n bakery-ia"
|
||||
echo " 📊 Resource usage: kubectl top pods -n bakery-ia"
|
||||
echo " 🔐 Certificate details: kubectl describe certificate bakery-ia-tls-cert -n bakery-ia"
|
||||
echo ""
|
||||
if [ -f "bakery-ia-ca.crt" ]; then
|
||||
print_warning "📋 Next steps:"
|
||||
echo " 1. Import 'bakery-ia-ca.crt' into your browser to remove certificate warnings"
|
||||
echo " 2. Access https://localhost to verify the setup"
|
||||
echo " 3. Run 'skaffold dev --profile=dev' for development with hot-reload"
|
||||
else
|
||||
print_warning "⚠️ Note: You may see certificate warnings until the CA certificate is properly configured"
|
||||
fi
|
||||
echo ""
|
||||
print_status "🎯 The application is now ready for secure development!"
|
||||
}
|
||||
|
||||
# Check current cert-manager status for debugging
|
||||
check_current_cert_manager_status() {
|
||||
print_status "Checking current cert-manager status..."
|
||||
|
||||
if kubectl get namespace cert-manager &> /dev/null; then
|
||||
echo ""
|
||||
echo "📋 Current cert-manager pods status:"
|
||||
kubectl get pods -n cert-manager
|
||||
|
||||
echo ""
|
||||
echo "🔍 cert-manager deployments:"
|
||||
kubectl get deployments -n cert-manager
|
||||
|
||||
# Check for any pending or failed pods
|
||||
local failed_pods=$(kubectl get pods -n cert-manager --field-selector=status.phase!=Running --no-headers 2>/dev/null | wc -l)
|
||||
if [ "$failed_pods" -gt 0 ]; then
|
||||
echo ""
|
||||
print_warning "Found $failed_pods non-running pods. Details:"
|
||||
kubectl get pods -n cert-manager --field-selector=status.phase!=Running
|
||||
fi
|
||||
echo ""
|
||||
else
|
||||
print_status "cert-manager namespace not found. Will install fresh."
|
||||
fi
|
||||
}
|
||||
|
||||
# Cleanup function for failed installations
|
||||
cleanup_on_failure() {
|
||||
print_warning "Cleaning up due to failure..."
|
||||
|
||||
# Optional cleanup - ask user
|
||||
read -p "Do you want to clean up the Kind cluster and start fresh? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
print_status "Cleaning up Kind cluster..."
|
||||
kind delete cluster --name bakery-ia-local || true
|
||||
print_success "Cleanup completed. You can run the script again."
|
||||
else
|
||||
print_status "Keeping existing setup. You can continue manually or run the script again."
|
||||
fi
|
||||
}
|
||||
|
||||
# Trap function to handle script interruption
|
||||
trap 'echo ""; print_warning "Script interrupted. Partial setup may be present."; cleanup_on_failure; exit 1' INT TERM
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo "Starting HTTPS setup for Bakery IA..."
|
||||
|
||||
# Set error handling for individual steps
|
||||
local step_failed=false
|
||||
|
||||
check_prerequisites || { step_failed=true; }
|
||||
if [ "$step_failed" = false ]; then
|
||||
check_current_cert_manager_status || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
install_cert_manager || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
install_nginx_ingress || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
setup_cluster_issuers || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
deploy_with_https || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
check_certificates || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
update_hosts_file || { step_failed=true; }
|
||||
fi
|
||||
if [ "$step_failed" = false ]; then
|
||||
export_ca_certificate || { step_failed=true; }
|
||||
fi
|
||||
|
||||
if [ "$step_failed" = false ]; then
|
||||
display_access_info
|
||||
print_success "Setup completed successfully! 🚀"
|
||||
else
|
||||
print_error "Setup failed at one or more steps. Check the output above for details."
|
||||
cleanup_on_failure
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
154
infrastructure/scripts/maintenance/tag-and-push-images.sh
Executable file
154
infrastructure/scripts/maintenance/tag-and-push-images.sh
Executable file
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to tag and push all Bakery IA images to a container registry
|
||||
# Usage: ./tag-and-push-images.sh [REGISTRY_PREFIX] [TAG]
|
||||
# Example: ./tag-and-push-images.sh myuser/bakery v1.0.0
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
REGISTRY_PREFIX="${1:-}"
|
||||
TAG="${2:-latest}"
|
||||
|
||||
if [ -z "$REGISTRY_PREFIX" ]; then
|
||||
echo -e "${RED}Error: Registry prefix required${NC}"
|
||||
echo "Usage: $0 REGISTRY_PREFIX [TAG]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " Docker Hub: $0 myusername/bakery v1.0.0"
|
||||
echo " GitHub: $0 ghcr.io/myorg/bakery v1.0.0"
|
||||
echo " MicroK8s: $0 YOUR_VPS_IP:32000/bakery v1.0.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# List of all services
|
||||
SERVICES=(
|
||||
"gateway"
|
||||
"dashboard"
|
||||
"auth-service"
|
||||
"tenant-service"
|
||||
"training-service"
|
||||
"forecasting-service"
|
||||
"sales-service"
|
||||
"external-service"
|
||||
"notification-service"
|
||||
"inventory-service"
|
||||
"recipes-service"
|
||||
"suppliers-service"
|
||||
"pos-service"
|
||||
"orders-service"
|
||||
"production-service"
|
||||
"procurement-service"
|
||||
"orchestrator-service"
|
||||
"alert-processor"
|
||||
"ai-insights-service"
|
||||
"demo-session-service"
|
||||
"distribution-service"
|
||||
)
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}Bakery IA - Image Tagging and Push${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo "Registry: $REGISTRY_PREFIX"
|
||||
echo "Tag: $TAG"
|
||||
echo ""
|
||||
|
||||
# Function to tag image
|
||||
tag_image() {
|
||||
local service=$1
|
||||
local local_name="bakery/${service}"
|
||||
local remote_name="${REGISTRY_PREFIX}-${service}:${TAG}"
|
||||
|
||||
echo -e "${YELLOW}Tagging ${local_name} -> ${remote_name}${NC}"
|
||||
|
||||
if docker tag "$local_name" "$remote_name"; then
|
||||
echo -e "${GREEN}✓ Tagged $service${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗ Failed to tag $service${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to push image
|
||||
push_image() {
|
||||
local service=$1
|
||||
local remote_name="${REGISTRY_PREFIX}-${service}:${TAG}"
|
||||
|
||||
echo -e "${YELLOW}Pushing ${remote_name}${NC}"
|
||||
|
||||
if docker push "$remote_name"; then
|
||||
echo -e "${GREEN}✓ Pushed $service${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗ Failed to push $service${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if user is logged in to registry
|
||||
echo -e "${YELLOW}Checking registry authentication...${NC}"
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo -e "${RED}Error: Docker daemon not running${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Docker is running${NC}"
|
||||
echo ""
|
||||
|
||||
# Ask for confirmation
|
||||
echo -e "${YELLOW}This will tag and push ${#SERVICES[@]} images.${NC}"
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Starting image tagging and push...${NC}"
|
||||
echo ""
|
||||
|
||||
# Track success/failure
|
||||
SUCCESS_COUNT=0
|
||||
FAILED_SERVICES=()
|
||||
|
||||
# Tag and push all images
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if tag_image "$service" && push_image "$service"; then
|
||||
((SUCCESS_COUNT++))
|
||||
else
|
||||
FAILED_SERVICES+=("$service")
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
# Summary
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}Summary${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo "Successfully pushed: $SUCCESS_COUNT/${#SERVICES[@]}"
|
||||
|
||||
if [ ${#FAILED_SERVICES[@]} -gt 0 ]; then
|
||||
echo -e "${RED}Failed services:${NC}"
|
||||
for service in "${FAILED_SERVICES[@]}"; do
|
||||
echo -e "${RED} - $service${NC}"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}All images pushed successfully!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Update image names in infrastructure/environments/prod/k8s-manifests/kustomization.yaml"
|
||||
echo "2. Deploy to production: kubectl apply -k infrastructure/environments/prod/k8s-manifests"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
126
infrastructure/scripts/setup/create-dockerhub-secret.sh
Executable file
126
infrastructure/scripts/setup/create-dockerhub-secret.sh
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Create Docker Hub Image Pull Secret
|
||||
# =============================================================================
|
||||
# This script creates a Kubernetes secret for pulling images from Docker Hub.
|
||||
# The secret is used by both:
|
||||
# 1. bakery-ia namespace deployments (Tilt + Kustomize)
|
||||
# 2. Signoz Helm deployment
|
||||
#
|
||||
# Usage:
|
||||
# ./create-dockerhub-secret.sh
|
||||
#
|
||||
# Prerequisites:
|
||||
# - kubectl configured with access to the cluster
|
||||
# - DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD environment variables set
|
||||
# - OR Docker CLI logged in (docker login)
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔐 Creating Docker Hub Image Pull Secret"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check for required environment variables
|
||||
if [ -z "$DOCKERHUB_USERNAME" ] || [ -z "$DOCKERHUB_PASSWORD" ]; then
|
||||
echo "⚠️ DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD environment variables not set"
|
||||
echo ""
|
||||
echo "Checking if Docker CLI is logged in..."
|
||||
|
||||
# Try to extract credentials from Docker config
|
||||
if [ -f "$HOME/.docker/config.json" ]; then
|
||||
# Check if using credential store
|
||||
if grep -q "credsStore" "$HOME/.docker/config.json"; then
|
||||
echo "⚠️ Docker is using a credential store. Please set environment variables manually:"
|
||||
echo ""
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try to extract from base64 encoded auth
|
||||
AUTH=$(cat "$HOME/.docker/config.json" | jq -r '.auths["https://index.docker.io/v1/"].auth // empty' 2>/dev/null)
|
||||
if [ -n "$AUTH" ]; then
|
||||
echo "✅ Found Docker Hub credentials in Docker config"
|
||||
DOCKERHUB_USERNAME=$(echo "$AUTH" | base64 -d | cut -d: -f1)
|
||||
DOCKERHUB_PASSWORD=$(echo "$AUTH" | base64 -d | cut -d: -f2-)
|
||||
else
|
||||
echo "❌ Could not find Docker Hub credentials"
|
||||
echo ""
|
||||
echo "Please either:"
|
||||
echo " 1. Run 'docker login' first, OR"
|
||||
echo " 2. Set environment variables:"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "❌ Docker config not found and environment variables not set"
|
||||
echo ""
|
||||
echo "Please set environment variables:"
|
||||
echo " export DOCKERHUB_USERNAME='your-username'"
|
||||
echo " export DOCKERHUB_PASSWORD='your-password-or-token'"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Using Docker Hub username: $DOCKERHUB_USERNAME"
|
||||
echo ""
|
||||
|
||||
# Function to create secret in a namespace
|
||||
create_secret_in_namespace() {
|
||||
local NAMESPACE=$1
|
||||
|
||||
echo "📦 Creating secret in namespace: $NAMESPACE"
|
||||
|
||||
# Create namespace if it doesn't exist
|
||||
if ! kubectl get namespace "$NAMESPACE" &>/dev/null; then
|
||||
echo " Creating namespace $NAMESPACE..."
|
||||
kubectl create namespace "$NAMESPACE"
|
||||
fi
|
||||
|
||||
# Delete existing secret if it exists
|
||||
if kubectl get secret dockerhub-creds -n "$NAMESPACE" &>/dev/null; then
|
||||
echo " Deleting existing secret..."
|
||||
kubectl delete secret dockerhub-creds -n "$NAMESPACE"
|
||||
fi
|
||||
|
||||
# Create the secret
|
||||
kubectl create secret docker-registry dockerhub-creds \
|
||||
--docker-server=https://index.docker.io/v1/ \
|
||||
--docker-username="$DOCKERHUB_USERNAME" \
|
||||
--docker-password="$DOCKERHUB_PASSWORD" \
|
||||
--docker-email="${DOCKERHUB_EMAIL:-noreply@bakery-ia.local}" \
|
||||
-n "$NAMESPACE"
|
||||
|
||||
echo " ✅ Secret created successfully"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Create secret in bakery-ia namespace (for Tilt deployments)
|
||||
create_secret_in_namespace "bakery-ia"
|
||||
|
||||
# Create secret in signoz namespace (for Signoz Helm deployment - if namespace exists)
|
||||
if kubectl get namespace signoz &>/dev/null; then
|
||||
create_secret_in_namespace "signoz"
|
||||
else
|
||||
echo "ℹ️ Signoz namespace not found, skipping (will be created on Helm install)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "✅ Docker Hub secrets created successfully!"
|
||||
echo ""
|
||||
echo "The secret 'dockerhub-creds' is now available in:"
|
||||
echo " - bakery-ia namespace (for Tilt/Kustomize deployments)"
|
||||
if kubectl get namespace signoz &>/dev/null; then
|
||||
echo " - signoz namespace (for Signoz Helm deployment)"
|
||||
fi
|
||||
echo ""
|
||||
echo "All pods with imagePullSecrets: dockerhub-creds will now use these credentials"
|
||||
echo "to pull images from Docker Hub."
|
||||
echo ""
|
||||
204
infrastructure/scripts/setup/generate-certificates.sh
Executable file
204
infrastructure/scripts/setup/generate-certificates.sh
Executable file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Generate TLS certificates for PostgreSQL and Redis
|
||||
# Self-signed certificates for internal cluster use
|
||||
|
||||
set -e
|
||||
|
||||
TLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CA_DIR="$TLS_DIR/ca"
|
||||
POSTGRES_DIR="$TLS_DIR/postgres"
|
||||
REDIS_DIR="$TLS_DIR/redis"
|
||||
|
||||
echo "Generating TLS certificates for Bakery IA..."
|
||||
echo "Directory: $TLS_DIR"
|
||||
echo ""
|
||||
|
||||
# Clean up old certificates
|
||||
echo "Cleaning up old certificates..."
|
||||
rm -rf "$CA_DIR"/* "$POSTGRES_DIR"/* "$REDIS_DIR"/* 2>/dev/null || true
|
||||
|
||||
# =====================================
|
||||
# 1. Generate Certificate Authority (CA)
|
||||
# =====================================
|
||||
|
||||
echo "Step 1: Generating Certificate Authority (CA)..."
|
||||
|
||||
# Generate CA private key
|
||||
openssl genrsa -out "$CA_DIR/ca-key.pem" 4096
|
||||
|
||||
# Generate CA certificate (valid for 10 years)
|
||||
openssl req -new -x509 -days 3650 -key "$CA_DIR/ca-key.pem" -out "$CA_DIR/ca-cert.pem" \
|
||||
-subj "/C=US/ST=California/L=SanFrancisco/O=BakeryIA/OU=Security/CN=BakeryIA-CA"
|
||||
|
||||
echo "✓ CA certificate generated"
|
||||
echo ""
|
||||
|
||||
# =====================================
|
||||
# 2. Generate PostgreSQL Server Certificates
|
||||
# =====================================
|
||||
|
||||
echo "Step 2: Generating PostgreSQL server certificates..."
|
||||
|
||||
# Generate PostgreSQL server private key
|
||||
openssl genrsa -out "$POSTGRES_DIR/server-key.pem" 4096
|
||||
|
||||
# Create certificate signing request (CSR)
|
||||
openssl req -new -key "$POSTGRES_DIR/server-key.pem" -out "$POSTGRES_DIR/server.csr" \
|
||||
-subj "/C=US/ST=California/L=SanFrancisco/O=BakeryIA/OU=Database/CN=*.bakery-ia.svc.cluster.local"
|
||||
|
||||
# Create SAN (Subject Alternative Names) configuration
|
||||
cat > "$POSTGRES_DIR/san.cnf" <<EOF
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = California
|
||||
L = SanFrancisco
|
||||
O = BakeryIA
|
||||
OU = Database
|
||||
CN = *.bakery-ia.svc.cluster.local
|
||||
|
||||
[v3_req]
|
||||
keyUsage = keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = *.bakery-ia.svc.cluster.local
|
||||
DNS.2 = *.bakery-ia
|
||||
DNS.3 = auth-db-service
|
||||
DNS.4 = tenant-db-service
|
||||
DNS.5 = training-db-service
|
||||
DNS.6 = forecasting-db-service
|
||||
DNS.7 = sales-db-service
|
||||
DNS.8 = external-db-service
|
||||
DNS.9 = notification-db-service
|
||||
DNS.10 = inventory-db-service
|
||||
DNS.11 = recipes-db-service
|
||||
DNS.12 = suppliers-db-service
|
||||
DNS.13 = pos-db-service
|
||||
DNS.14 = orders-db-service
|
||||
DNS.15 = production-db-service
|
||||
DNS.16 = alert-processor-db-service
|
||||
DNS.17 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Sign the certificate with CA (valid for 3 years)
|
||||
openssl x509 -req -in "$POSTGRES_DIR/server.csr" \
|
||||
-CA "$CA_DIR/ca-cert.pem" -CAkey "$CA_DIR/ca-key.pem" -CAcreateserial \
|
||||
-out "$POSTGRES_DIR/server-cert.pem" -days 1095 \
|
||||
-extensions v3_req -extfile "$POSTGRES_DIR/san.cnf"
|
||||
|
||||
# PostgreSQL requires specific permissions on key file
|
||||
chmod 600 "$POSTGRES_DIR/server-key.pem"
|
||||
chmod 644 "$POSTGRES_DIR/server-cert.pem"
|
||||
|
||||
# Copy CA cert for PostgreSQL clients
|
||||
cp "$CA_DIR/ca-cert.pem" "$POSTGRES_DIR/ca-cert.pem"
|
||||
|
||||
echo "✓ PostgreSQL certificates generated"
|
||||
echo ""
|
||||
|
||||
# =====================================
|
||||
# 3. Generate Redis Server Certificates
|
||||
# =====================================
|
||||
|
||||
echo "Step 3: Generating Redis server certificates..."
|
||||
|
||||
# Generate Redis server private key
|
||||
openssl genrsa -out "$REDIS_DIR/redis-key.pem" 4096
|
||||
|
||||
# Create certificate signing request (CSR)
|
||||
openssl req -new -key "$REDIS_DIR/redis-key.pem" -out "$REDIS_DIR/redis.csr" \
|
||||
-subj "/C=US/ST=California/L=SanFrancisco/O=BakeryIA/OU=Cache/CN=redis-service.bakery-ia.svc.cluster.local"
|
||||
|
||||
# Create SAN configuration for Redis
|
||||
cat > "$REDIS_DIR/san.cnf" <<EOF
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = California
|
||||
L = SanFrancisco
|
||||
O = BakeryIA
|
||||
OU = Cache
|
||||
CN = redis-service.bakery-ia.svc.cluster.local
|
||||
|
||||
[v3_req]
|
||||
keyUsage = keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = redis-service.bakery-ia.svc.cluster.local
|
||||
DNS.2 = redis-service.bakery-ia
|
||||
DNS.3 = redis-service
|
||||
DNS.4 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Sign the certificate with CA (valid for 3 years)
|
||||
openssl x509 -req -in "$REDIS_DIR/redis.csr" \
|
||||
-CA "$CA_DIR/ca-cert.pem" -CAkey "$CA_DIR/ca-key.pem" -CAcreateserial \
|
||||
-out "$REDIS_DIR/redis-cert.pem" -days 1095 \
|
||||
-extensions v3_req -extfile "$REDIS_DIR/san.cnf"
|
||||
|
||||
# Redis requires specific permissions
|
||||
chmod 600 "$REDIS_DIR/redis-key.pem"
|
||||
chmod 644 "$REDIS_DIR/redis-cert.pem"
|
||||
|
||||
# Copy CA cert for Redis clients
|
||||
cp "$CA_DIR/ca-cert.pem" "$REDIS_DIR/ca-cert.pem"
|
||||
|
||||
echo "✓ Redis certificates generated"
|
||||
echo ""
|
||||
|
||||
# =====================================
|
||||
# 4. Verify Certificates
|
||||
# =====================================
|
||||
|
||||
echo "Step 4: Verifying certificates..."
|
||||
|
||||
# Verify PostgreSQL certificate
|
||||
echo "PostgreSQL certificate details:"
|
||||
openssl x509 -in "$POSTGRES_DIR/server-cert.pem" -noout -subject -issuer -dates
|
||||
openssl verify -CAfile "$CA_DIR/ca-cert.pem" "$POSTGRES_DIR/server-cert.pem"
|
||||
|
||||
echo ""
|
||||
echo "Redis certificate details:"
|
||||
openssl x509 -in "$REDIS_DIR/redis-cert.pem" -noout -subject -issuer -dates
|
||||
openssl verify -CAfile "$CA_DIR/ca-cert.pem" "$REDIS_DIR/redis-cert.pem"
|
||||
|
||||
echo ""
|
||||
echo "===================="
|
||||
echo "✓ All certificates generated successfully!"
|
||||
echo ""
|
||||
echo "Generated files:"
|
||||
echo " CA:"
|
||||
echo " - $CA_DIR/ca-cert.pem (Certificate Authority certificate)"
|
||||
echo " - $CA_DIR/ca-key.pem (CA private key - keep secure!)"
|
||||
echo ""
|
||||
echo " PostgreSQL:"
|
||||
echo " - $POSTGRES_DIR/server-cert.pem (Server certificate)"
|
||||
echo " - $POSTGRES_DIR/server-key.pem (Server private key)"
|
||||
echo " - $POSTGRES_DIR/ca-cert.pem (CA certificate for clients)"
|
||||
echo ""
|
||||
echo " Redis:"
|
||||
echo " - $REDIS_DIR/redis-cert.pem (Server certificate)"
|
||||
echo " - $REDIS_DIR/redis-key.pem (Server private key)"
|
||||
echo " - $REDIS_DIR/ca-cert.pem (CA certificate for clients)"
|
||||
echo ""
|
||||
echo "Certificate validity: 3 years"
|
||||
echo "Next steps:"
|
||||
echo " 1. Create Kubernetes secrets from these certificates"
|
||||
echo " 2. Mount secrets in database pods"
|
||||
echo " 3. Configure PostgreSQL and Redis to use TLS"
|
||||
echo " 4. Update client connection strings to require SSL"
|
||||
111
infrastructure/scripts/setup/generate-minio-certificates.sh
Executable file
111
infrastructure/scripts/setup/generate-minio-certificates.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Generate MinIO TLS certificates using existing CA
|
||||
# This script generates certificates for MinIO server
|
||||
|
||||
set -e
|
||||
|
||||
TLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CA_DIR="$TLS_DIR/ca"
|
||||
MINIO_DIR="$TLS_DIR/minio"
|
||||
|
||||
mkdir -p "$MINIO_DIR"
|
||||
|
||||
echo "Generating MinIO TLS certificates using existing CA..."
|
||||
echo "CA Directory: $CA_DIR"
|
||||
echo "MinIO Directory: $MINIO_DIR"
|
||||
echo ""
|
||||
|
||||
# Check if CA exists
|
||||
if [ ! -f "$CA_DIR/ca-cert.pem" ] || [ ! -f "$CA_DIR/ca-key.pem" ]; then
|
||||
echo "ERROR: CA certificates not found. Please run generate-certificates.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate MinIO server private key
|
||||
echo "Step 1: Generating MinIO server private key..."
|
||||
openssl genrsa -out "$MINIO_DIR/minio-key.pem" 4096
|
||||
|
||||
# Convert to traditional RSA format (required by MinIO)
|
||||
echo "Step 1b: Converting private key to traditional RSA format..."
|
||||
openssl rsa -in "$MINIO_DIR/minio-key.pem" -traditional -out "$MINIO_DIR/minio-key.pem"
|
||||
|
||||
# Create certificate signing request (CSR)
|
||||
echo "Step 2: Creating MinIO certificate signing request..."
|
||||
openssl req -new -key "$MINIO_DIR/minio-key.pem" -out "$MINIO_DIR/minio.csr" \
|
||||
-subj "/C=US/ST=California/L=SanFrancisco/O=BakeryIA/OU=Storage/CN=minio.bakery-ia.svc.cluster.local"
|
||||
|
||||
# Create SAN (Subject Alternative Names) configuration for MinIO
|
||||
cat > "$MINIO_DIR/san.cnf" <<EOF
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = California
|
||||
L = SanFrancisco
|
||||
O = BakeryIA
|
||||
OU = Storage
|
||||
CN = minio.bakery-ia.svc.cluster.local
|
||||
|
||||
[v3_req]
|
||||
keyUsage = keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = minio.bakery-ia.svc.cluster.local
|
||||
DNS.2 = minio.bakery-ia
|
||||
DNS.3 = minio-console.bakery-ia.svc.cluster.local
|
||||
DNS.4 = minio-console.bakery-ia
|
||||
DNS.5 = minio
|
||||
DNS.6 = minio-console
|
||||
DNS.7 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Sign the certificate with CA (valid for 3 years)
|
||||
echo "Step 3: Signing MinIO certificate with CA..."
|
||||
openssl x509 -req -in "$MINIO_DIR/minio.csr" \
|
||||
-CA "$CA_DIR/ca-cert.pem" -CAkey "$CA_DIR/ca-key.pem" -CAcreateserial \
|
||||
-out "$MINIO_DIR/minio-cert.pem" -days 1095 \
|
||||
-extensions v3_req -extfile "$MINIO_DIR/san.cnf"
|
||||
|
||||
# Set proper permissions
|
||||
chmod 600 "$MINIO_DIR/minio-key.pem"
|
||||
chmod 644 "$MINIO_DIR/minio-cert.pem"
|
||||
|
||||
# Copy CA cert for MinIO
|
||||
cp "$CA_DIR/ca-cert.pem" "$MINIO_DIR/ca-cert.pem"
|
||||
|
||||
echo ""
|
||||
echo "Step 4: Verifying MinIO certificates..."
|
||||
|
||||
# Verify MinIO certificate
|
||||
echo "MinIO certificate details:"
|
||||
openssl x509 -in "$MINIO_DIR/minio-cert.pem" -noout -subject -issuer -dates
|
||||
openssl verify -CAfile "$CA_DIR/ca-cert.pem" "$MINIO_DIR/minio-cert.pem"
|
||||
|
||||
echo ""
|
||||
echo "==================="
|
||||
echo "✓ MinIO certificates generated successfully!"
|
||||
echo ""
|
||||
echo "Generated files:"
|
||||
echo " MinIO:"
|
||||
echo " - $MINIO_DIR/minio-cert.pem (Server certificate)"
|
||||
echo " - $MINIO_DIR/minio-key.pem (Server private key - traditional RSA format)"
|
||||
echo " - $MINIO_DIR/ca-cert.pem (CA certificate)"
|
||||
echo ""
|
||||
echo "Important Notes:"
|
||||
echo " • Private key is in traditional RSA format (BEGIN RSA PRIVATE KEY)"
|
||||
echo " • This format is required by MinIO to avoid 'The private key contains additional data' error"
|
||||
echo " • Certificates follow the standardized Opaque secret structure"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Update Kubernetes minio-tls secret with these certificates"
|
||||
echo " 2. Apply the updated secret to your cluster"
|
||||
echo " 3. Restart MinIO pods if necessary"
|
||||
echo ""
|
||||
echo "For more details, see: docs/MINIO_TLS_FIX_SUMMARY.md"
|
||||
152
infrastructure/scripts/verification/verify-registry.sh
Executable file
152
infrastructure/scripts/verification/verify-registry.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "======================================="
|
||||
echo "Registry Verification Script"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
|
||||
# 1. Check if registry container is running
|
||||
print_status "Checking if kind-registry container is running..."
|
||||
if docker ps | grep -q "kind-registry"; then
|
||||
print_success "Registry container is running"
|
||||
REGISTRY_STATUS=$(docker ps --filter "name=kind-registry" --format "{{.Status}}")
|
||||
echo " Status: $REGISTRY_STATUS"
|
||||
else
|
||||
print_error "Registry container is not running!"
|
||||
echo " Run: ./kubernetes_restart.sh setup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Check if registry is accessible on localhost:5001
|
||||
print_status "Checking if registry is accessible on localhost:5001..."
|
||||
if curl -s http://localhost:5001/v2/_catalog > /dev/null 2>&1; then
|
||||
print_success "Registry is accessible"
|
||||
CATALOG=$(curl -s http://localhost:5001/v2/_catalog)
|
||||
echo " Catalog: $CATALOG"
|
||||
else
|
||||
print_error "Registry is not accessible on localhost:5001"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Check if registry is connected to Kind network
|
||||
print_status "Checking if registry is connected to Kind network..."
|
||||
NETWORK_CHECK=$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' kind-registry 2>/dev/null)
|
||||
if [ "$NETWORK_CHECK" != "null" ] && [ -n "$NETWORK_CHECK" ]; then
|
||||
print_success "Registry is connected to Kind network"
|
||||
else
|
||||
print_warning "Registry is not connected to Kind network"
|
||||
print_status "Connecting registry to Kind network..."
|
||||
docker network connect "kind" "kind-registry"
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Registry connected successfully"
|
||||
else
|
||||
print_error "Failed to connect registry to Kind network"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. Check if Kind cluster exists
|
||||
print_status "Checking if Kind cluster exists..."
|
||||
if kind get clusters | grep -q "bakery-ia-local"; then
|
||||
print_success "Kind cluster 'bakery-ia-local' exists"
|
||||
else
|
||||
print_error "Kind cluster 'bakery-ia-local' not found"
|
||||
echo " Run: ./kubernetes_restart.sh setup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 5. Check if registry is documented in cluster
|
||||
print_status "Checking if registry is documented in cluster..."
|
||||
if kubectl get configmap -n kube-public local-registry-hosting &>/dev/null; then
|
||||
print_success "Registry is documented in cluster"
|
||||
REG_HOST=$(kubectl get configmap -n kube-public local-registry-hosting -o jsonpath='{.data.localRegistryHosting\.v1}' 2>/dev/null | grep -o 'host: "[^"]*"' | cut -d'"' -f2)
|
||||
echo " Registry host: $REG_HOST"
|
||||
else
|
||||
print_warning "Registry ConfigMap not found in cluster"
|
||||
print_status "Creating ConfigMap..."
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: local-registry-hosting
|
||||
namespace: kube-public
|
||||
data:
|
||||
localRegistryHosting.v1: |
|
||||
host: "localhost:5001"
|
||||
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
|
||||
EOF
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "ConfigMap created successfully"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. Test pushing a test image
|
||||
print_status "Testing image push to registry..."
|
||||
print_status "Pulling busybox image..."
|
||||
docker pull busybox:latest > /dev/null 2>&1
|
||||
|
||||
print_status "Tagging image for local registry..."
|
||||
docker tag busybox:latest localhost:5001/test/busybox:latest
|
||||
|
||||
print_status "Pushing image to local registry..."
|
||||
if docker push localhost:5001/test/busybox:latest > /dev/null 2>&1; then
|
||||
print_success "Successfully pushed test image to registry"
|
||||
else
|
||||
print_error "Failed to push image to registry"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Verifying image in registry catalog..."
|
||||
CATALOG=$(curl -s http://localhost:5001/v2/_catalog)
|
||||
if echo "$CATALOG" | grep -q "test/busybox"; then
|
||||
print_success "Test image found in registry catalog"
|
||||
else
|
||||
print_warning "Test image not found in catalog, but push succeeded"
|
||||
fi
|
||||
|
||||
# 7. Clean up test image
|
||||
print_status "Cleaning up test images..."
|
||||
docker rmi localhost:5001/test/busybox:latest > /dev/null 2>&1
|
||||
docker rmi busybox:latest > /dev/null 2>&1
|
||||
|
||||
echo ""
|
||||
echo "======================================="
|
||||
print_success "Registry verification completed!"
|
||||
echo "======================================="
|
||||
echo ""
|
||||
print_status "Summary:"
|
||||
echo " - Registry URL: localhost:5001"
|
||||
echo " - Registry container: kind-registry"
|
||||
echo " - Connected to Kind network: Yes"
|
||||
echo " - Accessible from host: Yes"
|
||||
echo " - Test push: Successful"
|
||||
echo ""
|
||||
print_status "Next steps:"
|
||||
echo " 1. Ensure your Tiltfile has: default_registry('localhost:5001')"
|
||||
echo " 2. Run: tilt up"
|
||||
echo " 3. Images will be automatically pushed to localhost:5001/bakery/<service>"
|
||||
echo ""
|
||||
446
infrastructure/scripts/verification/verify-signoz.sh
Executable file
446
infrastructure/scripts/verification/verify-signoz.sh
Executable file
@@ -0,0 +1,446 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# SigNoz Verification Script for Bakery IA
|
||||
# ============================================================================
|
||||
# This script verifies that SigNoz is properly deployed and functioning
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to display help
|
||||
show_help() {
|
||||
echo "Usage: $0 [OPTIONS] ENVIRONMENT"
|
||||
echo ""
|
||||
echo "Verify SigNoz deployment for Bakery IA"
|
||||
echo ""
|
||||
echo "Arguments:
|
||||
ENVIRONMENT Environment to verify (dev|prod)"
|
||||
echo ""
|
||||
echo "Options:
|
||||
-h, --help Show this help message
|
||||
-n, --namespace NAMESPACE Specify namespace (default: bakery-ia)"
|
||||
echo ""
|
||||
echo "Examples:
|
||||
$0 dev # Verify development deployment
|
||||
$0 prod # Verify production deployment
|
||||
$0 --namespace monitoring dev # Verify with custom namespace"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
NAMESPACE="bakery-ia"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-n|--namespace)
|
||||
NAMESPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
dev|prod)
|
||||
ENVIRONMENT="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate environment
|
||||
if [[ -z "$ENVIRONMENT" ]]; then
|
||||
echo "Error: Environment not specified. Use 'dev' or 'prod'."
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ENVIRONMENT" != "dev" && "$ENVIRONMENT" != "prod" ]]; then
|
||||
echo "Error: Invalid environment. Use 'dev' or 'prod'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to check if kubectl is configured
|
||||
check_kubectl() {
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo "${RED}Error: kubectl is not configured or cannot connect to cluster.${NC}"
|
||||
echo "Please ensure you have access to a Kubernetes cluster."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check namespace exists
|
||||
check_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
|
||||
echo "${RED}Error: Namespace $NAMESPACE does not exist.${NC}"
|
||||
echo "Please deploy SigNoz first using: ./deploy-signoz.sh $ENVIRONMENT"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to verify SigNoz deployment
|
||||
verify_deployment() {
|
||||
echo "${BLUE}"
|
||||
echo "=========================================="
|
||||
echo "🔍 Verifying SigNoz Deployment"
|
||||
echo "=========================================="
|
||||
echo "Environment: $ENVIRONMENT"
|
||||
echo "Namespace: $NAMESPACE"
|
||||
echo "${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if SigNoz helm release exists
|
||||
echo "${BLUE}1. Checking Helm release...${NC}"
|
||||
if helm list -n "$NAMESPACE" | grep -q signoz; then
|
||||
echo "${GREEN}✅ SigNoz Helm release found${NC}"
|
||||
else
|
||||
echo "${RED}❌ SigNoz Helm release not found${NC}"
|
||||
echo "Please deploy SigNoz first using: ./deploy-signoz.sh $ENVIRONMENT"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check pod status
|
||||
echo "${BLUE}2. Checking pod status...${NC}"
|
||||
local total_pods=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep -v "NAME" | wc -l | tr -d ' ' || echo "0")
|
||||
local running_pods=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz --field-selector=status.phase=Running 2>/dev/null | grep -c "Running" || echo "0")
|
||||
local ready_pods=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep "Running" | grep "1/1" | wc -l | tr -d ' ' || echo "0")
|
||||
|
||||
echo "Total pods: $total_pods"
|
||||
echo "Running pods: $running_pods"
|
||||
echo "Ready pods: $ready_pods"
|
||||
|
||||
if [[ $total_pods -eq 0 ]]; then
|
||||
echo "${RED}❌ No SigNoz pods found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $running_pods -eq $total_pods ]]; then
|
||||
echo "${GREEN}✅ All pods are running${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Some pods are not running${NC}"
|
||||
fi
|
||||
|
||||
if [[ $ready_pods -eq $total_pods ]]; then
|
||||
echo "${GREEN}✅ All pods are ready${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Some pods are not ready${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Show pod details
|
||||
echo "${BLUE}Pod Details:${NC}"
|
||||
kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
echo ""
|
||||
|
||||
# Check services
|
||||
echo "${BLUE}3. Checking services...${NC}"
|
||||
local service_count=$(kubectl get svc -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep -v "NAME" | wc -l | tr -d ' ' || echo "0")
|
||||
|
||||
if [[ $service_count -gt 0 ]]; then
|
||||
echo "${GREEN}✅ Services found ($service_count services)${NC}"
|
||||
kubectl get svc -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
else
|
||||
echo "${RED}❌ No services found${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check ingress
|
||||
echo "${BLUE}4. Checking ingress...${NC}"
|
||||
local ingress_count=$(kubectl get ingress -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep -v "NAME" | wc -l | tr -d ' ' || echo "0")
|
||||
|
||||
if [[ $ingress_count -gt 0 ]]; then
|
||||
echo "${GREEN}✅ Ingress found ($ingress_count ingress resources)${NC}"
|
||||
kubectl get ingress -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
else
|
||||
echo "${YELLOW}⚠️ No ingress found (may be configured in main namespace)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check PVCs
|
||||
echo "${BLUE}5. Checking persistent volume claims...${NC}"
|
||||
local pvc_count=$(kubectl get pvc -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep -v "NAME" | wc -l | tr -d ' ' || echo "0")
|
||||
|
||||
if [[ $pvc_count -gt 0 ]]; then
|
||||
echo "${GREEN}✅ PVCs found ($pvc_count PVCs)${NC}"
|
||||
kubectl get pvc -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
else
|
||||
echo "${YELLOW}⚠️ No PVCs found (may not be required for all components)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check resource usage
|
||||
echo "${BLUE}6. Checking resource usage...${NC}"
|
||||
if command -v kubectl &> /dev/null && kubectl top pods -n "$NAMESPACE" &> /dev/null; then
|
||||
echo "${GREEN}✅ Resource usage:${NC}"
|
||||
kubectl top pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz
|
||||
else
|
||||
echo "${YELLOW}⚠️ Metrics server not available or no resource usage data${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check logs for errors
|
||||
echo "${BLUE}7. Checking for errors in logs...${NC}"
|
||||
local error_found=false
|
||||
|
||||
# Check each pod for errors
|
||||
while IFS= read -r pod; do
|
||||
if [[ -n "$pod" ]]; then
|
||||
local pod_errors=$(kubectl logs -n "$NAMESPACE" "$pod" 2>/dev/null | grep -i "error\|exception\|fail\|crash" | wc -l || echo "0")
|
||||
if [[ $pod_errors -gt 0 ]]; then
|
||||
echo "${RED}❌ Errors found in pod $pod ($pod_errors errors)${NC}"
|
||||
error_found=true
|
||||
fi
|
||||
fi
|
||||
done < <(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz -o name | sed 's|pod/||')
|
||||
|
||||
if [[ "$error_found" == false ]]; then
|
||||
echo "${GREEN}✅ No errors found in logs${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Environment-specific checks
|
||||
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||||
verify_dev_specific
|
||||
else
|
||||
verify_prod_specific
|
||||
fi
|
||||
|
||||
# Show access information
|
||||
show_access_info
|
||||
}
|
||||
|
||||
# Function for development-specific verification
|
||||
verify_dev_specific() {
|
||||
echo "${BLUE}8. Development-specific checks...${NC}"
|
||||
|
||||
# Check if ingress is configured
|
||||
if kubectl get ingress -n "$NAMESPACE" 2>/dev/null | grep -q "monitoring.bakery-ia.local"; then
|
||||
echo "${GREEN}✅ Development ingress configured${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Development ingress not found${NC}"
|
||||
fi
|
||||
|
||||
# Check unified signoz component resource limits (should be lower for dev)
|
||||
local signoz_mem=$(kubectl get deployment -n "$NAMESPACE" -l app.kubernetes.io/component=query-service -o jsonpath='{.items[0].spec.template.spec.containers[0].resources.limits.memory}' 2>/dev/null || echo "")
|
||||
if [[ -n "$signoz_mem" ]]; then
|
||||
echo "${GREEN}✅ SigNoz component found (memory limit: $signoz_mem)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Could not verify SigNoz component resources${NC}"
|
||||
fi
|
||||
|
||||
# Check single replica setup for dev
|
||||
local replicas=$(kubectl get deployment -n "$NAMESPACE" -l app.kubernetes.io/component=query-service -o jsonpath='{.items[0].spec.replicas}' 2>/dev/null || echo "0")
|
||||
if [[ $replicas -eq 1 ]]; then
|
||||
echo "${GREEN}✅ Single replica configuration (appropriate for dev)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Multiple replicas detected (replicas: $replicas)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function for production-specific verification
|
||||
verify_prod_specific() {
|
||||
echo "${BLUE}8. Production-specific checks...${NC}"
|
||||
|
||||
# Check if TLS is configured
|
||||
if kubectl get ingress -n "$NAMESPACE" 2>/dev/null | grep -q "signoz-tls"; then
|
||||
echo "${GREEN}✅ TLS certificate configured${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ TLS certificate not found${NC}"
|
||||
fi
|
||||
|
||||
# Check if multiple replicas are running for HA
|
||||
local signoz_replicas=$(kubectl get deployment -n "$NAMESPACE" -l app.kubernetes.io/component=query-service -o jsonpath='{.items[0].spec.replicas}' 2>/dev/null || echo "1")
|
||||
if [[ $signoz_replicas -gt 1 ]]; then
|
||||
echo "${GREEN}✅ High availability configured ($signoz_replicas SigNoz replicas)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Single SigNoz replica detected (not highly available)${NC}"
|
||||
fi
|
||||
|
||||
# Check Zookeeper replicas (critical for production)
|
||||
local zk_replicas=$(kubectl get statefulset -n "$NAMESPACE" -l app.kubernetes.io/component=zookeeper -o jsonpath='{.items[0].spec.replicas}' 2>/dev/null || echo "0")
|
||||
if [[ $zk_replicas -eq 3 ]]; then
|
||||
echo "${GREEN}✅ Zookeeper properly configured with 3 replicas${NC}"
|
||||
elif [[ $zk_replicas -gt 0 ]]; then
|
||||
echo "${YELLOW}⚠️ Zookeeper has $zk_replicas replicas (recommend 3 for production)${NC}"
|
||||
else
|
||||
echo "${RED}❌ Zookeeper not found${NC}"
|
||||
fi
|
||||
|
||||
# Check OTel Collector replicas
|
||||
local otel_replicas=$(kubectl get deployment -n "$NAMESPACE" -l app.kubernetes.io/component=otel-collector -o jsonpath='{.items[0].spec.replicas}' 2>/dev/null || echo "1")
|
||||
if [[ $otel_replicas -gt 1 ]]; then
|
||||
echo "${GREEN}✅ OTel Collector HA configured ($otel_replicas replicas)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Single OTel Collector replica${NC}"
|
||||
fi
|
||||
|
||||
# Check resource limits (should be higher for prod)
|
||||
local signoz_mem=$(kubectl get deployment -n "$NAMESPACE" -l app.kubernetes.io/component=query-service -o jsonpath='{.items[0].spec.template.spec.containers[0].resources.limits.memory}' 2>/dev/null || echo "")
|
||||
if [[ -n "$signoz_mem" ]]; then
|
||||
echo "${GREEN}✅ Production resource limits applied (memory: $signoz_mem)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Could not verify resource limits${NC}"
|
||||
fi
|
||||
|
||||
# Check HPA (Horizontal Pod Autoscaler)
|
||||
local hpa_count=$(kubectl get hpa -n "$NAMESPACE" 2>/dev/null | grep -c signoz || echo "0")
|
||||
if [[ $hpa_count -gt 0 ]]; then
|
||||
echo "${GREEN}✅ Horizontal Pod Autoscaler configured${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ No HPA found (consider enabling for production)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to show access information
|
||||
show_access_info() {
|
||||
echo "${BLUE}"
|
||||
echo "=========================================="
|
||||
echo "📋 Access Information"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
|
||||
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||||
echo "SigNoz UI: http://monitoring.bakery-ia.local"
|
||||
echo ""
|
||||
echo "OpenTelemetry Collector (within cluster):"
|
||||
echo " gRPC: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4317"
|
||||
echo " HTTP: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4318"
|
||||
echo ""
|
||||
echo "Port-forward for local access:"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz 8080:8080"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz-otel-collector 4317:4317"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz-otel-collector 4318:4318"
|
||||
else
|
||||
echo "SigNoz UI: https://monitoring.bakewise.ai"
|
||||
echo ""
|
||||
echo "OpenTelemetry Collector (within cluster):"
|
||||
echo " gRPC: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4317"
|
||||
echo " HTTP: signoz-otel-collector.$NAMESPACE.svc.cluster.local:4318"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Default Credentials:"
|
||||
echo " Username: admin@example.com"
|
||||
echo " Password: admin"
|
||||
echo ""
|
||||
echo "⚠️ IMPORTANT: Change default password after first login!"
|
||||
echo ""
|
||||
|
||||
# Show connection test commands
|
||||
echo "Connection Test Commands:"
|
||||
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||||
echo " # Test SigNoz UI"
|
||||
echo " curl http://monitoring.bakery-ia.local"
|
||||
echo ""
|
||||
echo " # Test via port-forward"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz 8080:8080"
|
||||
echo " curl http://localhost:8080"
|
||||
else
|
||||
echo " # Test SigNoz UI"
|
||||
echo " curl https://monitoring.bakewise.ai"
|
||||
echo ""
|
||||
echo " # Test API health"
|
||||
echo " kubectl port-forward -n $NAMESPACE svc/signoz 8080:8080"
|
||||
echo " curl http://localhost:8080/api/v1/health"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to run connectivity tests
|
||||
run_connectivity_tests() {
|
||||
echo "${BLUE}"
|
||||
echo "=========================================="
|
||||
echo "🔗 Running Connectivity Tests"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
|
||||
# Test pod readiness first
|
||||
echo "Checking pod readiness..."
|
||||
local ready_pods=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz --field-selector=status.phase=Running 2>/dev/null | grep "Running" | grep -c "1/1\|2/2" || echo "0")
|
||||
local total_pods=$(kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/instance=signoz 2>/dev/null | grep -v "NAME" | wc -l | tr -d ' ' || echo "0")
|
||||
|
||||
if [[ $ready_pods -eq $total_pods && $total_pods -gt 0 ]]; then
|
||||
echo "${GREEN}✅ All pods are ready ($ready_pods/$total_pods)${NC}"
|
||||
else
|
||||
echo "${YELLOW}⚠️ Some pods not ready ($ready_pods/$total_pods)${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test internal service connectivity
|
||||
echo "Testing internal service connectivity..."
|
||||
local signoz_svc=$(kubectl get svc -n "$NAMESPACE" signoz -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
|
||||
if [[ -n "$signoz_svc" ]]; then
|
||||
echo "${GREEN}✅ SigNoz service accessible at $signoz_svc:8080${NC}"
|
||||
else
|
||||
echo "${RED}❌ SigNoz service not found${NC}"
|
||||
fi
|
||||
|
||||
local otel_svc=$(kubectl get svc -n "$NAMESPACE" signoz-otel-collector -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
|
||||
if [[ -n "$otel_svc" ]]; then
|
||||
echo "${GREEN}✅ OTel Collector service accessible at $otel_svc:4317 (gRPC), $otel_svc:4318 (HTTP)${NC}"
|
||||
else
|
||||
echo "${RED}❌ OTel Collector service not found${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "$ENVIRONMENT" == "prod" ]]; then
|
||||
echo "${YELLOW}⚠️ Production connectivity tests require valid DNS and TLS${NC}"
|
||||
echo " Please ensure monitoring.bakewise.ai resolves to your cluster"
|
||||
echo ""
|
||||
echo "Manual test:"
|
||||
echo " curl -I https://monitoring.bakewise.ai"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo "${BLUE}"
|
||||
echo "=========================================="
|
||||
echo "🔍 SigNoz Verification for Bakery IA"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
|
||||
# Check prerequisites
|
||||
check_kubectl
|
||||
check_namespace
|
||||
|
||||
# Verify deployment
|
||||
verify_deployment
|
||||
|
||||
# Run connectivity tests
|
||||
run_connectivity_tests
|
||||
|
||||
echo "${GREEN}"
|
||||
echo "=========================================="
|
||||
echo "✅ Verification Complete"
|
||||
echo "=========================================="
|
||||
echo "${NC}"
|
||||
|
||||
echo "Summary:"
|
||||
echo " Environment: $ENVIRONMENT"
|
||||
echo " Namespace: $NAMESPACE"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Access SigNoz UI and verify dashboards"
|
||||
echo " 2. Configure alert rules for your services"
|
||||
echo " 3. Instrument your applications with OpenTelemetry"
|
||||
echo " 4. Set up custom dashboards for key metrics"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
Reference in New Issue
Block a user