# Mode Management Commands # Execution mode switching and configuration for provisioning system # # Modes: # - solo: Single developer local development # - multi-user: Team collaboration with shared services # - cicd: CI/CD pipeline execution # - enterprise: Production enterprise deployment use ../utils/logging.nu * # Get current active mode export def "mode current" [] -> record { let mode_file = get-mode-config-path if not ($mode_file | path exists) { return { mode: "solo" configured: false config_file: null message: "No mode configured, using default 'solo' mode" } } let config = open $mode_file { mode: $config.mode configured: true config_file: $mode_file description: $config.description? } } # List all available modes export def "mode list" [] -> table { let modes_dir = get-modes-dir if not ($modes_dir | path exists) { return [] } ls $modes_dir | where name =~ ".yaml$" | each {|it| let config = open $it.name let is_current = (is-current-mode $config.mode) { mode: $config.mode description: $config.description current: $is_current config_file: $it.name } } | sort-by mode } # Show mode information export def "mode show" [ mode_name?: string # Mode to show (defaults to current) ] -> record { let target_mode = if ($mode_name == null) { (mode current).mode } else { $mode_name } let config_file = get-mode-file $target_mode if not ($config_file | path exists) { error make { msg: $"Mode '($target_mode)' not found" label: { text: "Available modes: solo, multi-user, cicd, enterprise" span: (metadata $mode_name).span } } } open $config_file } # Switch to a different mode export def "mode switch" [ mode_name: string # Mode to switch to (solo, multi-user, cicd, enterprise) --validate # Validate mode configuration before switching --dry-run # Show what would change without applying ] -> nothing { # Validate mode exists let config_file = get-mode-file $mode_name if not ($config_file | path exists) { error make { msg: $"Mode '($mode_name)' configuration not found" label: { text: $"Expected: ($config_file)" span: (metadata $mode_name).span } } } # Load and validate configuration let config = open $config_file if $validate { log-info $"Validating mode configuration: ($mode_name)" mode validate $mode_name } # Get current mode let current = (mode current).mode if $dry_run { print $"Would switch from '($current)' to '($mode_name)'" print $"\nConfiguration changes:" print $config return } # Confirm switch (unless in non-interactive mode) if (is-interactive) and ($current != $mode_name) { let confirm = (input $"Switch from '($current)' to '($mode_name)' mode? \(y/N\) ") if ($confirm | str downcase) != "y" { log-info "Mode switch cancelled" return } } # Copy mode config to active config let active_config = get-mode-config-path # Backup current config if exists if ($active_config | path exists) { let backup = $"($active_config).backup" cp $active_config $backup log-debug $"Backed up current config to ($backup)" } # Copy new mode config cp $config_file $active_config log-info $"Switched to '($mode_name)' mode" # Show next steps based on mode show-mode-next-steps $mode_name } # Validate mode configuration export def "mode validate" [ mode_name?: string # Mode to validate (defaults to current) ] -> record { let target_mode = if ($mode_name == null) { (mode current).mode } else { $mode_name } let config_file = get-mode-file $target_mode if not ($config_file | path exists) { error make { msg: $"Mode configuration not found: ($config_file)" } } log-info $"Validating mode: ($target_mode)" let config = open $config_file mut validation_results = [] # Validate required fields let required_fields = ["mode", "description", "authentication", "services", "extensions", "workspaces", "security"] for field in $required_fields { if ($field in $config) { $validation_results = ($validation_results | append { check: $"Required field: ($field)" status: "pass" }) } else { $validation_results = ($validation_results | append { check: $"Required field: ($field)" status: "fail" message: $"Missing required field: ($field)" }) } } # Validate service configurations let service_checks = validate-services $config.services $validation_results = ($validation_results | append $service_checks) # Validate authentication let auth_checks = validate-authentication $config.authentication $validation_results = ($validation_results | append $auth_checks) # Validate extensions let ext_checks = validate-extensions $config.extensions $validation_results = ($validation_results | append $ext_checks) # Count results let total = ($validation_results | length) let passed = ($validation_results | where status == "pass" | length) let failed = ($validation_results | where status == "fail" | length) # Display results print $"\nValidation Results for '($target_mode)' mode:" print $" Total checks: ($total)" print $" Passed: ($passed)" print $" Failed: ($failed)" if $failed > 0 { print $"\nFailed checks:" $validation_results | where status == "fail" | each {|it| print $" āœ— ($it.check): ($it.message)"} } { mode: $target_mode total_checks: $total passed: $passed failed: $failed success: ($failed == 0) checks: $validation_results } } # Initialize mode system export def "mode init" [] -> nothing { log-info "Initializing mode system" let modes_dir = get-modes-dir # Create modes directory if not exists if not ($modes_dir | path exists) { mkdir $modes_dir log-debug $"Created modes directory: ($modes_dir)" } # Check if mode configs exist let mode_files = ["solo.yaml", "multi-user.yaml", "cicd.yaml", "enterprise.yaml"] for mode_file in $mode_files { let full_path = ($modes_dir | path join $mode_file) if not ($full_path | path exists) { log-warning $"Mode configuration missing: ($mode_file)" } } # Initialize to solo mode if no active config let active_config = get-mode-config-path if not ($active_config | path exists) { log-info "No active mode configuration, initializing to 'solo' mode" mode switch solo --no-confirm } log-info "Mode system initialized" } # OCI registry management export def "mode oci-registry" [ action: string # Action: start, stop, status, logs ] -> nothing { let current_mode = (mode current) let config = mode show if ($config.services.oci-registry.deployment == "disabled") { log-error "OCI registry is disabled in current mode" return } if ($config.services.oci-registry.deployment != "local") { log-error "OCI registry management only available for local deployment" log-info $"Current deployment: ($config.services.oci-registry.deployment)" return } match $action { "start" => { start-oci-registry $config } "stop" => { stop-oci-registry $config } "status" => { oci-registry-status $config } "logs" => { oci-registry-logs $config } _ => { error make { msg: $"Unknown action: ($action)" label: { text: "Valid actions: start, stop, status, logs" span: (metadata $action).span } } } } } # Compare modes export def "mode compare" [ mode1: string # First mode to compare mode2: string # Second mode to compare ] -> record { let config1 = mode show $mode1 let config2 = mode show $mode2 print $"\nComparing '($mode1)' and '($mode2)' modes:\n" # Compare key aspects print "Authentication:" print $" ($mode1): ($config1.authentication.type)" print $" ($mode2): ($config2.authentication.type)" print "\nService Deployments:" print $" Orchestrator:" print $" ($mode1): ($config1.services.orchestrator.deployment)" print $" ($mode2): ($config2.services.orchestrator.deployment)" print $" OCI Registry:" print $" ($mode1): ($config1.services.oci-registry.deployment) \(($config1.services.oci-registry.type))" print $" ($mode2): ($config2.services.oci-registry.deployment) \(($config2.services.oci-registry.type))" print "\nExtension Source:" print $" ($mode1): ($config1.extensions.source)" print $" ($mode2): ($config2.extensions.source)" print "\nWorkspace Locking:" print $" ($mode1): ($config1.workspaces.locking)" print $" ($mode2): ($config2.workspaces.locking)" print "\nSecurity:" print $" Encryption at rest:" print $" ($mode1): ($config1.security.encryption_at_rest)" print $" ($mode2): ($config2.security.encryption_at_rest)" print $" Encryption in transit:" print $" ($mode1): ($config1.security.encryption_in_transit)" print $" ($mode2): ($config2.security.encryption_in_transit)" { mode1: $mode1 mode2: $mode2 comparison: { authentication: { mode1: $config1.authentication.type mode2: $config2.authentication.type different: ($config1.authentication.type != $config2.authentication.type) } deployment: { mode1: $config1.services.orchestrator.deployment mode2: $config2.services.orchestrator.deployment different: ($config1.services.orchestrator.deployment != $config2.services.orchestrator.deployment) } } } } # ============================================================================= # Helper Functions # ============================================================================= def get-mode-config-path [] -> string { let workspace_config = $env.PROVISIONING_WORKSPACE_CONFIG? | default ( $env.HOME | path join ".provisioning" "config" ) $workspace_config | path join "active-mode.yaml" } def get-modes-dir [] -> string { $env.PROVISIONING_PROJECT_ROOT? | default (pwd) | path join "workspace" "config" "modes" } def get-mode-file [mode: string] -> string { let modes_dir = get-modes-dir $modes_dir | path join $"($mode).yaml" } def is-current-mode [mode: string] -> bool { let current = (mode current).mode $current == $mode } def is-interactive [] -> bool { (term size).columns > 0 } def validate-services [services: record] -> list { mut results = [] # Check orchestrator if ("orchestrator" in $services) { $results = ($results | append { check: "Service: orchestrator" status: "pass" }) if ($services.orchestrator.deployment in ["local", "remote", "k8s"]) { $results = ($results | append { check: "Orchestrator deployment valid" status: "pass" }) } else { $results = ($results | append { check: "Orchestrator deployment valid" status: "fail" message: $"Invalid deployment: ($services.orchestrator.deployment)" }) } } else { $results = ($results | append { check: "Service: orchestrator" status: "fail" message: "Orchestrator service not configured" }) } # Check OCI registry if ("oci-registry" in $services) { $results = ($results | append { check: "Service: oci-registry" status: "pass" }) if ($services.oci-registry.deployment in ["local", "remote", "disabled"]) { $results = ($results | append { check: "OCI registry deployment valid" status: "pass" }) } } $results } def validate-authentication [auth: record] -> list { mut results = [] if ("type" in $auth) { let valid_types = ["none", "token", "mtls", "oauth", "kms"] if ($auth.type in $valid_types) { $results = ($results | append { check: "Authentication type valid" status: "pass" }) } else { $results = ($results | append { check: "Authentication type valid" status: "fail" message: $"Invalid auth type: ($auth.type)" }) } } else { $results = ($results | append { check: "Authentication type" status: "fail" message: "Authentication type not specified" }) } $results } def validate-extensions [extensions: record] -> list { mut results = [] if ("source" in $extensions) { let valid_sources = ["local", "gitea", "oci", "mixed"] if ($extensions.source in $valid_sources) { $results = ($results | append { check: "Extension source valid" status: "pass" }) } else { $results = ($results | append { check: "Extension source valid" status: "fail" message: $"Invalid source: ($extensions.source)" }) } } $results } def show-mode-next-steps [mode: string] -> nothing { print $"\nāœ“ Switched to '($mode)' mode\n" match $mode { "solo" => { print "Next steps:" print " 1. Start orchestrator: cd provisioning/platform/orchestrator && ./scripts/start-orchestrator.nu" print " 2. (Optional) Start OCI registry: provisioning mode oci-registry start" print " 3. Begin development: provisioning server create --check" } "multi-user" => { print "Next steps:" print " 1. Authenticate: provisioning auth login" print " 2. Lock workspace: provisioning workspace lock" print " 3. Pull extensions from OCI: provisioning extension pull" print " 4. Check quota: provisioning quota show" } "cicd" => { print "Next steps:" print " 1. Ensure CI/CD environment variables are set" print " 2. Token must be available at: /var/run/secrets/provisioning/token" print " 3. Run validation: provisioning validate --all" print " 4. Execute pipeline: provisioning server create --check" } "enterprise" => { print "Next steps:" print " 1. Ensure mTLS certificates are provisioned" print " 2. Verify Kubernetes connectivity: kubectl get pods -n provisioning-system" print " 3. Check Harbor access: docker login harbor.enterprise.local" print " 4. Review compliance policies: provisioning policy list" print " 5. Request workspace approval: provisioning workspace request" } } } def start-oci-registry [config: record] -> nothing { let oci_config = $config.services.oci-registry let data_dir = $oci_config.data_dir | str replace "~" $env.HOME # Create data directory if not exists if not ($data_dir | path exists) { mkdir $data_dir log-info $"Created OCI registry data directory: ($data_dir)" } # Check if already running let status_check = (do { http get $"http://($oci_config.endpoint):($oci_config.port)/v2/" } | complete) if $status_check.exit_code == 0 { log-warning "OCI registry already running" return } # Start registry (implementation depends on registry type) match $oci_config.type { "zot" => { start-zot-registry $oci_config } "distribution" => { start-distribution-registry $oci_config } _ => { log-error $"Unsupported registry type for local deployment: ($oci_config.type)" } } } def stop-oci-registry [config: record] -> nothing { log-info "Stopping OCI registry..." # Implementation depends on how registry was started # Could use PID file, systemd, or Docker } def oci-registry-status [config: record] -> nothing { let oci_config = $config.services.oci-registry let status_check = (do { http get $"http://($oci_config.endpoint):($oci_config.port)/v2/" } | complete) if $status_check.exit_code == 0 { print "OCI Registry: Running" print $" Endpoint: ($oci_config.endpoint):($oci_config.port)" print $" Type: ($oci_config.type)" } else { print "OCI Registry: Not running" } } def oci-registry-logs [config: record] -> nothing { let log_file = $config.services.oci-registry.log_file? | default ( $env.HOME | path join ".provisioning" "oci-registry" "registry.log" ) if ($log_file | path exists) { open $log_file } else { log-warning $"Log file not found: ($log_file)" } } def start-zot-registry [config: record] -> nothing { log-info "Starting Zot OCI registry..." # Implementation would start Zot binary with config } def start-distribution-registry [config: record] -> nothing { log-info "Starting Docker Distribution registry..." # Implementation would start distribution binary }