fix: apply hwlab follower pipeline via k8s api

This commit is contained in:
Codex
2026-07-03 14:26:59 +00:00
parent f3fccb8f35
commit 1f1c76941d
2 changed files with 60 additions and 6 deletions
@@ -3,6 +3,7 @@
// Responsibility: render and apply the HWLAB node Tekton Pipeline from a k8s git-mirror snapshot.
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import https from "node:https";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import path from "node:path";
@@ -13,6 +14,7 @@ const sourceStageRef = requiredEnv("SOURCE_STAGE_REF");
const gitReadUrl = requiredEnv("GIT_READ_URL");
const fieldManager = requiredEnv("FIELD_MANAGER");
const tektonNamespace = requiredEnv("TEKTON_NAMESPACE");
const kubeRequestTimeoutSeconds = requiredPositiveNumber("KUBE_REQUEST_TIMEOUT_SECONDS");
const overlay = JSON.parse(Buffer.from(requiredEnv("HWLAB_RENDER_OVERLAY_B64"), "base64").toString("utf8"));
const workDir = mkdtempSync(path.join(tmpdir(), `hwlab-control-plane-${sourceCommit.slice(0, 12)}-`));
const repoDir = path.join(workDir, "repo");
@@ -24,7 +26,7 @@ try {
prepareYamlDependency();
applyDeployOverlay();
renderControlPlane();
applyPipeline();
await applyPipeline();
emit({ ok: true, status: "applied" });
} finally {
rmSync(workDir, { recursive: true, force: true });
@@ -74,8 +76,7 @@ function canResolveYaml() {
}
function applyDeployOverlay() {
const requireFromDeps = createRequire(path.join(depsDir, "package.json"));
const YAML = requireFromDeps("yaml");
const YAML = yamlModule();
const deployPath = path.join(repoDir, "deploy/deploy.yaml");
const doc = YAML.parse(readFileSync(deployPath, "utf8"));
doc.nodes = doc.nodes || {};
@@ -132,10 +133,61 @@ function renderControlPlane() {
], { cwd: repoDir, env: renderEnv() });
}
function applyPipeline() {
async function applyPipeline() {
const pipelinePath = path.join(renderDir, overlay.tektonDir, "pipeline.yaml");
if (!existsSync(pipelinePath)) throw new Error(`rendered Pipeline missing: ${pipelinePath}`);
run("kubectl", ["apply", "--server-side", "--force-conflicts", `--field-manager=${fieldManager}`, "-f", pipelinePath], { cwd: repoDir, env: renderEnv() });
const pipelineText = readFileSync(pipelinePath, "utf8");
const pipeline = yamlModule().parse(pipelineText);
const pipelineName = pipeline?.metadata?.name;
if (typeof pipelineName !== "string" || pipelineName.length === 0) throw new Error(`rendered Pipeline metadata.name missing: ${pipelinePath}`);
await kubeRequest(
"PATCH",
`/apis/tekton.dev/v1/namespaces/${encodeURIComponent(tektonNamespace)}/pipelines/${encodeURIComponent(pipelineName)}?fieldManager=${encodeURIComponent(fieldManager)}&force=true`,
pipelineText,
"application/apply-patch+yaml",
);
}
function yamlModule() {
const requireFromDeps = createRequire(path.join(depsDir, "package.json"));
return requireFromDeps("yaml");
}
function kubeRequest(method, requestPath, body, contentType = "application/json") {
const host = requiredEnv("KUBERNETES_SERVICE_HOST");
const port = requiredPositiveNumber("KUBERNETES_SERVICE_PORT");
const token = readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/token", "utf8").trim();
const ca = readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
return new Promise((resolve, reject) => {
const payload = typeof body === "string" ? body : JSON.stringify(body);
const req = https.request({
host,
port,
path: requestPath,
method,
ca,
headers: {
authorization: `Bearer ${token}`,
"content-type": contentType,
"content-length": Buffer.byteLength(payload),
},
}, (res) => {
let text = "";
res.setEncoding("utf8");
res.on("data", (chunk) => { text += chunk; });
res.on("end", () => {
if ((res.statusCode || 0) < 200 || (res.statusCode || 0) >= 300) {
reject(new Error(`kube api ${method} ${requestPath} status ${res.statusCode}: ${tail(text, 1200)}`));
return;
}
resolve(text);
});
});
req.setTimeout(kubeRequestTimeoutSeconds * 1000, () => req.destroy(new Error(`kube api ${method} ${requestPath} timed out after ${kubeRequestTimeoutSeconds}s`)));
req.on("error", reject);
req.write(payload);
req.end();
});
}
function renderEnv(extra = {}) {