Public release release-09ca780bd67a
Source commit: 09ca780bd67a00467e78139cf38466b8201b66ca Public tree identity: sha256:2f744c260b5fc2cf074ad9129f97f87f03714902e5b9fe174128afdc342ec2d0
This commit is contained in:
commit
eb1077f380
221 changed files with 81144 additions and 0 deletions
139
scripts/recovery-build-smoke.js
Executable file
139
scripts/recovery-build-smoke.js
Executable file
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { DapClient } = require("./dap-client");
|
||||
const { configurePodmanTestEnvironment } = require("./podman-test-env");
|
||||
|
||||
const repo = path.resolve(__dirname, "..");
|
||||
const project = path.join(repo, "examples/recovery-build");
|
||||
const sourcePath = fs.realpathSync(path.join(project, "src/lib.rs"));
|
||||
const original = fs.readFileSync(sourcePath, "utf8");
|
||||
const failing = "\"exit 23\".to_owned()";
|
||||
const replacement =
|
||||
"\"printf 'recovered\\n' > /clusterflux/output/recovering.txt\".to_owned()";
|
||||
assert(original.includes(failing), "recovery source must contain the real failing command");
|
||||
configurePodmanTestEnvironment(repo);
|
||||
|
||||
(async () => {
|
||||
const client = new DapClient({
|
||||
cwd: repo,
|
||||
env: {
|
||||
...process.env,
|
||||
CLUSTERFLUX_DAP_TIMEOUT_MS:
|
||||
process.env.CLUSTERFLUX_DAP_TIMEOUT_MS || "120000",
|
||||
},
|
||||
});
|
||||
let restored = false;
|
||||
try {
|
||||
const initialize = client.send("initialize", {
|
||||
adapterID: "clusterflux",
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
});
|
||||
await client.response(initialize, "initialize");
|
||||
|
||||
const launch = client.send("launch", {
|
||||
entry: "build",
|
||||
project,
|
||||
runtimeBackend: "local-services",
|
||||
});
|
||||
await client.response(launch, "launch");
|
||||
await client.waitFor(
|
||||
(message) => message.type === "event" && message.event === "initialized"
|
||||
);
|
||||
|
||||
const configured = client.send("configurationDone");
|
||||
await client.response(configured, "configurationDone");
|
||||
const failed = await client.waitFor(
|
||||
(message) =>
|
||||
message.type === "event" &&
|
||||
message.event === "stopped" &&
|
||||
message.body.reason === "exception"
|
||||
);
|
||||
assert.strictEqual(failed.body.allThreadsStopped, false);
|
||||
|
||||
const threadRequest = client.send("threads");
|
||||
const threads = (await client.response(threadRequest, "threads")).body.threads;
|
||||
const laneThreads = threads
|
||||
.filter((thread) => /build[_ ]lane/.test(thread.name))
|
||||
.sort((left, right) => left.id - right.id);
|
||||
assert.strictEqual(
|
||||
laneThreads.length,
|
||||
1,
|
||||
"only the failed recovering lane should remain a live DAP thread"
|
||||
);
|
||||
const recoveringThread = laneThreads[0];
|
||||
const views = JSON.parse(
|
||||
fs.readFileSync(path.join(project, ".clusterflux/views.json"), "utf8")
|
||||
);
|
||||
const nodeReport = JSON.parse(
|
||||
views.inspector.find((item) => item.label === "Node report").value
|
||||
);
|
||||
const laneSnapshots = nodeReport.task_snapshots.snapshots.filter(
|
||||
(snapshot) => snapshot.task_definition === "build_lane"
|
||||
);
|
||||
assert.strictEqual(laneSnapshots.length, 2);
|
||||
assert.notStrictEqual(laneSnapshots[0].task, laneSnapshots[1].task);
|
||||
assert(laneSnapshots.some((snapshot) => snapshot.state === "completed"));
|
||||
assert(
|
||||
laneSnapshots.some(
|
||||
(snapshot) => snapshot.state === "failed_awaiting_action"
|
||||
)
|
||||
);
|
||||
const startedIds = new Set(
|
||||
client.messages
|
||||
.filter(
|
||||
(message) =>
|
||||
message.type === "event" &&
|
||||
message.event === "thread" &&
|
||||
message.body.reason === "started"
|
||||
)
|
||||
.map((message) => message.body.threadId)
|
||||
);
|
||||
const laneStartedIds = [...startedIds]
|
||||
.filter((id) => id !== 1)
|
||||
.sort((left, right) => left - right)
|
||||
.slice(-2);
|
||||
assert.strictEqual(laneStartedIds.length, 2);
|
||||
assert.notStrictEqual(laneStartedIds[0], laneStartedIds[1]);
|
||||
assert(startedIds.has(recoveringThread.id));
|
||||
|
||||
fs.writeFileSync(sourcePath, original.replace(failing, replacement));
|
||||
const restart = client.send("restartFrame", {
|
||||
threadId: recoveringThread.id,
|
||||
});
|
||||
await client.response(restart, "restartFrame");
|
||||
const terminated = await client.waitFor(
|
||||
(message) =>
|
||||
message.type === "event" &&
|
||||
message.event === "terminated"
|
||||
);
|
||||
assert(terminated);
|
||||
|
||||
const exitedIds = new Set(
|
||||
client.messages
|
||||
.filter(
|
||||
(message) =>
|
||||
message.type === "event" &&
|
||||
message.event === "thread" &&
|
||||
message.body.reason === "exited"
|
||||
)
|
||||
.map((message) => message.body.threadId)
|
||||
);
|
||||
assert(exitedIds.has(laneStartedIds[0]));
|
||||
assert(exitedIds.has(recoveringThread.id));
|
||||
|
||||
fs.writeFileSync(sourcePath, original);
|
||||
restored = true;
|
||||
await client.close();
|
||||
console.log("Recovery build DAP restart smoke passed");
|
||||
} finally {
|
||||
if (!restored) fs.writeFileSync(sourcePath, original);
|
||||
if (client.child.exitCode === null) client.child.kill("SIGKILL");
|
||||
}
|
||||
})().catch((error) => {
|
||||
console.error(error.stack || error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue