108 lines
4.8 KiB
Text
Executable file
108 lines
4.8 KiB
Text
Executable file
#!/usr/bin/env nu
|
|
# Prepare: decrypt SOPS SMB credentials and deploy one k8s Secret per declared
|
|
# StorageClass before install. Each storage_classes[].secret_name must have a
|
|
# matching <secret_name>.sops.yaml with 'username' and 'password' keys —
|
|
# provisioned out-of-band, never generated by this script.
|
|
# Runs on the provisioning machine. SSH to server to create the secrets.
|
|
|
|
let vars_path = ($env.PROVISIONING_VARS? | default "")
|
|
if ($vars_path | is-empty) or not ($vars_path | path exists) { exit 0 }
|
|
|
|
let wk_data = (open $vars_path)
|
|
let storage_classes = ($wk_data | get -o taskserv.storage_classes | default [])
|
|
let hostname = ($wk_data | get -o server.hostname | default "")
|
|
# Public IP first; fall back to the private IP for VPN-only control planes
|
|
# (libre-wuji-cp-0 has no public_ip — reachable only over the WireGuard subnet).
|
|
let server_ip = do {
|
|
let p = ($wk_data | get -o server.ip_addresses.pub | default "")
|
|
if ($p | is-not-empty) { $p } else { ($wk_data | get -o server.ip_addresses.priv | default "") }
|
|
}
|
|
|
|
if ($storage_classes | is-empty) {
|
|
print "⚠ prepare: no storage_classes declared — nothing to deploy"
|
|
exit 0
|
|
}
|
|
|
|
# The infra path (secrets live at <infra_path>/secrets/). The runner passes this
|
|
# RELATIVE ("infra/libre-wuji") resolved from CWD=workspace root; `path expand`
|
|
# makes it absolute. Do NOT derive it from PROVISIONING_WORKSPACES via
|
|
# `path dirname` — that env is relative ("infra") and dirname collapses to "",
|
|
# which silently skipped every secret deploy this component ever ran.
|
|
let infra_path = do {
|
|
let src = ($env.PROVISIONING_SETTINGS_SRC_PATH? | default "")
|
|
if ($src | is-not-empty) { $src | path expand } else {
|
|
let kloud = ($env.PROVISIONING_KLOUD_PATH? | default "")
|
|
if ($kloud | is-not-empty) { $kloud | path expand } else { "" }
|
|
}
|
|
}
|
|
|
|
if ($infra_path | is-empty) or ($server_ip | is-empty) {
|
|
print $"⚠ prepare: cannot resolve infra path or server IP — skipping secret deploy"
|
|
exit 0
|
|
}
|
|
|
|
let kage_path = if ($env.SOPS_AGE_KEY_FILE? | default "" | is-not-empty) {
|
|
$env.SOPS_AGE_KEY_FILE
|
|
} else {
|
|
let kloud_path = ($env.PROVISIONING_KLOUD_PATH? | default "")
|
|
if ($kloud_path | is-not-empty) and ($kloud_path | path join ".kage" | path exists) {
|
|
$kloud_path | path join ".kage"
|
|
} else { "" }
|
|
}
|
|
|
|
let settings_src = ($env.PROVISIONING_SETTINGS_SRC_PATH? | default "")
|
|
let ssh_key = if ($settings_src | is-not-empty) {
|
|
let key = ($env.PROVISIONING_SSH_KEY? | default "~/.ssh/htz_ops")
|
|
$key | str replace "~" $env.HOME
|
|
} else { "" }
|
|
|
|
let ssh_opts = "-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10"
|
|
let ssh_opts = if ($ssh_key | is-not-empty) and ($ssh_key | path exists) {
|
|
$"($ssh_opts) -i ($ssh_key)"
|
|
} else { $ssh_opts }
|
|
|
|
for sc in $storage_classes {
|
|
let secret_name = ($sc | get -o secret_name | default "")
|
|
let namespace = ($sc | get -o secret_namespace | default "kube-system")
|
|
if ($secret_name | is-empty) { continue }
|
|
|
|
let sops_file = ($infra_path | path join "secrets" | path join $"($secret_name).sops.yaml")
|
|
|
|
if not ($sops_file | path exists) {
|
|
print $"⚠ prepare: ($sops_file) not found — secret must exist on cluster"
|
|
continue
|
|
}
|
|
|
|
let decrypted = if ($kage_path | is-not-empty) {
|
|
(do -i { with-env { SOPS_AGE_KEY_FILE: $kage_path } { ^sops -d $sops_file } } | default "")
|
|
} else {
|
|
(do -i { ^sops -d $sops_file } | default "")
|
|
}
|
|
if ($decrypted | is-empty) {
|
|
print $"⚠ prepare: failed to decrypt ($sops_file)"
|
|
continue
|
|
}
|
|
|
|
let creds = ($decrypted | from yaml)
|
|
let username = ($creds | get -o username | default "")
|
|
let password = ($creds | get -o password | default "")
|
|
if ($username | is-empty) or ($password | is-empty) {
|
|
print $"⚠ prepare: username/password not found in ($sops_file)"
|
|
continue
|
|
}
|
|
|
|
# Detect the kubectl entrypoint on the target (libre-wuji is kubeadm → plain
|
|
# kubectl; k0s clusters expose `k0s kubectl`), instead of assuming k0s.
|
|
let kube = do {
|
|
let probe = (do -i { ^ssh ...($ssh_opts | split row " ") $"root@($server_ip)" "command -v k0s >/dev/null 2>&1 && echo k0s || echo kubectl" } | complete)
|
|
if (($probe.stdout | str trim) == "k0s") { "k0s kubectl" } else { "kubectl" }
|
|
}
|
|
let cmd = $"($kube) create namespace ($namespace) --dry-run=client -o yaml | ($kube) apply -f - && ($kube) -n ($namespace) create secret generic ($secret_name) --from-literal=username=($username) --from-literal=password=($password) --dry-run=client -o yaml | ($kube) apply -f -"
|
|
let res = (do -i { ^ssh ...($ssh_opts | split row " ") $"root@($server_ip)" $cmd } | complete)
|
|
|
|
if $res.exit_code == 0 {
|
|
print $"✓ prepare: ($secret_name) secret deployed to ($namespace) on ($hostname)"
|
|
} else {
|
|
print $"⚠ prepare: failed to deploy ($secret_name) — ($res.stderr)"
|
|
}
|
|
}
|