docs: add M3 rollout support runbook (#81)
Co-authored-by: HWLAB Code Queue <code-queue@pikastech.local>
This commit is contained in:
@@ -11,7 +11,8 @@ entry points that later service implementations must follow.
|
||||
- MVP scope is DEV only.
|
||||
- DEV browser/frontend endpoint is fixed at `http://74.48.78.17:16666/`.
|
||||
- DEV API/edge/live endpoint is fixed at `http://74.48.78.17:16667`.
|
||||
- PROD is not an MVP acceptance target.
|
||||
- Legacy `:6666` and `:6667` are historical/deprecated only and are not MVP
|
||||
acceptance targets.
|
||||
- UniDesk may be used only as an external scheduling, CI, or CD base.
|
||||
- HWLAB runtime services must not be replaced by UniDesk backend,
|
||||
provider-gateway, or microservice proxy implementations.
|
||||
@@ -60,6 +61,7 @@ The MVP branches may add implementation files under these paths:
|
||||
- MVP e2e contract: [protocol/mvp-e2e-contract.md](protocol/mvp-e2e-contract.md)
|
||||
- JSON Schemas: [protocol/schemas](protocol/schemas)
|
||||
- Deploy schema: [deploy/deploy.schema.json](deploy/deploy.schema.json)
|
||||
- M3 rollout support runbook: [docs/reference/m3-loop-rollout-runbook.md](docs/reference/m3-loop-rollout-runbook.md)
|
||||
|
||||
## Validation
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# M3 Loop Rollout Runbook
|
||||
|
||||
This reference is support / diagnostics / contract for the HWLAB P0 M3 virtual
|
||||
hardware trusted loop only. It is not P0 live evidence and it must not relabel
|
||||
SOURCE / LOCAL / DRY-RUN / fixture output as `DEV-LIVE`.
|
||||
|
||||
## M3 Loop Boundary
|
||||
|
||||
- P0 scope: `2 x hwlab-box-simu`, `2 x hwlab-gateway-simu`, `1 x hwlab-patch-panel`.
|
||||
- Accepted chain for live M3 proof: `DO1 -> patch-panel -> DI1`.
|
||||
- Current public endpoints: browser/frontend `http://74.48.78.17:16666/` and
|
||||
API/edge/live `http://74.48.78.17:16667/`.
|
||||
- Legacy public ports `:6666` and `:6667` are historical / deprecated only.
|
||||
- `pikasTech/HWLAB#63` remains a known CD blocker until suspended Job immutable
|
||||
apply is fixed.
|
||||
|
||||
## Source Of Truth
|
||||
|
||||
`deploy/deploy.json` is the deploy intent source. `deploy/artifact-catalog.dev.json`
|
||||
is the artifact identity source. No shell scrollback, issue comment, or report
|
||||
fixture is authoritative over those two files.
|
||||
|
||||
| Fact | Read From | Notes |
|
||||
| --- | --- | --- |
|
||||
| source commit | `deploy/deploy.json.commitId` and `deploy/artifact-catalog.dev.json.commitId` | They must match. |
|
||||
| image tag | `deploy/deploy.json.services[].image` and `deploy/artifact-catalog.dev.json.services[].imageTag` | The tag must stay tied to the source commit. |
|
||||
| digest | `deploy/artifact-catalog.dev.json.services[].digest` | Real digests come only from publish evidence; otherwise keep `not_published`. |
|
||||
| public endpoint | `deploy/deploy.json.publicEndpoints.*.url` and `deploy/deploy.json.endpoint` | Current endpoints are `16666/16667`; do not backslide to legacy ports. |
|
||||
| frp | `deploy/deploy.json.frp.proxies[]` | Derive proxy name, local service, local port, and remote port from the manifest. |
|
||||
| k3s service | `deploy/deploy.json.k3s.serviceMappings[]` | Derive service name, namespace, port, and targetPort from the manifest. |
|
||||
| health path | `deploy/deploy.json.health.path` and `deploy/deploy.json.services[].healthPath` | Must stay on `/health/live`. |
|
||||
|
||||
## Rollout Support
|
||||
|
||||
Use this order for M3 loop support work:
|
||||
|
||||
1. Render the manifest contract and compare it with the artifact catalog.
|
||||
2. Verify the M3 inventory cardinality for the two boxes, two gateways, and
|
||||
one patch-panel.
|
||||
3. Check that the current public endpoints stay on `16666/16667`.
|
||||
4. Confirm the health path is `/health/live`.
|
||||
5. Record any manual hotfix facts back into the issue, PR, and runbook before
|
||||
closing the task.
|
||||
|
||||
Recommended checks:
|
||||
|
||||
```sh
|
||||
node scripts/deploy-contract-plan.mjs --pretty
|
||||
node scripts/deploy-contract-plan.mjs --check
|
||||
node scripts/validate-dev-m3-cardinality.mjs
|
||||
npm run docs:validate:m3-rollout
|
||||
```
|
||||
|
||||
These checks are support only. They do not apply to DEV, do not touch PROD, do
|
||||
not restart services, and do not prove live M3 acceptance.
|
||||
|
||||
## Rollback Support
|
||||
|
||||
If a manual hotfix or rollback is required, record the following before the
|
||||
task closes:
|
||||
|
||||
- source commit and branch or PR;
|
||||
- image reference, image tag, and digest;
|
||||
- public endpoint and whether it was read-only or routed;
|
||||
- FRP proxy name and ownership;
|
||||
- k3s service mapping and ownership;
|
||||
- health path used for verification;
|
||||
- any field that diverged from `deploy/deploy.json` or the artifact catalog.
|
||||
|
||||
Rollback support must keep the live chain honest. If the chain was not actually
|
||||
`DO1 -> patch-panel -> DI1`, do not claim live M3. Only write support,
|
||||
diagnostics, or contract evidence.
|
||||
|
||||
## Prohibited Actions
|
||||
|
||||
- Do not treat this runbook as a general console or PROD flow.
|
||||
- Do not deploy or smoke PROD.
|
||||
- Do not read, print, or infer secrets, tokens, kubeconfig, or tunnel
|
||||
credentials.
|
||||
- Do not restart Code Queue, `backend-core`, or any runtime service from the
|
||||
runner.
|
||||
- Do not run heavyweight e2e or destructive hardware tests.
|
||||
- Do not bypass `pikasTech/HWLAB#63` and then claim CD is green.
|
||||
- Do not promote SOURCE / LOCAL / DRY-RUN / fixture output to `DEV-LIVE`.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"artifact-catalog:refresh-blocked": "node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --blocked",
|
||||
"dev-artifact:preflight": "node scripts/dev-artifact-publish.mjs --preflight",
|
||||
"dev-artifact:publish": "node scripts/dev-artifact-publish.mjs --publish",
|
||||
"docs:validate:m3-rollout": "node scripts/validate-m3-rollout-runbook.mjs",
|
||||
"d601:k3s:readonly": "node scripts/d601-k3s-readonly-observability.mjs",
|
||||
"runner:issue-visibility:preflight": "node scripts/runner-issue-visibility-preflight.mjs"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env node
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const docPath = "docs/reference/m3-loop-rollout-runbook.md";
|
||||
|
||||
async function readText(relativePath) {
|
||||
return readFile(path.join(repoRoot, relativePath), "utf8");
|
||||
}
|
||||
|
||||
function extractSection(doc, heading) {
|
||||
const marker = `## ${heading}\n`;
|
||||
const start = doc.indexOf(marker);
|
||||
assert.ok(start >= 0, `missing section ${heading}`);
|
||||
const bodyStart = start + marker.length;
|
||||
const nextHeading = doc.indexOf("\n## ", bodyStart);
|
||||
return doc.slice(bodyStart, nextHeading >= 0 ? nextHeading + 1 : doc.length);
|
||||
}
|
||||
|
||||
function assertIncludes(text, snippet, label) {
|
||||
assert.ok(text.includes(snippet), `${label} must include ${snippet}`);
|
||||
}
|
||||
|
||||
function assertNotIncludes(text, snippet, label) {
|
||||
assert.equal(text.includes(snippet), false, `${label} must not include ${snippet}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const doc = await readText(docPath);
|
||||
const boundary = extractSection(doc, "M3 Loop Boundary");
|
||||
|
||||
assertIncludes(doc, "support / diagnostics / contract", "m3 runbook");
|
||||
assertIncludes(doc, "2 x hwlab-box-simu", "m3 runbook");
|
||||
assertIncludes(doc, "2 x hwlab-gateway-simu", "m3 runbook");
|
||||
assertIncludes(doc, "1 x hwlab-patch-panel", "m3 runbook");
|
||||
assertIncludes(doc, "DO1 -> patch-panel -> DI1", "m3 runbook");
|
||||
assertIncludes(doc, "deploy/deploy.json", "m3 runbook");
|
||||
assertIncludes(doc, "deploy/artifact-catalog.dev.json", "m3 runbook");
|
||||
assertIncludes(doc, "pikasTech/HWLAB#63", "m3 runbook");
|
||||
assertIncludes(doc, "Do not promote SOURCE / LOCAL / DRY-RUN / fixture output to `DEV-LIVE`.", "m3 runbook");
|
||||
assertNotIncludes(boundary, "http://74.48.78.17:6666/", "m3 current boundary");
|
||||
assertNotIncludes(boundary, "http://74.48.78.17:6667/", "m3 current boundary");
|
||||
assertIncludes(boundary, "http://74.48.78.17:16666/", "m3 current boundary");
|
||||
assertIncludes(boundary, "http://74.48.78.17:16667/", "m3 current boundary");
|
||||
assertIncludes(boundary, ":6666", "m3 historical boundary");
|
||||
assertIncludes(boundary, ":6667", "m3 historical boundary");
|
||||
|
||||
console.log(`validated ${docPath}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await main();
|
||||
} catch (error) {
|
||||
process.stderr.write(`[validate-m3-rollout-runbook] ${error instanceof Error ? error.message : String(error)}\n`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user