Update core components including CLI, Nushell libraries, plugins system, and utility scripts for the provisioning system. CLI Updates: - Command implementations - CLI utilities and dispatching - Help system improvements - Command validation Library Updates: - Configuration management system - Infrastructure validation - Extension system improvements - Secrets management - Workspace operations - Cache management system Plugin System: - Interactive form plugin (inquire) - KCL integration plugin - Performance optimization plugins - Plugin registration system Utilities: - Build and distribution scripts - Installation procedures - Testing utilities - Development tools Documentation: - Library module documentation - Extension API guides - Plugin usage guides - Service management documentation All changes are backward compatible. No breaking changes.
423 lines
18 KiB
Plaintext
423 lines
18 KiB
Plaintext
use lib_provisioning *
|
|
use ../lib_provisioning/user/config.nu [get-active-workspace get-workspace-path]
|
|
# Removed broken imports - these modules don't exist
|
|
# use create.nu *
|
|
# use servers/delete.nu *
|
|
# use handlers.nu *
|
|
#use ../lib_provisioning/utils ssh_cmd
|
|
|
|
# Main CLI handler for infra commands
|
|
export def "main list" [
|
|
--infra (-i): string = "" # Infrastructure (ignored for list, kept for compatibility)
|
|
--notitles # Suppress title output
|
|
] {
|
|
# Get active workspace name
|
|
let active_workspace = (get-active-workspace)
|
|
|
|
if ($active_workspace | is-empty) {
|
|
_print "🛑 No active workspace"
|
|
_print " Run: provisioning workspace list"
|
|
_print " Then: provisioning workspace activate <name>"
|
|
return
|
|
}
|
|
|
|
# Get workspace path from the active workspace
|
|
let ws_path = (get-workspace-path $active_workspace)
|
|
|
|
if ($ws_path | is-empty) {
|
|
_print $"🛑 Cannot find workspace path for '$active_workspace'"
|
|
return
|
|
}
|
|
|
|
let infra_dir = ($ws_path | path join "infra")
|
|
let current_infra = (config-get "infra.current" "")
|
|
|
|
# List all infrastructures in the workspace infra directory
|
|
if ($infra_dir | path exists) {
|
|
# List directory contents, filter for directories that:
|
|
# 1. Do not start with underscore (not hidden/system)
|
|
# 2. Are directories
|
|
# 3. Contain a settings.k file (marks it as a real infra)
|
|
let infras = (ls -s $infra_dir | where {|it|
|
|
((($it.name | str starts-with "_") == false) and ($it.type == "dir") and (($infra_dir | path join $it.name "settings.k") | path exists))
|
|
} | each {|it| $it.name} | sort)
|
|
|
|
if ($infras | length) > 0 {
|
|
_print $"(_ansi cyan_bold)Infrastructures in workspace:(_ansi reset)\n"
|
|
for infra_name in $infras {
|
|
let is_current = if ($infra_name == $current_infra) {
|
|
$"(_ansi green_bold)●(_ansi reset) "
|
|
} else {
|
|
" "
|
|
}
|
|
_print $"($is_current)(_ansi blue)($infra_name)(_ansi reset)"
|
|
}
|
|
} else {
|
|
_print "No infrastructures found in workspace '$active_workspace'"
|
|
}
|
|
} else {
|
|
_print $"🛑 Infra directory not found: ($infra_dir)"
|
|
}
|
|
}
|
|
|
|
# Validate and display detailed infrastructure configuration
|
|
export def "main validate" [
|
|
infra_name?: string # Infrastructure name (optional, uses current or detects from args)
|
|
--infra (-i): string = "" # Infrastructure name (alternate flag format)
|
|
--check (-c) # Check mode (accepted but not used for validate)
|
|
--onsel: string = "" # On selection (accepted but not used for validate)
|
|
--yes (-y) # Auto-confirm (accepted but not used for validate)
|
|
--notitles # Suppress title output
|
|
] {
|
|
# Get active workspace name
|
|
let active_workspace = (get-active-workspace)
|
|
|
|
if ($active_workspace | is-empty) {
|
|
_print "🛑 No active workspace"
|
|
_print " Run: provisioning workspace list"
|
|
_print " Then: provisioning workspace activate <name>"
|
|
return
|
|
}
|
|
|
|
# Get workspace path from the active workspace
|
|
let ws_path = (get-workspace-path $active_workspace)
|
|
|
|
if ($ws_path | is-empty) {
|
|
_print $"🛑 Cannot find workspace path for '$active_workspace'"
|
|
return
|
|
}
|
|
|
|
let infra_dir = ($ws_path | path join "infra")
|
|
|
|
# Determine which infrastructure to validate
|
|
let target_infra = if ($infra_name | is-not-empty) {
|
|
$infra_name
|
|
} else if ($infra | is-not-empty) {
|
|
$infra
|
|
} else {
|
|
# Try to detect from config
|
|
(config-get "infra.current" "")
|
|
}
|
|
|
|
if ($target_infra | is-empty) {
|
|
_print "❌ No infrastructure specified"
|
|
_print ""
|
|
_print "Usage: provisioning infra validate [<infrastructure_name>]"
|
|
_print ""
|
|
_print "Available infrastructures:"
|
|
|
|
# List available infras
|
|
if ($infra_dir | path exists) {
|
|
let infras = (ls -s $infra_dir | where {|it|
|
|
((($it.name | str starts-with "_") == false) and ($it.type == "dir") and (($infra_dir | path join $it.name "settings.k") | path exists))
|
|
} | each {|it| $it.name} | sort)
|
|
|
|
for infra in $infras {
|
|
_print $" • (_ansi blue)($infra)(_ansi reset)"
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
let target_path = ($infra_dir | path join $target_infra)
|
|
|
|
if not ($target_path | path exists) {
|
|
_print $"❌ Infrastructure not found: (_ansi red)($target_infra)(_ansi reset)"
|
|
return
|
|
}
|
|
|
|
# Load infrastructure configuration files
|
|
let settings_file = ($target_path | path join "settings.k")
|
|
let servers_file = ($target_path | path join "defs" "servers.k")
|
|
|
|
if not ($settings_file | path exists) {
|
|
_print $"❌ Settings file not found: ($settings_file)"
|
|
return
|
|
}
|
|
|
|
# Display infrastructure header
|
|
_print ""
|
|
_print $"(ansi cyan_bold)════════════════════════════════════════════════════════════════════════════(ansi reset)"
|
|
_print $"(ansi cyan_bold) ($target_infra | str upcase) INFRASTRUCTURE CONFIGURATION (ansi reset)"
|
|
_print $"(ansi cyan_bold)════════════════════════════════════════════════════════════════════════════(ansi reset)"
|
|
_print ""
|
|
|
|
# Parse and display servers if the file exists
|
|
if ($servers_file | path exists) {
|
|
let servers_content = (open -r $servers_file)
|
|
|
|
# Extract servers from the _servers array
|
|
# Split by "upcloud_prov.Server_upcloud {" to find server blocks
|
|
let server_blocks = ($servers_content | split row "upcloud_prov.Server_upcloud {" | skip 1)
|
|
|
|
if ($server_blocks | length) > 0 {
|
|
_print $"(ansi green_bold)Servers:(ansi reset)"
|
|
_print ""
|
|
|
|
for srv_idx in (0..($server_blocks | length)) {
|
|
if $srv_idx >= ($server_blocks | length) { break }
|
|
let block = ($server_blocks | get $srv_idx)
|
|
let server_count = ($srv_idx + 1)
|
|
|
|
# Extract hostname - look for: hostname = "..."
|
|
let hostname = if ($block | str contains "hostname =") {
|
|
let lines = ($block | split row "\n" | where { |l| (($l | str contains "hostname =") and not ($l | str starts-with "#")) })
|
|
if ($lines | length) > 0 {
|
|
let line = ($lines | first)
|
|
let match = ($line | split row "\"" | get 1? | default "")
|
|
$match
|
|
} else {
|
|
"N/A"
|
|
}
|
|
} else {
|
|
"N/A"
|
|
}
|
|
|
|
# Check if server is disabled - look for: not_use = True
|
|
let is_disabled = ($block | str contains "not_use = True")
|
|
let status = if $is_disabled { $"(ansi yellow)DISABLED(ansi reset)" } else { $"(ansi green)ACTIVE(ansi reset)" }
|
|
|
|
# Extract plan - look for: plan = "..." (not commented, prefer last one)
|
|
let plan = if ($block | str contains "plan =") {
|
|
let lines = ($block | split row "\n" | where { |l| (($l | str contains "plan =") and ($l | str contains "\"") and not ($l | str starts-with "#")) })
|
|
if ($lines | length) > 0 {
|
|
let line = ($lines | last)
|
|
($line | split row "\"" | get 1? | default "")
|
|
} else {
|
|
"N/A"
|
|
}
|
|
} else {
|
|
"N/A"
|
|
}
|
|
|
|
# Extract total storage - look for: total = ...
|
|
let storage = if ($block | str contains "total =") {
|
|
let lines = ($block | split row "\n" | where { |l| (($l | str contains "total =") and not ($l | str starts-with "#")) })
|
|
if ($lines | length) > 0 {
|
|
let line = ($lines | first)
|
|
let value = ($line | str trim | split row "=" | get 1? | str trim)
|
|
($value | str replace "," "" | str trim)
|
|
} else {
|
|
"N/A"
|
|
}
|
|
} else {
|
|
"N/A"
|
|
}
|
|
|
|
# Extract IP - look for: network_private_ip = "..."
|
|
let ip = if ($block | str contains "network_private_ip =") {
|
|
let lines = ($block | split row "\n" | where { |l| (($l | str contains "network_private_ip =") and not ($l | str starts-with "#")) })
|
|
if ($lines | length) > 0 {
|
|
let line = ($lines | first)
|
|
($line | split row "\"" | get 1? | default "")
|
|
} else {
|
|
"N/A"
|
|
}
|
|
} else {
|
|
"N/A"
|
|
}
|
|
|
|
# Extract taskservs - look for all lines with {name = "..."} within taskservs array
|
|
let taskservs_list = if ($block | str contains "taskservs = [") {
|
|
let taskservs_section = ($block | split row "taskservs = [" | get 1? | split row "]" | first | default "")
|
|
let lines = ($taskservs_section | split row "\n" | where { |l| (($l | str contains "name =") and not ($l | str starts-with "#")) })
|
|
let taskservs = ($lines | each { |l|
|
|
let parts = ($l | split row "name =")
|
|
let value_part = if ($parts | length) > 1 { ($parts | get 1) } else { "" }
|
|
let name = ($value_part | split row "\"" | get 1? | default "")
|
|
if ($name | is-not-empty) { $name } else { null }
|
|
} | where { |n| ($n != null) })
|
|
$taskservs
|
|
} else {
|
|
[]
|
|
}
|
|
|
|
_print $" ($server_count). (ansi cyan_bold)($hostname)(ansi reset) - ($status)"
|
|
_print $" Plan: (ansi blue)($plan)(ansi reset)"
|
|
_print $" Storage: (ansi blue)($storage)GB(ansi reset)"
|
|
_print $" IP: (ansi blue)($ip)(ansi reset)"
|
|
|
|
if ($taskservs_list | length) > 0 {
|
|
_print $" Taskservs: (ansi yellow)($taskservs_list | length)(ansi reset) installed"
|
|
for svc in $taskservs_list {
|
|
_print $" • ($svc)"
|
|
}
|
|
}
|
|
_print ""
|
|
}
|
|
}
|
|
}
|
|
|
|
# Display summary
|
|
_print $"(ansi cyan_bold)Summary:(ansi reset)"
|
|
_print $" Workspace: (ansi green)($active_workspace)(ansi reset)"
|
|
_print $" Infrastructure: (ansi green)($target_infra)(ansi reset)"
|
|
_print $" Path: (ansi yellow)($target_path)(ansi reset)"
|
|
_print ""
|
|
_print $"(ansi green)✓ Infrastructure configuration validated(ansi reset)"
|
|
_print ""
|
|
}
|
|
|
|
export def on_create_infras [
|
|
infras_list: list # infras list
|
|
check: bool # Only check mode no servers will be created
|
|
wait: bool # Wait for creation
|
|
outfile?: string # Out file for creation
|
|
hostname?: string # Server hostname in settings
|
|
serverpos?: int # Server position in settings
|
|
] {
|
|
let create_infra = {|infra|
|
|
if not ($env.PROVISIONING_INFRA_PATH | path join $infra.item | path exists) {
|
|
print $"\n🛑 Path not found for (_ansi red)($infra.item)(_ansi reset) in (_ansi cyan)($env.PROVISIONING_KLOUD_PATH)(_ansi reset)"
|
|
} else {
|
|
let settings = (find_get_settings --infra $infra.item)
|
|
on_infra $infra $settings $check $wait $outfile $hostname $serverpos
|
|
}
|
|
}
|
|
if $check {
|
|
$infras_list | enumerate | each { |infra| do $create_infra $infra }
|
|
} else {
|
|
$infras_list | enumerate | par-each { |infra| do $create_infra $infra }
|
|
}
|
|
}
|
|
export def on_infra [
|
|
infra: record
|
|
settings: record
|
|
check: bool
|
|
wait: bool
|
|
outfile?: string # Out file for creation
|
|
hostname?: string # Server hostname in settings
|
|
serverpos?: int # Server position in settings
|
|
] {
|
|
print "TODO on_infra"
|
|
print $infra
|
|
}
|
|
export def on_taskserv_infras [
|
|
infras_list: list # infras list
|
|
check: bool # Only check mode no servers will be created
|
|
name?: string
|
|
server?: string
|
|
--iptype: string = "public" # Ip type to connect
|
|
] {
|
|
let run_create = { |infra|
|
|
let curr_settings = (find_get_settings --infra $infra)
|
|
$env.WK_CNPROV = $curr_settings.wk_path
|
|
let match_task = if $name == null or $name == "" { "" } else { $name }
|
|
let match_server = if $server == null or $server == "" { "" } else { $server}
|
|
on_taskservs $curr_settings $match_task $match_server $iptype $check
|
|
}
|
|
$infras_list | enumerate | par-each { |infra|
|
|
let task = { do $run_create $infra.item }
|
|
let result = desktop_run_notify $"($env.PROVISIONING_NAME) ($infra.item) taskservs create" "-> " $task --timeout 11sec
|
|
}
|
|
}
|
|
export def on_delete_infras [
|
|
infras_list: list # infras list
|
|
keep_storage: bool # keepstorage
|
|
wait: bool # Wait for creation
|
|
name?: string # Server hostname in settings
|
|
serverpos?: int # Server position in settings
|
|
] {
|
|
let run_delete = { |infra, keepstorage|
|
|
let curr_settings = (find_get_settings --infra $infra)
|
|
on_delete_servers $curr_settings $keepstorage $wait $name $serverpos
|
|
}
|
|
$infras_list | enumerate | par-each { |infra|
|
|
let task = { do $run_delete $infra.item $keep_storage }
|
|
let result = desktop_run_notify $"($env.PROVISIONING_NAME) ($infra.item) servers delete" "-> " $task --timeout 11sec
|
|
}
|
|
}
|
|
export def on_generate_infras [
|
|
infras_list: list # infras list
|
|
check: bool # Only check mode
|
|
wait: bool # Wait for creation
|
|
outfile?: string # Out file for generation
|
|
name?: string # Server hostname in settings
|
|
serverpos?: int # Server position in settings
|
|
] {
|
|
print "TODO on_generate_infras"
|
|
# let curr_settings = (find_get_settings --infra $infra)
|
|
}
|
|
export def infras_walk_by [
|
|
infras_list: list
|
|
match_hostname: string
|
|
check: bool # Only check mode no servers will be created
|
|
return_no_exists: bool
|
|
] {
|
|
mut infra_servers = {}
|
|
mut total_month = 0
|
|
mut total_hour = 0
|
|
mut total_day = 0
|
|
mut table_items = []
|
|
let sum_color = { fg: '#0000ff' bg: '#dadada' attr: b }
|
|
let total_color = { fg: '#ffff00' bg: '#0000ff' attr: b }
|
|
print $"(_ansi purple_reverse) Cost ($infras_list | str join ' ')(_ansi reset) "
|
|
for infra in $infras_list {
|
|
if not ($env.PROVISIONING_INFRA_PATH | path join $infra | path exists) {
|
|
print $"\n🛑 Path not found for (_ansi red)($infra)(_ansi reset) in (_ansi cyan)($env.PROVISIONING_KLOUD_PATH)(_ansi reset)"
|
|
continue
|
|
}
|
|
let settings = (find_get_settings --infra $infra)
|
|
mut c_infra_servers = {}
|
|
mut c_total_month = 0
|
|
mut c_total_hour = 0
|
|
mut c_total_day = 0
|
|
for server in $settings.data.servers {
|
|
if $match_hostname != null and $match_hostname != "" and $server.hostname != $match_hostname {
|
|
continue
|
|
}
|
|
# Check if provider key exists in infra_servers
|
|
if not (($infra_servers | columns) | any { |col| $col == $server.provider }) {
|
|
$infra_servers = ($infra_servers | merge { $server.provider: (mw_load_infra_servers_info $settings $server false)} )
|
|
}
|
|
let item_raw = (mw_get_infra_item $server $settings $infra_servers false)
|
|
let item = { item: $item_raw, target: "server" }
|
|
if $env.PROVISIONING_DEBUG_CHECK { print ($item | table -e)}
|
|
let price_month = (mw_get_infra_price $server $item "month" false | default 0)
|
|
let price_hour = (mw_get_infra_price $server $item "hour" false | default 0)
|
|
let price_day = ($price_hour * 24)
|
|
$total_month += $price_month
|
|
$total_hour += $price_hour
|
|
$total_day += ($price_day)
|
|
$c_total_month += $price_month
|
|
$c_total_hour += $price_hour
|
|
$c_total_day += ($price_day)
|
|
let already_created = (mw_server_exists $server false)
|
|
let host_color = if $already_created { "green_bold" } else { "red" }
|
|
$table_items = ($table_items | append {
|
|
host: $"(_ansi $host_color)($server.hostname)(_ansi reset) (_ansi blue_bold)($server.plan)(_ansi reset)",
|
|
prov: $"(_ansi default_bold) ($server.provider) (_ansi reset)",
|
|
hour: $"(_ansi default_bold) ($price_hour)€ (_ansi reset)",
|
|
day: $"(_ansi default_bold) ($price_day | math round -p 4)€ (_ansi reset)",
|
|
month: $"(_ansi default_bold) ($price_month)€ (_ansi reset)"
|
|
})
|
|
if not $check {
|
|
if not ($already_created) {
|
|
if $return_no_exists {
|
|
return { status: false, error: $"($server.hostname) not created" }
|
|
#} else {
|
|
#print $"(_ansi red_bold)($server.hostname)(_ansi reset) not created"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rm -rf $settings.wk_path
|
|
$table_items = ($table_items | append {
|
|
host: $"(_ansi --escape $sum_color) ($settings.infra) (_ansi reset)",
|
|
prov: $"(_ansi default_bold) (_ansi reset)",
|
|
hour: $"(_ansi --escape $sum_color) ($c_total_hour | math round -p 4)€ (_ansi reset)",
|
|
day: $"(_ansi --escape $sum_color) ($c_total_day | math round -p 4)€ (_ansi reset)",
|
|
month:$"(_ansi --escape $sum_color) ($c_total_month)€ (_ansi reset)"
|
|
})
|
|
}
|
|
$table_items = ($table_items | append { host: "", prov: "", month: "", day: "", hour: ""})
|
|
$table_items = ($table_items | append {
|
|
host: $"(_ansi --escape $total_color) TOTAL (_ansi reset)",
|
|
prov: $"(_ansi default_bold) (_ansi reset)",
|
|
hour: $"(_ansi --escape $total_color) ($total_hour | math round -p 4)€ (_ansi reset)",
|
|
day: $"(_ansi --escape $total_color) ($total_day | math round -p 4)€ (_ansi reset)",
|
|
month:$"(_ansi --escape $total_color) ($total_month)€ (_ansi reset)"
|
|
})
|
|
_print ($table_items | table -i false)
|
|
}
|