141 lines
5.8 KiB
Text
141 lines
5.8 KiB
Text
#!/usr/bin/env nu
|
|
# Prepare script for fip taskserv.
|
|
# Determines which FIPs apply to this server from floating_ips.ncl assignment policy,
|
|
# resolves their IP addresses, and writes FLOATING_IPS + IFACE to env-fip.
|
|
|
|
let vars_path = ($env.PROVISIONING_VARS? | default "")
|
|
if ($vars_path | is-empty) or not ($vars_path | path exists) {
|
|
print $"🛑 PROVISIONING_VARS not set or not found: ($vars_path)"
|
|
exit 1
|
|
}
|
|
|
|
let wk_data = (open $vars_path)
|
|
let iface = ($wk_data | get -o taskserv.interface | default "eth0")
|
|
let hostname = ($wk_data | get -o server.hostname | default "")
|
|
|
|
if ($hostname | is-empty) {
|
|
print "🛑 server.hostname not set in vars"
|
|
exit 1
|
|
}
|
|
|
|
# Resolve workspace root
|
|
let ws_root = do {
|
|
let explicit = ($env.PROVISIONING_WORKSPACE_PATH? | default "")
|
|
if ($explicit | is-not-empty) { $explicit } else {
|
|
let wss = ($env.PROVISIONING_WORKSPACES? | default "")
|
|
if ($wss | is-not-empty) { $wss | path dirname } else { "" }
|
|
}
|
|
}
|
|
|
|
# Load assignment policy from floating_ips.ncl
|
|
def load-fip-policy [ws_root: string]: nothing -> record {
|
|
let fips_ncl = ($ws_root | path join "infra/lib/floating_ips.ncl")
|
|
if not ($fips_ncl | path exists) { return {} }
|
|
let prov = ($env.PROVISIONING? | default ($ws_root | path join ".." ".." "provisioning" | path expand))
|
|
let import_args = ["--import-path" $prov, "--import-path" $ws_root]
|
|
let r = (do { ^nickel export --format json $fips_ncl ...$import_args } | complete)
|
|
if $r.exit_code != 0 { return {} }
|
|
$r.stdout | from json
|
|
}
|
|
|
|
# Determine which FIPs apply to this hostname based on assignment policy
|
|
def applicable-fips [fips: record, hostname: string, node_labels: record]: nothing -> list {
|
|
$fips | transpose key val | get val | where {|fip|
|
|
let mode = ($fip | get -o assignment.mode | default null)
|
|
match $mode {
|
|
"pinned" => {
|
|
($fip | get -o assignment.node | default "") == $hostname
|
|
},
|
|
"floating" => {
|
|
# Floating FIPs are owned end-to-end by fip-controller (follow-pod
|
|
# DaemonSet): it manages both the Hetzner assignment and the eth0 alias
|
|
# on the node currently running the workload. Statically pre-configuring
|
|
# them here on every can_use_fip-labelled node created a second writer,
|
|
# leaving stale aliases on non-home nodes (silent drift). The static
|
|
# taskserv now handles pinned FIPs only; cross-check with `just fip-reconcile`.
|
|
false
|
|
},
|
|
_ => { false },
|
|
}
|
|
}
|
|
}
|
|
|
|
# Load bootstrap state for IP resolution
|
|
let bs_data = do {
|
|
if ($ws_root | is-not-empty) {
|
|
let bs_path = ($ws_root | path join ".provisioning-state.json")
|
|
if ($bs_path | path exists) { open $bs_path } else { {} }
|
|
} else { {} }
|
|
}
|
|
|
|
def resolve-ip [fip_name: string, hostname: string, bs_data: record, ws_root: string]: nothing -> string {
|
|
# Bootstrap state is keyed by FIP name — always correct for multi-FIP scenarios.
|
|
# servers-state.floating_ip_address is only the server's primary assigned FIP, so
|
|
# checking it first gives the wrong IP when multiple FIPs apply to one server.
|
|
let fip_key = ($fip_name | str replace --all "librecloud-fip-" "" | str replace --all "-" "_")
|
|
let bs_rec = do -i { $bs_data.bootstrap.floating_ips | get $fip_key } | default null
|
|
if $bs_rec != null {
|
|
let addr = ($bs_rec | get -o ip | default "")
|
|
if ($addr | is-not-empty) {
|
|
print $"✓ ($fip_name) → ($addr) [bootstrap-state]"
|
|
return $addr
|
|
}
|
|
}
|
|
# Fallback: servers-state (single-FIP legacy, or bootstrap state missing)
|
|
if ($ws_root | is-not-empty) and ($hostname | is-not-empty) {
|
|
let infra_dir = ($ws_root | path join "infra")
|
|
if ($infra_dir | path exists) {
|
|
let candidates = (do -i { ls $infra_dir }
|
|
| where type == "dir"
|
|
| get name
|
|
| each {|d| $d | path join ".servers-state.json"}
|
|
| where {|p| $p | path exists})
|
|
for sc in $candidates {
|
|
let rec = (open $sc | get -o $hostname | default null)
|
|
if $rec != null {
|
|
let addr = ($rec | get -o floating_ip_address | default "")
|
|
if ($addr | is-not-empty) {
|
|
print $"✓ ($fip_name) → ($addr) [servers-state]"
|
|
return $addr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""
|
|
}
|
|
|
|
let fips = if ($ws_root | is-not-empty) { load-fip-policy $ws_root } else { {} }
|
|
|
|
let applicable = if ($fips | is-empty) {
|
|
# Fallback: legacy floating_ips array on server config
|
|
let arr = ($wk_data | get -o server.floating_ips | default [])
|
|
if ($arr | is-not-empty) {
|
|
$arr | each {|name| { name: $name }}
|
|
} else {
|
|
let singular = ($wk_data | get -o server.floating_ip | default "")
|
|
if ($singular | is-not-empty) { [{ name: $singular }] } else { [] }
|
|
}
|
|
} else {
|
|
let node_labels = ($wk_data | get -o server.node_labels | default {})
|
|
applicable-fips $fips $hostname $node_labels
|
|
}
|
|
|
|
if ($applicable | is-empty) {
|
|
print $"🛑 No FIPs applicable to ($hostname) — check floating_ips.ncl assignment policy"
|
|
exit 1
|
|
}
|
|
|
|
let resolved = ($applicable | each {|fip|
|
|
let addr = (resolve-ip $fip.name $hostname $bs_data $ws_root)
|
|
if ($addr | is-empty) {
|
|
print $"🛑 Cannot resolve IP for FIP '($fip.name)' on ($hostname) — run: provisioning bootstrap"
|
|
exit 1
|
|
}
|
|
$addr
|
|
})
|
|
|
|
let floating_ips_str = ($resolved | str join " ")
|
|
let out_dir = ($env.PROVISIONING_WK_ENV_PATH? | default ".")
|
|
$"IFACE=($iface)\nFLOATING_IPS=\"($floating_ips_str)\"\n" | save -f ($out_dir | path join "env-fip")
|
|
print $"✓ env-fip: IFACE=($iface) FLOATING_IPS=($floating_ips_str)"
|