1
0
forked from clan/clan-core

Merge pull request 'backups: implement list the easy way' (#614) from lassulus-backups2 into main

This commit is contained in:
clan-bot 2023-12-07 12:32:47 +00:00
commit 7f68da2715
4 changed files with 58 additions and 62 deletions

View File

@ -1,4 +1,4 @@
{ config, lib, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.clan.borgbackup; cfg = config.clan.borgbackup;
in in
@ -19,7 +19,7 @@ in
}; };
rsh = lib.mkOption { rsh = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = ""; default = "ssh -i ${config.clanCore.secrets.borgbackup.secrets."borgbackup.ssh".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
description = "the rsh to use for the backup"; description = "the rsh to use for the backup";
}; };
@ -55,15 +55,23 @@ in
}) })
cfg.destinations; cfg.destinations;
clanCore.secrets.borgbackup = {
facts."borgbackup.ssh.pub" = { };
secrets."borgbackup.ssh" = { };
generator.path = [ pkgs.openssh pkgs.coreutils ];
generator.script = ''
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
'';
};
clanCore.backups.providers.borgbackup = { clanCore.backups.providers.borgbackup = {
list = '' list = ''
${lib.concatMapStringsSep "\n" (dest: '' ssh ${config.clan.networking.deploymentAddress} -- '
( ${lib.concatMapStringsSep "\n" (dest: ''
export BORG_REPO=${lib.escapeShellArg dest.repo} borg-job-${dest.name} list --json
export BORG_RSH=${lib.escapeShellArg dest.rsh} '') (lib.attrValues cfg.destinations)}
${lib.getExe config.services.borgbackup.package} list '
)
'') (lib.attrValues cfg.destinations)}
''; '';
start = '' start = ''
ssh ${config.clan.networking.deploymentAddress} -- ' ssh ${config.clan.networking.deploymentAddress} -- '

View File

@ -54,7 +54,7 @@
}; };
}; };
})); }));
default = [ ]; default = { };
description = '' description = ''
Configured backup providers which are used by this machine Configured backup providers which are used by this machine
''; '';

View File

@ -1,57 +1,47 @@
import argparse import argparse
import pprint import json
from pathlib import Path import subprocess
from typing import Any
from ..errors import ClanError from ..errors import ClanError
from ..machines.machines import Machine
def list_backups( def list_backups(machine: Machine, provider: str | None = None) -> list[dict[str, Any]]:
flake_dir: Path, machine: str, provider: str | None = None backup_scripts = json.loads(
) -> dict[str, dict[str, list[dict[str, str]]]]: machine.eval_nix(f"nixosConfigurations.{machine.name}.config.clanCore.backups")
dummy_data = { )
"testhostname": { results = []
"borgbackup": [ if provider is None:
{"date": "2021-01-01T00:00:00Z", "id": "1"}, for provider in backup_scripts["providers"]:
{"date": "2022-01-01T00:00:00Z", "id": "2"}, proc = subprocess.run(
{"date": "2023-01-01T00:00:00Z", "id": "3"}, ["bash", "-c", backup_scripts["providers"][provider]["list"]],
], stdout=subprocess.PIPE,
"restic": [ )
{"date": "2021-01-01T00:00:00Z", "id": "1"}, if proc.returncode != 0:
{"date": "2022-01-01T00:00:00Z", "id": "2"}, # TODO this should be a warning, only raise exception if no providers succeed
{"date": "2023-01-01T00:00:00Z", "id": "3"}, raise ClanError("failed to list backups")
], else:
}, results.append(proc.stdout)
"another host": {
"borgbackup": [
{"date": "2021-01-01T00:00:00Z", "id": "1"},
{"date": "2022-01-01T00:00:00Z", "id": "2"},
{"date": "2023-01-01T00:00:00Z", "id": "3"},
],
},
}
if provider is not None:
new_data = {}
for machine_ in dummy_data:
if provider in dummy_data[machine_]:
new_data[machine_] = {provider: dummy_data[machine_][provider]}
dummy_data = new_data
if machine is None:
return dummy_data
else: else:
return {machine: dummy_data[machine]} if provider not in backup_scripts["providers"]:
raise ClanError(f"provider {provider} not found")
proc = subprocess.run(
["bash", "-c", backup_scripts["providers"][provider]["list"]],
stdout=subprocess.PIPE,
)
if proc.returncode != 0:
raise ClanError("failed to list backup")
else:
results.append(proc.stdout)
return list(map(json.loads, results))
def list_command(args: argparse.Namespace) -> None: def list_command(args: argparse.Namespace) -> None:
if args.flake is None: machine = Machine(name=args.machine, flake_dir=args.flake)
raise ClanError("Could not find clan flake toplevel directory") backups_data = list_backups(machine=machine, provider=args.provider)
backups = list_backups( print(list(backups_data))
Path(args.flake), machine=args.machine, provider=args.provider
)
if len(backups) > 0:
pp = pprint.PrettyPrinter(depth=4)
pp.pprint(backups)
def register_list_parser(parser: argparse.ArgumentParser) -> None: def register_list_parser(parser: argparse.ArgumentParser) -> None:

View File

@ -1,22 +1,20 @@
import logging import pytest
from cli import Cli from cli import Cli
from fixtures_flakes import FlakeForTest from fixtures_flakes import FlakeForTest
log = logging.getLogger(__name__)
@pytest.mark.impure
def test_backups( def test_backups(
test_flake: FlakeForTest, test_flake_with_core: FlakeForTest,
) -> None: ) -> None:
cli = Cli() cli = Cli()
cli.run( cli.run(
[ [
"--flake", "--flake",
str(test_flake.path), str(test_flake_with_core.path),
"backups", "backups",
"list", "list",
"testhostname", "vm1",
] ]
) )