provisioning-catalog/components/cert_manager/cluster/vars.nu

109 lines
4.2 KiB
Text

#!/usr/bin/env nu
# Derived variables for cert_manager bundle rendering.
# cf_api_token is injected by components.nu via SOPS decryption before this script runs.
# CF_TOKEN_<ZONE> env var still works as an override (CI/CD pipelines).
# Renders per-issuer blocks into flat strings for {{var}} substitution.
def render_solver [s: record]: nothing -> string {
# selector must nest INSIDE this solver's own list item, as a sibling of
# dns01 (cert-manager's Solver type) — not a sibling of the "solvers"
# array, which YAML would otherwise happily accept with no warning while
# cert-manager silently misapplies it. Built as an explicit line list so
# indentation is exact regardless of which fields are present.
let dns_zones = ($s.dns_zones? | default [])
let lines = (
if ($dns_zones | is-not-empty) {
let zone_lines = ($dns_zones | each {|z| $" - \"($z)\"" })
[" - selector:", " dnsZones:"] ++ $zone_lines ++ [" dns01:"]
} else {
[" - dns01:"]
}
) ++ [
" cloudflare:",
" apiTokenSecretRef:",
$" name: ($s.secret_ref)",
" key: api-token",
]
$lines | str join "\n"
}
# One issuer can carry multiple DNS-01 solvers — a catch-all (no dns_zones)
# plus zone-scoped ones for certs whose SANs span more than one Cloudflare
# zone. issuer.solvers takes precedence; issuer.secret_ref (single, legacy
# shape) is used only when solvers is absent, for backward compatibility.
def render_acme_issuer [issuer: record]: nothing -> string {
let server = ($issuer.acme_server? | default "https://acme-v02.api.letsencrypt.org/directory")
let email = ($issuer.email? | default "")
let solvers = if ($issuer.solvers? | default [] | is-not-empty) {
$issuer.solvers
} else {
[{ secret_ref: ($issuer.secret_ref? | default "") }]
}
let solvers_block = ($solvers | each {|s| render_solver $s } | str join "\n")
$"apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ($issuer.name)
spec:
acme:
server: ($server)
email: ($email)
privateKeySecretRef:
name: ($issuer.name)-acme-key
solvers:
($solvers_block)"
}
def cf_token_from_env [secret_ref: string]: nothing -> string {
let env_var = ($"CF_TOKEN_($secret_ref | str upcase | str replace --all '-' '_')")
$env | get -o $env_var | default ""
}
def main [component_json: string]: nothing -> nothing {
let d = ($component_json | from json)
let ns = ($d.namespace? | default "cert-manager")
let version = ($d.version? | default "v1.19.5")
let issuers_block = (
$d.cluster_issuers?
| default []
| where { |i| ($i.kind? | default "") == "acme" }
| each { |i| render_acme_issuer $i }
| str join "\n---\n"
)
let acme_issuers = (
$d.cluster_issuers?
| default []
| where { |i| ($i.kind? | default "") == "acme" }
)
let cf_secret_ref = (
if ($acme_issuers | length) > 0 {
$acme_issuers | first | get -o secret_ref | default ""
} else {
""
}
)
let injected = ($d.cf_api_token? | default "")
let cf_token = if ($injected | is-not-empty) { $injected } else { cf_token_from_env $cf_secret_ref }
let tls_backup = ($d.tls_backup? | default {})
{
namespace: $ns,
version: $version,
issuers_block: $issuers_block,
cf_secret_ref: $cf_secret_ref,
cf_api_token: $cf_token,
restic_repository: ($env.RESTIC_REPOSITORY? | default ""),
restic_password: ($env.RESTIC_PASSWORD? | default ""),
aws_access_key_id: ($env.AWS_ACCESS_KEY_ID? | default ""),
aws_secret_access_key: ($env.AWS_SECRET_ACCESS_KEY? | default ""),
aws_default_region: ($env.AWS_DEFAULT_REGION? | default ""),
tls_backup_enabled: ($tls_backup.enabled? | default false),
tls_backup_kubectl_version: ($tls_backup.kubectl_version? | default "v1.32.3"),
tls_backup_nushell_image: ($tls_backup.nushell_image? | default "ghcr.io/nushell/nushell:latest"),
}
| to json --raw
| print
}