- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
251 lines
6.8 KiB
Bash
Executable File
251 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# =============================================================================
|
|
# GAMILIT Platform - Kubernetes Deployment Script
|
|
# =============================================================================
|
|
# Purpose: Automated deployment to Kubernetes cluster
|
|
# Usage: ./deploy-k8s.sh [staging|production]
|
|
# =============================================================================
|
|
|
|
set -e # Exit on error
|
|
set -u # Exit on undefined variable
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
K8S_DIR="$PROJECT_ROOT/k8s"
|
|
|
|
# Functions
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
check_prerequisites() {
|
|
log_info "Checking prerequisites..."
|
|
|
|
# Check kubectl
|
|
if ! command -v kubectl &> /dev/null; then
|
|
log_error "kubectl not found. Please install kubectl."
|
|
exit 1
|
|
fi
|
|
|
|
# Check cluster connection
|
|
if ! kubectl cluster-info &> /dev/null; then
|
|
log_error "Cannot connect to Kubernetes cluster."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Prerequisites check passed"
|
|
}
|
|
|
|
create_namespace() {
|
|
local namespace=$1
|
|
|
|
log_info "Creating namespace: $namespace"
|
|
|
|
kubectl create namespace $namespace --dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
# Label namespace
|
|
kubectl label namespace $namespace \
|
|
app=gamilit \
|
|
environment=$ENVIRONMENT \
|
|
--overwrite
|
|
}
|
|
|
|
deploy_secrets() {
|
|
local namespace=$1
|
|
|
|
log_warn "Secrets must be created manually for security!"
|
|
log_info "Use the following commands to create secrets:"
|
|
|
|
echo ""
|
|
echo "kubectl create secret generic gamilit-db-secret \\"
|
|
echo " --from-literal=username='gamilit_user' \\"
|
|
echo " --from-literal=password='YOUR_STRONG_PASSWORD' \\"
|
|
echo " --namespace=$namespace"
|
|
echo ""
|
|
echo "kubectl create secret generic gamilit-jwt-secret \\"
|
|
echo " --from-literal=jwt_secret='YOUR_JWT_SECRET' \\"
|
|
echo " --namespace=$namespace"
|
|
echo ""
|
|
|
|
# Check if secrets exist
|
|
if ! kubectl get secret gamilit-db-secret -n $namespace &> /dev/null; then
|
|
log_error "Secret gamilit-db-secret does not exist in namespace $namespace"
|
|
log_error "Please create it before proceeding"
|
|
exit 1
|
|
fi
|
|
|
|
if ! kubectl get secret gamilit-jwt-secret -n $namespace &> /dev/null; then
|
|
log_error "Secret gamilit-jwt-secret does not exist in namespace $namespace"
|
|
log_error "Please create it before proceeding"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Secrets check passed"
|
|
}
|
|
|
|
deploy_database() {
|
|
local namespace=$1
|
|
|
|
log_info "Deploying PostgreSQL database..."
|
|
|
|
kubectl apply -f $K8S_DIR/database/statefulset.yaml -n $namespace
|
|
|
|
log_info "Waiting for database to be ready..."
|
|
kubectl wait --for=condition=ready pod \
|
|
-l app=gamilit,component=database \
|
|
-n $namespace \
|
|
--timeout=300s
|
|
|
|
log_info "Database deployed successfully"
|
|
}
|
|
|
|
deploy_backend() {
|
|
local namespace=$1
|
|
|
|
log_info "Deploying backend..."
|
|
|
|
# Apply manifests
|
|
kubectl apply -f $K8S_DIR/backend/deployment.yaml -n $namespace
|
|
kubectl apply -f $K8S_DIR/backend/service.yaml -n $namespace
|
|
kubectl apply -f $K8S_DIR/backend/hpa.yaml -n $namespace
|
|
kubectl apply -f $K8S_DIR/backend/ingress.yaml -n $namespace
|
|
|
|
log_info "Waiting for backend deployment..."
|
|
kubectl rollout status deployment/gamilit-backend -n $namespace --timeout=300s
|
|
|
|
log_info "Backend deployed successfully"
|
|
}
|
|
|
|
deploy_frontend() {
|
|
local namespace=$1
|
|
|
|
log_info "Deploying frontend..."
|
|
|
|
kubectl apply -f $K8S_DIR/frontend/deployment.yaml -n $namespace
|
|
kubectl apply -f $K8S_DIR/frontend/service.yaml -n $namespace
|
|
kubectl apply -f $K8S_DIR/frontend/ingress.yaml -n $namespace
|
|
|
|
log_info "Waiting for frontend deployment..."
|
|
kubectl rollout status deployment/gamilit-frontend -n $namespace --timeout=300s
|
|
|
|
log_info "Frontend deployed successfully"
|
|
}
|
|
|
|
run_migrations() {
|
|
local namespace=$1
|
|
|
|
log_info "Running database migrations..."
|
|
|
|
# Get first backend pod
|
|
BACKEND_POD=$(kubectl get pods -n $namespace -l app=gamilit,component=backend -o jsonpath='{.items[0].metadata.name}')
|
|
|
|
if [ -z "$BACKEND_POD" ]; then
|
|
log_error "No backend pods found"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Running migrations on pod: $BACKEND_POD"
|
|
|
|
kubectl exec -it $BACKEND_POD -n $namespace -- npm run migrate
|
|
|
|
log_info "Migrations completed"
|
|
}
|
|
|
|
health_check() {
|
|
local namespace=$1
|
|
|
|
log_info "Running health checks..."
|
|
|
|
# Backend health check
|
|
BACKEND_SERVICE=$(kubectl get svc gamilit-backend -n $namespace -o jsonpath='{.spec.clusterIP}')
|
|
|
|
kubectl run health-check --rm -i --restart=Never --image=curlimages/curl -- \
|
|
curl -f http://$BACKEND_SERVICE:3006/api/health || {
|
|
log_error "Backend health check failed"
|
|
exit 1
|
|
}
|
|
|
|
log_info "All health checks passed"
|
|
}
|
|
|
|
show_status() {
|
|
local namespace=$1
|
|
|
|
log_info "Deployment status:"
|
|
echo ""
|
|
|
|
kubectl get all -n $namespace -l app=gamilit
|
|
|
|
echo ""
|
|
log_info "Ingress:"
|
|
kubectl get ingress -n $namespace
|
|
|
|
echo ""
|
|
log_info "HPA status:"
|
|
kubectl get hpa -n $namespace
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
# Check arguments
|
|
if [ $# -lt 1 ]; then
|
|
log_error "Usage: $0 [staging|production]"
|
|
exit 1
|
|
fi
|
|
|
|
ENVIRONMENT=$1
|
|
NAMESPACE="gamilit-$ENVIRONMENT"
|
|
|
|
log_info "==================================================================="
|
|
log_info "GAMILIT Platform - Kubernetes Deployment"
|
|
log_info "Environment: $ENVIRONMENT"
|
|
log_info "Namespace: $NAMESPACE"
|
|
log_info "==================================================================="
|
|
echo ""
|
|
|
|
# Confirmation
|
|
read -p "Deploy to $ENVIRONMENT? (yes/no): " confirm
|
|
if [ "$confirm" != "yes" ]; then
|
|
log_warn "Deployment cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
# Execute deployment steps
|
|
check_prerequisites
|
|
create_namespace $NAMESPACE
|
|
deploy_secrets $NAMESPACE
|
|
deploy_database $NAMESPACE
|
|
deploy_backend $NAMESPACE
|
|
deploy_frontend $NAMESPACE
|
|
run_migrations $NAMESPACE
|
|
health_check $NAMESPACE
|
|
show_status $NAMESPACE
|
|
|
|
echo ""
|
|
log_info "==================================================================="
|
|
log_info "Deployment completed successfully!"
|
|
log_info "==================================================================="
|
|
log_info "Next steps:"
|
|
log_info "1. Verify application: kubectl get all -n $NAMESPACE"
|
|
log_info "2. Check logs: kubectl logs -f deployment/gamilit-backend -n $NAMESPACE"
|
|
log_info "3. Monitor: kubectl top pods -n $NAMESPACE"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|