Jörg Thalheim
ef9ed1ebea
Some checks failed
buildbot/nix-build .#checks.aarch64-darwin.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-iso-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.check-for-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-age Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-e2fsprogs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-apk Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-fakeroot Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-openssh Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sshpass Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-tor Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-zbar Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-without-core Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-pytest Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-no-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-archlinux Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-bash Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-deb Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-rpm Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-git Build done.
buildbot/nix-build .#checks.x86_64-linux.renderClanOptions Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-nix Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-mypy" Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-qemu" Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-rsync Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sops Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-default Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-inventory-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.inventory-schema-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-example-valid Build done.
buildbot/nix-build .#checks.x86_64-linux.container Build done.
buildbot/nix-build .#checks.x86_64-linux.borgbackup Build done.
buildbot/nix-build .#checks.x86_64-linux.module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-moonlight-sunshine-accept Build done.
buildbot/nix-build .#checks.x86_64-linux.package-pending-reviews Build done.
buildbot/nix-build .#checks.x86_64-linux.package-function-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.postgresql Build done.
buildbot/nix-build .#checks.x86_64-linux.matrix-synapse Build done.
buildbot/nix-build .#checks.x86_64-linux.deltachat Build done.
buildbot/nix-build .#checks.x86_64-linux.package-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.treefmt Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-ts-api Build done.
buildbot/nix-build .#checks.x86_64-linux.package-default Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-nix-unit-tests Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.package-editor Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.package-inventory-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-merge-after-ci Build done.
buildbot/nix-build .#checks.x86_64-linux.package-impure-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-tea-create-pr Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotier-members Build done.
buildbot/nix-build .#checks.x86_64-linux.package-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotierone Build done.
buildbot/nix-build .#checks.x86_64-linux.secrets Build done.
buildbot/nix-build .#checks.x86_64-linux.template-minimal Build done.
buildbot/nix-build .#checks.x86_64-linux.package-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-with-core Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.wayland-proxy-virtwl Build done.
buildbot/nix-build .#checks.x86_64-linux.package-deploy-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.syncthing Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-install-test-ubuntu-22-04 Build done.
buildbot/nix-build .#checks.x86_64-linux.test-backups Build done.
buildbot/nix-build .#checks.x86_64-linux.test-installation Build done.
buildbot/nix-eval Build done.
checks / checks-impure (pull_request) Successful in 2m26s
buildbot/nix-build .#checks.x86_64-linux.flash Build done.
Otherwise we cannot execute them via ssh and also have nix store dependencies.
243 lines
8.7 KiB
Nix
243 lines
8.7 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.clan.localbackup;
|
|
uniqueFolders = lib.unique (
|
|
lib.flatten (lib.mapAttrsToList (_name: state: state.folders) config.clan.core.state)
|
|
);
|
|
rsnapshotConfig = target: ''
|
|
config_version 1.2
|
|
snapshot_root ${target.directory}
|
|
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
|
|
|
|
${lib.optionalString (target.postBackupHook != null) ''
|
|
cmd_postexec ${pkgs.writeShellScript "postexec.sh" ''
|
|
set -efu -o pipefail
|
|
${target.postBackupHook}
|
|
''}
|
|
''}
|
|
retain snapshot ${builtins.toString config.clan.localbackup.snapshots}
|
|
${lib.concatMapStringsSep "\n" (folder: ''
|
|
backup ${folder} ${config.networking.hostName}/
|
|
'') uniqueFolders}
|
|
'';
|
|
in
|
|
{
|
|
options.clan.localbackup = {
|
|
targets = lib.mkOption {
|
|
type = lib.types.attrsOf (
|
|
lib.types.submodule (
|
|
{ name, ... }:
|
|
{
|
|
options = {
|
|
name = lib.mkOption {
|
|
type = lib.types.strMatching "^[a-zA-Z0-9._-]+$";
|
|
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.str;
|
|
default = null;
|
|
description = "mountpoint of the directory to backup. If set, the directory will be mounted before the backup and unmounted afterwards";
|
|
};
|
|
preMountHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run before the directory is mounted";
|
|
};
|
|
postMountHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run after the directory is mounted";
|
|
};
|
|
preUnmountHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run before the directory is unmounted";
|
|
};
|
|
postUnmountHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run after the directory is unmounted";
|
|
};
|
|
preBackupHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run before the backup";
|
|
};
|
|
postBackupHook = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.lines;
|
|
default = null;
|
|
description = "Shell commands to run after the backup";
|
|
};
|
|
};
|
|
}
|
|
)
|
|
);
|
|
default = { };
|
|
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
|
|
mountHook = target: ''
|
|
if [[ -x /run/current-system/sw/bin/localbackup-mount-${target.name} ]]; then
|
|
/run/current-system/sw/bin/localbackup-mount-${target.name}
|
|
fi
|
|
if [[ -x /run/current-system/sw/bin/localbackup-unmount-${target.name} ]]; then
|
|
trap "/run/current-system/sw/bin/localbackup-unmount-${target.name}" EXIT
|
|
fi
|
|
'';
|
|
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: ''
|
|
${mountHook target}
|
|
echo "Creating backup '${target.name}'"
|
|
|
|
${lib.optionalString (target.preBackupHook != null) ''
|
|
(
|
|
${target.preBackupHook}
|
|
)
|
|
''}
|
|
|
|
declare -A preCommandErrors
|
|
${lib.concatMapStringsSep "\n" (
|
|
state:
|
|
lib.optionalString (state.preBackupCommand != null) ''
|
|
echo "Running pre-backup command for ${state.name}"
|
|
if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
|
|
preCommandErrors["${state.name}"]=1
|
|
fi
|
|
''
|
|
) (builtins.attrValues config.clan.core.state)}
|
|
|
|
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" sync
|
|
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" 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: ''
|
|
(
|
|
${mountHook target}
|
|
find ${lib.escapeShellArg target.directory} -mindepth 1 -maxdepth 1 -name "snapshot.*" -print0 -type d \
|
|
| jq -Rs 'split("\u0000") | .[] | select(. != "") | { "name": ("${target.name}::" + .)}'
|
|
)
|
|
'') (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
|
|
]
|
|
}
|
|
if [[ "''${NAME:-}" == "" ]]; then
|
|
echo "No backup name given via NAME environment variable"
|
|
exit 1
|
|
fi
|
|
if [[ "''${FOLDERS:-}" == "" ]]; then
|
|
echo "No folders given via FOLDERS environment variable"
|
|
exit 1
|
|
fi
|
|
name=$(awk -F'::' '{print $1}' <<< $NAME)
|
|
backupname=''${NAME#$name::}
|
|
|
|
if command -v localbackup-mount-$name; then
|
|
localbackup-mount-$name
|
|
fi
|
|
if command -v localbackup-unmount-$name; then
|
|
trap "localbackup-unmount-$name" EXIT
|
|
fi
|
|
|
|
if [[ ! -d $backupname ]]; then
|
|
echo "No backup found $backupname"
|
|
exit 1
|
|
fi
|
|
|
|
IFS=':' read -ra FOLDER <<< "''$FOLDERS"
|
|
for folder in "''${FOLDER[@]}"; do
|
|
mkdir -p "$folder"
|
|
rsync -a "$backupname/${config.networking.hostName}$folder/" "$folder"
|
|
done
|
|
'')
|
|
]
|
|
++ (lib.mapAttrsToList (
|
|
name: target:
|
|
pkgs.writeShellScriptBin ("localbackup-mount-" + name) ''
|
|
set -efu -o pipefail
|
|
${lib.optionalString (target.preMountHook != null) target.preMountHook}
|
|
${lib.optionalString (target.mountpoint != null) ''
|
|
if ! ${pkgs.util-linux}/bin/mountpoint -q ${lib.escapeShellArg target.mountpoint}; then
|
|
${pkgs.util-linux}/bin/mount -o X-mount.mkdir ${lib.escapeShellArg target.mountpoint}
|
|
fi
|
|
''}
|
|
${lib.optionalString (target.postMountHook != null) target.postMountHook}
|
|
''
|
|
) cfg.targets)
|
|
++ lib.mapAttrsToList (
|
|
name: target:
|
|
pkgs.writeShellScriptBin ("localbackup-unmount-" + name) ''
|
|
set -efu -o pipefail
|
|
${lib.optionalString (target.preUnmountHook != null) target.preUnmountHook}
|
|
${lib.optionalString (
|
|
target.mountpoint != null
|
|
) "${pkgs.util-linux}/bin/umount ${lib.escapeShellArg target.mountpoint}"}
|
|
${lib.optionalString (target.postUnmountHook != null) target.postUnmountHook}
|
|
''
|
|
) cfg.targets;
|
|
|
|
clan.core.backups.providers.localbackup = {
|
|
# TODO list needs to run locally or on the remote machine
|
|
list = "localbackup-list";
|
|
create = "localbackup-create";
|
|
restore = "localbackup-restore";
|
|
};
|
|
};
|
|
}
|