direct remote environments - remote stacks dir configurable, for putting the whole stack onto the remote
This commit is contained in:
19 files changed
+2316
-506
No files matched your search
@@ -27,9 +27,9 @@ export interface MetadataFile {
|
||||
* The metadata files (metadata.json, and for stacks the compose/.env and the
|
||||
* whole stackfiles/ tree) are NOT written by this script — they are streamed into
|
||||
* the container's /metadata directory via the Docker put-archive API BEFORE the
|
||||
* container starts (see restic.ts runInHelper). Embedding their base64 inline in
|
||||
* the Cmd used to blow the kernel ARG_MAX for large stack files ("argument list
|
||||
* too long"); put-archive has no such limit.
|
||||
* container starts (see restic.ts runInHelper). Inlining their base64 in the Cmd
|
||||
* would hit the kernel ARG_MAX for large stack files ("argument list too long");
|
||||
* put-archive has no such limit.
|
||||
*
|
||||
* So the script only:
|
||||
* 1. ensures /metadata exists (put-archive already created it, but be defensive
|
||||
@@ -41,7 +41,7 @@ export interface MetadataFile {
|
||||
* @param resticArgs the full restic argv (starting with 'backup') — already
|
||||
* tag/exclude/flag-assembled by the caller.
|
||||
*/
|
||||
export function buildBackupScript(resticArgs: string[]): string {
|
||||
export function buildBackupScript(resticArgs: string[], stackDirProbe?: { volumeKey: string; composeFileName: string; label?: string; hostPath?: string }): string {
|
||||
// restic runs backgrounded so the SIGINT-forwarding trap can reach it on cancel
|
||||
// (SIGINT lets restic release its repo lock; a SIGKILL orphans it and hangs the
|
||||
// next backup on --retry-lock). The kill-0 re-wait loop only fires when a signal
|
||||
@@ -49,7 +49,30 @@ export function buildBackupScript(resticArgs: string[]): string {
|
||||
// real code (0/3) for the marker.
|
||||
const restic = `restic ${resticArgs.map(shellQuote).join(' ')}`;
|
||||
const step = `${restic} & __rpid=$!; trap 'kill -INT $__rpid 2>/dev/null' INT TERM; wait $__rpid; __rc=$?; while kill -0 $__rpid 2>/dev/null; do wait $__rpid; __rc=$?; done; ( exit $__rc )`;
|
||||
return `mkdir -p /metadata; set +e; ${finishScript(step)}`;
|
||||
|
||||
// STACK-DIR VOLUME PROBE (100% runtime guard for the 'volume' capture mode). The plan
|
||||
// decided the target daemon is local and bind-mounted the stack dir at
|
||||
// /volumes/<volumeKey>. If that mount is a phantom (a remote daemon Docker
|
||||
// auto-created an EMPTY dir for), the compose file will be MISSING under it — backing
|
||||
// that up would be a silent-empty snapshot. So before restic runs, assert the compose
|
||||
// is visible; if not, exit non-zero (the finishScript marker turns this into a hard
|
||||
// backup failure), never a silent success.
|
||||
let probe = '';
|
||||
if (stackDirProbe) {
|
||||
const composePath = shellQuote(`/volumes/${stackDirProbe.volumeKey}/${stackDirProbe.composeFileName}`);
|
||||
const label = stackDirProbe.label ? ` ${stackDirProbe.label}` : '';
|
||||
// Name the REAL host path we mounted (<remote_stacks_dir>/<stack>) so the operator knows
|
||||
// exactly where to look, and what to do: redeploy the stack to stage its files there.
|
||||
const hostHint = stackDirProbe.hostPath
|
||||
? ` The stack folder is empty or missing at ${stackDirProbe.hostPath} on the Docker host - redeploy the stack to stage its files there.`
|
||||
: '';
|
||||
probe =
|
||||
`if [ ! -f ${composePath} ]; then ` +
|
||||
`echo "[backup] $(date -u +%Y-%m-%dT%H:%M:%SZ)${label} STACKDIR PROBE FAILED: the stack's compose (${stackDirProbe.composeFileName}) is not present under the mounted stack dir.${hostHint} Refusing to write a silent-empty snapshot." 1>&2; ` +
|
||||
`exit 1; fi; `;
|
||||
}
|
||||
|
||||
return `mkdir -p /metadata; set +e; ${probe}${finishScript(step)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user