forked from clan/clan-core
Merge pull request 'clan.core.state: wrap all commands in shell scripts' (#1639) from refactor-state into main
This commit is contained in:
commit
b3123b150f
@ -70,15 +70,7 @@
|
|||||||
};
|
};
|
||||||
clan.core.facts.secretStore = "vm";
|
clan.core.facts.secretStore = "vm";
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
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 [ ];
|
||||||
@ -90,11 +82,15 @@
|
|||||||
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
|
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
|
||||||
|
|
||||||
clan.core.state.test-service = {
|
clan.core.state.test-service = {
|
||||||
preBackupCommand = ''
|
preBackupScript = ''
|
||||||
touch /var/test-service/pre-backup-command
|
touch /var/test-service/pre-backup-command
|
||||||
'';
|
'';
|
||||||
preRestoreCommand = "pre-restore-command";
|
preRestoreScript = ''
|
||||||
postRestoreCommand = "post-restore-command";
|
touch /var/test-service/pre-restore-command
|
||||||
|
'';
|
||||||
|
postRestoreScript = ''
|
||||||
|
touch /var/test-service/post-restore-command
|
||||||
|
'';
|
||||||
folders = [ "/var/test-service" ];
|
folders = [ "/var/test-service" ];
|
||||||
};
|
};
|
||||||
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
machine.succeed("""
|
machine.succeed("""
|
||||||
set -x
|
set -x
|
||||||
${nodes.machine.clan.core.state.postgresql-test.postRestoreCommand}
|
${nodes.machine.clan.core.state.test.postRestoreCommand}
|
||||||
""")
|
""")
|
||||||
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -l >&2")
|
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -l >&2")
|
||||||
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
|
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
|
||||||
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
# check if restore works if the database does not exist
|
# check if restore works if the database does not exist
|
||||||
machine.succeed("runuser -u postgres -- dropdb test")
|
machine.succeed("runuser -u postgres -- dropdb test")
|
||||||
machine.succeed("${nodes.machine.clanCore.state.postgresql-test.postRestoreCommand}")
|
machine.succeed("${nodes.machine.clan.core.state.test.postRestoreCommand}")
|
||||||
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
|
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
|
@ -13,14 +13,14 @@ let
|
|||||||
state:
|
state:
|
||||||
lib.optionalString (state.preBackupCommand != null) ''
|
lib.optionalString (state.preBackupCommand != null) ''
|
||||||
echo "Running pre-backup command for ${state.name}"
|
echo "Running pre-backup command for ${state.name}"
|
||||||
if ! ( ${state.preBackupCommand} ) then
|
if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
|
||||||
preCommandErrors["${state.name}"]=1
|
preCommandErrors["${state.name}"]=1
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
) (lib.attrValues config.clan.core.state)}
|
) (lib.attrValues config.clan.core.state)}
|
||||||
|
|
||||||
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
||||||
echo "PreBackupCommand failed for the following services:"
|
echo "pre-backup commands failed for the following services:"
|
||||||
for state in "''${!preCommandErrors[@]}"; do
|
for state in "''${!preCommandErrors[@]}"; do
|
||||||
echo " $state"
|
echo " $state"
|
||||||
done
|
done
|
||||||
|
@ -125,7 +125,6 @@ in
|
|||||||
}
|
}
|
||||||
${lib.concatMapStringsSep "\n" (target: ''
|
${lib.concatMapStringsSep "\n" (target: ''
|
||||||
${mountHook target}
|
${mountHook target}
|
||||||
set -x
|
|
||||||
echo "Creating backup '${target.name}'"
|
echo "Creating backup '${target.name}'"
|
||||||
|
|
||||||
${lib.optionalString (target.preBackupHook != null) ''
|
${lib.optionalString (target.preBackupHook != null) ''
|
||||||
@ -139,7 +138,7 @@ in
|
|||||||
state:
|
state:
|
||||||
lib.optionalString (state.preBackupCommand != null) ''
|
lib.optionalString (state.preBackupCommand != null) ''
|
||||||
echo "Running pre-backup command for ${state.name}"
|
echo "Running pre-backup command for ${state.name}"
|
||||||
if ! ( ${state.preBackupCommand} ) then
|
if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
|
||||||
preCommandErrors["${state.name}"]=1
|
preCommandErrors["${state.name}"]=1
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
|
@ -185,7 +185,7 @@ in
|
|||||||
+ lib.concatMapStringsSep "\n" (user: ''
|
+ lib.concatMapStringsSep "\n" (user: ''
|
||||||
# only create user if it doesn't exist
|
# only create user if it doesn't exist
|
||||||
/run/current-system/sw/bin/matrix-synapse-register_new_matrix_user --exists-ok --password-file ${
|
/run/current-system/sw/bin/matrix-synapse-register_new_matrix_user --exists-ok --password-file ${
|
||||||
config.clanCore.facts.services."matrix-password-${user.name}".secret."matrix-password-${user.name}".path
|
config.clan.core.facts.services."matrix-password-${user.name}".secret."matrix-password-${user.name}".path
|
||||||
} --user "${user.name}" ${if user.admin then "--admin" else "--no-admin"}
|
} --user "${user.name}" ${if user.admin then "--admin" else "--no-admin"}
|
||||||
'') (lib.attrValues cfg.users);
|
'') (lib.attrValues cfg.users);
|
||||||
in
|
in
|
||||||
|
@ -14,7 +14,7 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
folders = [ folder ];
|
folders = [ folder ];
|
||||||
preBackupCommand = ''
|
preBackupScript = ''
|
||||||
export PATH=${
|
export PATH=${
|
||||||
lib.makeBinPath [
|
lib.makeBinPath [
|
||||||
config.services.postgresql.package
|
config.services.postgresql.package
|
||||||
@ -32,7 +32,41 @@ let
|
|||||||
runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp"
|
runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp"
|
||||||
mv "${current}.tmp" ${current}
|
mv "${current}.tmp" ${current}
|
||||||
'';
|
'';
|
||||||
postRestoreCommand = "postgres-db-restore-command-${db.name}";
|
postRestoreScript = ''
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath [
|
||||||
|
config.services.postgresql.package
|
||||||
|
config.systemd.package
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.util-linux
|
||||||
|
pkgs.zstd
|
||||||
|
pkgs.gnugrep
|
||||||
|
]
|
||||||
|
}
|
||||||
|
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Waiting for postgres to be ready..."
|
||||||
|
while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do
|
||||||
|
if ! systemctl is-active postgresql; then exit 1; fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -e "${current}" ]]; then
|
||||||
|
(
|
||||||
|
systemctl stop ${lib.concatStringsSep " " db.restore.stopOnRestore}
|
||||||
|
trap "systemctl start ${lib.concatStringsSep " " db.restore.stopOnRestore}" EXIT
|
||||||
|
|
||||||
|
mkdir -p "${folder}"
|
||||||
|
if runuser -u postgres -- psql -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${db.name}'" | grep -q 1; then
|
||||||
|
runuser -u postgres -- dropdb "${db.name}"
|
||||||
|
fi
|
||||||
|
runuser -u postgres -- pg_restore -C -d postgres "${current}"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
echo No database backup found, skipping restore
|
||||||
|
fi
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
createDatabase = db: ''
|
createDatabase = db: ''
|
||||||
@ -66,6 +100,12 @@ in
|
|||||||
name = lib.mkOption {
|
name = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = name;
|
default = name;
|
||||||
|
description = "Database name.";
|
||||||
|
};
|
||||||
|
service = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = name;
|
||||||
|
description = "Service name that we associate with the database.";
|
||||||
};
|
};
|
||||||
# set to false, in case the upstream module uses ensureDatabase option
|
# set to false, in case the upstream module uses ensureDatabase option
|
||||||
create.enable = lib.mkOption {
|
create.enable = lib.mkOption {
|
||||||
@ -87,7 +127,7 @@ in
|
|||||||
restore.stopOnRestore = lib.mkOption {
|
restore.stopOnRestore = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
description = "List of services to stop before restoring the database.";
|
description = "List of systemd services to stop before restoring the database.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -129,7 +169,7 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
clan.core.state = lib.mapAttrs' (
|
clan.core.state = lib.mapAttrs' (
|
||||||
_: db: lib.nameValuePair "postgresql-${db.name}" (createDatatbaseState db)
|
_: db: lib.nameValuePair db.service (createDatatbaseState db)
|
||||||
) config.clan.postgresql.databases;
|
) config.clan.postgresql.databases;
|
||||||
|
|
||||||
environment.systemPackages = builtins.map (
|
environment.systemPackages = builtins.map (
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
{ lib, ... }:
|
|
||||||
{
|
{
|
||||||
# defaults
|
lib,
|
||||||
config.clan.core.state.HOME.folders = [ "/home" ];
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
# interface
|
# interface
|
||||||
options.clan.core.state = lib.mkOption {
|
options.clan.core.state = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
lib.types.submodule (
|
lib.types.submodule (
|
||||||
{ name, ... }:
|
{ name, config, ... }:
|
||||||
{
|
{
|
||||||
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 = ''
|
description = ''
|
||||||
Name of the state
|
Name of the state
|
||||||
@ -24,8 +26,9 @@
|
|||||||
Folder where state resides in
|
Folder where state resides in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
preBackupCommand = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
preBackupScript = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.lines;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
script to run before backing up the state dir
|
script to run before backing up the state dir
|
||||||
@ -34,6 +37,15 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
preBackupCommand = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = if config.preBackupScript == null then null else "pre-backup-${name}";
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
Use this command in backup providers. It contains the content of preBackupScript.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# TODO: implement this
|
# TODO: implement this
|
||||||
#stopOnRestore = lib.mkOption {
|
#stopOnRestore = lib.mkOption {
|
||||||
# type = lib.types.listOf lib.types.str;
|
# type = lib.types.listOf lib.types.str;
|
||||||
@ -45,8 +57,8 @@
|
|||||||
# '';
|
# '';
|
||||||
#};
|
#};
|
||||||
|
|
||||||
preRestoreCommand = lib.mkOption {
|
preRestoreScript = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.lines;
|
||||||
default = null;
|
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
|
||||||
@ -55,8 +67,18 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
postRestoreCommand = lib.mkOption {
|
preRestoreCommand = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = if config.preRestoreScript == null then null else "pre-restore-${name}";
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
This command can be called to restore the state dir from a backup.
|
||||||
|
It contains the content of preRestoreScript.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
postRestoreScript = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.lines;
|
||||||
default = null;
|
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
|
||||||
@ -64,9 +86,47 @@
|
|||||||
Utilize this to start services which were previously stopped
|
Utilize this to start services which were previously stopped
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
postRestoreCommand = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = if config.postRestoreScript == null then null else "post-restore-${name}";
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
This command is called after a restore of the state dir from a backup.
|
||||||
|
|
||||||
|
It contains the content of postRestoreScript.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# defaults
|
||||||
|
config.clan.core.state.HOME.folders = [ "/home" ];
|
||||||
|
config.environment.systemPackages = lib.optional (config.clan.core.state != { }) (
|
||||||
|
pkgs.runCommand "state-commands" { } ''
|
||||||
|
${builtins.concatStringsSep "\n" (
|
||||||
|
builtins.map (state: ''
|
||||||
|
writeShellScript() {
|
||||||
|
local name=$1
|
||||||
|
local content=$2
|
||||||
|
printf "#!${pkgs.runtimeShell}\nset -eu -o pipefail\n%s" "$content" > $out/bin/$name
|
||||||
|
}
|
||||||
|
mkdir -p $out/bin/
|
||||||
|
${lib.optionalString (state.preBackupCommand != null) ''
|
||||||
|
writeShellScript ${lib.escapeShellArg state.preBackupCommand} ${lib.escapeShellArg state.preBackupScript}
|
||||||
|
''}
|
||||||
|
${lib.optionalString (state.preRestoreCommand != null) ''
|
||||||
|
writeShellScript ${lib.escapeShellArg state.preRestoreCommand} ${lib.escapeShellArg state.preRestoreScript}
|
||||||
|
''}
|
||||||
|
${lib.optionalString (state.postRestoreCommand != null) ''
|
||||||
|
writeShellScript ${lib.escapeShellArg state.postRestoreCommand} ${lib.escapeShellArg state.postRestoreScript}
|
||||||
|
''}
|
||||||
|
find $out/bin/ -type f -print0 | xargs --no-run-if-empty -0 chmod 755
|
||||||
|
'') (builtins.attrValues config.clan.core.state)
|
||||||
|
)}
|
||||||
|
''
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user