provisioning/scripts/setup-platform-config.sh

1222 lines
33 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Platform Services Configuration Setup Script
# Generates and manages platform service configurations in provisioning/config/runtime/
#
# Usage: ./setup-platform-config.sh [options]
# ./setup-platform-config.sh --service orchestrator --mode solo
# ./setup-platform-config.sh --mode multiuser --backend web
# ./setup-platform-config.sh --list-modes
#
# Features:
# - Interactive TypeDialog configuration via provisioning/.typedialog/platform/scripts/configure.nu
# - Quick mode setup (solo, multiuser, cicd, enterprise) from defaults
# - Automatic TOML export for Rust service consumption
# - Runtime config detection and management (clean, update, preserve)
set -euo pipefail
# Ensure TypeDialog is installed
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/ensure-typedialog.sh" || true
# Check TypeDialog availability
if ! command -v typedialog &>/dev/null; then
echo -e "\033[1;33m⚠ TypeDialog not found. Attempting installation...\033[0m"
ensure_typedialog_installed true || {
echo -e "\033[0;31m❌ Failed to install TypeDialog\033[0m"
exit 1
}
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
TYPEDIALOG_SCRIPTS="${PROJECT_ROOT}/.typedialog/platform/scripts"
SCHEMAS_PLATFORM="${PROJECT_ROOT}/schemas/platform"
SCHEMAS_VALUES="${SCHEMAS_PLATFORM}/values"
SCHEMAS_CONFIGS="${SCHEMAS_PLATFORM}/configs"
PLATFORM_CONFIG="${PROJECT_ROOT}/../platform/config"
# User platform config path (set by CLI via PROVISIONING_USER_PLATFORM or fallback to OS-specific default)
# CLI sets: PROVISIONING_USER_PLATFORM="${HOME}/Library/Application Support/provisioning/platform" (darwin)
# PROVISIONING_USER_PLATFORM="${HOME}/.config/provisioning/platform" (linux)
if [[ -n "${PROVISIONING_USER_PLATFORM:-}" ]]; then
PLATFORM_CONFIG_BASE="${PROVISIONING_USER_PLATFORM}"
elif [[ "$OSTYPE" == "darwin"* ]]; then
PLATFORM_CONFIG_BASE="${HOME}/Library/Application Support/provisioning/platform"
else
PLATFORM_CONFIG_BASE="${HOME}/.config/provisioning/platform"
fi
# User config directory for service-specific configs (*.ncl files)
USER_CONFIG_DIR="${PLATFORM_CONFIG_BASE}/config"
# Available services and modes
SERVICES=("orchestrator" "control-center" "mcp-server" "vault-service" "extension-registry" "rag" "ai-service" "provisioning-daemon")
MODES=("solo" "multiuser" "cicd" "enterprise")
# Default values
BACKEND="${BACKEND:-web}"
SERVICE=""
MODE=""
ACTION=""
FORCE=false
# ============================================================================
# Helper Functions
# ============================================================================
print_header() {
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
}
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}" >&2
}
# Check if directory/file exists
check_exists() {
if [[ ! -e "$1" ]]; then
print_error "Not found: $1"
return 1
fi
}
# ============================================================================
# Service/Mode Validation
# ============================================================================
validate_service() {
local service="$1"
# "all" is handled by bash script itself, not passed to Nushell
if [[ "$service" == "all" ]]; then
return 0
fi
if [[ ! " ${SERVICES[*]} " =~ " ${service} " ]]; then
print_error "Invalid service: $service"
echo "Valid services: ${SERVICES[*]}, all"
return 1
fi
}
validate_mode() {
local mode="$1"
if [[ ! " ${MODES[*]} " =~ " ${mode} " ]]; then
print_error "Invalid mode: $mode"
echo "Valid modes: ${MODES[*]}"
return 1
fi
}
# ============================================================================
# Directory Setup
# ============================================================================
ensure_config_dirs() {
# Ensure schemas/platform/values directory exists for user configs
if [[ ! -d "$SCHEMAS_VALUES" ]]; then
print_info "Creating values directory: $SCHEMAS_VALUES"
mkdir -p "$SCHEMAS_VALUES"
fi
# Ensure platform/config directory exists for TOML outputs
if [[ ! -d "$PLATFORM_CONFIG" ]]; then
print_info "Creating platform config directory: $PLATFORM_CONFIG"
mkdir -p "$PLATFORM_CONFIG"
fi
# Ensure user platform config directory exists
if [[ ! -d "$USER_CONFIG_DIR" ]]; then
print_info "Creating user config directory: $USER_CONFIG_DIR"
mkdir -p "$USER_CONFIG_DIR"
fi
# Create .gitignore in values directory if not present
if [[ ! -f "$SCHEMAS_VALUES/.gitignore" ]]; then
cat >"$SCHEMAS_VALUES/.gitignore" <<EOF
# User configuration values (private/deployment-specific)
*.ncl
*.toml
# Backup files
*.bak
*.backup
EOF
print_info "Created .gitignore in $SCHEMAS_VALUES"
fi
}
# ============================================================================
# Deployment Mode Management
# ============================================================================
ensure_deployment_mode_config() {
local deployment_mode_file="${PLATFORM_CONFIG_BASE}/deployment-mode.ncl"
# If deployment-mode.ncl already exists, don't overwrite
if [[ -f "$deployment_mode_file" ]]; then
print_info "Deployment mode config already exists: $deployment_mode_file"
return 0
fi
# Create default local deployment mode config
mkdir -p "$PLATFORM_CONFIG_BASE"
cat >"$deployment_mode_file" <<'EOF'
# Platform Deployment Mode Configuration
# Generated by setup-platform-config.sh on initial setup
#
# Modes: "local" | "docker-compose" | "kubernetes"
# This determines HOW platform services are deployed, not WHAT features are enabled
{
# Deployment mode: local | docker-compose | kubernetes
mode = "local",
# Manager configuration (adapts based on mode)
manager = {
# Local: localhost or custom hostname
hostname = "localhost",
port = 9090,
},
# User service configurations directory
config_dir = "$USER_CONFIG_DIR",
# Enable health checks and monitoring
health_checks_enabled = true,
# Timeout for service startup (seconds)
startup_timeout = 60,
# External infrastructure services (databases, registries, CI/CD, etc.)
external_services = [],
# Metadata
description = "Local development with binaries in ~/.local/bin",
created_at = "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
}
EOF
if [[ -f "$deployment_mode_file" ]]; then
print_success "Created deployment mode config: $deployment_mode_file"
print_info "Mode: local (binaries in ~/.local/bin)"
print_info "To change mode, edit: $deployment_mode_file"
return 0
else
print_error "Failed to create deployment mode config"
return 1
fi
}
print_deployment_mode_help() {
cat <<EOF
${BLUE}Deployment Modes${NC}
─────────────────────────────────────────────────────────────
${GREEN}local${NC}
Services run as local binaries (binaries in ~/.local/bin)
Config: Manager with hostname and port
Use case: Development, testing
File: $PLATFORM_CONFIG_BASE/deployment-mode.ncl
${GREEN}docker-compose${NC}
Services run in Docker, orchestrated via docker-compose.yml
Config: Docker daemon socket or host
Use case: Development with full environment
File: $PLATFORM_CONFIG_BASE/deployment-mode.ncl
${GREEN}kubernetes${NC}
Services deployed to K8s cluster via manifests
Config: K8s cluster API server, namespace, kubeconfig
Use case: Production, multi-node environments
File: $PLATFORM_CONFIG_BASE/deployment-mode.ncl
To change deployment mode, edit:
$PLATFORM_CONFIG_BASE/deployment-mode.ncl
EOF
}
# ============================================================================
# Config Detection & Management
# ============================================================================
detect_user_configs() {
# Check if any user config exists in user config directory
shopt -s nullglob
local files=("$USER_CONFIG_DIR"/*.ncl)
shopt -u nullglob
if [[ ${#files[@]} -gt 0 ]]; then
echo "existing"
else
echo "empty"
fi
return 0
}
list_user_services() {
local services=()
shopt -s nullglob
for file in "$USER_CONFIG_DIR"/*.ncl; do
if [[ -f "$file" ]]; then
local basename=$(basename "$file" .ncl)
services+=("$basename")
fi
done
shopt -u nullglob
if [[ ${#services[@]} -gt 0 ]]; then
printf '%s\n' "${services[@]}"
fi
}
# Generate TOML from NCL config in USER_CONFIG_DIR for deployment use
generate_toml_for_user_config() {
local service="$1"
local mode="$2"
local ncl_file="${USER_CONFIG_DIR}/${service}.${mode}.ncl"
local toml_file="${USER_CONFIG_DIR}/${service}.${mode}.toml"
if [[ ! -f "$ncl_file" ]]; then
print_warning "Nickel config not found: $ncl_file"
return 1
fi
print_info "Generating TOML for $service ($mode)..."
# Generate TOML from Nickel with proper NICKEL_IMPORT_PATH
if NICKEL_IMPORT_PATH="$PROJECT_ROOT" nickel export --format toml "$ncl_file" >"$toml_file" 2>/dev/null; then
print_success "Generated: $toml_file"
return 0
else
print_error "Failed to generate TOML for $service ($mode)"
return 1
fi
}
# ============================================================================
# Interactive Prompts
# ============================================================================
prompt_action_existing_config() {
while true; do
echo ""
print_warning "User configurations already exist in: $USER_CONFIG_DIR"
echo ""
echo "Choose action:"
echo " 1) Clean up and start fresh (removes all .ncl and .toml files)"
echo " 2) Use TypeDialog to update configuration [default]"
echo " 3) Setup quick mode (solo/multiuser/cicd/enterprise)"
echo " 4) List existing configurations"
echo " 5) Cancel"
echo ""
echo "Press CTRL-C to cancel at any time"
echo ""
read -rp "Enter choice [1-5] (default: 2): " choice
# Default to 2 (TypeDialog update)
choice="${choice:-2}"
case "$choice" in
1)
ACTION="clean-start"
return 0
;;
2)
ACTION="typedialog"
return 0
;;
3)
ACTION="quick-mode"
return 0
;;
4)
ACTION="list"
return 0
;;
5)
print_info "Cancelled."
exit 0
;;
*) print_error "Invalid choice. Please enter 1-5 (or press CTRL-C to abort)." ;;
esac
done
}
prompt_action_empty_config() {
while true; do
echo ""
echo "Choose how to setup platform configuration:"
echo " 1) Interactive TypeDialog (recommended, with UI form) [default]"
echo " 2) Quick mode setup (choose solo/multiuser/cicd/enterprise)"
echo " 3) Cancel"
echo ""
echo "Press CTRL-C to cancel at any time"
echo ""
read -rp "Enter choice [1-3] (default: 1): " choice
# Default to 1 if empty
choice="${choice:-1}"
case "$choice" in
1)
ACTION="typedialog"
return 0
;;
2)
ACTION="quick-mode"
return 0
;;
3)
print_info "Cancelled."
exit 0
;;
*) print_error "Invalid choice. Please enter 1, 2, or 3 (or press CTRL-C to abort)." ;;
esac
done
}
prompt_for_service() {
local max_choice=$((${#SERVICES[@]} + 1))
while true; do
echo ""
echo "Select service to configure:"
for i in "${!SERVICES[@]}"; do
echo " $((i + 1))) ${SERVICES[$i]}"
done
echo " $max_choice) Configure all services [default]"
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-$max_choice] (default: $max_choice): " choice
# Default to "all services"
choice="${choice:-$max_choice}"
# Validate numeric input
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
print_error "Invalid input. Please enter a number (or press CTRL-C to abort)."
continue
fi
if [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]]; then
if [[ "$choice" == "$max_choice" ]]; then
SERVICE="all"
else
SERVICE="${SERVICES[$((choice - 1))]}"
fi
return 0
else
print_error "Invalid choice. Please enter a number between 1 and $max_choice (or press CTRL-C to abort)."
fi
done
}
prompt_for_mode() {
local max_choice=${#MODES[@]}
while true; do
echo ""
echo "Select deployment mode:"
for i in "${!MODES[@]}"; do
local marker=""
# Mark solo as default
if [[ "${MODES[$i]}" == "solo" ]]; then
marker=" [default]"
fi
echo " $((i + 1))) ${MODES[$i]}$marker"
done
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-$max_choice] (default: 1): " choice
# Default to 1 (solo)
choice="${choice:-1}"
# Validate numeric input
if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
print_error "Invalid input. Please enter a number (or press CTRL-C to abort)."
continue
fi
if [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]]; then
MODE="${MODES[$((choice - 1))]}"
return 0
else
print_error "Invalid choice. Please enter a number between 1 and $max_choice (or press CTRL-C to abort)."
fi
done
}
prompt_for_backend() {
while true; do
echo ""
echo "Select TypeDialog backend:"
echo " 1) web (browser-based, recommended) [default]"
echo " 2) tui (terminal UI)"
echo " 3) cli (command-line)"
echo ""
echo "Press CTRL-C to cancel"
echo ""
read -rp "Enter choice [1-3] (default: 1): " choice
# Default to 1 (web)
choice="${choice:-1}"
case "$choice" in
1)
BACKEND="web"
return 0
;;
2)
BACKEND="tui"
return 0
;;
3)
BACKEND="cli"
return 0
;;
*) print_error "Invalid choice. Please enter 1, 2, or 3 (or press CTRL-C to abort)." ;;
esac
done
}
# ============================================================================
# User Config Management
# ============================================================================
get_or_create_user_config() {
local service="$1"
local mode="$2"
local user_config_file="${USER_CONFIG_DIR}/${service}.${mode}.ncl"
# Ensure user config directory exists
if [[ ! -d "$USER_CONFIG_DIR" ]]; then
mkdir -p "$USER_CONFIG_DIR" || {
print_error "Failed to create user config directory: $USER_CONFIG_DIR"
return 1
}
fi
# If user config exists, return path
if [[ -f "$user_config_file" ]]; then
echo "$user_config_file"
return 0
fi
# User config doesn't exist - initialize from template
local template_path="${SCHEMAS_PLATFORM}/defaults/${service}-defaults.ncl"
local mode_defaults="${SCHEMAS_PLATFORM}/defaults/deployment/${mode}-defaults.ncl"
if [[ ! -f "$template_path" ]]; then
print_error "Default template not found: $template_path"
return 1
fi
if [[ ! -f "$mode_defaults" ]]; then
print_error "Mode defaults not found: $mode_defaults"
return 1
fi
print_info "Initializing user config from defaults: $user_config_file"
# Create initial config by composing defaults + mode overlay
cat >"$user_config_file" <<EOF
# User configuration for $service ($mode mode)
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
#
# This configuration is composed from:
# - Defaults: provisioning/schemas/platform/defaults/${service}-defaults.ncl
# - Mode: provisioning/schemas/platform/defaults/deployment/${mode}-defaults.ncl
let helpers = import "${PROJECT_ROOT}/schemas/platform/common/helpers.ncl"
let defaults = import "${PROJECT_ROOT}/schemas/platform/defaults/${service}-defaults.ncl"
let mode_config = import "${PROJECT_ROOT}/schemas/platform/defaults/deployment/${mode}-defaults.ncl"
# Compose: base defaults + mode overlay
helpers.compose_config defaults mode_config {}
EOF
if [[ -f "$user_config_file" ]]; then
print_success "Created user config: $user_config_file"
echo "$user_config_file"
return 0
else
print_error "Failed to create user config"
return 1
fi
}
# ============================================================================
# Configuration Generation
# ============================================================================
# ============================================================================
# TypeDialog Configuration
# ============================================================================
run_typedialog() {
local service="$1"
local mode="$2"
local backend="$3"
local typedialog_dir="${PROJECT_ROOT}/.typedialog/platform"
local form_path="${typedialog_dir}/forms/${service}-form.toml"
local template_path="${SCHEMAS_PLATFORM}/templates/${service}-config.ncl.j2"
local output_path="${USER_CONFIG_DIR}/${service}.${mode}.ncl"
# Verify form exists
if [[ ! -f "$form_path" ]]; then
print_error "Form not found for $service: $form_path"
return 1
fi
# Verify template exists
if [[ ! -f "$template_path" ]]; then
print_error "Template not found for $service: $template_path"
return 1
fi
# Get or create user config (source file for nickel-roundtrip)
local user_config_path
user_config_path=$(get_or_create_user_config "$service" "$mode") || return 1
# Select TypeDialog binary based on backend
local typedialog_cmd="typedialog"
case "$backend" in
web)
if ! command -v typedialog-web &>/dev/null; then
print_error "typedialog-web not found. Install with: cargo install typedialog --features web"
return 1
fi
typedialog_cmd="typedialog-web"
;;
tui)
if ! command -v typedialog-tui &>/dev/null; then
print_error "typedialog-tui not found. Install with: cargo install typedialog --features tui"
return 1
fi
typedialog_cmd="typedialog-tui"
;;
cli)
if ! command -v typedialog &>/dev/null; then
print_error "typedialog not found. Install with: cargo install typedialog"
return 1
fi
;;
*)
print_error "Invalid backend: $backend"
return 1
;;
esac
# Run TypeDialog with proper argument ordering
# Arguments: nickel-roundtrip --output <output> --ncl-template <template> <input_config> <form> --open
# - input_config: User config file (NCL) to read values from and update
# - output: Where to save the updated config
# - form: Form definition (TOML)
# - template: Jinja2 template for validation/transformation
# - --open: Auto-open browser for web backend
print_info "Launching TypeDialog ($backend) - editing: $user_config_path"
$typedialog_cmd nickel-roundtrip --output "$user_config_path" --ncl-template "$template_path" "$user_config_path" "$form_path" --open || {
print_error "TypeDialog failed or was cancelled"
return 1
}
# Configuration is saved to user config dir (no intermediate copy or TOML export needed)
print_success "Configuration saved to $user_config_path"
}
configure_via_typedialog() {
echo ""
print_header "TypeDialog Configuration"
# Prompt for service if not provided
if [[ -z "$SERVICE" ]]; then
prompt_for_service
else
validate_service "$SERVICE" || return 1
fi
# Prompt for mode if not provided
if [[ -z "$MODE" ]]; then
prompt_for_mode
else
validate_mode "$MODE" || return 1
fi
# Prompt for backend if using web
if [[ "$BACKEND" == "default" ]]; then
prompt_for_backend
fi
# Handle "all" services by iterating
if [[ "$SERVICE" == "all" ]]; then
print_info "Configuring all services for mode: $MODE"
echo ""
local success_count=0
local fail_count=0
for svc in "${SERVICES[@]}"; do
print_info "Launching TypeDialog ($BACKEND backend) for $svc ($MODE)..."
# Direct TypeDialog call (proper TTY handling)
if run_typedialog "$svc" "$MODE" "$BACKEND"; then
print_success "Configuration completed for $svc"
# Generate TOML for deployment use
if generate_toml_for_user_config "$svc" "$MODE"; then
((success_count++))
else
((fail_count++))
fi
else
print_error "Failed to configure $svc - stopping"
return 1
fi
echo ""
done
print_success "Configured all $success_count services"
return 0
fi
print_info "Launching TypeDialog ($BACKEND backend) for $SERVICE ($MODE)..."
echo ""
# Direct TypeDialog call (proper TTY handling)
if run_typedialog "$SERVICE" "$MODE" "$BACKEND"; then
print_success "Configuration completed for $SERVICE"
# Generate TOML from NCL for deployment use
generate_toml_for_user_config "$SERVICE" "$MODE"
print_info ""
print_info "Next steps:"
print_info " - Review config: cat $USER_CONFIG_DIR/$SERVICE.$MODE.ncl"
print_info " - Run service: ORCHESTRATOR_MODE=$MODE cargo run -p $SERVICE"
return 0
else
print_error "TypeDialog configuration cancelled or failed"
return 1
fi
}
# ============================================================================
# Quick Mode Setup (from defaults)
# ============================================================================
setup_quick_mode() {
echo ""
print_header "Quick Mode Setup"
# Prompt for mode if not provided
if [[ -z "$MODE" ]]; then
prompt_for_mode
else
validate_mode "$MODE" || return 1
fi
print_info "Setting up all services for mode: $MODE"
echo ""
local created_count=0
local failed_count=0
# Create config for each service by composing defaults + mode overlay
# Store in user config directory, not temporary schemas directory
mkdir -p "$USER_CONFIG_DIR"
for service in "${SERVICES[@]}"; do
local ncl_file="$USER_CONFIG_DIR/${service}.${MODE}.ncl"
local schema_file="$SCHEMAS_PLATFORM/schemas/${service}.ncl"
local defaults_file="$SCHEMAS_PLATFORM/defaults/${service}-defaults.ncl"
local mode_defaults="$SCHEMAS_PLATFORM/defaults/deployment/${MODE}-defaults.ncl"
if [[ ! -f "$schema_file" ]] || [[ ! -f "$defaults_file" ]]; then
print_warning "Schema or defaults not found for $service (skipping)"
((failed_count++))
continue
fi
# Create Nickel config that composes: schema + defaults + mode overlay
cat >"$ncl_file" <<'EOF'
# Generated configuration for SERVICE (MODE mode)
# Generated: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# DO NOT EDIT MANUALLY - Re-run setup-platform-config.sh to update
#
# This configuration is composed from:
# - Schema: provisioning/schemas/platform/SERVICE.ncl
# - Defaults: provisioning/schemas/platform/defaults/SERVICE-defaults.ncl
# - Mode: provisioning/schemas/platform/defaults/deployment/MODE-defaults.ncl
let helpers = import "schemas/platform/common/helpers.ncl" in
let defaults = import "schemas/platform/defaults/SERVICE-defaults.ncl" in
let mode_config = import "schemas/platform/defaults/deployment/MODE-defaults.ncl" in
# Compose: base defaults + mode overlay
helpers.compose_config defaults mode_config {}
EOF
# Replace placeholders with actual values
sed -i.bak "s|SERVICE|$service|g" "$ncl_file"
sed -i.bak "s|MODE|$MODE|g" "$ncl_file"
rm -f "${ncl_file}.bak"
if [[ -f "$ncl_file" ]]; then
print_success "Created config: $service ($MODE)"
# Generate TOML for deployment use
if generate_toml_for_user_config "$service" "$MODE"; then
((created_count++))
else
((failed_count++))
fi
fi
done
echo ""
print_success "Setup complete: $created_count services configured"
if [[ $failed_count -gt 0 ]]; then
print_warning "$failed_count services failed"
fi
}
# ============================================================================
# Cleanup Operations
# ============================================================================
cleanup_user_configs() {
echo ""
print_warning "This will delete all user configurations in $USER_CONFIG_DIR"
read -rp "Are you sure? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
print_info "Cancelled"
return 0
fi
print_info "Removing user configurations..."
rm -f "$USER_CONFIG_DIR"/*.ncl "$USER_CONFIG_DIR"/*.toml
print_success "Cleanup complete"
}
# ============================================================================
# Listing Operations
# ============================================================================
list_deployment_modes() {
echo ""
print_header "Application Modes (what features are enabled)"
print_info "These control which platform services are available:"
for mode in "${MODES[@]}"; do
echo " - $mode"
done
echo ""
print_header "Infrastructure Deployment Modes (how services are deployed)"
print_info "These control WHERE and HOW services are deployed:"
echo " - local (binaries in ~/.local/bin)"
echo " - docker-compose (Docker containers orchestrated via docker-compose)"
echo " - kubernetes (K8s cluster with manifests)"
}
show_current_deployment_mode() {
local deployment_mode_file="${PLATFORM_CONFIG_BASE}/deployment-mode.ncl"
echo ""
print_header "Current Deployment Mode Configuration"
if [[ ! -f "$deployment_mode_file" ]]; then
print_warning "Deployment mode config not found: $deployment_mode_file"
print_info "Creating default (local) configuration..."
ensure_deployment_mode_config
return 0
fi
echo ""
print_info "File: $deployment_mode_file"
echo ""
cat "$deployment_mode_file"
echo ""
}
set_deployment_mode() {
local new_mode="$1"
local deployment_mode_file="${PLATFORM_CONFIG_BASE}/deployment-mode.ncl"
# Validate mode
if [[ ! " local docker-compose kubernetes " =~ " ${new_mode} " ]]; then
print_error "Invalid deployment mode: $new_mode"
echo "Valid modes: local, docker-compose, kubernetes"
return 1
fi
print_header "Setting Deployment Mode: $new_mode"
# Ensure directory exists
mkdir -p "$PLATFORM_CONFIG_BASE"
# Back up existing config if present
if [[ -f "$deployment_mode_file" ]]; then
local backup_file="${deployment_mode_file}.$(date +%s).bak"
cp "$deployment_mode_file" "$backup_file"
print_warning "Existing configuration backed up to: $backup_file"
print_warning "IMPORTANT: Any manually added external_services will be lost."
print_warning "To preserve external_services, manually add them back to the new config."
fi
# Generate config based on mode (all templates include external_services = [])
case "$new_mode" in
local)
cat >"$deployment_mode_file" <<'EOF'
# Platform Deployment Mode: Local
# Services run as binaries (in ~/.local/bin)
# Generated by setup-platform-config.sh
{
mode = "local",
manager = {
hostname = "localhost",
port = 9090,
},
config_dir = "$USER_CONFIG_DIR",
health_checks_enabled = true,
startup_timeout = 60,
external_services = [],
description = "Local development with binaries",
created_at = "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
}
EOF
;;
docker-compose)
cat >"$deployment_mode_file" <<'EOF'
# Platform Deployment Mode: Docker Compose
# Services run in Docker, orchestrated via docker-compose.yml
# Generated by setup-platform-config.sh
{
mode = "docker-compose",
manager = {
host = "unix:///var/run/docker.sock",
api_version = "v1.45",
},
config_dir = "$USER_CONFIG_DIR",
health_checks_enabled = true,
startup_timeout = 120,
external_services = [],
description = "Docker Compose local development",
created_at = "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
}
EOF
;;
kubernetes)
cat >"$deployment_mode_file" <<'EOF'
# Platform Deployment Mode: Kubernetes
# Services deployed to K8s cluster
# Generated by setup-platform-config.sh
{
mode = "kubernetes",
manager = {
cluster_name = "local-cluster",
api_server = "https://localhost:6443",
namespace = "provisioning",
kubeconfig_path = "~/.kube/config",
},
config_dir = "$USER_CONFIG_DIR",
health_checks_enabled = true,
startup_timeout = 300,
external_services = [],
description = "Kubernetes cluster deployment",
created_at = "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
}
EOF
;;
esac
# Substitute variable
sed -i.bak "s|\$USER_CONFIG_DIR|$USER_CONFIG_DIR|g" "$deployment_mode_file"
sed -i.bak "s|\$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")|$(date -u +"%Y-%m-%dT%H:%M:%SZ")|g" "$deployment_mode_file"
rm -f "${deployment_mode_file}.bak"
if [[ -f "$deployment_mode_file" ]]; then
print_success "Deployment mode set to: $new_mode"
print_info "Configuration file: $deployment_mode_file"
echo ""
print_info "Current configuration:"
cat "$deployment_mode_file"
return 0
else
print_error "Failed to set deployment mode"
return 1
fi
}
list_available_services() {
echo ""
print_info "Available services:"
for service in "${SERVICES[@]}"; do
echo " - $service"
done
}
list_user_configs() {
echo ""
print_header "User Configurations"
local status=$(detect_user_configs)
if [[ "$status" == "empty" ]]; then
print_info "No user configurations found"
return 0
fi
echo "Configured services:"
list_user_services | while read -r service; do
echo "$service"
done
echo ""
echo "Location: $SCHEMAS_VALUES"
}
# ============================================================================
# Help Text
# ============================================================================
show_help() {
cat <<EOF
${BLUE}Platform Services Configuration Setup${NC}
Usage: $(basename "$0") [OPTIONS]
Options:
-s, --service SERVICE Configure specific service (orchestrator, control-center, etc.)
-m, --mode MODE Set application mode (solo, multiuser, cicd, enterprise)
-b, --backend BACKEND TypeDialog backend: web (default), tui, cli
--typedialog Use TypeDialog for configuration
--quick-mode Use quick mode setup from defaults
--list-modes List available application modes
--list-services List available services
--list-configs List existing runtime configurations
--list-deployment-modes List available infrastructure deployment modes
--show-deployment-mode Show current deployment mode config
--set-deployment-mode M Set infrastructure deployment mode (local, docker-compose, kubernetes)
--clean Remove all runtime configurations
--generate-toml Generate TOML files from Nickel configs
-h, --help Show this help message
Examples:
# Interactive setup (prompts for choices)
$(basename "$0")
# Configure specific service via TypeDialog
$(basename "$0") --service orchestrator --mode solo --backend web
# Quick setup for all services in solo mode
$(basename "$0") --quick-mode --mode solo
# List available modes
$(basename "$0") --list-modes
# Regenerate TOML files
$(basename "$0") --generate-toml
Environment Variables:
TYPEDIALOG_BACKEND Override TypeDialog backend (web, tui, cli)
EOF
}
# ============================================================================
# Main Entry Point
# ============================================================================
main() {
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-s | --service)
SERVICE="$2"
shift 2
;;
-m | --mode)
MODE="$2"
shift 2
;;
-b | --backend)
BACKEND="$2"
shift 2
;;
--typedialog)
ACTION="typedialog"
shift
;;
--quick-mode)
ACTION="quick-mode"
shift
;;
--list-modes)
ACTION="list-modes"
shift
;;
--list-services)
ACTION="list-services"
shift
;;
--list-configs)
ACTION="list"
shift
;;
--list-deployment-modes)
ACTION="list-deployment-modes"
shift
;;
--show-deployment-mode)
ACTION="show-deployment-mode"
shift
;;
--set-deployment-mode)
ACTION="set-deployment-mode"
DEPLOYMENT_MODE="$2"
shift 2
;;
--clean)
ACTION="clean"
shift
;;
--generate-toml)
ACTION="generate-toml"
shift
;;
--force)
FORCE=true
shift
;;
-h | --help)
show_help
exit 0
;;
*)
print_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Ensure directories exist
ensure_config_dirs
# Ensure deployment mode config exists (create if missing with local default)
ensure_deployment_mode_config
# Check for required tools
if ! command -v nickel &>/dev/null; then
print_error "Nickel not found. Please install Nickel (https://nickel-lang.org/)"
exit 1
fi
if ! command -v nu &>/dev/null; then
print_error "Nushell not found. Please install Nushell"
exit 1
fi
# Execute requested action
case "${ACTION:-}" in
typedialog)
configure_via_typedialog || exit 1
;;
quick-mode)
setup_quick_mode || exit 1
;;
clean)
cleanup_user_configs
;;
list)
list_user_configs
;;
list-modes)
list_deployment_modes
;;
list-services)
list_available_services
;;
list-deployment-modes)
list_deployment_modes
;;
show-deployment-mode)
show_current_deployment_mode
;;
set-deployment-mode)
if [[ -z "${DEPLOYMENT_MODE:-}" ]]; then
print_error "Deployment mode not specified"
echo "Usage: $0 --set-deployment-mode {local|docker-compose|kubernetes}"
exit 1
fi
set_deployment_mode "$DEPLOYMENT_MODE"
;;
*)
# No action specified - interactive mode
print_header "Platform Services Configuration Setup"
local user_config_status=$(detect_user_configs)
if [[ "$user_config_status" == "existing" ]]; then
prompt_action_existing_config
else
prompt_action_empty_config
fi
# Execute chosen action
case "$ACTION" in
typedialog) configure_via_typedialog || exit 1 ;;
quick-mode) setup_quick_mode || exit 1 ;;
clean) cleanup_user_configs ;;
list) list_user_configs ;;
*)
print_error "No action selected"
exit 1
;;
esac
;;
esac
echo ""
print_success "Done!"
}
# Run main function
main "$@"