diff --git a/flake.nix b/flake.nix index 5a348b4..a3b2abc 100644 --- a/flake.nix +++ b/flake.nix @@ -103,6 +103,7 @@ overlays/itp overlays/virtiofsd-zfs overlays/show-files-to-be-deleted + overlays/rolling_datasets inputs.uterranix.flakeModule ]; diff --git a/overlays/rolling_datasets/bin/roll_dataset b/overlays/rolling_datasets/bin/roll_dataset new file mode 100644 index 0000000..02685f2 --- /dev/null +++ b/overlays/rolling_datasets/bin/roll_dataset @@ -0,0 +1,28 @@ +# -*- mode: shell-script -*- + +source @out@/share/functions/get_old_datasets + +if [[ "$#" != 3 ]] ; then + echo "This script requires precisely 2 arguments!" + exit 1 +fi + +dataset="$1" + +generation="$(zfs get :generation "$dataset" -H -o value)" +generation="$(("$generation" + 1))" + +zfs set ":generation=$generation" "$dataset" +zfs send "$dataset" | zfs recv "$dataset/$generation" +zfs rollback "$dataset@blank" && echo "rollback complete" + +while IFS="\n" read gen +do + printf "$gen < $(("$generation" - 10)) => " + if [ "$gen" -lt "$(("$generation" - 10))" ]; then + zfs destroy -r "$dataset/$gen" + echo "destroyed" + else + echo "kept" + fi +done <<< "$(get_old_datasets "$dataset")" diff --git a/overlays/rolling_datasets/default.nix b/overlays/rolling_datasets/default.nix new file mode 100644 index 0000000..60017fd --- /dev/null +++ b/overlays/rolling_datasets/default.nix @@ -0,0 +1,67 @@ +{ inputs, ... }: +{ + flake.overlays.rolling_datasets = + final: prev: + let + writeTextFile = + { name # the name of the derivation + , text + , executable ? false # run chmod +x ? + , destination ? "" # relative path appended to $out eg "/bin/foo" + , checkPhase ? "" # syntax checks, e.g. for scripts + , env ? {} + }: + final.runCommand name + ({ inherit text executable; + passAsFile = [ "text" ]; + # Pointless to do this on a remote machine. + preferLocalBuild = true; + allowSubstitutes = false; + } // env) + '' + n=$out${destination} + mkdir -p "$(dirname "$n")" + + if [ -e "$textPath" ]; then + mv "$textPath" "$n" + else + echo -n "$text" > "$n" + fi + + ${checkPhase} + + (test -n "$executable" && chmod +x "$n") || true + ''; + writeShellScriptBin = name : text : env : + writeTextFile { + inherit name; + executable = true; + destination = "/bin/${name}"; + text = '' + #!${final.runtimeShell} + ${text} + ''; + checkPhase = '' + ${final.stdenv.shell} -n $out/bin/${name} + ''; + }; + in + { + rolling_datasets = + final.runCommand "rolling_datasets" { + + } '' + mkdir -p $out/bin + mkdir -p $out/share/functions + + for binary in ${./bin}/* ; do + substituteAll "$binary" "$out/bin/$(basename "$binary")" + done + + for function in ${./functions}/* ; do + substituteAll "$function" "$out/share/functions/$(basename "$function")" + done + ''; + }; +} + diff --git a/overlays/rolling_datasets/functions/get_old_datasets b/overlays/rolling_datasets/functions/get_old_datasets new file mode 100644 index 0000000..836abea --- /dev/null +++ b/overlays/rolling_datasets/functions/get_old_datasets @@ -0,0 +1,6 @@ +# -*- mode: shell-script -*- +function get_old_datasets() +{ + dataset="$1" + zfs list "$dataset" -t filesystem -r -H -o name | xargs -I {} ${final.runtimeShell} -c 'echo "$1" | rev | cut -f 1 -d "/" | rev' sh {} | grep -v "$dataset" +}