Support home-based mailboxes during automated upgrades

This commit is contained in:
admin committed 2026-09-10 14:31:32 +02:00
1 parent 8e6c9a058f
commit 56fbcdad74
8 files changed
+194 -9

No files matched your search

+70
View File
@@ -440,6 +440,76 @@ QUIESCE_SERVICES=(nc-cron)
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
self.assertNotIn('systemctl stop',self.events.read_text())
def test_missing_mail_store_requires_explicit_resolution(self):
canonical=self.configure_upgrade()
with canonical.open('a') as f: f.write(f'MAIL_DIR="{self.src}/missing-mail"\n')
original=canonical.read_bytes()
package=self.build_package()
result=subprocess.run(['bash',str(package),'--foreground'],env=self.env,capture_output=True,text=True,timeout=60)
self.assertNotEqual(result.returncode,0)
self.assertIn('--mail-dir DIR',result.stdout+result.stderr)
self.assertEqual(canonical.read_bytes(),original)
self.assertFalse((self.src/'missing-mail').exists())
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
self.assertNotIn('systemctl stop',self.events.read_text())
def test_package_mail_directory_override(self):
canonical=self.configure_upgrade()
with canonical.open('a') as f: f.write(f'MAIL_DIR="{self.src}/missing-mail"\n')
package=self.build_package()
result=subprocess.run(['bash',str(package),'--foreground','--mail-dir',str(self.src/'mail')],env=self.env,capture_output=True,text=True,timeout=120)
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
self.assertIn('ENABLE_MAIL_STORE=true',canonical.read_text())
run=next((self.base/'work/archives').glob('appbackup_*'))
self.assertTrue((run/'mail.tar.zst').is_file())
self.assertTrue((run/'UPLOADED').is_file())
def test_package_homes_only_keeps_mail_configs_and_service_stops(self):
canonical=self.configure_upgrade()
home=self.src/'johannes'
for folder in ['cur','new','tmp']:
(home/'Mail'/folder).mkdir(parents=True)
mailbox=home/'Mail/cur/test-message:2,S'
mailbox.write_bytes(b'Subject: retained mail\n\nMailbox content\n')
mailbox.chmod(0o600)
original=mailbox.read_bytes()
with canonical.open('a') as f:
f.write(f'MAIL_DIR="{self.src}/missing-mail"\nENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=("{home}")\n')
package=self.build_package()
result=subprocess.run(['bash',str(package),'--foreground','--mail-homes-only'],env=self.env,capture_output=True,text=True,timeout=120)
self.assertEqual(result.returncode,0,result.stdout+result.stderr)
self.assertIn('ENABLE_MAIL_STORE=false',canonical.read_text())
run=next((self.base/'work/archives').glob('appbackup_*'))
for name in ['mail-home-0','postfix','dovecot']:
self.assertTrue((run/f'{name}.tar.zst').is_file())
self.assertFalse((run/'mail.tar.zst').exists())
self.assertTrue((run/'UPLOADED').is_file())
for unit in ['postfix','dovecot']:
self.assertIn(f'systemctl stop {unit}',self.events.read_text())
self.assertEqual(json.loads(self.state.read_text())['services'][unit],'active')
mailbox.write_text('changed after backup')
restored=subprocess.run(['bash',str(REPO/'usr-local-sbin/app-restore.sh'),'--config',str(canonical),
'--local-run',str(run),'--components','mail-home-0','--apply','--yes'],
env=self.env,capture_output=True,text=True,timeout=60)
self.assertEqual(restored.returncode,0,restored.stdout+restored.stderr)
self.assertEqual(mailbox.read_bytes(),original)
self.assertEqual(mailbox.stat().st_mode & 0o777,0o600)
def test_homes_only_rejects_disabled_empty_and_missing_homes(self):
canonical=self.configure_upgrade()
original=canonical.read_text()
package=self.build_package()
for settings in ['ENABLE_MAIL_HOME_DIRS=false', 'ENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=()',
f'ENABLE_MAIL_HOME_DIRS=true\nMAIL_HOME_DIRS=("{self.src}/missing-home")']:
with self.subTest(settings=settings):
canonical.write_text(original+'\n'+settings+'\n')
before=canonical.read_bytes()
result=subprocess.run(['bash',str(package),'--foreground','--mail-homes-only'],env=self.env,capture_output=True,text=True,timeout=60)
self.assertNotEqual(result.returncode,0,result.stdout+result.stderr)
self.assertEqual(canonical.read_bytes(),before)
self.assertEqual(json.loads(self.state.read_text())['services']['app-backup.timer'],'active')
self.assertNotIn('systemctl stop',self.events.read_text())
def test_package_failed_backup_keeps_timer_stopped_and_rollback_available(self):
canonical=self.configure_upgrade()
original=canonical.read_bytes()