provisioning/scripts/config-deploy.nu

315 lines
9.6 KiB
Text
Raw Permalink Normal View History

#!/usr/bin/env nu
# Configuration Deployment Orchestrator
# Orchestrates Nickel configuration + SOPS secrets workflow
# Handles: config generation → secrets decryption → deployment
# Usage: config-deploy <action> [options]
use std log
def get-vault-url [] {
$env.VAULT_SERVICE_URL? // "http://localhost:9094"
}
def get-vault-token [] {
$env.VAULT_SERVICE_TOKEN? // ""
}
def check-nickel [] {
let result = (^nickel --version | complete)
if $result.exit_code != 0 {
error make {
msg: "Nickel not found or not in PATH"
label: {
text: "Install Nickel or add to PATH: https://github.com/tweag/nickel"
}
}
}
true
}
def check-sops [] {
let result = (^sops --version | complete)
if $result.exit_code != 0 {
error make {
msg: "SOPS not found or not in PATH"
label: {
text: "Install SOPS: https://github.com/mozilla/sops"
}
}
}
true
}
def validate-environment [environment: string] {
if $environment not-in ["dev", "staging", "prod"] {
error make {
msg: "Invalid environment"
label: {
text: "Must be: dev, staging, or prod"
}
}
}
true
}
def decrypt-secrets [environment: string] {
let secrets_file = $"provisioning/config/secrets/secrets.($environment).yaml.enc"
if not ($secrets_file | path exists) {
print $"⚠️ Secrets file not found: ($secrets_file)"
return {}
}
print $"Decrypting secrets for ($environment)..."
let token = (get-vault-token)
if ($token | is-empty) {
error make {
msg: "VAULT_SERVICE_TOKEN required for secret decryption"
label: {
text: "Set environment variable: export VAULT_SERVICE_TOKEN=..."
}
}
}
let output_file = $"provisioning/config/secrets/secrets.($environment).yaml"
let result = (
nu provisioning/scripts/secrets-decrypt.nu $secrets_file --environment $environment --output $output_file
)
if not ($result.success) {
error make {
msg: "Failed to decrypt secrets"
label: {
text: $result
}
}
}
print "✓ Secrets decrypted successfully"
$output_file
}
def generate-nickel-config [config_path: string, environment: string, secrets_path: string] {
print "Generating configuration from Nickel..."
let result = (
^nickel eval -f json provisioning/schemas/examples/deployment-with-secrets.ncl | complete
)
if $result.exit_code != 0 {
error make {
msg: "Nickel evaluation failed"
label: {
text: $result.stderr
}
}
}
print "✓ Configuration generated"
$result.stdout | from json
}
def validate-config [config: record] {
print "Validating configuration..."
let required_keys = ["database", "redis", "api"]
let missing = (
$required_keys
| where {|key| not ($key in ($config | keys))}
)
if ($missing | length) > 0 {
let missing_str = ($missing | str join ", ")
error make {
msg: "Configuration validation failed"
label: {
text: $"Missing required sections: ($missing_str)"
}
}
}
print "✓ Configuration validated"
true
}
def create-deployment-package [config: record, environment: string, output_dir: string] {
print $"Creating deployment package for ($environment)..."
if not ($output_dir | path exists) {
^mkdir -p $output_dir
}
# Write configuration as JSON
let config_file = $"($output_dir)/config.json"
$config | to json | save -f $config_file
print $"✓ Config: ($config_file)"
# Write configuration as YAML (for readability)
let yaml_file = $"($output_dir)/config.yaml"
# Note: Would use actual JSON-to-YAML converter in production
$config | to json | save -f $yaml_file
print $"✓ Config: ($yaml_file)"
# Create deployment manifest
let manifest = {
environment: $environment,
timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ"),
config_version: "1.0",
components: [
{ name: "database", host: ($config.database.host // "unknown") },
{ name: "redis", host: ($config.redis.host // "unknown") },
{ name: "api", port: ($config.api.port // 0) },
],
}
let manifest_file = $"($output_dir)/manifest.json"
$manifest | to json | save -f $manifest_file
print $"✓ Manifest: ($manifest_file)"
$output_dir
}
def main [
action: string = "help"
--environment: string = "dev"
--config-path: string = "provisioning/schemas/examples/deployment-with-secrets.ncl"
--output-dir: string = "deploy/output"
--clean
] {
match $action {
"validate-tools" => {
print "Checking required tools..."
check-nickel
print "✓ Nickel: installed"
check-sops
print "✓ SOPS: installed"
print ""
print "All tools present"
},
"generate" => {
validate-environment $environment
print ""
print $"====== Generate Configuration for ($environment) ======"
print ""
print "Step 1: Checking tools..."
check-nickel
check-sops
print "✓ Tools validated"
print ""
print "Step 2: Decrypting secrets..."
let secrets_file = (decrypt-secrets $environment)
print ""
print "Step 3: Generating configuration..."
let config = (generate-nickel-config $config_path $environment $secrets_file)
print ""
print "Step 4: Validating configuration..."
validate-config $config
print ""
print "Step 5: Creating deployment package..."
let output = (create-deployment-package $config $environment $output_dir)
print ""
print $"====== Configuration Ready for Deployment ======"
print $"Output directory: ($output)"
print ""
if $clean {
print "Cleaning up decrypted secrets..."
rm -f $secrets_file
print "✓ Secrets cleaned up"
}
},
"validate-nickel" => {
print "Validating Nickel configuration..."
let result = (^nickel check $config_path | complete)
if $result.exit_code == 0 {
print "✓ Nickel configuration valid"
} else {
error make {
msg: "Nickel validation failed"
label: {
text: $result.stderr
}
}
}
},
"list-environments" => {
print "Available environments:"
["dev", "staging", "prod"] | each {|env_name|
let secrets_file = $"provisioning/config/secrets/secrets.($env_name).yaml.enc"
let exists = if ($secrets_file | path exists) { "✓" } else { "✗" }
print $" ($exists) ($env_name) - ($secrets_file)"
}
},
"status" => {
print ""
print "====== Deployment Configuration Status ======"
print ""
print "Tools:"
(check-nickel) | if $in { print " ✓ Nickel" } else { print " ✗ Nickel" }
(check-sops) | if $in { print " ✓ SOPS" } else { print " ✗ SOPS" }
print ""
print "Vault Service:"
print $" URL: (get-vault-url)"
print $" Token: $(if (get-vault-token | is-empty) { 'not set' } else { 'set' })"
print ""
print "Configuration:"
print $" Nickel config: ($config_path)"
print $" Output dir: ($output_dir)"
print ""
print "Secrets:"
(main "list-environments")
print ""
},
"help" => {
print "Configuration Deployment Orchestrator"
print ""
print "Combines Nickel infrastructure-as-code with SOPS secrets"
print ""
print "Usage: config-deploy <action> [--environment dev|staging|prod] [--config-path <path>] [--output-dir <dir>]"
print ""
print "Actions:"
print " validate-tools - Check Nickel and SOPS installation"
print " validate-nickel - Validate Nickel configuration syntax"
print " list-environments - List available secret environments"
print " status - Show deployment configuration status"
print " generate - Full workflow: decrypt → generate → validate → package"
print " help - Show this help message"
print ""
print "Examples:"
print " config-deploy validate-tools"
print " config-deploy status"
print " config-deploy generate --environment prod --clean"
print " config-deploy generate --environment dev --output-dir ./deploy/dev"
print ""
print "Environment Variables:"
print " VAULT_SERVICE_URL - Vault endpoint (default: http://localhost:9094)"
print " VAULT_SERVICE_TOKEN - Vault authentication token (required for decrypt)"
print " PROVISIONING_ENV - Environment (dev, staging, prod)"
print ""
},
_ => {
print $"Unknown action: ($action)"
print "Run 'config-deploy help' for available commands"
}
}
}