provisioning-core/nulib/domain/cluster/access.nu

66 lines
3.8 KiB
Text
Raw Normal View History

# Cluster kube-apiserver access resolution — extracted from cli/components.nu (ADR-026 slice 2a-iii).
use tools/nickel/process.nu [comp-ncl-export]
use domain/workspace/resolve.nu [comp-state-server comp-ws-path comp-ws-infra]
use platform/clients/ssh.nu [comp-server-ssh-info comp-ssh-run]
# Resolve how to reach the cluster's kube-apiserver for an infra.
# provisioning NEVER runs kubectl locally: the API server is reachable only from
# the control-plane node. The SSH host and remote kubectl invocation are declared
# in settings.cluster_access (ssh_host + remote_kubectl). When unset, fall back to
# the first server present in the state file and `k0s kubectl`.
export def comp-kubectl-access [ws_root: string, infra: string]: nothing -> record {
let raw = (comp-ncl-export $ws_root $"infra/($infra)/settings.ncl")
let s = ($raw | get -o settings | default {})
let ca = ($s | get -o cluster_access | default {})
# Operator-delegated workspaces (adr-002) own no cluster — they deploy onto a
# target cluster. With no local cluster_access, resolve the target workspace's
# access so kubectl reads hit the operator cluster's control-plane, not a CP
# this workspace does not have. Guarded: a failed cross-workspace resolve
# falls through to the local default rather than aborting the caller.
let target_ws = ($s | get -o deployment_target | default {}
| get -o target_cluster | default {} | get -o workspace | default "")
if ($ca | is-empty) and ($target_ws | is-not-empty) {
let delegated = (try {
let tgt_root = (comp-ws-path $target_ws)
if ($tgt_root | is-not-empty) and ($tgt_root != $ws_root) and ($tgt_root | path exists) {
comp-kubectl-access $tgt_root (comp-ws-infra $tgt_root)
} else { null }
} catch { null })
if ($delegated != null) { return $delegated }
}
let host = ($ca | get -o ssh_host | default "")
let kc = ($ca | get -o remote_kubectl | default "k0s kubectl")
let resolved_host = if ($host | is-not-empty) { $host } else {
let state_root = if (($ws_root | path join "infra" $infra ".provisioning-state.ncl") | path exists) {
$ws_root | path join "infra" $infra
} else { $ws_root }
comp-state-server $state_root
}
# ws_root/infra identify the workspace that OWNS the resolved host, so callers
# resolve SSH connection details (ip/user/key) from the right servers.ncl even
# when access was delegated from an operator-delegated workspace.
{ host: $resolved_host, kubectl: $kc, ws_root: $ws_root, infra: $infra }
}
# Run a remote kubectl command via SSH using the infra's declared cluster_access.
# Hard-bounded so it can never hang: ConnectTimeout caps the connect phase,
# ServerAlive kills a half-dead session, and a remote `timeout` caps the kubectl
# call itself. Returns the {exit_code, stdout, stderr} record from `complete`.
export def comp-kubectl-ssh [
ws_root: string,
infra: string,
kargs: string, # kubectl arguments, e.g. "get pods -A -o json"
--cmd-timeout: int = 20, # remote wall-clock cap on the kubectl invocation
]: nothing -> record {
let access = (comp-kubectl-access $ws_root $infra)
if ($access.host | is-empty) {
return { exit_code: 1, stdout: "", stderr: "cluster_access.ssh_host unset and no server in state file" }
}
# Resolve SSH details from the workspace that owns the host (the operator
# cluster when access was delegated), not necessarily the calling workspace.
let info = (comp-server-ssh-info $access.ws_root $access.infra $access.host)
let remote = $"export PATH=/usr/local/bin:/usr/bin:/bin:$PATH; timeout ($cmd_timeout) ($access.kubectl) ($kargs)"
comp-ssh-run $info.user $info.key $info.host $remote
}