forked from clan/clan-core
Inventory: implement borgbackup
This commit is contained in:
parent
39ec23bd31
commit
d934b67c72
@ -2,57 +2,36 @@
|
|||||||
let
|
let
|
||||||
clanDir = config.clan.core.clanDir;
|
clanDir = config.clan.core.clanDir;
|
||||||
machineDir = clanDir + "/machines/";
|
machineDir = clanDir + "/machines/";
|
||||||
|
|
||||||
|
cfg = config.clan.borgbackup-static;
|
||||||
|
|
||||||
|
machine_name = config.clan.core.machineName;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ../borgbackup ];
|
imports = [ ../borgbackup ];
|
||||||
|
|
||||||
options.clan.borgbackup-static = {
|
# Inventory / Interface.nix
|
||||||
excludeMachines = lib.mkOption {
|
# options.clan.inventory.borgbackup-static.description.
|
||||||
type = lib.types.listOf lib.types.str;
|
options.clan.borgbackup-static.roles = lib.mkOption {
|
||||||
example = [ config.clan.core.machineName ];
|
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should not be backuped.
|
|
||||||
Mutually exclusive with includeMachines.
|
|
||||||
If this is not empty, every other machine except the targets in the clan will be backuped by this module.
|
|
||||||
If includeMachines is set, only the included machines will be backuped.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
includeMachines = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
example = [ config.clan.core.machineName ];
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should be backuped.
|
|
||||||
Mutually exclusive with excludeMachines.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
targets = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should act as target machines for backups.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config.services.borgbackup.repos =
|
config.services.borgbackup.repos =
|
||||||
let
|
let
|
||||||
machines = builtins.readDir machineDir;
|
|
||||||
|
filteredMachines = builtins.attrNames (lib.filterAttrs (_: v: builtins.elem "client" v) cfg.roles);
|
||||||
|
|
||||||
borgbackupIpMachinePath = machines: machineDir + machines + "/facts/borgbackup.ssh.pub";
|
borgbackupIpMachinePath = machines: machineDir + machines + "/facts/borgbackup.ssh.pub";
|
||||||
filteredMachines =
|
machinesMaybeKey = builtins.map (
|
||||||
if ((builtins.length config.clan.borgbackup-static.includeMachines) != 0) then
|
machine:
|
||||||
lib.filterAttrs (name: _: (lib.elem name config.clan.borgbackup-static.includeMachines)) machines
|
|
||||||
else
|
|
||||||
lib.filterAttrs (name: _: !(lib.elem name config.clan.borgbackup-static.excludeMachines)) machines;
|
|
||||||
machinesMaybeKey = lib.mapAttrsToList (
|
|
||||||
machine: _:
|
|
||||||
let
|
let
|
||||||
fullPath = borgbackupIpMachinePath machine;
|
fullPath = borgbackupIpMachinePath machine;
|
||||||
in
|
in
|
||||||
if builtins.pathExists fullPath then machine else null
|
if builtins.pathExists fullPath then machine else null
|
||||||
) filteredMachines;
|
) filteredMachines;
|
||||||
|
|
||||||
machinesWithKey = lib.filter (x: x != null) machinesMaybeKey;
|
machinesWithKey = lib.filter (x: x != null) machinesMaybeKey;
|
||||||
|
|
||||||
hosts = builtins.map (machine: {
|
hosts = builtins.map (machine: {
|
||||||
name = machine;
|
name = machine;
|
||||||
value = {
|
value = {
|
||||||
@ -61,41 +40,20 @@ in
|
|||||||
};
|
};
|
||||||
}) machinesWithKey;
|
}) machinesWithKey;
|
||||||
in
|
in
|
||||||
lib.mkIf
|
lib.mkIf (builtins.elem "server" cfg.roles.${machine_name}) (
|
||||||
(builtins.any (
|
if (builtins.listToAttrs hosts) != null then builtins.listToAttrs hosts else { }
|
||||||
target: target == config.clan.core.machineName
|
);
|
||||||
) config.clan.borgbackup-static.targets)
|
|
||||||
(if (builtins.listToAttrs hosts) != null then builtins.listToAttrs hosts else { });
|
|
||||||
|
|
||||||
config.clan.borgbackup.destinations =
|
config.clan.borgbackup.destinations =
|
||||||
let
|
let
|
||||||
destinations = builtins.map (d: {
|
servers = builtins.attrNames (lib.filterAttrs (_n: v: (builtins.elem "server" v)) cfg.roles);
|
||||||
name = d;
|
|
||||||
value = {
|
|
||||||
repo = "borg@${d}:/var/lib/borgbackup/${config.clan.core.machineName}";
|
|
||||||
};
|
|
||||||
}) config.clan.borgbackup-static.targets;
|
|
||||||
in
|
|
||||||
lib.mkIf (builtins.any (
|
|
||||||
target: target == config.clan.core.machineName
|
|
||||||
) config.clan.borgbackup-static.includeMachines) (builtins.listToAttrs destinations);
|
|
||||||
|
|
||||||
config.assertions = [
|
destinations = builtins.map (server_name: {
|
||||||
{
|
name = server_name;
|
||||||
assertion =
|
value = {
|
||||||
!(
|
repo = "borg@${server_name}:/var/lib/borgbackup/${machine_name}";
|
||||||
((builtins.length config.clan.borgbackup-static.excludeMachines) != 0)
|
};
|
||||||
&& ((builtins.length config.clan.borgbackup-static.includeMachines) != 0)
|
}) servers;
|
||||||
);
|
in
|
||||||
message = ''
|
lib.mkIf (builtins.elem "client" cfg.roles.${machine_name}) (builtins.listToAttrs destinations);
|
||||||
The options:
|
|
||||||
config.clan.borgbackup-static.excludeMachines = [${builtins.toString config.clan.borgbackup-static.excludeMachines}]
|
|
||||||
and
|
|
||||||
config.clan.borgbackup-static.includeMachines = [${builtins.toString config.clan.borgbackup-static.includeMachines}]
|
|
||||||
are mutually exclusive.
|
|
||||||
Use excludeMachines to exclude certain machines and backup the other clan machines.
|
|
||||||
Use include machines to only backup certain machines.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
89
inventory/default.nix
Normal file
89
inventory/default.nix
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{ inputs, self, ... }:
|
||||||
|
let
|
||||||
|
clan-core = self;
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
# syncthing_inventory = builtins.fromJSON (builtins.readFile ./src/tests/syncthing.json);
|
||||||
|
syncthing_inventory = builtins.fromJSON (builtins.readFile ./src/tests/borgbackup.json);
|
||||||
|
|
||||||
|
machines = machinesFromInventory {
|
||||||
|
inherit clan-core;
|
||||||
|
lib = pkgs.lib;
|
||||||
|
} syncthing_inventory;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns a NixOS configuration for every machine in the inventory.
|
||||||
|
|
||||||
|
machinesFromInventory :: Inventory -> { ${machine_name} :: NixOSConfiguration }
|
||||||
|
*/
|
||||||
|
machinesFromInventory =
|
||||||
|
{ lib, clan-core, ... }:
|
||||||
|
inventory:
|
||||||
|
# For every machine in the inventory, build a NixOS configuration
|
||||||
|
# For each machine generate config, forEach service, if the machine is used.
|
||||||
|
builtins.mapAttrs (
|
||||||
|
machine_name: _:
|
||||||
|
builtins.foldl' (
|
||||||
|
acc: service_name:
|
||||||
|
let
|
||||||
|
service_config = inventory.services.${service_name};
|
||||||
|
isInService = builtins.elem machine_name (builtins.attrNames service_config.machineConfig);
|
||||||
|
|
||||||
|
machine_service_config = (service_config.machineConfig.${machine_name} or { }).config or { };
|
||||||
|
global_config = inventory.services.${service_name}.config;
|
||||||
|
module_name = inventory.services.${service_name}.module;
|
||||||
|
in
|
||||||
|
# Possible roles: "server", "client", "peer"
|
||||||
|
if
|
||||||
|
builtins.trace ''
|
||||||
|
isInService ${builtins.toJSON isInService},
|
||||||
|
${builtins.toJSON machine_name} ${builtins.toJSON (builtins.attrNames service_config.machineConfig)}
|
||||||
|
'' isInService
|
||||||
|
then
|
||||||
|
acc
|
||||||
|
++ [
|
||||||
|
{
|
||||||
|
imports = [ clan-core.clanModules.${module_name} ];
|
||||||
|
config.clan.${module_name} = lib.mkMerge [
|
||||||
|
global_config
|
||||||
|
machine_service_config
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
config.clan.${module_name} = {
|
||||||
|
# TODO: filter, show only the roles that are needed by the machine
|
||||||
|
roles = builtins.mapAttrs (_m: c: c.roles) service_config.machineConfig;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
else
|
||||||
|
acc
|
||||||
|
) [ ] (builtins.attrNames inventory.services)
|
||||||
|
) inventory.machines;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
clan = clan-core.lib.buildClan {
|
||||||
|
meta.name = "vis clans";
|
||||||
|
# Should usually point to the directory of flake.nix
|
||||||
|
directory = self;
|
||||||
|
|
||||||
|
machines = {
|
||||||
|
"vi_machine" = {
|
||||||
|
imports = machines.vi_machine;
|
||||||
|
};
|
||||||
|
"vyr_machine" = {
|
||||||
|
imports = machines.vyr_machine;
|
||||||
|
};
|
||||||
|
"camina_machine" = {
|
||||||
|
imports = machines.camina_machine;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
intern = machines;
|
||||||
|
# inherit (clan) nixosConfigurations clanInternals;
|
||||||
|
# add the Clan cli tool to the dev shell
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = [ clan-core.packages.${system}.clan-cli ];
|
||||||
|
};
|
||||||
|
}
|
@ -1,137 +0,0 @@
|
|||||||
{
|
|
||||||
description = "<Put your description here>";
|
|
||||||
|
|
||||||
inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
|
|
||||||
|
|
||||||
outputs =
|
|
||||||
{ clan-core, ... }:
|
|
||||||
let
|
|
||||||
pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system};
|
|
||||||
system = "x86_64-linux";
|
|
||||||
in
|
|
||||||
# Usage see: https://docs.clan.lol
|
|
||||||
# nice_flake_interface -> buildInventory() -> Inventory -> buildClanFromInventory() -> nixosConfigurations
|
|
||||||
# buildClanFromInventory = inventory: evalModules {
|
|
||||||
# extraAttrs = { inherit inventory; };
|
|
||||||
# # (attrNames inventory.machines)
|
|
||||||
# };
|
|
||||||
# clan =
|
|
||||||
# clan-core.lib.buildClanFromInventory [
|
|
||||||
# # Inventory 0 (loads the json file managed by the Python API)
|
|
||||||
# (builtins.fromJSON (builtins.readFile ./inventory.json))
|
|
||||||
# # ->
|
|
||||||
# # {
|
|
||||||
# # services."backups_1".autoIncludeMachines = true;
|
|
||||||
# # services."backups_1".module = "borgbackup";
|
|
||||||
# # ... etc.
|
|
||||||
# # }
|
|
||||||
# ]
|
|
||||||
# ++ (buildInventory {
|
|
||||||
# clanName = "nice_flake_interface";
|
|
||||||
# description = "A nice flake interface";
|
|
||||||
# icon = "assets/icon.png";
|
|
||||||
# machines = {
|
|
||||||
# jon = {
|
|
||||||
# # Just regular nixos/clan configuration ?
|
|
||||||
# # config = {
|
|
||||||
# # imports = [
|
|
||||||
# # ./modules/shared.nix
|
|
||||||
# # ./machines/jon/configuration.nix
|
|
||||||
# # ];
|
|
||||||
# # nixpkgs.hostPlatform = system;
|
|
||||||
# # # Set this for clan commands use ssh i.e. `clan machines update`
|
|
||||||
# # # If you change the hostname, you need to update this line to root@<new-hostname>
|
|
||||||
# # # This only works however if you have avahi running on your admin machine else use IP
|
|
||||||
# # clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
|
|
||||||
# # # ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
|
||||||
# # disko.devices.disk.main = {
|
|
||||||
# # device = "/dev/disk/by-id/__CHANGE_ME__";
|
|
||||||
# # };
|
|
||||||
# # # IMPORTANT! Add your SSH key here
|
|
||||||
# # # e.g. > cat ~/.ssh/id_ed25519.pub
|
|
||||||
# # users.users.root.openssh.authorizedKeys.keys = throw ''
|
|
||||||
# # Don't forget to add your SSH key here!
|
|
||||||
# # users.users.root.openssh.authorizedKeys.keys = [ "<YOUR SSH_KEY>" ]
|
|
||||||
# # '';
|
|
||||||
# # # Zerotier needs one controller to accept new nodes. Once accepted
|
|
||||||
# # # the controller can be offline and routing still works.
|
|
||||||
# # clan.networking.zerotier.controller.enable = true;
|
|
||||||
# # };
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# })
|
|
||||||
# ++ [
|
|
||||||
# # Low level inventory overrides (comes at the end)
|
|
||||||
# {
|
|
||||||
# services."backups_2".autoIncludeMachines = true;
|
|
||||||
# services."backups_2".module = "borgbackup";
|
|
||||||
# }
|
|
||||||
# ];
|
|
||||||
# # buildClan :: [ Partial<Inventory> ] -> Inventory
|
|
||||||
# # foldl' (acc: v: lib.recursiveUpdate acc v) {} []
|
|
||||||
# inventory = [
|
|
||||||
# # import json
|
|
||||||
# {...}
|
|
||||||
# # power user flake
|
|
||||||
# {...}
|
|
||||||
# ]
|
|
||||||
# # With Module system
|
|
||||||
# # Pros: Easy to understand,
|
|
||||||
# # Cons: Verbose, hard to maintain
|
|
||||||
# # buildClan :: { modules = [ { config = Partial<Inventory>; options :: InventoryOptions; } } ]; } -> Inventory
|
|
||||||
# eval = lib.evalModules {
|
|
||||||
# modules = [
|
|
||||||
# {
|
|
||||||
# # Inventory Schema
|
|
||||||
# # Python validation
|
|
||||||
# options = {...}
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# config = map lib.mkDefault
|
|
||||||
# (builtins.fromJSON (builtins.readFile ./inventory.json))
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# # User provided
|
|
||||||
# config = {...}
|
|
||||||
# }
|
|
||||||
# # Later overrides.
|
|
||||||
# {
|
|
||||||
# lib.mkForce ...
|
|
||||||
# }
|
|
||||||
# ];
|
|
||||||
# }
|
|
||||||
# nixosConfigurations = lib.evalModules inventory;
|
|
||||||
# eval.config.inventory
|
|
||||||
# #
|
|
||||||
# eval.config.machines.jon#nixosConfig
|
|
||||||
# eval.config.machines.sara#nixosConfig
|
|
||||||
#
|
|
||||||
# {inventory, config, ...}:{
|
|
||||||
# hostname = config.machines.sara # Invalid
|
|
||||||
# hostname = inventory.machines.sara.hostname # Valid
|
|
||||||
# }
|
|
||||||
/*
|
|
||||||
# Type
|
|
||||||
|
|
||||||
buildInventory :: {
|
|
||||||
clanName :: string
|
|
||||||
machines :: {
|
|
||||||
${name} :: {
|
|
||||||
config :: {
|
|
||||||
# NixOS configuration
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# ... More mapped inventory options
|
|
||||||
# i.e. shared config for all machines
|
|
||||||
} -> Inventory
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
# all machines managed by Clan
|
|
||||||
inherit (clan) nixosConfigurations clanInternals;
|
|
||||||
# add the Clan cli tool to the dev shell
|
|
||||||
devShells.${system}.default = pkgs.mkShell {
|
|
||||||
packages = [ clan-core.packages.${system}.clan-cli ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
{ ... }:
|
{ inputs, self, ... }:
|
||||||
{
|
{
|
||||||
|
flake.inventory = import ./default.nix { inherit inputs self; };
|
||||||
perSystem =
|
perSystem =
|
||||||
{ pkgs, config, ... }:
|
{ pkgs, config, ... }:
|
||||||
{
|
{
|
||||||
@ -13,6 +14,7 @@
|
|||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.inventory-schema = pkgs.mkShell { inputsFrom = [ config.packages.inventory-schema ]; };
|
devShells.inventory-schema = pkgs.mkShell { inputsFrom = [ config.packages.inventory-schema ]; };
|
||||||
|
|
||||||
checks.inventory-schema-checks = pkgs.stdenv.mkDerivation {
|
checks.inventory-schema-checks = pkgs.stdenv.mkDerivation {
|
||||||
|
@ -18,21 +18,19 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "My backup"
|
"name": "My backup"
|
||||||
},
|
},
|
||||||
"module": "borbackup-static",
|
"module": "borgbackup-static",
|
||||||
"machineConfig": {
|
"machineConfig": {
|
||||||
"vyr": {
|
"vyr_machine": {
|
||||||
"roles": ["server"]
|
"roles": ["server"]
|
||||||
},
|
},
|
||||||
"vi": {
|
"vi_machine": {
|
||||||
"roles": ["client"]
|
"roles": ["client"]
|
||||||
},
|
},
|
||||||
"camina_machine": {
|
"camina_machine": {
|
||||||
"roles": ["client"]
|
"roles": ["client"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {}
|
||||||
"folders": ["/home", "/root", "/var", "/etc"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
"camina_machine": {
|
"camina_machine": {
|
||||||
"name": "camina"
|
"name": "camina"
|
||||||
},
|
},
|
||||||
"vyr": {
|
"vyr_machine": {
|
||||||
"name": "vyr"
|
"name": "vyr"
|
||||||
},
|
},
|
||||||
"vi": {
|
"vi_machine": {
|
||||||
"name": "vi"
|
"name": "vi"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -20,23 +20,23 @@
|
|||||||
},
|
},
|
||||||
"module": "syncthing-static-peers",
|
"module": "syncthing-static-peers",
|
||||||
"machineConfig": {
|
"machineConfig": {
|
||||||
"vyr": {},
|
"vyr_machine": {},
|
||||||
"vi": {},
|
"vi_machine": {},
|
||||||
"camina_machine": {}
|
"camina_machine": {}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"folders": {
|
"folders": {
|
||||||
"test": {
|
"test": {
|
||||||
"path": "~/data/docs",
|
"path": "~/data/docs",
|
||||||
"devices": ["camina", "vyr", "vi"]
|
"devices": ["camina_machine", "vyr_machine", "vi_machine"]
|
||||||
},
|
},
|
||||||
"videos": {
|
"videos": {
|
||||||
"path": "~/data/videos",
|
"path": "~/data/videos",
|
||||||
"devices": ["camina", "vyr", "ezra"]
|
"devices": ["camina_machine", "vyr_machine"]
|
||||||
},
|
},
|
||||||
"playlist": {
|
"playlist": {
|
||||||
"path": "~/data/playlist",
|
"path": "~/data/playlist",
|
||||||
"devices": ["camina", "vyr", "ezra"]
|
"devices": ["camina_machine", "vi_machine"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user