240 lines
5.8 KiB
Text
240 lines
5.8 KiB
Text
|
|
# Example: Complete Deployment Configuration with Nickel + SOPS Integration
|
||
|
|
#
|
||
|
|
# This example shows the hybrid pattern:
|
||
|
|
# 1. Infrastructure config in .ncl (readable, version-controlled)
|
||
|
|
# 2. Secrets in YAML (encrypted with SOPS)
|
||
|
|
# 3. Merged at deployment time
|
||
|
|
|
||
|
|
let sops = import "schemas/security/sops/main.ncl" in
|
||
|
|
let secrets_loader = import "schemas/security/secrets-loader.ncl" in
|
||
|
|
let config_merger = import "schemas/security/config-merger.ncl" in
|
||
|
|
|
||
|
|
{
|
||
|
|
# ============================================
|
||
|
|
# STEP 1: Default Configuration
|
||
|
|
# ============================================
|
||
|
|
defaults = {
|
||
|
|
environment = "dev",
|
||
|
|
deployment_mode = "solo",
|
||
|
|
|
||
|
|
database = {
|
||
|
|
type = "postgresql",
|
||
|
|
host = "localhost",
|
||
|
|
port = 5432,
|
||
|
|
name = "myapp",
|
||
|
|
user = "app_user",
|
||
|
|
# Password placeholder - will be replaced by secrets
|
||
|
|
password = "${secret:database.password}",
|
||
|
|
ssl = false,
|
||
|
|
pool_size = 10,
|
||
|
|
},
|
||
|
|
|
||
|
|
redis = {
|
||
|
|
host = "localhost",
|
||
|
|
port = 6379,
|
||
|
|
# Password placeholder - will be replaced by secrets
|
||
|
|
password = "${secret:redis.password}",
|
||
|
|
db = 0,
|
||
|
|
ttl = 3600,
|
||
|
|
},
|
||
|
|
|
||
|
|
api = {
|
||
|
|
host = "0.0.0.0",
|
||
|
|
port = 8080,
|
||
|
|
# API key placeholder - will be replaced by secrets
|
||
|
|
api_key = "${secret:api.api_key}",
|
||
|
|
timeout = 30,
|
||
|
|
max_connections = 100,
|
||
|
|
},
|
||
|
|
|
||
|
|
tls = {
|
||
|
|
enabled = false,
|
||
|
|
# Certificate placeholders - will be replaced by secrets
|
||
|
|
certificate = "${secret:tls.certificate}",
|
||
|
|
private_key = "${secret:tls.private_key}",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# STEP 2: Environment-Specific Overrides
|
||
|
|
# ============================================
|
||
|
|
environments = {
|
||
|
|
# All environments inherit these
|
||
|
|
all = {
|
||
|
|
logging = {
|
||
|
|
level = "info",
|
||
|
|
format = "json",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# Development overrides
|
||
|
|
dev = {
|
||
|
|
database = {
|
||
|
|
host = "postgres-dev.local",
|
||
|
|
ssl = false,
|
||
|
|
},
|
||
|
|
redis = {
|
||
|
|
host = "redis-dev.local",
|
||
|
|
},
|
||
|
|
api = {
|
||
|
|
port = 8080,
|
||
|
|
},
|
||
|
|
logging = {
|
||
|
|
level = "debug",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# Staging overrides
|
||
|
|
staging = {
|
||
|
|
database = {
|
||
|
|
host = "postgres-staging.example.com",
|
||
|
|
ssl = true,
|
||
|
|
},
|
||
|
|
redis = {
|
||
|
|
host = "redis-staging.example.com",
|
||
|
|
},
|
||
|
|
api = {
|
||
|
|
port = 443,
|
||
|
|
},
|
||
|
|
tls = {
|
||
|
|
enabled = true,
|
||
|
|
},
|
||
|
|
logging = {
|
||
|
|
level = "info",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# Production overrides
|
||
|
|
prod = {
|
||
|
|
database = {
|
||
|
|
host = "postgres-prod-cluster.example.com",
|
||
|
|
port = 5432,
|
||
|
|
ssl = true,
|
||
|
|
pool_size = 50,
|
||
|
|
},
|
||
|
|
redis = {
|
||
|
|
host = "redis-prod-cluster.example.com",
|
||
|
|
},
|
||
|
|
api = {
|
||
|
|
port = 443,
|
||
|
|
max_connections = 1000,
|
||
|
|
},
|
||
|
|
tls = {
|
||
|
|
enabled = true,
|
||
|
|
},
|
||
|
|
logging = {
|
||
|
|
level = "warn",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# STEP 3: Deployment Modes
|
||
|
|
# ============================================
|
||
|
|
deployment_modes = {
|
||
|
|
solo = {
|
||
|
|
replicas = 1,
|
||
|
|
resources = {
|
||
|
|
cpu = "1",
|
||
|
|
memory = "512Mi",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
ha = {
|
||
|
|
replicas = 3,
|
||
|
|
resources = {
|
||
|
|
cpu = "2",
|
||
|
|
memory = "2Gi",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
enterprise = {
|
||
|
|
replicas = 5,
|
||
|
|
resources = {
|
||
|
|
cpu = "4",
|
||
|
|
memory = "4Gi",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# STEP 4: SOPS Configuration
|
||
|
|
# ============================================
|
||
|
|
sops_config = {
|
||
|
|
dev = (sops.generate_sops_yaml "dev"),
|
||
|
|
staging = (sops.generate_sops_yaml "staging"),
|
||
|
|
prod = (sops.generate_sops_yaml "prod"),
|
||
|
|
},
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# STEP 5: Build Final Configuration
|
||
|
|
# ============================================
|
||
|
|
# This function is called at deployment time with:
|
||
|
|
# - environment: "dev" | "staging" | "prod"
|
||
|
|
# - secrets: loaded from config/secrets/{env}.yaml (SOPS-encrypted)
|
||
|
|
# - deployment_mode: "solo" | "ha" | "enterprise"
|
||
|
|
|
||
|
|
build_config = fun environment deployment_mode secrets =>
|
||
|
|
let env_config = config_merger.by_environment @ { defaults = $.defaults, environments = $.environments } environment in
|
||
|
|
let mode_config = ($.deployment_modes | std.record.get deployment_mode | default {}) in
|
||
|
|
let base = config_merger.compose_config $.defaults env_config {} in
|
||
|
|
let with_mode = config_merger.compose_config base mode_config {} in
|
||
|
|
let final = config_merger.compose_config with_mode secrets {} in
|
||
|
|
|
||
|
|
# Merge secrets into placeholders
|
||
|
|
secrets_loader.merge final secrets,
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# Export Configuration for Different Scenarios
|
||
|
|
# ============================================
|
||
|
|
|
||
|
|
# Development configuration (for local testing)
|
||
|
|
config_dev = {
|
||
|
|
environment = "dev",
|
||
|
|
deployment_mode = "solo",
|
||
|
|
config = (
|
||
|
|
config_merger.compose_config
|
||
|
|
$.defaults
|
||
|
|
($.environments | std.record.get "dev")
|
||
|
|
{}
|
||
|
|
),
|
||
|
|
},
|
||
|
|
|
||
|
|
# Staging configuration (requires secrets)
|
||
|
|
config_staging = {
|
||
|
|
environment = "staging",
|
||
|
|
deployment_mode = "ha",
|
||
|
|
config = (
|
||
|
|
config_merger.compose_config
|
||
|
|
$.defaults
|
||
|
|
($.environments | std.record.get "staging")
|
||
|
|
{}
|
||
|
|
),
|
||
|
|
},
|
||
|
|
|
||
|
|
# Production configuration (requires secrets)
|
||
|
|
config_prod = {
|
||
|
|
environment = "prod",
|
||
|
|
deployment_mode = "enterprise",
|
||
|
|
config = (
|
||
|
|
config_merger.compose_config
|
||
|
|
$.defaults
|
||
|
|
($.environments | std.record.get "prod")
|
||
|
|
{}
|
||
|
|
),
|
||
|
|
},
|
||
|
|
|
||
|
|
# ============================================
|
||
|
|
# Validation
|
||
|
|
# ============================================
|
||
|
|
|
||
|
|
validate = fun configuration =>
|
||
|
|
let required_paths = [
|
||
|
|
"database.host",
|
||
|
|
"database.user",
|
||
|
|
"redis.host",
|
||
|
|
"api.port",
|
||
|
|
] in
|
||
|
|
config_merger.validate_complete configuration required_paths,
|
||
|
|
}
|