provisioning-core/nulib/domain/diagnostics/system_status.nu

309 lines
9.6 KiB
Text
Raw Normal View History

# System Status Module
# Provides comprehensive system status checks for provisioning platform
use std log
# Selective imports (ADR-025 Phase 3 Layer 2).
# plugins/mod.nu star-import was dead — dropped.
use platform/config/accessor/core.nu [config-get]
use platform/user/config.nu [load-user-config]
# Check Nushell version meets requirements
def check-nushell-version [] {
let current = (version).version
let required = "0.107.1"
let meets_requirement = (($current | str contains $required) or ($current >= $required))
{
component: "Nushell"
status: (if $meets_requirement { "✅" } else { "❌" })
version: $current
required: $required
message: (if $meets_requirement {
"Version OK"
} else {
$"Update required: ($required)+"
})
docs: "https://nushell.sh"
}
}
# Check if Nickel is installed
def check-nickel-installed [] {
let nickel_bin = (which nickel | get path.0? | default "")
let installed = ($nickel_bin | is-not-empty)
let version_info = if $installed {
let result = (do { ^nickel --version } | complete)
if $result.exit_code == 0 {
let version_full = ($result.stdout | str trim)
# Extract version number and revision: "X.Y.Z (rev ...)"
let version_short = ($version_full | str replace 'nickel-lang-cli nickel ' '')
$version_short
} else {
"unknown"
}
} else {
"not installed"
}
{
component: "Nickel CLI"
status: (if $installed { "✅" } else { "❌" })
version: $version_info
required: "0.11.2+"
message: (if $installed {
"Installed and available"
} else {
"Not found in PATH"
})
docs: "https://nickel-lang.io/docs/user_docs/getting-started/install"
}
}
# Check required Nushell plugins
def check-plugins [] {
let required_plugins = [
{
name: "nickel"
description: "Nickel integration"
optional: true
docs: "docs/user/PLUGIN_INTEGRATION_GUIDE.md"
}
{
name: "tera"
description: "Template rendering"
optional: false
docs: "docs/user/PLUGIN_INTEGRATION_GUIDE.md"
}
{
name: "auth"
description: "Authentication"
optional: true
docs: "docs/user/AUTHENTICATION_LAYER_GUIDE.md"
}
{
name: "kms"
description: "Key management"
optional: true
docs: "docs/user/RUSTYVAULT_KMS_GUIDE.md"
}
{
name: "orchestrator"
description: "Orchestrator integration"
optional: true
docs: ".claude/features/orchestrator-architecture.md"
}
]
# Get list of registered plugins - use plugin list which returns structured data
let installed = (plugin list)
$required_plugins | each {|plugin|
let is_installed = ($installed | any {|p| $p.name == $plugin.name})
{
component: $plugin.name
status: (if $is_installed {
"✅"
} else if $plugin.optional {
"⚠️"
} else {
"❌"
})
version: (if $is_installed { "registered" } else { "not registered" })
required: (if $plugin.optional { "optional" } else { "required" })
message: (if $is_installed {
$plugin.description
} else if $plugin.optional {
$"($plugin.description) - optional, HTTP fallback available"
} else {
$"($plugin.description) - required for functionality"
})
docs: $plugin.docs
}
}
}
# Check active workspace configuration
def check-workspace [] {
let user_config = (load-user-config)
let active = ($user_config.active_workspace? | default null)
if $active == null {
return {
component: "Active Workspace"
status: "❌"
version: "none"
required: "configured"
message: "No active workspace set"
docs: "docs/user/WORKSPACE_SWITCHING_GUIDE.md"
}
}
let workspace = ($user_config.workspaces | where name == $active | first)
let ws_path = ($workspace.path? | default "")
let ws_exists = ($ws_path | path exists)
{
component: "Active Workspace"
status: (if $ws_exists { "✅" } else { "❌" })
version: $active
required: "configured"
message: (if $ws_exists {
$"Using ($active) at ($ws_path)"
} else {
$"Workspace ($active) path not found"
})
docs: "docs/user/WORKSPACE_SWITCHING_GUIDE.md"
}
}
# Check available providers
def check-providers [] {
let providers_path = config-get "paths.providers" "provisioning/catalog/providers"
let available_providers = if ($providers_path | path exists) {
ls $providers_path
| where type == dir
| where { |item|
let provider_name = ($item.name | path basename)
let bin_install_sh = ($providers_path | path join $provider_name | path join "bin" | path join "install.sh" | path exists)
let bin_install_nu = ($providers_path | path join $provider_name | path join "bin" | path join "install.nu" | path exists)
$bin_install_sh or $bin_install_nu
}
| get name
| path basename
| str join ", "
} else {
"none"
}
let has_providers = ($available_providers != "none")
{
component: "Cloud Providers"
status: (if $has_providers { "✅" } else { "⚠️" })
version: $available_providers
required: "at least one"
message: (if $has_providers {
$"Available: ($available_providers)"
} else {
"No providers found"
})
docs: "docs/architecture/multi-repo-strategy.md"
}
}
# Check orchestrator service
def check-orchestrator [] {
let orchestrator_url = (config-get "platform.orchestrator.url" "http://localhost:9011")
# Try to ping orchestrator health endpoint (handle connection errors gracefully)
let result = (do { ^curl -s -f $"($orchestrator_url)/health" --max-time 2 } | complete)
let is_running = ($result.exit_code == 0)
{
component: "Orchestrator Service"
status: (if $is_running { "✅" } else { "⚠️" })
version: (if $is_running { $"running on ($orchestrator_url)" } else { "not running" })
required: "recommended"
message: (if $is_running {
"Service healthy and responding"
} else {
"Optional service not running. Review startup options"
})
docs: ".claude/features/orchestrator-architecture.md"
}
}
# Check platform services
def check-platform-services [] {
let services = [
{
name: "Control Center"
port: (config-get "control_center.port" 8080)
optional: true
docs: "provisioning/platform/control-center/README.md"
}
{
name: "MCP Server"
port: (config-get "mcp_server.port" 3000)
optional: true
docs: "provisioning/platform/mcp-server/README.md"
}
{
name: "API Gateway"
port: (config-get "api_gateway.port" 8090)
optional: true
docs: "provisioning/platform/api-gateway/README.md"
}
]
$services | each {|svc|
let result = (do { ^curl -s -f $"http://localhost:($svc.port)/health" --max-time 1 } | complete)
let is_running = ($result.exit_code == 0)
{
component: $svc.name
status: (if $is_running { "✅" } else { "⚠️" })
version: (if $is_running { $"running on :($svc.port)" } else { "not running" })
required: "optional"
message: (if $is_running {
"Service available"
} else {
"Service not running - optional"
})
docs: $svc.docs
}
}
}
# Collect all status checks
# Refactored to use immutable pattern per Rule 3 (Nushell 0.110.0 compatibility)
def get-all-checks [] {
# Concatenate all check results immutably
[
(check-nushell-version)
(check-nickel-installed)
(check-plugins)
(check-workspace)
(check-providers)
(check-orchestrator)
(check-platform-services)
] | flatten
}
# Main system status command
# Comprehensive system status check showing all component states
export def "provisioning status" [] {
print $"(ansi cyan_bold)Provisioning Platform Status(ansi reset)\n"
let all_checks = (get-all-checks)
let results = ($all_checks | select component status version message)
print ($results | table)
}
# Get status summary (machine-readable)
export def "provisioning status-json" [] {
let all_checks = (get-all-checks)
let total = ($all_checks | length)
let passing = ($all_checks | where status == "✅" | length)
let warnings = ($all_checks | where status == "⚠️" | length)
let failing = ($all_checks | where status == "❌" | length)
{
timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
summary: {
total: $total
passing: $passing
warnings: $warnings
failing: $failing
health_percentage: (($passing / $total) * 100 | math round)
}
checks: $all_checks
ready_for_deployment: ($failing == 0)
}
}