Source commit: 55b5a563d6421f49b5f5e77bc3c1f29752da8801 Public tree identity: sha256:52790e23e2b1806b00cc220c92edcfeed445600dcde620342af2b3369e7883fa
151 lines
8.9 KiB
JavaScript
Executable file
151 lines
8.9 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
const assert = require("assert");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const repo = path.resolve(__dirname, "..");
|
|
|
|
function read(relativePath) {
|
|
return fs.readFileSync(path.join(repo, relativePath), "utf8");
|
|
}
|
|
|
|
function expect(source, name, pattern) {
|
|
assert.match(source, pattern, `missing node lifecycle evidence: ${name}`);
|
|
}
|
|
|
|
const nodeMain = read("crates/clusterflux-node/src/daemon.rs");
|
|
const nodeIdentity = read("crates/clusterflux-node/src/node_identity.rs");
|
|
const cliNode = read("crates/clusterflux-cli/src/node.rs");
|
|
const nodeTaskReports = read("crates/clusterflux-node/src/task_reports.rs");
|
|
const nodeDebugAgent = read("crates/clusterflux-node/src/debug_agent.rs");
|
|
const nodeLib = read("crates/clusterflux-node/src/lib.rs");
|
|
const sharedWasmtimeRuntime = `${read("crates/clusterflux-wasm-runtime/src/lib.rs")}\n${read("crates/clusterflux-wasm-runtime/src/task_host_linker.rs")}`;
|
|
const nodeRuntimeSurface = `${nodeLib}\n${sharedWasmtimeRuntime}`;
|
|
const nodeLifecycleSurface = `${nodeMain}\n${nodeIdentity}\n${nodeTaskReports}\n${nodeDebugAgent}`;
|
|
const nodeAssignmentRunner = `${read("crates/clusterflux-node/src/assignment_runner.rs")}\n${read("crates/clusterflux-node/src/assignment_runner/control_watcher.rs")}\n${read("crates/clusterflux-node/src/assignment_runner/process_runner.rs")}\n${read("crates/clusterflux-node/src/assignment_runner/validation.rs")}`;
|
|
const coordinatorCore = read("crates/clusterflux-coordinator/src/lib.rs");
|
|
const coordinatorService = `${read("crates/clusterflux-coordinator/src/service.rs")}\n${read("crates/clusterflux-coordinator/src/service/routing.rs")}`;
|
|
const coordinatorServiceTests = read("crates/clusterflux-coordinator/src/service/tests.rs");
|
|
const coordinatorServiceSurface = `${coordinatorService}\n${coordinatorServiceTests}`;
|
|
const cliLocalRunSmoke = read("scripts/cli-local-run-smoke.js");
|
|
const liveSmoke = read("scripts/cli-happy-path-live-smoke.js");
|
|
const wasmtimeSmoke = read("scripts/wasmtime-node-smoke.js");
|
|
const debugCore = read("crates/clusterflux-core/src/debug.rs");
|
|
|
|
assert.strictEqual(
|
|
(nodeMain.match(/CoordinatorSession::connect/g) || []).length,
|
|
1,
|
|
"node runtime should open one coordinator session in the local process-boundary runtime"
|
|
);
|
|
|
|
for (const [name, pattern] of [
|
|
["enrollment exchange over session", /"type": "exchange_node_enrollment_grant"/],
|
|
["persisted node identity is reused locally", /"type": "node_identity_reused"/],
|
|
["heartbeat over session", /"type": "node_heartbeat"/],
|
|
["node-originated requests use signed envelope", /"type": "signed_node"/],
|
|
["capability report over session", /"type": "report_node_capabilities"/],
|
|
["task assignment polling over session", /"type": "poll_task_assignment"/],
|
|
["process start over session", /"type": "start_process"/],
|
|
["reconnect over session", /"type": "reconnect_node"/],
|
|
["debug command polling over session", /"type": "poll_debug_command"/],
|
|
["log event over session", /"type": "report_task_log"/],
|
|
["VFS metadata over session", /"type": "report_vfs_metadata"/],
|
|
["task control polling over session", /"type": "poll_task_control"/],
|
|
["completion over session", /"type": "task_completed"/],
|
|
["cancellation uses same session", /poll_task_cancellation\(session, args, &task, node_private_key\)/],
|
|
["request count is reported", /session\.requests\(\)/],
|
|
]) {
|
|
expect(nodeLifecycleSurface, name, pattern);
|
|
}
|
|
|
|
expect(cliNode, "user-authorized node attach over Client session", /"type": "attach_node"/);
|
|
assert.doesNotMatch(
|
|
nodeIdentity,
|
|
/"type": "attach_node"/,
|
|
"a persisted node must authenticate with its signed identity instead of replaying Client attach"
|
|
);
|
|
|
|
const runtimeAcceptanceSurface = `${cliLocalRunSmoke}\n${liveSmoke}\n${coordinatorServiceTests}\n${nodeLib}\n${nodeAssignmentRunner}`;
|
|
for (const [name, pattern] of [
|
|
["real CLI launches a coordinator main", /node_report\.run\.status, "main_launched"/],
|
|
["real CLI observes coordinator-main task spawning", /task_spawn_host_import, true/],
|
|
["real CLI keeps child task instances distinct", /every live task event must retain its unique instance identity/],
|
|
["signed active Wasm parent spawns and joins child", /fn signed_active_wasm_task_can_spawn_and_join_child_in_its_process_only\(\)/],
|
|
["controlled native process abort is exercised", /fn abort_requested[\s\S]*poll_task_control/],
|
|
["native lifecycle freeze and resume are exercised", /linux_task_lifecycle_supports_cancel_and_all_stop_freeze_resume/],
|
|
["strict live run requires a usable partial freeze", /partial_freeze\.partially_frozen/],
|
|
["strict live run requires hosted restart evidence", /serviceRestart\.executed === true/],
|
|
]) {
|
|
expect(runtimeAcceptanceSurface, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["cooperative cancellation and abort are distinct", /cancel_requested: false,[\s\S]*abort_requested: true/],
|
|
["controlled runner polls abort while command runs", /fn abort_requested[\s\S]*poll_task_control/],
|
|
["controlled runner creates a process group", /process\.process_group\(0\)/],
|
|
["controlled runner freezes the native process group", /libc::kill\(process_group, libc::SIGSTOP\)/],
|
|
["controlled runner resumes the native process group", /libc::kill\(process_group, libc::SIGCONT\)/],
|
|
["controlled runner kills the process group", /libc::kill\(process_group, libc::SIGKILL\)/],
|
|
["Wasm code can poll cooperative cancellation", /task_control_v1/],
|
|
["matched Wasm probes remain at a quiescent boundary", /TaskHostOperation::DebugProbe[\s\S]*enter_quiescent_host_boundary[\s\S]*leave_quiescent_host_boundary/],
|
|
["debug snapshots use the live Wasm task handle registry", /debug_handle_snapshot[\s\S]*task_handle_\{handle_id\}[\s\S]*state=active/],
|
|
["native command status comes from the controlled runner", /set_command_status[\s\S]*frozen command pid[\s\S]*native command exited with status/],
|
|
]) {
|
|
expect(`${coordinatorServiceSurface}\n${nodeLifecycleSurface}\n${sharedWasmtimeRuntime}\n${nodeAssignmentRunner}`, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["coordinator rejects stale process ownership", /fn node_reconnect_rejects_stale_process_epoch_after_restart\(\)/],
|
|
["reconnect preserves enrolled node identity", /reconnect_node\(&NodeId::from\("node"\), None\)/],
|
|
["stale process epoch is rejected", /CoordinatorError::StaleProcessEpoch/],
|
|
]) {
|
|
expect(coordinatorCore, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["coordinator delivers cancellation to connected node", /fn service_delivers_cancellation_to_connected_node_and_records_terminal_state\(\)/],
|
|
["node polls task control", /CoordinatorRequest::PollTaskControl/],
|
|
["cancelled terminal state is recorded", /TaskTerminalState::Cancelled/],
|
|
]) {
|
|
expect(coordinatorServiceSurface, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["native lifecycle test exists", /fn linux_task_lifecycle_supports_cancel_and_all_stop_freeze_resume\(\)/],
|
|
["native freeze succeeds when supported", /lifecycle\.freeze_for_debug_epoch\(\)\.unwrap\(\)/],
|
|
["native resume succeeds", /lifecycle\.resume_after_debug_epoch\(\)/],
|
|
["native cancel reaches lifecycle", /lifecycle\.cancel\(\)/],
|
|
["unsupported freeze errors", /BackendError::DebugFreezeUnsupported/],
|
|
["wasmtime runtime exposes freeze resume probe", /pub fn freeze_resume_i32_export_probe/],
|
|
["wasmtime runtime captures Wasm frame locals", /debug_i32_export_snapshot[\s\S]*local_values/],
|
|
["wasmtime runtime creates Wasm debug participant", /kind: DebugParticipantKind::WasmTask/],
|
|
["wasmtime debug participant carries local values", /local_values: snapshot\.local_values\.clone\(\)/],
|
|
["wasmtime runtime resumes after freeze", /epoch\.continue_all\(\)/],
|
|
]) {
|
|
expect(nodeRuntimeSurface, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["wasmtime smoke runs debug freeze resume mode", /--debug-freeze-resume/],
|
|
["wasmtime smoke verifies frozen state", /debugReport\.frozen_state, "Frozen"/],
|
|
["wasmtime smoke verifies resumed state", /debugReport\.resumed_state, "Running"/],
|
|
["wasmtime smoke verifies frame local values", /debugReport\.local_values[\s\S]*wasm_local_0/],
|
|
["wasmtime smoke proves node runtime reached wasm task", /node_runtime_reached_wasm_task/],
|
|
["wasmtime smoke proves node captured locals", /node_runtime_captured_wasm_locals/],
|
|
]) {
|
|
expect(wasmtimeSmoke, name, pattern);
|
|
}
|
|
|
|
for (const [name, pattern] of [
|
|
["debug model freezes wasm and command participants", /fn breakpoint_creates_all_stop_debug_epoch_for_wasm_and_command_tasks\(\)/],
|
|
["debug model rejects unsupported freeze", /fn debug_epoch_reports_failure_when_no_participant_can_freeze\(\)/],
|
|
["debug model resumes frozen participants", /fn continue_resumes_every_frozen_participant\(\)/],
|
|
["debug model includes captured locals", /local_values/],
|
|
["wasm participants are modeled", /DebugParticipantKind::WasmTask/],
|
|
["controlled native command participants are modeled", /DebugParticipantKind::ControlledNativeCommand/],
|
|
]) {
|
|
expect(debugCore, name, pattern);
|
|
}
|
|
|
|
console.log("Node lifecycle contract smoke passed");
|