@forgezero/agent 0.1.144 → 0.1.145
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 +9 -1
- package/dist/fz-agent.js +27 -1
- package/dist/fz.js +11 -3
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +9 -1
- package/dist/platform-bootstrap-runtime.d.ts +2 -0
- package/dist/platform-bootstrap-runtime.js +26 -0
- package/dist/platform-fleet-verification.js +27 -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
|
@@ -1460,7 +1460,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1460
1460
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1461
1461
|
|
|
1462
1462
|
// src/version.ts
|
|
1463
|
-
var VERSION = "0.1.
|
|
1463
|
+
var VERSION = "0.1.145";
|
|
1464
1464
|
|
|
1465
1465
|
// src/software.ts
|
|
1466
1466
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -3555,6 +3555,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
3555
3555
|
boundedInteger("greenPort", input.greenPort, 1024, 65535);
|
|
3556
3556
|
if (input.bluePort === input.greenPort)
|
|
3557
3557
|
throw new Error("Activation slot ports must differ.");
|
|
3558
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
3559
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
3560
|
+
}
|
|
3558
3561
|
boundedInteger("keepReleases", input.keepReleases, 2, 100);
|
|
3559
3562
|
const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
3560
3563
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -3565,6 +3568,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
3565
3568
|
`FZ_USER=${input.serviceUser}`,
|
|
3566
3569
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
3567
3570
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
3571
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
3568
3572
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
3569
3573
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
3570
3574
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -4937,11 +4941,15 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4937
4941
|
concurrencyLimit: runtime.environment.concurrencyLimit,
|
|
4938
4942
|
workerDrainSeconds: Math.ceil(runtime.environment.drainDeadlineMs / 1000)
|
|
4939
4943
|
});
|
|
4944
|
+
const seedMeshAddress = runtime.environment.databaseAddress ?? config.database.address;
|
|
4945
|
+
if (!seedMeshAddress)
|
|
4946
|
+
throw new Error("platform activation requires the private seed mesh address");
|
|
4940
4947
|
const activation = renderPlatformActivationFiles({
|
|
4941
4948
|
root: config.deployRoot ?? "/opt/forgezero",
|
|
4942
4949
|
serviceUser: runtime.serviceUser,
|
|
4943
4950
|
bluePort: runtime.bluePort,
|
|
4944
4951
|
greenPort: runtime.greenPort,
|
|
4952
|
+
seedMeshAddress,
|
|
4945
4953
|
healthPath: runtime.healthPath,
|
|
4946
4954
|
keepReleases: runtime.keepReleases,
|
|
4947
4955
|
drainDeadlineMs: runtime.environment.drainDeadlineMs
|
package/dist/fz-agent.js
CHANGED
|
@@ -9684,7 +9684,7 @@ async function writeAndCloseProcessInput(input, value) {
|
|
|
9684
9684
|
}
|
|
9685
9685
|
|
|
9686
9686
|
// src/version.ts
|
|
9687
|
-
var VERSION2 = "0.1.
|
|
9687
|
+
var VERSION2 = "0.1.145";
|
|
9688
9688
|
|
|
9689
9689
|
// src/ssh-bootstrap.ts
|
|
9690
9690
|
function deriveMetalAdmissionProvisioning(lscpu) {
|
|
@@ -15340,6 +15340,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
15340
15340
|
boundedInteger2("greenPort", input.greenPort, 1024, 65535);
|
|
15341
15341
|
if (input.bluePort === input.greenPort)
|
|
15342
15342
|
throw new Error("Activation slot ports must differ.");
|
|
15343
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
15344
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
15345
|
+
}
|
|
15343
15346
|
boundedInteger2("keepReleases", input.keepReleases, 2, 100);
|
|
15344
15347
|
const drainDeadlineMs = boundedInteger2("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
15345
15348
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -15350,6 +15353,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
15350
15353
|
`FZ_USER=${input.serviceUser}`,
|
|
15351
15354
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
15352
15355
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
15356
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
15353
15357
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
15354
15358
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
15355
15359
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -15446,6 +15450,15 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
15446
15450
|
}
|
|
15447
15451
|
const target2 = previousSlot === "blue" ? "green" : "blue";
|
|
15448
15452
|
const port = target2 === "blue" ? normalized.bluePort : normalized.greenPort;
|
|
15453
|
+
const meshProbe = async (slotPort, path2) => {
|
|
15454
|
+
try {
|
|
15455
|
+
const response = await request(`http://${normalized.seedMeshAddress}:${slotPort}/_internal/forgezero/seed-mesh/${path2}`, { signal: AbortSignal.timeout(2000) });
|
|
15456
|
+
return response.ok;
|
|
15457
|
+
} catch {
|
|
15458
|
+
return false;
|
|
15459
|
+
}
|
|
15460
|
+
};
|
|
15461
|
+
const previousHeldSeed = previousSlot ? await meshProbe(previousSlot === "blue" ? normalized.bluePort : normalized.greenPort, "resident") : false;
|
|
15449
15462
|
const targetLink = join10(slots, target2);
|
|
15450
15463
|
let previousTarget;
|
|
15451
15464
|
try {
|
|
@@ -15483,6 +15496,19 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
15483
15496
|
await stopTarget();
|
|
15484
15497
|
throw new Error("target slot failed its health deadline");
|
|
15485
15498
|
}
|
|
15499
|
+
if (previousHeldSeed) {
|
|
15500
|
+
let seedReady = false;
|
|
15501
|
+
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
15502
|
+
seedReady = await meshProbe(port, "ready");
|
|
15503
|
+
if (seedReady)
|
|
15504
|
+
break;
|
|
15505
|
+
await sleep2(1000);
|
|
15506
|
+
}
|
|
15507
|
+
if (!seedReady) {
|
|
15508
|
+
await stopTarget();
|
|
15509
|
+
throw new Error("target slot failed RAM-seed handover deadline");
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15486
15512
|
const upstream = "/etc/nginx/conf.d/forgezero-upstream.conf";
|
|
15487
15513
|
const backup = `${upstream}.forgezero-backup`;
|
|
15488
15514
|
if (existsSync19(upstream))
|
package/dist/fz.js
CHANGED
|
@@ -6007,7 +6007,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
6007
6007
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
6008
6008
|
|
|
6009
6009
|
// src/version.ts
|
|
6010
|
-
var VERSION2 = "0.1.
|
|
6010
|
+
var VERSION2 = "0.1.145";
|
|
6011
6011
|
|
|
6012
6012
|
// src/software.ts
|
|
6013
6013
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -12922,6 +12922,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
12922
12922
|
boundedInteger("greenPort", input.greenPort, 1024, 65535);
|
|
12923
12923
|
if (input.bluePort === input.greenPort)
|
|
12924
12924
|
throw new Error("Activation slot ports must differ.");
|
|
12925
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
12926
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
12927
|
+
}
|
|
12925
12928
|
boundedInteger("keepReleases", input.keepReleases, 2, 100);
|
|
12926
12929
|
const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
12927
12930
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -12932,6 +12935,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
12932
12935
|
`FZ_USER=${input.serviceUser}`,
|
|
12933
12936
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
12934
12937
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
12938
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
12935
12939
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
12936
12940
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
12937
12941
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -15580,11 +15584,15 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
15580
15584
|
concurrencyLimit: runtime.environment.concurrencyLimit,
|
|
15581
15585
|
workerDrainSeconds: Math.ceil(runtime.environment.drainDeadlineMs / 1000)
|
|
15582
15586
|
});
|
|
15587
|
+
const seedMeshAddress = runtime.environment.databaseAddress ?? config.database.address;
|
|
15588
|
+
if (!seedMeshAddress)
|
|
15589
|
+
throw new Error("platform activation requires the private seed mesh address");
|
|
15583
15590
|
const activation = renderPlatformActivationFiles({
|
|
15584
15591
|
root: config.deployRoot ?? "/opt/forgezero",
|
|
15585
15592
|
serviceUser: runtime.serviceUser,
|
|
15586
15593
|
bluePort: runtime.bluePort,
|
|
15587
15594
|
greenPort: runtime.greenPort,
|
|
15595
|
+
seedMeshAddress,
|
|
15588
15596
|
healthPath: runtime.healthPath,
|
|
15589
15597
|
keepReleases: runtime.keepReleases,
|
|
15590
15598
|
drainDeadlineMs: runtime.environment.drainDeadlineMs
|
|
@@ -18545,7 +18553,7 @@ function platformGenesisBootstrapConfigs(template, guests, nodes, metalHostname,
|
|
|
18545
18553
|
databaseAddress: guest.address,
|
|
18546
18554
|
databaseMaster: role === "joiner" ? master : undefined,
|
|
18547
18555
|
nodeHostname: node.nodeHostname,
|
|
18548
|
-
seedSyncPeers: fleet.
|
|
18556
|
+
seedSyncPeers: fleet.flatMap((peer) => [config.runtime.bluePort, config.runtime.greenPort].map((port) => `ws://${peer.address}:${port}/_internal/forgezero/seed-mesh/v2`)),
|
|
18549
18557
|
seedSyncMembers: nodes.map(({ nodeHostname }) => nodeHostname),
|
|
18550
18558
|
initialInventory,
|
|
18551
18559
|
custodianEmail: template.runtime.environment.custodianEmail
|
|
@@ -18650,7 +18658,7 @@ function forgeZeroLaunchTemplate(profile, guests, bundle, owner) {
|
|
|
18650
18658
|
apiOrigin: profile.apiOrigin,
|
|
18651
18659
|
publicApiPort: 3000,
|
|
18652
18660
|
sharedDirectory: "/opt/forgezero/shared",
|
|
18653
|
-
seedSyncPeers: fleet.
|
|
18661
|
+
seedSyncPeers: fleet.flatMap(({ address }) => [3001, 3002].map((port) => `ws://${address}:${port}/_internal/forgezero/seed-mesh/v2`)),
|
|
18654
18662
|
seedSyncMembers: nodeHostnames,
|
|
18655
18663
|
seedSyncEpoch: profile.seedSyncEpoch,
|
|
18656
18664
|
concurrencyLimit: 128,
|
package/dist/metal-bootstrap.js
CHANGED
|
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// src/version.ts
|
|
369
|
-
var VERSION = "0.1.
|
|
369
|
+
var VERSION = "0.1.145";
|
|
370
370
|
|
|
371
371
|
// src/otel-collector.ts
|
|
372
372
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -1460,7 +1460,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1460
1460
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1461
1461
|
|
|
1462
1462
|
// src/version.ts
|
|
1463
|
-
var VERSION = "0.1.
|
|
1463
|
+
var VERSION = "0.1.145";
|
|
1464
1464
|
|
|
1465
1465
|
// src/software.ts
|
|
1466
1466
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -3555,6 +3555,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
3555
3555
|
boundedInteger("greenPort", input.greenPort, 1024, 65535);
|
|
3556
3556
|
if (input.bluePort === input.greenPort)
|
|
3557
3557
|
throw new Error("Activation slot ports must differ.");
|
|
3558
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
3559
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
3560
|
+
}
|
|
3558
3561
|
boundedInteger("keepReleases", input.keepReleases, 2, 100);
|
|
3559
3562
|
const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
3560
3563
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -3565,6 +3568,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
3565
3568
|
`FZ_USER=${input.serviceUser}`,
|
|
3566
3569
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
3567
3570
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
3571
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
3568
3572
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
3569
3573
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
3570
3574
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -4937,11 +4941,15 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4937
4941
|
concurrencyLimit: runtime.environment.concurrencyLimit,
|
|
4938
4942
|
workerDrainSeconds: Math.ceil(runtime.environment.drainDeadlineMs / 1000)
|
|
4939
4943
|
});
|
|
4944
|
+
const seedMeshAddress = runtime.environment.databaseAddress ?? config.database.address;
|
|
4945
|
+
if (!seedMeshAddress)
|
|
4946
|
+
throw new Error("platform activation requires the private seed mesh address");
|
|
4940
4947
|
const activation = renderPlatformActivationFiles({
|
|
4941
4948
|
root: config.deployRoot ?? "/opt/forgezero",
|
|
4942
4949
|
serviceUser: runtime.serviceUser,
|
|
4943
4950
|
bluePort: runtime.bluePort,
|
|
4944
4951
|
greenPort: runtime.greenPort,
|
|
4952
|
+
seedMeshAddress,
|
|
4945
4953
|
healthPath: runtime.healthPath,
|
|
4946
4954
|
keepReleases: runtime.keepReleases,
|
|
4947
4955
|
drainDeadlineMs: runtime.environment.drainDeadlineMs
|
|
@@ -176,6 +176,8 @@ export interface PlatformActivationConfig {
|
|
|
176
176
|
serviceUser: string;
|
|
177
177
|
bluePort: number;
|
|
178
178
|
greenPort: number;
|
|
179
|
+
/** Private address used by the seed mesh; never exposed through nginx. */
|
|
180
|
+
seedMeshAddress: string;
|
|
179
181
|
healthPath: string;
|
|
180
182
|
keepReleases: number;
|
|
181
183
|
drainDeadlineMs?: number;
|
|
@@ -1168,6 +1168,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
1168
1168
|
boundedInteger("greenPort", input.greenPort, 1024, 65535);
|
|
1169
1169
|
if (input.bluePort === input.greenPort)
|
|
1170
1170
|
throw new Error("Activation slot ports must differ.");
|
|
1171
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
1172
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
1173
|
+
}
|
|
1171
1174
|
boundedInteger("keepReleases", input.keepReleases, 2, 100);
|
|
1172
1175
|
const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
1173
1176
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -1178,6 +1181,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
1178
1181
|
`FZ_USER=${input.serviceUser}`,
|
|
1179
1182
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
1180
1183
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
1184
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
1181
1185
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
1182
1186
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
1183
1187
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -1274,6 +1278,15 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
1274
1278
|
}
|
|
1275
1279
|
const target = previousSlot === "blue" ? "green" : "blue";
|
|
1276
1280
|
const port = target === "blue" ? normalized.bluePort : normalized.greenPort;
|
|
1281
|
+
const meshProbe = async (slotPort, path2) => {
|
|
1282
|
+
try {
|
|
1283
|
+
const response = await request(`http://${normalized.seedMeshAddress}:${slotPort}/_internal/forgezero/seed-mesh/${path2}`, { signal: AbortSignal.timeout(2000) });
|
|
1284
|
+
return response.ok;
|
|
1285
|
+
} catch {
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
const previousHeldSeed = previousSlot ? await meshProbe(previousSlot === "blue" ? normalized.bluePort : normalized.greenPort, "resident") : false;
|
|
1277
1290
|
const targetLink = join2(slots, target);
|
|
1278
1291
|
let previousTarget;
|
|
1279
1292
|
try {
|
|
@@ -1311,6 +1324,19 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
1311
1324
|
await stopTarget();
|
|
1312
1325
|
throw new Error("target slot failed its health deadline");
|
|
1313
1326
|
}
|
|
1327
|
+
if (previousHeldSeed) {
|
|
1328
|
+
let seedReady = false;
|
|
1329
|
+
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
1330
|
+
seedReady = await meshProbe(port, "ready");
|
|
1331
|
+
if (seedReady)
|
|
1332
|
+
break;
|
|
1333
|
+
await sleep(1000);
|
|
1334
|
+
}
|
|
1335
|
+
if (!seedReady) {
|
|
1336
|
+
await stopTarget();
|
|
1337
|
+
throw new Error("target slot failed RAM-seed handover deadline");
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1314
1340
|
const upstream = "/etc/nginx/conf.d/forgezero-upstream.conf";
|
|
1315
1341
|
const backup = `${upstream}.forgezero-backup`;
|
|
1316
1342
|
if (existsSync2(upstream))
|
|
@@ -3050,7 +3050,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
3050
3050
|
}
|
|
3051
3051
|
|
|
3052
3052
|
// src/version.ts
|
|
3053
|
-
var VERSION3 = "0.1.
|
|
3053
|
+
var VERSION3 = "0.1.145";
|
|
3054
3054
|
|
|
3055
3055
|
// src/egress-policy.ts
|
|
3056
3056
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
@@ -4685,6 +4685,9 @@ function renderPlatformActivationFiles(input) {
|
|
|
4685
4685
|
boundedInteger("greenPort", input.greenPort, 1024, 65535);
|
|
4686
4686
|
if (input.bluePort === input.greenPort)
|
|
4687
4687
|
throw new Error("Activation slot ports must differ.");
|
|
4688
|
+
if (!/^[A-Za-z0-9.-]{1,253}$/.test(input.seedMeshAddress) || input.seedMeshAddress.startsWith(".") || input.seedMeshAddress.endsWith(".")) {
|
|
4689
|
+
throw new Error("Activation seed mesh address is malformed.");
|
|
4690
|
+
}
|
|
4688
4691
|
boundedInteger("keepReleases", input.keepReleases, 2, 100);
|
|
4689
4692
|
const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
|
|
4690
4693
|
if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
|
|
@@ -4695,6 +4698,7 @@ function renderPlatformActivationFiles(input) {
|
|
|
4695
4698
|
`FZ_USER=${input.serviceUser}`,
|
|
4696
4699
|
`FZ_BLUE_PORT=${input.bluePort}`,
|
|
4697
4700
|
`FZ_GREEN_PORT=${input.greenPort}`,
|
|
4701
|
+
`FZ_SEED_MESH_ADDRESS=${input.seedMeshAddress}`,
|
|
4698
4702
|
`FZ_HEALTH_PATH=${input.healthPath}`,
|
|
4699
4703
|
`FZ_KEEP_RELEASES=${input.keepReleases}`,
|
|
4700
4704
|
`FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
|
|
@@ -4791,6 +4795,15 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
4791
4795
|
}
|
|
4792
4796
|
const target = previousSlot === "blue" ? "green" : "blue";
|
|
4793
4797
|
const port = target === "blue" ? normalized.bluePort : normalized.greenPort;
|
|
4798
|
+
const meshProbe = async (slotPort, path2) => {
|
|
4799
|
+
try {
|
|
4800
|
+
const response = await request(`http://${normalized.seedMeshAddress}:${slotPort}/_internal/forgezero/seed-mesh/${path2}`, { signal: AbortSignal.timeout(2000) });
|
|
4801
|
+
return response.ok;
|
|
4802
|
+
} catch {
|
|
4803
|
+
return false;
|
|
4804
|
+
}
|
|
4805
|
+
};
|
|
4806
|
+
const previousHeldSeed = previousSlot ? await meshProbe(previousSlot === "blue" ? normalized.bluePort : normalized.greenPort, "resident") : false;
|
|
4794
4807
|
const targetLink = join5(slots, target);
|
|
4795
4808
|
let previousTarget;
|
|
4796
4809
|
try {
|
|
@@ -4828,6 +4841,19 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
|
|
|
4828
4841
|
await stopTarget();
|
|
4829
4842
|
throw new Error("target slot failed its health deadline");
|
|
4830
4843
|
}
|
|
4844
|
+
if (previousHeldSeed) {
|
|
4845
|
+
let seedReady = false;
|
|
4846
|
+
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
4847
|
+
seedReady = await meshProbe(port, "ready");
|
|
4848
|
+
if (seedReady)
|
|
4849
|
+
break;
|
|
4850
|
+
await sleep(1000);
|
|
4851
|
+
}
|
|
4852
|
+
if (!seedReady) {
|
|
4853
|
+
await stopTarget();
|
|
4854
|
+
throw new Error("target slot failed RAM-seed handover deadline");
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4831
4857
|
const upstream = "/etc/nginx/conf.d/forgezero-upstream.conf";
|
|
4832
4858
|
const backup = `${upstream}.forgezero-backup`;
|
|
4833
4859
|
if (existsSync8(upstream))
|
package/dist/provision.js
CHANGED
|
@@ -3050,7 +3050,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
3050
3050
|
}
|
|
3051
3051
|
|
|
3052
3052
|
// src/version.ts
|
|
3053
|
-
var VERSION3 = "0.1.
|
|
3053
|
+
var VERSION3 = "0.1.145";
|
|
3054
3054
|
|
|
3055
3055
|
// src/egress-policy.ts
|
|
3056
3056
|
import { realpathSync as realpathSync3 } 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.145";
|