From 19d0a614082e98353eef9cffd6027377456fe549 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 12 Jun 2026 22:59:22 +0000 Subject: [PATCH] fix: apply D601 runtime YAML env overrides --- config/hwlab-node-lanes.yaml | 4 +- scripts/src/hwlab-node.ts | 79 ++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 920bbef1..13b5ba44 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -133,8 +133,8 @@ lanes: - source: fatedier/frpc:v0.68.1 target: 127.0.0.1:5000/hwlab/frpc:v0.68.1 public: - webUrl: https://v03.hwpod.com - apiUrl: https://v03.hwpod.com + webUrl: http://74.48.78.17:20666 + apiUrl: http://74.48.78.17:20667 externalPostgres: provider: PK01 configRef: config/platform-db/postgres-pk01.yaml diff --git a/scripts/src/hwlab-node.ts b/scripts/src/hwlab-node.ts index bdcb24ab..a2b3599f 100644 --- a/scripts/src/hwlab-node.ts +++ b/scripts/src/hwlab-node.ts @@ -1444,6 +1444,8 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " observability: overlay.observability,", " runtimeImageRewrites: overlay.runtimeImageRewrites,", " gitReadUrl: overlay.gitReadUrl,", + " publicWebUrl: overlay.publicWebUrl,", + " publicApiUrl: overlay.publicApiUrl,", " });", " return `node - <<'NODE_UNIDESK_RUNTIME_GITOPS_POSTPROCESS'", "const fs = require('fs');", @@ -1544,7 +1546,19 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " const item = container.env.find((env) => env && env.name === name);", " return item ? item.value : undefined;", "}", + "function setEnvValue(container, name, value) {", + " if (!isObject(container) || typeof value !== 'string') return false;", + " container.env = Array.isArray(container.env) ? container.env : [];", + " let item = container.env.find((env) => env && env.name === name);", + " if (!item) { item = { name }; container.env.push(item); }", + " const changed = item.value !== value || item.valueFrom !== undefined;", + " item.value = value;", + " delete item.valueFrom;", + " return changed;", + "}", "function isEnvReuseContainer(container) { return envValue(container, 'HWLAB_RUNTIME_MODE') === 'env-reuse-git-mirror-checkout' || envValue(container, 'HWLAB_BOOT_SH') !== undefined || envValue(container, 'HWLAB_BOOT_COMMIT') !== undefined; }", + "function workloadName(item) { return item && item.metadata && item.metadata.labels && item.metadata.labels['app.kubernetes.io/name'] ? String(item.metadata.labels['app.kubernetes.io/name']) : String(item && item.metadata && item.metadata.name || ''); }", + "function expectedPublicEndpoint(item) { return workloadName(item) === 'hwlab-cloud-web' ? overlay.publicWebUrl : overlay.publicApiUrl; }", "function startupProbeFrom(probe) {", " const next = JSON.parse(JSON.stringify(probe));", " next.periodSeconds = 10;", @@ -1602,11 +1616,27 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " }", " return changed;", "}", + "function patchRuntimeEnv(item, podSpec) {", + " if (!isObject(podSpec)) return { publicEndpointChanged: false, dbSslModeChanged: false };", + " let publicEndpointChanged = false;", + " let dbSslModeChanged = false;", + " const pg = overlay.externalPostgres;", + " for (const group of ['containers', 'initContainers']) {", + " for (const container of Array.isArray(podSpec[group]) ? podSpec[group] : []) {", + " if (!isObject(container)) continue;", + " if (envValue(container, 'HWLAB_PUBLIC_ENDPOINT') !== undefined) publicEndpointChanged = setEnvValue(container, 'HWLAB_PUBLIC_ENDPOINT', expectedPublicEndpoint(item)) || publicEndpointChanged;", + " if (pg && pg.sslmode && envValue(container, 'HWLAB_CLOUD_DB_SSL_MODE') !== undefined) dbSslModeChanged = setEnvValue(container, 'HWLAB_CLOUD_DB_SSL_MODE', pg.sslmode) || dbSslModeChanged;", + " }", + " }", + " return { publicEndpointChanged, dbSslModeChanged };", + "}", "function patchRuntimeWorkloads() {", " let observabilityChanged = false;", " let startupProbeChanged = false;", " let imageRewriteChanged = false;", " let gitReadUrlChanged = false;", + " let publicEndpointChanged = false;", + " let dbSslModeChanged = false;", " for (const file of yamlFiles(runtimePath)) {", " if (path.basename(file) === 'kustomization.yaml') continue;", " const docs = readYamlDocuments(file);", @@ -1631,11 +1661,15 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " const gitUrlChanged = patchGitReadUrlEnv(podSpecFor(item));", " changed = gitUrlChanged || changed;", " gitReadUrlChanged = gitReadUrlChanged || gitUrlChanged;", + " const envChanged = patchRuntimeEnv(item, podSpecFor(item));", + " changed = envChanged.publicEndpointChanged || envChanged.dbSslModeChanged || changed;", + " publicEndpointChanged = publicEndpointChanged || envChanged.publicEndpointChanged;", + " dbSslModeChanged = dbSslModeChanged || envChanged.dbSslModeChanged;", " }", " }", " if (changed) writeYamlDocuments(file, docs);", " }", - " return { observabilityChanged, startupProbeChanged, imageRewriteChanged, gitReadUrlChanged };", + " return { observabilityChanged, startupProbeChanged, imageRewriteChanged, gitReadUrlChanged, publicEndpointChanged, dbSslModeChanged };", "}", "function patchKustomization() {", " const file = path.join(runtimePath, 'kustomization.yaml');", @@ -1683,10 +1717,34 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " if (changed) writeYaml(file, normalizeList(items));", " return changed;", "}", + "function patchHealthContract() {", + " const file = path.join(runtimePath, 'health-contract.yaml');", + " if (!fs.existsSync(file)) return false;", + " const doc = readYaml(file);", + " const items = listItems(doc).filter(Boolean);", + " let changed = false;", + " const pg = overlay.externalPostgres;", + " for (const item of items) {", + " if (!item || item.kind !== 'ConfigMap') continue;", + " item.data = item.data || {};", + " if (item.data.endpoint !== overlay.publicWebUrl) { item.data.endpoint = overlay.publicWebUrl; changed = true; }", + " const cloudApi = 'GET /health/live through ' + overlay.publicApiUrl;", + " if (item.data['cloud-api'] !== cloudApi) { item.data['cloud-api'] = cloudApi; changed = true; }", + " const cloudWeb = 'GET /health/live on ' + overlay.publicWebUrl + '; consumes cloud-api only';", + " if (item.data['cloud-web'] !== cloudWeb) { item.data['cloud-web'] = cloudWeb; changed = true; }", + " if (pg && pg.sslmode && typeof item.data['cloud-api-db'] === 'string') {", + " const next = item.data['cloud-api-db'].replace(/HWLAB_CLOUD_DB_SSL_MODE=[A-Za-z0-9_-]+/g, 'HWLAB_CLOUD_DB_SSL_MODE=' + pg.sslmode);", + " if (next !== item.data['cloud-api-db']) { item.data['cloud-api-db'] = next; changed = true; }", + " }", + " }", + " if (changed) writeYaml(file, normalizeList(items));", + " return changed;", + "}", "const kustomizationChanged = patchKustomization();", "const runtimeWorkloadsChanged = patchRuntimeWorkloads();", "const externalPostgresChanged = patchExternalPostgres();", - "console.error(JSON.stringify({ event: 'unidesk-runtime-gitops-postprocess', ok: true, runtimePath, sourcePath, pathRelocated: sourcePath !== runtimePath, observabilityPrometheusOperator: overlay.observability ? overlay.observability.prometheusOperator : null, runtimeImageRewriteCount: (overlay.runtimeImageRewrites || []).length, kustomizationChanged, observabilityWorkloadsChanged: runtimeWorkloadsChanged.observabilityChanged, startupProbeChanged: runtimeWorkloadsChanged.startupProbeChanged, runtimeImageRewriteChanged: runtimeWorkloadsChanged.imageRewriteChanged, gitReadUrlChanged: runtimeWorkloadsChanged.gitReadUrlChanged, externalPostgresChanged }));", + "const healthContractChanged = patchHealthContract();", + "console.error(JSON.stringify({ event: 'unidesk-runtime-gitops-postprocess', ok: true, runtimePath, sourcePath, pathRelocated: sourcePath !== runtimePath, observabilityPrometheusOperator: overlay.observability ? overlay.observability.prometheusOperator : null, runtimeImageRewriteCount: (overlay.runtimeImageRewrites || []).length, kustomizationChanged, observabilityWorkloadsChanged: runtimeWorkloadsChanged.observabilityChanged, startupProbeChanged: runtimeWorkloadsChanged.startupProbeChanged, runtimeImageRewriteChanged: runtimeWorkloadsChanged.imageRewriteChanged, gitReadUrlChanged: runtimeWorkloadsChanged.gitReadUrlChanged, publicEndpointChanged: runtimeWorkloadsChanged.publicEndpointChanged, dbSslModeChanged: runtimeWorkloadsChanged.dbSslModeChanged, externalPostgresChanged, healthContractChanged }));", "NODE_UNIDESK_RUNTIME_GITOPS_POSTPROCESS`;", "}", "function runtimeGitopsVerifyScript() {", @@ -1697,6 +1755,8 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " observability: overlay.observability,", " runtimeImageRewrites: overlay.runtimeImageRewrites,", " gitReadUrl: overlay.gitReadUrl,", + " publicWebUrl: overlay.publicWebUrl,", + " publicApiUrl: overlay.publicApiUrl,", " });", " return `node - <<'NODE_UNIDESK_RUNTIME_GITOPS_VERIFY'", "const fs = require('fs');", @@ -1736,12 +1796,16 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " return item ? item.value : undefined;", "}", "function isEnvReuseContainer(container) { return envValue(container, 'HWLAB_RUNTIME_MODE') === 'env-reuse-git-mirror-checkout' || envValue(container, 'HWLAB_BOOT_SH') !== undefined || envValue(container, 'HWLAB_BOOT_COMMIT') !== undefined; }", + "function workloadName(item) { return item && item.metadata && item.metadata.labels && item.metadata.labels['app.kubernetes.io/name'] ? String(item.metadata.labels['app.kubernetes.io/name']) : String(item && item.metadata && item.metadata.name || ''); }", + "function expectedPublicEndpoint(item) { return workloadName(item) === 'hwlab-cloud-web' ? overlay.publicWebUrl : overlay.publicApiUrl; }", "function workloadRef(item, file, container) { return { file, kind: item && item.kind, name: item && item.metadata && item.metadata.name, container: container && container.name }; }", "function workloadChecks() {", " const metricsRefs = [];", " const missingStartupProbes = [];", " const publicRuntimeImages = [];", " const staleGitReadUrls = [];", + " const wrongPublicEndpoints = [];", + " const wrongDbSslModes = [];", " const rewriteSources = new Set((overlay.runtimeImageRewrites || []).map((item) => item && item.source).filter(Boolean));", " for (const file of yamlFiles(runtimePath)) {", " if (path.basename(file) === 'kustomization.yaml') continue;", @@ -1755,12 +1819,16 @@ function nodeRuntimePipelinePostprocessScript(): string[] { " if (isEnvReuseContainer(container) && (container.readinessProbe || container.livenessProbe) && !container.startupProbe) missingStartupProbes.push(workloadRef(item, file, container));", " if (typeof container.image === 'string' && rewriteSources.has(container.image)) publicRuntimeImages.push({ ...workloadRef(item, file, container), image: container.image });", " if (Array.isArray(container.env) && container.env.some((env) => env && typeof env.value === 'string' && env.value.includes('git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git') && env.value !== overlay.gitReadUrl)) staleGitReadUrls.push(workloadRef(item, file, container));", + " const publicEndpoint = envValue(container, 'HWLAB_PUBLIC_ENDPOINT');", + " if (publicEndpoint !== undefined && publicEndpoint !== expectedPublicEndpoint(item)) wrongPublicEndpoints.push({ ...workloadRef(item, file, container), value: publicEndpoint, expected: expectedPublicEndpoint(item) });", + " const dbSslMode = envValue(container, 'HWLAB_CLOUD_DB_SSL_MODE');", + " if (overlay.externalPostgres && overlay.externalPostgres.sslmode && dbSslMode !== undefined && dbSslMode !== overlay.externalPostgres.sslmode) wrongDbSslModes.push({ ...workloadRef(item, file, container), value: dbSslMode, expected: overlay.externalPostgres.sslmode });", " }", " if (Array.isArray(podSpec.volumes) && podSpec.volumes.some((volume) => volume && volume.name === 'hwlab-metrics-sidecar')) metricsRefs.push(workloadRef(item, file, { name: 'volume/hwlab-metrics-sidecar' }));", " }", " }", " }", - " return { metricsRefs, missingStartupProbes, publicRuntimeImages, staleGitReadUrls };", + " return { metricsRefs, missingStartupProbes, publicRuntimeImages, staleGitReadUrls, wrongPublicEndpoints, wrongDbSslModes };", "}", "const checks = [];", "const workloadCheck = workloadChecks();", @@ -1778,6 +1846,10 @@ function nodeRuntimePipelinePostprocessScript(): string[] { "if ((overlay.runtimeImageRewrites || []).length > 0) checks.push('runtime-image-rewrites');", "if (workloadCheck.staleGitReadUrls.length > 0) fail('runtime-git-read-url-stale', { refs: workloadCheck.staleGitReadUrls.slice(0, 12), count: workloadCheck.staleGitReadUrls.length, expected: overlay.gitReadUrl });", "checks.push('runtime-git-read-url');", + "if (workloadCheck.wrongPublicEndpoints.length > 0) fail('runtime-public-endpoint-mismatch', { refs: workloadCheck.wrongPublicEndpoints.slice(0, 12), count: workloadCheck.wrongPublicEndpoints.length });", + "checks.push('runtime-public-endpoint');", + "if (workloadCheck.wrongDbSslModes.length > 0) fail('runtime-db-ssl-mode-mismatch', { refs: workloadCheck.wrongDbSslModes.slice(0, 12), count: workloadCheck.wrongDbSslModes.length });", + "if (overlay.externalPostgres && overlay.externalPostgres.sslmode) checks.push('runtime-db-ssl-mode');", "const pg = overlay.externalPostgres;", "if (pg && pg.serviceName) {", " const file = path.join(runtimePath, 'external-postgres.yaml');", @@ -2031,6 +2103,7 @@ function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record