@supacloud/admin 0.7.8 → 0.7.10
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/index.js +50 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25215,7 +25215,10 @@ class SshTransport {
|
|
|
25215
25215
|
let connectionReusable = true;
|
|
25216
25216
|
let timeoutHandle;
|
|
25217
25217
|
try {
|
|
25218
|
-
const operationPromise = openSftp(conn).then(
|
|
25218
|
+
const operationPromise = openSftp(conn).then(async (sftp) => {
|
|
25219
|
+
await operation(sftp);
|
|
25220
|
+
sftp.end();
|
|
25221
|
+
});
|
|
25219
25222
|
await Promise.race([
|
|
25220
25223
|
operationPromise,
|
|
25221
25224
|
new Promise((_resolve, reject) => {
|
|
@@ -26396,6 +26399,7 @@ async function prepareLocalUpgradeBundle(request) {
|
|
|
26396
26399
|
var REMOTE_STAGE_ROOT = "/var/lib/supacloud/upgrade-staging";
|
|
26397
26400
|
var REMOTE_RUN_ROOT = "/var/lib/supacloud/upgrade-runs";
|
|
26398
26401
|
var REMOTE_LOG_ROOT = "/var/log/supacloud";
|
|
26402
|
+
var REMOTE_UPLOAD_ROOT = "/var/tmp";
|
|
26399
26403
|
var POLL_INTERVAL_MS = 2000;
|
|
26400
26404
|
var STATE_READ_ATTEMPTS = 3;
|
|
26401
26405
|
var REMOTE_STATE_READ_TIMEOUT_MS = 15000;
|
|
@@ -26430,9 +26434,9 @@ function trustedInstalledGithubFunction() {
|
|
|
26430
26434
|
].join(`
|
|
26431
26435
|
`);
|
|
26432
26436
|
}
|
|
26433
|
-
function
|
|
26437
|
+
function buildRemoteUpgradePaths(runId) {
|
|
26434
26438
|
return {
|
|
26435
|
-
drop:
|
|
26439
|
+
drop: `${REMOTE_UPLOAD_ROOT}/.supacloud-upgrade-upload-${runId}`,
|
|
26436
26440
|
stage: `${REMOTE_STAGE_ROOT}/${runId}`,
|
|
26437
26441
|
status: `${REMOTE_RUN_ROOT}/${runId}.status`,
|
|
26438
26442
|
log: `${REMOTE_LOG_ROOT}/upgrade-${runId}.log`,
|
|
@@ -26489,9 +26493,19 @@ function requiredRemoteBytes(bundle) {
|
|
|
26489
26493
|
const transferBytes = [...bundle.files, ...bundle.verifierArchive ? [bundle.verifierArchive] : []].reduce((total, upload) => total + upload.size, 0);
|
|
26490
26494
|
return transferBytes * 3 + 512 * 1024 * 1024;
|
|
26491
26495
|
}
|
|
26492
|
-
function
|
|
26496
|
+
function remoteUploadRootGate() {
|
|
26497
|
+
return [
|
|
26498
|
+
`UPLOAD_ROOT=${quoteShell2(REMOTE_UPLOAD_ROOT)}`,
|
|
26499
|
+
`test -d "$UPLOAD_ROOT" && test ! -L "$UPLOAD_ROOT" || { echo 'Remote upload root must be a directory, not a symlink' >&2; exit 1; }`,
|
|
26500
|
+
`test "$(stat -c '%u' "$UPLOAD_ROOT")" = 0 || { echo 'Remote upload root must be owned by root' >&2; exit 1; }`,
|
|
26501
|
+
`UPLOAD_ROOT_MODE=$(stat -c '%a' "$UPLOAD_ROOT")`,
|
|
26502
|
+
`case "$UPLOAD_ROOT_MODE" in [0-7]|[0-7][0-7]|[0-7][0-7][0-7]|[0-7][0-7][0-7][0-7]) ;; *) echo 'Remote upload root has invalid permission bits' >&2; exit 1 ;; esac`,
|
|
26503
|
+
"(( (8#$UPLOAD_ROOT_MODE & 01000) != 0 )) || { echo 'Remote upload root must set the sticky bit' >&2; exit 1; }",
|
|
26504
|
+
`test -w "$UPLOAD_ROOT" && test -x "$UPLOAD_ROOT" || { echo 'Remote upload root is not writable and searchable by the SSH user' >&2; exit 1; }`
|
|
26505
|
+
];
|
|
26506
|
+
}
|
|
26507
|
+
function buildPrepareDropCommand(paths, bundle) {
|
|
26493
26508
|
const directories = [
|
|
26494
|
-
paths.drop,
|
|
26495
26509
|
`${paths.drop}/bundle`,
|
|
26496
26510
|
`${paths.drop}/bundle/management-api`,
|
|
26497
26511
|
`${paths.drop}/bundle/edge-runtime`
|
|
@@ -26502,16 +26516,18 @@ function prepareDropCommand(paths, bundle) {
|
|
|
26502
26516
|
return [
|
|
26503
26517
|
"set -euo pipefail",
|
|
26504
26518
|
"umask 077",
|
|
26505
|
-
|
|
26506
|
-
|
|
26519
|
+
...remoteUploadRootGate(),
|
|
26520
|
+
`DROP=${quoteShell2(paths.drop)}`,
|
|
26521
|
+
`UPLOAD_AVAILABLE_KB=$(df -Pk "$UPLOAD_ROOT" | awk 'NR == 2 { print $4 }')`,
|
|
26507
26522
|
"VAR_AVAILABLE_KB=$(df -Pk /var/lib/supacloud | awk 'NR == 2 { print $4 }')",
|
|
26508
|
-
`test "${"$"}
|
|
26523
|
+
`test "${"$"}UPLOAD_AVAILABLE_KB" -ge ${Math.ceil(requiredBytes / 1024)} && test "${"$"}VAR_AVAILABLE_KB" -ge ${Math.ceil(requiredBytes / 1024)} || { echo 'Insufficient remote disk space for verified upgrade staging' >&2; exit 1; }`,
|
|
26524
|
+
`mkdir -m 700 -- "$DROP" || { echo 'Unable to create exclusive remote upload drop' >&2; exit 1; }`,
|
|
26509
26525
|
`install -d -m 700 ${directories.map(quoteShell2).join(" ")}`
|
|
26510
26526
|
].join(`
|
|
26511
26527
|
`);
|
|
26512
26528
|
}
|
|
26513
26529
|
async function prepareRemoteDrop(ssh, paths, bundle) {
|
|
26514
|
-
const prepared = await ssh.exec(
|
|
26530
|
+
const prepared = await ssh.exec(buildPrepareDropCommand(paths, bundle), 30000);
|
|
26515
26531
|
if (!prepared.success)
|
|
26516
26532
|
throw remoteFailure("Unable to prepare remote upload drop", prepared);
|
|
26517
26533
|
}
|
|
@@ -26716,7 +26732,10 @@ function buildAdoptDropScript(paths) {
|
|
|
26716
26732
|
"trap 'exit 130' INT",
|
|
26717
26733
|
"trap 'exit 143' TERM",
|
|
26718
26734
|
`install -d -o root -g root -m 700 ${quoteShell2(REMOTE_STAGE_ROOT)} ${quoteShell2(REMOTE_RUN_ROOT)} ${quoteShell2(REMOTE_LOG_ROOT)}`,
|
|
26719
|
-
'
|
|
26735
|
+
'if [ -e "$STAGE" ] || [ -L "$STAGE" ] || [ -e "$STATUS" ] || [ -L "$STATUS" ] || [ -e "$LOG" ] || [ -L "$LOG" ]; then',
|
|
26736
|
+
" echo 'Upgrade adoption target already exists' >&2",
|
|
26737
|
+
" exit 1",
|
|
26738
|
+
"fi",
|
|
26720
26739
|
`systemctl status "$UNIT" >/dev/null 2>&1 && { echo 'Upgrade unit already exists' >&2; exit 1; } || true`,
|
|
26721
26740
|
"ADOPTION_ACTIVE=true",
|
|
26722
26741
|
'mv "$DROP" "$STAGE"',
|
|
@@ -26793,7 +26812,7 @@ async function startRemoteUpgrade(ssh, paths) {
|
|
|
26793
26812
|
}
|
|
26794
26813
|
throw startFailure;
|
|
26795
26814
|
}
|
|
26796
|
-
function
|
|
26815
|
+
function buildRemoteStateScript(paths) {
|
|
26797
26816
|
return [
|
|
26798
26817
|
"set -euo pipefail",
|
|
26799
26818
|
`DROP=${quoteShell2(paths.drop)}`,
|
|
@@ -26803,11 +26822,11 @@ function readStateScript(paths) {
|
|
|
26803
26822
|
`UNIT=${quoteShell2(paths.unit)}`,
|
|
26804
26823
|
`printf 'STATUS=%s\\n' "$(sed -n '1p' "$STATUS" 2>/dev/null || true)"`,
|
|
26805
26824
|
`printf 'UNIT=%s\\n' "$(systemctl is-active "$UNIT" 2>/dev/null || true)"`,
|
|
26806
|
-
'if [ -e "$STAGE" ]; then echo STAGE_EXISTS=yes; else echo STAGE_EXISTS=no; fi',
|
|
26825
|
+
'if [ -e "$STAGE" ] || [ -L "$STAGE" ]; then echo STAGE_EXISTS=yes; else echo STAGE_EXISTS=no; fi',
|
|
26807
26826
|
'if [ -d "$STAGE" ] && [ ! -L "$STAGE" ]; then echo STAGE_DIRECTORY=yes; else echo STAGE_DIRECTORY=no; fi',
|
|
26808
|
-
'if [ -e "$DROP" ]; then echo DROP_EXISTS=yes; else echo DROP_EXISTS=no; fi',
|
|
26809
|
-
'if [ -e "$STATUS" ]; then echo STATUS_EXISTS=yes; else echo STATUS_EXISTS=no; fi',
|
|
26810
|
-
'if [ -e "$LOG" ]; then echo LOG_EXISTS=yes; else echo LOG_EXISTS=no; fi',
|
|
26827
|
+
'if [ -e "$DROP" ] || [ -L "$DROP" ]; then echo DROP_EXISTS=yes; else echo DROP_EXISTS=no; fi',
|
|
26828
|
+
'if [ -e "$STATUS" ] || [ -L "$STATUS" ]; then echo STATUS_EXISTS=yes; else echo STATUS_EXISTS=no; fi',
|
|
26829
|
+
'if [ -e "$LOG" ] || [ -L "$LOG" ]; then echo LOG_EXISTS=yes; else echo LOG_EXISTS=no; fi',
|
|
26811
26830
|
'UNIT_LOAD_STATE=$(systemctl show --property=LoadState --value "$UNIT" 2>/dev/null || true)',
|
|
26812
26831
|
`test -n "$UNIT_LOAD_STATE" || { echo 'Unable to read remote upgrade unit load state' >&2; exit 1; }`,
|
|
26813
26832
|
`printf 'UNIT_LOAD=%s\\n' "$UNIT_LOAD_STATE"`,
|
|
@@ -26829,7 +26848,7 @@ function remoteStatePresence(output, field) {
|
|
|
26829
26848
|
return presence === "yes";
|
|
26830
26849
|
}
|
|
26831
26850
|
async function readRemoteState(ssh, paths, timeoutMs = REMOTE_STATE_READ_TIMEOUT_MS) {
|
|
26832
|
-
const state = await ssh.exec(rootCommand(
|
|
26851
|
+
const state = await ssh.exec(rootCommand(buildRemoteStateScript(paths)), timeoutMs);
|
|
26833
26852
|
if (!state.success)
|
|
26834
26853
|
throw remoteFailure("Unable to read remote upgrade state", state);
|
|
26835
26854
|
return {
|
|
@@ -26918,6 +26937,9 @@ function remoteEvidence(paths) {
|
|
|
26918
26937
|
function remoteReconciliationFailure(message, failures, paths) {
|
|
26919
26938
|
return new RemoteUpgradeReconciliationError(failures.map((failure) => operationError(failure, message)), `${message}; reconcile remote evidence at ${remoteEvidence(paths)}; do not retry blindly`);
|
|
26920
26939
|
}
|
|
26940
|
+
function buildUploadDropCleanupFailure(transferFailure, cleanupFailure, paths) {
|
|
26941
|
+
return remoteReconciliationFailure("Local upgrade failed and upload-drop cleanup did not complete", [transferFailure, cleanupFailure], paths);
|
|
26942
|
+
}
|
|
26921
26943
|
function failureRequiresRemoteReconciliation(error) {
|
|
26922
26944
|
return error instanceof RemoteUpgradeReconciliationError;
|
|
26923
26945
|
}
|
|
@@ -26938,6 +26960,14 @@ function assertSuccessfulUnitStoppedNormally(state, paths) {
|
|
|
26938
26960
|
return;
|
|
26939
26961
|
throw remoteReconciliationFailure(`Remote upgrade published SUCCEEDED but the unit stopped abnormally or ambiguously ` + `(state=${state.serviceState} load=${state.unitLoadState})`, [], paths);
|
|
26940
26962
|
}
|
|
26963
|
+
function failedUnitReachedTerminalState(state) {
|
|
26964
|
+
return unitStoppedNormally(state) || state.serviceState === "failed" && state.unitLoadState === "loaded";
|
|
26965
|
+
}
|
|
26966
|
+
function assertFailedUnitReachedTerminalState(state, paths) {
|
|
26967
|
+
if (failedUnitReachedTerminalState(state))
|
|
26968
|
+
return;
|
|
26969
|
+
throw remoteReconciliationFailure(`Remote upgrade published FAILED but the unit state is ambiguous ` + `(state=${state.serviceState} load=${state.unitLoadState})`, [], paths);
|
|
26970
|
+
}
|
|
26941
26971
|
function assertTerminalEvidence(state, paths) {
|
|
26942
26972
|
if (state.status !== "SUCCEEDED" && !state.status.startsWith("FAILED:"))
|
|
26943
26973
|
return;
|
|
@@ -27015,6 +27045,7 @@ async function awaitRemoteUpgrade(ssh, paths) {
|
|
|
27015
27045
|
return await completedUpgradeOutput(ssh, paths);
|
|
27016
27046
|
}
|
|
27017
27047
|
if (state.status.startsWith("FAILED:") && !unitRunning) {
|
|
27048
|
+
assertFailedUnitReachedTerminalState(state, paths);
|
|
27018
27049
|
await throwRemoteUpgradeFailure(ssh, paths, state.status);
|
|
27019
27050
|
}
|
|
27020
27051
|
if (Date.now() >= observationDeadline) {
|
|
@@ -27033,7 +27064,7 @@ async function awaitRemoteUpgrade(ssh, paths) {
|
|
|
27033
27064
|
}
|
|
27034
27065
|
async function executeLocalUpgradeTransfer(ssh, request) {
|
|
27035
27066
|
const runId = randomUUID3();
|
|
27036
|
-
const paths =
|
|
27067
|
+
const paths = buildRemoteUpgradePaths(runId);
|
|
27037
27068
|
const preflight = await remoteUpgradePreflight(ssh);
|
|
27038
27069
|
const bundle = await prepareLocalUpgradeBundle({ ...preflight, ...request });
|
|
27039
27070
|
let adopted = false;
|
|
@@ -27053,7 +27084,7 @@ async function executeLocalUpgradeTransfer(ssh, request) {
|
|
|
27053
27084
|
try {
|
|
27054
27085
|
await cleanupRemoteDrop(ssh, paths);
|
|
27055
27086
|
} catch (cleanupError) {
|
|
27056
|
-
transferError =
|
|
27087
|
+
transferError = buildUploadDropCleanupFailure(transferError, cleanupError, paths);
|
|
27057
27088
|
}
|
|
27058
27089
|
}
|
|
27059
27090
|
throw transferError;
|
|
@@ -28949,7 +28980,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
|
|
|
28949
28980
|
// package.json
|
|
28950
28981
|
var package_default = {
|
|
28951
28982
|
name: "@supacloud/admin",
|
|
28952
|
-
version: "0.7.
|
|
28983
|
+
version: "0.7.10",
|
|
28953
28984
|
description: "Platform administration CLI for SupaCloud operators",
|
|
28954
28985
|
type: "module",
|
|
28955
28986
|
main: "./dist/index.js",
|