use platform/target.nu [get-deployment-service-config get-enabled-services list-required-platform-services] use platform/health.nu [check-service-health] use platform/service_manager.nu [get-external-services is-port-listening] def check-external-services-internal [external_config: record]: nothing -> list { let db = ($external_config.database? | default {backend: "filesystem"}) let oci_registries = ($external_config.oci_registries? | default []) let git_sources = ($external_config.git_sources? | default []) mut results = [] if ($db.backend? | default "filesystem") == "filesystem" { let path = ($db.path? | default "~/.provisioning/data") let expanded_path = if ($path | str starts-with "~") { $"($env.HOME)/($path | str substring 1..)" } else { $path } if ($expanded_path | path exists) { $results = ($results | append { service: "database", backend: $db.backend, status: "✓" message: $"Filesystem storage available at ($expanded_path)" }) } else { $results = ($results | append { service: "database", backend: $db.backend, status: "✗" message: $"Path does not exist: ($expanded_path)" }) } } $results } export def check-service-available [ service_url: string service_name: string ]: nothing -> record { let parsed = ($service_url | parse "http://{host}:{port}") let port = if ($parsed | is-empty) { "unknown" } else { ($parsed | get port.0) } let port_check = (do {^lsof -i :($port) -P -n | ^grep LISTEN} | complete) let is_listening = ($port_check.exit_code == 0) if $is_listening { {available: true, port: $port, message: $"($service_name) is available on port ($port)"} } else { {available: false, port: $port, message: $"($service_name) is not available on port ($port)"} } } export def check-external-services-status []: nothing -> list { let external_services = (get-external-services) if ($external_services | is-empty) { return [] } $external_services | each {|svc| let healthy = (is-port-listening $svc.port) { name: $svc.name, srvc: ($svc.srvc? | default "external"), url: $svc.url, port: $svc.port, required: ($svc.required? | default false), healthy: $healthy, dependencies: ($svc.dependencies? | default []) } } } # Transactional pre-operation health gate. # Checks all enabled platform services and required external services. # Fails hard (error make) if any required service is down — stops the operation before it starts. # context: human-readable label for the error message (e.g. "component docker-mailserver update") export def health-gate [context: string] { let ext = (check-external-services-status) # Gate only on platform services marked required=true in deployment config. # Optional services (control-center, catalog-registry, etc.) don't block operations. let failed_plat = ( list-required-platform-services | where {|svc| not (try { check-service-health $svc.name } catch { false }) } | each {|svc| {name: $svc.name} } ) let failed_ext = ($ext | where {|s| $s.required and (not $s.healthy) }) if ($failed_ext | is-empty) and ($failed_plat | is-empty) { return } mut lines: list = [$"Operation blocked — required services are down: ($context)", ""] if ($failed_plat | is-not-empty) { $lines = ($lines | append " Platform services:") for s in $failed_plat { $lines = ($lines | append $" - ($s.name)") } $lines = ($lines | append "") } if ($failed_ext | is-not-empty) { $lines = ($lines | append " External services (required=true):") for s in $failed_ext { $lines = ($lines | append $" - ($s.name) ($s.url)") } $lines = ($lines | append "") } $lines = ($lines | append " Check: prvng plat st && prvng plat ext") $lines = ($lines | append " Start: prvng plat start") error make --unspanned {msg: ($lines | str join "\n")} } export def check-platform-services-status []: nothing -> list { let services = (get-enabled-services) $services | each {|svc| let healthy = (check-service-health $svc.name) {name: $svc.name, status: (if $healthy {"healthy"} else {"unhealthy"}), priority: $svc.priority} } } export def show-cascade-failure-report [failed_service: string]: nothing -> nothing { print "" print $"❌ ($failed_service) is not running." print "" print "Start all platform services:" print " provisioning platform start" print " prvng plat start # short alias" print "" print "Check service status:" print " provisioning platform status" print " prvng plat st # short alias" print "" } export def verify-service-or-fail [ service_url: string service_name: string --check-command: string = "" --check-alias: string = "" --start-command: string = "" --start-alias: string = "" ]: nothing -> record { let check_result = (check-service-available $service_url $service_name) if not $check_result.available { print $"❌ ($service_name) not available at ($service_url)" print "" print $"Connection refused - ($service_name) is not running on port ($check_result.port)." print "" show-cascade-failure-report $service_name if ($check_command | is-not-empty) { print "To check service status:" print $" ($check_command)" if ($check_alias | is-not-empty) { print $" ($check_alias) # short alias" } print "" } if ($start_command | is-not-empty) { print "To start service:" print $" ($start_command)" if ($start_alias | is-not-empty) { print $" ($start_alias) # short alias" } print "" } print $"Current endpoint: ($service_url)" print "If using a custom endpoint, verify it with: --orchestrator " return {status: "error", message: $"($service_name) not available"} } {status: "ok", message: $"($service_name) is available"} } export def is-service-available [service_url: string, service_name: string]: nothing -> bool { (check-service-available $service_url $service_name).available } export def check-daemon-availability []: nothing -> record { let daemon_config = (get-deployment-service-config "provisioning_daemon") let daemon_port = ($daemon_config.server?.port? | default 9014) let port_check = (do {^lsof -i :($daemon_port) -P -n | ^grep LISTEN} | complete) {available: ($port_check.exit_code == 0), port: $daemon_port} } export def check-nu-daemon-availability []: nothing -> record { let daemon_config = (get-deployment-service-config "nu_daemon") let daemon_port = ($daemon_config.server?.port? | default 9095) let port_check = (do {^lsof -i :($daemon_port) -P -n | ^grep LISTEN} | complete) {available: ($port_check.exit_code == 0), port: $daemon_port} } export def verify-daemon-or-block [operation: string]: nothing -> record { let daemon_check = (check-daemon-availability) if not $daemon_check.available { print "" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "❌ CRITICAL: provisioning_daemon not available" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "" print $"The provisioning daemon is required for operation: ($operation)" print $"Daemon is not listening on port ($daemon_check.port)" print "" print "The daemon is a CRITICAL component - all operations require it." print "" print "To check daemon status:" print " provisioning platform status" print " prvng plat st # short alias" print "" print "To start the daemon:" print " provisioning platform start provisioning_daemon" print " prvng plat start provisioning_daemon # short alias" print "" print "Allowed operations without daemon:" print " • help / -h / --help - View help" print " • platform - Manage platform services" print " • setup - Initial setup" print "" return {status: "error", message: "provisioning_daemon not available"} } {status: "ok", message: "provisioning_daemon is available"} } export def verify-nu-daemon-or-block [operation: string]: nothing -> record { let daemon_check = (check-nu-daemon-availability) if not $daemon_check.available { print "" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "❌ CRITICAL: nu_daemon not available" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "" print $"The nu_daemon is required for operation: ($operation)" print $"nu_daemon is not listening on port ($daemon_check.port)" print "" print "To check status:" print " prvng plat st" print "" print "To start:" print " prvng plat start nu_daemon" print "" return {status: "error", message: "nu_daemon not available"} } {status: "ok", message: "nu_daemon is available"} }