guanghu-ice-heart/server-tools/lake-lamp-authz/ghdr-controller-broker.test.js

116 lines
3.8 KiB
JavaScript
Raw Permalink Normal View History

"use strict";
const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");
const { GhdrAuthorizer } = require("./ghdr-authorizer");
const { GhdrControllerBroker } = require("./ghdr-controller-broker");
test("two outbound controllers poll one email-authorized job and return independent layout signatures", async () => {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), "ghdr-broker-"));
const now = Math.floor(Date.now() / 1000);
const nodes = [
makeController("GH-CTRL-GZ-01", "tencent/ap-guangzhou/BS-GZ-006"),
makeController("GH-CTRL-SG-01", "tencent/ap-singapore/ZY-SG-006"),
];
const authorizer = new GhdrAuthorizer({
privateKeyFile: path.join(directory, "authorizer.pem"),
now: () => now,
});
const broker = new GhdrControllerBroker({
controllers: nodes.map(item => item.binding),
stateDir: path.join(directory, "jobs"),
authorizer,
now: () => now,
waitMs: 2000,
});
const plan = {
schema: "guanghu.ghdr-signed-layout-plan/v1",
payload: {
node_id: "GH-CVM-MAIN-PROD-01",
generation: 1,
},
signatures: [],
};
const digest = crypto.createHash("sha256").update(JSON.stringify(plan.payload)).digest("hex");
const binding = {
payload_sha256: digest,
resource: `GH-CVM-MAIN-PROD-01:${digest}:1`,
};
try {
const pending = broker.queueAndWait({
plan,
binding,
workorderId: "00000000-0000-4000-8000-000000000001",
});
const submitted = [];
for (const node of nodes) {
const poll = {
schema: "guanghu.ghdr-controller-poll/v1",
node_id: node.binding.node_id,
issued_at_unix: now,
nonce: crypto.randomBytes(24).toString("base64url"),
};
const body = {
request: poll,
request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(poll)), node.transport.privateKey).toString("hex"),
};
const job = broker.poll(body).job;
assert.ok(job);
assert.equal(broker.poll(body).error, "ghdr_controller_auth_replayed");
const layoutSignatureHex = crypto.sign(
null,
Buffer.from(JSON.stringify(plan.payload)),
node.layout.privateKey,
).toString("hex");
const result = {
schema: "guanghu.ghdr-controller-result/v1",
node_id: node.binding.node_id,
job_id: job.job_id,
layout_payload_sha256: digest,
signature_hex: layoutSignatureHex,
issued_at_unix: now,
nonce: crypto.randomBytes(24).toString("base64url"),
};
const signature = {
node_id: node.binding.node_id,
failure_domain: node.binding.failure_domain,
public_key_hex: node.binding.layout_public_key_hex,
signature_hex: layoutSignatureHex,
};
assert.equal(broker.submit({
request: result,
request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(result)), node.transport.privateKey).toString("hex"),
signature,
}).ok, true);
submitted.push(signature);
}
const completed = await pending;
assert.equal(completed.ok, true);
assert.deepEqual(completed.signatures, submitted);
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}
});
function makeController(nodeId, failureDomain) {
const layout = crypto.generateKeyPairSync("ed25519");
const transport = crypto.generateKeyPairSync("ed25519");
return {
layout,
transport,
binding: {
node_id: nodeId,
failure_domain: failureDomain,
layout_public_key_hex: rawPublic(layout.publicKey),
transport_public_key_hex: rawPublic(transport.publicKey),
},
};
}
function rawPublic(key) {
return key.export({ type: "spki", format: "der" }).subarray(-32).toString("hex");
}