From 4b4371ed3f61dee80acd0bdd09132e44ad62dbdb Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 28 Oct 2024 17:01:14 +0100 Subject: [PATCH] add restore-path argument for undeployed apps closes #59 --- backupbot.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/backupbot.py b/backupbot.py index 61d5772..b835ceb 100755 --- a/backupbot.py +++ b/backupbot.py @@ -116,7 +116,10 @@ def create(retries): def restore(snapshot_id, target, noninteractive, volumes, container, no_commands): app_settings = parse_backup_labels('restore', container) if SERVICE != 'ALL': - app_settings = {SERVICE: app_settings[SERVICE]} + if not app_settings.get(SERVICE): + logger.error(f"The app {SERVICE} is not running, use the restore-path argument to restore paths of undeployed apps") + exit(1) + app_settings = {SERVICE: app_settings.get(SERVICE)} pre_commands, post_commands, backup_paths, apps_versions = get_backup_details(app_settings, volumes) snapshots = get_snapshots(snapshot_id) if not snapshots: @@ -159,6 +162,41 @@ def restore(snapshot_id, target, noninteractive, volumes, container, no_commands logger.debug(result) +@cli.command() +@click.option('snapshot_id', '--snapshot', '-s', envvar='SNAPSHOT', default='latest') +@click.option('target', '--target', '-t', envvar='TARGET', default='/') +@click.option('noninteractive', '--noninteractive', envvar='NONINTERACTIVE', is_flag=True) +@click.argument('paths', nargs=-1, required=True, envvar='INCLUDE_PATH') +def restore_path(snapshot_id, target, noninteractive, paths): + """ PATHS: list of paths to restore""" + snapshots = get_snapshots(snapshot_id) + if not snapshots: + logger.error(f"No Snapshots with ID {snapshot_id} for app {SERVICE} found.") + exit(1) + snapshot = snapshots[0] + snapshot_id = snapshot['short_id'] + if not noninteractive: + print(f"Snapshot to restore: \t{snapshot_id}") + restore_app_versions = app_versions_from_tags(snapshot.get('tags')) + print("Apps:") + for app, version in restore_app_versions.items(): + if SERVICE == 'ALL' or SERVICE == app: + print(f"\t{app} \t {version}") + print("The following paths will be restored:") + for p in paths: + print(f'\t{p}') + snapshot_date = datetime.fromisoformat(snapshot['time']) + delta = datetime.now(tz=timezone.utc) - snapshot_date + print(f"This snapshot is {delta} old") + print("\nTHIS COMMAND WILL IRREVERSIBLY OVERWRITES FILES") + prompt = input("Type YES (uppercase) to continue: ") + if prompt != 'YES': + logger.error("Restore aborted") + exit(1) + print(f"Restoring Snapshot {snapshot_id} at {target}") + result = restic_restore(snapshot_id=snapshot_id, include=paths, target_dir=target) + logger.debug(result) + def restic_restore(snapshot_id, include=[], target_dir=None): cmd = restic.cat.base_command() + ['restore', snapshot_id] for path in include: