105 lines
4.4 KiB
JavaScript
105 lines
4.4 KiB
JavaScript
#!/usr/bin/env node
|
|
import assert from "node:assert/strict";
|
|
import path from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
import { readStructuredFile } from "./src/structured-config.mjs";
|
|
|
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const namespace = "hwlab-dev";
|
|
|
|
async function readConfig(relativePath) {
|
|
return readStructuredFile(repoRoot, relativePath);
|
|
}
|
|
|
|
function listItems(document) {
|
|
return document?.kind === "List" ? document.items ?? [] : [document];
|
|
}
|
|
|
|
function serviceIdsFromDeploy(deploy) {
|
|
return (deploy.services ?? []).map((service) => service.serviceId).filter(Boolean);
|
|
}
|
|
|
|
function assertUnique(label, values) {
|
|
assert.equal(new Set(values).size, values.length, `${label} must be unique`);
|
|
}
|
|
|
|
function assertDeployManifestConsistency(deploy) {
|
|
assert.equal(deploy.environment, "dev", "deploy/deploy.yaml environment");
|
|
assert.equal(deploy.namespace, namespace, "deploy/deploy.yaml namespace");
|
|
assert.equal(deploy.profiles?.prod?.enabled, false, "deploy/deploy.yaml prod disabled");
|
|
|
|
const serviceIds = serviceIdsFromDeploy(deploy);
|
|
assert.ok(serviceIds.length > 0, "deploy/deploy.yaml services must not be empty");
|
|
assertUnique("deploy/deploy.yaml serviceId", serviceIds);
|
|
|
|
const byId = new Set(serviceIds);
|
|
for (const mapping of deploy.k3s?.serviceMappings ?? []) {
|
|
assert.ok(byId.has(mapping.serviceId), `deploy/deploy.yaml k3s serviceMapping ${mapping.name} references a declared service`);
|
|
assert.equal(mapping.namespace, namespace, `${mapping.name} namespace`);
|
|
}
|
|
|
|
const v02 = deploy.lanes?.v02;
|
|
if (v02) {
|
|
const envReuseServices = Array.isArray(v02.envReuseServices) ? v02.envReuseServices.filter(Boolean) : [];
|
|
assert.ok(envReuseServices.length > 0, "deploy.lanes.v02.envReuseServices must not be empty");
|
|
assertUnique("deploy.lanes.v02.envReuseServices", envReuseServices);
|
|
for (const serviceId of envReuseServices) {
|
|
assert.ok(byId.has(serviceId), `deploy.lanes.v02.envReuseServices ${serviceId} must be declared in deploy.services`);
|
|
assert.ok(v02.serviceDeclarations?.[serviceId], `deploy.lanes.v02.serviceDeclarations.${serviceId} is required`);
|
|
assert.ok(v02.bootScripts?.[serviceId], `deploy.lanes.v02.bootScripts.${serviceId} is required`);
|
|
}
|
|
}
|
|
}
|
|
|
|
function assertK8sServiceMappings(deploy, services) {
|
|
const serviceDocs = listItems(services).filter((item) => item?.kind === "Service");
|
|
const serviceNames = new Set(serviceDocs.map((item) => item.metadata?.name).filter(Boolean));
|
|
for (const mapping of deploy.k3s?.serviceMappings ?? []) {
|
|
assert.ok(serviceNames.has(mapping.name), `deploy/k8s/base/services.yaml missing Service ${mapping.name}`);
|
|
const service = serviceDocs.find((item) => item.metadata?.name === mapping.name);
|
|
const port = service?.spec?.ports?.find((entry) => entry.name === "http") ?? service?.spec?.ports?.[0];
|
|
assert.equal(port?.port, mapping.port, `${mapping.name} service port`);
|
|
assert.equal(port?.targetPort, mapping.targetPort, `${mapping.name} targetPort`);
|
|
}
|
|
}
|
|
|
|
function assertRuntimeServiceWorkloads(deploy, workloads) {
|
|
const workloadNames = new Set(listItems(workloads).map((item) => item?.metadata?.name).filter(Boolean));
|
|
for (const service of deploy.services ?? []) {
|
|
if (service.externalImage || service.kind === "external") continue;
|
|
assert.ok(workloadNames.has(service.serviceId), `deploy/k8s/base/workloads.yaml missing workload ${service.serviceId}`);
|
|
}
|
|
}
|
|
|
|
export async function readDevM3CardinalityInputs() {
|
|
const [deploy, workloads, services, topology] = await Promise.all([
|
|
readConfig("deploy/deploy.yaml"),
|
|
readConfig("deploy/k8s/base/workloads.yaml"),
|
|
readConfig("deploy/k8s/base/services.yaml"),
|
|
readConfig("fixtures/mvp/m3-hardware-loop/topology.json").catch(() => null)
|
|
]);
|
|
|
|
return { deploy, workloads, services, topology };
|
|
}
|
|
|
|
export function validateDevM3Cardinality({ deploy, workloads, services }) {
|
|
assertDeployManifestConsistency(deploy);
|
|
assertK8sServiceMappings(deploy, services);
|
|
assertRuntimeServiceWorkloads(deploy, workloads);
|
|
}
|
|
|
|
export async function main() {
|
|
validateDevM3Cardinality(await readDevM3CardinalityInputs());
|
|
console.log("validated DEV deploy manifest service mapping consistency");
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
try {
|
|
await main();
|
|
} catch (error) {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exitCode = 1;
|
|
}
|
|
}
|