Prepare MariaDB restore tests and recover backup disk space safely

This commit is contained in:
admin committed 2026-09-12 20:36:18 +02:00
1 parent f0c761213d
commit b0416461bc
23 files changed
+918 -129

No files matched your search

+77
View File
@@ -311,6 +311,83 @@ exec /usr/bin/rclone "$@"
with self.config.open('a') as f:
f.write(f'ENABLE_DB_DUMPS=true\nWP_DB_NAME=production\nWP_DB_CNF=/unused\nRESTORE_TEST_DB_IMAGE="{image}"\nRESTORE_TEST_TIMEOUT=120\n')
@unittest.skipUnless(os.environ.get('RESTORE_TEST_REAL_DOCKER_IMAGE'), 'opt-in local Docker integration')
def test_installer_detects_mariadb_and_probes_real_image(self):
self.prepare_database(b'CREATE TABLE example (id INT);')
cnf=self.base/'backup.cnf'
cnf.write_text('[client]\n')
cnf.chmod(0o600)
client=self.base/'version-client'
client.write_text('#!/bin/sh\necho 10.11.18-MariaDB\n')
client.chmod(0o700)
with self.config.open('a') as f:
f.write(f'WP_DB_CNF="{cnf}"\nDB_CLIENT_BIN="{client}"\nRESTORE_TEST_DB_IMAGE=""\n')
result=subprocess.run(['bash',str(REPO/'lib/prepare-restore-test.sh'),str(self.config)],capture_output=True,text=True,timeout=120)
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
self.assertIn('RESTORE_TEST_DB_IMAGE=mariadb:10.11',self.config.read_text())
self.assertIn('initialization, import, check and cleanup passed',result.stdout)
self.assertFalse((self.root/'last-success.json').exists())
self.no_container()
@unittest.skipUnless(os.environ.get('RESTORE_TEST_REAL_DOCKER_IMAGE'), 'opt-in local Docker integration')
def test_installer_rejects_wrong_mariadb_series_without_config_change(self):
self.prepare_database(b'CREATE TABLE example (id INT);')
cnf=self.base/'backup.cnf'
cnf.write_text('[client]\n')
cnf.chmod(0o600)
client=self.base/'version-client'
client.write_text('#!/bin/sh\necho 11.4.1-MariaDB\n')
client.chmod(0o700)
with self.config.open('a') as f: f.write(f'WP_DB_CNF="{cnf}"\nDB_CLIENT_BIN="{client}"\n')
original=self.config.read_bytes()
result=subprocess.run(['bash',str(REPO/'lib/prepare-restore-test.sh'),str(self.config)],capture_output=True,text=True,timeout=60)
self.assertNotEqual(result.returncode,0,result.stdout+result.stderr)
self.assertIn('does not match production MariaDB series',result.stdout+result.stderr)
self.assertEqual(self.config.read_bytes(),original)
self.assertFalse(self.root.exists())
def test_space_recovery_uses_parent_lock_and_preserves_three_local_sets(self):
self.work.chmod(0o700)
for name in ('archives','staging','restore'): (self.work/name).mkdir(mode=0o700)
old=[]
for day in range(1,5):
run=f'appbackup_2020-01-0{day}_00-00-00_aaaaaaaa'
path=self.work/'archives'/run
shutil.copytree(self.folder,path)
path.chmod(0o700)
data=json.loads((path/'manifest.json').read_text())
data['run_id']=run
(path/'manifest.json').write_text(json.dumps(data))
(path/'COMPLETE').write_text('APP_BACKUP_V2 '+backup_set.digest(path/'manifest.json')+'\n')
(path/'UPLOADED').write_text('verified previously\n')
subprocess.run(['rclone','--config',str(self.crypt),'copy',str(path),'crypt:sets/'+run],check=True,capture_output=True)
old.append(path)
with self.config.open('a') as f: f.write('RESTORE_TEST_MIN_FREE_GB=0.001\n')
# Simulate pressure consistently for the Python supervisor and Bash df.
# Once one old local set is removed, the real free-space figure returns.
bindir=self.base/'bin'
bindir.mkdir()
script=self.base/'supervisor.py'
script.write_text('import os,runpy,shutil,sys\nfrom pathlib import Path\noriginal=shutil.disk_usage\npressure=Path('+repr(str(old[0]))+')\ndef usage(p):\n value=original(p)\n return value._replace(free=0) if pressure.exists() else value\nshutil.disk_usage=usage\npath=sys.argv.pop(1)\nsys.path.insert(0,str(Path(path).parent))\nrunpy.run_path(path,run_name="__main__")\n')
python=bindir/'python3'
python.write_text('#!/bin/sh\ncase "$1" in */restore_test.py) exec /usr/bin/python3 "'+str(script)+'" "$@";; esac\nexec /usr/bin/python3 "$@"\n')
python.chmod(0o700)
df=bindir/'df'
df.write_text('#!/bin/sh\nif test -d "'+str(old[0])+'"; then printf "Filesystem 1B-blocks Used Available Use%% Mounted\\nroot 100 100 0 100%% /\\n"; else exec /usr/bin/df "$@"; fi\n')
df.chmod(0o700)
result=subprocess.run(['bash',str(REPO/'usr-local-sbin/app-restore-test.sh'),'--config',str(self.config)],env=dict(os.environ,PATH=str(bindir)+':'+os.environ['PATH']),capture_output=True,text=True,timeout=60)
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
self.assertFalse(old[0].exists())
self.assertTrue(all(p.exists() for p in old[1:]))
self.assertEqual(self.state()['status'],'SUCCESS')
self.clean()
def test_adaptive_budget_accepts_zero(self):
with self.config.open('a') as f: f.write('RESTORE_TEST_MAX_WORK_GB=0\n')
self.execute()
self.clean()
self.assertEqual(self.state()['status'],'SUCCESS')
def no_container(self):
self.clean()
names=subprocess.check_output(restore_test.DOCKER+['ps','-a','--filter',f'label={restore_test.OWNER_LABEL}', '--format','{{.Names}}'],text=True)