deploy: configure NC01 HWLAB monitor exposure

This commit is contained in:
root
2026-07-08 05:34:11 +02:00
parent 5dfb78f000
commit a328aa909e
41 changed files with 2522 additions and 110 deletions
+37 -11
View File
@@ -476,6 +476,23 @@ export function nodeRuntimeStatusDegradedReason(input: {
}
export function nodeRuntimePublicProbeStatus(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
if (spec.publicExposure?.enabled !== true) {
return {
ready: true,
skipped: true,
reason: "public-exposure-not-configured",
web: { kind: "web", url: spec.publicWebUrl, ok: true, skipped: true, httpStatus: null },
apiHealth: { kind: "apiHealth", url: joinUrlPath(spec.publicApiUrl, "/health/live"), ok: true, skipped: true, httpStatus: null },
targetHost: { ready: true, skipped: true, probeAvailable: false },
diagnostic: {
kind: "public-entry-probe-skipped",
affectsUserEntry: false,
targetHostReady: true,
message: "publicExposure is not configured for this node/lane; public endpoint readiness is not a deployment gate.",
nextAction: null,
},
};
}
const web = publicHttpProbe("web", spec.publicWebUrl);
const apiHealth = publicHttpProbe("apiHealth", joinUrlPath(spec.publicApiUrl, "/health/live"));
const ready = web.ok === true && apiHealth.ok === true;
@@ -1896,10 +1913,11 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" if (!fs.existsSync(file)) return false;",
" const doc = readYaml(file) || {};",
" const resources = Array.isArray(doc.resources) ? doc.resources : [];",
" const publicExposureEnabled = overlay.publicExposure && overlay.publicExposure.enabled;",
" const next = resources.filter((item) => {",
" if (!(overlay.observability && overlay.observability.prometheusOperator === false)) return true;",
" const resource = String(item);",
" if (resource === 'observability.yaml') return false;",
" if (!publicExposureEnabled && resource === 'node-frpc.yaml') return false;",
" if (overlay.observability && overlay.observability.prometheusOperator === false && resource === 'observability.yaml') return false;",
" return !(/\\.ya?ml$/u.test(resource) && !fs.existsSync(path.join(runtimePath, resource)));",
" });",
" let changed = false;",
@@ -1918,7 +1936,6 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" const items = listItems(doc).filter(Boolean);",
" let changed = false;",
" const endpointIsIpv4 = /^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$/.test(String(access.endpointAddress));",
" const endpointSliceName = String(pg.serviceName) + '-host';",
" for (const item of items) {",
" if (!item || typeof item !== 'object') continue;",
" item.metadata = item.metadata || {};",
@@ -1946,11 +1963,14 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" }",
" if (item.kind === 'EndpointSlice') {",
" if (!endpointIsIpv4) { item.__delete = true; changed = true; continue; }",
" item.metadata.name = endpointSliceName;",
" item.apiVersion = 'v1';",
" item.kind = 'Endpoints';",
" item.metadata.name = pg.serviceName;",
" item.metadata.labels['kubernetes.io/service-name'] = pg.serviceName;",
" item.addressType = 'IPv4';",
" item.ports = [{ name: 'postgres', port: access.port, protocol: 'TCP' }];",
" item.endpoints = [{ addresses: [access.endpointAddress], conditions: { ready: true } }];",
" delete item.addressType;",
" delete item.ports;",
" delete item.endpoints;",
" item.subsets = [{ addresses: [{ ip: access.endpointAddress }], ports: [{ name: 'postgres', port: access.port, protocol: 'TCP' }] }];",
" changed = true;",
" }",
" }",
@@ -2015,8 +2035,12 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
"}",
"function patchPublicExposure() {",
" const exposure = overlay.publicExposure;",
" if (!exposure || !exposure.enabled) return { configured: false, changed: false };",
" const file = path.join(runtimePath, 'node-frpc.yaml');",
" if (!exposure || !exposure.enabled) {",
" const changed = fs.existsSync(file);",
" if (changed) fs.rmSync(file, { force: true });",
" return { configured: false, changed };",
" }",
" if (!fs.existsSync(file)) {",
" console.error(JSON.stringify({ event: 'unidesk-public-exposure-postprocess', ok: false, reason: 'node-frpc-yaml-missing', filePath: file, hostname: exposure.hostname }));",
" process.exit(49);",
@@ -2253,6 +2277,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" if (!fs.existsSync(file)) fail('external-postgres-missing');",
" const items = listItems(readYaml(file)).filter(Boolean);",
" const service = items.find((item) => item && item.kind === 'Service' && item.metadata && item.metadata.name === pg.serviceName);",
" const endpoints = items.find((item) => item && item.kind === 'Endpoints' && item.metadata && item.metadata.name === String(pg.serviceName));",
" const endpointSlice = items.find((item) => item && item.kind === 'EndpointSlice' && item.metadata && item.metadata.name === String(pg.serviceName) + '-host');",
" if (!service) fail('external-postgres-service-missing', { expected: pg.serviceName });",
" const endpointIsIpv4 = /^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$/.test(String(access.endpointAddress));",
@@ -2262,11 +2287,12 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" if (!service.spec || String(service.spec.externalName) !== String(access.endpointAddress)) fail('external-postgres-external-name-mismatch', { value: service.spec && service.spec.externalName, expected: access.endpointAddress });",
" if (Number(servicePort) !== Number(access.port)) fail('external-postgres-port-mismatch', { servicePort, expectedPort: access.port });",
" if (endpointSlice) fail('external-postgres-endpointslice-unexpected', { name: String(pg.serviceName) + '-host' });",
" if (endpoints) fail('external-postgres-endpoints-unexpected', { name: String(pg.serviceName) });",
" checks.push('external-postgres-bridge');",
" } else {",
" if (!endpointSlice) fail('external-postgres-endpointslice-missing', { expected: String(pg.serviceName) + '-host' });",
" const endpointPort = Array.isArray(endpointSlice.ports) && endpointSlice.ports[0] ? endpointSlice.ports[0].port : null;",
" const endpointAddress = Array.isArray(endpointSlice.endpoints) && endpointSlice.endpoints[0] && Array.isArray(endpointSlice.endpoints[0].addresses) ? endpointSlice.endpoints[0].addresses[0] : null;",
" if (!endpoints && !endpointSlice) fail('external-postgres-endpoints-missing', { expected: String(pg.serviceName) });",
" const endpointPort = endpoints && Array.isArray(endpoints.subsets) && endpoints.subsets[0] && Array.isArray(endpoints.subsets[0].ports) && endpoints.subsets[0].ports[0] ? endpoints.subsets[0].ports[0].port : endpointSlice && Array.isArray(endpointSlice.ports) && endpointSlice.ports[0] ? endpointSlice.ports[0].port : null;",
" const endpointAddress = endpoints && Array.isArray(endpoints.subsets) && endpoints.subsets[0] && Array.isArray(endpoints.subsets[0].addresses) && endpoints.subsets[0].addresses[0] ? endpoints.subsets[0].addresses[0].ip : endpointSlice && Array.isArray(endpointSlice.endpoints) && endpointSlice.endpoints[0] && Array.isArray(endpointSlice.endpoints[0].addresses) ? endpointSlice.endpoints[0].addresses[0] : null;",
" if (Number(servicePort) !== Number(access.port) || Number(endpointPort) !== Number(access.port)) fail('external-postgres-port-mismatch', { servicePort, endpointPort, expectedPort: access.port });",
" if (String(endpointAddress) !== String(access.endpointAddress)) fail('external-postgres-address-mismatch', { endpointAddress, expectedEndpointAddress: access.endpointAddress });",
" checks.push('external-postgres-bridge');",