From f59a046bbc56e80815429620af960b59009a2427 Mon Sep 17 00:00:00 2001 From: Jannik Date: Tue, 26 May 2020 10:42:29 +0200 Subject: [PATCH 1/3] Output backup destination path with output command --- src/restic_compose_backup/cli.py | 4 ++-- src/restic_compose_backup/containers.py | 9 ++++++++- src/restic_compose_backup/containers_db.py | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/restic_compose_backup/cli.py b/src/restic_compose_backup/cli.py index 9e969fc..7ac85c5 100644 --- a/src/restic_compose_backup/cli.py +++ b/src/restic_compose_backup/cli.py @@ -91,12 +91,12 @@ def status(config, containers): if container.volume_backup_enabled: for mount in container.filter_mounts(): - logger.info(' - volume: %s', mount.source) + logger.info(' - volume: %s -> %s', mount.source, container.get_volume_backup_destination(mount, '/volumes')) if container.database_backup_enabled: instance = container.instance ping = instance.ping() - logger.info(' - %s (is_ready=%s)', instance.container_type, ping == 0) + logger.info(' - %s (is_ready=%s) -> %s', instance.container_type, ping == 0, instance.backup_destination_path()) if ping != 0: logger.error("Database '%s' in service %s cannot be reached", instance.container_type, container.service_name) diff --git a/src/restic_compose_backup/containers.py b/src/restic_compose_backup/containers.py index fbc2049..f5ce4d7 100644 --- a/src/restic_compose_backup/containers.py +++ b/src/restic_compose_backup/containers.py @@ -224,12 +224,15 @@ class Container: volumes = {} for mount in mounts: volumes[mount.source] = { - 'bind': str(Path(source_prefix) / self.service_name / Path(utils.strip_root(mount.destination))), + 'bind': self.get_volume_backup_destination(mount, source_prefix), 'mode': mode, } return volumes + def get_volume_backup_destination(self, mount, source_prefix) -> str: + return str(Path(source_prefix) / self.service_name / Path(utils.strip_root(mount.destination))) + def get_credentials(self) -> dict: """dict: get credentials for the service""" raise NotImplementedError("Base container class don't implement this") @@ -242,6 +245,10 @@ class Container: """Back up this service""" raise NotImplementedError("Base container class don't implement this") + def backup_destination_path(self) -> str: + """Return the path backups will be saved at""" + raise NotImplementedError("Base container class don't implement this") + def dump_command(self) -> list: """list: create a dump command restic and use to send data through stdin""" raise NotImplementedError("Base container class don't implement this") diff --git a/src/restic_compose_backup/containers_db.py b/src/restic_compose_backup/containers_db.py index 5ad3ce0..156273d 100644 --- a/src/restic_compose_backup/containers_db.py +++ b/src/restic_compose_backup/containers_db.py @@ -48,10 +48,13 @@ class MariadbContainer(Container): with utils.environment('MYSQL_PWD', creds['password']): return restic.backup_from_stdin( config.repository, - f'/databases/{self.service_name}/all_databases.sql', + self.backup_destination_path(), self.dump_command(), ) + def backup_destination_path(self) -> str: + return f'/databases/{self.service_name}/all_databases.sql' + class MysqlContainer(Container): container_type = 'mysql' @@ -94,10 +97,13 @@ class MysqlContainer(Container): with utils.environment('MYSQL_PWD', creds['password']): return restic.backup_from_stdin( config.repository, - f'/databases/{self.service_name}/all_databases.sql', + self.backup_destination_path(), self.dump_command(), ) + def backup_destination_path(self) -> str: + return f'/databases/{self.service_name}/all_databases.sql' + class PostgresContainer(Container): container_type = 'postgres' @@ -141,6 +147,9 @@ class PostgresContainer(Container): with utils.environment('PGPASSWORD', creds['password']): return restic.backup_from_stdin( config.repository, - f"/databases/{self.service_name}/{creds['database']}.sql", + self.backup_destination_path(), self.dump_command(), ) + + def backup_destination_path(self) -> str: + return f"/databases/{self.service_name}/{self.get_credentials()['database']}.sql" From 18ddb173ac2e7dfdd441525119de5991943ddf80 Mon Sep 17 00:00:00 2001 From: Jannik Date: Tue, 26 May 2020 14:30:59 +0200 Subject: [PATCH 2/3] Allow inclusion of project name in backup path --- restic_compose_backup.env | 1 + src/restic_compose_backup/cli.py | 1 + src/restic_compose_backup/config.py | 1 + src/restic_compose_backup/containers.py | 12 ++++++- src/restic_compose_backup/containers_db.py | 40 +++++++++++++++++++--- 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/restic_compose_backup.env b/restic_compose_backup.env index bd474f9..728bed5 100644 --- a/restic_compose_backup.env +++ b/restic_compose_backup.env @@ -5,6 +5,7 @@ # DOCKER_CERT_PATH='' SWARM_MODE=true +INCLUDE_PROJECT_NAME=false RESTIC_REPOSITORY=/restic_data RESTIC_PASSWORD=password diff --git a/src/restic_compose_backup/cli.py b/src/restic_compose_backup/cli.py index 7ac85c5..661603f 100644 --- a/src/restic_compose_backup/cli.py +++ b/src/restic_compose_backup/cli.py @@ -66,6 +66,7 @@ def status(config, containers): logger.info("Status for compose project '%s'", containers.project_name) logger.info("Repository: '%s'", config.repository) logger.info("Backup currently running?: %s", containers.backup_process_running) + logger.info("Include project name in backup path?: %s", utils.is_true(config.include_project_name)) logger.info("Checking docker availability") utils.list_containers() diff --git a/src/restic_compose_backup/config.py b/src/restic_compose_backup/config.py index e5c5693..264bfcc 100644 --- a/src/restic_compose_backup/config.py +++ b/src/restic_compose_backup/config.py @@ -13,6 +13,7 @@ class Config: self.cron_schedule = os.environ.get('CRON_SCHEDULE') or self.default_crontab_schedule self.cron_command = os.environ.get('CRON_COMMAND') or self.default_backup_command self.swarm_mode = os.environ.get('SWARM_MODE') or False + self.include_project_name = os.environ.get('INCLUDE_PROJECT_NAME') or False # Log self.log_level = os.environ.get('LOG_LEVEL') diff --git a/src/restic_compose_backup/containers.py b/src/restic_compose_backup/containers.py index f5ce4d7..02a3f7e 100644 --- a/src/restic_compose_backup/containers.py +++ b/src/restic_compose_backup/containers.py @@ -231,7 +231,17 @@ class Container: return volumes def get_volume_backup_destination(self, mount, source_prefix) -> str: - return str(Path(source_prefix) / self.service_name / Path(utils.strip_root(mount.destination))) + destination = Path(source_prefix) + + if utils.is_true(config.include_project_name): + project_name = self.project_name + if project_name != '': + destination /= project_name + + destination /= self.service_name + destination /= Path(utils.strip_root(mount.destination)) + + return str(destination) def get_credentials(self) -> dict: """dict: get credentials for the service""" diff --git a/src/restic_compose_backup/containers_db.py b/src/restic_compose_backup/containers_db.py index 156273d..ea9c66e 100644 --- a/src/restic_compose_backup/containers_db.py +++ b/src/restic_compose_backup/containers_db.py @@ -1,5 +1,7 @@ +from pathlib import Path + from restic_compose_backup.containers import Container -from restic_compose_backup.config import Config +from restic_compose_backup.config import config, Config from restic_compose_backup import ( commands, restic, @@ -53,7 +55,17 @@ class MariadbContainer(Container): ) def backup_destination_path(self) -> str: - return f'/databases/{self.service_name}/all_databases.sql' + destination = Path("/databases") + + if utils.is_true(config.include_project_name): + project_name = self.project_name + if project_name != "": + destination /= project_name + + destination /= self.service_name + destination /= "all_databases.sql" + + return destination class MysqlContainer(Container): @@ -102,7 +114,17 @@ class MysqlContainer(Container): ) def backup_destination_path(self) -> str: - return f'/databases/{self.service_name}/all_databases.sql' + destination = Path("/databases") + + if utils.is_true(config.include_project_name): + project_name = self.project_name + if project_name != "": + destination /= project_name + + destination /= self.service_name + destination /= "all_databases.sql" + + return destination class PostgresContainer(Container): @@ -152,4 +174,14 @@ class PostgresContainer(Container): ) def backup_destination_path(self) -> str: - return f"/databases/{self.service_name}/{self.get_credentials()['database']}.sql" + destination = Path("/databases") + + if utils.is_true(config.include_project_name): + project_name = self.project_name + if project_name != "": + destination /= project_name + + destination /= self.service_name + destination /= f"{self.get_credentials()['database']}.sql" + + return destination From f7958d7db90b136482b93e292bce0b6d2c675e19 Mon Sep 17 00:00:00 2001 From: Jannik Date: Tue, 26 May 2020 14:54:36 +0200 Subject: [PATCH 3/3] Add docstring for get_volume_backup_destination --- src/restic_compose_backup/containers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/restic_compose_backup/containers.py b/src/restic_compose_backup/containers.py index 02a3f7e..863677d 100644 --- a/src/restic_compose_backup/containers.py +++ b/src/restic_compose_backup/containers.py @@ -231,6 +231,7 @@ class Container: return volumes def get_volume_backup_destination(self, mount, source_prefix) -> str: + """Get the destination path for backups of the given mount""" destination = Path(source_prefix) if utils.is_true(config.include_project_name):