fix: separate runner kubeconfig gap from D601 status (#144)
Commander review: direction is correct. This PR separates runner kubeconfig/read-only observability gaps from D601 public endpoint/k3s availability and keeps M3 blocked until real operation/trace/audit/evidence are observed. It does not touch PROD or restart services.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { accessSync, constants as fsConstants } from "node:fs";
|
||||
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { request as httpRequest } from "node:http";
|
||||
import { request as httpsRequest } from "node:https";
|
||||
@@ -9,7 +10,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { activeReportLifecycle } from "../internal/dev-report-lifecycle.mjs";
|
||||
import { DEV_ENDPOINT, ENVIRONMENT_DEV } from "../internal/protocol/index.mjs";
|
||||
import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV } from "../internal/protocol/index.mjs";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const defaultReportPath = "reports/dev-gate/dev-m3-hardware-loop.json";
|
||||
@@ -19,6 +20,7 @@ const deployWorkloadsPath = "deploy/k8s/base/workloads.yaml";
|
||||
const requiredManifestCardinalityCommand = "node scripts/validate-dev-m3-cardinality.mjs";
|
||||
const requiredM3BoxIds = Object.freeze(["boxsimu_1", "boxsimu_2"]);
|
||||
const requiredM3BoxResources = Object.freeze(["res_boxsimu_1", "res_boxsimu_2"]);
|
||||
const runnerK3sKubeconfigPath = "/etc/rancher/k3s/k3s.yaml";
|
||||
|
||||
const serviceTargets = Object.freeze([
|
||||
{
|
||||
@@ -156,12 +158,57 @@ function commandResult(command, args, { timeoutMs = 5000, maxChars = 3000 } = {}
|
||||
}
|
||||
}
|
||||
|
||||
function runnerKubeconfigCommandText(args) {
|
||||
return [`KUBECONFIG=${runnerK3sKubeconfigPath}`, "kubectl", ...args].join(" ");
|
||||
}
|
||||
|
||||
function commandExists(command) {
|
||||
const result = commandResult("bash", ["-lc", `command -v ${command}`], { timeoutMs: 2000 });
|
||||
return result.exitCode === 0 && result.stdout.trim().length > 0;
|
||||
}
|
||||
|
||||
function collectLocalRuntimeObservations() {
|
||||
function fileReadable(filePath) {
|
||||
try {
|
||||
accessSync(filePath, fsConstants.R_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function collectD601RunnerObservability(ingress) {
|
||||
const args = ["-n", namespace, "get", "pods", "-o", "name"];
|
||||
const probe = commandResult("env", [`KUBECONFIG=${runnerK3sKubeconfigPath}`, "kubectl", ...args], {
|
||||
timeoutMs: 5000,
|
||||
maxChars: 2000
|
||||
});
|
||||
const stderr = oneLine(probe.stderr || "") || "empty";
|
||||
const stdoutLines = String(probe.stdout ?? "")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
const d601PublicEndpointsReachable = ingress.probes.some((item) => item.ok && isHwlabDevIdentity(item));
|
||||
const runnerKubeconfigReadable = fileReadable(runnerK3sKubeconfigPath);
|
||||
|
||||
return {
|
||||
runnerKubeconfigReadable,
|
||||
runnerKubeconfigProbeOk: probe.exitCode === 0,
|
||||
runnerKubeconfigProbeExitCode: probe.exitCode,
|
||||
runnerKubeconfigProbeStderr: stderr,
|
||||
d601PublicEndpointsReachable,
|
||||
d601K3sUnavailable: false,
|
||||
classification: "runner_permission_mount_gap",
|
||||
sourceIssue: "pikasTech/HWLAB#46",
|
||||
command: runnerKubeconfigCommandText(args),
|
||||
stdoutSummary: probe.exitCode === 0 ? `${stdoutLines.length} pod name(s) observed` : undefined,
|
||||
summary: "Runner /etc/rancher/k3s/k3s.yaml is not treated as readable from this container; a kubectl success may come from alternate in-cluster config, so file readability and command exit code stay separate.",
|
||||
inferenceRule: "runnerKubeconfigReadable=false must not imply d601K3sUnavailable=true; public 16666/16667 endpoint reachability is tracked separately.",
|
||||
secretValuesRead: false,
|
||||
secretResourcesRead: false
|
||||
};
|
||||
}
|
||||
|
||||
function collectLocalRuntimeObservations(d601Observability) {
|
||||
const observations = [];
|
||||
|
||||
if (commandExists("docker")) {
|
||||
@@ -206,6 +253,13 @@ function collectLocalRuntimeObservations() {
|
||||
});
|
||||
}
|
||||
|
||||
observations.push({
|
||||
id: "runner-k3s-kubeconfig-readonly",
|
||||
status: d601Observability.runnerKubeconfigProbeOk ? "observed" : "blocked",
|
||||
command: d601Observability.command,
|
||||
summary: `${d601Observability.summary} runnerKubeconfigReadable=${d601Observability.runnerKubeconfigReadable}; exitCode=${d601Observability.runnerKubeconfigProbeExitCode}; stderr=${d601Observability.runnerKubeconfigProbeStderr}.`
|
||||
});
|
||||
|
||||
return observations;
|
||||
}
|
||||
|
||||
@@ -587,6 +641,7 @@ function baseReport({ commitId, observedAt }) {
|
||||
],
|
||||
runtimeTarget: {
|
||||
endpoint: DEV_ENDPOINT,
|
||||
frontendEndpoint: DEV_FRONTEND_ENDPOINT,
|
||||
namespace,
|
||||
environment: ENVIRONMENT_DEV,
|
||||
requiredBoxSimulators: 2,
|
||||
@@ -623,6 +678,17 @@ function baseReport({ commitId, observedAt }) {
|
||||
],
|
||||
readOnlySupplementalEvidence: [],
|
||||
localRuntimeObservations: [],
|
||||
d601Observability: {
|
||||
runnerKubeconfigReadable: false,
|
||||
runnerKubeconfigProbeOk: false,
|
||||
runnerKubeconfigProbeExitCode: null,
|
||||
runnerKubeconfigProbeStderr: "not_observed",
|
||||
d601PublicEndpointsReachable: false,
|
||||
d601K3sUnavailable: false,
|
||||
classification: "not_observed",
|
||||
sourceIssue: "pikasTech/HWLAB#46",
|
||||
inferenceRule: "runnerKubeconfigReadable=false must not imply d601K3sUnavailable=true; public 16666/16667 endpoint reachability is tracked separately."
|
||||
},
|
||||
liveOperation: {
|
||||
status: "not_run",
|
||||
operationId: "not_observed",
|
||||
@@ -683,7 +749,8 @@ async function main() {
|
||||
report.readOnlySupplementalEvidence = await collectReadOnlySupplementalEvidence();
|
||||
|
||||
const ingress = await observeDevIngress();
|
||||
report.localRuntimeObservations = collectLocalRuntimeObservations();
|
||||
report.d601Observability = collectD601RunnerObservability(ingress);
|
||||
report.localRuntimeObservations = collectLocalRuntimeObservations(report.d601Observability);
|
||||
report.liveChecks.push({
|
||||
id: "dev-ingress-health",
|
||||
status: ingress.accepted ? "pass" : "blocked",
|
||||
@@ -727,21 +794,22 @@ async function main() {
|
||||
if (missing.length > 0) {
|
||||
addNotRunM3Checks(report, "Stopped before M3 direct checks because service target URLs were not provided.");
|
||||
report.blockers.push({
|
||||
type: "runtime_blocker",
|
||||
type: "observability_blocker",
|
||||
scope: "m3-service-discovery",
|
||||
status: "open",
|
||||
classification: "hardware loop",
|
||||
summary: `DEV ingress is reachable, but M3 smoke lacks direct DEV service URLs: ${missing.join(", ")}.`
|
||||
classification: "runner permission/mount gap",
|
||||
sourceIssue: "pikasTech/HWLAB#46",
|
||||
summary: `DEV ingress is reachable on frozen :16667, but this runner cannot use ${runnerK3sKubeconfigPath} as readable kubeconfig to discover direct M3 service URLs (${missing.join(", ")}); this is a #46 runner permission/mount or read-only observability gap, not D601 global offline.`
|
||||
});
|
||||
report.summary = {
|
||||
status: "blocked",
|
||||
classification: "hardware loop",
|
||||
classification: "runner permission/mount gap",
|
||||
observedAt,
|
||||
result: "DEV ingress was reachable, but live M3 simulator and patch-panel targets were not discoverable."
|
||||
};
|
||||
await writeReport(report, reportPath);
|
||||
console.log(`[dev-m3-smoke] status=blocked report=${relativePath(reportPath)}`);
|
||||
console.log("[dev-m3-smoke] blocker=runtime_blocker scope=m3-service-discovery");
|
||||
console.log("[dev-m3-smoke] blocker=observability_blocker scope=m3-service-discovery");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user