diff --git a/backupbot.py b/backupbot.py index 822782f..ab1745f 100755 --- a/backupbot.py +++ b/backupbot.py @@ -7,6 +7,8 @@ import subprocess import logging import docker import restic +import tarfile +import io from datetime import datetime, timezone from restic.errors import ResticFailedError from pathlib import Path @@ -235,50 +237,66 @@ def list_files(snapshot, path): @click.option('volumes', '--volumes', '-v', is_flag=True) @click.option('secrets', '--secrets', '-c', is_flag=True) def download(snapshot, path, volumes, secrets): - if sum(map(bool, [path, volumes, secrets])) != 1: - logging.error( - "Please specify exactly one of '--path', '--volumes', '--secrets'") - exit(1) + file_dumps = [] + if not any([path, volumes, secrets]): + volumes = secrets = True if path: path = path.removesuffix('/') + binary_output = dump(snapshot, path) files = list_files(snapshot, path) filetype = [f.get('type') for f in files if f.get('path') == path][0] - filename = "/tmp/" + Path(path).name + filename = Path(path).name if filetype == 'dir': filename = filename + ".tar" - output = dump(snapshot, path) - with open(filename, "wb") as file: - file.write(output) - print(filename) - elif volumes: + tarinfo = tarfile.TarInfo(name=filename) + tarinfo.size = len(binary_output) + file_dumps.append((binary_output, tarinfo)) + if volumes: if not SERVICE: logging.error("Please specify '--host' when using '--volumes'") exit(1) - filename = f"/tmp/{SERVICE}.tar" files = list_files(snapshot, VOLUME_PATH) for f in files[1:]: path = f['path'] - if SERVICE in path and f['type'] == 'dir': - content = dump(snapshot, path) - # Concatenate tar files (extract with tar -xi) - with open(filename, "ab") as file: - file.write(content) - elif secrets: + if Path(path).name.startswith(SERVICE) and f['type'] == 'dir': + binary_output = dump(snapshot, path) + filename = f"{Path(path).name}.tar" + tarinfo = tarfile.TarInfo(name=filename) + tarinfo.size = len(binary_output) + file_dumps.append((binary_output, tarinfo)) + if secrets: if not SERVICE: logging.error("Please specify '--host' when using '--secrets'") exit(1) - filename = f"/tmp/SECRETS_{SERVICE}.json" + filename = f"{SERVICE}.json" files = list_files(snapshot, SECRET_PATH) secrets = {} for f in files[1:]: path = f['path'] - if SERVICE in path and f['type'] == 'file': + if Path(path).name.startswith(SERVICE) and f['type'] == 'file': secret = dump(snapshot, path).decode() secret_name = path.removeprefix(f'{SECRET_PATH}{SERVICE}_') secrets[secret_name] = secret - with open(filename, "w") as file: - json.dump(secrets, file) - print(filename) + binary_output = json.dumps(secrets).encode() + tarinfo = tarfile.TarInfo(name=filename) + tarinfo.size = len(binary_output) + file_dumps.append((binary_output, tarinfo)) + with tarfile.open('/tmp/backup.tar.gz', "w:gz") as tar: + print(f"Writing files to /tmp/backup.tar.gz...") + for binary_output, tarinfo in file_dumps: + tar.addfile(tarinfo, fileobj=io.BytesIO(binary_output)) + size = get_formatted_size('/tmp/backup.tar.gz') + print(f"Backup has been written to /tmp/backup.tar.gz with a size of {size}") + + +def get_formatted_size(file_path): + file_size = os.path.getsize(file_path) + units = ['Bytes', 'KB', 'MB', 'GB', 'TB'] + for unit in units: + if file_size < 1024: + return f"{round(file_size, 3)} {unit}" + file_size /= 1024 + return f"{round(file_size, 3)} {units[-1]}" def dump(snapshot, path): @@ -286,7 +304,7 @@ def dump(snapshot, path): if SERVICE: cmd = cmd + ['--tag', SERVICE] cmd = cmd + [snapshot, path] - logging.debug(f"Dumping {path} from snapshot '{snapshot}'") + print(f"Dumping {path} from snapshot '{snapshot}'") output = subprocess.run(cmd, capture_output=True) if output.returncode: logging.error(