@forgezero/agent 0.1.50 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/community-rehearsal-host.d.ts +2 -0
- package/dist/community-rehearsal-host.js +259 -10
- package/dist/fz-agent.js +11 -1
- package/dist/fz.js +1 -1
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +1 -1
- package/dist/platform-fleet-verification.js +1 -1
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent-heartbeat.js
CHANGED
package/dist/bootstrap.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SoftwareExec } from './software';
|
|
1
2
|
export declare const COMMUNITY_REHEARSAL_VERSION = "3.11.14";
|
|
2
3
|
export declare const COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
|
|
3
4
|
export declare const COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
|
|
@@ -43,6 +44,7 @@ export type CommunityHostOperation = {
|
|
|
43
44
|
stdin?: string;
|
|
44
45
|
accepted?: readonly number[];
|
|
45
46
|
};
|
|
47
|
+
export declare function ensureCommunityRehearsalArango(execute?: SoftwareExec): Promise<void>;
|
|
46
48
|
export declare function parseCommunityRehearsalHostRequest(value: unknown): CommunityRehearsalHostRequest;
|
|
47
49
|
export declare function planCommunityRehearsalPrepare(request: Extract<CommunityRehearsalHostRequest, {
|
|
48
50
|
action: 'prepare';
|
|
@@ -1,6 +1,244 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/software.ts
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
accessSync,
|
|
5
|
+
chmodSync,
|
|
6
|
+
copyFileSync,
|
|
7
|
+
mkdtempSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
symlinkSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
writeFileSync
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
var PINNED_BUN_VERSION = "1.3.14";
|
|
19
|
+
var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
|
|
20
|
+
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
21
|
+
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
22
|
+
var OS_CATALOG = [
|
|
23
|
+
{ id: "ubuntu", version: "26.04", architecture: "x64", status: "active" }
|
|
24
|
+
];
|
|
25
|
+
var SOFTWARE_CATALOG = [
|
|
26
|
+
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
27
|
+
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
28
|
+
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
29
|
+
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
30
|
+
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
31
|
+
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
32
|
+
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
33
|
+
];
|
|
34
|
+
var UBUNTU_2604_X64 = [
|
|
35
|
+
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
36
|
+
{ requirement: { id: "nginx", version: "ubuntu-26.04" } },
|
|
37
|
+
{ requirement: { id: "arangodb", version: "3.11.14" } },
|
|
38
|
+
{ requirement: { id: "cloudflared", version: "2026.7.3" } },
|
|
39
|
+
{ requirement: { id: "cloudflare-warp", version: "2026.6.822.0-min" } },
|
|
40
|
+
{ requirement: { id: "ufw", version: "ubuntu-26.04" } },
|
|
41
|
+
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } }
|
|
42
|
+
];
|
|
43
|
+
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
44
|
+
var run = async (argv, env = {}) => {
|
|
45
|
+
const child = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe", env: { PATH: path, LANG: "C", LC_ALL: "C", ...env } });
|
|
46
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
47
|
+
new Response(child.stdout).text(),
|
|
48
|
+
new Response(child.stderr).text(),
|
|
49
|
+
child.exited
|
|
50
|
+
]);
|
|
51
|
+
return { exitCode, output: `${stdout}${stderr}` };
|
|
52
|
+
};
|
|
53
|
+
var download = async (url, destination, sha256) => {
|
|
54
|
+
const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(120000) });
|
|
55
|
+
if (!response.ok)
|
|
56
|
+
throw new Error(`download failed with HTTP ${response.status}`);
|
|
57
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
58
|
+
if (createHash("sha256").update(bytes).digest("hex") !== sha256)
|
|
59
|
+
throw new Error("download checksum mismatch");
|
|
60
|
+
writeFileSync(destination, bytes, { mode: 384, flag: "wx" });
|
|
61
|
+
};
|
|
62
|
+
var successful = (result, pattern) => result.exitCode === 0 && (!pattern || pattern.test(result.output));
|
|
63
|
+
var aptInstall = async (name) => {
|
|
64
|
+
const environment = { DEBIAN_FRONTEND: "noninteractive" };
|
|
65
|
+
const update = await run(["/usr/bin/apt-get", "update", "-qq"], environment);
|
|
66
|
+
return update.exitCode === 0 ? run(["/usr/bin/apt-get", "install", "-y", name], environment) : update;
|
|
67
|
+
};
|
|
68
|
+
async function executeSoftwareOperation(operation) {
|
|
69
|
+
const { software, version } = operation;
|
|
70
|
+
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
71
|
+
return { exitCode: 2, output: "unsupported software operation" };
|
|
72
|
+
}
|
|
73
|
+
if (operation.kind === "check") {
|
|
74
|
+
if (software === "bun")
|
|
75
|
+
return run(["/usr/local/bin/bun", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /^1\.3\.14\s*$/m) ? 0 : 1 }));
|
|
76
|
+
if (software === "nginx") {
|
|
77
|
+
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
78
|
+
return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
|
|
79
|
+
}
|
|
80
|
+
if (software === "arangodb") {
|
|
81
|
+
const binary = await run(["/usr/bin/arangod", "--version"]);
|
|
82
|
+
if (!successful(binary, /3\.11\.14/))
|
|
83
|
+
return { ...binary, exitCode: 1 };
|
|
84
|
+
const [active, enabled] = await Promise.all([
|
|
85
|
+
run(["/usr/bin/systemctl", "is-active", "--quiet", "arangodb3.service"]),
|
|
86
|
+
run(["/usr/bin/systemctl", "is-enabled", "--quiet", "arangodb3.service"])
|
|
87
|
+
]);
|
|
88
|
+
return active.exitCode !== 0 && enabled.exitCode !== 0 ? { exitCode: 0, output: binary.output } : { exitCode: 1, output: "vendor standalone unit remains active or enabled" };
|
|
89
|
+
}
|
|
90
|
+
if (software === "cloudflared")
|
|
91
|
+
return run(["/usr/local/bin/cloudflared", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /2026\.7\.3/) ? 0 : 1 }));
|
|
92
|
+
if (software === "cloudflare-warp")
|
|
93
|
+
return run(["/usr/bin/warp-cli", "--version"]).then((result) => {
|
|
94
|
+
const match = result.output.match(/(\d{4})\.(\d+)\.(\d+)\.(\d+)/);
|
|
95
|
+
const observed = match?.slice(1).map(Number);
|
|
96
|
+
const minimum = [2026, 6, 822, 0];
|
|
97
|
+
const supported = observed && observed.some((part, index) => part > minimum[index] && observed.slice(0, index).every((prior, priorIndex) => prior === minimum[priorIndex])) || observed?.every((part, index) => part === minimum[index]);
|
|
98
|
+
return { ...result, exitCode: result.exitCode === 0 && supported ? 0 : 1 };
|
|
99
|
+
});
|
|
100
|
+
const binaries = software === "ufw" ? ["/usr/sbin/ufw"] : ["/usr/bin/ssh", "/usr/bin/scp", "/usr/bin/ssh-keyscan", "/usr/bin/ssh-keygen"];
|
|
101
|
+
try {
|
|
102
|
+
binaries.forEach((binary) => accessSync(binary));
|
|
103
|
+
return { exitCode: 0, output: "" };
|
|
104
|
+
} catch (cause) {
|
|
105
|
+
return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
109
|
+
const installed = await aptInstall(software === "openssh-client" ? "openssh-client" : software);
|
|
110
|
+
if (installed.exitCode !== 0 || software !== "nginx")
|
|
111
|
+
return installed;
|
|
112
|
+
return run(["/usr/bin/systemctl", "enable", "--now", "nginx.service"]);
|
|
113
|
+
}
|
|
114
|
+
const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
|
|
115
|
+
try {
|
|
116
|
+
if (software === "cloudflare-warp") {
|
|
117
|
+
const key = join(directory, "cloudflare-warp-key.gpg");
|
|
118
|
+
await download("https://pkg.cloudflareclient.com/pubkey.gpg", key, "0f37fc298c98e88ee3c0ee68c95b69f1dba9eb477abe3167e13982105911264d");
|
|
119
|
+
mkdirSync("/usr/share/keyrings", { recursive: true, mode: 493 });
|
|
120
|
+
const dearmored = await run([
|
|
121
|
+
"/usr/bin/gpg",
|
|
122
|
+
"--batch",
|
|
123
|
+
"--yes",
|
|
124
|
+
"--dearmor",
|
|
125
|
+
"-o",
|
|
126
|
+
"/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg",
|
|
127
|
+
key
|
|
128
|
+
]);
|
|
129
|
+
if (dearmored.exitCode !== 0)
|
|
130
|
+
return dearmored;
|
|
131
|
+
mkdirSync("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
|
|
132
|
+
writeFileSync("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ resolute main
|
|
133
|
+
`, { mode: 420 });
|
|
134
|
+
return aptInstall("cloudflare-warp");
|
|
135
|
+
}
|
|
136
|
+
if (software === "bun") {
|
|
137
|
+
const archive = join(directory, "bun.zip");
|
|
138
|
+
await download("https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip", archive, BUN_RELEASE_SHA256);
|
|
139
|
+
const unpacked = join(directory, "unpacked");
|
|
140
|
+
mkdirSync(unpacked, { mode: 448 });
|
|
141
|
+
const unzipped = await run(["/usr/bin/unzip", "-q", archive, "-d", unpacked]);
|
|
142
|
+
if (unzipped.exitCode !== 0)
|
|
143
|
+
return unzipped;
|
|
144
|
+
mkdirSync("/usr/local/lib/forgezero/runtime", { recursive: true, mode: 493 });
|
|
145
|
+
copyFileSync(join(unpacked, "bun-linux-x64", "bun"), "/usr/local/lib/forgezero/runtime/bun.next");
|
|
146
|
+
chmodSync("/usr/local/lib/forgezero/runtime/bun.next", 493);
|
|
147
|
+
renameSync("/usr/local/lib/forgezero/runtime/bun.next", "/usr/local/lib/forgezero/runtime/bun");
|
|
148
|
+
try {
|
|
149
|
+
unlinkSync("/usr/local/bin/bun");
|
|
150
|
+
} catch {}
|
|
151
|
+
symlinkSync("/usr/local/lib/forgezero/runtime/bun", "/usr/local/bin/bun");
|
|
152
|
+
return { exitCode: 0, output: "" };
|
|
153
|
+
}
|
|
154
|
+
if (software === "cloudflared") {
|
|
155
|
+
const binary = join(directory, "cloudflared");
|
|
156
|
+
await download("https://github.com/cloudflare/cloudflared/releases/download/2026.7.3/cloudflared-linux-amd64", binary, CLOUDFLARED_SHA256);
|
|
157
|
+
chmodSync(binary, 493);
|
|
158
|
+
copyFileSync(binary, "/usr/local/bin/cloudflared");
|
|
159
|
+
chmodSync("/usr/local/bin/cloudflared", 493);
|
|
160
|
+
return { exitCode: 0, output: "" };
|
|
161
|
+
}
|
|
162
|
+
const deb = join(directory, "arangodb.deb");
|
|
163
|
+
await download("https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb", deb, ARANGO_SHA256);
|
|
164
|
+
let installed = await run(["/usr/bin/dpkg", "-i", deb], { DEBIAN_FRONTEND: "noninteractive" });
|
|
165
|
+
if (installed.exitCode !== 0)
|
|
166
|
+
installed = await run(["/usr/bin/apt-get", "-y", "-f", "install"], { DEBIAN_FRONTEND: "noninteractive" });
|
|
167
|
+
if (installed.exitCode !== 0)
|
|
168
|
+
return installed;
|
|
169
|
+
await run(["/usr/bin/systemctl", "disable", "--now", "arangodb3.service"]);
|
|
170
|
+
return { exitCode: 0, output: "" };
|
|
171
|
+
} finally {
|
|
172
|
+
rmSync(directory, { recursive: true, force: true });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8"), architecture = process.arch) {
|
|
176
|
+
const values = Object.fromEntries(osRelease.split(`
|
|
177
|
+
`).flatMap((line) => {
|
|
178
|
+
const separator = line.indexOf("=");
|
|
179
|
+
return separator > 0 ? [[line.slice(0, separator), line.slice(separator + 1).replace(/^['"]|['"]$/g, "")]] : [];
|
|
180
|
+
}));
|
|
181
|
+
return {
|
|
182
|
+
os: { id: (values.ID ?? "unknown").toLowerCase(), versionId: values.VERSION_ID ?? "unknown" },
|
|
183
|
+
architecture
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function validateSoftwareRequirements(value, _options = {}) {
|
|
187
|
+
if (!Array.isArray(value) || value.length > 32)
|
|
188
|
+
throw new Error("software requirements must be an array of at most 32 entries");
|
|
189
|
+
const seen = new Set;
|
|
190
|
+
return value.map((item) => {
|
|
191
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
192
|
+
throw new Error("software requirement must be an object");
|
|
193
|
+
const row = item;
|
|
194
|
+
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
195
|
+
throw new Error("software requirement contains an unknown field");
|
|
196
|
+
}
|
|
197
|
+
if (!["bun", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
198
|
+
throw new Error("software requirement coordinate is invalid");
|
|
199
|
+
}
|
|
200
|
+
const requirement = { id: row.id, version: row.version };
|
|
201
|
+
if (seen.has(requirement.id))
|
|
202
|
+
throw new Error(`duplicate software requirement: ${requirement.id}`);
|
|
203
|
+
seen.add(requirement.id);
|
|
204
|
+
const catalog = SOFTWARE_CATALOG.find((candidate) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
205
|
+
if (!catalog || catalog.status !== "active") {
|
|
206
|
+
throw new Error(`software requirement is not active: ${requirement.id}@${requirement.version}`);
|
|
207
|
+
}
|
|
208
|
+
return requirement;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
212
|
+
const requirements = validateSoftwareRequirements(requirementsInput);
|
|
213
|
+
const observation = options.observation ?? observeSoftwareHost();
|
|
214
|
+
const os = OS_CATALOG.find((candidate) => candidate.id === observation.os.id && candidate.version === observation.os.versionId && candidate.architecture === observation.architecture);
|
|
215
|
+
if (!os || os.status !== "active") {
|
|
216
|
+
throw new Error(`unsupported software strategy: ${observation.os.id} ${observation.os.versionId} ${observation.architecture}`);
|
|
217
|
+
}
|
|
218
|
+
const results = [];
|
|
219
|
+
for (const requirement of requirements) {
|
|
220
|
+
const strategy = UBUNTU_2604_X64.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
221
|
+
if (!strategy)
|
|
222
|
+
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
223
|
+
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
224
|
+
if (before.exitCode === 0) {
|
|
225
|
+
results.push({ ...requirement, changed: false });
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const installed = await options.exec({ kind: "install", software: requirement.id, version: requirement.version });
|
|
229
|
+
if (installed.exitCode !== 0)
|
|
230
|
+
throw new Error(`could not install ${requirement.id}@${requirement.version}: ${installed.output.trim()}`);
|
|
231
|
+
const after = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
232
|
+
if (after.exitCode !== 0)
|
|
233
|
+
throw new Error(`${requirement.id}@${requirement.version} did not pass its post-install check`);
|
|
234
|
+
results.push({ ...requirement, changed: true });
|
|
235
|
+
}
|
|
236
|
+
return results;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/community-rehearsal-host.ts
|
|
240
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
241
|
+
import { lstatSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4
242
|
import { dirname } from "node:path";
|
|
5
243
|
var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
|
|
6
244
|
var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
|
|
@@ -13,6 +251,15 @@ var NODES = new Map([
|
|
|
13
251
|
["dev-fz-n4", { address: "10.42.0.24", agency: false }]
|
|
14
252
|
]);
|
|
15
253
|
var RELEASE = /^[a-f0-9]{64}$/;
|
|
254
|
+
async function ensureCommunityRehearsalArango(execute = executeSoftwareOperation) {
|
|
255
|
+
const requirement = { software: "arangodb", version: COMMUNITY_REHEARSAL_VERSION };
|
|
256
|
+
if ((await execute({ kind: "check", ...requirement })).exitCode === 0)
|
|
257
|
+
return;
|
|
258
|
+
const installed = await execute({ kind: "install", ...requirement });
|
|
259
|
+
if (installed.exitCode !== 0 || (await execute({ kind: "check", ...requirement })).exitCode !== 0) {
|
|
260
|
+
throw new Error(`unable to install and verify ArangoDB ${COMMUNITY_REHEARSAL_VERSION}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
16
263
|
var exactObject = (value) => {
|
|
17
264
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
18
265
|
throw new Error("community rehearsal request must be an object");
|
|
@@ -55,7 +302,7 @@ function parseCommunityRehearsalHostRequest(value) {
|
|
|
55
302
|
throw new Error("unsupported community rehearsal host action");
|
|
56
303
|
}
|
|
57
304
|
var databaseUnit = (node) => {
|
|
58
|
-
const
|
|
305
|
+
const join2 = node.name === "dev-fz-n1" ? "" : " --starter.join=10.42.0.21";
|
|
59
306
|
const role = node.agency ? "" : " --cluster.start-agent=false --cluster.start-coordinator=true --cluster.start-dbserver=true";
|
|
60
307
|
return `[Unit]
|
|
61
308
|
Description=ForgeZero isolated Community ${COMMUNITY_REHEARSAL_VERSION} rehearsal cluster
|
|
@@ -67,7 +314,7 @@ Type=simple
|
|
|
67
314
|
User=arangodb
|
|
68
315
|
Group=arangodb
|
|
69
316
|
LoadCredentialEncrypted=arangodb-jwt:${COMMUNITY_CREDENTIAL_ROOT}/arangodb-jwt.cred
|
|
70
|
-
ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${node.address} --starter.host=${node.address} --starter.data-dir=${COMMUNITY_DATABASE_ROOT} --starter.disable-ipv6 --auth.jwt-secret=%d/arangodb-jwt${
|
|
317
|
+
ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${node.address} --starter.host=${node.address} --starter.data-dir=${COMMUNITY_DATABASE_ROOT} --starter.disable-ipv6 --auth.jwt-secret=%d/arangodb-jwt${join2}${role}
|
|
71
318
|
Restart=always
|
|
72
319
|
RestartSec=5
|
|
73
320
|
LimitNOFILE=100000
|
|
@@ -168,19 +415,19 @@ async function exec(argv, stdin) {
|
|
|
168
415
|
async function applyOperations(operations) {
|
|
169
416
|
for (const operation of operations) {
|
|
170
417
|
if (operation.kind === "remove-tree")
|
|
171
|
-
|
|
418
|
+
rmSync2(operation.path, { recursive: true, force: true });
|
|
172
419
|
else if (operation.kind === "write") {
|
|
173
|
-
|
|
174
|
-
|
|
420
|
+
mkdirSync2(dirname(operation.path), { recursive: true });
|
|
421
|
+
writeFileSync2(operation.path, operation.content, { mode: operation.mode });
|
|
175
422
|
} else if (operation.kind === "unlink") {
|
|
176
423
|
try {
|
|
177
|
-
|
|
424
|
+
unlinkSync2(operation.path);
|
|
178
425
|
} catch (cause) {
|
|
179
426
|
if (cause.code !== "ENOENT")
|
|
180
427
|
throw cause;
|
|
181
428
|
}
|
|
182
429
|
} else if (operation.kind === "symlink")
|
|
183
|
-
|
|
430
|
+
symlinkSync2(operation.target, operation.path);
|
|
184
431
|
else {
|
|
185
432
|
const result = await exec(operation.argv, operation.stdin);
|
|
186
433
|
if (!(operation.accepted ?? [0]).includes(result.exitCode))
|
|
@@ -216,9 +463,10 @@ async function runCommunityRehearsalHost(request) {
|
|
|
216
463
|
const node = exactNode(request.node);
|
|
217
464
|
if (request.action === "prepare") {
|
|
218
465
|
const metadata = lstatSync(request.archivePath);
|
|
219
|
-
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 ||
|
|
466
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash2("sha256").update(readFileSync2(request.archivePath)).digest("hex") !== request.archiveSha256) {
|
|
220
467
|
throw new Error("community rehearsal archive is not the declared bounded release");
|
|
221
468
|
}
|
|
469
|
+
await ensureCommunityRehearsalArango();
|
|
222
470
|
await applyOperations(planCommunityRehearsalPrepare(request));
|
|
223
471
|
return { ok: true, action: request.action, node: node.name, release: request.release };
|
|
224
472
|
}
|
|
@@ -265,6 +513,7 @@ export {
|
|
|
265
513
|
runCommunityRehearsalHost,
|
|
266
514
|
planCommunityRehearsalPrepare,
|
|
267
515
|
parseCommunityRehearsalHostRequest,
|
|
516
|
+
ensureCommunityRehearsalArango,
|
|
268
517
|
COMMUNITY_REHEARSAL_VERSION,
|
|
269
518
|
COMMUNITY_REHEARSAL_ROOT,
|
|
270
519
|
COMMUNITY_DATABASE_ROOT,
|
package/dist/fz-agent.js
CHANGED
|
@@ -7000,7 +7000,7 @@ function assertSupportedGuestImage(imageKey) {
|
|
|
7000
7000
|
}
|
|
7001
7001
|
|
|
7002
7002
|
// src/version.ts
|
|
7003
|
-
var VERSION = "0.1.
|
|
7003
|
+
var VERSION = "0.1.51";
|
|
7004
7004
|
|
|
7005
7005
|
// src/ssh-bootstrap.ts
|
|
7006
7006
|
class SshBootstrapError extends Error {
|
|
@@ -13216,6 +13216,15 @@ var NODES = new Map([
|
|
|
13216
13216
|
["dev-fz-n4", { address: "10.42.0.24", agency: false }]
|
|
13217
13217
|
]);
|
|
13218
13218
|
var RELEASE = /^[a-f0-9]{64}$/;
|
|
13219
|
+
async function ensureCommunityRehearsalArango(execute3 = executeSoftwareOperation) {
|
|
13220
|
+
const requirement = { software: "arangodb", version: COMMUNITY_REHEARSAL_VERSION };
|
|
13221
|
+
if ((await execute3({ kind: "check", ...requirement })).exitCode === 0)
|
|
13222
|
+
return;
|
|
13223
|
+
const installed = await execute3({ kind: "install", ...requirement });
|
|
13224
|
+
if (installed.exitCode !== 0 || (await execute3({ kind: "check", ...requirement })).exitCode !== 0) {
|
|
13225
|
+
throw new Error(`unable to install and verify ArangoDB ${COMMUNITY_REHEARSAL_VERSION}`);
|
|
13226
|
+
}
|
|
13227
|
+
}
|
|
13219
13228
|
var exactObject = (value) => {
|
|
13220
13229
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
13221
13230
|
throw new Error("community rehearsal request must be an object");
|
|
@@ -13422,6 +13431,7 @@ async function runCommunityRehearsalHost(request) {
|
|
|
13422
13431
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash8("sha256").update(readFileSync16(request.archivePath)).digest("hex") !== request.archiveSha256) {
|
|
13423
13432
|
throw new Error("community rehearsal archive is not the declared bounded release");
|
|
13424
13433
|
}
|
|
13434
|
+
await ensureCommunityRehearsalArango();
|
|
13425
13435
|
await applyOperations(planCommunityRehearsalPrepare(request));
|
|
13426
13436
|
return { ok: true, action: request.action, node: node.name, release: request.release };
|
|
13427
13437
|
}
|
package/dist/fz.js
CHANGED
package/dist/metal-bootstrap.js
CHANGED
|
@@ -292,7 +292,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
// src/version.ts
|
|
295
|
-
var VERSION = "0.1.
|
|
295
|
+
var VERSION = "0.1.51";
|
|
296
296
|
|
|
297
297
|
// src/otel-collector.ts
|
|
298
298
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -1697,7 +1697,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
1697
1697
|
}
|
|
1698
1698
|
|
|
1699
1699
|
// src/version.ts
|
|
1700
|
-
var VERSION3 = "0.1.
|
|
1700
|
+
var VERSION3 = "0.1.51";
|
|
1701
1701
|
|
|
1702
1702
|
// src/egress-policy.ts
|
|
1703
1703
|
import { realpathSync as realpathSync2 } from "node:fs";
|
package/dist/provision.js
CHANGED
|
@@ -1697,7 +1697,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
1697
1697
|
}
|
|
1698
1698
|
|
|
1699
1699
|
// src/version.ts
|
|
1700
|
-
var VERSION3 = "0.1.
|
|
1700
|
+
var VERSION3 = "0.1.51";
|
|
1701
1701
|
|
|
1702
1702
|
// src/egress-policy.ts
|
|
1703
1703
|
import { realpathSync as realpathSync2 } from "node:fs";
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.51";
|