Merge pull request 'Inventory: improve role imports' (#2189) from hsjobeki/clan-core:hsjobeki-main into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/2189
This commit is contained in:
@@ -1,20 +1,6 @@
|
|||||||
{ lib, config, ... }:
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
|
# Dont author new modules with this file.
|
||||||
{
|
{
|
||||||
options.clan.admin = {
|
imports = [ ./roles/default.nix ];
|
||||||
allowedKeys = lib.mkOption {
|
|
||||||
default = { };
|
|
||||||
type = lib.types.attrsOf lib.types.str;
|
|
||||||
description = "The allowed public keys for ssh access to the admin user";
|
|
||||||
example = {
|
|
||||||
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
imports = [
|
|
||||||
../sshd
|
|
||||||
../root-password
|
|
||||||
];
|
|
||||||
config = {
|
|
||||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues config.clan.admin.allowedKeys;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
22
clanModules/admin/roles/default.nix
Normal file
22
clanModules/admin/roles/default.nix
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
{
|
||||||
|
options.clan.admin = {
|
||||||
|
allowedKeys = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
description = "The allowed public keys for ssh access to the admin user";
|
||||||
|
example = {
|
||||||
|
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Bad practice.
|
||||||
|
# Should we add 'clanModules' to specialArgs?
|
||||||
|
imports = [
|
||||||
|
../../sshd
|
||||||
|
../../root-password
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues config.clan.admin.allowedKeys;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,215 +1,6 @@
|
|||||||
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
|
# Dont author new modules with this file.
|
||||||
{
|
{
|
||||||
config,
|
imports = [ ./roles/client.nix ];
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.clan.borgbackup;
|
|
||||||
preBackupScript = ''
|
|
||||||
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
|
|
||||||
''
|
|
||||||
) (lib.attrValues config.clan.core.state)}
|
|
||||||
|
|
||||||
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
|
||||||
echo "pre-backup commands failed for the following services:"
|
|
||||||
for state in "''${!preCommandErrors[@]}"; do
|
|
||||||
echo " $state"
|
|
||||||
done
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
# Each .nix file in the roles directory is a role
|
|
||||||
# TODO: Helper function to set available roles within module meta.
|
|
||||||
# roles =
|
|
||||||
# if builtins.pathExists ./roles then
|
|
||||||
# lib.pipe ./roles [
|
|
||||||
# builtins.readDir
|
|
||||||
# (lib.filterAttrs (_n: v: v == "regular"))
|
|
||||||
# lib.attrNames
|
|
||||||
# (map (fileName: lib.removeSuffix ".nix" fileName))
|
|
||||||
# ]
|
|
||||||
# else
|
|
||||||
# null;
|
|
||||||
# TODO: make this an interface of every module
|
|
||||||
# Maybe load from readme.md
|
|
||||||
# metaInfoOption = lib.mkOption {
|
|
||||||
# readOnly = true;
|
|
||||||
# description = ''
|
|
||||||
# Meta is used to retrieve information about this module.
|
|
||||||
# - `availableRoles` is a list of roles that can be assigned via the inventory.
|
|
||||||
# - `category` is used to group services in the clan marketplace.
|
|
||||||
# - `description` is a short description of the service for the clan marketplace.
|
|
||||||
# '';
|
|
||||||
# default = {
|
|
||||||
# description = "Borgbackup is a backup program. Optionally, it supports compression and authenticated encryption.";
|
|
||||||
# availableRoles = roles;
|
|
||||||
# category = "backup";
|
|
||||||
# };
|
|
||||||
# type = lib.types.submodule {
|
|
||||||
# options = {
|
|
||||||
# description = lib.mkOption { type = lib.types.str; };
|
|
||||||
# availableRoles = lib.mkOption { type = lib.types.nullOr (lib.types.listOf lib.types.str); };
|
|
||||||
# category = lib.mkOption {
|
|
||||||
# description = "A category for the service. This is used to group services in the clan ui";
|
|
||||||
# type = lib.types.enum [
|
|
||||||
# "backup"
|
|
||||||
# "network"
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
{
|
|
||||||
|
|
||||||
# options.clan.borgbackup.meta = metaInfoOption;
|
|
||||||
|
|
||||||
options.clan.borgbackup.destinations = 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";
|
|
||||||
};
|
|
||||||
repo = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "the borgbackup repository to backup to";
|
|
||||||
};
|
|
||||||
rsh = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "ssh -i ${
|
|
||||||
config.clan.core.facts.services.borgbackup.secret."borgbackup.ssh".path
|
|
||||||
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes";
|
|
||||||
defaultText = "ssh -i \${config.clan.core.facts.services.borgbackup.secret.\"borgbackup.ssh\".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
|
|
||||||
description = "the rsh to use for the backup";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
default = { };
|
|
||||||
description = ''
|
|
||||||
destinations where the machine should be backuped to
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
options.clan.borgbackup.exclude = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
example = [ "*.pyc" ];
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Directories/Files to exclude from the backup.
|
|
||||||
Use * as a wildcard.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
(lib.mkRemovedOptionModule [
|
|
||||||
"clan"
|
|
||||||
"borgbackup"
|
|
||||||
"enable"
|
|
||||||
] "Just define clan.borgbackup.destinations to enable it")
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkIf (cfg.destinations != { }) {
|
|
||||||
systemd.services = lib.mapAttrs' (
|
|
||||||
_: dest:
|
|
||||||
lib.nameValuePair "borgbackup-job-${dest.name}" {
|
|
||||||
# since borgbackup mounts the system read-only, we need to run in a ExecStartPre script, so we can generate additional files.
|
|
||||||
serviceConfig.ExecStartPre = [
|
|
||||||
''+${pkgs.writeShellScript "borgbackup-job-${dest.name}-pre-backup-commands" preBackupScript}''
|
|
||||||
];
|
|
||||||
}
|
|
||||||
) cfg.destinations;
|
|
||||||
|
|
||||||
services.borgbackup.jobs = lib.mapAttrs (_: dest: {
|
|
||||||
paths = lib.unique (
|
|
||||||
lib.flatten (map (state: state.folders) (lib.attrValues config.clan.core.state))
|
|
||||||
);
|
|
||||||
exclude = cfg.exclude;
|
|
||||||
repo = dest.repo;
|
|
||||||
environment.BORG_RSH = dest.rsh;
|
|
||||||
compression = "auto,zstd";
|
|
||||||
startAt = "*-*-* 01:00:00";
|
|
||||||
persistentTimer = true;
|
|
||||||
|
|
||||||
encryption = {
|
|
||||||
mode = "repokey";
|
|
||||||
passCommand = "cat ${config.clan.core.facts.services.borgbackup.secret."borgbackup.repokey".path}";
|
|
||||||
};
|
|
||||||
|
|
||||||
prune.keep = {
|
|
||||||
within = "1d"; # Keep all archives from the last day
|
|
||||||
daily = 7;
|
|
||||||
weekly = 4;
|
|
||||||
monthly = 0;
|
|
||||||
};
|
|
||||||
}) cfg.destinations;
|
|
||||||
|
|
||||||
clan.core.facts.services.borgbackup = {
|
|
||||||
public."borgbackup.ssh.pub" = { };
|
|
||||||
secret."borgbackup.ssh" = { };
|
|
||||||
secret."borgbackup.repokey" = { };
|
|
||||||
generator.path = [
|
|
||||||
pkgs.openssh
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.xkcdpass
|
|
||||||
];
|
|
||||||
generator.script = ''
|
|
||||||
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
|
|
||||||
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
|
|
||||||
xkcdpass -n 4 -d - > "$secrets"/borgbackup.repokey
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
(pkgs.writeShellScriptBin "borgbackup-create" ''
|
|
||||||
set -efu -o pipefail
|
|
||||||
${lib.concatMapStringsSep "\n" (dest: ''
|
|
||||||
systemctl start borgbackup-job-${dest.name}
|
|
||||||
'') (lib.attrValues cfg.destinations)}
|
|
||||||
'')
|
|
||||||
(pkgs.writeShellScriptBin "borgbackup-list" ''
|
|
||||||
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 /
|
|
||||||
IFS=':' read -ra FOLDER <<< "$FOLDERS"
|
|
||||||
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[@]}"
|
|
||||||
'')
|
|
||||||
];
|
|
||||||
|
|
||||||
clan.core.backups.providers.borgbackup = {
|
|
||||||
list = "borgbackup-list";
|
|
||||||
create = "borgbackup-create";
|
|
||||||
restore = "borgbackup-restore";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
{ config, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
instances = config.clan.inventory.services.borgbackup;
|
# Instances might be empty, if the module is not used via the inventory
|
||||||
|
instances = config.clan.inventory.services.borgbackup or { };
|
||||||
# roles = { ${role_name} :: { machines :: [string] } }
|
# roles = { ${role_name} :: { machines :: [string] } }
|
||||||
allServers = lib.foldlAttrs (
|
allServers = lib.foldlAttrs (
|
||||||
acc: _instanceName: instanceConfig:
|
acc: _instanceName: instanceConfig:
|
||||||
@@ -14,11 +20,79 @@ let
|
|||||||
) [ ] instances;
|
) [ ] instances;
|
||||||
|
|
||||||
inherit (config.clan.core) machineName;
|
inherit (config.clan.core) machineName;
|
||||||
|
|
||||||
|
cfg = config.clan.borgbackup;
|
||||||
|
preBackupScript = ''
|
||||||
|
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
|
||||||
|
''
|
||||||
|
) (lib.attrValues config.clan.core.state)}
|
||||||
|
|
||||||
|
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
||||||
|
echo "pre-backup commands failed for the following services:"
|
||||||
|
for state in "''${!preCommandErrors[@]}"; do
|
||||||
|
echo " $state"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config.clan.borgbackup.destinations =
|
options.clan.borgbackup.destinations = lib.mkOption {
|
||||||
let
|
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";
|
||||||
|
};
|
||||||
|
repo = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "the borgbackup repository to backup to";
|
||||||
|
};
|
||||||
|
rsh = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "ssh -i ${
|
||||||
|
config.clan.core.facts.services.borgbackup.secret."borgbackup.ssh".path
|
||||||
|
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes";
|
||||||
|
defaultText = "ssh -i \${config.clan.core.facts.services.borgbackup.secret.\"borgbackup.ssh\".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
|
||||||
|
description = "the rsh to use for the backup";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
destinations where the machine should be backuped to
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.clan.borgbackup.exclude = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
example = [ "*.pyc" ];
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Directories/Files to exclude from the backup.
|
||||||
|
Use * as a wildcard.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
|
||||||
|
# Destinations
|
||||||
|
clan.borgbackup.destinations =
|
||||||
|
let
|
||||||
destinations = builtins.map (serverName: {
|
destinations = builtins.map (serverName: {
|
||||||
name = serverName;
|
name = serverName;
|
||||||
value = {
|
value = {
|
||||||
@@ -27,4 +101,94 @@ in
|
|||||||
}) allServers;
|
}) allServers;
|
||||||
in
|
in
|
||||||
(builtins.listToAttrs destinations);
|
(builtins.listToAttrs destinations);
|
||||||
|
|
||||||
|
# Derived from the destinations
|
||||||
|
systemd.services = lib.mapAttrs' (
|
||||||
|
_: dest:
|
||||||
|
lib.nameValuePair "borgbackup-job-${dest.name}" {
|
||||||
|
# since borgbackup mounts the system read-only, we need to run in a ExecStartPre script, so we can generate additional files.
|
||||||
|
serviceConfig.ExecStartPre = [
|
||||||
|
''+${pkgs.writeShellScript "borgbackup-job-${dest.name}-pre-backup-commands" preBackupScript}''
|
||||||
|
];
|
||||||
|
}
|
||||||
|
) cfg.destinations;
|
||||||
|
|
||||||
|
services.borgbackup.jobs = lib.mapAttrs (_: dest: {
|
||||||
|
paths = lib.unique (
|
||||||
|
lib.flatten (map (state: state.folders) (lib.attrValues config.clan.core.state))
|
||||||
|
);
|
||||||
|
exclude = cfg.exclude;
|
||||||
|
repo = dest.repo;
|
||||||
|
environment.BORG_RSH = dest.rsh;
|
||||||
|
compression = "auto,zstd";
|
||||||
|
startAt = "*-*-* 01:00:00";
|
||||||
|
persistentTimer = true;
|
||||||
|
|
||||||
|
encryption = {
|
||||||
|
mode = "repokey";
|
||||||
|
passCommand = "cat ${config.clan.core.facts.services.borgbackup.secret."borgbackup.repokey".path}";
|
||||||
|
};
|
||||||
|
|
||||||
|
prune.keep = {
|
||||||
|
within = "1d"; # Keep all archives from the last day
|
||||||
|
daily = 7;
|
||||||
|
weekly = 4;
|
||||||
|
monthly = 0;
|
||||||
|
};
|
||||||
|
}) cfg.destinations;
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
(pkgs.writeShellScriptBin "borgbackup-create" ''
|
||||||
|
set -efu -o pipefail
|
||||||
|
${lib.concatMapStringsSep "\n" (dest: ''
|
||||||
|
systemctl start borgbackup-job-${dest.name}
|
||||||
|
'') (lib.attrValues cfg.destinations)}
|
||||||
|
'')
|
||||||
|
(pkgs.writeShellScriptBin "borgbackup-list" ''
|
||||||
|
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 /
|
||||||
|
IFS=':' read -ra FOLDER <<< "$FOLDERS"
|
||||||
|
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[@]}"
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
# Facts generation. So the client can authenticate to the server
|
||||||
|
clan.core.facts.services.borgbackup = {
|
||||||
|
public."borgbackup.ssh.pub" = { };
|
||||||
|
secret."borgbackup.ssh" = { };
|
||||||
|
secret."borgbackup.repokey" = { };
|
||||||
|
generator.path = [
|
||||||
|
pkgs.openssh
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.xkcdpass
|
||||||
|
];
|
||||||
|
generator.script = ''
|
||||||
|
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
|
||||||
|
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
|
||||||
|
xkcdpass -n 4 -d - > "$secrets"/borgbackup.repokey
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
clan.core.backups.providers.borgbackup = {
|
||||||
|
list = "borgbackup-list";
|
||||||
|
create = "borgbackup-create";
|
||||||
|
restore = "borgbackup-restore";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ let
|
|||||||
machineDir = clanDir + "/machines/";
|
machineDir = clanDir + "/machines/";
|
||||||
inherit (config.clan.core) machineName;
|
inherit (config.clan.core) machineName;
|
||||||
|
|
||||||
instances = config.clan.inventory.services.borgbackup;
|
# Instances might be empty, if the module is not used via the inventory
|
||||||
|
#
|
||||||
# roles = { ${role_name} :: { machines :: [string] } }
|
# Type: { ${instanceName} :: { roles :: Roles } }
|
||||||
|
# Roles :: { ${role_name} :: { machines :: [string] } }
|
||||||
|
instances = config.clan.inventory.services.borgbackup or { };
|
||||||
|
|
||||||
allClients = lib.foldlAttrs (
|
allClients = lib.foldlAttrs (
|
||||||
acc: _instanceName: instanceConfig:
|
acc: _instanceName: instanceConfig:
|
||||||
|
|||||||
@@ -1,26 +1,6 @@
|
|||||||
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
|
# Dont author new modules with this file.
|
||||||
{
|
{
|
||||||
config,
|
imports = [ ./roles/default.nix ];
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
config = {
|
|
||||||
clan.core.vars.generators.disk-id = {
|
|
||||||
files.diskId.secret = false;
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.bash
|
|
||||||
];
|
|
||||||
script = ''
|
|
||||||
uuid=$(bash ${../uuid4.sh})
|
|
||||||
|
|
||||||
# Remove the hyphens from the UUID
|
|
||||||
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
|
||||||
|
|
||||||
echo -n "$uuid_no_hyphens" > "$out/diskId"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
26
clanModules/disk-id/roles/default.nix
Normal file
26
clanModules/disk-id/roles/default.nix
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
config = {
|
||||||
|
clan.core.vars.generators.disk-id = {
|
||||||
|
files.diskId.secret = false;
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.bash
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
uuid=$(bash ${../uuid4.sh})
|
||||||
|
|
||||||
|
# Remove the hyphens from the UUID
|
||||||
|
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
||||||
|
|
||||||
|
echo -n "$uuid_no_hyphens" > "$out/diskId"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,86 +1,6 @@
|
|||||||
{ lib, config, ... }:
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
let
|
# Dont author new modules with this file.
|
||||||
cfg = config.clan.iwd;
|
|
||||||
secret_path = ssid: config.clan.core.facts.services."iwd.${ssid}".secret."iwd.${ssid}".path;
|
|
||||||
secret_generator = name: value: {
|
|
||||||
name = "iwd.${value.ssid}";
|
|
||||||
value =
|
|
||||||
let
|
|
||||||
secret_name = "iwd.${value.ssid}";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
secret.${secret_name} = { };
|
imports = [ ./roles/default.nix ];
|
||||||
generator.prompt = "Wifi password for '${value.ssid}'";
|
|
||||||
generator.script = ''
|
|
||||||
config="
|
|
||||||
[Security]
|
|
||||||
Passphrase=$prompt_value
|
|
||||||
"
|
|
||||||
echo "$config" > $secrets/${secret_name}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.clan.iwd = {
|
|
||||||
networks = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule (
|
|
||||||
{ name, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
ssid = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = name;
|
|
||||||
description = "The name of the wifi network";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
default = { };
|
|
||||||
description = "Wifi networks to predefine";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
(lib.mkRemovedOptionModule [
|
|
||||||
"clan"
|
|
||||||
"iwd"
|
|
||||||
"enable"
|
|
||||||
] "Just define clan.iwd.networks to enable it")
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkMerge [
|
|
||||||
(lib.mkIf (cfg.networks != { }) {
|
|
||||||
# Systemd tmpfiles rule to create /var/lib/iwd/example.psk file
|
|
||||||
systemd.tmpfiles.rules = lib.mapAttrsToList (
|
|
||||||
_: value: "C /var/lib/iwd/${value.ssid}.psk 0600 root root - ${secret_path value.ssid}"
|
|
||||||
) cfg.networks;
|
|
||||||
|
|
||||||
clan.core.facts.services = lib.mapAttrs' secret_generator cfg.networks;
|
|
||||||
|
|
||||||
# TODO: restart the iwd.service if something changes
|
|
||||||
})
|
|
||||||
{
|
|
||||||
# disable wpa supplicant
|
|
||||||
networking.wireless.enable = false;
|
|
||||||
|
|
||||||
# Set the network manager backend to iwd
|
|
||||||
networking.networkmanager.wifi.backend = "iwd";
|
|
||||||
|
|
||||||
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
|
||||||
networking.wireless.iwd = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
Network = {
|
|
||||||
EnableIPv6 = true;
|
|
||||||
RoutePriorityOffset = 300;
|
|
||||||
};
|
|
||||||
Settings.AutoConnect = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
86
clanModules/iwd/roles/default.nix
Normal file
86
clanModules/iwd/roles/default.nix
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.clan.iwd;
|
||||||
|
secret_path = ssid: config.clan.core.facts.services."iwd.${ssid}".secret."iwd.${ssid}".path;
|
||||||
|
secret_generator = name: value: {
|
||||||
|
name = "iwd.${value.ssid}";
|
||||||
|
value =
|
||||||
|
let
|
||||||
|
secret_name = "iwd.${value.ssid}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
secret.${secret_name} = { };
|
||||||
|
generator.prompt = "Wifi password for '${value.ssid}'";
|
||||||
|
generator.script = ''
|
||||||
|
config="
|
||||||
|
[Security]
|
||||||
|
Passphrase=$prompt_value
|
||||||
|
"
|
||||||
|
echo "$config" > $secrets/${secret_name}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.clan.iwd = {
|
||||||
|
networks = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
ssid = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = name;
|
||||||
|
description = "The name of the wifi network";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
default = { };
|
||||||
|
description = "Wifi networks to predefine";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
(lib.mkRemovedOptionModule [
|
||||||
|
"clan"
|
||||||
|
"iwd"
|
||||||
|
"enable"
|
||||||
|
] "Just define clan.iwd.networks to enable it")
|
||||||
|
];
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf (cfg.networks != { }) {
|
||||||
|
# Systemd tmpfiles rule to create /var/lib/iwd/example.psk file
|
||||||
|
systemd.tmpfiles.rules = lib.mapAttrsToList (
|
||||||
|
_: value: "C /var/lib/iwd/${value.ssid}.psk 0600 root root - ${secret_path value.ssid}"
|
||||||
|
) cfg.networks;
|
||||||
|
|
||||||
|
clan.core.facts.services = lib.mapAttrs' secret_generator cfg.networks;
|
||||||
|
|
||||||
|
# TODO: restart the iwd.service if something changes
|
||||||
|
})
|
||||||
|
{
|
||||||
|
# disable wpa supplicant
|
||||||
|
networking.wireless.enable = false;
|
||||||
|
|
||||||
|
# Set the network manager backend to iwd
|
||||||
|
networking.networkmanager.wifi.backend = "iwd";
|
||||||
|
|
||||||
|
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
||||||
|
networking.wireless.iwd = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
Network = {
|
||||||
|
EnableIPv6 = true;
|
||||||
|
RoutePriorityOffset = 300;
|
||||||
|
};
|
||||||
|
Settings.AutoConnect = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,45 +1,6 @@
|
|||||||
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
|
# Dont author new modules with this file.
|
||||||
{
|
{
|
||||||
config,
|
imports = [ ./roles/default.nix ];
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
var = config.clan.core.vars.generators.machine-id.files.machineId or { };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
config = lib.mkMerge [
|
|
||||||
(lib.mkIf ((var.machineId.value or null) != null) {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = lib.stringLength var.machineId.value == 32;
|
|
||||||
message = "machineId must be exactly 32 characters long.";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
boot.kernelParams = [
|
|
||||||
''systemd.machine_id=${var.machineId.value}''
|
|
||||||
];
|
|
||||||
environment.etc."machine-id" = {
|
|
||||||
text = var.machineId.value;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
{
|
|
||||||
clan.core.vars.generators.machine-id = {
|
|
||||||
files.machineId.secret = false;
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.bash
|
|
||||||
];
|
|
||||||
script = ''
|
|
||||||
uuid=$(bash ${../uuid4.sh})
|
|
||||||
|
|
||||||
# Remove the hyphens from the UUID
|
|
||||||
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
|
||||||
|
|
||||||
echo -n "$uuid_no_hyphens" > "$out/machineId"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
clanModules/machine-id/roles/default.nix
Normal file
45
clanModules/machine-id/roles/default.nix
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
var = config.clan.core.vars.generators.machine-id.files.machineId or { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf ((var.machineId.value or null) != null) {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = lib.stringLength var.machineId.value == 32;
|
||||||
|
message = "machineId must be exactly 32 characters long.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
boot.kernelParams = [
|
||||||
|
''systemd.machine_id=${var.machineId.value}''
|
||||||
|
];
|
||||||
|
environment.etc."machine-id" = {
|
||||||
|
text = var.machineId.value;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
{
|
||||||
|
clan.core.vars.generators.machine-id = {
|
||||||
|
files.machineId.secret = false;
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.bash
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
uuid=$(bash ${../uuid4.sh})
|
||||||
|
|
||||||
|
# Remove the hyphens from the UUID
|
||||||
|
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
||||||
|
|
||||||
|
echo -n "$uuid_no_hyphens" > "$out/machineId"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,19 +1,6 @@
|
|||||||
|
# Dont import this file
|
||||||
|
# It is only here for backwards compatibility.
|
||||||
|
# Dont author new modules with this file.
|
||||||
{
|
{
|
||||||
config,
|
imports = [ ./roles/default.nix ];
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
options.clan.packages = {
|
|
||||||
packages = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
description = "The packages to install on the machine";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = {
|
|
||||||
environment.systemPackages = map (
|
|
||||||
pName: lib.getAttrFromPath (lib.splitString "." pName) pkgs
|
|
||||||
) config.clan.packages.packages;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
19
clanModules/packages/roles/default.nix
Normal file
19
clanModules/packages/roles/default.nix
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
options.clan.packages = {
|
||||||
|
packages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "The packages to install on the machine";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
environment.systemPackages = map (
|
||||||
|
pName: lib.getAttrFromPath (lib.splitString "." pName) pkgs
|
||||||
|
) config.clan.packages.packages;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,55 +1,3 @@
|
|||||||
{ lib, config, ... }:
|
|
||||||
{
|
{
|
||||||
options.clan.single-disk = {
|
imports = [ ./roles/default.nix ];
|
||||||
device = lib.mkOption {
|
|
||||||
default = null;
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
description = "The primary disk device to install the system on";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = {
|
|
||||||
warnings = [
|
|
||||||
"clanModules.single-disk is deprecated. Please copy the disko config from the module into your machine config."
|
|
||||||
];
|
|
||||||
|
|
||||||
boot.loader.grub.efiSupport = lib.mkDefault true;
|
|
||||||
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
|
|
||||||
disko.devices = {
|
|
||||||
disk = {
|
|
||||||
main = {
|
|
||||||
type = "disk";
|
|
||||||
# This is set through the UI
|
|
||||||
device = config.clan.single-disk.device;
|
|
||||||
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
partitions = {
|
|
||||||
boot = {
|
|
||||||
size = "1M";
|
|
||||||
type = "EF02"; # for grub MBR
|
|
||||||
priority = 1;
|
|
||||||
};
|
|
||||||
ESP = {
|
|
||||||
size = "512M";
|
|
||||||
type = "EF00";
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "vfat";
|
|
||||||
mountpoint = "/boot";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
root = {
|
|
||||||
size = "100%";
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "ext4";
|
|
||||||
mountpoint = "/";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
55
clanModules/single-disk/roles/default.nix
Normal file
55
clanModules/single-disk/roles/default.nix
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
{
|
||||||
|
options.clan.single-disk = {
|
||||||
|
device = lib.mkOption {
|
||||||
|
default = null;
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
description = "The primary disk device to install the system on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
warnings = [
|
||||||
|
"clanModules.single-disk is deprecated. Please copy the disko config from the module into your machine config."
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.loader.grub.efiSupport = lib.mkDefault true;
|
||||||
|
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
|
||||||
|
disko.devices = {
|
||||||
|
disk = {
|
||||||
|
main = {
|
||||||
|
type = "disk";
|
||||||
|
# This is set through the UI
|
||||||
|
device = config.clan.single-disk.device;
|
||||||
|
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
boot = {
|
||||||
|
size = "1M";
|
||||||
|
type = "EF02"; # for grub MBR
|
||||||
|
priority = 1;
|
||||||
|
};
|
||||||
|
ESP = {
|
||||||
|
size = "512M";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,18 +1,6 @@
|
|||||||
{ config, lib, ... }:
|
# Dont import this file
|
||||||
let
|
# It is only here for backwards compatibility.
|
||||||
var = config.clan.core.vars.generators.state-version.files.version or { };
|
# Dont author new modules with this file.
|
||||||
in
|
|
||||||
{
|
{
|
||||||
system.stateVersion = lib.mkDefault var.value;
|
imports = [ ./roles/default.nix ];
|
||||||
|
|
||||||
clan.core.vars.generators.state-version = {
|
|
||||||
files.version = {
|
|
||||||
secret = false;
|
|
||||||
value = lib.mkDefault lib.version;
|
|
||||||
};
|
|
||||||
runtimeInputs = [ ];
|
|
||||||
script = ''
|
|
||||||
echo -n ${lib.versions.majorMinor config.system.stateVersion} > $out/version
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
clanModules/state-version/roles/default.nix
Normal file
18
clanModules/state-version/roles/default.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
var = config.clan.core.vars.generators.state-version.files.version or { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
system.stateVersion = lib.mkDefault var.value;
|
||||||
|
|
||||||
|
clan.core.vars.generators.state-version = {
|
||||||
|
files.version = {
|
||||||
|
secret = false;
|
||||||
|
value = lib.mkDefault lib.version;
|
||||||
|
};
|
||||||
|
runtimeInputs = [ ];
|
||||||
|
script = ''
|
||||||
|
echo -n ${lib.versions.majorMinor config.system.stateVersion} > $out/version
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -17,6 +17,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
"borgbackup": {
|
||||||
|
"simple": {
|
||||||
|
"roles": {
|
||||||
|
"client": {
|
||||||
|
"machines": ["test-inventory-machine"]
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"tags": ["2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"double": {
|
||||||
|
"roles": {
|
||||||
|
"server": {
|
||||||
|
"machines": ["test-inventory-machine"]
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"tags": ["1"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"editors": {
|
"editors": {
|
||||||
"meta": {
|
"meta": {
|
||||||
|
|||||||
@@ -81,17 +81,10 @@ let
|
|||||||
builtins.attrValues resolvedRoles
|
builtins.attrValues resolvedRoles
|
||||||
);
|
);
|
||||||
|
|
||||||
# Inverse map of roles. Allows for easy lookup of roles for a given machine.
|
# all roles where the machine is present
|
||||||
# { ${machine_name} :: [roles]
|
machineRoles = builtins.attrNames (
|
||||||
inverseRoles = lib.foldlAttrs (
|
lib.filterAttrs (_role: roleConfig: builtins.elem machineName roleConfig.machines) resolvedRoles
|
||||||
acc: roleName:
|
);
|
||||||
{ machines }:
|
|
||||||
acc
|
|
||||||
// builtins.foldl' (
|
|
||||||
acc2: machineName: acc2 // { ${machineName} = (acc.${machineName} or [ ]) ++ [ roleName ]; }
|
|
||||||
) { } machines
|
|
||||||
) { } resolvedRoles;
|
|
||||||
|
|
||||||
machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { };
|
machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { };
|
||||||
globalConfig = serviceConfig.config or { };
|
globalConfig = serviceConfig.config or { };
|
||||||
|
|
||||||
@@ -99,7 +92,7 @@ let
|
|||||||
machineExtraModules = serviceConfig.machines.${machineName}.extraModules or [ ];
|
machineExtraModules = serviceConfig.machines.${machineName}.extraModules or [ ];
|
||||||
roleServiceExtraModules = builtins.foldl' (
|
roleServiceExtraModules = builtins.foldl' (
|
||||||
acc: role: acc ++ serviceConfig.roles.${role}.extraModules or [ ]
|
acc: role: acc ++ serviceConfig.roles.${role}.extraModules or [ ]
|
||||||
) [ ] inverseRoles.${machineName} or [ ];
|
) [ ] machineRoles;
|
||||||
|
|
||||||
# TODO: maybe optimize this dont lookup the role in inverse roles. Imports are not lazy
|
# TODO: maybe optimize this dont lookup the role in inverse roles. Imports are not lazy
|
||||||
roleModules = builtins.map (
|
roleModules = builtins.map (
|
||||||
@@ -109,14 +102,12 @@ let
|
|||||||
in
|
in
|
||||||
if builtins.pathExists path then
|
if builtins.pathExists path then
|
||||||
path
|
path
|
||||||
else if role == "default" then
|
|
||||||
{ }
|
|
||||||
else
|
else
|
||||||
throw "Module doesn't have role: '${role}'. Path: ${path} not found."
|
throw "Module doesn't have role: '${role}'. Path: ${path} not found."
|
||||||
) inverseRoles.${machineName} or [ ];
|
) machineRoles;
|
||||||
|
|
||||||
roleServiceConfigs = builtins.filter (m: m != { }) (
|
roleServiceConfigs = builtins.filter (m: m != { }) (
|
||||||
builtins.map (role: serviceConfig.roles.${role}.config or { }) inverseRoles.${machineName} or [ ]
|
builtins.map (role: serviceConfig.roles.${role}.config or { }) machineRoles
|
||||||
);
|
);
|
||||||
|
|
||||||
extraModules = map (s: if builtins.typeOf s == "string" then "${directory}/${s}" else s) (
|
extraModules = map (s: if builtins.typeOf s == "string" then "${directory}/${s}" else s) (
|
||||||
@@ -128,7 +119,7 @@ let
|
|||||||
acc2
|
acc2
|
||||||
++ [
|
++ [
|
||||||
{
|
{
|
||||||
imports = [ clan-core.clanModules.${serviceName} ] ++ roleModules ++ extraModules;
|
imports = roleModules ++ extraModules;
|
||||||
}
|
}
|
||||||
(lib.optionalAttrs (globalConfig != { } || machineServiceConfig != { } || roleServiceConfigs != [ ])
|
(lib.optionalAttrs (globalConfig != { } || machineServiceConfig != { } || roleServiceConfigs != [ ])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,15 +42,12 @@ in
|
|||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
server_imports = [
|
server_imports = [
|
||||||
clan-core.clanModules.borgbackup
|
|
||||||
"${clan-core.clanModules.borgbackup}/roles/server.nix"
|
"${clan-core.clanModules.borgbackup}/roles/server.nix"
|
||||||
];
|
];
|
||||||
client_1_imports = [
|
client_1_imports = [
|
||||||
clan-core.clanModules.borgbackup
|
|
||||||
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
||||||
];
|
];
|
||||||
client_2_imports = [
|
client_2_imports = [
|
||||||
clan-core.clanModules.borgbackup
|
|
||||||
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -117,7 +114,6 @@ in
|
|||||||
};
|
};
|
||||||
expected = {
|
expected = {
|
||||||
machine_1_imports = [
|
machine_1_imports = [
|
||||||
clan-core.clanModules.borgbackup
|
|
||||||
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
"${clan-core.clanModules.borgbackup}/roles/client.nix"
|
||||||
"${clan-core.clanModules.borgbackup}/roles/server.nix"
|
"${clan-core.clanModules.borgbackup}/roles/server.nix"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.clan.inventory.services = lib.mkOption {
|
options.clan.inventory.services = lib.mkOption {
|
||||||
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Configuration for each inventory service.
|
Configuration for each inventory service.
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ def get_roles(module_path: Path) -> None | list[str]:
|
|||||||
|
|
||||||
roles_dir = module_path / "roles"
|
roles_dir = module_path / "roles"
|
||||||
if not roles_dir.exists() or not roles_dir.is_dir():
|
if not roles_dir.exists() or not roles_dir.is_dir():
|
||||||
return ["default"]
|
return []
|
||||||
|
|
||||||
return [
|
return [
|
||||||
role.stem # filename without .nix extension
|
role.stem # filename without .nix extension
|
||||||
|
|||||||
Reference in New Issue
Block a user