#!/usr/bin/python3 """Bounded daily backup. Never modifies application data or schema.""" from pathlib import Path import subprocess, json, datetime, shutil, os os.umask(0o077) root=Path('/root/anwarpy-backups');root.mkdir(mode=0o700,exist_ok=True) now=datetime.datetime.now(datetime.timezone.utc);folder=root/('daily-'+now.strftime('%Y%m%dT%H%M%SZ'));folder.mkdir(mode=0o700) try: with (folder/'database.dump').open('wb') as out: subprocess.run(['docker','exec','anwarpi-db-1','pg_dump','-U','anwarpi','-d','anwarpi','-Fc'],stdout=out,check=True) subprocess.run(['tar','-czf',str(folder/'source-config.tar.gz'),'-C','/root','anwarpi'],check=True) info=json.loads(subprocess.check_output(['docker','inspect','anwarpi-web-1'],text=True))[0] for mount in info['Mounts']: if mount['Destination'] in ['/app/media','/app/files-private']: subprocess.run(['tar','-czf',str(folder/(mount['Destination'].split('/')[-1]+'.tar.gz')),'-C',mount['Source'],'.'],check=True) subprocess.run(['docker','cp',str(folder/'database.dump'),'anwarpi-db-1:/var/lib/postgresql/daily-archive-check.dump'],check=True) archive=subprocess.check_output(['docker','exec','anwarpi-db-1','pg_restore','--list','/var/lib/postgresql/daily-archive-check.dump'],text=True) assert 'TABLE DATA' in archive record={'status':'ok','finished_at':now.isoformat(),'backup':str(folder),'image':info['Image'],'archive_list_valid':True} (folder/'verification.json').write_text(json.dumps(record)) for old in root.glob('daily-*'): if old.is_dir() and old!=folder and now.timestamp()-old.stat().st_mtime>30*86400:shutil.rmtree(old) except Exception as error: record={'status':'failed','finished_at':now.isoformat(),'error_type':type(error).__name__} (root/'health.json').write_text(json.dumps(record));print(json.dumps(record));raise (root/'health.json').write_text(json.dumps(record));print(json.dumps(record))