diff --git a/scripts/native/hwlab/runtime-gitops-postprocess.mjs b/scripts/native/hwlab/runtime-gitops-postprocess.mjs index d15d46c0..f38ba2ee 100644 --- a/scripts/native/hwlab/runtime-gitops-postprocess.mjs +++ b/scripts/native/hwlab/runtime-gitops-postprocess.mjs @@ -42,6 +42,8 @@ function postprocessRuntimeGitops() { function stripPrometheusOperatorResourcesFromFile(file) { const original = readFileSync(file, "utf8"); + const jsonResult = stripPrometheusOperatorResourcesFromJsonFile(file, original); + if (jsonResult !== null) return jsonResult; const docs = splitYamlDocuments(original); const nextDocs = docs.filter((doc) => !isPrometheusOperatorDocument(doc)); if (nextDocs.length === docs.length) return "unchanged"; @@ -53,6 +55,29 @@ function stripPrometheusOperatorResourcesFromFile(file) { return "changed"; } +function stripPrometheusOperatorResourcesFromJsonFile(file, text) { + const parsed = parseJsonDocument(text); + if (parsed === null) return null; + const next = stripPrometheusOperatorResourcesFromJsonValue(parsed); + if (!next.changed) return "unchanged"; + if (next.value === null) { + unlinkSync(file); + return "deleted"; + } + writeFileSync(file, `${JSON.stringify(next.value, null, 2)}\n`, "utf8"); + return "changed"; +} + +function stripPrometheusOperatorResourcesFromJsonValue(value) { + if (isPrometheusOperatorResource(value)) return { changed: true, value: null }; + if (isKubernetesList(value)) { + const originalItems = Array.isArray(value.items) ? value.items : []; + const items = originalItems.filter((item) => !isPrometheusOperatorResource(item)); + if (items.length !== originalItems.length) return { changed: true, value: { ...value, items } }; + } + return { changed: false, value }; +} + function pruneMissingKustomizationResources() { const file = path.join(runtimeDir, "kustomization.yaml"); if (!existsSync(file)) return false; @@ -94,6 +119,33 @@ function isPrometheusOperatorDocument(text) { return Boolean(api && kind && prometheusOperatorKinds.has(kind[1])); } +function isPrometheusOperatorResource(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) return false; + return typeof value.apiVersion === "string" + && value.apiVersion.startsWith("monitoring.coreos.com/") + && typeof value.kind === "string" + && prometheusOperatorKinds.has(value.kind); +} + +function isKubernetesList(value) { + return value + && typeof value === "object" + && !Array.isArray(value) + && value.apiVersion === "v1" + && value.kind === "List" + && Array.isArray(value.items); +} + +function parseJsonDocument(text) { + const trimmed = text.trim(); + if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null; + try { + return JSON.parse(trimmed); + } catch { + return null; + } +} + function leadingSpaces(value) { const match = value.match(/^ */u); return match ? match[0].length : 0; diff --git a/scripts/native/hwlab/runtime-gitops-verify.mjs b/scripts/native/hwlab/runtime-gitops-verify.mjs index b8bbb2ee..c79b4d91 100644 --- a/scripts/native/hwlab/runtime-gitops-verify.mjs +++ b/scripts/native/hwlab/runtime-gitops-verify.mjs @@ -28,8 +28,7 @@ function findPrometheusOperatorResources() { for (const file of listYamlFiles(runtimeDir)) { const rel = path.relative(repoDir, file); for (const doc of splitYamlDocuments(readFileSync(file, "utf8"))) { - const ref = prometheusOperatorResourceRef(doc, rel); - if (ref !== null) refs.push(ref); + refs.push(...prometheusOperatorResourceRefs(doc, rel)); } } return refs; @@ -39,12 +38,53 @@ function splitYamlDocuments(text) { return text.split(/^---[ \t]*(?:#.*)?$/mu).map((doc) => doc.trim()).filter(Boolean); } -function prometheusOperatorResourceRef(text, file) { +function prometheusOperatorResourceRefs(text, file) { + const jsonRefs = prometheusOperatorJsonResourceRefs(text, file); + if (jsonRefs !== null) return jsonRefs; const api = text.match(/^\s*apiVersion:\s*["']?(monitoring\.coreos\.com\/[^"'\s#]+)["']?\s*(?:#.*)?$/mu); const kind = text.match(/^\s*kind:\s*["']?([^"'\s#]+)["']?\s*(?:#.*)?$/mu); - if (!api || !kind || !prometheusOperatorKinds.has(kind[1])) return null; + if (!api || !kind || !prometheusOperatorKinds.has(kind[1])) return []; const name = text.match(/^\s*name:\s*["']?([^"'\s#]+)["']?\s*(?:#.*)?$/mu); - return { file, kind: kind[1], name: name ? name[1] : null, container: null }; + return [{ file, kind: kind[1], name: name ? name[1] : null, container: null }]; +} + +function prometheusOperatorJsonResourceRefs(text, file) { + const parsed = parseJsonDocument(text); + if (parsed === null) return null; + const items = isKubernetesList(parsed) ? parsed.items : [parsed]; + return items.filter(isPrometheusOperatorResource).map((item) => ({ + file, + kind: item.kind, + name: typeof item.metadata?.name === "string" ? item.metadata.name : null, + container: isKubernetesList(parsed) ? "List.items" : null, + })); +} + +function isPrometheusOperatorResource(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) return false; + return typeof value.apiVersion === "string" + && value.apiVersion.startsWith("monitoring.coreos.com/") + && typeof value.kind === "string" + && prometheusOperatorKinds.has(value.kind); +} + +function isKubernetesList(value) { + return value + && typeof value === "object" + && !Array.isArray(value) + && value.apiVersion === "v1" + && value.kind === "List" + && Array.isArray(value.items); +} + +function parseJsonDocument(text) { + const trimmed = text.trim(); + if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null; + try { + return JSON.parse(trimmed); + } catch { + return null; + } } function listYamlFiles(root) {