440 lines
13 KiB
Plaintext
440 lines
13 KiB
Plaintext
|
|
#!/usr/bin/env nu
|
||
|
|
# Info: Script to run Provisioning Template Management
|
||
|
|
# Author: JesusPerezLorenzo
|
||
|
|
# Release: 1.0.0
|
||
|
|
# Date: 29-09-2025
|
||
|
|
|
||
|
|
use std log
|
||
|
|
|
||
|
|
use lib_provisioning *
|
||
|
|
use env.nu *
|
||
|
|
|
||
|
|
# - > Help on Template
|
||
|
|
export def "main help" [
|
||
|
|
--src: string = ""
|
||
|
|
--notitles # not titles
|
||
|
|
--out: string # Print Output format: json, yaml, text (default)
|
||
|
|
]: nothing -> nothing {
|
||
|
|
if $notitles == null or not $notitles { show_titles }
|
||
|
|
^$"($env.PROVISIONING_NAME)" -mod template --help
|
||
|
|
if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false }
|
||
|
|
print (provisioning_options $src)
|
||
|
|
if not $env.PROVISIONING_DEBUG { end_run "" }
|
||
|
|
}
|
||
|
|
|
||
|
|
# > Template Management
|
||
|
|
def main [
|
||
|
|
...args: string # Other options, use help to get info
|
||
|
|
-v # Show version
|
||
|
|
-i # Show Info
|
||
|
|
--version (-V) # Show version with title
|
||
|
|
--info (-I) # Show Info with title
|
||
|
|
--about (-a) # Show About
|
||
|
|
--infra: string # Infrastructure name
|
||
|
|
--provider: string = "" # Provider name
|
||
|
|
--type: string = "" # Template type: taskservs, providers, servers, clusters
|
||
|
|
--layer: string = "workspace" # Layer: core, workspace, infra
|
||
|
|
--dry-run # Show what would be done without doing it
|
||
|
|
--backup # Create backup before operations
|
||
|
|
--force (-f) # Force operation
|
||
|
|
--check (-c) # Only check mode, no actual changes
|
||
|
|
--yes (-y) # Confirm task
|
||
|
|
--debug (-x) # Use Debug mode
|
||
|
|
--xm # Debug with PROVISIONING_METADATA
|
||
|
|
--xld # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug
|
||
|
|
--metadata # Error with metadata (-xm)
|
||
|
|
--notitles # Do not show banner titles
|
||
|
|
--helpinfo (-h) # For more details use options "help" (no dashes)
|
||
|
|
--out: string # Print Output format: json, yaml, text (default)
|
||
|
|
]: nothing -> nothing {
|
||
|
|
if ($out | is-not-empty) {
|
||
|
|
$env.PROVISIONING_OUT = $out
|
||
|
|
$env.PROVISIONING_NO_TERMINAL = true
|
||
|
|
}
|
||
|
|
provisioning_init $helpinfo "template" $args
|
||
|
|
if $version or $v { ^$env.PROVISIONING_NAME -v ; exit }
|
||
|
|
if $info or $i { ^$env.PROVISIONING_NAME -i ; exit }
|
||
|
|
if $about {
|
||
|
|
_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 { "" }
|
||
|
|
let ops = $"($env.PROVISIONING_ARGS? | default "") " | str replace $" ($task) " "" | str trim
|
||
|
|
let template_name = if ($ops | is-not-empty) and not ($ops | str starts-with "-") {
|
||
|
|
($ops | split row " " | get 0)
|
||
|
|
} else {
|
||
|
|
""
|
||
|
|
}
|
||
|
|
let target_infra = if ($ops | split row " " | length) > 1 {
|
||
|
|
($ops | split row " " | get 1)
|
||
|
|
} else {
|
||
|
|
$infra
|
||
|
|
}
|
||
|
|
|
||
|
|
$env.PROVISIONING_MODULE = "template"
|
||
|
|
|
||
|
|
match $task {
|
||
|
|
"h" | "help" => {
|
||
|
|
# Redirect to main categorized help system
|
||
|
|
exec $"($env.PROVISIONING_NAME)" help workspace --notitles
|
||
|
|
},
|
||
|
|
"list" | "ls" => {
|
||
|
|
template_list --type $type --out $out
|
||
|
|
},
|
||
|
|
"types" => {
|
||
|
|
template_types --out $out
|
||
|
|
},
|
||
|
|
"show" => {
|
||
|
|
if ($template_name | is-empty) {
|
||
|
|
print "❌ Template name required"
|
||
|
|
print "Usage: provisioning template show <name>"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
template_show $template_name --type $type --out $out
|
||
|
|
},
|
||
|
|
"apply" => {
|
||
|
|
if ($template_name | is-empty) or ($target_infra | is-empty) {
|
||
|
|
print "❌ Template name and target infrastructure required"
|
||
|
|
print "Usage: provisioning template apply <template_name> <infra_name> --provider <provider>"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if $dry_run {
|
||
|
|
template_apply $template_name $target_infra --provider $provider --dry-run
|
||
|
|
} else {
|
||
|
|
template_apply $template_name $target_infra --provider $provider
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"extract" => {
|
||
|
|
if ($target_infra | is-empty) {
|
||
|
|
print "❌ Infrastructure name required"
|
||
|
|
print "Usage: provisioning template extract <infra_name>"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if $dry_run {
|
||
|
|
template_extract $target_infra --type $type --dry-run
|
||
|
|
} else {
|
||
|
|
template_extract $target_infra --type $type
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"convert" => {
|
||
|
|
if ($target_infra | is-empty) {
|
||
|
|
print "❌ Infrastructure name required"
|
||
|
|
print "Usage: provisioning template convert <infra_name>"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if $backup and $dry_run {
|
||
|
|
template_convert $target_infra --backup --dry-run
|
||
|
|
} else if $backup {
|
||
|
|
template_convert $target_infra --backup
|
||
|
|
} else if $dry_run {
|
||
|
|
template_convert $target_infra --dry-run
|
||
|
|
} else {
|
||
|
|
template_convert $target_infra
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"validate" => {
|
||
|
|
if ($target_infra | is-empty) {
|
||
|
|
print "❌ Infrastructure name required"
|
||
|
|
print "Usage: provisioning template validate <infra_name>"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
template_validate $target_infra
|
||
|
|
},
|
||
|
|
"layer" => {
|
||
|
|
template_layer_info --infra $target_infra --out $out
|
||
|
|
},
|
||
|
|
_ => {
|
||
|
|
print $"❌ Unknown task: ($task)"
|
||
|
|
print "Use 'provisioning template help' for available commands"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# List available templates
|
||
|
|
def template_list [
|
||
|
|
--type: string
|
||
|
|
--out: string
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let templates_path = ($provisioning_root | path join "workspace" "templates")
|
||
|
|
|
||
|
|
if not ($templates_path | path exists) {
|
||
|
|
print "❌ Templates directory not found"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
let template_types = if ($type | is-not-empty) {
|
||
|
|
[$type]
|
||
|
|
} else {
|
||
|
|
["taskservs", "providers", "servers", "clusters"]
|
||
|
|
}
|
||
|
|
|
||
|
|
let all_templates = ($template_types | each {|ttype|
|
||
|
|
let type_path = ($templates_path | path join $ttype)
|
||
|
|
if ($type_path | path exists) {
|
||
|
|
let items = (ls $type_path | where type == dir | select name)
|
||
|
|
$items | each {|item| {
|
||
|
|
type: $ttype
|
||
|
|
name: ($item.name | path basename)
|
||
|
|
path: $item.name
|
||
|
|
}}
|
||
|
|
} else {
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
} | flatten)
|
||
|
|
|
||
|
|
if ($out | is-not-empty) {
|
||
|
|
if $out == "json" {
|
||
|
|
print ($all_templates | to json)
|
||
|
|
} else if $out == "yaml" {
|
||
|
|
print ($all_templates | to yaml)
|
||
|
|
} else {
|
||
|
|
print ($all_templates | table)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
print "📋 Available Templates:"
|
||
|
|
for template in $all_templates {
|
||
|
|
print $" [($template.type)] ($template.name)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# List template types
|
||
|
|
def template_types [--out: string]: nothing -> nothing {
|
||
|
|
let types = [
|
||
|
|
{type: "taskservs", description: "Task service configurations (kubernetes, containerd, etc.)"}
|
||
|
|
{type: "providers", description: "Cloud provider templates (upcloud, aws, local)"}
|
||
|
|
{type: "servers", description: "Server configuration patterns"}
|
||
|
|
{type: "clusters", description: "Complete cluster templates"}
|
||
|
|
]
|
||
|
|
|
||
|
|
if ($out | is-not-empty) {
|
||
|
|
if $out == "json" {
|
||
|
|
print ($types | to json)
|
||
|
|
} else if $out == "yaml" {
|
||
|
|
print ($types | to yaml)
|
||
|
|
} else {
|
||
|
|
print ($types | table)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
print "📁 Template Types:"
|
||
|
|
for t in $types {
|
||
|
|
print $" ($t.type): ($t.description)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show template details
|
||
|
|
def template_show [
|
||
|
|
name: string
|
||
|
|
--type: string
|
||
|
|
--out: string
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let templates_path = ($provisioning_root | path join "workspace" "templates")
|
||
|
|
|
||
|
|
let template_types = if ($type | is-not-empty) {
|
||
|
|
[$type]
|
||
|
|
} else {
|
||
|
|
["taskservs", "providers", "servers", "clusters"]
|
||
|
|
}
|
||
|
|
|
||
|
|
let found = ($template_types | each {|ttype|
|
||
|
|
let template_path = ($templates_path | path join $ttype $name)
|
||
|
|
if ($template_path | path exists) {
|
||
|
|
let files = (ls $template_path | select name type size)
|
||
|
|
{
|
||
|
|
type: $ttype
|
||
|
|
name: $name
|
||
|
|
path: $template_path
|
||
|
|
files: $files
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
null
|
||
|
|
}
|
||
|
|
} | where {|x| $x != null} | first)
|
||
|
|
|
||
|
|
if ($found | is-empty) {
|
||
|
|
print $"❌ Template not found: ($name)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($out | is-not-empty) {
|
||
|
|
_print $found
|
||
|
|
} else {
|
||
|
|
print $"📦 Template: ($found.name)"
|
||
|
|
print $" Type: ($found.type)"
|
||
|
|
print $" Path: ($found.path)"
|
||
|
|
print " Files:"
|
||
|
|
for file in $found.files {
|
||
|
|
print $" - ($file.name | path basename) [($file.type)] ($file.size)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Apply template to infrastructure
|
||
|
|
def template_apply [
|
||
|
|
template_name: string
|
||
|
|
target_infra: string
|
||
|
|
--provider: string
|
||
|
|
--dry-run
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let templates_path = ($provisioning_root | path join "workspace" "templates")
|
||
|
|
|
||
|
|
# Find template
|
||
|
|
let template_types = ["taskservs", "providers", "servers", "clusters"]
|
||
|
|
let template_path = ($template_types | each {|ttype|
|
||
|
|
let path = ($templates_path | path join $ttype $template_name)
|
||
|
|
if ($path | path exists) {
|
||
|
|
{type: $ttype, path: $path}
|
||
|
|
} else {
|
||
|
|
null
|
||
|
|
}
|
||
|
|
} | where {|x| $x != null} | first)
|
||
|
|
|
||
|
|
if ($template_path | is-empty) {
|
||
|
|
print $"❌ Template not found: ($template_name)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
let config = get-config
|
||
|
|
let workspace_root = $config.paths.base? | default $env.PWD
|
||
|
|
let target_path = ($workspace_root | path join "infra" $target_infra)
|
||
|
|
|
||
|
|
if not ($target_path | path exists) {
|
||
|
|
print $"❌ Infrastructure not found: ($target_infra)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
if $dry_run {
|
||
|
|
print $"🔍 DRY RUN: Would apply template ($template_name) to ($target_infra)"
|
||
|
|
print $" Template type: ($template_path.type)"
|
||
|
|
print $" Template path: ($template_path.path)"
|
||
|
|
print $" Target path: ($target_path)"
|
||
|
|
print $" Provider: ($provider)"
|
||
|
|
} else {
|
||
|
|
print $"🔧 Applying template ($template_name) to ($target_infra)"
|
||
|
|
|
||
|
|
let target_subdir = match $template_path.type {
|
||
|
|
"taskservs" => "task-servs"
|
||
|
|
"providers" => "defs"
|
||
|
|
"servers" => "defs"
|
||
|
|
"clusters" => "clusters"
|
||
|
|
_ => $template_path.type
|
||
|
|
}
|
||
|
|
|
||
|
|
let dest_dir = ($target_path | path join $target_subdir)
|
||
|
|
mkdir $dest_dir
|
||
|
|
|
||
|
|
# Copy template files
|
||
|
|
cp -r ($template_path.path | path join "*") $dest_dir
|
||
|
|
|
||
|
|
print $"✅ Template applied: ($template_name) -> ($target_infra)"
|
||
|
|
print $"📁 Files copied to: ($dest_dir)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Extract patterns from infrastructure
|
||
|
|
def template_extract [
|
||
|
|
infra_name: string
|
||
|
|
--type: string
|
||
|
|
--dry-run
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu")
|
||
|
|
|
||
|
|
if not ($migrate_script | path exists) {
|
||
|
|
print $"❌ Migration script not found: ($migrate_script)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
let type_flag = if ($type | is-not-empty) { ["--type" $type] } else { [] }
|
||
|
|
let dry_run_flag = if $dry_run { ["--dry-run"] } else { [] }
|
||
|
|
|
||
|
|
^nu $migrate_script extract $infra_name ...$type_flag ...$dry_run_flag
|
||
|
|
}
|
||
|
|
|
||
|
|
# Convert infrastructure to use templates
|
||
|
|
def template_convert [
|
||
|
|
infra_name: string
|
||
|
|
--backup
|
||
|
|
--dry-run
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu")
|
||
|
|
|
||
|
|
if not ($migrate_script | path exists) {
|
||
|
|
print $"❌ Migration script not found: ($migrate_script)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
let backup_flag = if not $backup { ["--no-backup"] } else { [] }
|
||
|
|
let dry_run_flag = if $dry_run { ["--dry-run"] } else { [] }
|
||
|
|
|
||
|
|
^nu $migrate_script convert $infra_name ...$backup_flag ...$dry_run_flag
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate template usage in infrastructure
|
||
|
|
def template_validate [infra_name: string]: nothing -> nothing {
|
||
|
|
let provisioning_root = $env.PROVISIONING? | default "/usr/local/provisioning"
|
||
|
|
let migrate_script = ($provisioning_root | path join "workspace" "tools" "migrate-infra.nu")
|
||
|
|
|
||
|
|
if not ($migrate_script | path exists) {
|
||
|
|
print $"❌ Migration script not found: ($migrate_script)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
^nu $migrate_script validate $infra_name
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show layer information
|
||
|
|
def template_layer_info [
|
||
|
|
--infra: string
|
||
|
|
--out: string
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let layers = [
|
||
|
|
{
|
||
|
|
name: "core"
|
||
|
|
priority: 100
|
||
|
|
path: "provisioning/extensions/"
|
||
|
|
description: "Base provisioning system extensions"
|
||
|
|
}
|
||
|
|
{
|
||
|
|
name: "workspace"
|
||
|
|
priority: 200
|
||
|
|
path: "provisioning/workspace/templates/"
|
||
|
|
description: "Shared templates and reusable configurations"
|
||
|
|
}
|
||
|
|
{
|
||
|
|
name: "infrastructure"
|
||
|
|
priority: 300
|
||
|
|
path: $"workspace/infra/($infra | default '{name}')/"
|
||
|
|
description: "Infrastructure-specific configurations and overrides"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
|
||
|
|
if ($out | is-not-empty) {
|
||
|
|
if $out == "json" {
|
||
|
|
print ($layers | to json)
|
||
|
|
} else if $out == "yaml" {
|
||
|
|
print ($layers | to yaml)
|
||
|
|
} else {
|
||
|
|
print ($layers | table)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
print "🏗️ Layer Resolution Order:"
|
||
|
|
print " (Higher priority overrides lower priority)"
|
||
|
|
print ""
|
||
|
|
for layer in ($layers | reverse) {
|
||
|
|
print $" ($layer.priority) - ($layer.name | str upcase)"
|
||
|
|
print $" Path: ($layer.path)"
|
||
|
|
print $" ($layer.description)"
|
||
|
|
print ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|