Implement bounded primary archive transfer and explicit essentials capture
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
codex 2026-09-06 08:00:59 +02:00
parent 69187c2f45
commit 4707bf08d7
4 changed files with 165 additions and 7 deletions

View file

@ -9,18 +9,24 @@ import time
import zipfile
def validate_archive(path):
ESSENTIALS_FLAGS = ['--skip-package-data', '--skip-log', '--skip-index', '--skip-repo-archives']
def validate_archive(path, profile="full"):
with zipfile.ZipFile(path) as archive:
if sum(i.file_size for i in archive.infolist()) > 20*1024**3:
raise ValueError('archive exceeds recovery size bound')
names = set(archive.namelist())
if 'forgejo-db.sql' not in names or not any(n.startswith('repos/') for n in names):
raise ValueError('required archive content missing')
if profile == 'essentials' and any(n.startswith(('data/packages/', 'data/repo-archive/', 'data/indexers/')) and not n.endswith('/') for n in names):
raise ValueError('excluded data present in essentials archive')
if archive.testzip() is not None:
raise ValueError('archive checksum failure')
def capture(namespace, pod, destination):
def capture(namespace, pod, destination, profile="full"):
if profile not in ("full", "essentials"): raise ValueError("unknown archive profile")
k = ['kubectl', 'exec', '--request-timeout=120s', '-n', namespace, pod, '-c', 'gitea', '--']
def call(args, timeout=150):
r = subprocess.run(k + args, capture_output=True, timeout=timeout)
@ -30,7 +36,8 @@ def capture(namespace, pod, destination):
completed = False
try:
# Completion belongs to this exact process, not a global pgrep or file existence.
command = f'umask 077; forgejo dump -f {remote}.zip >{remote}.log 2>&1; result=$?; printf "%s" "$result" >{remote}.exit'
flags = ' '.join(ESSENTIALS_FLAGS) if profile == 'essentials' else ''
command = f'umask 077; forgejo dump {flags} -f {remote}.zip >{remote}.log 2>&1; result=$?; printf "%s" "$result" >{remote}.exit'
call(['sh','-c', 'nohup sh -c "$1" >/dev/null 2>&1 </dev/null &', 'sh', command])
for _ in range(180):
r = subprocess.run(k + ['cat',remote+'.exit'], capture_output=True, timeout=30)
@ -53,7 +60,9 @@ def capture(namespace, pod, destination):
with destination.open('rb') as source:
actual = hashlib.file_digest(source,'sha256').hexdigest()
if actual != expected: raise ValueError('archive transfer mismatch')
validate_archive(destination)
validate_archive(destination, profile)
if profile == "essentials" and destination.stat().st_size > 600*1024**2:
raise ValueError("essentials archive exceeds budget")
finally:
# Do not remove a file while a timed-out producer may still be writing it.
if completed:
@ -64,8 +73,9 @@ def main():
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('--namespace',required=True); p.add_argument('--pod',required=True)
p.add_argument('--output',required=True,type=Path)
p.add_argument("--profile",choices=["full","essentials"],default="full")
a=p.parse_args()
try: capture(a.namespace,a.pod,a.output)
try: capture(a.namespace,a.pod,a.output,a.profile)
except Exception:
print('ERROR: Forgejo archive capture or integrity validation failed')
return 1