Reject unfinished or truncated Forgejo backup archives before encryption
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
parent
6124a15c24
commit
d0c7249a0d
3 changed files with 98 additions and 93 deletions
|
|
@ -87,99 +87,8 @@ ok "forgejo-db" "${FORGEJO_DB_NAMESPACE}/${FORGEJO_DB_POD}"
|
|||
|
||||
# 1. Application dump (repos, packages, attachments, LFS, avatars)
|
||||
ok "forgejo dump" "running in pod…"
|
||||
DUMP_REMOTE="/tmp/forgejo-backup-${TS}.zip"
|
||||
# Long dumps drop the kubectl exec websocket if attached to the process; run in-pod
|
||||
# background and poll for the archive file instead.
|
||||
kubectl exec --request-timeout=0 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
sh -eu -c "rm -f '${DUMP_REMOTE}'; nohup forgejo dump -f '${DUMP_REMOTE}' >/tmp/forgejo-dump.log 2>&1 &"
|
||||
dump_wait=0
|
||||
while [[ "${dump_wait}" -lt 1800 ]]; do
|
||||
if kubectl exec --request-timeout=30 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
test -s "${DUMP_REMOTE}" 2>/dev/null; then
|
||||
if kubectl exec --request-timeout=30 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
sh -c "pgrep -f 'forgejo dump' >/dev/null"; then
|
||||
sleep 10
|
||||
dump_wait=$((dump_wait + 10))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
fi
|
||||
sleep 10
|
||||
dump_wait=$((dump_wait + 10))
|
||||
done
|
||||
if ! kubectl exec --request-timeout=30 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
test -s "${DUMP_REMOTE}" 2>/dev/null; then
|
||||
bad "forgejo dump" "timed out or empty archive (see /tmp/forgejo-dump.log in pod)"
|
||||
exit 1
|
||||
fi
|
||||
# Large dumps (~700M) fail via stdout stream or single kubectl cp — use chunks.
|
||||
dump_size="$(kubectl exec -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
stat -c%s "${DUMP_REMOTE}" 2>/dev/null || echo 0)"
|
||||
refresh_prod_pod() {
|
||||
PROD_POD="$(kubectl get pods -n "${FORGEJO_NAMESPACE}" \
|
||||
-l "app.kubernetes.io/instance=${FORGEJO_RELEASE}" \
|
||||
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)"
|
||||
}
|
||||
|
||||
copy_from_pod() {
|
||||
local remote_path="$1" local_path="$2" attempt
|
||||
for attempt in 1 2 3 4 5; do
|
||||
refresh_prod_pod
|
||||
if [[ -z "${PROD_POD}" ]]; then
|
||||
sleep $((attempt * 5))
|
||||
continue
|
||||
fi
|
||||
if kubectl cp -n "${FORGEJO_NAMESPACE}" -c gitea \
|
||||
"${FORGEJO_NAMESPACE}/${PROD_POD}:${remote_path}" "${local_path}" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
if kubectl exec --request-timeout=120 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
cat "${remote_path}" > "${local_path}" 2>/dev/null && [[ -s "${local_path}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ "${dump_size}" -lt 52428800 ]]; then
|
||||
copy_from_pod "${DUMP_REMOTE}" "${DUMP_PLAIN}" || exit 1
|
||||
else
|
||||
ok "forgejo dump" "chunked copy (${dump_size} bytes)…"
|
||||
chunk_dir="$(mktemp -d)"
|
||||
refresh_prod_pod
|
||||
parts="$(kubectl exec --request-timeout=120 -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
sh -c "rm -f /tmp/forgejo-backup-part-*; split -b 4m '${DUMP_REMOTE}' /tmp/forgejo-backup-part- && ls /tmp/forgejo-backup-part-*")"
|
||||
for part in ${parts}; do
|
||||
base="$(basename "${part}")"
|
||||
local_chunk="${chunk_dir}/${base}"
|
||||
if [[ -f "${local_chunk}" && -s "${local_chunk}" ]]; then
|
||||
ok "forgejo dump" "reuse cached chunk ${base}"
|
||||
continue
|
||||
fi
|
||||
chunk_ok=0
|
||||
for chunk_try in 1 2 3 4 5 6 7 8; do
|
||||
if copy_from_pod "${part}" "${local_chunk}"; then
|
||||
chunk_ok=1
|
||||
ok "forgejo dump" "chunk ${base} (${chunk_try})"
|
||||
break
|
||||
fi
|
||||
sleep $((chunk_try * 3))
|
||||
done
|
||||
if [[ "${chunk_ok}" -ne 1 ]]; then
|
||||
bad "forgejo dump" "chunk copy failed: ${base}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
cat "${chunk_dir}"/forgejo-backup-part-* > "${DUMP_PLAIN}"
|
||||
rm -rf "${chunk_dir}"
|
||||
kubectl exec -n "${FORGEJO_NAMESPACE}" "${PROD_POD}" -c gitea -- \
|
||||
rm -f /tmp/forgejo-backup-part-* "${DUMP_REMOTE}" || true
|
||||
fi
|
||||
if [[ ! -f "${DUMP_PLAIN}" || ! -s "${DUMP_PLAIN}" ]]; then
|
||||
bad "forgejo dump" "failed to copy dump from pod"
|
||||
exit 1
|
||||
fi
|
||||
python3 "${ROOT}/scripts/capture_forgejo_archive.py" \
|
||||
--namespace "${FORGEJO_NAMESPACE}" --pod "${PROD_POD}" --output "${DUMP_PLAIN}"
|
||||
ok "forgejo dump" "$(du -h "${DUMP_PLAIN}" | awk '{print $1}') $(basename "${DUMP_PLAIN}")"
|
||||
|
||||
# 2. PostgreSQL logical dump (plain SQL to stdout; CNPG root FS is read-only)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue