diff --git a/package.json b/package.json index 288ba97..c6cb9b9 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "setup": "./scripts/setup.sh", "deploy": "./scripts/deploy.sh", "deploy:k8s": "./scripts/deploy-k8s.sh", + "undeploy:k8s": "./scripts/undeploy-k8s.sh", "validate:env": "./scripts/validate-env.sh", "validate:deployment": "./scripts/validate-deployment.sh", "seed:production": "bun scripts/seed-production.js", diff --git a/scripts/deploy-k8s.sh b/scripts/deploy-k8s.sh index 5bd0f62..50284cb 100755 --- a/scripts/deploy-k8s.sh +++ b/scripts/deploy-k8s.sh @@ -94,17 +94,34 @@ validate_env() { fi } +# Function to ensure namespace exists +ensure_namespace() { + if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then + print_info "Creating namespace: $NAMESPACE" + kubectl create namespace "$NAMESPACE" + print_success "Created namespace: $NAMESPACE" + else + print_info "Using existing namespace: $NAMESPACE" + fi +} + # Function to apply Kubernetes manifests apply_manifests() { local manifest_dir="$1" print_info "Applying Kubernetes manifests from $manifest_dir" - # Apply non-template files first + # Apply static files that don't have template counterparts for manifest_file in "$K8S_DIR"/*.yaml; do if [[ -f "$manifest_file" && ! "$manifest_file" =~ \.template$ ]]; then - print_info "Applying: $(basename "$manifest_file")" - kubectl apply -f "$manifest_file" + local basename_file=$(basename "$manifest_file") + local template_file="$K8S_DIR/${basename_file}.template" + + # Only apply if there's no corresponding template file + if [[ ! -f "$template_file" ]]; then + print_info "Applying static file: $basename_file" + kubectl apply -f "$manifest_file" -n "$NAMESPACE" + fi fi done @@ -112,8 +129,8 @@ apply_manifests() { if [[ -d "$TEMP_DIR" ]]; then for manifest_file in "$TEMP_DIR"/*.yaml; do if [[ -f "$manifest_file" ]]; then - print_info "Applying: $(basename "$manifest_file")" - kubectl apply -f "$manifest_file" + print_info "Applying template: $(basename "$manifest_file")" + kubectl apply -f "$manifest_file" -n "$NAMESPACE" fi done fi @@ -133,15 +150,15 @@ show_status() { echo "" print_info "Pods:" - kubectl get pods -l app=rxminder + kubectl get pods -l app=rxminder -n "$NAMESPACE" echo "" print_info "Services:" - kubectl get services -l app=rxminder + kubectl get services -l app=rxminder -n "$NAMESPACE" echo "" print_info "Ingress:" - kubectl get ingress -l app=rxminder + kubectl get ingress -l app=rxminder -n "$NAMESPACE" echo "" if [[ -n "${INGRESS_HOST:-}" ]]; then @@ -236,9 +253,15 @@ main() { # Load environment variables load_env "$env_file" + # Set default namespace if not provided in environment + NAMESPACE="${NAMESPACE:-rxminder}" + # Validate required environment variables validate_env + # Ensure namespace exists + ensure_namespace + # Process templates substitute_templates diff --git a/scripts/undeploy-k8s.sh b/scripts/undeploy-k8s.sh new file mode 100755 index 0000000..50284cb --- /dev/null +++ b/scripts/undeploy-k8s.sh @@ -0,0 +1,298 @@ +#!/bin/bash + +# Kubernetes deployment script with environment variable substitution +# This script processes template files and applies them to Kubernetes + +set -euo pipefail + +# 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 + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +K8S_DIR="$PROJECT_ROOT/k8s" +TEMP_DIR="/tmp/meds-k8s-deploy" + +# Function to print colored output +print_info() { + echo -e "${BLUE}ℹ️ $1${NC}" +} + +print_success() { + echo -e "${GREEN}✅ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +print_error() { + echo -e "${RED}❌ $1${NC}" +} + +# Function to load environment variables +load_env() { + local env_file="$1" + if [[ -f "$env_file" ]]; then + print_info "Loading environment from $env_file" + # Export variables from .env file + set -a + source "$env_file" + set +a + else + print_warning "Environment file $env_file not found" + fi +} + +# Function to substitute environment variables in template files +substitute_templates() { + print_info "Processing template files..." + + # Create temporary directory + mkdir -p "$TEMP_DIR" + + # Process each template file + for template_file in "$K8S_DIR"/*.template; do + if [[ -f "$template_file" ]]; then + local filename=$(basename "$template_file" .template) + local output_file="$TEMP_DIR/$filename" + + print_info "Processing template: $filename" + + # Substitute environment variables + envsubst < "$template_file" > "$output_file" + + print_success "Generated: $output_file" + fi + done +} + +# Function to validate required environment variables +validate_env() { + local required_vars=("INGRESS_HOST") + local missing_vars=() + + for var in "${required_vars[@]}"; do + if [[ -z "${!var:-}" ]]; then + missing_vars+=("$var") + fi + done + + if [[ ${#missing_vars[@]} -gt 0 ]]; then + print_error "Missing required environment variables:" + for var in "${missing_vars[@]}"; do + echo " - $var" + done + echo "" + echo "Please set these variables in your .env file or environment." + exit 1 + fi +} + +# Function to ensure namespace exists +ensure_namespace() { + if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then + print_info "Creating namespace: $NAMESPACE" + kubectl create namespace "$NAMESPACE" + print_success "Created namespace: $NAMESPACE" + else + print_info "Using existing namespace: $NAMESPACE" + fi +} + +# Function to apply Kubernetes manifests +apply_manifests() { + local manifest_dir="$1" + + print_info "Applying Kubernetes manifests from $manifest_dir" + + # Apply static files that don't have template counterparts + for manifest_file in "$K8S_DIR"/*.yaml; do + if [[ -f "$manifest_file" && ! "$manifest_file" =~ \.template$ ]]; then + local basename_file=$(basename "$manifest_file") + local template_file="$K8S_DIR/${basename_file}.template" + + # Only apply if there's no corresponding template file + if [[ ! -f "$template_file" ]]; then + print_info "Applying static file: $basename_file" + kubectl apply -f "$manifest_file" -n "$NAMESPACE" + fi + fi + done + + # Apply processed template files + if [[ -d "$TEMP_DIR" ]]; then + for manifest_file in "$TEMP_DIR"/*.yaml; do + if [[ -f "$manifest_file" ]]; then + print_info "Applying template: $(basename "$manifest_file")" + kubectl apply -f "$manifest_file" -n "$NAMESPACE" + fi + done + fi +} + +# Function to cleanup temporary files +cleanup() { + if [[ -d "$TEMP_DIR" ]]; then + print_info "Cleaning up temporary files..." + rm -rf "$TEMP_DIR" + fi +} + +# Function to show deployment status +show_status() { + print_info "Deployment Status:" + echo "" + + print_info "Pods:" + kubectl get pods -l app=rxminder -n "$NAMESPACE" + echo "" + + print_info "Services:" + kubectl get services -l app=rxminder -n "$NAMESPACE" + echo "" + + print_info "Ingress:" + kubectl get ingress -l app=rxminder -n "$NAMESPACE" + echo "" + + if [[ -n "${INGRESS_HOST:-}" ]]; then + print_success "Application should be available at: http://$INGRESS_HOST" + fi +} + +# Function to show usage +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -e, --env FILE Environment file to load (default: .env)" + echo " -d, --dry-run Show what would be applied without applying" + echo " -s, --status Show deployment status only" + echo " -c, --cleanup Cleanup temporary files and exit" + echo " -h, --help Show this help message" + echo "" + echo "Examples:" + echo " $0 Deploy with default .env file" + echo " $0 -e .env.prod Deploy with production environment" + echo " $0 --dry-run Preview what would be deployed" + echo " $0 --status Check deployment status" +} + +# Main function +main() { + local env_file=".env" + local dry_run=false + local status_only=false + local cleanup_only=false + + # Parse command line arguments + while [[ $# -gt 0 ]]; do + case $1 in + -e|--env) + env_file="$2" + shift 2 + ;; + -d|--dry-run) + dry_run=true + shift + ;; + -s|--status) + status_only=true + shift + ;; + -c|--cleanup) + cleanup_only=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + print_error "Unknown option: $1" + usage + exit 1 + ;; + esac + done + + # Handle cleanup only + if [[ "$cleanup_only" == true ]]; then + cleanup + exit 0 + fi + + # Handle status only + if [[ "$status_only" == true ]]; then + show_status + exit 0 + fi + + # Check if kubectl is available + if ! command -v kubectl &> /dev/null; then + print_error "kubectl is not installed or not in PATH" + exit 1 + fi + + # Check if we can connect to Kubernetes cluster + if ! kubectl cluster-info &> /dev/null; then + print_error "Cannot connect to Kubernetes cluster" + print_info "Make sure your kubectl is configured correctly" + exit 1 + fi + + print_info "🚀 Deploying Medication Reminder App to Kubernetes" + echo "" + + # Load environment variables + load_env "$env_file" + + # Set default namespace if not provided in environment + NAMESPACE="${NAMESPACE:-rxminder}" + + # Validate required environment variables + validate_env + + # Ensure namespace exists + ensure_namespace + + # Process templates + substitute_templates + + if [[ "$dry_run" == true ]]; then + print_info "Dry run mode - showing generated manifests:" + echo "" + + for manifest_file in "$TEMP_DIR"/*.yaml; do + if [[ -f "$manifest_file" ]]; then + echo "=== $(basename "$manifest_file") ===" + cat "$manifest_file" + echo "" + fi + done + else + # Apply manifests + apply_manifests "$K8S_DIR" + + print_success "Deployment completed!" + echo "" + + # Show status + show_status + fi + + # Cleanup + cleanup +} + +# Trap to ensure cleanup happens +trap cleanup EXIT + +# Run main function +main "$@"