101 lines
4.2 KiB
Text
101 lines
4.2 KiB
Text
#!/usr/bin/env nu
|
|
|
|
# Provider State Management - Shared state change logic across providers
|
|
#
|
|
# This module consolidates all state transition logic:
|
|
# - Server state changes (start/stop/restart)
|
|
# - Storage state changes (online/available)
|
|
# - Generic resource state polling
|
|
|
|
# Generic state change polling loop
|
|
def provider_state_loop [resource_name: string, get_status_fn: closure, target_state: string, timeout_secs: int, poll_interval_secs: int, elapsed_secs: int = 0] {
|
|
let current_status = (do { $get_status_fn } | complete)
|
|
if $current_status.exit_code != 0 {
|
|
print $"Error getting status for ($resource_name)"
|
|
return false
|
|
}
|
|
let status = $current_status.stdout
|
|
if ($status | str contains $target_state) {
|
|
return true
|
|
} else if $timeout_secs > 0 and $elapsed_secs > $timeout_secs {
|
|
print $"\n🛑 Timeout waiting for ($resource_name) to reach ($target_state)"
|
|
return false
|
|
} else {
|
|
if (is-debug-enabled) {
|
|
print $" Waiting for ($resource_name) -> ($status)"
|
|
} else {
|
|
print -n "."
|
|
}
|
|
sleep ($"($poll_interval_secs)sec" | into duration)
|
|
provider_state_loop $resource_name $get_status_fn $target_state $timeout_secs $poll_interval_secs ($elapsed_secs + $poll_interval_secs)
|
|
}
|
|
}
|
|
|
|
# Wait for server state change with provider-specific status function
|
|
export def provider_wait_server_state [server: record, target_state: string, get_status_fn: closure, context: string = "server"] {
|
|
let timeout = if $server.running_timeout? != null { $server.running_timeout } else { 60 }
|
|
let wait_interval = if $server.running_wait? != null { $server.running_wait } else { 10 }
|
|
print $"Waiting for ($context) to reach ($target_state)..."
|
|
provider_state_loop $context $get_status_fn $target_state $timeout $wait_interval 0
|
|
}
|
|
|
|
# Wait for storage/volume state change
|
|
export def provider_wait_storage_state [server: record, storage_id: string, target_state: string, get_status_fn: closure, context: string = "storage"] {
|
|
let timeout = if $server.storage_timeout? != null { $server.storage_timeout } else { 300 }
|
|
let wait_interval = if $server.storage_wait? != null { $server.storage_wait } else { 5 }
|
|
print $"Waiting for storage ($storage_id) to reach ($target_state)..."
|
|
provider_state_loop $context $get_status_fn $target_state $timeout $wait_interval 0
|
|
}
|
|
|
|
# Check if state is already in target state
|
|
export def provider_state_is_current [current_state: string, target_state: string] {
|
|
($current_state | str contains $target_state)
|
|
}
|
|
|
|
# Validate state transition is allowed
|
|
export def provider_state_transition_valid [current_state: string, target_state: string, allowed_transitions: record = {}] {
|
|
if ($allowed_transitions | is-empty) { return true }
|
|
if not ($allowed_transitions | has $current_state) { return true }
|
|
let valid_targets = ($allowed_transitions | get $current_state)
|
|
$target_state in $valid_targets
|
|
}
|
|
|
|
# Map provider-specific state names to standard names
|
|
export def provider_normalize_state [state: string, state_mapping: record = {}] {
|
|
if ($state_mapping | is-empty) { return $state }
|
|
if ($state_mapping | has $state) {
|
|
$state_mapping | get $state
|
|
} else {
|
|
$state
|
|
}
|
|
}
|
|
|
|
# Extract numeric progress from state polling
|
|
export def provider_extract_progress [state_response: record, progress_path: string = ""] {
|
|
if ($progress_path | is-empty) { -1 } else {
|
|
try {
|
|
let parts = ($progress_path | split row ".")
|
|
let result = ($parts | reduce -f $state_response {|part, obj| $obj | get $part })
|
|
if ($result | describe) == "int" { $result } else { -1 }
|
|
} catch {
|
|
-1
|
|
}
|
|
}
|
|
}
|
|
|
|
# Format elapsed time for display
|
|
export def provider_format_elapsed [seconds: int] {
|
|
let minutes = ($seconds / 60)
|
|
let secs = ($seconds mod 60)
|
|
if $minutes > 0 { $"($minutes)m $($secs)s" } else { $"($secs)s" }
|
|
}
|
|
|
|
# Server state transition rules
|
|
export def provider_server_transitions [] {
|
|
{running: ["stopped"], stopped: ["running"], terminated: []}
|
|
}
|
|
|
|
# Storage state transition rules
|
|
export def provider_storage_transitions [] {
|
|
{available: ["in-use"], "in-use": ["available"], creating: ["available"], deleting: ["deleted"]}
|
|
}
|