409 lines
13 KiB
Plaintext
409 lines
13 KiB
Plaintext
|
|
# Configuration Migration Module
|
||
|
|
# Handles migration from existing workspace configurations to new setup system
|
||
|
|
# Follows Nushell guidelines: explicit types, single purpose, no try-catch
|
||
|
|
|
||
|
|
use ./mod.nu *
|
||
|
|
use ./detection.nu *
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# EXISTING CONFIGURATION DETECTION
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Detect existing workspace configuration
|
||
|
|
export def detect-existing-workspace [
|
||
|
|
workspace_path: string
|
||
|
|
]: nothing -> record {
|
||
|
|
let config_path = $"($workspace_path)/config/provisioning.yaml"
|
||
|
|
let providers_path = $"($workspace_path)/.providers"
|
||
|
|
let infra_path = $"($workspace_path)/infra"
|
||
|
|
|
||
|
|
{
|
||
|
|
workspace_path: $workspace_path
|
||
|
|
has_config: ($config_path | path exists)
|
||
|
|
config_path: $config_path
|
||
|
|
has_providers: ($providers_path | path exists)
|
||
|
|
providers_path: $providers_path
|
||
|
|
has_infra: ($infra_path | path exists)
|
||
|
|
infra_path: $infra_path
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Find existing workspace directories
|
||
|
|
export def find-existing-workspaces []: nothing -> list<string> {
|
||
|
|
mut workspaces = []
|
||
|
|
|
||
|
|
# Check common workspace locations
|
||
|
|
let possible_paths = [
|
||
|
|
"workspace_librecloud"
|
||
|
|
"./workspace_librecloud"
|
||
|
|
"../workspace_librecloud"
|
||
|
|
"workspaces"
|
||
|
|
"./workspaces"
|
||
|
|
]
|
||
|
|
|
||
|
|
for path in $possible_paths {
|
||
|
|
let expanded_path = ($path | path expand)
|
||
|
|
if ($expanded_path | path exists) and (($expanded_path | path type) == "dir") {
|
||
|
|
let workspace_config = $"($expanded_path)/config/provisioning.yaml"
|
||
|
|
if ($workspace_config | path exists) {
|
||
|
|
$workspaces = ($workspaces | append $expanded_path)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$workspaces
|
||
|
|
}
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# CONFIGURATION MIGRATION
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Migrate workspace configuration from YAML to new system
|
||
|
|
export def migrate-workspace-config [
|
||
|
|
workspace_path: string
|
||
|
|
config_base: string
|
||
|
|
--backup = true
|
||
|
|
]: nothing -> record {
|
||
|
|
let source_config = $"($workspace_path)/config/provisioning.yaml"
|
||
|
|
|
||
|
|
if not ($source_config | path exists) {
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
error: "Source configuration not found"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Load existing configuration
|
||
|
|
let existing_config = (load-config-yaml $source_config)
|
||
|
|
|
||
|
|
# Extract workspace name from path
|
||
|
|
let workspace_name = ($workspace_path | path basename)
|
||
|
|
|
||
|
|
# Create backup if requested
|
||
|
|
if $backup {
|
||
|
|
let timestamp_for_backup = (get-timestamp-iso8601 | str replace -a ':' '-')
|
||
|
|
let backup_path = $"($config_base)/migration-backup-($workspace_name)-($timestamp_for_backup).yaml"
|
||
|
|
let backup_result = (do { cp $source_config $backup_path } | complete)
|
||
|
|
|
||
|
|
if ($backup_result.exit_code != 0) {
|
||
|
|
print-setup-warning $"Failed to create backup at ($backup_path)"
|
||
|
|
} else {
|
||
|
|
print-setup-success $"Configuration backed up to ($backup_path)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create migration record
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
workspace_name: $workspace_name
|
||
|
|
source_path: $source_config
|
||
|
|
migrated_at: (get-timestamp-iso8601)
|
||
|
|
backup_created: $backup
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Migrate provider configurations
|
||
|
|
export def migrate-provider-configs [
|
||
|
|
workspace_path: string
|
||
|
|
config_base: string
|
||
|
|
]: nothing -> record {
|
||
|
|
let providers_source = $"($workspace_path)/.providers"
|
||
|
|
|
||
|
|
if not ($providers_source | path exists) {
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
migrated_providers: []
|
||
|
|
error: "No provider directory found"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mut migrated = []
|
||
|
|
|
||
|
|
# Get list of provider directories
|
||
|
|
let result = (do {
|
||
|
|
ls $providers_source | where type == "dir"
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
if ($result.exit_code != 0) {
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
migrated_providers: []
|
||
|
|
error: "Failed to read provider directories"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Migrate each provider
|
||
|
|
for provider_entry in $result.stdout {
|
||
|
|
let provider_name = ($provider_entry | str trim)
|
||
|
|
if ($provider_name | str length) > 0 {
|
||
|
|
print-setup-info $"Migrating provider: ($provider_name)"
|
||
|
|
$migrated = ($migrated | append $provider_name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let success_status = ($migrated | length) > 0
|
||
|
|
let migrated_at_value = (get-timestamp-iso8601)
|
||
|
|
{
|
||
|
|
success: $success_status
|
||
|
|
migrated_providers: $migrated
|
||
|
|
source_path: $providers_source
|
||
|
|
migrated_at: $migrated_at_value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# MIGRATION VALIDATION
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Validate migration can proceed safely
|
||
|
|
export def validate-migration [
|
||
|
|
workspace_path: string
|
||
|
|
config_base: string
|
||
|
|
]: nothing -> record {
|
||
|
|
mut warnings = []
|
||
|
|
mut errors = []
|
||
|
|
|
||
|
|
# Check source workspace exists
|
||
|
|
if not ($workspace_path | path exists) {
|
||
|
|
$errors = ($errors | append "Source workspace path does not exist")
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check configuration base exists
|
||
|
|
if not ($config_base | path exists) {
|
||
|
|
$errors = ($errors | append "Target configuration base does not exist")
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check if migration already happened
|
||
|
|
let migration_marker = $"($config_base)/migration_completed.yaml"
|
||
|
|
if ($migration_marker | path exists) {
|
||
|
|
$warnings = ($warnings | append "Migration appears to have been run before")
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for conflicts
|
||
|
|
let workspace_name = ($workspace_path | path basename)
|
||
|
|
let registry_path = $"($config_base)/workspaces_registry.yaml"
|
||
|
|
|
||
|
|
if ($registry_path | path exists) {
|
||
|
|
let registry = (load-config-yaml $registry_path)
|
||
|
|
if ($registry.workspaces? | default [] | any { |w| $w.name == $workspace_name }) {
|
||
|
|
$warnings = ($warnings | append $"Workspace '($workspace_name)' already registered")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let can_proceed_status = ($errors | length) == 0
|
||
|
|
let error_count_value = ($errors | length)
|
||
|
|
let warning_count_value = ($warnings | length)
|
||
|
|
{
|
||
|
|
can_proceed: $can_proceed_status
|
||
|
|
errors: $errors
|
||
|
|
warnings: $warnings
|
||
|
|
error_count: $error_count_value
|
||
|
|
warning_count: $warning_count_value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# MIGRATION EXECUTION
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Execute complete workspace migration
|
||
|
|
export def execute-migration [
|
||
|
|
workspace_path: string
|
||
|
|
config_base: string = ""
|
||
|
|
--backup = true
|
||
|
|
--verbose = false
|
||
|
|
]: nothing -> record {
|
||
|
|
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
|
||
|
|
|
||
|
|
print-setup-header "Workspace Configuration Migration"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
# Validate migration can proceed
|
||
|
|
let validation = (validate-migration $workspace_path $base)
|
||
|
|
if not $validation.can_proceed {
|
||
|
|
for error in $validation.errors {
|
||
|
|
print-setup-error $error
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
errors: $validation.errors
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show warnings
|
||
|
|
if ($validation.warnings | length) > 0 {
|
||
|
|
for warning in $validation.warnings {
|
||
|
|
print-setup-warning $warning
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print ""
|
||
|
|
print-setup-info "Starting migration process..."
|
||
|
|
print ""
|
||
|
|
|
||
|
|
# Step 1: Migrate workspace configuration
|
||
|
|
print-setup-info "Migrating workspace configuration..."
|
||
|
|
let config_migration = (migrate-workspace-config $workspace_path $base --backup=$backup)
|
||
|
|
if not $config_migration.success {
|
||
|
|
print-setup-error $config_migration.error
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
error: $config_migration.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
print-setup-success "Workspace configuration migrated"
|
||
|
|
|
||
|
|
# Step 2: Migrate provider configurations
|
||
|
|
print-setup-info "Migrating provider configurations..."
|
||
|
|
let provider_migration = (migrate-provider-configs $workspace_path $base)
|
||
|
|
if $provider_migration.success {
|
||
|
|
print-setup-success $"Migrated ($provider_migration.migrated_providers | length) providers"
|
||
|
|
} else {
|
||
|
|
print-setup-warning "No provider configurations to migrate"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Step 3: Create migration marker
|
||
|
|
let workspace_name = ($workspace_path | path basename)
|
||
|
|
let migration_marker_path = $"($base)/migration_completed.yaml"
|
||
|
|
let migration_record = {
|
||
|
|
version: "1.0.0"
|
||
|
|
completed_at: (get-timestamp-iso8601)
|
||
|
|
workspace_migrated: $workspace_name
|
||
|
|
source_path: $workspace_path
|
||
|
|
target_path: $base
|
||
|
|
backup_created: $backup
|
||
|
|
}
|
||
|
|
|
||
|
|
let save_result = (save-config-yaml $migration_marker_path $migration_record)
|
||
|
|
if not $save_result {
|
||
|
|
print-setup-warning "Failed to create migration marker"
|
||
|
|
}
|
||
|
|
|
||
|
|
print ""
|
||
|
|
print-setup-success "Migration completed successfully!"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
# Summary
|
||
|
|
print "Migration Summary:"
|
||
|
|
print $" Source Workspace: ($workspace_path)"
|
||
|
|
print $" Target Config Base: ($base)"
|
||
|
|
print $" Configuration Migrated: ✅"
|
||
|
|
print $" Providers Migrated: ($provider_migration.migrated_providers | length)"
|
||
|
|
if $backup {
|
||
|
|
print " Backup Created: ✅"
|
||
|
|
}
|
||
|
|
print ""
|
||
|
|
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
workspace_name: $workspace_name
|
||
|
|
config_migration: $config_migration
|
||
|
|
provider_migration: $provider_migration
|
||
|
|
migration_completed_at: (get-timestamp-iso8601)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# MIGRATION ROLLBACK
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Rollback migration from backup
|
||
|
|
export def rollback-migration [
|
||
|
|
workspace_name: string
|
||
|
|
config_base: string = ""
|
||
|
|
--restore_backup = true
|
||
|
|
]: nothing -> record {
|
||
|
|
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
|
||
|
|
|
||
|
|
print-setup-header "Rolling Back Migration"
|
||
|
|
print ""
|
||
|
|
print-setup-warning "Initiating migration rollback..."
|
||
|
|
print ""
|
||
|
|
|
||
|
|
# Find and restore backup
|
||
|
|
let migration_marker = $"($base)/migration_completed.yaml"
|
||
|
|
if not ($migration_marker | path exists) {
|
||
|
|
print-setup-error "No migration record found - cannot rollback"
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
error: "No migration record found"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let migration_record = (load-config-yaml $migration_marker)
|
||
|
|
|
||
|
|
# Find backup file
|
||
|
|
let backup_pattern = $"($base)/migration-backup-($workspace_name)-*.yaml"
|
||
|
|
print-setup-info $"Looking for backup matching: ($backup_pattern)"
|
||
|
|
|
||
|
|
# Remove migration artifacts
|
||
|
|
if ($migration_marker | path exists) {
|
||
|
|
let rm_result = (do { rm $migration_marker } | complete)
|
||
|
|
if ($rm_result.exit_code == 0) {
|
||
|
|
print-setup-success "Migration marker removed"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print ""
|
||
|
|
print-setup-success "Migration rollback completed"
|
||
|
|
print ""
|
||
|
|
print "Note: Please verify your workspace is in the desired state"
|
||
|
|
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
workspace_name: $workspace_name
|
||
|
|
rolled_back_at: (get-timestamp-iso8601)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# ============================================================================
|
||
|
|
# AUTO-MIGRATION
|
||
|
|
# ============================================================================
|
||
|
|
|
||
|
|
# Automatically detect and migrate existing workspaces
|
||
|
|
export def auto-migrate-existing [
|
||
|
|
config_base: string = ""
|
||
|
|
--verbose = false
|
||
|
|
]: nothing -> record {
|
||
|
|
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
|
||
|
|
|
||
|
|
print-setup-header "Detecting Existing Workspaces"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
# Find existing workspaces
|
||
|
|
let existing = (find-existing-workspaces)
|
||
|
|
|
||
|
|
if ($existing | length) == 0 {
|
||
|
|
print-setup-info "No existing workspaces detected"
|
||
|
|
return {
|
||
|
|
success: true
|
||
|
|
workspaces_found: 0
|
||
|
|
workspaces: []
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print-setup-success $"Found ($existing | length) existing workspace(s)"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
mut migrated = []
|
||
|
|
|
||
|
|
for workspace_path in $existing {
|
||
|
|
let workspace_name = ($workspace_path | path basename)
|
||
|
|
print-setup-info $"Auto-migrating: ($workspace_name)"
|
||
|
|
|
||
|
|
let migration_result = (execute-migration $workspace_path $base --verbose=$verbose)
|
||
|
|
if $migration_result.success {
|
||
|
|
$migrated = ($migrated | append $workspace_name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
workspaces_found: ($existing | length)
|
||
|
|
workspaces: $existing
|
||
|
|
migrated_count: ($migrated | length)
|
||
|
|
migrated_workspaces: $migrated
|
||
|
|
timestamp: (get-timestamp-iso8601)
|
||
|
|
}
|
||
|
|
}
|