359 lines
10 KiB
Plaintext
Executable File
359 lines
10 KiB
Plaintext
Executable File
#!/usr/bin/env nu
|
|
|
|
# Deploy Provisioning Platform
|
|
# Complete platform deployment automation
|
|
|
|
def main [
|
|
--mode: string = "solo" # Deployment mode: solo, multi-user, cicd, enterprise
|
|
--env-file: string = ".env" # Environment file path
|
|
--pull # Pull latest images before deploy
|
|
--build # Build images from source
|
|
--clean # Clean volumes before deploy (WARNING: data loss)
|
|
--wait: int = 300 # Wait timeout for health checks (seconds)
|
|
--skip-health-check # Skip health checks after deploy
|
|
--dry-run # Show what would be deployed without deploying
|
|
] {
|
|
print $"(ansi green_bold)Provisioning Platform Deployment(ansi reset)"
|
|
print $"Mode: ($mode)"
|
|
print ""
|
|
|
|
# Validate mode
|
|
let valid_modes = ["solo", "multi-user", "cicd", "enterprise"]
|
|
if $mode not-in $valid_modes {
|
|
print $"(ansi red_bold)Error:(ansi reset) Invalid mode '($mode)'"
|
|
print $"Valid modes: (valid_modes | str join ', ')"
|
|
return 1
|
|
}
|
|
|
|
# Change to platform directory
|
|
let platform_dir = $env.FILE_PWD | path dirname
|
|
cd $platform_dir
|
|
|
|
# Check prerequisites
|
|
print $"(ansi cyan)Checking prerequisites...(ansi reset)"
|
|
if not (check_docker) {
|
|
return 1
|
|
}
|
|
if not (check_docker_compose) {
|
|
return 1
|
|
}
|
|
|
|
# Generate .env if missing
|
|
if not ($env_file | path exists) {
|
|
if (".env.example" | path exists) {
|
|
print $"(ansi yellow)Warning:(ansi reset) ($env_file) not found. Creating from .env.example"
|
|
cp .env.example $env_file
|
|
print $"(ansi yellow)Please edit ($env_file) and set required secrets before deploying!(ansi reset)"
|
|
|
|
if not $dry_run {
|
|
print ""
|
|
print "Press Enter to continue after editing .env, or Ctrl+C to cancel..."
|
|
input
|
|
}
|
|
} else {
|
|
print $"(ansi red_bold)Error:(ansi reset) Neither ($env_file) nor .env.example found"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Validate environment file
|
|
print $"(ansi cyan)Validating environment configuration...(ansi reset)"
|
|
if not (validate_env_file $env_file) {
|
|
return 1
|
|
}
|
|
|
|
# Build compose file list
|
|
let compose_files = build_compose_file_list $mode
|
|
|
|
print $"(ansi cyan)Compose files:(ansi reset)"
|
|
for file in $compose_files {
|
|
print $" - ($file)"
|
|
}
|
|
print ""
|
|
|
|
if $dry_run {
|
|
print $"(ansi yellow)DRY RUN: Would deploy with these settings(ansi reset)"
|
|
return 0
|
|
}
|
|
|
|
# Clean volumes if requested
|
|
if $clean {
|
|
print $"(ansi yellow_bold)WARNING: --clean will delete all data volumes!(ansi reset)"
|
|
print "Type 'yes' to confirm: "
|
|
let confirm = input
|
|
if $confirm != "yes" {
|
|
print "Cancelled."
|
|
return 0
|
|
}
|
|
|
|
print $"(ansi cyan)Cleaning volumes...(ansi reset)"
|
|
docker_compose_down $compose_files --volumes
|
|
}
|
|
|
|
# Pull images if requested
|
|
if $pull {
|
|
print $"(ansi cyan)Pulling latest images...(ansi reset)"
|
|
docker_compose_pull $compose_files
|
|
}
|
|
|
|
# Build images if requested
|
|
if $build {
|
|
print $"(ansi cyan)Building images from source...(ansi reset)"
|
|
docker_compose_build $compose_files
|
|
}
|
|
|
|
# Create networks
|
|
print $"(ansi cyan)Creating networks...(ansi reset)"
|
|
create_networks
|
|
|
|
# Deploy services
|
|
print $"(ansi cyan)Deploying services...(ansi reset)"
|
|
docker_compose_up $compose_files
|
|
|
|
# Wait for health checks
|
|
if not $skip_health_check {
|
|
print ""
|
|
print $"(ansi cyan)Waiting for services to be healthy (timeout: ($wait)s)...(ansi reset)"
|
|
if not (wait_for_health $wait) {
|
|
print $"(ansi red_bold)Error:(ansi reset) Some services failed health checks"
|
|
print "Check logs with: docker compose logs"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Display access URLs
|
|
print ""
|
|
print $"(ansi green_bold)✓ Platform deployed successfully!(ansi reset)"
|
|
print ""
|
|
display_access_urls $mode
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check if Docker is installed and running
|
|
def check_docker [] {
|
|
try {
|
|
docker ps | complete | get exit_code | $in == 0
|
|
print $"(ansi green)✓ Docker is running(ansi reset)"
|
|
true
|
|
} catch {
|
|
print $"(ansi red_bold)✗ Docker is not running or not installed(ansi reset)"
|
|
print "Please install Docker and ensure it's running"
|
|
false
|
|
}
|
|
}
|
|
|
|
# Check if docker compose is installed
|
|
def check_docker_compose [] {
|
|
try {
|
|
docker compose version | complete | get exit_code | $in == 0
|
|
print $"(ansi green)✓ docker compose is installed(ansi reset)"
|
|
true
|
|
} catch {
|
|
print $"(ansi red_bold)✗ docker compose is not installed(ansi reset)"
|
|
print "Please install docker compose plugin"
|
|
false
|
|
}
|
|
}
|
|
|
|
# Build list of compose files for mode
|
|
def build_compose_file_list [mode: string] {
|
|
mut files = ["docker-compose.yaml"]
|
|
|
|
if $mode == "solo" {
|
|
$files = ($files | append "docker-compose/docker-compose.solo.yaml")
|
|
} else if $mode == "multi-user" {
|
|
$files = ($files | append "docker-compose/docker-compose.multi-user.yaml")
|
|
} else if $mode == "cicd" {
|
|
$files = ($files | append "docker-compose/docker-compose.multi-user.yaml")
|
|
$files = ($files | append "docker-compose/docker-compose.cicd.yaml")
|
|
} else if $mode == "enterprise" {
|
|
$files = ($files | append "docker-compose/docker-compose.multi-user.yaml")
|
|
$files = ($files | append "docker-compose/docker-compose.cicd.yaml")
|
|
$files = ($files | append "docker-compose/docker-compose.enterprise.yaml")
|
|
}
|
|
|
|
$files
|
|
}
|
|
|
|
# Validate environment file
|
|
def validate_env_file [file: string] {
|
|
let content = (open $file)
|
|
|
|
# Check for placeholder secrets
|
|
let placeholders = [
|
|
"CHANGE_ME_RANDOM_SECRET_HERE",
|
|
"CHANGE_ME_GITEA_SECRET_KEY",
|
|
"CHANGE_ME_ADMIN_PASSWORD",
|
|
"CHANGE_ME_POSTGRES_PASSWORD",
|
|
"CHANGE_ME_API_SERVER_JWT_SECRET",
|
|
"CHANGE_ME_GRAFANA_PASSWORD"
|
|
]
|
|
|
|
mut has_placeholder = false
|
|
for placeholder in $placeholders {
|
|
if ($content | str contains $placeholder) {
|
|
print $"(ansi yellow)Warning:(ansi reset) Found placeholder '($placeholder)' in ($file)"
|
|
$has_placeholder = true
|
|
}
|
|
}
|
|
|
|
if $has_placeholder {
|
|
print $"(ansi yellow)Please replace all CHANGE_ME placeholders with actual secrets(ansi reset)"
|
|
print "You can generate secrets with: openssl rand -base64 32"
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
# Create Docker networks
|
|
def create_networks [] {
|
|
try {
|
|
docker network create provisioning-net | ignore
|
|
} catch {}
|
|
|
|
try {
|
|
docker network create provisioning-net-frontend | ignore
|
|
} catch {}
|
|
|
|
try {
|
|
docker network create provisioning-net-backend | ignore
|
|
} catch {}
|
|
|
|
try {
|
|
docker network create provisioning-net-storage | ignore
|
|
} catch {}
|
|
}
|
|
|
|
# Run docker compose down
|
|
def docker_compose_down [files: list<string>, --volumes] {
|
|
mut cmd = ["docker", "compose"]
|
|
|
|
for file in $files {
|
|
$cmd = ($cmd | append ["-f", $file])
|
|
}
|
|
|
|
$cmd = ($cmd | append "down")
|
|
|
|
if $volumes {
|
|
$cmd = ($cmd | append "--volumes")
|
|
}
|
|
|
|
run-external $cmd.0 ...($cmd | skip 1)
|
|
}
|
|
|
|
# Run docker compose pull
|
|
def docker_compose_pull [files: list<string>] {
|
|
mut cmd = ["docker", "compose"]
|
|
|
|
for file in $files {
|
|
$cmd = ($cmd | append ["-f", $file])
|
|
}
|
|
|
|
$cmd = ($cmd | append "pull")
|
|
|
|
run-external $cmd.0 ...($cmd | skip 1)
|
|
}
|
|
|
|
# Run docker compose build
|
|
def docker_compose_build [files: list<string>] {
|
|
mut cmd = ["docker", "compose"]
|
|
|
|
for file in $files {
|
|
$cmd = ($cmd | append ["-f", $file])
|
|
}
|
|
|
|
$cmd = ($cmd | append "build")
|
|
|
|
run-external $cmd.0 ...($cmd | skip 1)
|
|
}
|
|
|
|
# Run docker compose up
|
|
def docker_compose_up [files: list<string>] {
|
|
mut cmd = ["docker", "compose"]
|
|
|
|
for file in $files {
|
|
$cmd = ($cmd | append ["-f", $file])
|
|
}
|
|
|
|
$cmd = ($cmd | append ["up", "-d"])
|
|
|
|
run-external $cmd.0 ...($cmd | skip 1)
|
|
}
|
|
|
|
# Wait for services to be healthy
|
|
def wait_for_health [timeout: int] {
|
|
let start = (date now)
|
|
let end = ($start + ($timeout * 1sec))
|
|
|
|
mut all_healthy = false
|
|
|
|
while (date now) < $end {
|
|
let containers = (docker ps --filter "name=provisioning-" --format "{{.Names}}")
|
|
|
|
if ($containers | is-empty) {
|
|
print $"(ansi red)No containers running(ansi reset)"
|
|
sleep 2sec
|
|
continue
|
|
}
|
|
|
|
mut healthy_count = 0
|
|
mut total_count = 0
|
|
|
|
for container in ($containers | lines) {
|
|
$total_count = $total_count + 1
|
|
|
|
let health = (docker inspect $container | from json | get 0.State.Health?.Status? | default "unknown")
|
|
|
|
if $health == "healthy" or $health == "unknown" {
|
|
$healthy_count = $healthy_count + 1
|
|
print $" (ansi green)✓(ansi reset) ($container): ($health)"
|
|
} else {
|
|
print $" (ansi yellow)○(ansi reset) ($container): ($health)"
|
|
}
|
|
}
|
|
|
|
if $healthy_count == $total_count {
|
|
$all_healthy = true
|
|
break
|
|
}
|
|
|
|
print $"Healthy: ($healthy_count)/($total_count) - Retrying in 5s..."
|
|
sleep 5sec
|
|
print ""
|
|
}
|
|
|
|
$all_healthy
|
|
}
|
|
|
|
# Display access URLs
|
|
def display_access_urls [mode: string] {
|
|
print $"(ansi cyan_bold)Access URLs:(ansi reset)"
|
|
print $" Orchestrator: http://localhost:8080"
|
|
print $" Control Center: http://localhost:8081"
|
|
|
|
if $mode in ["multi-user", "cicd", "enterprise"] {
|
|
print $" Gitea: http://localhost:3000"
|
|
}
|
|
|
|
print $" OCI Registry: http://localhost:5000"
|
|
print $" Extension Registry: http://localhost:8082"
|
|
|
|
if $mode in ["cicd", "enterprise"] {
|
|
print $" API Server: http://localhost:8083"
|
|
}
|
|
|
|
if $mode == "enterprise" {
|
|
print $" Prometheus: http://localhost:9090"
|
|
print $" Grafana: http://localhost:3001"
|
|
print $" Kibana: http://localhost:5601"
|
|
}
|
|
|
|
print ""
|
|
print $"(ansi cyan_bold)Useful commands:(ansi reset)"
|
|
print $" View logs: docker compose logs -f"
|
|
print $" Check status: docker compose ps"
|
|
print $" Stop platform: docker compose down"
|
|
print $" Restart service: docker compose restart <service>"
|
|
}
|