From 486a1e98090e6abc1c6629405f06a12117676477 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Mon, 13 Feb 2023 00:12:30 +0100 Subject: [PATCH] Add restore option to restic-batch --- .../backups/restic/setup/files/restic-batch | 35 ++++++++++++++++--- .../roles/backups/restic/setup/tasks/main.yml | 4 +++ .../backups/include/meta/argument_specs.yml | 7 ---- .../backups/include/vars/datasets.yml | 4 +++ .../services/backups/include/vars/main.yml | 2 -- .../backups/restic/meta/argument_specs.yml | 6 ++++ .../services/backups/restic/tasks/main.yml | 2 +- .../templates/restic-volumes-service.yml.j2 | 4 +++ 8 files changed, 50 insertions(+), 14 deletions(-) delete mode 100644 playbooks/roles/services/backups/include/meta/argument_specs.yml create mode 100644 playbooks/roles/services/backups/include/vars/datasets.yml delete mode 100644 playbooks/roles/services/backups/include/vars/main.yml diff --git a/playbooks/roles/backups/restic/setup/files/restic-batch b/playbooks/roles/backups/restic/setup/files/restic-batch index a1a48c9..50d8c9c 100644 --- a/playbooks/roles/backups/restic/setup/files/restic-batch +++ b/playbooks/roles/backups/restic/setup/files/restic-batch @@ -44,6 +44,10 @@ class Volume(abc.ABC): def _backup_path(self): raise NotImplementedError + @abc.abstractproperty + def _restore_path(self): + raise NotImplementedError + def backup(self): print(f"Backing up {self._bucket_name}", flush=True) @@ -68,9 +72,10 @@ class Volume(abc.ABC): # -------------------------------------------------------------------------------------- # Perform the backup. # -------------------------------------------------------------------------------------- - subprocess.run(self._restic_cmd_base + ["backup", "."], cwd=self._backup_path, - env=self._environ, check=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + ps = subprocess.run(self._restic_cmd_base + ["backup", "."], cwd=self._backup_path, + env=self._environ, check=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print(ps.stdout.decode("ascii"), flush=True) # -------------------------------------------------------------------------------------- # Forget and prune old snapshots. @@ -94,6 +99,19 @@ class Volume(abc.ABC): print(err.stdout.decode("ascii"), flush=True) raise + def restore(self): + print(f"Restoring {self._bucket_name}", flush=True) + + try: + subprocess.run(self._restic_cmd_base + ["restore", "latest", "--target", "."], + cwd=self._restore_path, + env=self._environ, check=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + except subprocess.CalledProcessError as err: + print(err.stdout.decode("ascii"), flush=True) + raise + class DirectoryVolume(Volume): @@ -105,6 +123,10 @@ class DirectoryVolume(Volume): def _backup_path(self): return self.__directory + @property + def _restore_path(self): + return self.__directory + class DatasetMount: @@ -142,8 +164,9 @@ class DatasetVolume(Volume): super().__init__(name, repo_config) self.__dataset = dataset self.__snapshot = self.__get_last_daily_snapshot() + self.__mountpoint = mountpoint self.__backup_path = os.path.normpath( - os.path.join("/", "mnt", os.path.relpath(mountpoint, "/")) + os.path.join("/", "mnt", os.path.relpath(self.__mountpoint, "/")) ) def __get_last_daily_snapshot(self): @@ -160,6 +183,10 @@ class DatasetVolume(Volume): def _backup_path(self): return self.__backup_path + @property + def _restore_path(self): + return self.__mountpoint + def backup(self): with DatasetMount(self.__snapshot, self._backup_path): super().backup() diff --git a/playbooks/roles/backups/restic/setup/tasks/main.yml b/playbooks/roles/backups/restic/setup/tasks/main.yml index 8030646..91915bc 100644 --- a/playbooks/roles/backups/restic/setup/tasks/main.yml +++ b/playbooks/roles/backups/restic/setup/tasks/main.yml @@ -40,6 +40,10 @@ state: "directory" mode: 0755 +- name: "install restic-batch dependencies" + ansible.builtin.apt: + name: "python3-yaml" + - name: "install the restic-batch script" ansible.builtin.copy: src: "./restic-batch" diff --git a/playbooks/roles/services/backups/include/meta/argument_specs.yml b/playbooks/roles/services/backups/include/meta/argument_specs.yml deleted file mode 100644 index ff7595a..0000000 --- a/playbooks/roles/services/backups/include/meta/argument_specs.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -argument_specs: - main: - options: - services_data_dataset: - type: "str" - required: true diff --git a/playbooks/roles/services/backups/include/vars/datasets.yml b/playbooks/roles/services/backups/include/vars/datasets.yml new file mode 100644 index 0000000..a0aa52c --- /dev/null +++ b/playbooks/roles/services/backups/include/vars/datasets.yml @@ -0,0 +1,4 @@ +--- +services_backups_user_data_dataset: "{{ services_data_dataset }}/{{ services_service_user_name }}" +services_backups_user_data_directory: "\ + {{ services_data_directory }}/{{ services_service_user_name }}" diff --git a/playbooks/roles/services/backups/include/vars/main.yml b/playbooks/roles/services/backups/include/vars/main.yml deleted file mode 100644 index acf2753..0000000 --- a/playbooks/roles/services/backups/include/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -services_backups_user_data_dataset: "{{ services_data_dataset }}/{{ services_service_user_name }}" diff --git a/playbooks/roles/services/backups/restic/meta/argument_specs.yml b/playbooks/roles/services/backups/restic/meta/argument_specs.yml index ec0e528..3286b48 100644 --- a/playbooks/roles/services/backups/restic/meta/argument_specs.yml +++ b/playbooks/roles/services/backups/restic/meta/argument_specs.yml @@ -8,6 +8,12 @@ argument_specs: services_service_name: type: "str" required: true + services_data_dataset: + type: "str" + required: false + services_data_directory: + type: "str" + required: false services_backups_restic_services: type: "dict" elem: "dict" diff --git a/playbooks/roles/services/backups/restic/tasks/main.yml b/playbooks/roles/services/backups/restic/tasks/main.yml index 48f2134..0eb9364 100644 --- a/playbooks/roles/services/backups/restic/tasks/main.yml +++ b/playbooks/roles/services/backups/restic/tasks/main.yml @@ -7,7 +7,7 @@ - name: "{{ services_service_name }} : tasks:vars" ansible.builtin.import_role: name: "services/backups/include" - vars_from: "main" + vars_from: "datasets" - name: "{{ services_service_name }} : create restic password file" ansible.builtin.template: diff --git a/playbooks/roles/services/backups/restic/templates/restic-volumes-service.yml.j2 b/playbooks/roles/services/backups/restic/templates/restic-volumes-service.yml.j2 index 138f7bd..a620918 100644 --- a/playbooks/roles/services/backups/restic/templates/restic-volumes-service.yml.j2 +++ b/playbooks/roles/services/backups/restic/templates/restic-volumes-service.yml.j2 @@ -1,4 +1,8 @@ +{% if services_data_dataset is defined %} dataset: {{ services_backups_user_data_dataset }} +{% else %} +directory: {{ services_backups_user_data_directory }} +{% endif %} aws_bucket_keys_file: {{ services_backups_restic_services[services_service_name].aws_keys_file }} aws_bucket_endpoint: {{ services_backups_restic_services[services_service_name].aws_bucket_endpoint }} aws_bucket_prefix: {{ services_backups_restic_services[services_service_name].aws_bucket_prefix }}