#!/usr/bin/env nu # Infrastructure Migration Tool # Helps migrate existing infrastructures to use the new layered template system # Extract infrastructure patterns to templates export def "migrate extract" [ infra_name: string, # Source infrastructure (e.g., "wuji") --dry-run = false # Show what would be done without doing it ] { print $"๐Ÿ”„ Extracting patterns from ($infra_name) infrastructure" let infra_path = $"../../../workspace/infra/($infra_name)" let templates_path = "../templates" if not ($infra_path | path exists) { error make {msg: $"Infrastructure ($infra_name) not found at ($infra_path)"} } # Extract taskservs if ($"($infra_path)/taskservs" | path exists) { print " ๐Ÿ“ฆ Extracting taskserv patterns..." extract_taskservs $infra_path $templates_path $dry_run } # Extract provider defaults if ($"($infra_path)/defs" | path exists) { print " ๐Ÿ“ฆ Extracting provider patterns..." extract_providers $infra_path $templates_path $dry_run } # Extract server patterns if ($"($infra_path)/defs/servers.k" | path exists) { print " ๐Ÿ“ฆ Extracting server patterns..." extract_servers $infra_path $templates_path $dry_run } if not $dry_run { print $"โœ… Extraction completed for ($infra_name)" print $"๐Ÿ“ Templates created in ($templates_path)" print $"Next step: Run 'migrate convert ($infra_name)' to convert infrastructure to use templates" } else { print $"โœ… Dry run completed - no files were created" } } # Convert infrastructure to use templates export def "migrate convert" [ infra_name: string, # Infrastructure to convert --backup = true, # Create backup before conversion --dry-run = false # Show what would be done ] { print $"๐Ÿ”„ Converting ($infra_name) to use template system" let infra_path = $"../../../workspace/infra/($infra_name)" if not ($infra_path | path exists) { error make {msg: $"Infrastructure ($infra_name) not found"} } if $backup and not $dry_run { let backup_path = $"($infra_path).backup-(date now | format date '%Y%m%d_%H%M%S')" print $"๐Ÿ“ Creating backup at ($backup_path)" cp -r $infra_path $backup_path } # Convert taskservs to use templates convert_taskservs_to_templates $infra_path $infra_name $dry_run # Convert provider configs convert_providers_to_templates $infra_path $infra_name $dry_run if not $dry_run { print $"โœ… Conversion completed for ($infra_name)" print $"๐Ÿงช Test with: module-loader-enhanced layer test kubernetes --infra ($infra_name)" } else { print $"โœ… Dry run completed - no files were modified" } } # Validate template usage in infrastructure export def "migrate validate" [ infra_name: string # Infrastructure to validate ] { print $"๐Ÿ” Validating template usage in ($infra_name)" let infra_path = $"../../../workspace/infra/($infra_name)" let issues = [] if not ($infra_path | path exists) { error make {msg: $"Infrastructure ($infra_name) not found"} } # Check for template imports validate_template_imports $infra_path $infra_name # Check layer resolution validate_layer_resolution $infra_name print $"โœ… Validation completed for ($infra_name)" } # Compare configurations between infrastructures export def "migrate diff" [ source_infra: string, # Source infrastructure target_infra: string # Target infrastructure ] { print $"๐Ÿ” Comparing ($source_infra) vs ($target_infra)" let source_path = $"../../../workspace/infra/($source_infra)" let target_path = $"../../../workspace/infra/($target_infra)" compare_taskservs $source_path $target_path compare_providers $source_path $target_path print $"โœ… Comparison completed" } # === HELPER FUNCTIONS === def extract_taskservs [infra_path: string, templates_path: string, dry_run: bool] { let taskservs_dir = $"($infra_path)/taskservs" if ($taskservs_dir | path exists) { for file in (ls $taskservs_dir | where type == file | get name) { let filename = ($file | path basename) let taskserv_name = ($filename | str replace ".k" "") # Determine template category based on taskserv name let category = categorize_taskserv $taskserv_name let target_dir = $"($templates_path)/taskservs/($category)" let target_file = $"($target_dir)/($filename)" print $" โžœ ($taskserv_name) -> ($category)/($filename)" if not $dry_run { mkdir $target_dir # Read original file and convert to template let content = open $file let template_content = create_taskserv_template $content $taskserv_name $template_content | save $target_file } } } } def extract_providers [infra_path: string, templates_path: string, dry_run: bool] { let defs_dir = $"($infra_path)/defs" if ($defs_dir | path exists) { for file in (ls $defs_dir | where name =~ "_defaults\.k$" | get name) { let filename = ($file | path basename) let provider_name = ($filename | str replace "_defaults.k" "") let target_dir = $"($templates_path)/providers/($provider_name)" let target_file = $"($target_dir)/defaults.k" print $" โžœ ($provider_name) defaults -> providers/($provider_name)/defaults.k" if not $dry_run { mkdir $target_dir # Read and convert to template let content = open $file let template_content = create_provider_template $content $provider_name $template_content | save $target_file } } } } def extract_servers [infra_path: string, templates_path: string, dry_run: bool] { let servers_file = $"($infra_path)/defs/servers.k" if ($servers_file | path exists) { print $" โžœ Extracting server patterns" if not $dry_run { # Parse servers.k and create templates create_server_templates $servers_file $templates_path } } } def convert_taskservs_to_templates [infra_path: string, infra_name: string, dry_run: bool] { let taskservs_dir = $"($infra_path)/taskservs" if ($taskservs_dir | path exists) { print $" ๐Ÿ”ง Converting taskservs to use templates..." for file in (ls $taskservs_dir | where type == file | get name) { let filename = ($file | path basename) let taskserv_name = ($filename | str replace ".k" "") print $" โžœ Converting ($taskserv_name)" if not $dry_run { # Create new file that uses templates let new_content = create_template_based_taskserv $taskserv_name $infra_name $new_content | save $file } } } } def convert_providers_to_templates [infra_path: string, infra_name: string, dry_run: bool] { let defs_dir = $"($infra_path)/defs" if ($defs_dir | path exists) { print $" ๐Ÿ”ง Converting providers to use templates..." for file in (ls $defs_dir | where name =~ "_defaults\.k$" | get name) { let filename = ($file | path basename) let provider_name = ($filename | str replace "_defaults.k" "") print $" โžœ Converting ($provider_name) defaults" if not $dry_run { # Create new file that uses templates let new_content = create_template_based_provider $provider_name $infra_name $new_content | save $file } } } } def categorize_taskserv [name: string] -> string { match $name { $n if ($n | str contains "kubernetes") => "kubernetes" $n if ($n | str contains "k8s") => "kubernetes" $n if ($n in ["rook-ceph", "mayastor"]) => "storage" $n if ($n in ["cilium", "coredns", "proxy"]) => "networking" $n if ($n in ["containerd", "crio", "podman", "runc", "crun", "youki"]) => "container-runtime" _ => "misc" } } def create_taskserv_template [content: string, name: string] -> string { # Convert existing taskserv to template format # This is a simplified version - in practice, you'd parse the KCL properly let template_header = $"# ($name | str title-case) Template # Extracted from infrastructure patterns # Provides ($name) configuration with customizable defaults import taskservs.($name).kcl.($name) as ($name)_core import ../../../workspace/templates/lib/compose as comp " # Add the existing content with template wrapper $"($template_header)($content)" } def create_provider_template [content: string, provider: string] -> string { let template_header = $"# ($provider | str title-case) Provider Template # Extracted from infrastructure patterns # Provides ($provider) provider defaults with customizable options import ($provider)_prov import ../../../workspace/templates/lib/compose as comp import ../../../workspace/templates/lib/override as ovr " $"($template_header)($content)" } def create_server_templates [servers_file: string, templates_path: string] { # Parse servers.k and create control-plane and worker templates # This would need more sophisticated KCL parsing let servers_content = open $servers_file # Create control-plane template let cp_template = $"# Control Plane Server Template # Extracted from server patterns import upcloud_prov import ../../../workspace/templates/lib/compose as comp schema ControlPlaneServer { hostname: str plan: str = \"2xCPU-4GB\" # ... other control plane specific settings } control_plane_server = ControlPlaneServer {}" mkdir $"($templates_path)/servers" $cp_template | save $"($templates_path)/servers/control-plane.k" } def create_template_based_taskserv [name: string, infra: string] -> string { $"# ($name | str title-case) Configuration for ($infra) # Uses workspace templates with infrastructure-specific overrides import ../../../provisioning/workspace/registry/imports as reg import ../../../provisioning/workspace/templates/lib/compose as comp import ../../../provisioning/workspace/templates/lib/override as ovr # Use template with infrastructure-specific overrides _taskserv = ovr.infrastructure_overrides.taskserv_override ( reg.tpl_($name)_base.($name)_base, \"($infra)\", \"($name)\", { cluster_name: \"($infra)\" # Add other infrastructure-specific overrides here } ) _taskserv" } def create_template_based_provider [provider: string, infra: string] -> string { $"# ($provider | str title-case) Defaults for ($infra) # Uses workspace templates with infrastructure-specific overrides import ../../../provisioning/workspace/registry/imports as reg import ../../../provisioning/workspace/templates/lib/compose as comp import ../../../provisioning/workspace/templates/lib/override as ovr # Use template with infrastructure-specific overrides ovr.infrastructure_overrides.create_infra_config ( reg.tpl_($provider)_defaults.($provider)_defaults, \"($infra)\", \"($provider)\", \"($infra).local\", # Update with actual domain { # Add infrastructure-specific overrides here } )" } def validate_template_imports [infra_path: string, infra_name: string] { print $" ๐Ÿ” Checking template imports..." # Check if files use template imports let taskservs_dir = $"($infra_path)/taskservs" if ($taskservs_dir | path exists) { for file in (ls $taskservs_dir | where type == file | get name) { let content = open $file let has_template_import = ($content | str contains "workspace/templates") or ($content | str contains "workspace/registry/imports") let filename = ($file | path basename) if $has_template_import { print $" โœ… ($filename) uses templates" } else { print $" โš ๏ธ ($filename) not using templates" } } } } def validate_layer_resolution [infra_name: string] { print $" ๐Ÿ” Validating layer resolution..." # Test common taskservs let common_taskservs = ["kubernetes", "cilium", "containerd"] for taskserv in $common_taskservs { let core_exists = ($"../../../provisioning/extensions/taskservs/($taskserv)" | path exists) let workspace_exists = ($"../templates/taskservs/($taskserv)" | path exists) let infra_exists = ($"../../../workspace/infra/($infra_name)/taskservs/($taskserv).k" | path exists) let resolution = [] if $infra_exists { $resolution = ($resolution | append "infra") } if $workspace_exists { $resolution = ($resolution | append "workspace") } if $core_exists { $resolution = ($resolution | append "core") } if ($resolution | length) > 0 { print $" โœ… ($taskserv): (($resolution | str join ' -> '))" } else { print $" โŒ ($taskserv): not found in any layer" } } } def compare_taskservs [source_path: string, target_path: string] { print $" ๐Ÿ“Š Comparing taskservs..." let source_taskservs = if ($"($source_path)/taskservs" | path exists) { ls $"($source_path)/taskservs" | get name | each {|f| $f | path basename | str replace ".k" ""} } else { [] } let target_taskservs = if ($"($target_path)/taskservs" | path exists) { ls $"($target_path)/taskservs" | get name | each {|f| $f | path basename | str replace ".k" ""} } else { [] } let common = $source_taskservs | filter {|t| $t in $target_taskservs} let source_only = $source_taskservs | filter {|t| not ($t in $target_taskservs)} let target_only = $target_taskservs | filter {|t| not ($t in $source_taskservs)} if ($common | length) > 0 { print $" โœ… Common: (($common | str join ', '))" } if ($source_only | length) > 0 { print $" โžก๏ธ Source only: (($source_only | str join ', '))" } if ($target_only | length) > 0 { print $" โฌ…๏ธ Target only: (($target_only | str join ', '))" } } def compare_providers [source_path: string, target_path: string] { print $" ๐Ÿ“Š Comparing providers..." let source_providers = if ($"($source_path)/defs" | path exists) { ls $"($source_path)/defs" | where name =~ "_defaults\.k$" | get name | each {|f| $f | path basename | str replace "_defaults.k" ""} } else { [] } let target_providers = if ($"($target_path)/defs" | path exists) { ls $"($target_path)/defs" | where name =~ "_defaults\.k$" | get name | each {|f| $f | path basename | str replace "_defaults.k" ""} } else { [] } let common = $source_providers | filter {|p| $p in $target_providers} let source_only = $source_providers | filter {|p| not ($p in $target_providers)} let target_only = $target_providers | filter {|p| not ($p in $source_providers)} if ($common | length) > 0 { print $" โœ… Common: (($common | str join ', '))" } if ($source_only | length) > 0 { print $" โžก๏ธ Source only: (($source_only | str join ', '))" } if ($target_only | length) > 0 { print $" โฌ…๏ธ Target only: (($target_only | str join ', '))" } }