1222 lines
33 KiB
Bash
Executable file
1222 lines
33 KiB
Bash
Executable file
#!/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 "$@"
|