# Provider-Agnostic Middleware # Uses dynamic provider loading and interface-based dispatch # Supports multi-provider infrastructure deployments use ../../../core/nulib/lib_provisioning/config/accessor.nu * use ../../../core/nulib/lib_provisioning/extensions/profiles.nu * use ../../../core/nulib/lib_provisioning/providers/registry.nu * use ../../../core/nulib/lib_provisioning/providers/loader.nu * use ../../../core/nulib/lib_provisioning/providers/interface.nu * use ../../../core/nulib/lib_provisioning/utils/logging.nu * # Initialize middleware export def init-middleware []: nothing -> nothing { init-provider-registry log-info "Provider-agnostic middleware initialized" "middleware" } # Check if provider is allowed by profile (safer version) def is_provider_allowed [provider_name: string]: nothing -> bool { try { let profile = (load-profile) # If not restricted, allow everything if not $profile.restricted { return true } # Check if provider is explicitly blocked if ($profile.blocked.providers | where {|p| $p == $provider_name} | length) > 0 { return false } # If allowed list is empty, allow all (except blocked) if ($profile.allowed.providers | length) == 0 { return true } # Check if provider is in allowed list ($profile.allowed.providers | where {|p| $p == $provider_name} | length) > 0 } catch { # If profile loading fails, default to allow true } } def provider_undefined [ server: record ] { let available_providers = (list-providers --available-only | get name | str join ", ") log-error $"Provider ($server.provider) not found or not available" "middleware" print $"(_ansi blue_bold)($server.hostname)(_ansi reset) with provider " + $"(_ansi green_bold)($server.provider)(_ansi reset) ($server.zone?) " print $"Error 🛑 provider expected to be one of available providers [(_ansi green_italic)($available_providers)(_ansi reset)], " + $"got (_ansi green_bold)($server.provider)(_ansi reset)" } # Dynamic provider dispatch helper def dispatch_provider_function [ provider_name: string function_name: string ...args ]: nothing -> any { # Check if provider is allowed if not (is_provider_allowed $provider_name) { log-error $"Provider ($provider_name) blocked by profile" "middleware" return null } # Load provider if not already loaded let provider = (get-provider $provider_name) if ($provider | is-empty) { log-error $"Failed to load provider ($provider_name)" "middleware" return null } # Call provider function call-provider-function $provider_name $function_name ...$args } # === CORE MIDDLEWARE FUNCTIONS === # Query servers (supports multi-provider) export def mw_query_servers [ settings: record find?: string cols?: string --prov: string --serverpos: int ]: nothing -> list { if not ($env.PROVIDER_REGISTRY_INITIALIZED? | default false) { init-middleware | ignore } let str_find = if $find != null { $find } else { "" } let str_cols = if $cols != null { $cols } else { "" } $settings.data.servers | enumerate | each { |it| if $prov == null or $it.item.provider == $prov { if $serverpos == null or $serverpos == $it.index { let provider_name = $it.item.provider # Dynamic provider dispatch let res = try { dispatch_provider_function $provider_name "query_servers" $str_find $str_cols } catch { provider_undefined $it.item [] } if ($res != null) and ($res | length) > 0 { let result = if $str_find != "" { $res | find $str_find } else { $res } if $str_cols != "" { let field_list = ($str_cols | split row ",") ($result | select -o $field_list) } else { $result } } else { [] } } } } | flatten } # Server information (provider-agnostic) export def mw_server_info [ server: record check: bool find?: string cols?: string ]: nothing -> record { let str_find = if $find != null { $find } else { "" } let str_cols = if $cols != null { $cols } else { "" } let res = try { dispatch_provider_function $server.provider "server_info" $server $check $str_find $str_cols } catch { provider_undefined $server {} } if ($res | describe | str starts-with "record") and $res.hostname? != null { let result = if $str_find != "" { $res | find $str_find } else { $res } let info = if $str_cols != "" { let field_list = ($str_cols | split row ",") ($result | select -o $field_list) } else { $result } # Handle provider-specific private IP formats let priv = if $server.provider == "aws" { ($info | get -o private_ips | default [] | each {|it| ($it | select Description PrivateIpAddress VpcId SubnetId Groups) }) } else { ($info | get -o priv | default []) } let full_info = if ($priv | length) > 0 { ($info | merge { private_ips: $priv }) } else { $info } let out = (get-provisioning-out) if ($out | is-empty) { print ($full_info | table -e) } if (not $check) { ($full_info | table -e) } $full_info } else { $res } } # Server creation (provider-agnostic with profile checking) export def mw_create_server [ settings: record server: record check: bool error_exit: bool ]: nothing -> bool { # Check if provider is allowed by profile if not (is_provider_allowed $server.provider) { log-error $"Provider ($server.provider) blocked by current profile" "middleware" if $error_exit { exit } else { return false } } let zone = $server.zone? | default "" # Dynamic provider dispatch for requirements check let res = try { dispatch_provider_function $server.provider "check_server_requirements" $settings $server $check } catch { provider_undefined $server if $error_exit { exit } else { false } } if not $res { log-error $"($server.provider) check requirements error for server ($server.hostname)" "middleware" return false } # Show provider-specific server creation message try { let msg = (dispatch_provider_function $server.provider "on_prov_server" $server) if $msg != null { print $msg } } catch { } print ($"Create (_ansi blue_bold)($server.hostname)(_ansi reset) with provider " + $"(_ansi green_bold)($server.provider)(_ansi reset) ($zone) ") return true } # Server state management (provider-agnostic) export def mw_server_state [ server: record new_state: string error_exit: bool wait: bool settings: record ]: nothing -> bool { # Check if provider is allowed by profile if not (is_provider_allowed $server.provider) { log-error $"Provider ($server.provider) blocked by current profile" "middleware" if $error_exit { exit } else { return false } } try { let result = (dispatch_provider_function $server.provider "server_state" $server $new_state $error_exit $wait $settings) $result != null } catch { provider_undefined $server if $error_exit { exit } else { return false } } } # Server existence check (provider-agnostic) export def mw_server_exists [ server: record error_exit: bool ]: nothing -> bool { try { let result = (dispatch_provider_function $server.provider "server_exists" $server $error_exit) $result != null } catch { provider_undefined $server if $error_exit { exit } else { false } } } # Server running status (provider-agnostic) export def mw_server_is_running [ server: record error_exit: bool ]: nothing -> bool { try { let result = (dispatch_provider_function $server.provider "server_is_running" $server $error_exit) $result != null } catch { provider_undefined $server if $error_exit { exit } else { false } } } # Get IP (provider-agnostic) export def mw_get_ip [ settings: record server: record ip_type: string error_exit: bool ]: nothing -> string { try { let result = (dispatch_provider_function $server.provider "get_ip" $settings $server $ip_type $error_exit) if $result != null { $result | str trim } else { "" } } catch { provider_undefined $server if $error_exit { exit } else { "" } } } # Multi-provider infrastructure operations export def mw_servers_ips [ settings: record data: list prov?: string serverpos?: int ]: nothing -> list { mut result = [] for srv in ($data | enumerate) { let settings_server = ($settings.data.servers | where {|it| $it.hostname == $srv.item.hostname}) if ($settings_server | length) == 0 { continue } let provider = ($settings_server | get -o 0 | get -o provider | default "") if $prov != null and $provider != $prov { continue } if $serverpos != null and $serverpos != $srv.index { continue } try { let provider_result = (dispatch_provider_function $provider "servers_ips" $settings [$srv.item] $prov $serverpos) if $provider_result != null { $result = ($result | append $provider_result) } } catch { provider_undefined { provider: $provider, hostname: $srv.item.hostname } } } $result } # Multi-provider server deletion export def mw_delete_server [ settings: record server: record keep_storage: bool error_exit: bool ]: nothing -> bool { let zone = $server.zone? | default "" try { let msg = (dispatch_provider_function $server.provider "on_prov_server" $server) if $msg != null { print $msg } let result = (dispatch_provider_function $server.provider "delete_server" $settings $server $keep_storage $error_exit) $result != null } catch { provider_undefined $server if $error_exit { exit } else { false } } } # === ENHANCED MULTI-PROVIDER OPERATIONS === # Deploy multi-provider infrastructure (simplified) export def mw_deploy_multi_provider_infra [ settings: record deployment_plan: record ]: nothing -> record { log-section "Starting multi-provider deployment" "middleware" # Group servers by provider let provider_groups = ($settings.data.servers | group-by provider) let providers_used = ($provider_groups | columns) log-info $"Will deploy to providers: ($providers_used | str join ', ')" "middleware" # Return basic deployment info { providers_used: $providers_used total_servers: ($settings.data.servers | length) deployment_plan: $deployment_plan status: "planned" timestamp: (date now) } } # Get provider status with capabilities export def mw_provider_status [ --verbose ]: nothing -> table { if not ($env.PROVIDER_REGISTRY_INITIALIZED? | default false) { init-middleware | ignore } let providers = (list-providers --available-only) $providers | each {|provider| let allowed = (is_provider_allowed $provider.name) let capabilities = (get-provider-capabilities-for $provider.name) let basic_info = { name: $provider.name type: $provider.type available: $provider.available loaded: $provider.loaded profile_allowed: $allowed status: (if $allowed { "✅ Available" } else { "🛑 Blocked by profile" }) } if $verbose { $basic_info | merge { capabilities: $provider.capabilities server_management: ($capabilities.server_management? | default false) multi_region: ($capabilities.multi_region? | default false) auto_scaling: ($capabilities.auto_scaling? | default false) } } else { $basic_info } } } # Get deployment recommendations for multi-provider setup export def mw_suggest_deployment_strategy [ requirements: record ]: nothing -> record { log-info "Analyzing requirements for deployment strategy" "middleware" let available_providers = (list-providers --available-only) mut recommendations = { strategy: "single-provider" # default primary_provider: "" secondary_providers: [] rationale: [] } # Analyze requirements and suggest strategy if ($requirements.regions? | default [] | length) > 1 { $recommendations.strategy = "multi-provider" $recommendations.rationale = ($recommendations.rationale | append "Multi-region requirement detected") } if ($requirements.high_availability? | default false) { $recommendations.strategy = "multi-provider" $recommendations.rationale = ($recommendations.rationale | append "High availability requirement") } if ($requirements.cost_optimization? | default false) { $recommendations.rationale = ($recommendations.rationale | append "Cost optimization: consider mixing providers") } # Select primary provider based on capabilities let suitable_providers = ($available_providers | where {|p| let caps = (get-provider-capabilities-for $p.name) $caps.server_management == true }) if ($suitable_providers | length) > 0 { $recommendations.primary_provider = ($suitable_providers | get 0 | get name) if $recommendations.strategy == "multi-provider" { $recommendations.secondary_providers = ($suitable_providers | skip 1 | get name) } } log-info $"Recommended strategy: ($recommendations.strategy)" "middleware" $recommendations } # Initialize middleware when loaded export-env { # This will be set when middleware functions are first called }