@forgezero/agent 0.1.88 → 0.1.89
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/README.md +23 -9
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.d.ts +8 -1
- package/dist/bootstrap.js +228 -54
- package/dist/capacity-calibration.d.ts +35 -5
- package/dist/capacity-calibration.js +66 -22
- package/dist/cli/agent-install.d.ts +3 -0
- package/dist/community-rehearsal-host.js +7 -2
- package/dist/control.d.ts +3 -1
- package/dist/definition.js +83 -28
- package/dist/deploy-file.js +83 -28
- package/dist/deployment.d.ts +5 -0
- package/dist/egress-policy.d.ts +4 -0
- package/dist/fz-agent.js +416 -150
- package/dist/fz.js +413 -129
- package/dist/host-maintenance.js +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/metal-bootstrap.js +1 -1
- package/dist/metal-helper-socket.js +7 -2
- package/dist/metal-provision.js +7 -2
- package/dist/operator-bootstrap.d.ts +12 -0
- package/dist/operator-bootstrap.js +372 -64
- package/dist/platform-bootstrap-runtime.d.ts +1 -0
- package/dist/platform-bootstrap-runtime.js +47 -8
- package/dist/platform-fleet-verification.js +262 -87
- package/dist/platform-genesis.js +7 -2
- package/dist/provision.d.ts +5 -1
- package/dist/provision.js +185 -71
- package/dist/recovery-host.js +1 -1
- package/dist/software-helper.js +144 -48
- package/dist/software.js +7 -2
- package/dist/ubuntu.js +7 -2
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/schema/deploy-v3.json +5 -2
package/dist/software-helper.js
CHANGED
|
@@ -355,7 +355,12 @@ async function executeSoftwareOperation(operation) {
|
|
|
355
355
|
}
|
|
356
356
|
if (software === "nginx") {
|
|
357
357
|
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
358
|
-
|
|
358
|
+
if (binary.exitCode !== 0)
|
|
359
|
+
return binary;
|
|
360
|
+
if (!existsSync("/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so")) {
|
|
361
|
+
return { exitCode: 1, output: "Nginx response-header suppression module is unavailable" };
|
|
362
|
+
}
|
|
363
|
+
return run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]);
|
|
359
364
|
}
|
|
360
365
|
if (software === "arangodb") {
|
|
361
366
|
const binary = await run(["/usr/sbin/arangod", "--version"]);
|
|
@@ -387,7 +392,7 @@ async function executeSoftwareOperation(operation) {
|
|
|
387
392
|
}
|
|
388
393
|
if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client" || software === "git") {
|
|
389
394
|
const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
|
|
390
|
-
const installed = await aptInstall(packageName);
|
|
395
|
+
const installed = software === "nginx" ? await aptInstallMany(["nginx", "libnginx-mod-http-headers-more-filter"]) : await aptInstall(packageName);
|
|
391
396
|
if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
|
|
392
397
|
return installed;
|
|
393
398
|
if (software === "docker") {
|
|
@@ -539,11 +544,22 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
539
544
|
}
|
|
540
545
|
|
|
541
546
|
// src/capacity-calibration.ts
|
|
542
|
-
|
|
547
|
+
import { cpus, freemem, totalmem } from "node:os";
|
|
548
|
+
var percentile = (values, quantile) => {
|
|
543
549
|
if (values.length === 0)
|
|
544
550
|
return Number.POSITIVE_INFINITY;
|
|
545
551
|
const sorted = values.toSorted((left, right) => left - right);
|
|
546
|
-
return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length *
|
|
552
|
+
return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * quantile) - 1)];
|
|
553
|
+
};
|
|
554
|
+
var hostSample = () => {
|
|
555
|
+
let cpuIdle = 0;
|
|
556
|
+
let cpuTotal = 0;
|
|
557
|
+
for (const cpu of cpus()) {
|
|
558
|
+
cpuIdle += cpu.times.idle;
|
|
559
|
+
cpuTotal += Object.values(cpu.times).reduce((sum, value) => sum + value, 0);
|
|
560
|
+
}
|
|
561
|
+
const memoryTotal = totalmem();
|
|
562
|
+
return { cpuIdle, cpuTotal, memoryUsed: memoryTotal - freemem(), memoryTotal };
|
|
547
563
|
};
|
|
548
564
|
function localCalibrationEndpoint(value) {
|
|
549
565
|
const endpoint = new URL(value);
|
|
@@ -558,9 +574,12 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
558
574
|
const requestsPerWorker = options.requestsPerWorker ?? 8;
|
|
559
575
|
const maxP95Ms = options.maxP95Ms ?? 250;
|
|
560
576
|
const maxErrorRate = options.maxErrorRate ?? 0.01;
|
|
561
|
-
const
|
|
577
|
+
const safetyRatio = options.safetyRatio ?? 0.8;
|
|
562
578
|
const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
|
|
563
|
-
|
|
579
|
+
const minimumStageDurationMs = options.minimumStageDurationMs ?? 5000;
|
|
580
|
+
const maxCpuUtilizationPercent = options.maxCpuUtilizationPercent ?? 90;
|
|
581
|
+
const maxMemoryUtilizationPercent = options.maxMemoryUtilizationPercent ?? 90;
|
|
582
|
+
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
564
583
|
throw new Error("Capacity calibration bounds are invalid.");
|
|
565
584
|
}
|
|
566
585
|
return {
|
|
@@ -569,18 +588,24 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
569
588
|
requestsPerWorker,
|
|
570
589
|
maxP95Ms,
|
|
571
590
|
maxErrorRate,
|
|
572
|
-
|
|
573
|
-
requestTimeoutMs
|
|
591
|
+
safetyRatio,
|
|
592
|
+
requestTimeoutMs,
|
|
593
|
+
minimumStageDurationMs,
|
|
594
|
+
maxCpuUtilizationPercent,
|
|
595
|
+
maxMemoryUtilizationPercent
|
|
574
596
|
};
|
|
575
597
|
}
|
|
576
|
-
async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, fetcher) {
|
|
598
|
+
async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
577
599
|
const latencies = [];
|
|
578
600
|
let succeeded = 0;
|
|
579
601
|
let failed = 0;
|
|
580
602
|
let overloaded = 0;
|
|
603
|
+
const systemBefore = sampler();
|
|
581
604
|
const started = performance.now();
|
|
582
605
|
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
583
|
-
|
|
606
|
+
let request = 0;
|
|
607
|
+
while (request < requestsPerWorker || performance.now() - started < minimumDurationMs) {
|
|
608
|
+
request += 1;
|
|
584
609
|
const requestStarted = performance.now();
|
|
585
610
|
try {
|
|
586
611
|
const response = await fetcher(endpoint, {
|
|
@@ -604,50 +629,74 @@ async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, fetche
|
|
|
604
629
|
}
|
|
605
630
|
}
|
|
606
631
|
}));
|
|
607
|
-
const
|
|
632
|
+
const durationMs = Math.max(performance.now() - started, 1);
|
|
633
|
+
const elapsedSeconds = durationMs / 1000;
|
|
634
|
+
const systemAfter = sampler();
|
|
635
|
+
const cpuTotal = Math.max(0, systemAfter.cpuTotal - systemBefore.cpuTotal);
|
|
636
|
+
const cpuIdle = Math.max(0, systemAfter.cpuIdle - systemBefore.cpuIdle);
|
|
637
|
+
const cpuUtilizationPercent = cpuTotal === 0 ? 0 : (cpuTotal - cpuIdle) / cpuTotal * 100;
|
|
638
|
+
const memoryUtilizationPercent = Math.max(systemBefore.memoryTotal > 0 ? systemBefore.memoryUsed / systemBefore.memoryTotal * 100 : 0, systemAfter.memoryTotal > 0 ? systemAfter.memoryUsed / systemAfter.memoryTotal * 100 : 0);
|
|
639
|
+
const requests = succeeded + failed;
|
|
608
640
|
return {
|
|
609
641
|
concurrency,
|
|
610
|
-
requests
|
|
642
|
+
requests,
|
|
611
643
|
succeeded,
|
|
612
644
|
failed,
|
|
613
645
|
overloaded,
|
|
614
|
-
|
|
615
|
-
|
|
646
|
+
successfulRequestsPerSecond: Number((succeeded / elapsedSeconds).toFixed(2)),
|
|
647
|
+
attemptedRequestsPerSecond: Number((requests / elapsedSeconds).toFixed(2)),
|
|
648
|
+
durationMs: Number(durationMs.toFixed(2)),
|
|
649
|
+
errorRate: Number((failed / Math.max(requests, 1)).toFixed(6)),
|
|
650
|
+
p95Ms: Number(percentile(latencies, 0.95).toFixed(2)),
|
|
651
|
+
p99Ms: Number(percentile(latencies, 0.99).toFixed(2)),
|
|
652
|
+
cpuUtilizationPercent: Number(cpuUtilizationPercent.toFixed(2)),
|
|
653
|
+
memoryUtilizationPercent: Number(memoryUtilizationPercent.toFixed(2))
|
|
616
654
|
};
|
|
617
655
|
}
|
|
618
|
-
async function calibrateHttpConcurrency(options, fetcher = fetch) {
|
|
656
|
+
async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = hostSample) {
|
|
619
657
|
const {
|
|
620
658
|
endpoint,
|
|
621
659
|
maxConcurrency,
|
|
622
660
|
requestsPerWorker,
|
|
623
661
|
maxP95Ms,
|
|
624
662
|
maxErrorRate,
|
|
625
|
-
|
|
626
|
-
requestTimeoutMs: timeoutMs
|
|
663
|
+
safetyRatio,
|
|
664
|
+
requestTimeoutMs: timeoutMs,
|
|
665
|
+
minimumStageDurationMs,
|
|
666
|
+
maxCpuUtilizationPercent,
|
|
667
|
+
maxMemoryUtilizationPercent
|
|
627
668
|
} = validateCapacityCalibrationOptions(options);
|
|
628
669
|
const stages = [];
|
|
629
|
-
let lastSafe
|
|
670
|
+
let lastSafe;
|
|
630
671
|
let stopReason = "maximum-tested";
|
|
631
672
|
for (let concurrency = 1;; concurrency = Math.min(maxConcurrency, concurrency * 2)) {
|
|
632
|
-
const measured = await stage(endpoint, concurrency, requestsPerWorker, timeoutMs, fetcher);
|
|
673
|
+
const measured = await stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
633
674
|
stages.push(measured);
|
|
634
|
-
const errorRate = measured.failed / measured.requests;
|
|
635
675
|
const previous = stages.at(-2);
|
|
636
|
-
const throughputRegressed = Boolean(previous && concurrency > 1 && measured.
|
|
637
|
-
if (measured.overloaded > 0 || errorRate > maxErrorRate)
|
|
676
|
+
const throughputRegressed = Boolean(previous && concurrency > 1 && measured.successfulRequestsPerSecond < previous.successfulRequestsPerSecond * 0.9);
|
|
677
|
+
if (measured.overloaded > 0 || measured.errorRate > maxErrorRate)
|
|
638
678
|
stopReason = "errors";
|
|
639
679
|
else if (measured.p95Ms > maxP95Ms)
|
|
640
680
|
stopReason = "latency";
|
|
681
|
+
else if (measured.cpuUtilizationPercent > maxCpuUtilizationPercent)
|
|
682
|
+
stopReason = "cpu";
|
|
683
|
+
else if (measured.memoryUtilizationPercent > maxMemoryUtilizationPercent)
|
|
684
|
+
stopReason = "memory";
|
|
641
685
|
else if (throughputRegressed)
|
|
642
686
|
stopReason = "throughput-regression";
|
|
643
687
|
else
|
|
644
|
-
lastSafe =
|
|
688
|
+
lastSafe = measured;
|
|
645
689
|
if (stopReason !== "maximum-tested" || concurrency === maxConcurrency)
|
|
646
690
|
break;
|
|
647
691
|
}
|
|
692
|
+
if (!lastSafe)
|
|
693
|
+
throw new Error("Capacity calibration found no safe request stage.");
|
|
648
694
|
return {
|
|
649
695
|
endpoint: endpoint.toString(),
|
|
650
|
-
|
|
696
|
+
measuredSustainableRequestsPerSecond: lastSafe.successfulRequestsPerSecond,
|
|
697
|
+
allowedRequestsPerSecond: Math.max(1, Math.floor(lastSafe.successfulRequestsPerSecond * safetyRatio)),
|
|
698
|
+
safetyRatio,
|
|
699
|
+
recommendedConcurrency: Math.max(1, Math.floor(lastSafe.concurrency * safetyRatio)),
|
|
651
700
|
stopReason,
|
|
652
701
|
stages
|
|
653
702
|
};
|
|
@@ -724,8 +773,11 @@ function capacityCalibration(value, where) {
|
|
|
724
773
|
"requestsPerWorker",
|
|
725
774
|
"maxP95Ms",
|
|
726
775
|
"maxErrorRate",
|
|
727
|
-
"
|
|
728
|
-
"requestTimeoutMs"
|
|
776
|
+
"safetyRatio",
|
|
777
|
+
"requestTimeoutMs",
|
|
778
|
+
"minimumStageDurationMs",
|
|
779
|
+
"maxCpuUtilizationPercent",
|
|
780
|
+
"maxMemoryUtilizationPercent"
|
|
729
781
|
], where);
|
|
730
782
|
const endpoint = text(calibration.endpoint, `${where}.endpoint`);
|
|
731
783
|
try {
|
|
@@ -749,8 +801,11 @@ function capacityCalibration(value, where) {
|
|
|
749
801
|
"requestsPerWorker",
|
|
750
802
|
"maxP95Ms",
|
|
751
803
|
"maxErrorRate",
|
|
752
|
-
"
|
|
753
|
-
"requestTimeoutMs"
|
|
804
|
+
"safetyRatio",
|
|
805
|
+
"requestTimeoutMs",
|
|
806
|
+
"minimumStageDurationMs",
|
|
807
|
+
"maxCpuUtilizationPercent",
|
|
808
|
+
"maxMemoryUtilizationPercent"
|
|
754
809
|
].flatMap((name) => {
|
|
755
810
|
const found = optionalNumber(name);
|
|
756
811
|
return found === undefined ? [] : [[name, found]];
|
|
@@ -1941,30 +1996,71 @@ function requestServiceActivation(request, socketPath = DEFAULT_SOFTWARE_HELPER_
|
|
|
1941
1996
|
function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCKET, timeoutMs = 15 * 60000) {
|
|
1942
1997
|
validateSoftwareRequirements(requirements);
|
|
1943
1998
|
return new Promise((resolve3, reject) => {
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
socket.destroy();
|
|
1949
|
-
reject(new Error("software helper did not answer before its deadline"));
|
|
1950
|
-
});
|
|
1951
|
-
socket.on("data", (chunk) => {
|
|
1952
|
-
buffer += chunk.toString("utf8");
|
|
1953
|
-
const newline = buffer.indexOf(`
|
|
1954
|
-
`);
|
|
1955
|
-
if (newline < 0)
|
|
1999
|
+
const deadline = Date.now() + timeoutMs;
|
|
2000
|
+
let settled = false;
|
|
2001
|
+
const finish = (cause, results) => {
|
|
2002
|
+
if (settled)
|
|
1956
2003
|
return;
|
|
1957
|
-
|
|
2004
|
+
settled = true;
|
|
2005
|
+
if (cause)
|
|
2006
|
+
reject(cause);
|
|
2007
|
+
else
|
|
2008
|
+
resolve3(results);
|
|
2009
|
+
};
|
|
2010
|
+
const attempt = () => {
|
|
2011
|
+
if (settled)
|
|
2012
|
+
return;
|
|
2013
|
+
const remaining = deadline - Date.now();
|
|
2014
|
+
if (remaining <= 0)
|
|
2015
|
+
return finish(new Error("software helper did not answer before its deadline"));
|
|
2016
|
+
if (!existsSync4(socketPath)) {
|
|
2017
|
+
setTimeout(attempt, Math.min(100, Math.max(1, remaining)));
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
let buffer = "";
|
|
2021
|
+
let socket;
|
|
1958
2022
|
try {
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
throw new Error(response.error?.message ?? "software helper refused the request");
|
|
1962
|
-
resolve3(response.results);
|
|
2023
|
+
socket = connect(socketPath, () => socket.write(`${JSON.stringify({ op: "ensure", requirements })}
|
|
2024
|
+
`));
|
|
1963
2025
|
} catch (cause) {
|
|
1964
|
-
|
|
2026
|
+
const error = cause;
|
|
2027
|
+
if ((error.code === "ENOENT" || error.code === "ECONNREFUSED") && Date.now() < deadline) {
|
|
2028
|
+
setTimeout(attempt, Math.min(100, Math.max(1, deadline - Date.now())));
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
finish(error);
|
|
2032
|
+
return;
|
|
1965
2033
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
2034
|
+
socket.setTimeout(remaining, () => {
|
|
2035
|
+
socket.destroy();
|
|
2036
|
+
finish(new Error("software helper did not answer before its deadline"));
|
|
2037
|
+
});
|
|
2038
|
+
socket.on("data", (chunk) => {
|
|
2039
|
+
buffer += chunk.toString("utf8");
|
|
2040
|
+
const newline = buffer.indexOf(`
|
|
2041
|
+
`);
|
|
2042
|
+
if (newline < 0)
|
|
2043
|
+
return;
|
|
2044
|
+
socket.end();
|
|
2045
|
+
try {
|
|
2046
|
+
const response = JSON.parse(buffer.slice(0, newline));
|
|
2047
|
+
if (!response.ok || !response.results)
|
|
2048
|
+
throw new Error(response.error?.message ?? "software helper refused the request");
|
|
2049
|
+
finish(undefined, response.results);
|
|
2050
|
+
} catch (cause) {
|
|
2051
|
+
finish(cause);
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
socket.on("error", (cause) => {
|
|
2055
|
+
socket.destroy();
|
|
2056
|
+
if ((cause.code === "ENOENT" || cause.code === "ECONNREFUSED") && Date.now() < deadline) {
|
|
2057
|
+
setTimeout(attempt, Math.min(100, Math.max(1, deadline - Date.now())));
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
finish(cause);
|
|
2061
|
+
});
|
|
2062
|
+
};
|
|
2063
|
+
attempt();
|
|
1968
2064
|
});
|
|
1969
2065
|
}
|
|
1970
2066
|
export {
|
package/dist/software.js
CHANGED
|
@@ -355,7 +355,12 @@ async function executeSoftwareOperation(operation) {
|
|
|
355
355
|
}
|
|
356
356
|
if (software === "nginx") {
|
|
357
357
|
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
358
|
-
|
|
358
|
+
if (binary.exitCode !== 0)
|
|
359
|
+
return binary;
|
|
360
|
+
if (!existsSync("/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so")) {
|
|
361
|
+
return { exitCode: 1, output: "Nginx response-header suppression module is unavailable" };
|
|
362
|
+
}
|
|
363
|
+
return run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]);
|
|
359
364
|
}
|
|
360
365
|
if (software === "arangodb") {
|
|
361
366
|
const binary = await run(["/usr/sbin/arangod", "--version"]);
|
|
@@ -387,7 +392,7 @@ async function executeSoftwareOperation(operation) {
|
|
|
387
392
|
}
|
|
388
393
|
if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client" || software === "git") {
|
|
389
394
|
const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
|
|
390
|
-
const installed = await aptInstall(packageName);
|
|
395
|
+
const installed = software === "nginx" ? await aptInstallMany(["nginx", "libnginx-mod-http-headers-more-filter"]) : await aptInstall(packageName);
|
|
391
396
|
if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
|
|
392
397
|
return installed;
|
|
393
398
|
if (software === "docker") {
|
package/dist/ubuntu.js
CHANGED
|
@@ -355,7 +355,12 @@ async function executeSoftwareOperation(operation) {
|
|
|
355
355
|
}
|
|
356
356
|
if (software === "nginx") {
|
|
357
357
|
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
358
|
-
|
|
358
|
+
if (binary.exitCode !== 0)
|
|
359
|
+
return binary;
|
|
360
|
+
if (!existsSync("/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so")) {
|
|
361
|
+
return { exitCode: 1, output: "Nginx response-header suppression module is unavailable" };
|
|
362
|
+
}
|
|
363
|
+
return run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]);
|
|
359
364
|
}
|
|
360
365
|
if (software === "arangodb") {
|
|
361
366
|
const binary = await run(["/usr/sbin/arangod", "--version"]);
|
|
@@ -387,7 +392,7 @@ async function executeSoftwareOperation(operation) {
|
|
|
387
392
|
}
|
|
388
393
|
if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client" || software === "git") {
|
|
389
394
|
const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
|
|
390
|
-
const installed = await aptInstall(packageName);
|
|
395
|
+
const installed = software === "nginx" ? await aptInstallMany(["nginx", "libnginx-mod-http-headers-more-filter"]) : await aptInstall(packageName);
|
|
391
396
|
if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
|
|
392
397
|
return installed;
|
|
393
398
|
if (software === "docker") {
|
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.89";
|
package/package.json
CHANGED
package/schema/deploy-v3.json
CHANGED
|
@@ -84,8 +84,11 @@
|
|
|
84
84
|
"requestsPerWorker": { "type": "integer", "minimum": 2, "maximum": 100 },
|
|
85
85
|
"maxP95Ms": { "type": "number", "exclusiveMinimum": 0 },
|
|
86
86
|
"maxErrorRate": { "type": "number", "minimum": 0, "maximum": 0.2 },
|
|
87
|
-
"
|
|
88
|
-
"requestTimeoutMs": { "type": "integer", "minimum": 100, "maximum": 30000 }
|
|
87
|
+
"safetyRatio": { "type": "number", "minimum": 0.25, "maximum": 0.95 },
|
|
88
|
+
"requestTimeoutMs": { "type": "integer", "minimum": 100, "maximum": 30000 },
|
|
89
|
+
"minimumStageDurationMs": { "type": "integer", "minimum": 100, "maximum": 60000 },
|
|
90
|
+
"maxCpuUtilizationPercent": { "type": "number", "minimum": 25, "maximum": 100 },
|
|
91
|
+
"maxMemoryUtilizationPercent": { "type": "number", "minimum": 25, "maximum": 100 }
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
}
|