fix(backup): worker-safe paths and python WebDAV upload fallback
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 6s

Default cache to /tmp when HOME is unwritable; allow python3 upload when
curl is absent (activity-core worker image).
This commit is contained in:
tegwick 2026-07-22 20:34:21 +02:00
parent 16a93b8e5c
commit ca24dc6507
2 changed files with 39 additions and 5 deletions

View file

@ -38,16 +38,41 @@ railiance_backup_require_tools() {
export PATH="$(dirname "${RAILIANCE_BACKUP_AGE_BIN}"):${PATH}" export PATH="$(dirname "${RAILIANCE_BACKUP_AGE_BIN}"):${PATH}"
fi fi
local t local t
for t in age curl kubectl; do for t in age kubectl; do
command -v "$t" >/dev/null 2>&1 || { echo "ERROR: missing required tool: $t (run tools/cmd/install-cnpg-backup-vendor-tools)" >&2; exit 1; } command -v "$t" >/dev/null 2>&1 || { echo "ERROR: missing required tool: $t (run tools/cmd/install-cnpg-backup-vendor-tools)" >&2; exit 1; }
done done
# curl optional — upload falls back to python3 urllib (activity-core worker has no curl)
if ! command -v curl >/dev/null 2>&1 && ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: need curl or python3 for Nextcloud upload" >&2
exit 1
fi
} }
railiance_backup_nc_upload() { railiance_backup_nc_upload() {
local file="$1" remote_name="$2" local file="$1" remote_name="$2"
local dest="${RAILIANCE_BACKUP_NC_WEBDAV_URL}/${RAILIANCE_BACKUP_NC_PREFIX}/${remote_name}" local dest="${RAILIANCE_BACKUP_NC_WEBDAV_URL%/}/${RAILIANCE_BACKUP_NC_PREFIX}/${remote_name}"
curl -sf -u "${RAILIANCE_BACKUP_NC_TOKEN}:" -T "$file" "$dest" \ if command -v curl >/dev/null 2>&1; then
|| { echo "ERROR: Nextcloud upload failed: ${remote_name}" >&2; return 1; } curl -sf -u "${RAILIANCE_BACKUP_NC_TOKEN}:" -T "$file" "$dest" \
|| { echo "ERROR: Nextcloud upload failed: ${remote_name}" >&2; return 1; }
return 0
fi
# Python fallback (worker image)
RAILIANCE_BACKUP_NC_TOKEN="${RAILIANCE_BACKUP_NC_TOKEN}" \
RAILIANCE_BACKUP_UPLOAD_URL="${dest}" \
RAILIANCE_BACKUP_UPLOAD_FILE="${file}" \
python3 - <<'PY' || { echo "ERROR: Nextcloud upload failed: ${remote_name}" >&2; return 1; }
import os, urllib.request, base64
url = os.environ["RAILIANCE_BACKUP_UPLOAD_URL"]
path = os.environ["RAILIANCE_BACKUP_UPLOAD_FILE"]
token = os.environ["RAILIANCE_BACKUP_NC_TOKEN"]
data = open(path, "rb").read()
req = urllib.request.Request(url, data=data, method="PUT")
req.add_header("Authorization", "Basic " + base64.b64encode(f"{token}:".encode()).decode())
req.add_header("Content-Type", "application/octet-stream")
with urllib.request.urlopen(req, timeout=600) as resp:
if resp.status not in (200, 201, 204):
raise SystemExit(f"HTTP {resp.status}")
PY
} }
railiance_backup_encrypt() { railiance_backup_encrypt() {

View file

@ -28,7 +28,16 @@ source "${ROOT}/lib/railiance-backup-common.sh"
DRY_RUN="${RAILIANCE_BACKUP_DRY_RUN:-0}" DRY_RUN="${RAILIANCE_BACKUP_DRY_RUN:-0}"
UPDATE_STATUS="${CNPG_UPDATE_STATUS_CM:-1}" UPDATE_STATUS="${CNPG_UPDATE_STATUS_CM:-1}"
STATUS_CM="${CNPG_OPTION_A_STATUS_CM:-cnpg-option-a-status}" STATUS_CM="${CNPG_OPTION_A_STATUS_CM:-cnpg-option-a-status}"
BACKUP_ROOT="${BACKUP_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/railiance/backups/cnpg}" # Prefer explicit BACKUP_DIR; fall back to cache, then /tmp (worker pods often lack $HOME).
if [[ -n "${BACKUP_DIR:-}" ]]; then
BACKUP_ROOT="${BACKUP_DIR}"
elif [[ -n "${XDG_CACHE_HOME:-}" ]]; then
BACKUP_ROOT="${XDG_CACHE_HOME}/railiance/backups/cnpg"
elif [[ -n "${HOME:-}" && -w "${HOME}" ]]; then
BACKUP_ROOT="${HOME}/.cache/railiance/backups/cnpg"
else
BACKUP_ROOT="/tmp/railiance-backups/cnpg"
fi
JSON_ONLY="${CNPG_BACKUP_JSON:-1}" JSON_ONLY="${CNPG_BACKUP_JSON:-1}"
KUBECONFIG_CORE="${KUBECONFIG_CORE:-${KUBECONFIG_COULOMBCORE:-$HOME/.kube/config}}" KUBECONFIG_CORE="${KUBECONFIG_CORE:-${KUBECONFIG_COULOMBCORE:-$HOME/.kube/config}}"