Merge pull request 'localbackup' (#1020) from localbackup into main
This commit is contained in:
@@ -76,7 +76,15 @@
|
|||||||
clanCore.secretStore = "vm";
|
clanCore.secretStore = "vm";
|
||||||
clanCore.clanDir = ../..;
|
clanCore.clanDir = ../..;
|
||||||
|
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
environment.systemPackages = [
|
||||||
|
self.packages.${pkgs.system}.clan-cli
|
||||||
|
(pkgs.writeShellScriptBin "pre-restore-command" ''
|
||||||
|
touch /var/test-service/pre-restore-command
|
||||||
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "post-restore-command" ''
|
||||||
|
touch /var/test-service/post-restore-command
|
||||||
|
'')
|
||||||
|
];
|
||||||
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
||||||
nix.settings = {
|
nix.settings = {
|
||||||
substituters = lib.mkForce [ ];
|
substituters = lib.mkForce [ ];
|
||||||
@@ -86,6 +94,12 @@
|
|||||||
};
|
};
|
||||||
system.extraDependencies = dependencies;
|
system.extraDependencies = dependencies;
|
||||||
clanCore.state.test-backups.folders = [ "/var/test-backups" ];
|
clanCore.state.test-backups.folders = [ "/var/test-backups" ];
|
||||||
|
|
||||||
|
clanCore.state.test-service = {
|
||||||
|
preRestoreCommand = "pre-restore-command";
|
||||||
|
postRestoreCommand = "post-restore-command";
|
||||||
|
folders = [ "/var/test-service" ];
|
||||||
|
};
|
||||||
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
||||||
|
|
||||||
services.borgbackup.repos.test-backups = {
|
services.borgbackup.repos.test-backups = {
|
||||||
@@ -110,7 +124,7 @@
|
|||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
# dummy data
|
# dummy data
|
||||||
machine.succeed("mkdir -p /var/test-backups")
|
machine.succeed("mkdir -p /var/test-backups /var/test-service")
|
||||||
machine.succeed("echo testing > /var/test-backups/somefile")
|
machine.succeed("echo testing > /var/test-backups/somefile")
|
||||||
|
|
||||||
# create
|
# create
|
||||||
@@ -119,14 +133,16 @@
|
|||||||
|
|
||||||
# list
|
# list
|
||||||
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
|
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
|
||||||
out = machine.succeed("clan --debug --flake ${self} backups list test-backup")
|
out = machine.succeed("clan --debug --flake ${self} backups list test-backup").strip()
|
||||||
print(out)
|
print(out)
|
||||||
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
||||||
|
|
||||||
# restore
|
# restore
|
||||||
machine.succeed("rm -f /var/test-backups/somefile")
|
machine.succeed("rm -f /var/test-backups/somefile")
|
||||||
machine.succeed(f"clan --debug --flake ${self} backups restore test-backup borgbackup borg@machine:.::{backup_id} >&2")
|
machine.succeed(f"clan --debug --flake ${self} backups restore test-backup borgbackup {out} >&2")
|
||||||
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
||||||
|
machine.succeed("test -f /var/test-service/pre-restore-command")
|
||||||
|
machine.succeed("test -f /var/test-service/post-restore-command")
|
||||||
'';
|
'';
|
||||||
} { inherit pkgs self; };
|
} { inherit pkgs self; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ in
|
|||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
name = lib.mkOption {
|
name = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.strMatching "^[a-zA-Z0-9._-]+$";
|
||||||
default = name;
|
default = name;
|
||||||
description = "the name of the backup job";
|
description = "the name of the backup job";
|
||||||
};
|
};
|
||||||
@@ -90,32 +90,41 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [ pkgs.jq ];
|
environment.systemPackages = [
|
||||||
|
(pkgs.writeShellScriptBin "borgbackup-create" ''
|
||||||
clanCore.backups.providers.borgbackup = {
|
set -efu -o pipefail
|
||||||
# TODO list needs to run locally or on the remote machine
|
|
||||||
list = ''
|
|
||||||
set -efu
|
|
||||||
# we need yes here to skip the changed url verification
|
|
||||||
${
|
|
||||||
lib.concatMapStringsSep "\\\n" (
|
|
||||||
dest:
|
|
||||||
''yes y | borg-job-${dest.name} list --json | jq '[.archives[] | {"name": ("${dest.repo}::" + .name), "job_name": "${dest.name}"}]' ''
|
|
||||||
) (lib.attrValues cfg.destinations)
|
|
||||||
} | jq -s 'add'
|
|
||||||
'';
|
|
||||||
create = ''
|
|
||||||
${lib.concatMapStringsSep "\n" (dest: ''
|
${lib.concatMapStringsSep "\n" (dest: ''
|
||||||
systemctl start borgbackup-job-${dest.name}
|
systemctl start borgbackup-job-${dest.name}
|
||||||
'') (lib.attrValues cfg.destinations)}
|
'') (lib.attrValues cfg.destinations)}
|
||||||
'';
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "borgbackup-list" ''
|
||||||
restore = ''
|
|
||||||
set -efu
|
set -efu
|
||||||
|
(${
|
||||||
|
lib.concatMapStringsSep "\n" (
|
||||||
|
dest:
|
||||||
|
# we need yes here to skip the changed url verification
|
||||||
|
''yes y | borg-job-${dest.name} list --json | jq '[.archives[] | {"name": ("${dest.name}::${dest.repo}::" + .name)}]' ''
|
||||||
|
) (lib.attrValues cfg.destinations)
|
||||||
|
}) | ${pkgs.jq}/bin/jq -s 'add'
|
||||||
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "borgbackup-restore" ''
|
||||||
|
set -efux
|
||||||
cd /
|
cd /
|
||||||
IFS=';' read -ra FOLDER <<< "$FOLDERS"
|
IFS=';' read -ra FOLDER <<< "$FOLDERS"
|
||||||
yes y | borg-job-"$JOB_NAME" extract --list "$NAME" "''${FOLDER[@]}"
|
job_name=$(echo "$NAME" | ${pkgs.gawk}/bin/awk -F'::' '{print $1}')
|
||||||
'';
|
backup_name=''${NAME#"$job_name"::}
|
||||||
|
if ! command -v borg-job-"$job_name" &> /dev/null; then
|
||||||
|
echo "borg-job-$job_name not found: Backup name is invalid" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
yes y | borg-job-"$job_name" extract --list "$backup_name" "''${FOLDER[@]}"
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
clanCore.backups.providers.borgbackup = {
|
||||||
|
list = "borgbackup-list";
|
||||||
|
create = "borgbackup-create";
|
||||||
|
restore = "borgbackup-restore";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
borgbackup = ./borgbackup.nix;
|
borgbackup = ./borgbackup.nix;
|
||||||
|
localbackup = ./localbackup.nix;
|
||||||
deltachat = ./deltachat.nix;
|
deltachat = ./deltachat.nix;
|
||||||
moonlight = ./moonlight.nix;
|
moonlight = ./moonlight.nix;
|
||||||
sunshine = ./sunshine.nix;
|
sunshine = ./sunshine.nix;
|
||||||
|
|||||||
151
clanModules/localbackup.nix
Normal file
151
clanModules/localbackup.nix
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.clan.localbackup;
|
||||||
|
rsnapshotConfig = target: states: ''
|
||||||
|
config_version 1.2
|
||||||
|
snapshot_root ${target}
|
||||||
|
sync_first 1
|
||||||
|
cmd_cp ${pkgs.coreutils}/bin/cp
|
||||||
|
cmd_rm ${pkgs.coreutils}/bin/rm
|
||||||
|
cmd_rsync ${pkgs.rsync}/bin/rsync
|
||||||
|
cmd_ssh ${pkgs.openssh}/bin/ssh
|
||||||
|
cmd_logger ${pkgs.inetutils}/bin/logger
|
||||||
|
cmd_du ${pkgs.coreutils}/bin/du
|
||||||
|
cmd_rsnapshot_diff ${pkgs.rsnapshot}/bin/rsnapshot-diff
|
||||||
|
retain snapshot ${builtins.toString config.clan.localbackup.snapshots}
|
||||||
|
${lib.concatMapStringsSep "\n" (state: ''
|
||||||
|
${lib.concatMapStringsSep "\n" (folder: ''
|
||||||
|
backup ${folder} ${config.networking.hostName}/
|
||||||
|
'') state.folders}
|
||||||
|
'') states}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.clan.localbackup = {
|
||||||
|
targets = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = name;
|
||||||
|
description = "the name of the backup job";
|
||||||
|
};
|
||||||
|
directory = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "the directory to backup";
|
||||||
|
};
|
||||||
|
mountpoint = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.strMatching "^[a-zA-Z0-9./_-]+$");
|
||||||
|
default = null;
|
||||||
|
description = "mountpoint of the directory to backup. If set, the directory will be mounted before the backup and unmounted afterwards";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
description = "List of directories where backups are stored";
|
||||||
|
};
|
||||||
|
|
||||||
|
snapshots = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 20;
|
||||||
|
description = "Number of snapshots to keep";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
setupMount =
|
||||||
|
mountpoint:
|
||||||
|
lib.optionalString (mountpoint != null) ''
|
||||||
|
mkdir -p ${lib.escapeShellArg mountpoint}
|
||||||
|
if mountpoint -q ${lib.escapeShellArg mountpoint}; then
|
||||||
|
umount ${lib.escapeShellArg mountpoint}
|
||||||
|
fi
|
||||||
|
mount ${lib.escapeShellArg mountpoint}
|
||||||
|
trap "umount ${lib.escapeShellArg mountpoint}" EXIT
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
lib.mkIf (cfg.targets != [ ]) {
|
||||||
|
environment.systemPackages = [
|
||||||
|
(pkgs.writeShellScriptBin "localbackup-create" ''
|
||||||
|
set -efu -o pipefail
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath [
|
||||||
|
pkgs.rsnapshot
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.util-linux
|
||||||
|
]
|
||||||
|
}
|
||||||
|
${lib.concatMapStringsSep "\n" (target: ''
|
||||||
|
(
|
||||||
|
echo "Creating backup '${target.name}'"
|
||||||
|
${setupMount target.mountpoint}
|
||||||
|
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target.directory (lib.attrValues config.clanCore.state))}" sync
|
||||||
|
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target.directory (lib.attrValues config.clanCore.state))}" snapshot
|
||||||
|
)
|
||||||
|
'') (builtins.attrValues cfg.targets)}
|
||||||
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "localbackup-list" ''
|
||||||
|
set -efu -o pipefail
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath [
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.findutils
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.util-linux
|
||||||
|
]
|
||||||
|
}
|
||||||
|
(${
|
||||||
|
lib.concatMapStringsSep "\n" (target: ''
|
||||||
|
(
|
||||||
|
${setupMount target.mountpoint}
|
||||||
|
find ${lib.escapeShellArg target.directory} -mindepth 1 -maxdepth 1 -name "snapshot.*" -print0 -type d \
|
||||||
|
| jq -Rs 'split("\u0000") | .[] | select(. != "") | { "name": ("${target.mountpoint}::" + .)}'
|
||||||
|
)
|
||||||
|
'') (builtins.attrValues cfg.targets)
|
||||||
|
}) | jq -s .
|
||||||
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "localbackup-restore" ''
|
||||||
|
set -efu -o pipefail
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath [
|
||||||
|
pkgs.rsync
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.util-linux
|
||||||
|
pkgs.gawk
|
||||||
|
]
|
||||||
|
}
|
||||||
|
mountpoint=$(awk -F'::' '{print $1}' <<< $NAME)
|
||||||
|
backupname=''${NAME#$mountpoint::}
|
||||||
|
|
||||||
|
mkdir -p "$mountpoint"
|
||||||
|
if mountpoint -q "$mountpoint"; then
|
||||||
|
umount "$mountpoint"
|
||||||
|
fi
|
||||||
|
mount "$mountpoint"
|
||||||
|
trap "umount $mountpoint" EXIT
|
||||||
|
|
||||||
|
IFS=';' read -ra FOLDER <<< "$FOLDERS"
|
||||||
|
for folder in "''${FOLDER[@]}"; do
|
||||||
|
rsync -a "$backupname/${config.networking.hostName}$folder/" "$folder"
|
||||||
|
done
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
clanCore.backups.providers.localbackup = {
|
||||||
|
# TODO list needs to run locally or on the remote machine
|
||||||
|
list = "localbackup-list";
|
||||||
|
create = "localbackup-create";
|
||||||
|
restore = "localbackup-restore";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -18,25 +18,21 @@
|
|||||||
list = lib.mkOption {
|
list = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = ''
|
description = ''
|
||||||
script to list backups
|
Command to list backups.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
restore = lib.mkOption {
|
restore = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = ''
|
description = ''
|
||||||
script to restore a backup
|
Command to restore a backup.
|
||||||
should take an optional service name as argument
|
The name of the backup and the folders to restore will be
|
||||||
gets ARCHIVE_ID, LOCATION, JOB and FOLDERS as environment variables
|
set as environment variables NAME and FOLDERS respectively.
|
||||||
ARCHIVE_ID is the id of the backup
|
|
||||||
LOCATION is the remote identifier of the backup
|
|
||||||
JOB is the job name of the backup
|
|
||||||
FOLDERS is a colon separated list of folders to restore
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
create = lib.mkOption {
|
create = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = ''
|
description = ''
|
||||||
script to start a backup
|
Command to start a backup
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,18 +17,18 @@
|
|||||||
Folder where state resides in
|
Folder where state resides in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
preRestoreScript = lib.mkOption {
|
preRestoreCommand = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
default = ":";
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
script to run before restoring the state dir from a backup
|
script to run before restoring the state dir from a backup
|
||||||
|
|
||||||
Utilize this to stop services which currently access these folders
|
Utilize this to stop services which currently access these folders
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
postRestoreScript = lib.mkOption {
|
postRestoreCommand = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
default = ":";
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
script to restore the service after the state dir was restored from a backup
|
script to restore the service after the state dir was restored from a backup
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def create_backup(machine: Machine, provider: str | None = None) -> None:
|
|||||||
if provider is None:
|
if provider is None:
|
||||||
for provider in backup_scripts["providers"]:
|
for provider in backup_scripts["providers"]:
|
||||||
proc = machine.target_host.run(
|
proc = machine.target_host.run(
|
||||||
["bash", "-c", backup_scripts["providers"][provider]["create"]],
|
[backup_scripts["providers"][provider]["create"]],
|
||||||
)
|
)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise ClanError("failed to start backup")
|
raise ClanError("failed to start backup")
|
||||||
@@ -24,7 +24,7 @@ def create_backup(machine: Machine, provider: str | None = None) -> None:
|
|||||||
if provider not in backup_scripts["providers"]:
|
if provider not in backup_scripts["providers"]:
|
||||||
raise ClanError(f"provider {provider} not found")
|
raise ClanError(f"provider {provider} not found")
|
||||||
proc = machine.target_host.run(
|
proc = machine.target_host.run(
|
||||||
["bash", "-c", backup_scripts["providers"][provider]["create"]],
|
[backup_scripts["providers"][provider]["create"]],
|
||||||
)
|
)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise ClanError("failed to start backup")
|
raise ClanError("failed to start backup")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def list_provider(machine: Machine, provider: str) -> list[Backup]:
|
|||||||
results = []
|
results = []
|
||||||
backup_metadata = json.loads(machine.eval_nix("config.clanCore.backups"))
|
backup_metadata = json.loads(machine.eval_nix("config.clanCore.backups"))
|
||||||
proc = machine.target_host.run(
|
proc = machine.target_host.run(
|
||||||
["bash", "-c", backup_metadata["providers"][provider]["list"]],
|
[backup_metadata["providers"][provider]["list"]],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,42 +4,29 @@ import subprocess
|
|||||||
|
|
||||||
from ..errors import ClanError
|
from ..errors import ClanError
|
||||||
from ..machines.machines import Machine
|
from ..machines.machines import Machine
|
||||||
from .list import Backup, list_backups
|
|
||||||
|
|
||||||
|
|
||||||
def restore_service(
|
def restore_service(machine: Machine, name: str, provider: str, service: str) -> None:
|
||||||
machine: Machine, backup: Backup, provider: str, service: str
|
|
||||||
) -> None:
|
|
||||||
backup_metadata = json.loads(machine.eval_nix("config.clanCore.backups"))
|
backup_metadata = json.loads(machine.eval_nix("config.clanCore.backups"))
|
||||||
backup_folders = json.loads(machine.eval_nix("config.clanCore.state"))
|
backup_folders = json.loads(machine.eval_nix("config.clanCore.state"))
|
||||||
folders = backup_folders[service]["folders"]
|
folders = backup_folders[service]["folders"]
|
||||||
env = {}
|
env = {}
|
||||||
env["NAME"] = backup.name
|
env["NAME"] = name
|
||||||
env["FOLDERS"] = ":".join(folders)
|
env["FOLDERS"] = ":".join(folders)
|
||||||
|
|
||||||
if backup.job_name is not None:
|
if pre_restore := backup_folders[service]["preRestoreCommand"]:
|
||||||
env["JOB_NAME"] = backup.job_name
|
proc = machine.target_host.run(
|
||||||
|
[pre_restore],
|
||||||
proc = machine.target_host.run(
|
stdout=subprocess.PIPE,
|
||||||
[
|
extra_env=env,
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
backup_folders[service]["preRestoreScript"],
|
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
extra_env=env,
|
|
||||||
)
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise ClanError(
|
|
||||||
f"failed to run preRestoreScript: {backup_folders[service]['preRestoreScript']}, error was: {proc.stdout}"
|
|
||||||
)
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise ClanError(
|
||||||
|
f"failed to run preRestoreCommand: {pre_restore}, error was: {proc.stdout}"
|
||||||
|
)
|
||||||
|
|
||||||
proc = machine.target_host.run(
|
proc = machine.target_host.run(
|
||||||
[
|
[backup_metadata["providers"][provider]["restore"]],
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
backup_metadata["providers"][provider]["restore"],
|
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
extra_env=env,
|
extra_env=env,
|
||||||
)
|
)
|
||||||
@@ -48,52 +35,36 @@ def restore_service(
|
|||||||
f"failed to restore backup: {backup_metadata['providers'][provider]['restore']}"
|
f"failed to restore backup: {backup_metadata['providers'][provider]['restore']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
proc = machine.target_host.run(
|
if post_restore := backup_folders[service]["postRestoreCommand"]:
|
||||||
[
|
proc = machine.target_host.run(
|
||||||
"bash",
|
[post_restore],
|
||||||
"-c",
|
stdout=subprocess.PIPE,
|
||||||
backup_folders[service]["postRestoreScript"],
|
extra_env=env,
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
extra_env=env,
|
|
||||||
)
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise ClanError(
|
|
||||||
f"failed to run postRestoreScript: {backup_folders[service]['postRestoreScript']}, error was: {proc.stdout}"
|
|
||||||
)
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise ClanError(
|
||||||
|
f"failed to run postRestoreCommand: {post_restore}, error was: {proc.stdout}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def restore_backup(
|
def restore_backup(
|
||||||
machine: Machine,
|
machine: Machine,
|
||||||
backups: list[Backup],
|
|
||||||
provider: str,
|
provider: str,
|
||||||
name: str,
|
name: str,
|
||||||
service: str | None = None,
|
service: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if service is None:
|
if service is None:
|
||||||
for backup in backups:
|
backup_folders = json.loads(machine.eval_nix("config.clanCore.state"))
|
||||||
if backup.name == name:
|
for _service in backup_folders:
|
||||||
backup_folders = json.loads(machine.eval_nix("config.clanCore.state"))
|
restore_service(machine, name, provider, _service)
|
||||||
for _service in backup_folders:
|
|
||||||
restore_service(machine, backup, provider, _service)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ClanError(f"backup {name} not found")
|
|
||||||
else:
|
else:
|
||||||
for backup in backups:
|
restore_service(machine, name, provider, service)
|
||||||
if backup.name == name:
|
|
||||||
restore_service(machine, backup, provider, service)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ClanError(f"backup {name} not found")
|
|
||||||
|
|
||||||
|
|
||||||
def restore_command(args: argparse.Namespace) -> None:
|
def restore_command(args: argparse.Namespace) -> None:
|
||||||
machine = Machine(name=args.machine, flake=args.flake)
|
machine = Machine(name=args.machine, flake=args.flake)
|
||||||
backups = list_backups(machine=machine, provider=args.provider)
|
|
||||||
restore_backup(
|
restore_backup(
|
||||||
machine=machine,
|
machine=machine,
|
||||||
backups=backups,
|
|
||||||
provider=args.provider,
|
provider=args.provider,
|
||||||
name=args.name,
|
name=args.name,
|
||||||
service=args.service,
|
service=args.service,
|
||||||
|
|||||||
Reference in New Issue
Block a user