Jesús Pérez 85ce530733
feat: update provisioning core CLI, libraries, and plugins
Update core components including CLI, Nushell libraries, plugins system,
and utility scripts for the provisioning system.

CLI Updates:
- Command implementations
- CLI utilities and dispatching
- Help system improvements
- Command validation

Library Updates:
- Configuration management system
- Infrastructure validation
- Extension system improvements
- Secrets management
- Workspace operations
- Cache management system

Plugin System:
- Interactive form plugin (inquire)
- KCL integration plugin
- Performance optimization plugins
- Plugin registration system

Utilities:
- Build and distribution scripts
- Installation procedures
- Testing utilities
- Development tools

Documentation:
- Library module documentation
- Extension API guides
- Plugin usage guides
- Service management documentation

All changes are backward compatible. No breaking changes.
2025-12-11 21:57:05 +00:00

357 lines
12 KiB
Plaintext

# Provider Configuration Module
# Manages infrastructure provider setup and configuration (UpCloud, AWS, Hetzner)
# Follows Nushell guidelines: explicit types, single purpose, no try-catch
use ./mod.nu *
use ./validation.nu *
# ============================================================================
# PROVIDER VALIDATION
# ============================================================================
# Check if provider is available in current workspace
export def is-provider-available [
provider_name: string
workspace_path: string
]: nothing -> bool {
let provider_config = $"($workspace_path)/config/providers/($provider_name).toml"
($provider_config | path exists)
}
# Get list of available providers
export def get-available-providers [
config_base: string
]: nothing -> list<string> {
let providers_dir = $"($config_base)/providers"
if not ($providers_dir | path exists) {
return []
}
let result = (do {
ls -la $providers_dir | where name =~ r'\.toml$' | get name | str replace ".toml" ""
} | complete)
if ($result.exit_code == 0) {
$result.stdout | split row "\n" | where { |x| ($x | str length) > 0 }
} else {
[]
}
}
# ============================================================================
# PROVIDER CONFIGURATION FILES
# ============================================================================
# Create UpCloud provider configuration
export def create-upcloud-config [
config_base: string
]: nothing -> bool {
let provider_config = $"($config_base)/providers/upcloud.toml"
let upcloud_config = {
api_url: "https://api.upcloud.com/1.3"
interface: "API"
credentials_source: "rustyvault://system/providers/upcloud"
timeout_seconds: 30
zones: ["de-fra1", "nl-ams1", "sg-sin1", "us-nyc1", "us-sfo1", "gb-lnd1"]
}
save-config-toml $provider_config $upcloud_config
}
# Create AWS provider configuration
export def create-aws-config [
config_base: string
]: nothing -> bool {
let provider_config = $"($config_base)/providers/aws.toml"
let aws_config = {
region: "us-east-1"
credentials_source: "rustyvault://system/providers/aws"
timeout_seconds: 30
regions: ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"]
}
save-config-toml $provider_config $aws_config
}
# Create Hetzner provider configuration
export def create-hetzner-config [
config_base: string
]: nothing -> bool {
let provider_config = $"($config_base)/providers/hetzner.toml"
let hetzner_config = {
api_url: "https://api.hetzner.cloud/v1"
credentials_source: "rustyvault://system/providers/hetzner"
timeout_seconds: 30
locations: ["fsn1", "nbg1", "ash", "hel1", "sin"]
}
save-config-toml $provider_config $hetzner_config
}
# Create local provider configuration
export def create-local-config [
config_base: string
]: nothing -> bool {
let provider_config = $"($config_base)/providers/local.toml"
let local_config = {
base_path: "/tmp/provisioning-local"
timeout_seconds: 10
}
save-config-toml $provider_config $local_config
}
# ============================================================================
# PROVIDER SETUP
# ============================================================================
# Setup provider configuration
export def setup-provider [
provider_name: string
config_base: string = ""
--interactive = false
]: nothing -> record {
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
# Validate provider name
let validation = (validate-provider-name $provider_name)
if not $validation.valid {
print-setup-error $validation.error
return {
success: false
error: $validation.error
}
}
print-setup-header $"Setting up ($provider_name) Provider"
print ""
# Create provider configuration
let creation_result = (match $provider_name {
"upcloud" => { create-upcloud-config $base }
"aws" => { create-aws-config $base }
"hetzner" => { create-hetzner-config $base }
"local" => { create-local-config $base }
_ => { false }
})
if not $creation_result {
print-setup-error $"Failed to create ($provider_name) provider configuration"
return {
success: false
error: $"Failed to create provider configuration for ($provider_name)"
}
}
print-setup-success $"($provider_name) provider configuration created"
print-setup-info "Credentials will be loaded from RustyVault at runtime"
let timestamp_value = (get-timestamp-iso8601)
{
success: true
provider: $provider_name
config_path: $"($base)/providers/($provider_name).toml"
timestamp: $timestamp_value
}
}
# Setup all selected providers
export def setup-providers [
providers: list<string>
config_base: string = ""
]: nothing -> record {
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
print-setup-header "Setting up Providers"
print ""
mut results = []
mut errors = []
for provider in $providers {
print-setup-info $"Configuring ($provider)..."
let result = (setup-provider $provider $base)
if $result.success {
$results = ($results | append $result)
print-setup-success $"($provider) configured"
} else {
$errors = ($errors | append $result.error)
print-setup-warning $result.error
}
}
print ""
let success_status = ($errors | length) == 0
let provider_list = ($results | map { |r| $r.provider })
let configured_count = ($results | length)
let error_count = ($errors | length)
let timestamp_value = (get-timestamp-iso8601)
{
success: $success_status
providers: $provider_list
configured_count: $configured_count
error_count: $error_count
errors: $errors
timestamp: $timestamp_value
}
}
# ============================================================================
# PROVIDER CREDENTIALS (References to RustyVault)
# ============================================================================
# Get provider credentials reference
export def get-provider-credentials-reference [
provider_name: string
workspace_name: string = "system"
]: nothing -> string {
$"rustyvault://($workspace_name)/providers/($provider_name)"
}
# Validate credentials reference format
export def validate-credentials-reference [
credentials_source: string
]: nothing -> record {
let is_valid = (
($credentials_source | str starts-with "rustyvault://") or
($credentials_source | str starts-with "vault://") or
($credentials_source | str starts-with "kms://")
)
let error_message = (if not $is_valid {
"Credentials source must start with rustyvault://, vault://, or kms://"
} else {
null
})
{
value: $credentials_source
valid: $is_valid
error: $error_message
}
}
# ============================================================================
# PROVIDER INFORMATION
# ============================================================================
# Print provider setup instructions
export def print-provider-setup-instructions []: nothing -> nothing {
print ""
print "╔═══════════════════════════════════════════════════════════════╗"
print "║ PROVIDER SETUP INSTRUCTIONS ║"
print "╚═══════════════════════════════════════════════════════════════╝"
print ""
print "📋 PROVIDER CREDENTIALS"
print "─────────────────────────────────────────────────────────────"
print ""
print "Provider credentials are stored SECURELY in RustyVault with:"
print " ✅ Encryption at rest (AES-256)"
print " ✅ Dynamic secrets with TTL"
print " ✅ Automatic rotation policies"
print " ✅ Audit logging of all access"
print ""
print "UpCloud Provider"
print "─────────────────────────────────────────────────────────────"
print "To setup UpCloud credentials in RustyVault:"
print ""
print " 1. Obtain API credentials from https://hub.upcloud.com/account"
print " 2. Store in RustyVault:"
print " provisioning secrets store upcloud-creds \\"
print " --username <username> \\"
print " --password <api-token>"
print ""
print " 3. Credentials available as: rustyvault://system/providers/upcloud"
print ""
print "AWS Provider"
print "─────────────────────────────────────────────────────────────"
print "To setup AWS credentials in RustyVault:"
print ""
print " 1. Create IAM user with programmatic access"
print " 2. Store in RustyVault:"
print " provisioning secrets store aws-creds \\"
print " --access-key-id <key-id> \\"
print " --secret-access-key <secret-key> \\"
print " --ttl 3600"
print ""
print " 3. Credentials available as: rustyvault://system/providers/aws"
print ""
print "Hetzner Provider"
print "─────────────────────────────────────────────────────────────"
print "To setup Hetzner credentials in RustyVault:"
print ""
print " 1. Create API token from https://console.hetzner.cloud"
print " 2. Store in RustyVault:"
print " provisioning secrets store hetzner-creds \\"
print " --api-token <token>"
print ""
print " 3. Credentials available as: rustyvault://system/providers/hetzner"
print ""
print "Local Provider"
print "─────────────────────────────────────────────────────────────"
print "Local provider requires no credentials - perfect for testing!"
print ""
print "═══════════════════════════════════════════════════════════════"
print ""
}
# Print available providers
export def print-available-providers [
config_base: string = ""
]: nothing -> nothing {
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
let available = (get-available-providers $base)
print ""
print "Configured Providers:"
print "─────────────────────────────────────────────────────────────"
if ($available | length) == 0 {
print " (No providers configured yet)"
} else {
for provider in $available {
let config_path = $"($base)/providers/($provider).toml"
print $" ✅ ($provider)"
print $" Path: ($config_path)"
}
}
print ""
}
# Get provider information
export def get-provider-info [
provider_name: string
config_base: string = ""
]: nothing -> record {
let base = (if ($config_base == "") { (get-config-base-path) } else { $config_base })
let config_path = $"($base)/providers/($provider_name).toml"
let config = if ($config_path | path exists) {
load-config-toml $config_path
} else {
{}
}
let exists_status = ($config_path | path exists)
{
provider: $provider_name
exists: $exists_status
config_path: $config_path
configuration: $config
}
}