456 lines
16 KiB
Plaintext
456 lines
16 KiB
Plaintext
|
|
# 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 ""
|
||
|
|
}
|