252 lines
9.8 KiB
Text
252 lines
9.8 KiB
Text
|
|
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<string> = [$"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 <url>"
|
||
|
|
|
||
|
|
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 <cmd> - 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"}
|
||
|
|
}
|