456 lines
16 KiB
Plaintext
Raw Permalink Normal View History

# System Setup Module
# Orchestrates complete provisioning system setup and initialization
# Follows Nushell guidelines: explicit types, single purpose, no try-catch
use ./mod.nu *
use ./detection.nu *
use ./validation.nu *
use ./wizard.nu *
# ============================================================================
# SYSTEM CONFIGURATION CREATION
# ============================================================================
# Create system configuration file
export def create-system-config-file [
config_base: string
config_data: record
]: nothing -> bool {
let system_config_path = $"($config_base)/system.toml"
let system_config = {
version: "1.0.0"
install_path: (config_data.install_path? | default "")
os_name: (config_data.os_name? | default (detect-os))
os_version: (config_data.os_version? | default "unknown")
config_base_path: $config_base
cache_base_path: $"($config_base)/cache"
workspaces_dir: $"($config_base)/workspaces"
system_architecture: (detect-architecture)
cpu_count: (config_data.cpu_count? | default (get-cpu-count))
memory_total_gb: (config_data.memory_gb? | default (get-system-memory-gb))
disk_total_gb: (config_data.disk_gb? | default (get-system-disk-gb))
setup_date: (get-timestamp-iso8601)
setup_by_user: (get-current-user)
setup_hostname: (get-system-hostname)
}
save-config-toml $system_config_path $system_config
}
# Create platform services configuration
export def create-platform-config-file [
config_base: string
config_data: record
]: nothing -> bool {
let platform_config_path = $"($config_base)/platform/deployment.toml"
let platform_config = {
version: "1.0.0"
deployment: {
mode: (config_data.deployment_mode? | default "docker-compose")
location_type: "local"
}
docker_compose: {
enabled: true
path: (config_data.docker_compose_path? | default ".")
}
kubernetes: {
enabled: false
namespace: "provisioning"
kubeconfig: ""
manifests_path: "./k8s"
}
remote_ssh: {
enabled: false
host: ""
user: "deploy"
ssh_key: "~/.ssh/id_rsa"
}
systemd: {
enabled: false
service_prefix: "provisioning-"
}
services: {
orchestrator: {
endpoint: "http://localhost:9090/health"
timeout_seconds: 30
type: "http"
}
control_center: {
endpoint: "http://localhost:3000/health"
timeout_seconds: 30
type: "http"
}
kms_service: {
endpoint: "http://localhost:3001/health"
timeout_seconds: 30
type: "http"
}
}
platform: {
orchestrator: {
endpoint: "http://localhost:9090"
api_version: "v1"
}
control_center: {
url: "http://localhost:3000"
token: ""
}
kms: {
endpoint: "http://localhost:3001"
backend: "rustyvault"
rustyvault: {
enabled: true
master_key: ""
}
}
}
}
save-config-toml $platform_config_path $platform_config
}
# Create user preferences configuration
export def create-user-preferences-file [
config_base: string
config_data: record
]: nothing -> bool {
let user_prefs_path = $"($config_base)/user_preferences.toml"
let user_prefs = {
output_format: (config_data.output_format? | default "yaml")
use_colors: (config_data.use_colors? | default true)
file_viewer: (config_data.file_viewer? | default "bat")
confirm_delete: (config_data.confirm_delete? | default true)
confirm_deploy: (config_data.confirm_deploy? | default true)
default_log_level: (config_data.default_log_level? | default "info")
default_provider: (config_data.default_provider? | default "local")
http_use_curl: (config_data.http_use_curl? | default false)
http_timeout_seconds: (config_data.http_timeout_seconds? | default 30)
terraform_auto_approve: (config_data.terraform_auto_approve? | default false)
editor: (config_data.editor? | default "vim")
}
save-config-toml $user_prefs_path $user_prefs
}
# ============================================================================
# PROVIDER CONFIGURATION
# ============================================================================
# Create provider configuration file
export def create-provider-config-file [
config_base: string
provider_name: string
credentials_source: string = ""
]: nothing -> bool {
let provider_config_path = $"($config_base)/providers/($provider_name).toml"
let provider_config = (match $provider_name {
"upcloud" => {
{
api_url: "https://api.upcloud.com/1.3"
interface: "API"
credentials_source: ($credentials_source | default "rustyvault://system/providers/upcloud")
timeout_seconds: 30
}
}
"aws" => {
{
region: "us-east-1"
credentials_source: ($credentials_source | default "rustyvault://system/providers/aws")
timeout_seconds: 30
}
}
"hetzner" => {
{
api_url: "https://api.hetzner.cloud/v1"
credentials_source: ($credentials_source | default "rustyvault://system/providers/hetzner")
timeout_seconds: 30
}
}
"local" => {
{
base_path: "/tmp/provisioning-local"
timeout_seconds: 10
}
}
_ => {}
})
save-config-toml $provider_config_path $provider_config
}
# ============================================================================
# RUSTYVAULT BOOTSTRAP SETUP
# ============================================================================
# Create RustyVault bootstrap key placeholder
export def create-rustyvault-bootstrap-placeholder [
config_base: string
]: nothing -> bool {
let bootstrap_path = $"($config_base)/rustyvault_bootstrap.age"
# Create placeholder file with instructions
let bootstrap_content = "# RustyVault Bootstrap Key (SOPS-Encrypted)\n# This file contains the encrypted RustyVault master key\n# Generated: (get-timestamp-iso8601)\n#\n# To generate the actual key:\n# age-keygen -o > ~/.age/keys.txt\n# sops encrypt --age <public-key> rustyvault_bootstrap.age > .sops.yaml\n#\n# This is the ONLY secret stored with SOPS - all other credentials\n# are stored encrypted in RustyVault with encrypt/decrypt at-rest support.\n\n# Placeholder - run: provisioning setup rustyvault-bootstrap\n"
let result = (do { $bootstrap_content | save -f $bootstrap_path } | complete)
($result.exit_code == 0)
}
# ============================================================================
# WORKSPACE REGISTRY
# ============================================================================
# Create workspace registry file
export def create-workspace-registry [
config_base: string
]: nothing -> bool {
let registry_path = $"($config_base)/workspaces_registry.yaml"
let workspace_registry = {
version: "1.0.0"
active_workspace: ""
workspaces: []
created_at: (get-timestamp-iso8601)
metadata: {
initialized: true
}
}
save-config-yaml $registry_path $workspace_registry
}
# ============================================================================
# CEDAR POLICIES SETUP
# ============================================================================
# Create default Cedar policies directory and files
export def setup-cedar-policies [
config_base: string
]: nothing -> bool {
let policies_dir = $"($config_base)/cedar-policies"
# Create directory
let mkdir_result = (do { mkdir $policies_dir } | complete)
if ($mkdir_result.exit_code != 0) {
return false
}
# Create default policies
let default_policies = "// Default Cedar Authorization Policies\n// Version: 1.0.0\n// Auto-generated during system setup\n\n// Allow admin full access\npermit(\n principal in Role::\"Admin\",\n action,\n resource\n);\n\n// Allow users workspace-scoped access\npermit(\n principal in Workspace::\".*\",\n action in [\n Action::\"server.create\",\n Action::\"server.read\",\n Action::\"taskserv.install\"\n ],\n resource\n) when {\n context has workspace_id &&\n resource.workspace_id == principal.id\n};\n\n// Require MFA for production operations\nforbid(\n principal,\n action in [\n Action::\"server.delete\",\n Action::\"cluster.delete\",\n Action::\"database.delete\"\n ],\n resource in Environment::\"production\"\n) unless {\n context.mfa_verified == true\n};\n"
let policies_file = $"($policies_dir)/default.cedar"
let result = (do { $default_policies | save -f $policies_file } | complete)
($result.exit_code == 0)
}
# ============================================================================
# COMPLETE SYSTEM SETUP
# ============================================================================
# Execute complete system setup
export def setup-system-complete [
setup_config: record
--verbose = false
]: nothing -> record {
print-setup-header "Complete System Setup"
print ""
let config_base = (get-config-base-path)
# Step 1: Ensure directories exist
print-setup-info "Creating configuration directories..."
if not (ensure-config-dirs) {
print-setup-error "Failed to create configuration directories"
return {
success: false
errors: ["Failed to create configuration directories"]
}
}
print-setup-success "Configuration directories created"
# Step 2: Create system configuration
print-setup-info "Creating system configuration..."
if not (create-system-config-file $config_base $setup_config.system_config) {
print-setup-error "Failed to create system configuration"
return {
success: false
errors: ["Failed to create system configuration"]
}
}
print-setup-success "System configuration created"
# Step 3: Create platform configuration
print-setup-info "Creating platform services configuration..."
if not (create-platform-config-file $config_base $setup_config) {
print-setup-error "Failed to create platform configuration"
return {
success: false
errors: ["Failed to create platform configuration"]
}
}
print-setup-success "Platform services configuration created"
# Step 4: Create user preferences
print-setup-info "Creating user preferences..."
if not (create-user-preferences-file $config_base {}) {
print-setup-error "Failed to create user preferences"
return {
success: false
errors: ["Failed to create user preferences"]
}
}
print-setup-success "User preferences created"
# Step 5: Setup provider configurations
print-setup-info "Setting up provider configurations..."
for provider in ($setup_config.providers? | default ["local"]) {
if not (create-provider-config-file $config_base $provider) {
print-setup-warning $"Failed to create provider config for ($provider)"
}
}
print-setup-success "Provider configurations created"
# Step 6: Create RustyVault bootstrap placeholder
print-setup-info "Setting up RustyVault bootstrap key..."
if not (create-rustyvault-bootstrap-placeholder $config_base) {
print-setup-warning "Failed to create RustyVault bootstrap placeholder"
}
print-setup-success "RustyVault bootstrap key placeholder created"
# Step 7: Create workspace registry
print-setup-info "Creating workspace registry..."
if not (create-workspace-registry $config_base) {
print-setup-error "Failed to create workspace registry"
return {
success: false
errors: ["Failed to create workspace registry"]
}
}
print-setup-success "Workspace registry created"
# Step 8: Setup Cedar policies
print-setup-info "Setting up Cedar authorization policies..."
if not (setup-cedar-policies $config_base) {
print-setup-warning "Failed to setup Cedar policies"
}
print-setup-success "Cedar authorization policies configured"
# Step 9: Create setup metadata
print-setup-info "Finalizing setup..."
let setup_metadata = {
version: "1.0.0"
completed_at: (get-timestamp-iso8601)
setup_by: (get-current-user)
setup_on_hostname: (get-system-hostname)
deployment_mode: ($setup_config.deployment_mode? | default "docker-compose")
providers: ($setup_config.providers? | default ["local"])
security: ($setup_config.security? | default {})
}
let metadata_path = $"($config_base)/setup_metadata.yaml"
let save_result = (save-config-yaml $metadata_path $setup_metadata)
if not $save_result {
print-setup-warning "Failed to save setup metadata"
}
print ""
print-setup-success "System setup completed successfully!"
print ""
{
success: true
config_base: $config_base
timestamp: (get-timestamp-iso8601)
setup_config: $setup_config
}
}
# Run interactive setup wizard with all steps
export def run-interactive-setup [
--verbose = false
]: nothing -> record {
let wizard_result = (run-setup-wizard --verbose=$verbose)
if not $wizard_result.completed {
return {
success: false
reason: "Setup wizard cancelled by user"
}
}
setup-system-complete $wizard_result --verbose=$verbose
}
# Run setup with defaults (no interaction)
export def run-setup-defaults [
--verbose = false
]: nothing -> record {
let defaults = (run-setup-with-defaults)
setup-system-complete $defaults --verbose=$verbose
}
# Run minimal setup
export def run-setup-minimal [
--verbose = false
]: nothing -> record {
let minimal = (run-minimal-setup)
setup-system-complete $minimal --verbose=$verbose
}
# ============================================================================
# SETUP INFORMATION
# ============================================================================
# Print setup status
export def print-setup-status []: nothing -> nothing {
let config_base = (get-config-base-path)
print ""
print "╔═══════════════════════════════════════════════════════════════╗"
print "║ PROVISIONING SYSTEM SETUP STATUS ║"
print "╚═══════════════════════════════════════════════════════════════╝"
print ""
print $"Configuration Base: ($config_base)"
print ""
if ($"($config_base)/system.toml" | path exists) {
print "✅ System configuration found"
} else {
print "❌ System configuration NOT found"
}
if ($"($config_base)/platform/deployment.toml" | path exists) {
print "✅ Platform configuration found"
} else {
print "❌ Platform configuration NOT found"
}
if ($"($config_base)/user_preferences.toml" | path exists) {
print "✅ User preferences found"
} else {
print "❌ User preferences NOT found"
}
if ($"($config_base)/workspaces_registry.yaml" | path exists) {
print "✅ Workspace registry found"
} else {
print "❌ Workspace registry NOT found"
}
if ($"($config_base)/cedar-policies" | path exists) {
print "✅ Cedar policies directory found"
} else {
print "❌ Cedar policies directory NOT found"
}
print ""
print "═══════════════════════════════════════════════════════════════"
print ""
}