Protect dedicated staging disk and document verified migration
This commit is contained in:
1 parent
5ab3d078fb
commit
46644e5041
7 files changed
+158
-4
No files matched your search
@@ -186,6 +186,22 @@ class Runner:
|
||||
return count, size
|
||||
|
||||
def staging_preflight(self, manifest):
|
||||
# Check a dedicated staging disk before mkdir or any sync. Without this
|
||||
# guard a missing mount could silently put the staging tree on root.
|
||||
stage_mount = os.environ.get('EXPECTED_STAGE_MOUNT', '')
|
||||
stage_uuid = os.environ.get('EXPECTED_STAGE_UUID', '')
|
||||
if bool(stage_mount) != bool(stage_uuid):
|
||||
raise BackupError('EXPECTED_STAGE_MOUNT and EXPECTED_STAGE_UUID must be configured together')
|
||||
if stage_mount:
|
||||
mount_path = Path(stage_mount).absolute()
|
||||
if mount_path not in self.stage.parents or mount_path.resolve() != mount_path:
|
||||
raise BackupError('Staging must be below the configured staging mount')
|
||||
mounts = json.loads(self.run(['findmnt', '--json', '--target', mount_path,
|
||||
'--output', 'TARGET,UUID,FSTYPE'], capture=True))['filesystems']
|
||||
if (len(mounts) != 1 or mounts[0].get('target') != str(mount_path)
|
||||
or mounts[0].get('uuid') != stage_uuid
|
||||
or mounts[0].get('fstype') not in ('ext4', 'btrfs', 'xfs', 'f2fs', 'zfs')):
|
||||
raise BackupError('Dedicated staging disk is missing or has the wrong identity')
|
||||
# Refuse symlinked, overlapping, mounted or foreign staging paths before rclone sync.
|
||||
for other in (self.repo, self.state, Path.home() / 'Nextcloud'):
|
||||
if self.stage == other or self.stage in other.parents or other in self.stage.parents:
|
||||
@@ -339,7 +355,7 @@ class Runner:
|
||||
self.run([self.borg, 'check', '--repository-only', self.repo])
|
||||
# Success means a complete verified archive. Retention failure must not lose this fact.
|
||||
self.success = {'completed_at': time.time(), 'archive': archive, 'files': count,
|
||||
'bytes': size, 'repo': str(self.repo), 'remote': self.remote,
|
||||
'bytes': size, 'repo': str(self.repo), 'remote': self.remote, 'stage': str(self.stage),
|
||||
'archive_id': data['archives'][0]['id'], 'maintenance_pending': True}
|
||||
atomic_json(self.success_file, self.success)
|
||||
log(f'BACKUP VERIFIED: {archive} ({count} files, {size} bytes)')
|
||||
@@ -400,7 +416,7 @@ def main():
|
||||
sys.stdout = Tee()
|
||||
# Child output goes to journal; lifecycle/phase/status additionally go to the run log.
|
||||
try:
|
||||
log('Verified cloud-to-NAS runner 2.0.0, revision 3')
|
||||
log('Verified cloud-to-NAS runner 2.0.0, revision 4')
|
||||
runner.backup('--if-due' in sys.argv)
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
Reference in new issue
Block a user