provisioning/scripts/setup-platform-config.sh

738 lines
22 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"
CONFIG_RUNTIME="${PROJECT_ROOT}/config/runtime"
CONFIG_GENERATED="${CONFIG_RUNTIME}/generated"
# 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_runtime_dir() {
if [[ ! -d "$CONFIG_RUNTIME" ]]; then
print_info "Creating runtime config directory: $CONFIG_RUNTIME"
mkdir -p "$CONFIG_RUNTIME" "$CONFIG_GENERATED"
fi
# Create .gitignore if not present
if [[ ! -f "$CONFIG_RUNTIME/.gitignore" ]]; then
cat > "$CONFIG_RUNTIME/.gitignore" <<EOF
# Runtime configurations (private/deployment-specific)
*.ncl
*.toml
# Generated artifacts
generated/
# Backup files
*.bak
*.backup
EOF
print_info "Created .gitignore in $CONFIG_RUNTIME"
fi
}
# ============================================================================
# Config Detection & Management
# ============================================================================
detect_runtime_config() {
# Check if any runtime config exists
if ls "$CONFIG_RUNTIME"/*.ncl 1>/dev/null 2>&1; then
echo "existing"
return 0
fi
echo "empty"
return 0
}
list_runtime_services() {
local services=()
for file in "$CONFIG_RUNTIME"/*.ncl; do
if [[ -f "$file" ]]; then
local basename=$(basename "$file" .ncl)
services+=("$basename")
fi
done
if [[ ${#services[@]} -gt 0 ]]; then
printf '%s\n' "${services[@]}"
fi
}
# ============================================================================
# Interactive Prompts
# ============================================================================
prompt_action_existing_config() {
while true; do
echo ""
print_warning "Runtime configuration already exists in: $CONFIG_RUNTIME"
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
}
# ============================================================================
# Configuration Generation
# ============================================================================
generate_toml_for_service() {
local service="$1"
local mode="$2"
local ncl_file="${CONFIG_RUNTIME}/${service}.${mode}.ncl"
if [[ ! -f "$ncl_file" ]]; then
print_warning "Nickel config not found: $ncl_file"
return 1
fi
print_info "Exporting TOML for $service ($mode)..."
local toml_file="${CONFIG_GENERATED}/${service}.${mode}.toml"
# Generate TOML from Nickel
if 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
}
generate_all_tomls() {
echo ""
print_header "Generating TOML Exports"
local generated_count=0
local failed_count=0
# Scan for all .ncl files in runtime
for ncl_file in "$CONFIG_RUNTIME"/*.ncl; do
if [[ -f "$ncl_file" ]]; then
local basename=$(basename "$ncl_file" .ncl)
local service="${basename%.*}" # Remove mode suffix
local mode="${basename##*.}" # Extract mode
if generate_toml_for_service "$service" "$mode"; then
((generated_count++))
else
((failed_count++))
fi
fi
done
echo ""
print_success "Generated $generated_count TOML files"
if [[ $failed_count -gt 0 ]]; then
print_warning "$failed_count files failed"
fi
}
# ============================================================================
# TypeDialog Configuration
# ============================================================================
configure_via_typedialog() {
check_exists "$TYPEDIALOG_SCRIPTS/configure.nu" || return 1
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)..."
# Execute from scripts dir so relative module imports work
if (cd "$TYPEDIALOG_SCRIPTS" && nu configure.nu "$svc" "$MODE" --backend "$BACKEND"); then
print_success "Configuration completed for $svc"
generate_toml_for_service "$svc" "$MODE" && ((success_count++)) || ((fail_count++))
else
print_warning "Configuration skipped or failed for $svc"
((fail_count++))
fi
echo ""
done
print_info "Configured $success_count services, $fail_count failed/skipped"
return 0
fi
print_info "Launching TypeDialog ($BACKEND backend) for $SERVICE ($MODE)..."
echo ""
# Run TypeDialog via Nushell script (execute from scripts dir so module imports work)
if (cd "$TYPEDIALOG_SCRIPTS" && nu configure.nu "$SERVICE" "$MODE" --backend "$BACKEND"); then
print_success "Configuration completed for $SERVICE"
# Auto-generate TOML
generate_toml_for_service "$SERVICE" "$MODE"
print_info ""
print_info "Next steps:"
print_info " - Review config: cat $CONFIG_RUNTIME/$SERVICE.$MODE.ncl"
print_info " - Export TOML: $TYPEDIALOG_SCRIPTS/generate-configs.nu $SERVICE $MODE"
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
for service in "${SERVICES[@]}"; do
local ncl_file="$CONFIG_RUNTIME/${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/schemas/SERVICE.ncl
# - Defaults: provisioning/schemas/platform/defaults/SERVICE-defaults.ncl
# - Mode: provisioning/schemas/platform/defaults/deployment/MODE-defaults.ncl
let helpers = import "provisioning/schemas/platform/common/helpers.ncl"
let schema = import "provisioning/schemas/platform/schemas/SERVICE.ncl"
let defaults = import "provisioning/schemas/platform/defaults/SERVICE-defaults.ncl"
let mode_config = import "provisioning/schemas/platform/defaults/deployment/MODE-defaults.ncl"
# Compose: base defaults + mode overlay + validators
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
if generate_toml_for_service "$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_runtime_config() {
echo ""
print_warning "This will delete all runtime configurations"
read -rp "Are you sure? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
print_info "Cancelled"
return 0
fi
print_info "Removing runtime configurations..."
rm -f "$CONFIG_RUNTIME"/*.ncl "$CONFIG_GENERATED"/*.toml
print_success "Cleanup complete"
}
# ============================================================================
# Listing Operations
# ============================================================================
list_deployment_modes() {
echo ""
print_info "Available deployment modes:"
for mode in "${MODES[@]}"; do
echo " - $mode"
done
}
list_available_services() {
echo ""
print_info "Available services:"
for service in "${SERVICES[@]}"; do
echo " - $service"
done
}
list_runtime_configs() {
echo ""
print_header "Runtime Configurations"
local status=$(detect_runtime_config)
if [[ "$status" == "empty" ]]; then
print_info "No runtime configurations found"
return 0
fi
echo "Configured services:"
list_runtime_services | while read -r service; do
echo "$service"
done
echo ""
echo "Location: $CONFIG_RUNTIME"
}
# ============================================================================
# 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 deployment 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 deployment modes
--list-services List available services
--list-configs List existing runtime configurations
--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 ;;
--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_runtime_dir
# 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_runtime_config
;;
list)
list_runtime_configs
;;
list-modes)
list_deployment_modes
;;
list-services)
list_available_services
;;
generate-toml)
generate_all_tomls
;;
*)
# No action specified - interactive mode
print_header "Platform Services Configuration Setup"
local runtime_status=$(detect_runtime_config)
if [[ "$runtime_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_runtime_config ;;
list) list_runtime_configs ;;
*) print_error "No action selected"; exit 1 ;;
esac
;;
esac
echo ""
print_success "Done!"
}
# Run main function
main "$@"