4import * as _ from 'lodash-es' 9/** Common props for functions requiring cluster_name */ 10export type WithClusterName = { cluster_name: string } 12/** Props for functions requiring cluster_name + app/resource name */ 13export type WithClusterAndName = WithClusterName & { name: string } 15/** Props for functions requiring cluster_name + k8s resource */ 16export type KubeResource = { metadata: { name: string, namespace?: string }, kind?: string, apiVersion?: string, spec?: Record<string, unknown> } 17export type WithClusterAndResource = WithClusterName & { resource: KubeResource } 19/** Per-cluster node ports config from nodeports/*.mjs */ 20export type NodePortsCfg = { 21 nodeIpsExtHost?: string 24 openPortNoA?: import('../../../sharedF/portInOpenAAI.mts').OpenPortNoA 25 lanHostIpsH?: { clusterVip?: string; [hostname: string]: string | undefined } 26 [svcName: string]: { isCnPg?: boolean; ptNodePortNo?: number; needsKubePortFwd?: boolean } | string | { clusterVip?: string; [k: string]: string | undefined } | import('../../../sharedF/portInOpenAAI.mts').OpenPortNoA | undefined 29/** Per-cluster storage-class policy. Absent → PVCs use the cluster's default class. */ 30export type StorClassGenFn = (props: { useM2?: boolean, pvcReplicaCnt?: number, [key: string]: any }) => { pvcStorClassName: string } 32/** Cluster infrastructure config - rarely changes per-app within a cluster */ 33export type KlusterCfg = WithClusterName & { 35 dockregPoolA?: string[] 37 regcacheEnabled?: boolean 39 regcacheLanHost?: string 40 useZotOverDockreg?: boolean 41 nodeIpsExtHost?: string 44 klusterVipHostname?: string 46 storClassGenFn?: StorClassGenFn 47 pvcReplicaCnt?: number 48 allNodePorts?: Record<string, NodePortsCfg> 49 klustCertsH?: Record<string, string | string[]> 50 domainNames?: string[] 51 cfApiKeySecretName?: string 53 kubeTlsInsecure?: boolean 54 extraActions?: ActionsH 57 sshNodeHostname?: string 60 * Reachability caveat for this cluster. Mutually exclusive dual-purpose: 61 * - string: cluster is known DOWN (value = reason). Reads behave as "not found"; mutating ops throw instantly. 62 * - number: cluster is up but slow/flaky; used as the per-request timeout in seconds (overrides 20s default). 63 * - absent: healthy; requests capped at defaultKubeTimeoutSec. 65 reachability?: string | number 70/** Import-time registry of cluster configs (populated by each klustersF/<name>/common.mjs). Enables 71 * cross-cluster lookups (reachability, LAN-rewrite) in entrypoints that never enter a klusterCtx, 72 * e.g. zhong jobs that only import an ethStatusCfg. */ 73const klusterCfgRegistry = new Map<string, KlusterCfg>() 75export const registerKlusterCfg = (kcfg: KlusterCfg) => { 76 const prev = klusterCfgRegistry.get(kcfg.cluster_name) 77 klusterCfgRegistry.set(kcfg.cluster_name, prev ? {...prev, ...kcfg} : kcfg) 80/** Resolve a registry hostname alias to its canonical external host. A registry is one credential 81 * identity; LAN/derived hostnames are network aliases. Without this, `getCreds` mints a second 82 * cred file per alias (different password) and registry auth (htpasswd) no longer matches. */ 83export const getDockregCanonicalHost = (host: string): string => { 85 if (ctx?.dockLanHost === host && ctx.dockreg_host) return ctx.dockreg_host 86 for (const cfg of klusterCfgRegistry.values()) if (cfg.dockLanHost === host && cfg.dockreg_host) return cfg.dockreg_host 90export const getKlusterCtx = (mergeH ?: object) => { 91 const store = klusterCtx.getStore() ?? getAppCfg() ?? {} 95export const getReqKlusterCtx = (): KlusterCfg => { 96 const store = klusterCtx.getStore() 102 * Lookup a specific cluster's config by name. Use this when operating on a cluster 103 * that may differ from the currently-active one (e.g. cross-cluster db ops). 105 * 1. `allKlusters` registry on appCfg (set by `enterKlusterCtxs` or `js3DevAppCfg`) 106 * 2. `klusterCtx` if its `cluster_name` matches the request 107 * 3. `appCfg` if its `cluster_name` matches the request 108 * 4. `klusterCfgRegistry` (populated at import time by klustersF/<name>/common.mjs) 109 * 5. `{}` (so destructuring is safe; caller should assertDefined on required fields) 111export const getCurKlusterCfg = (cluster_name: string): Partial<KlusterCfg> => { 113 const registered = _.get(getAppCfg(), ['allKlusters', cluster_name]) as KlusterCfg | undefined 114 if (registered) return registered 115 const ctxStore = klusterCtx.getStore() 116 if (ctxStore?.cluster_name === cluster_name) return ctxStore 118 if (appCfg?.cluster_name === cluster_name) return appCfg as Partial<KlusterCfg> 119 const imported = klusterCfgRegistry.get(cluster_name) 120 if (imported) return imported 124/** Get node ports config for a cluster, checking both klusterCtx and appCfgCtx */ 125export const getNodePortsCfg = (cluster_name?: string): NodePortsCfg | undefined => { 126 if (!cluster_name) return undefined 127 const ctx = klusterCtx.getStore() 129 return ctx?.allNodePorts?.[cluster_name] ?? appCfg?.allNodePorts?.[cluster_name] 132export const enterKlusterCtxs = (klusterCfg: KlusterCfg, opts?: {importMetaUrl?: string}) => { 133 klusterCtx.enterWith(klusterCfg) 134 appCfgCtx.enterWith({ 136 allKlusters: {[klusterCfg.cluster_name]: klusterCfg}, 137 importMetaUrl: opts?.importMetaUrl as AppCfg['importMetaUrl'], 139 if (klusterCfg.extraActions) { 140 availActionsCtx.enterWith({...availActionsCtx.getStore(), ...klusterCfg.extraActions})