forked from clan/clan-core
clan.core.state: wrap all commands in shell scripts
Otherwise we cannot execute them via ssh and also have nix store dependencies.
This commit is contained in:
parent
117aed49e3
commit
ef9ed1ebea
|
@ -70,15 +70,7 @@
|
|||
};
|
||||
clan.core.facts.secretStore = "vm";
|
||||
|
||||
environment.systemPackages = [
|
||||
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.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
|
@ -90,11 +82,15 @@
|
|||
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
|
||||
|
||||
clan.core.state.test-service = {
|
||||
preBackupCommand = ''
|
||||
preBackupScript = ''
|
||||
touch /var/test-service/pre-backup-command
|
||||
'';
|
||||
preRestoreCommand = "pre-restore-command";
|
||||
postRestoreCommand = "post-restore-command";
|
||||
preRestoreScript = ''
|
||||
touch /var/test-service/pre-restore-command
|
||||
'';
|
||||
postRestoreScript = ''
|
||||
touch /var/test-service/post-restore-command
|
||||
'';
|
||||
folders = [ "/var/test-service" ];
|
||||
};
|
||||
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
machine.succeed("""
|
||||
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 -d test -c '\dt' >&2")
|
||||
|
@ -66,7 +66,7 @@
|
|||
|
||||
# check if restore works if the database does not exist
|
||||
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")
|
||||
'';
|
||||
})
|
||||
|
|
|
@ -13,14 +13,14 @@ let
|
|||
state:
|
||||
lib.optionalString (state.preBackupCommand != null) ''
|
||||
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
|
||||
fi
|
||||
''
|
||||
) (lib.attrValues config.clan.core.state)}
|
||||
|
||||
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
|
||||
echo " $state"
|
||||
done
|
||||
|
|
|
@ -125,7 +125,6 @@ in
|
|||
}
|
||||
${lib.concatMapStringsSep "\n" (target: ''
|
||||
${mountHook target}
|
||||
set -x
|
||||
echo "Creating backup '${target.name}'"
|
||||
|
||||
${lib.optionalString (target.preBackupHook != null) ''
|
||||
|
@ -139,7 +138,7 @@ in
|
|||
state:
|
||||
lib.optionalString (state.preBackupCommand != null) ''
|
||||
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
|
||||
fi
|
||||
''
|
||||
|
|
|
@ -14,7 +14,7 @@ let
|
|||
in
|
||||
{
|
||||
folders = [ folder ];
|
||||
preBackupCommand = ''
|
||||
preBackupScript = ''
|
||||
export PATH=${
|
||||
lib.makeBinPath [
|
||||
config.services.postgresql.package
|
||||
|
@ -32,7 +32,38 @@ let
|
|||
runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp"
|
||||
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
|
||||
]
|
||||
}
|
||||
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}"
|
||||
runuser -u postgres -- dropdb "${db.name}"
|
||||
runuser -u postgres -- pg_restore -C -d postgres "${current}"
|
||||
)
|
||||
else
|
||||
echo No database backup found, skipping restore
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
createDatabase = db: ''
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
{ lib, ... }:
|
||||
{
|
||||
# defaults
|
||||
config.clan.core.state.HOME.folders = [ "/home" ];
|
||||
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# interface
|
||||
options.clan.core.state = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, ... }:
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
type = lib.types.strMatching "^[a-zA-Z0-9_-]+$";
|
||||
default = name;
|
||||
description = ''
|
||||
Name of the state
|
||||
|
@ -24,8 +26,9 @@
|
|||
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;
|
||||
description = ''
|
||||
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
|
||||
#stopOnRestore = lib.mkOption {
|
||||
# type = lib.types.listOf lib.types.str;
|
||||
|
@ -45,8 +57,8 @@
|
|||
# '';
|
||||
#};
|
||||
|
||||
preRestoreCommand = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
preRestoreScript = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.lines;
|
||||
default = null;
|
||||
description = ''
|
||||
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;
|
||||
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;
|
||||
description = ''
|
||||
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
|
||||
'';
|
||||
};
|
||||
|
||||
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