413 lines
12 KiB
Plaintext
Raw Normal View History

2025-10-07 10:32:04 +01:00
#!/usr/bin/env nu
# Pre-flight Check System
# Validates service prerequisites before operations
use manager.nu [load-service-registry is-service-running get-service-definition]
use dependencies.nu [resolve-dependencies get-startup-order]
# Check required services for operation
export def check-required-services [
operation: string
]: nothing -> record {
2025-10-07 10:32:04 +01:00
let registry = (load-service-registry)
# Find all services required for this operation
let required_services = (
$registry
| transpose name config
| where { |row|
$operation in $row.config.required_for
}
| get name
)
if ($required_services | is-empty) {
return {
operation: $operation
required_services: []
all_running: true
missing_services: []
can_auto_start: true
message: "No services required for this operation"
}
}
# Check which services are running
def partition-services [services: list, running: list, missing: list]: nothing -> record {
if ($services | is-empty) {
return { running: $running, missing: $missing }
}
let first = ($services | get 0)
let rest = ($services | skip 1)
if (is-service-running $first) {
partition-services $rest ($running | append $first) $missing
2025-10-07 10:32:04 +01:00
} else {
partition-services $rest $running ($missing | append $first)
2025-10-07 10:32:04 +01:00
}
}
let service_status = (partition-services $required_services [] [])
let running = $service_status.running
let missing = $service_status.missing
2025-10-07 10:32:04 +01:00
# Check if missing services can be auto-started
let can_auto_start = (
$missing
| all { |service|
let service_def = (get-service-definition $service)
$service_def.startup.auto_start
}
)
{
operation: $operation
required_services: $required_services
running_services: $running
missing_services: $missing
all_running: ($missing | is-empty)
can_auto_start: $can_auto_start
message: (if ($missing | is-empty) {
"All required services are running"
} else if $can_auto_start {
$"Services need to be started: ($missing | str join ', ')"
} else {
$"Required services not running and cannot auto-start: ($missing | str join ', ')"
})
}
}
# Validate service prerequisites
export def validate-service-prerequisites [
service_name: string
]: nothing -> record {
2025-10-07 10:32:04 +01:00
let service_def = (get-service-definition $service_name)
# Check deployment mode requirements
let issues = (
if $service_def.deployment.mode == "docker" or $service_def.deployment.mode == "docker-compose" {
2025-10-07 10:32:04 +01:00
# Check if Docker is available
let docker_check = (do {
docker --version
} | complete)
if $docker_check.exit_code != 0 {
["Docker is not installed or not running"]
} else {
[]
2025-10-07 10:32:04 +01:00
}
} else if $service_def.deployment.mode == "kubernetes" {
2025-10-07 10:32:04 +01:00
# Check if kubectl is available
let kubectl_check = (do {
kubectl version --client
} | complete)
if $kubectl_check.exit_code != 0 {
["kubectl is not installed"]
} else {
[]
2025-10-07 10:32:04 +01:00
}
} else if $service_def.deployment.mode == "binary" {
2025-10-07 10:32:04 +01:00
# Check if binary exists
let binary_path = ($service_def.deployment.binary.binary_path | str replace -a '${HOME}' $env.HOME)
if not ($binary_path | path exists) {
[$"Binary not found: ($binary_path)"]
} else {
[]
2025-10-07 10:32:04 +01:00
}
} else {
[]
2025-10-07 10:32:04 +01:00
}
)
2025-10-07 10:32:04 +01:00
# Check dependencies
def check-deps [deps: list, warnings: list]: nothing -> list {
if ($deps | is-empty) {
return $warnings
}
let first = ($deps | get 0)
let rest = ($deps | skip 1)
let new_warnings = if (is-service-running $first) {
$warnings
} else {
$warnings | append $"Dependency '($first)' is not running"
2025-10-07 10:32:04 +01:00
}
check-deps $rest $new_warnings
2025-10-07 10:32:04 +01:00
}
let warnings = (check-deps $service_def.dependencies [])
2025-10-07 10:32:04 +01:00
# Check conflicts
def check-conflicts [conflicts: list, issues: list]: nothing -> list {
if ($conflicts | is-empty) {
return $issues
2025-10-07 10:32:04 +01:00
}
let first = ($conflicts | get 0)
let rest = ($conflicts | skip 1)
let new_issues = if (is-service-running $first) {
$issues | append $"Conflicting service '($first)' is running"
} else {
$issues
}
check-conflicts $rest $new_issues
2025-10-07 10:32:04 +01:00
}
let all_issues = (check-conflicts $service_def.conflicts $issues)
2025-10-07 10:32:04 +01:00
{
service: $service_name
valid: ($all_issues | is-empty)
issues: $all_issues
2025-10-07 10:32:04 +01:00
warnings: $warnings
can_start: ($all_issues | is-empty)
message: (if ($all_issues | is-empty) {
2025-10-07 10:32:04 +01:00
"All prerequisites met"
} else {
$"Prerequisites not met: ($all_issues | str join '; ')"
2025-10-07 10:32:04 +01:00
})
}
}
# Auto-start required services
export def auto-start-required-services [
operation: string
]: nothing -> record {
2025-10-07 10:32:04 +01:00
let check = (check-required-services $operation)
if $check.all_running {
return {
success: true
message: "All required services already running"
started_services: []
}
}
if not $check.can_auto_start {
return {
success: false
message: "Some required services cannot be auto-started"
failed_services: $check.missing_services
}
}
# Get startup order for missing services
let startup_order = (get-startup-order $check.missing_services)
print $"Starting required services in order: ($startup_order | str join ' -> ')"
# Helper to start services in sequence
def start-services-seq [services: list, started: list, failed: list]: nothing -> record {
if ($services | is-empty) {
return { started: $started, failed: $failed }
}
let first = ($services | get 0)
let rest = ($services | skip 1)
2025-10-07 10:32:04 +01:00
print $"Starting [$first]..."
2025-10-07 10:32:04 +01:00
use manager.nu start-service
let start_result = (do {
start-service $first
} | complete)
2025-10-07 10:32:04 +01:00
if $start_result.exit_code == 0 and ($start_result.stdout == "true" or $start_result.stdout == true) {
print $"✅ [$first] started successfully"
start-services-seq $rest ($started | append $first) $failed
} else {
print $"❌ Failed to start [$first]: ($start_result.stderr)"
{ started: $started, failed: ($failed | append $first) }
2025-10-07 10:32:04 +01:00
}
}
let result = (start-services-seq $startup_order [] [])
let started = $result.started
let failed = $result.failed
2025-10-07 10:32:04 +01:00
{
success: ($failed | is-empty)
message: (if ($failed | is-empty) {
$"Successfully started ($started | length) services"
} else {
$"Failed to start some services: ($failed | str join ', ')"
})
started_services: $started
failed_services: $failed
}
}
# Check service conflicts
export def check-service-conflicts [
service_name: string
]: nothing -> record {
2025-10-07 10:32:04 +01:00
let service_def = (get-service-definition $service_name)
# Helper to check conflicts
def find-conflicts [conflicts: list, result: list]: nothing -> list {
if ($conflicts | is-empty) {
return $result
}
let first = ($conflicts | get 0)
let rest = ($conflicts | skip 1)
let new_result = if (is-service-running $first) {
$result | append {
service: $first
2025-10-07 10:32:04 +01:00
status: "running"
action_required: "stop"
}
} else {
$result
2025-10-07 10:32:04 +01:00
}
find-conflicts $rest $new_result
2025-10-07 10:32:04 +01:00
}
let conflicts = (find-conflicts $service_def.conflicts [])
2025-10-07 10:32:04 +01:00
{
service: $service_name
has_conflicts: (not ($conflicts | is-empty))
conflicts: $conflicts
can_start: ($conflicts | is-empty)
message: (if ($conflicts | is-empty) {
"No conflicting services running"
} else {
$"Conflicting services running: (($conflicts | get service) | str join ', ')"
})
}
}
# Validate all services
export def validate-all-services []: nothing -> record {
2025-10-07 10:32:04 +01:00
let registry = (load-service-registry)
let validation_results = (
$registry
| transpose name config
| each { |row|
validate-service-prerequisites $row.name
}
)
let total = ($validation_results | length)
let valid = ($validation_results | where valid | length)
let invalid = ($validation_results | where { |r| not $r.valid } | length)
{
total_services: $total
valid_services: $valid
invalid_services: $invalid
all_valid: ($invalid == 0)
results: $validation_results
message: $"($valid)/($total) services have valid configurations"
}
}
# Pre-flight check for service start
export def preflight-start-service [
service_name: string
]: nothing -> record {
2025-10-07 10:32:04 +01:00
print $"Running pre-flight checks for ($service_name)..."
# 1. Validate prerequisites
let prereq_check = (validate-service-prerequisites $service_name)
if not $prereq_check.valid {
return {
can_start: false
reason: "prerequisites"
details: $prereq_check
}
}
# 2. Check conflicts
let conflict_check = (check-service-conflicts $service_name)
if $conflict_check.has_conflicts {
return {
can_start: false
reason: "conflicts"
details: $conflict_check
}
}
# 3. Check dependencies
let service_def = (get-service-definition $service_name)
# Helper to collect missing dependencies
def collect-missing-deps [deps: list, missing: list]: nothing -> list {
if ($deps | is-empty) {
return $missing
}
let first = ($deps | get 0)
let rest = ($deps | skip 1)
let new_missing = if (is-service-running $first) {
$missing
} else {
let dep_def = (get-service-definition $first)
$missing | append {
service: $first
2025-10-07 10:32:04 +01:00
auto_start: $dep_def.startup.auto_start
}
2025-10-07 10:32:04 +01:00
}
collect-missing-deps $rest $new_missing
2025-10-07 10:32:04 +01:00
}
let missing_deps = (collect-missing-deps $service_def.dependencies [])
2025-10-07 10:32:04 +01:00
if not ($missing_deps | is-empty) {
let can_auto_start = ($missing_deps | all { |d| $d.auto_start })
return {
can_start: $can_auto_start
reason: "dependencies"
details: {
missing_dependencies: $missing_deps
can_auto_start: $can_auto_start
}
}
}
# All checks passed
{
can_start: true
reason: "all_checks_passed"
details: {
message: "Pre-flight checks passed"
}
}
}
# Get service readiness report
export def get-readiness-report []: nothing -> record {
2025-10-07 10:32:04 +01:00
let registry = (load-service-registry)
let services = (
$registry
| transpose name config
| each { |row|
let is_running = (is-service-running $row.name)
let prereq_check = (validate-service-prerequisites $row.name)
{
name: $row.name
type: $row.config.type
category: $row.config.category
running: $is_running
auto_start: $row.config.startup.auto_start
valid_config: $prereq_check.valid
can_start: $prereq_check.can_start
issues: ($prereq_check.issues | length)
}
}
)
let total = ($services | length)
let running = ($services | where running | length)
let ready = ($services | where can_start | length)
{
total_services: $total
running_services: $running
ready_to_start: $ready
services: $services
summary: $"($running) running, ($ready) ready to start out of ($total) total"
}
}