fix: rewrite D601 runtime GitOps images
This commit is contained in:
@@ -85,6 +85,11 @@ export interface HwlabRuntimeObservabilitySpec {
|
||||
readonly prometheusOperator: boolean;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeImageRewriteSpec {
|
||||
readonly source: string;
|
||||
readonly target: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeLaneSpec {
|
||||
readonly lane: HwlabRuntimeLane;
|
||||
readonly nodeId: string;
|
||||
@@ -123,6 +128,7 @@ export interface HwlabRuntimeLaneSpec {
|
||||
readonly buildkit?: HwlabRuntimeBuildkitSpec;
|
||||
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
readonly networkProfileId: string;
|
||||
readonly downloadProfileId: string;
|
||||
readonly networkProfile: HwlabNetworkProfileSpec;
|
||||
@@ -161,6 +167,7 @@ interface HwlabLaneConfig {
|
||||
readonly buildkit?: HwlabRuntimeBuildkitSpec;
|
||||
readonly externalPostgres?: HwlabRuntimeExternalPostgresSpec;
|
||||
readonly observability: HwlabRuntimeObservabilitySpec;
|
||||
readonly runtimeImageRewrites: readonly HwlabRuntimeImageRewriteSpec[];
|
||||
}
|
||||
|
||||
interface HwlabNodeLaneConfig {
|
||||
@@ -359,6 +366,7 @@ function laneConfig(id: HwlabRuntimeLane, raw: Record<string, unknown>): HwlabLa
|
||||
buildkit: buildkitConfig(raw.buildkit, `lanes.${id}.buildkit`),
|
||||
externalPostgres: externalPostgresConfig(raw.externalPostgres, `lanes.${id}.externalPostgres`),
|
||||
observability: observabilityConfig(raw.observability, `lanes.${id}.observability`),
|
||||
runtimeImageRewrites: runtimeImageRewritesConfig(raw.runtimeImageRewrites, `lanes.${id}.runtimeImageRewrites`),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -426,6 +434,18 @@ function observabilityConfig(value: unknown, path: string): HwlabRuntimeObservab
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeImageRewritesConfig(value: unknown, path: string): HwlabRuntimeImageRewriteSpec[] {
|
||||
if (value === undefined) return [];
|
||||
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
||||
return value.map((item, index) => {
|
||||
const raw = asRecord(item, `${path}[${index}]`);
|
||||
return {
|
||||
source: stringField(raw, "source", `${path}[${index}]`),
|
||||
target: stringField(raw, "target", `${path}[${index}]`),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function readHwlabNodeLaneConfig(): HwlabNodeLaneConfig {
|
||||
const path = rootPath(HWLAB_NODE_LANE_CONFIG_PATH);
|
||||
const raw = readFileSync(path, "utf8");
|
||||
@@ -508,6 +528,7 @@ function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec {
|
||||
...(config.buildkit === undefined ? {} : { buildkit: config.buildkit }),
|
||||
...(config.externalPostgres === undefined ? {} : { externalPostgres: config.externalPostgres }),
|
||||
observability: config.observability,
|
||||
runtimeImageRewrites: config.runtimeImageRewrites,
|
||||
networkProfileId: networkProfile.id,
|
||||
downloadProfileId: downloadProfile.id,
|
||||
networkProfile,
|
||||
|
||||
@@ -264,6 +264,7 @@ function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown
|
||||
npm: spec.downloadProfile.npm,
|
||||
},
|
||||
observability: spec.observability,
|
||||
runtimeImageRewrites: spec.runtimeImageRewrites,
|
||||
externalPostgres: spec.externalPostgres === undefined ? null : {
|
||||
provider: spec.externalPostgres.provider,
|
||||
configRef: spec.externalPostgres.configRef,
|
||||
@@ -1241,6 +1242,7 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" publicApiUrl: overlay.publicApiUrl,",
|
||||
" externalPostgres: overlay.externalPostgres,",
|
||||
" observability: overlay.observability,",
|
||||
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
|
||||
" dockerProxyHttp: overlay.dockerProxyHttp,",
|
||||
" dockerProxyHttps: overlay.dockerProxyHttps,",
|
||||
" dockerNoProxyList: overlay.dockerNoProxyList,",
|
||||
@@ -1283,6 +1285,7 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" imageTagMode: 'full',",
|
||||
" sourceRepo: overlay.gitUrl,",
|
||||
" externalPostgres: overlay.externalPostgres,",
|
||||
" observability: overlay.observability,",
|
||||
" envRecipe: { ...envRecipe, downloadStack },",
|
||||
"};",
|
||||
"fs.writeFileSync(file, YAML.stringify(doc));",
|
||||
@@ -1319,6 +1322,8 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" runtimeNamespace: overlay.runtimeNamespace,",
|
||||
" externalPostgres: overlay.externalPostgres,",
|
||||
" observability: overlay.observability,",
|
||||
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
|
||||
" gitReadUrl: overlay.gitReadUrl,",
|
||||
" });",
|
||||
" return `node - <<'NODE_UNIDESK_RUNTIME_GITOPS_POSTPROCESS'",
|
||||
"const fs = require('fs');",
|
||||
@@ -1441,9 +1446,47 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" }",
|
||||
" return changed;",
|
||||
"}",
|
||||
"function rewriteRuntimeImage(image) {",
|
||||
" if (typeof image !== 'string') return image;",
|
||||
" const match = (overlay.runtimeImageRewrites || []).find((item) => item && item.source === image);",
|
||||
" return match ? match.target : image;",
|
||||
"}",
|
||||
"function patchRuntimeImages(podSpec) {",
|
||||
" if (!isObject(podSpec)) return false;",
|
||||
" let changed = false;",
|
||||
" for (const group of ['containers', 'initContainers']) {",
|
||||
" for (const container of Array.isArray(podSpec[group]) ? podSpec[group] : []) {",
|
||||
" if (!isObject(container) || typeof container.image !== 'string') continue;",
|
||||
" const nextImage = rewriteRuntimeImage(container.image);",
|
||||
" if (nextImage !== container.image) { container.image = nextImage; changed = true; }",
|
||||
" }",
|
||||
" }",
|
||||
" return changed;",
|
||||
"}",
|
||||
"function rewriteEnvValue(value) {",
|
||||
" if (typeof value !== 'string') return value;",
|
||||
" return value.replaceAll('http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git', overlay.gitReadUrl);",
|
||||
"}",
|
||||
"function patchGitReadUrlEnv(podSpec) {",
|
||||
" if (!isObject(podSpec)) return false;",
|
||||
" let changed = false;",
|
||||
" for (const group of ['containers', 'initContainers']) {",
|
||||
" for (const container of Array.isArray(podSpec[group]) ? podSpec[group] : []) {",
|
||||
" if (!isObject(container) || !Array.isArray(container.env)) continue;",
|
||||
" for (const env of container.env) {",
|
||||
" if (!isObject(env) || typeof env.value !== 'string') continue;",
|
||||
" const nextValue = rewriteEnvValue(env.value);",
|
||||
" if (nextValue !== env.value) { env.value = nextValue; changed = true; }",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
" return changed;",
|
||||
"}",
|
||||
"function patchRuntimeWorkloads() {",
|
||||
" let observabilityChanged = false;",
|
||||
" let startupProbeChanged = false;",
|
||||
" let imageRewriteChanged = false;",
|
||||
" let gitReadUrlChanged = false;",
|
||||
" for (const file of yamlFiles(runtimePath)) {",
|
||||
" if (path.basename(file) === 'kustomization.yaml') continue;",
|
||||
" const docs = readYamlDocuments(file);",
|
||||
@@ -1462,11 +1505,17 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" const probeChanged = addEnvReuseStartupProbe(podSpecFor(item));",
|
||||
" changed = probeChanged || changed;",
|
||||
" startupProbeChanged = startupProbeChanged || probeChanged;",
|
||||
" const imageChanged = patchRuntimeImages(podSpecFor(item));",
|
||||
" changed = imageChanged || changed;",
|
||||
" imageRewriteChanged = imageRewriteChanged || imageChanged;",
|
||||
" const gitUrlChanged = patchGitReadUrlEnv(podSpecFor(item));",
|
||||
" changed = gitUrlChanged || changed;",
|
||||
" gitReadUrlChanged = gitReadUrlChanged || gitUrlChanged;",
|
||||
" }",
|
||||
" }",
|
||||
" if (changed) writeYamlDocuments(file, docs);",
|
||||
" }",
|
||||
" return { observabilityChanged, startupProbeChanged };",
|
||||
" return { observabilityChanged, startupProbeChanged, imageRewriteChanged, gitReadUrlChanged };",
|
||||
"}",
|
||||
"function patchKustomization() {",
|
||||
" const file = path.join(runtimePath, 'kustomization.yaml');",
|
||||
@@ -1517,7 +1566,7 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"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, kustomizationChanged, observabilityWorkloadsChanged: runtimeWorkloadsChanged.observabilityChanged, startupProbeChanged: runtimeWorkloadsChanged.startupProbeChanged, externalPostgresChanged }));",
|
||||
"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 }));",
|
||||
"NODE_UNIDESK_RUNTIME_GITOPS_POSTPROCESS`;",
|
||||
"}",
|
||||
"function runtimeGitopsVerifyScript() {",
|
||||
@@ -1526,6 +1575,8 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" runtimeNamespace: overlay.runtimeNamespace,",
|
||||
" externalPostgres: overlay.externalPostgres,",
|
||||
" observability: overlay.observability,",
|
||||
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
|
||||
" gitReadUrl: overlay.gitReadUrl,",
|
||||
" });",
|
||||
" return `node - <<'NODE_UNIDESK_RUNTIME_GITOPS_VERIFY'",
|
||||
"const fs = require('fs');",
|
||||
@@ -1569,6 +1620,9 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"function workloadChecks() {",
|
||||
" const metricsRefs = [];",
|
||||
" const missingStartupProbes = [];",
|
||||
" const publicRuntimeImages = [];",
|
||||
" const staleGitReadUrls = [];",
|
||||
" 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;",
|
||||
" for (const doc of readYamlDocuments(file)) {",
|
||||
@@ -1579,12 +1633,14 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" if (!isObject(container)) continue;",
|
||||
" if (container.name === 'hwlab-metrics' || (Array.isArray(container.volumeMounts) && container.volumeMounts.some((mount) => mount && mount.name === 'hwlab-metrics-sidecar'))) metricsRefs.push(workloadRef(item, file, container));",
|
||||
" 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));",
|
||||
" }",
|
||||
" 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 };",
|
||||
" return { metricsRefs, missingStartupProbes, publicRuntimeImages, staleGitReadUrls };",
|
||||
"}",
|
||||
"const checks = [];",
|
||||
"const workloadCheck = workloadChecks();",
|
||||
@@ -1598,6 +1654,10 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"}",
|
||||
"if (workloadCheck.missingStartupProbes.length > 0) fail('env-reuse-startup-probe-missing', { refs: workloadCheck.missingStartupProbes.slice(0, 12), count: workloadCheck.missingStartupProbes.length });",
|
||||
"checks.push('env-reuse-startup-probes');",
|
||||
"if (workloadCheck.publicRuntimeImages.length > 0) fail('runtime-image-rewrite-missing', { refs: workloadCheck.publicRuntimeImages.slice(0, 12), count: workloadCheck.publicRuntimeImages.length });",
|
||||
"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');",
|
||||
"const pg = overlay.externalPostgres;",
|
||||
"if (pg && pg.serviceName) {",
|
||||
" const file = path.join(runtimePath, 'external-postgres.yaml');",
|
||||
@@ -1829,6 +1889,7 @@ function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<string, un
|
||||
npmFetchTimeoutMs: spec.downloadProfile.npm.fetchTimeoutSeconds * 1000,
|
||||
stepEnv: spec.stepEnv,
|
||||
observability: spec.observability,
|
||||
runtimeImageRewrites: spec.runtimeImageRewrites,
|
||||
registryPrefix: spec.registryPrefix,
|
||||
buildkitSidecarImage: spec.buildkit?.sidecarImage,
|
||||
publicWebUrl: spec.publicWebUrl,
|
||||
|
||||
Reference in New Issue
Block a user