800 lines
32 KiB
Plaintext
Executable File
800 lines
32 KiB
Plaintext
Executable File
#!/usr/bin/env nu
|
||
# Info: Script to run Provisioning
|
||
# Author: JesusPerezLorenzo
|
||
# Release: 1.0.4
|
||
# Date: 6-2-2024
|
||
|
||
#use std # assert
|
||
use std log
|
||
|
||
# Detect project root and set up module paths early
|
||
# This ensures NU_LIB_DIRS is properly configured before loading modules
|
||
export-env {
|
||
# Project root detection: look for kcl.mod or provisioning structure
|
||
let potential_roots = [
|
||
$env.PWD
|
||
($env.PWD | path dirname)
|
||
($env.PWD | path dirname | path dirname)
|
||
]
|
||
|
||
let matching_roots = ($potential_roots
|
||
| where ($it | path join "kcl.mod" | path exists)
|
||
or ($it | path join "core" "nulib" | path exists))
|
||
|
||
let project_root = if ($matching_roots | length) > 0 {
|
||
$matching_roots | first
|
||
} else {
|
||
$env.PWD
|
||
}
|
||
|
||
# Update PWD in NU_LIB_DIRS to use detected project root
|
||
if ($env.NU_LIB_DIRS? | default [] | any {|path| $path == $env.PWD}) {
|
||
$env.NU_LIB_DIRS = ($env.NU_LIB_DIRS | each {|path|
|
||
if $path == $env.PWD { $project_root } else { $path }
|
||
})
|
||
}
|
||
|
||
# Add project-local env if it exists - will be loaded after main env.nu
|
||
}
|
||
|
||
use lib_provisioning *
|
||
use env.nu *
|
||
|
||
#Load all main defs
|
||
use main_provisioning *
|
||
|
||
#module srv { use instances.nu * }
|
||
|
||
use servers/ssh.nu *
|
||
use servers/utils.nu *
|
||
use taskservs/utils.nu find_taskserv
|
||
|
||
def run_module [
|
||
args: string
|
||
module: string
|
||
option?: string
|
||
--exec
|
||
] {
|
||
let use_debug = if $env.PROVISIONING_DEBUG { "-x" } else { "" }
|
||
# print $"($env.PROVISIONING_NAME) ($use_debug) -mod ($module) ($option | default "") ($args) --notitles "
|
||
if $exec {
|
||
exec $"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args --notitles
|
||
} else {
|
||
^$"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args --notitles
|
||
}
|
||
}
|
||
# - > Help on cprov
|
||
export def "main help" [
|
||
--notitles # not titles
|
||
--out: string # Print Output format: json, yaml, text (default)
|
||
] {
|
||
if $notitles == null or not $notitles { show_titles }
|
||
^($env.PROVISIONING_NAME) "--help"
|
||
if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false }
|
||
print (provisioning_options)
|
||
if not $env.PROVISIONING_DEBUG { end_run "" }
|
||
}
|
||
|
||
def main [
|
||
...args: string # Other options, use help to get info
|
||
--infra (-i): string # Cloud directory
|
||
--settings (-s): string # Settings path
|
||
--serverpos (-p): int # Server position in settings
|
||
--outfile (-o): string # Output file
|
||
--template(-t): string # Template path or name in PROVISION_KLOUDS_PATH
|
||
--check (-c) # Only check mode no servers will be created
|
||
--yes (-y) # confirm task
|
||
--wait (-w) # Wait servers to be created
|
||
--keepstorage # keep storage
|
||
--select: string # Select with task as option
|
||
--onsel: string # On selection: e (edit) | v (view) | l (list) | t (tree)
|
||
--infras: string # Infra list names separated by commas
|
||
--new (-n): string # New infrastructure name
|
||
--debug (-x) # Use Debug mode
|
||
--xm # Debug with PROVISIONING_METADATA
|
||
--xc # Debug for task and services locally PROVISIONING_DEBUG_CHECK
|
||
--xr # Debug for remote servers PROVISIONING_DEBUG_REMOTE
|
||
--xld # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug
|
||
--nc # Not clean working settings
|
||
--metadata # Error with metadata (-xm)
|
||
--notitles # not tittles
|
||
-v # Show version
|
||
--version (-V) # Show version with title
|
||
--info (-I) # Show Info with title
|
||
--about (-a) # Show About
|
||
--helpinfo (-h) # For more details use options "help" (no dashes)
|
||
--out: string # Print Output format: json, yaml, text (default)
|
||
--view # Print with highlight
|
||
--inputfile: string # Input format: json, yaml, text (default)
|
||
--include_notuse # Include servers not use
|
||
]: nothing -> nothing {
|
||
if ($out | is-not-empty) {
|
||
$env.PROVISIONING_OUT = $out
|
||
$env.PROVISIONING_NO_TERMINAL = true
|
||
}
|
||
provisioning_init $helpinfo "" $args
|
||
if $version or $v { ^$env.PROVISIONING_NAME -v ; exit }
|
||
if $info { ^$env.PROVISIONING_NAME -i ; exit }
|
||
if $about {
|
||
#use defs/about.nu [ about_info ]
|
||
_print (get_about_info)
|
||
exit
|
||
}
|
||
if $debug { $env.PROVISIONING_DEBUG = true }
|
||
if $metadata { $env.PROVISIONING_METADATA = true }
|
||
let task = if ($args | length) > 0 { ($args| get 0) } else { if ($new | is-not-empty) { "new" } else { "" } }
|
||
let ops = if ($args | length) > 0 {
|
||
($args| skip 1)
|
||
} else {
|
||
( $"($env.PROVISIONING_ARGS? | default "") " | str replace $"($task) " ""
|
||
| str trim | split row " ")
|
||
}
|
||
let str_ops = ($ops | str join " ")
|
||
match $task {
|
||
# "upcloud" => {
|
||
# #use upcloud/servers.nu upcloud
|
||
# if $infra == null {
|
||
# upcloud $args
|
||
# } else {
|
||
# upcloud --infra $infra $args
|
||
# }
|
||
# },
|
||
# "aws" => {
|
||
# #use aws/servers.nu aws
|
||
# if $infra == null {
|
||
# aws $args
|
||
# } else {
|
||
# aws --infra $infra $args
|
||
# }
|
||
# },
|
||
# "local" => {
|
||
# #use local/servers.nu local
|
||
# if $infra == null {
|
||
# local $args
|
||
# } else {
|
||
# local --infra $infra $args
|
||
# }
|
||
# },
|
||
"h" => {
|
||
^($env.PROVISIONING_NAME) help ($env.PROVISIONING_ARGS | str replace $task '') "--notitles"
|
||
},
|
||
"cache" => {
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
let str_outfile = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
||
let str_out = if $out != null { $"--out ($out) "} else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($str_out) ($str_outfile)" "server" "cache"
|
||
},
|
||
"providers" => {
|
||
#use defs/lists.nu *
|
||
_print $"(_ansi green)PROVIDERS(_ansi reset) list: \n"
|
||
_print (providers_list "selection" | to json) "json" "result" "table"
|
||
},
|
||
"ssh" => {
|
||
#use servers/ssh.nu *
|
||
#use utils/settings.nu *
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings $include_notuse)
|
||
rm -rf $curr_settings.wk_path
|
||
server_ssh $curr_settings "" "pub" false
|
||
}
|
||
"sed" | "sops" => {
|
||
if ($ops | length) == -2 {
|
||
(throw-error $"🛑 No file found" $"for (_ansi yellow_bold)sops(_ansi reset) edit")
|
||
exit -1
|
||
}
|
||
let pos = if $task == "sed" { 0 } else { 1 }
|
||
let target_file = ($ops | get -o $pos | default "")
|
||
let target_full_path = if ($target_file | path exists) == false {
|
||
let infra_path = (get_infra $infra)
|
||
if ($infra_path | path join $target_file | path exists) {
|
||
($infra_path | path join $target_file)
|
||
} else {
|
||
(throw-error $"🛑 No file (_ansi green_italic)($target_file)(_ansi reset) found" $"for (_ansi yellow_bold)sops(_ansi reset) edit")
|
||
exit -1
|
||
}
|
||
} else { $target_file }
|
||
if $env.PROVISIONING_SOPS? == null {
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings $include_notuse)
|
||
rm -rf $curr_settings.wk_path
|
||
$env.CURRENT_INFRA_PATH = ($curr_settings.infra_path | path join $curr_settings.infra)
|
||
use sops_env.nu
|
||
}
|
||
#use sops on_sops
|
||
if $task == "sed" {
|
||
on_sops "sed" $target_full_path
|
||
} else {
|
||
on_sops $task $target_full_path ($ops | skip 1)
|
||
}
|
||
},
|
||
"e" | "env" => {
|
||
match $out {
|
||
"json" => { _print (show_env | to json) "json" "result" "table" },
|
||
"yaml" => { _print (show_env | to yaml) "yaml" "result" "table" },
|
||
"toml" => { _print (show_env | to toml) "toml" "result" "table" },
|
||
_ => { print (show_env | table -e) } ,
|
||
}
|
||
},
|
||
"allenv" => {
|
||
let all_env = {
|
||
env: (show_env),
|
||
providers: (on_list "providers" "-" ""),
|
||
taskservs: (on_list "taskservs" "-" ""),
|
||
clusters: (on_list "clusters" "-" ""),
|
||
infras: (on_list "infras" "-" ""),
|
||
itemdefs: {
|
||
providers: (find_provgendefs),
|
||
taskserv: (
|
||
open ($env.PROVISIONING_TASKSERVS_PATH | path join $env.PROVISIONING_GENERATE_DIRPATH | path join $env.PROVISIONING_GENERATE_DEFSFILE)
|
||
)
|
||
}
|
||
}
|
||
if ($view) {
|
||
match $out {
|
||
"json" => ($all_env | to json | highlight),
|
||
"yaml" => ($all_env | to yaml | highlight),
|
||
"toml" => ($all_env | to toml | highlight),
|
||
_ => ($all_env | to json | highlight),
|
||
}
|
||
} else {
|
||
match $out {
|
||
"json" => { _print ($all_env | to json) "json" "result" "table" },
|
||
"yaml" => { _print ($all_env | to yaml) "yaml" "result" "table" },
|
||
"toml" => { _print ($all_env | to toml) "toml" "result" "table" },
|
||
_ => { print ($all_env | to json) } ,
|
||
}
|
||
}
|
||
},
|
||
"show" => {
|
||
match ($ops | get -o 0 | default "") {
|
||
"h" |"help" => {
|
||
print (provisioning_show_options)
|
||
exit
|
||
},
|
||
}
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings $include_notuse)
|
||
if ($curr_settings | is-empty) {
|
||
if ($out | is-empty) {
|
||
_print $"🛑 Errors found in infra (_ansi yellow_bold)($infra)(_ansi reset) notuse ($include_notuse)"
|
||
print ($curr_settings | describe)
|
||
print $settings
|
||
}
|
||
exit
|
||
}
|
||
let show_info = (get_show_info $ops $curr_settings ($out | default ""))
|
||
if ($view) {
|
||
match $out {
|
||
"json" => { print ($show_info | to json | highlight json) },
|
||
"yaml" => { print ($show_info | to yaml | highlight yaml) },
|
||
"toml" => { print ($show_info | to toml | highlight toml) },
|
||
_ => { print ($show_info | to json | highlight) },
|
||
}
|
||
} else {
|
||
match $out {
|
||
"json" => { _print ($show_info | to json) "json" "result" "table" },
|
||
"yaml" => { _print ($show_info | to yaml) "yaml" "result" "table" },
|
||
"toml" => { _print ($show_info | to toml) "toml" "result" "table" },
|
||
_ => { print ($show_info | to json) } ,
|
||
}
|
||
}
|
||
},
|
||
"c" | "create" => {
|
||
let use_debug = if $debug or $env.PROVISIONING_DEBUG { "-x"} else { "" }
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
||
exec $"($env.PROVISIONING_NAME)" $use_debug "create" $str_ops $use_check $str_infra $str_out --notitles
|
||
},
|
||
"d" | "delete" => {
|
||
let use_debug = if $debug { "-x"} else { "" }
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let use_yes = if $yes { "--yes "} else { "" }
|
||
let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
exec $"($env.PROVISIONING_NAME)" "delete" $str_ops $use_check $use_yes $use_keepstorage $str_infra --notitles
|
||
},
|
||
"u" | "update" => {
|
||
let use_debug = if $debug { "-x"} else { "" }
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
exec $"($env.PROVISIONING_NAME)" "update" $str_ops $use_check $str_infra --notitles
|
||
},
|
||
"cst" | "create-server-task" | "csts" | "create-servers-tasks" => {
|
||
run_module $str_ops "server" "create"
|
||
if $env.LAST_EXIT_CODE != 0 {
|
||
_print $"🛑 Errors found in (_ansi yellow_bold)create-server(_ansi reset)"
|
||
exit 1
|
||
}
|
||
run_module $"- ($str_ops)" "taskserv" "create"
|
||
},
|
||
"s" | "server" => {
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let use_yes = if $yes { "--yes" } else { "" }
|
||
let use_wait = if $wait { "--wait" } else { "" }
|
||
let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
let str_outfile = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
||
let str_out = if $out != null { $"--out ($out) "} else { "" }
|
||
let arg_include_notuse = if $include_notuse { $"--include_notuse "} else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($use_check) ($str_out) ($str_outfile) ($use_yes) ($use_wait) ($use_keepstorage) ($arg_include_notuse)" "server" --exec
|
||
},
|
||
"price" | "prices" | "cost" | "costs" => {
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($use_check) ($str_out)" "server" "price" --exec
|
||
},
|
||
"t" | "task" | "taskserv" => {
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($use_check)" "taskserv" --exec
|
||
},
|
||
"cl" | "cluster" => {
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($use_check)" "cluster" --exec
|
||
},
|
||
"g" | "gen" | "generate" => {
|
||
match ($ops | get -o 0 | default "") {
|
||
"h" |"help" => {
|
||
print (provisioning_generate_options)
|
||
exit
|
||
},
|
||
}
|
||
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
||
let use_debug = if $debug { "-x"} else { "" }
|
||
let use_check = if $check { "--check "} else { "" }
|
||
let str_out = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
||
let str_input = if $inputfile != null { $"--inputfile ($inputfile) "} else { "" }
|
||
let str_template = if ($template != null) { $"--template ($template)" } else { "" }
|
||
let str_select = if ($select != null) { $"--select ($select)" } else { "" }
|
||
if ($str_ops | is-empty) {
|
||
exec $"($env.PROVISIONING_NAME)" $use_debug "generate" $str_ops $use_check $str_infra $str_out --notitles
|
||
} else {
|
||
let target = ($ops | get -o 0)
|
||
let gen_ops = ($ops | skip 1 | str join " ") + $" ($str_infra) ($str_template) ($str_out) ($use_check) ($use_debug) ($str_select) ($str_input)"
|
||
match $target {
|
||
"s" | "server" => { run_module $"- ($gen_ops)" "server" "generate" --exec },
|
||
"t" | "task" | "taskserv" => { run_module $"- ($gen_ops)" "taskserv" "generate" --exec },
|
||
"i" | "infra" | "infras" => { run_module $"- ($gen_ops)" "infra" "generate" --exec },
|
||
"cl" | "cluster" => { run_module $"- ($gen_ops)" "cluster" "generate" --exec },
|
||
"h" | "help" => {
|
||
_print $"\n(provisioning_generate_options)"
|
||
exit
|
||
},
|
||
"new" => {
|
||
exec $"($env.PROVISIONING_NAME)" $use_debug "generate" "new" $gen_ops $str_template $use_check $str_infra $str_out --notitles
|
||
},
|
||
"_" => {
|
||
invalid_task "" $target --end
|
||
exit
|
||
}
|
||
}
|
||
}
|
||
print $"($str_ops) ($str_infra)"
|
||
#generate
|
||
},
|
||
"ctx" | "context" => {
|
||
^$"($env.PROVISIONING_NAME)" "context" $str_ops --notitles
|
||
run_module $str_ops "" --exec
|
||
},
|
||
"setup" | "st" | "config" => {
|
||
run_module $str_ops "setup" --exec
|
||
},
|
||
"i" | "infra" | "infras" => {
|
||
if ($str_ops | str contains "help") or $str_ops == "h" {
|
||
run_module "help" "infra"
|
||
exit -2
|
||
}
|
||
let infra_ops = if ($infra | is-not-empty) { $"-i ($infra)"
|
||
} else if $infras != null {
|
||
$"--infras ($infras)"
|
||
} else {
|
||
$"-i (get_infra | path basename)"
|
||
}
|
||
let use_yes = if $yes { "--yes"} else { "" }
|
||
let use_check = if $check { "--check"} else { "" }
|
||
let use_onsel = if $onsel != null { $"--onsel ($onsel)"} else { "" }
|
||
run_module $"($str_ops) ($infra_ops) ($use_check) ($use_onsel) ($use_yes)" "infra"
|
||
},
|
||
"deploy-rm" | "deploy-del" | "dp-rm" | "d-r" | "destroy" => {
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings)
|
||
deploy_remove $curr_settings ($str_ops | split row "-" | get -o 0 | default "")
|
||
rm -rf $curr_settings.wk_path
|
||
if $task == "destroy" {
|
||
let with_yes = if $yes { "--yes" } else { "" }
|
||
exec $"($env.PROVISIONING_NAME)" "delete" server --notitles $with_yes
|
||
}
|
||
},
|
||
"deploy-sel" | "deploy-list" | "dp-sel" | "d-s" => {
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings)
|
||
deploy_list $curr_settings ($str_ops | split row "-" | get -o 0 | default "") ($onsel | default "")
|
||
rm -rf $curr_settings.wk_path
|
||
},
|
||
"deploy-sel-tree" | "deploy-list-tree" | "dp-sel-t" | "d-st" => {
|
||
let curr_settings = (find_get_settings --infra $infra --settings $settings)
|
||
(deploy_list $curr_settings $str_ops "tree")
|
||
rm -rf $curr_settings.wk_path
|
||
},
|
||
"nu" => {
|
||
let run_ops = if ($str_ops | str trim | str starts-with "-") {
|
||
""
|
||
} else {
|
||
($ops | get -o 0)
|
||
}
|
||
if ($infra | is-not-empty) and ($env.PROVISIONING_INFRA_PATH | path join $infra |path exists) {
|
||
cd ($env.PROVISIONING_INFRA_PATH | path join $infra)
|
||
}
|
||
if ($env.PROVISIONING_OUT | is-empty) {
|
||
if ($run_ops | is-empty) {
|
||
print (
|
||
$"\nTo exit (_ansi purple_bold)NuShell(_ansi reset) session, with (_ansi default_dimmed)lib_provisioning(_ansi reset) loaded, " +
|
||
$"use (_ansi green_bold)exit(_ansi reset) or (_ansi green_bold)[CTRL-D](_ansi reset)"
|
||
)
|
||
^nu -i -e $"use lib_provisioning * ; use env.nu * ; show_titles;"
|
||
#^nu -e $"use lib_provisioning * ; show_titles; $env.PROMPT_INDICATOR = {|| 'provisioning> ' } ; $env.PROMPT_COMMAND = {|| create_left_prompt } "
|
||
} else {
|
||
^nu -c $"($run_ops)"
|
||
}
|
||
}
|
||
},
|
||
"list" | "l" | "ls" => {
|
||
#use defs/lists.nu on_list
|
||
let target_list = if ($args | length) > -1 { ($args| get -o 1 | default "") } else { "" }
|
||
let list_ops = ($ops | str join " " | str replace $"($target_list) " "" | str trim)
|
||
on_list $target_list ($onsel | default "") $list_ops
|
||
},
|
||
"qr" => {
|
||
#use utils/qr.nu *
|
||
make_qr
|
||
},
|
||
"nuinfo" => {
|
||
print $"\n (_ansi yellow)Nu shell info(_ansi reset)"
|
||
print (version)
|
||
},
|
||
"plugin" | "plugins" => {
|
||
print $"\n (_ansi yellow)Nu shell Plugins(_ansi reset)"
|
||
^nu -c "plugin list"
|
||
},
|
||
"new" => {
|
||
let str_new = ($new | default "")
|
||
print $"\n (_ansi yellow)New Infra ($str_new)(_ansi reset)"
|
||
},
|
||
"ai" => {
|
||
# AI command module
|
||
let str_infra = if $infra != null { $"--infra ($infra) " } else { "" }
|
||
let str_settings = if $settings != null { $"--settings ($settings) " } else { "" }
|
||
let str_out = if $out != null { $"--out ($out) " } else { "" }
|
||
run_module $"($str_ops) ($str_infra) ($str_settings) ($str_out)" "ai" --exec
|
||
},
|
||
"validate" | "val" => {
|
||
# Infrastructure validation module
|
||
let sub_command = ($ops | get -o 0 | default "")
|
||
|
||
match $sub_command {
|
||
"help" | "h" => {
|
||
use main_provisioning/ops.nu *
|
||
print (provisioning_validate_options)
|
||
}
|
||
"test" => {
|
||
# Run the test script directly
|
||
nu test_validation.nu
|
||
}
|
||
"quick" => {
|
||
let target_path = if $infra != null { $infra } else {
|
||
let next_arg = ($ops | get -o 1 | default ".")
|
||
if ($next_arg | path exists) { $next_arg } else { "." }
|
||
}
|
||
|
||
print "🚀 Quick Infrastructure Validation"
|
||
print "=================================="
|
||
print ""
|
||
print $"📁 Target: ($target_path | path expand)"
|
||
print ""
|
||
print "🔄 Running quick validation (errors and critical issues only)..."
|
||
print ""
|
||
|
||
let result = (nu test_validation.nu | complete)
|
||
|
||
if $result.exit_code == 0 {
|
||
print "✅ Quick validation passed!"
|
||
print ""
|
||
print " No critical errors or blocking issues found."
|
||
print $" Infrastructure ($target_path) is ready for deployment."
|
||
} else {
|
||
print "❌ Quick validation found issues"
|
||
print ""
|
||
print " Please review and fix critical/error-level issues before deployment."
|
||
}
|
||
print ""
|
||
}
|
||
"rules" => {
|
||
# Show rules list
|
||
print "📋 Available Validation Rules"
|
||
print "============================"
|
||
print ""
|
||
print "🔧 👁️ VAL001: 🚨 YAML Syntax Validation (critical)"
|
||
print " Category: syntax | Severity: critical | Auto-fix: false"
|
||
print ""
|
||
print "🔧 👁️ VAL002: 🚨 KCL Compilation Check (critical)"
|
||
print " Category: compilation | Severity: critical | Auto-fix: false"
|
||
print ""
|
||
print "🔧 ✅ VAL003: ❌ Unquoted Variable References (error)"
|
||
print " Category: syntax | Severity: error | Auto-fix: true"
|
||
print ""
|
||
print "🔧 👁️ VAL004: ❌ Required Fields Validation (error)"
|
||
print " Category: schema | Severity: error | Auto-fix: false"
|
||
print ""
|
||
print "🔧 ✅ VAL005: ⚠️ Resource Naming Conventions (warning)"
|
||
print " Category: best_practices | Severity: warning | Auto-fix: true"
|
||
print ""
|
||
print "🔧 👁️ VAL006: ❌ Basic Security Checks (error)"
|
||
print " Category: security | Severity: error | Auto-fix: false"
|
||
print ""
|
||
print "🔧 👁️ VAL007: ⚠️ Version Compatibility Check (warning)"
|
||
print " Category: compatibility | Severity: warning | Auto-fix: false"
|
||
print ""
|
||
print "🔧 👁️ VAL008: ❌ Network Configuration Validation (error)"
|
||
print " Category: networking | Severity: error | Auto-fix: false"
|
||
print ""
|
||
print "Legend:"
|
||
print "✅ = Auto-fixable | 👁️ = Manual fix required"
|
||
print "🚨 = Critical | ❌ = Error | ⚠️ = Warning | ℹ️ = Info"
|
||
}
|
||
_ => {
|
||
# Execute actual validation
|
||
let target_path = if $infra != null {
|
||
$infra
|
||
} else if ($sub_command | path exists) {
|
||
$sub_command
|
||
} else {
|
||
# Use current directory if it contains infrastructure files
|
||
# Check for common infrastructure indicators: settings.k, kcl.mod, or .k files
|
||
let current_dir = "."
|
||
let has_settings = ($current_dir | path join "settings.k" | path exists)
|
||
let has_kcl_mod = ($current_dir | path join "kcl.mod" | path exists)
|
||
let has_k_files = ((glob "*.k") | length) > 0
|
||
|
||
if $has_settings or $has_kcl_mod or $has_k_files {
|
||
$current_dir
|
||
} else {
|
||
# If no infrastructure files in current dir, show help
|
||
use main_provisioning/ops.nu *
|
||
print (provisioning_validate_options)
|
||
return
|
||
}
|
||
}
|
||
|
||
print "🔍 Infrastructure Validation & Review Tool"
|
||
print "=========================================="
|
||
print ""
|
||
print $"📁 Validating: ($target_path | path expand)"
|
||
print ""
|
||
|
||
# Check if target path exists
|
||
if not ($target_path | path exists) {
|
||
print $"🛑 Infrastructure path not found: ($target_path)"
|
||
print ""
|
||
print "Use 'provisioning validate help' for usage information"
|
||
exit 1
|
||
}
|
||
|
||
# Run the validation using our working test system
|
||
print "🔄 Running infrastructure validation..."
|
||
print ""
|
||
|
||
# Run basic validation directly without external script dependency
|
||
# Count and validate infrastructure files recursively
|
||
let k_files = (glob "**/*.k")
|
||
let yaml_files = (glob "**/*.yaml" | append (glob "**/*.yml"))
|
||
let toml_files = (glob "**/*.toml")
|
||
|
||
let total_files = ($k_files | length) + ($yaml_files | length) + ($toml_files | length)
|
||
|
||
print $"📊 Found ($total_files) infrastructure files:"
|
||
print $" • KCL files: ($k_files | length)"
|
||
print $" • YAML files: ($yaml_files | length)"
|
||
print $" • TOML files: ($toml_files | length)"
|
||
print ""
|
||
|
||
# Simple validation checks
|
||
mut issues = []
|
||
|
||
# Check for settings.k file
|
||
if ("settings.k" | path exists) {
|
||
print "✅ settings.k file found"
|
||
} else {
|
||
print "⚠️ No settings.k file found"
|
||
$issues = ($issues | append "Missing settings.k file")
|
||
}
|
||
|
||
# Basic KCL syntax check for each .k file
|
||
for file in $k_files {
|
||
print $"🔍 Checking KCL file: ($file)"
|
||
|
||
# Check if file is SOPS encrypted
|
||
let content = (open $file --raw)
|
||
let is_sops_file = ($content | str contains "\"sops\":") or ($content | str contains "ENC[AES256_GCM")
|
||
|
||
if $is_sops_file {
|
||
# Handle SOPS encrypted file
|
||
print $" 🔐 ($file) - SOPS encrypted file detected"
|
||
|
||
# Set up SOPS environment using the config-driven approach
|
||
$env.PROVISIONING_USE_SOPS = ($env.PROVISIONING_USE_SOPS? | default "age")
|
||
$env.PROVISIONING_SOPS = ($env.PROVISIONING_SOPS? | default "")
|
||
|
||
use lib_provisioning/sops/lib.nu get_def_age
|
||
let kage_path = (get_def_age $env.PROVISIONING_KLOUD_PATH)
|
||
if ($kage_path | is-not-empty) and ($kage_path | path exists) {
|
||
$env.SOPS_AGE_KEY_FILE = $kage_path
|
||
}
|
||
|
||
# Check if SOPS can decrypt it
|
||
let sops_check = (^sops -d $file | complete)
|
||
if $sops_check.exit_code == 0 {
|
||
# Try to validate the decrypted content
|
||
let kcl_check = (^sops -d $file | ^kcl - | complete)
|
||
if $kcl_check.exit_code == 0 {
|
||
print $" ✅ ($file) - SOPS encrypted KCL syntax OK"
|
||
} else {
|
||
print $" ❌ ($file) - SOPS encrypted KCL syntax error"
|
||
$issues = ($issues | append $"KCL syntax error in SOPS file ($file)")
|
||
}
|
||
} else {
|
||
print $" ⚠️ ($file) - SOPS decryption failed - check keys/config"
|
||
print $" Skipping validation (SOPS error: ($sops_check.stderr | str trim))"
|
||
# Don't add to issues - this might be expected if keys aren't available
|
||
}
|
||
} else {
|
||
# Regular KCL file validation
|
||
let check_result = (^kcl $file | complete)
|
||
if $check_result.exit_code == 0 {
|
||
print $" ✅ ($file) - KCL syntax OK"
|
||
} else {
|
||
print $" ❌ ($file) - KCL syntax error"
|
||
$issues = ($issues | append $"KCL syntax error in ($file)")
|
||
}
|
||
}
|
||
}
|
||
|
||
# Basic YAML syntax check
|
||
for file in $yaml_files {
|
||
print $"🔍 Checking YAML file: ($file)"
|
||
let yaml_result = (^yq eval . $file | complete)
|
||
if $yaml_result.exit_code == 0 {
|
||
print $" ✅ ($file) - YAML syntax OK"
|
||
} else {
|
||
print $" ❌ ($file) - YAML syntax error"
|
||
$issues = ($issues | append $"YAML syntax error in ($file)")
|
||
}
|
||
}
|
||
|
||
let result = {
|
||
exit_code: (if ($issues | length) > 0 { 1 } else { 0 })
|
||
issues: $issues
|
||
}
|
||
|
||
print ""
|
||
|
||
if $result.exit_code == 0 {
|
||
print "✅ Validation completed successfully!"
|
||
print ""
|
||
print "📊 Summary:"
|
||
print " • No critical issues found"
|
||
print " • All infrastructure files are valid"
|
||
print " • Infrastructure is ready for deployment"
|
||
print ""
|
||
print $"📁 Files processed in: ($target_path | path expand)"
|
||
print ""
|
||
print "💡 For detailed validation options, use:"
|
||
print " provisioning validate help"
|
||
} else {
|
||
print "❌ Validation found issues"
|
||
print ""
|
||
print "🔍 Issues found:"
|
||
for issue in $result.issues {
|
||
print $" • ($issue)"
|
||
}
|
||
print ""
|
||
print "💡 Please fix these issues before deployment"
|
||
print " Use 'provisioning validate help' for more options"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
_ => {
|
||
invalid_task "" $task --end
|
||
exit
|
||
},
|
||
}
|
||
if not $env.PROVISIONING_DEBUG { end_run "" }
|
||
#print $"($env.PWD)\n($env.FILE_PWD)\n($env.PROCESS_PATH)\n"
|
||
}
|
||
export def get_show_info [
|
||
ops: list
|
||
curr_settings: record
|
||
out: string
|
||
]: nothing -> record {
|
||
match ($ops | get -o 0 | default "") {
|
||
"set" |"setting" | "settings" => $curr_settings,
|
||
"def" | "defs" |"defsetting" | "defsettings" => {
|
||
let src = ($curr_settings | get -o src | default "");
|
||
let src_path = ($curr_settings | get -o src_path | default "");
|
||
let def_settings = if ($src_path | path join $src | path exists) {
|
||
open -r ($src_path | path join $src)
|
||
} else { "" }
|
||
let main_path = ($env.PROVISIONING | path join "kcl" | path join "settings.k")
|
||
let src_main_settings = if ($main_path | path exists) {
|
||
open -r $main_path
|
||
} else { "" }
|
||
{
|
||
def: $src,
|
||
def_path: $src_path,
|
||
infra: ($curr_settings | get -o infra | default ""),
|
||
infra_path: ($curr_settings | get -o infra_path | default ""),
|
||
def_settings: $def_settings,
|
||
main_path: $main_path,
|
||
main_settings: $src_main_settings,
|
||
}
|
||
},
|
||
"server" |"servers" | "s" => {
|
||
let servers = ($curr_settings | get -o data | get -o servers | default {})
|
||
let item = ($ops | get -o 1 | default "")
|
||
if ($item | is-empty) {
|
||
$servers
|
||
} else {
|
||
let server = (find_server $item $servers ($out | default ""))
|
||
let def_target = ($ops | get -o 2 | default "")
|
||
match $def_target {
|
||
"t" | "task" | "taskserv" => {
|
||
let task = ($ops | get -o 3 | default "")
|
||
(find_taskserv $curr_settings $server $task ($out | default ""))
|
||
},
|
||
_ => $server,
|
||
}
|
||
}
|
||
},
|
||
"serverdefs" |"serversdefs" | "sd" => {
|
||
(find_serversdefs $curr_settings)
|
||
},
|
||
"provgendefs" |"provgendef" | "pgd" => {
|
||
(find_provgendefs)
|
||
},
|
||
"taskservs" |"taskservs" | "ts" => {
|
||
#(list_taskservs $curr_settings)
|
||
let list_taskservs = (taskservs_list)
|
||
if ($list_taskservs | length) == 0 {
|
||
_print $"🛑 no items found for (_ansi cyan)taskservs list(_ansi reset)"
|
||
return
|
||
}
|
||
$list_taskservs
|
||
},
|
||
"taskservsgendefs" |"taskservsgendef" | "tsd" => {
|
||
let defs_path = ($env.PROVISIONING_TASKSERVS_PATH | path join $env.PROVISIONING_GENERATE_DIRPATH | path join $env.PROVISIONING_GENERATE_DEFSFILE)
|
||
if ($defs_path | path exists) {
|
||
open $defs_path
|
||
}
|
||
},
|
||
"cost" | "costs" | "c" | "price" | "prices" | "p" => {
|
||
(servers_walk_by_costs $curr_settings "" false false "stdout")
|
||
},
|
||
"alldata" => ($curr_settings | get -o data | default {}
|
||
| merge { costs: (servers_walk_by_costs $curr_settings "" false false "stdout") }
|
||
),
|
||
"data" | _ => {
|
||
if ($out | is-not-empty) {
|
||
($curr_settings | get -o data | default {})
|
||
} else {
|
||
print ($" (_ansi cyan_bold)($curr_settings | get -o data | get -o main_name | default '')"
|
||
+ $"(_ansi reset): (_ansi yellow_bold)($curr_settings | get -o data | get -o main_title | default '') (_ansi reset)"
|
||
)
|
||
print ($curr_settings | get -o data | default {} | merge { servers: ''})
|
||
($curr_settings | get -o data | default {} | get -o servers | each {|item|
|
||
print $"\n server: (_ansi cyan_bold)($item.hostname | default '') (_ansi reset)"
|
||
print $item
|
||
})
|
||
""
|
||
}
|
||
},
|
||
}
|
||
}
|