From 7ac51848b616edc2a7b3537a61ba4e559727fece Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Sun, 3 Oct 2021 23:21:52 +0300 Subject: [PATCH] Registry backup script. Meant to be run on a different machine. It can be used on the same machine and then the resulting files can be copied over. --- backup/backup | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/backup/backup b/backup/backup index 82505f4..f52d9f8 100755 --- a/backup/backup +++ b/backup/backup @@ -1,2 +1,74 @@ #!/usr/bin/env python3 """Backup a container image registry.""" + + +import argparse +import pathlib +import sys +import docker +import requests + + +def get_images(registry): + """Return a list of images from the registry.""" + return requests.get(f"https://{registry}/v2/_catalog").json()[ + "repositories" + ] + + +def get_image_tags(registry, image): + """Return a list of tags for an image in the registry.""" + return requests.get(f"https://{registry}/v2/{image}/tags/list").json()[ + "tags" + ] + + +def backup(docker_client, registry, image, tag, dest): + """Backup an image tag from the registry to a file in the destination + directory.""" + full_name = f"{registry}/{image}:{tag}" + print(f"Backing up {full_name}.", file=sys.stderr) + try: + docker_client.images.get(full_name) + image_existed = True + except docker.errors.ImageNotFound: + image_existed = False + docker_image = docker_client.images.pull(full_name) + with open(dest / f"{tag}.tar", "wb") as tarball: + for chunk in docker_image.save(): + tarball.write(chunk) + if not image_existed: + docker_client.images.remove(full_name) + + +def backup_registry(registry, dest): + """Backup the images in the registry to the destination.""" + docker_client = docker.from_env(timeout=90) + docker_client.ping() + for image in get_images(registry): + (dest / image).mkdir(exist_ok=True) + for tag in get_image_tags(registry, image): + backup(docker_client, registry, image, tag, dest / image) + + +if __name__ == "__main__": + arg_parser = argparse.ArgumentParser(description=__doc__) + arg_parser.add_argument("registry", help="FQDN of the registry to backup.") + arg_parser.add_argument( + "destination", help="Location to store the images.", type=pathlib.Path + ) + args = arg_parser.parse_args() + destination = args.destination.expanduser() + if not destination.exists() or not destination.is_dir(): + arg_parser.error( + "Backup destination doesn't exists or isn't a directory." + ) + try: + destination.touch() + except IsADirectoryError: + arg_parser.error("Can't write to the backup destination.") + try: + backup_registry(args.registry, destination) + except Exception as exception: + arg_parser.error(str(exception)) + sys.exit() -- GitLab