本地 K3s 节点此前在 Kubelet 中登记为宿主机资源,负载可挤压 etcd 并在 server 重启后继续污染同一 Run。现在为三节点设置真实可调度预算、独立 etcd/数据卷和锁定依赖镜像,并持续核对 server 与 API/etcd 健康。\n\n每次验收生成独立 Run ID,报告使用独占或原子写入,负载错误记录具体阶段;数据库鉴权不可用返回带 Retry-After 的 503,避免基础设施故障被误报为 401。\n\n验证:Go 全量测试、go vet、gofmt、OpenAPI、bash -n、ShellCheck、报告测试和 k3d 配置解析均通过。
343 lines
13 KiB
JavaScript
Executable File
343 lines
13 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { createHash } from 'node:crypto';
|
|
import { lstat, mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';
|
|
import { dirname, resolve } from 'node:path';
|
|
|
|
const shaPattern = /^[0-9a-f]{40}$/;
|
|
const digestPattern = /^sha256:[0-9a-f]{64}$/;
|
|
const hashPattern = /^[0-9a-f]{64}$/;
|
|
const unsafeKeyPattern = /(password|secret|token|credential|authorization|api.?key|access.?key|private.?key|connection.?string|database.?url|proxy)/i;
|
|
|
|
function parseArgs(argv) {
|
|
const command = argv.shift();
|
|
const values = {};
|
|
while (argv.length > 0) {
|
|
const key = argv.shift();
|
|
if (!key?.startsWith('--') || argv.length === 0) throw new Error('invalid arguments');
|
|
values[key.slice(2)] = argv.shift();
|
|
}
|
|
return { command, values };
|
|
}
|
|
|
|
async function regularJSON(path) {
|
|
const absolute = resolve(path);
|
|
const info = await lstat(absolute);
|
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`${path} must be a regular non-symlink file`);
|
|
return JSON.parse(await readFile(absolute, 'utf8'));
|
|
}
|
|
|
|
async function writeNewJSON(path, value) {
|
|
const absolute = resolve(path);
|
|
await mkdir(dirname(absolute), { recursive: true, mode: 0o700 });
|
|
await writeFile(absolute, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
}
|
|
|
|
async function replaceJSONAtomically(input, output, value) {
|
|
const inputPath = resolve(input);
|
|
const outputPath = resolve(output);
|
|
if (inputPath !== outputPath) {
|
|
await writeNewJSON(outputPath, value);
|
|
return;
|
|
}
|
|
const temporary = `${outputPath}.tmp-${process.pid}-${Date.now()}`;
|
|
try {
|
|
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
await rename(temporary, outputPath);
|
|
} catch (error) {
|
|
await unlink(temporary).catch(() => {});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function assertSecretSafe(value, path = '$') {
|
|
if (Array.isArray(value)) {
|
|
value.forEach((item, index) => assertSecretSafe(item, `${path}[${index}]`));
|
|
return;
|
|
}
|
|
if (value && typeof value === 'object') {
|
|
for (const [key, nested] of Object.entries(value)) {
|
|
if (key !== 'secretSafe' && unsafeKeyPattern.test(key)) {
|
|
throw new Error(`unsafe report key at ${path}.${key}`);
|
|
}
|
|
assertSecretSafe(nested, `${path}.${key}`);
|
|
}
|
|
return;
|
|
}
|
|
if (typeof value === 'string' && /(?:postgres(?:ql)?|https?):\/\/[^\s]+/i.test(value)) {
|
|
throw new Error(`URL-like value is forbidden at ${path}`);
|
|
}
|
|
}
|
|
|
|
function validate(report) {
|
|
if (report.schemaVersion !== 'acceptance-report/v1' || report.secretSafe !== true) {
|
|
throw new Error('unsupported or non-secret-safe acceptance report');
|
|
}
|
|
if (!report.runId || !shaPattern.test(report.release?.sourceSha ?? '') ||
|
|
!digestPattern.test(report.release?.apiImageDigest ?? '') ||
|
|
!digestPattern.test(report.release?.workerImageDigest ?? '') ||
|
|
!shaPattern.test(report.snapshot?.sourceReleaseSha ?? '') ||
|
|
!hashPattern.test(report.snapshot?.configHash ?? '') ||
|
|
!hashPattern.test(report.snapshot?.sha256 ?? '')) {
|
|
throw new Error('acceptance report identity fields are invalid');
|
|
}
|
|
if (!report.stages || !Array.isArray(report.gates) ||
|
|
report.promotion?.requiresManualConfirmation !== true ||
|
|
!['validation', 'live', 'not-applicable'].includes(report.promotion?.trafficMode)) {
|
|
throw new Error('acceptance report stages, gates, or promotion are invalid');
|
|
}
|
|
for (const [name, stage] of Object.entries(report.stages)) {
|
|
if (!['passed', 'failed', 'not-run'].includes(stage?.status)) {
|
|
throw new Error(`invalid stage status for ${name}`);
|
|
}
|
|
}
|
|
assertSecretSafe(report);
|
|
}
|
|
|
|
async function loadReports(directory) {
|
|
const entries = (await readdir(resolve(directory), { withFileTypes: true }))
|
|
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
|
.map((entry) => entry.name)
|
|
.sort();
|
|
const reports = [];
|
|
for (const name of entries) {
|
|
const report = await regularJSON(resolve(directory, name));
|
|
if (report.schemaVersion === 'acceptance-load-report/v1') {
|
|
if (report.secretSafe !== true) throw new Error(`load report ${name} is not secret-safe`);
|
|
reports.push({
|
|
file: name,
|
|
profile: report.profile,
|
|
passed: report.passed,
|
|
phases: report.phases,
|
|
failure: report.failure,
|
|
failureOperation: report.failureOperation,
|
|
startedAt: report.startedAt,
|
|
finishedAt: report.finishedAt
|
|
});
|
|
}
|
|
}
|
|
return reports;
|
|
}
|
|
|
|
async function buildLocal(values) {
|
|
for (const key of ['runtime', 'snapshot', 'reports', 'artifact', 'output']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const runtime = await regularJSON(values.runtime);
|
|
const snapshot = await regularJSON(values.snapshot);
|
|
const artifact = await regularJSON(values.artifact);
|
|
const loads = await loadReports(values.reports);
|
|
if (runtime.schemaVersion !== 'acceptance-runtime/v1' ||
|
|
snapshot.schemaVersion !== 'acceptance-snapshot/v1' ||
|
|
artifact.schemaVersion !== 'acceptance-artifact-smoke/v1') {
|
|
throw new Error('local report inputs have incompatible schema versions');
|
|
}
|
|
if (runtime.releaseSha !== artifact.releaseSha ||
|
|
artifact.apiImageDigest !== values['api-digest'] ||
|
|
runtime.snapshotConfigHash !== snapshot.source.configHash ||
|
|
runtime.snapshotSha256 !== snapshot.snapshotSha256) {
|
|
throw new Error('local report CAS identity mismatch');
|
|
}
|
|
const localPassed = loads.length > 0 && loads.every((item) => item.passed === true);
|
|
const gates = [
|
|
{ id: 'local_load_reports', passed: localPassed, detail: `${loads.length} load reports` },
|
|
{ id: 'amd64_artifact_smoke', passed: artifact.passed === true }
|
|
];
|
|
const report = {
|
|
schemaVersion: 'acceptance-report/v1',
|
|
runId: runtime.runId,
|
|
release: {
|
|
sourceSha: artifact.releaseSha,
|
|
apiImageDigest: artifact.apiImageDigest,
|
|
workerImageDigest: artifact.apiImageDigest
|
|
},
|
|
snapshot: {
|
|
sourceReleaseSha: snapshot.source.releaseSha,
|
|
configHash: snapshot.source.configHash,
|
|
sha256: snapshot.snapshotSha256
|
|
},
|
|
stages: {
|
|
localNative: { status: localPassed ? 'passed' : 'failed', loadReports: loads },
|
|
amd64Artifact: { status: artifact.passed ? 'passed' : 'failed', ...artifact },
|
|
onlineSimulation: { status: 'not-run' },
|
|
realCanary: { status: 'not-run' }
|
|
},
|
|
certifiedProfile: values['certified-profile'] ? {
|
|
name: values['certified-profile'],
|
|
source: 'local-candidate-only'
|
|
} : null,
|
|
gates,
|
|
promotion: {
|
|
ready: false,
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'not-applicable'
|
|
},
|
|
createdAt: new Date().toISOString(),
|
|
secretSafe: true
|
|
};
|
|
validate(report);
|
|
await writeNewJSON(values.output, report);
|
|
process.stdout.write(`acceptance_report_local=PASS sha256=${createHash('sha256').update(JSON.stringify(report)).digest('hex')}\n`);
|
|
}
|
|
|
|
async function buildLocalPartial(values) {
|
|
for (const key of ['runtime', 'snapshot', 'reports', 'output', 'failure-phase']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const runtime = await regularJSON(values.runtime);
|
|
const snapshot = await regularJSON(values.snapshot);
|
|
const loads = await loadReports(values.reports);
|
|
if (runtime.schemaVersion !== 'acceptance-runtime/v1' ||
|
|
snapshot.schemaVersion !== 'acceptance-snapshot/v1' ||
|
|
!shaPattern.test(runtime.releaseSha ?? '') ||
|
|
!digestPattern.test(runtime.apiImageDigest ?? '') ||
|
|
!digestPattern.test(runtime.workerImageDigest ?? '') ||
|
|
runtime.snapshotConfigHash !== snapshot.source.configHash ||
|
|
runtime.snapshotSha256 !== snapshot.snapshotSha256) {
|
|
throw new Error('partial local report inputs have incompatible CAS identity');
|
|
}
|
|
const completedLoadsPassed = loads.every((item) => item.passed === true);
|
|
const failureGate = values['failure-gate'] ?? 'local_execution_incomplete';
|
|
if (!/^[a-z0-9_]+$/.test(failureGate)) {
|
|
throw new Error('--failure-gate must be a stable lowercase gate ID');
|
|
}
|
|
const report = {
|
|
schemaVersion: 'acceptance-report/v1',
|
|
runId: runtime.runId,
|
|
release: {
|
|
sourceSha: runtime.releaseSha,
|
|
apiImageDigest: runtime.apiImageDigest,
|
|
workerImageDigest: runtime.workerImageDigest
|
|
},
|
|
snapshot: {
|
|
sourceReleaseSha: snapshot.source.releaseSha,
|
|
configHash: snapshot.source.configHash,
|
|
sha256: snapshot.snapshotSha256
|
|
},
|
|
stages: {
|
|
localNative: {
|
|
status: 'failed',
|
|
failurePhase: values['failure-phase'],
|
|
completedLoadReportsPassed: completedLoadsPassed,
|
|
loadReports: loads
|
|
},
|
|
amd64Artifact: { status: 'not-run' },
|
|
onlineSimulation: { status: 'not-run' },
|
|
realCanary: { status: 'not-run' }
|
|
},
|
|
certifiedProfile: values['certified-profile'] ? {
|
|
name: values['certified-profile'],
|
|
source: 'last-local-stable-candidate'
|
|
} : null,
|
|
gates: [
|
|
{
|
|
id: failureGate,
|
|
passed: false,
|
|
detail: `stopped during ${values['failure-phase']}`
|
|
},
|
|
{
|
|
id: 'completed_load_reports',
|
|
passed: completedLoadsPassed,
|
|
detail: `${loads.length} load reports`
|
|
}
|
|
],
|
|
promotion: {
|
|
ready: false,
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'not-applicable'
|
|
},
|
|
createdAt: new Date().toISOString(),
|
|
secretSafe: true
|
|
};
|
|
validate(report);
|
|
await writeNewJSON(values.output, report);
|
|
process.stdout.write('acceptance_report_local_partial=PASS\n');
|
|
}
|
|
|
|
async function mergeProduction(values) {
|
|
for (const key of ['local-report', 'production-summary', 'output']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const local = await regularJSON(values['local-report']);
|
|
const production = await regularJSON(values['production-summary']);
|
|
validate(local);
|
|
if (local.release.sourceSha !== production.releaseSha ||
|
|
local.release.apiImageDigest !== production.apiImageDigest ||
|
|
local.snapshot.configHash !== production.snapshotConfigHash ||
|
|
local.snapshot.sha256 !== production.snapshotSha256) {
|
|
throw new Error('production summary does not match local acceptance CAS fields');
|
|
}
|
|
local.stages.onlineSimulation = {
|
|
status: production.passed === true ? 'passed' : 'failed',
|
|
stableCapacityProfile: production.stableCapacityProfile,
|
|
tasks: production.tasks,
|
|
geminiTasks: production.geminiTasks,
|
|
videoTasks: production.videoTasks
|
|
};
|
|
local.stages.realCanary = {
|
|
status: production.realCanaryPassed === true ? 'passed' : 'failed'
|
|
};
|
|
local.certifiedProfile = production.certifiedProfile ?? local.certifiedProfile;
|
|
local.gates.push(...(production.gates ?? []));
|
|
local.promotion = {
|
|
ready: production.passed === true && production.realCanaryPassed === true &&
|
|
local.gates.every((gate) => gate.passed === true),
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'validation'
|
|
};
|
|
local.createdAt = new Date().toISOString();
|
|
validate(local);
|
|
await writeNewJSON(values.output, local);
|
|
process.stdout.write(`acceptance_report_production=PASS promotion_ready=${local.promotion.ready}\n`);
|
|
}
|
|
|
|
const { command, values } = parseArgs(process.argv.slice(2));
|
|
if (command === 'validate') {
|
|
const report = await regularJSON(values.input ?? '');
|
|
validate(report);
|
|
process.stdout.write('acceptance_report_validate=PASS\n');
|
|
} else if (command === 'build-local') {
|
|
await buildLocal(values);
|
|
} else if (command === 'build-local-partial') {
|
|
await buildLocalPartial(values);
|
|
} else if (command === 'merge-production') {
|
|
await mergeProduction(values);
|
|
} else if (command === 'mark-promoted') {
|
|
const input = values.input ?? '';
|
|
const output = values.output ?? input;
|
|
const report = await regularJSON(input);
|
|
validate(report);
|
|
if (report.promotion.ready !== true || report.promotion.trafficMode !== 'validation') {
|
|
throw new Error('only a ready validation report can be marked promoted');
|
|
}
|
|
report.promotion.trafficMode = 'live';
|
|
report.promotion.promotedAt = new Date().toISOString();
|
|
await replaceJSONAtomically(input, output, report);
|
|
process.stdout.write('acceptance_report_promoted=PASS\n');
|
|
} else if (command === 'attach-monitor') {
|
|
const input = values.input ?? '';
|
|
const output = values.output ?? input;
|
|
const report = await regularJSON(input);
|
|
const monitor = await regularJSON(values.monitor ?? '');
|
|
validate(report);
|
|
if (monitor.schemaVersion !== 'acceptance-monitor-report/v1' ||
|
|
monitor.runId !== report.runId || monitor.releaseSha !== report.release.sourceSha ||
|
|
monitor.secretSafe !== true) {
|
|
throw new Error('monitor report does not match overall acceptance report');
|
|
}
|
|
report.stages.postPromotionMonitor = {
|
|
status: monitor.passed === true ? 'passed' : 'failed',
|
|
startedAt: monitor.startedAt,
|
|
finishedAt: monitor.finishedAt,
|
|
samples: monitor.samples,
|
|
failureGateId: monitor.failureGateId
|
|
};
|
|
if (monitor.passed !== true) {
|
|
report.promotion.ready = false;
|
|
report.promotion.trafficMode = 'validation';
|
|
}
|
|
await replaceJSONAtomically(input, output, report);
|
|
process.stdout.write(`acceptance_report_monitor=PASS monitor_passed=${monitor.passed === true}\n`);
|
|
} else {
|
|
throw new Error('usage: report.mjs {validate|build-local|build-local-partial|merge-production|mark-promoted|attach-monitor} [options]');
|
|
}
|