357 lines
12 KiB
Plaintext
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
|
||
|
|
}
|
||
|
|
}
|