diff options
author | makefu <github@syntax-fehler.de> | 2015-12-28 21:00:27 +0100 |
---|---|---|
committer | makefu <github@syntax-fehler.de> | 2015-12-28 21:00:27 +0100 |
commit | 72d821d96cc7c19134773d6a07317bf7d6c2465c (patch) | |
tree | 8d30a69e6feb2a3de26591a87f1acdaba7f5a758 | |
parent | 246116dabbe849e75612fbdb57b01696913ff27e (diff) | |
parent | 5a9ccbef0abe1f3acb16d716a2e1d7faa9bb0af1 (diff) |
Merge remote-tracking branch 'cd/master'
-rw-r--r-- | krebs/3modules/backup.nix | 286 | ||||
-rw-r--r-- | krebs/3modules/default.nix | 1 | ||||
-rw-r--r-- | krebs/4lib/types.nix | 17 | ||||
-rw-r--r-- | tv/2configs/backup.nix | 42 | ||||
-rw-r--r-- | tv/2configs/default.nix | 1 | ||||
-rw-r--r-- | tv/2configs/vim.nix | 34 | ||||
-rw-r--r-- | tv/2configs/xserver/default.nix | 2 |
7 files changed, 377 insertions, 6 deletions
diff --git a/krebs/3modules/backup.nix b/krebs/3modules/backup.nix new file mode 100644 index 000000000..01bb16a2b --- /dev/null +++ b/krebs/3modules/backup.nix @@ -0,0 +1,286 @@ +{ config, lib, pkgs, ... }: +with lib; +let + out = { + options.krebs.backup = api; + config = mkIf cfg.enable imp; + }; + + cfg = config.krebs.backup; + + api = { + enable = mkEnableOption "krebs.backup" // { default = true; }; + plans = mkOption { + default = {}; + type = types.attrsOf (types.submodule ({ + # TODO enable = mkEnableOption "TODO" // { default = true; }; + options = { + method = mkOption { + type = types.enum ["pull" "push"]; + }; + name = mkOption { + type = types.str; + }; + src = mkOption { + type = types.krebs.file-location; + }; + dst = mkOption { + type = types.krebs.file-location; + }; + startAt = mkOption { + type = types.str; + }; + snapshots = mkOption { + type = types.attrsOf (types.submodule { + options = { + format = mkOption { + type = types.str; # TODO date's +FORMAT + }; + retain = mkOption { + type = types.nullOr types.int; + default = null; # null = retain all snapshots + }; + }; + }); + }; + }; + })); + }; + }; + + imp = { + users.groups.backup.gid = genid "backup"; + users.users = {} + // { + root.openssh.authorizedKeys.keys = + map (plan: plan.dst.host.ssh.pubkey) + (filter isPullSrc (attrValues cfg.plans)) + ++ + map (plan: plan.src.host.ssh.pubkey) + (filter isPushDst (attrValues cfg.plans)) + ; + } + ; + systemd.services = + flip mapAttrs' (filterAttrs (_:isPullDst) cfg.plans) (name: plan: { + name = "backup.${name}.pull"; + value = makePullService plan; + }) + // + flip mapAttrs' (filterAttrs (_:isPushSrc) cfg.plans) (name: plan: { + name = "backup.${name}.push"; + value = makePushService plan; + }) + ; + }; + + isPushSrc = plan: + plan.method == "push" && + plan.src.host.name == config.krebs.build.host.name; + + isPullSrc = plan: + plan.method == "pull" && + plan.src.host.name == config.krebs.build.host.name; + + isPushDst = plan: + plan.method == "push" && + plan.dst.host.name == config.krebs.build.host.name; + + isPullDst = plan: + plan.method == "pull" && + plan.dst.host.name == config.krebs.build.host.name; + + # TODO push destination needs this in the dst.user's PATH + service-path = [ + pkgs.coreutils + pkgs.gnused + pkgs.openssh + pkgs.rsync + pkgs.utillinux + ]; + + # TODO if there is plan.user, then use its privkey + makePushService = plan: assert isPushSrc plan; { + path = service-path; + serviceConfig = { + ExecStart = push plan; + Type = "oneshot"; + }; + startAt = plan.startAt; + }; + + makePullService = plan: assert isPullDst plan; { + path = service-path; + serviceConfig = { + ExecStart = pull plan; + Type = "oneshot"; + }; + startAt = plan.startAt; + }; + + push = plan: let + # We use writeDashBin and return the absolute path so systemd will produce + # nice names in the log, i.e. without the Nix store hash. + out = "${main}/bin/${main.name}"; + + main = writeDashBin "backup.${plan.name}.push" '' + set -efu + dst=${shell.escape plan.dst.path} + + mkdir -m 0700 -p "$dst" + exec flock -n "$dst" ${critical-section} + ''; + + critical-section = writeDash "backup.${plan.name}.push.critical-section" '' + # TODO check if there is a previous + set -efu + identity=${shell.escape plan.src.host.ssh.privkey.path} + src=${shell.escape plan.src.path} + dst_target=${shell.escape "root@${getFQDN plan.dst.host}"} + dst_path=${shell.escape plan.dst.path} + dst=$dst_target:$dst_path + + # Export NOW so runtime of rsync doesn't influence snapshot naming. + export NOW + NOW=$(date +%s) + + echo >&2 "update snapshot: current; $src -> $dst" + rsync >&2 \ + -aAXF --delete \ + -e "ssh -F /dev/null -i $identity" \ + --rsync-path ${shell.escape + "mkdir -m 0700 -p ${shell.escape plan.dst.path} && rsync"} \ + --link-dest="$dst_path/current" \ + "$src/" \ + "$dst/.partial" + + exec ssh -F /dev/null \ + -i "$identity" \ + "$dst_target" \ + -T \ + env NOW="$NOW" /bin/sh < ${remote-snapshot} + EOF + ''; + + remote-snapshot = writeDash "backup.${plan.name}.push.remote-snapshot" '' + set -efu + dst=${shell.escape plan.dst.path} + + if test -e "$dst/current"; then + mv "$dst/current" "$dst/.previous" + fi + mv "$dst/.partial" "$dst/current" + rm -fR "$dst/.previous" + echo >&2 + + (${(take-snapshots plan).text}) + ''; + + in out; + + # TODO admit plan.dst.user and its ssh identity + pull = plan: let + # We use writeDashBin and return the absolute path so systemd will produce + # nice names in the log, i.e. without the Nix store hash. + out = "${main}/bin/${main.name}"; + + main = writeDashBin "backup.${plan.name}.pull" '' + set -efu + dst=${shell.escape plan.dst.path} + + mkdir -m 0700 -p "$dst" + exec flock -n "$dst" ${critical-section} + ''; + + critical-section = writeDash "backup.${plan.name}.pull.critical-section" '' + # TODO check if there is a previous + set -efu + identity=${shell.escape plan.dst.host.ssh.privkey.path} + src=${shell.escape "root@${getFQDN plan.src.host}:${plan.src.path}"} + dst=${shell.escape plan.dst.path} + + # Export NOW so runtime of rsync doesn't influence snapshot naming. + export NOW + NOW=$(date +%s) + + echo >&2 "update snapshot: current; $dst <- $src" + mkdir -m 0700 -p ${shell.escape plan.dst.path} + rsync >&2 \ + -aAXF --delete \ + -e "ssh -F /dev/null -i $identity" \ + --link-dest="$dst/current" \ + "$src/" \ + "$dst/.partial" + mv "$dst/current" "$dst/.previous" + mv "$dst/.partial" "$dst/current" + rm -fR "$dst/.previous" + echo >&2 + + exec ${take-snapshots plan} + ''; + in out; + + take-snapshots = plan: writeDash "backup.${plan.name}.take-snapshots" '' + set -efu + NOW=''${NOW-$(date +%s)} + dst=${shell.escape plan.dst.path} + + snapshot() {( + : $ns $format $retain + name=$(date --date="@$NOW" +"$format") + if ! test -e "$dst/$ns/$name"; then + echo >&2 "create snapshot: $ns/$name" + mkdir -m 0700 -p "$dst/$ns" + rsync >&2 \ + -aAXF --delete \ + --link-dest="$dst/current" \ + "$dst/current/" \ + "$dst/$ns/.partial.$name" + mv "$dst/$ns/.partial.$name" "$dst/$ns/$name" + echo >&2 + fi + case $retain in + ([0-9]*) + delete_from=$(($retain + 1)) + ls -r "$dst/$ns" \ + | sed -n "$delete_from,\$p" \ + | while read old_name; do + echo >&2 "delete snapshot: $ns/$old_name" + rm -fR "$dst/$ns/$old_name" + done + ;; + (ALL) + : + ;; + esac + )} + + ${concatStringsSep "\n" (mapAttrsToList (ns: { format, retain ? null, ... }: + toString (map shell.escape [ + "ns=${ns}" + "format=${format}" + "retain=${if retain == null then "ALL" else toString retain}" + "snapshot" + ])) + plan.snapshots)} + ''; + + # TODO getFQDN: admit hosts in other domains + getFQDN = host: "${host.name}.${config.krebs.search-domain}"; + + writeDash = name: text: pkgs.writeScript name '' + #! ${pkgs.dash}/bin/dash + ${text} + ''; + + writeDashBin = name: text: pkgs.writeTextFile { + executable = true; + destination = "/bin/${name}"; + name = name; + text = '' + #! ${pkgs.dash}/bin/dash + ${text} + ''; + }; + +in out diff --git a/krebs/3modules/default.nix b/krebs/3modules/default.nix index cbc1291fa..ba1f425d9 100644 --- a/krebs/3modules/default.nix +++ b/krebs/3modules/default.nix @@ -7,6 +7,7 @@ let out = { imports = [ ./apt-cacher-ng.nix + ./backup.nix ./bepasty-server.nix ./build.nix ./buildbot/master.nix diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix index c52afa246..81ce659bd 100644 --- a/krebs/4lib/types.nix +++ b/krebs/4lib/types.nix @@ -177,4 +177,21 @@ types // rec { addr6 = str; hostname = str; label = str; + + krebs.file-location = types.submodule { + options = { + # TODO user + host = mkOption { + type = host; + }; + # TODO merge with ssl.privkey.path + path = mkOption { + type = types.either types.path types.str; + apply = x: { + path = toString x; + string = x; + }.${typeOf x}; + }; + }; + }; } diff --git a/tv/2configs/backup.nix b/tv/2configs/backup.nix new file mode 100644 index 000000000..51d3bb8a7 --- /dev/null +++ b/tv/2configs/backup.nix @@ -0,0 +1,42 @@ +{ config, lib, ... }: +with lib; +{ + krebs.backup.plans = addNames { + xu-test-cd = { + method = "push"; + + src = { host = config.krebs.hosts.xu; path = "/tmp/xu-test"; }; + dst = { host = config.krebs.hosts.cd; path = "/tmp/backups/xu-test"; }; + + #startAt = "0,6,12,18:00"; + startAt = "minutely"; + snapshots = { + minutely = { format = "%Y-%m-%dT%H:%M"; retain = 5; }; + hourly = { format = "%Y-%m-%dT%H"; retain = 4; }; + daily = { format = "%Y-%m-%d"; retain = 7; }; + weekly = { format = "%YW%W"; retain = 4; }; + monthly = { format = "%Y-%m"; retain = 12; }; + yearly = { format = "%Y"; }; + }; + }; + #xu-test-wu = { + # method = "push"; + # dst = { user = tv; host = wu; path = "/krebs/backup/xu-test"; }; + #}; + cd-test-xu = { + method = "pull"; + src = { host = config.krebs.hosts.cd; path = "/tmp/cd-test"; }; + dst = { host = config.krebs.hosts.xu; path = "/tmp/backups/cd-test"; }; + startAt = "minutely"; + snapshots = { + minutely = { format = "%Y-%m-%dT%H:%M"; retain = 5; }; + hourly = { format = "%Y-%m-%dT%H"; retain = 4; }; + daily = { format = "%Y-%m-%d"; retain = 7; }; + weekly = { format = "%YW%W"; retain = 4; }; + monthly = { format = "%Y-%m"; retain = 12; }; + yearly = { format = "%Y"; }; + }; + }; + + }; +} diff --git a/tv/2configs/default.nix b/tv/2configs/default.nix index 3400c13b6..c300633bb 100644 --- a/tv/2configs/default.nix +++ b/tv/2configs/default.nix @@ -28,6 +28,7 @@ with lib; imports = [ <secrets> + ./backup.nix ./vim.nix { # stockholm dependencies diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix index 0822fb5bf..0537fa7d8 100644 --- a/tv/2configs/vim.nix +++ b/tv/2configs/vim.nix @@ -4,7 +4,7 @@ with lib; let out = { environment.systemPackages = [ - pkgs.vim + vim ]; # Nano really is just a stupid name for Vim. @@ -22,14 +22,38 @@ let "${pkgs.vimPlugins.undotree}/share/vim-plugins/undotree" ]; + dirs = { + backupdir = "$HOME/.cache/vim/backup"; + swapdir = "$HOME/.cache/vim/swap"; + undodir = "$HOME/.cache/vim/undo"; + }; + files = { + viminfo = "$HOME/.cache/vim/info"; + }; + + mkdirs = let + dirOf = s: let out = concatStringsSep "/" (init (splitString "/" s)); + in assert out != ""; out; + alldirs = attrValues dirs ++ map dirOf (attrValues files); + in unique (sort lessThan alldirs); + + vim = pkgs.writeScriptBin "vim" '' + #! ${pkgs.dash}/bin/dash + set -f + umask 0077 + ${concatStringsSep "\n" (map (x: "mkdir -p ${x}") mkdirs)} + umask 0022 + exec ${pkgs.vim}/bin/vim "$@" + ''; + vimrc = pkgs.writeText "vimrc" '' set nocompatible set autoindent set backspace=indent,eol,start set backup - set backupdir=$HOME/.vim/backup/ - set directory=$HOME/.vim/cache// + set backupdir=${dirs.backupdir}/ + set directory=${dirs.swapdir}// set hlsearch set incsearch set mouse=a @@ -40,11 +64,11 @@ let set showcmd set showmatch set ttimeoutlen=0 - set undodir=$HOME/.vim/undo + set undodir=${dirs.undodir} set undofile set undolevels=1000000 set undoreload=1000000 - set viminfo='20,<1000,s100,h,n$HOME/.vim/cache/info + set viminfo='20,<1000,s100,h,n${files.viminfo} set visualbell set wildignore+=*.o,*.class,*.hi,*.dyn_hi,*.dyn_o set wildmenu diff --git a/tv/2configs/xserver/default.nix b/tv/2configs/xserver/default.nix index f56da7dcc..facde4e76 100644 --- a/tv/2configs/xserver/default.nix +++ b/tv/2configs/xserver/default.nix @@ -48,7 +48,7 @@ let "slock" ]; - systemd.services.display-manager = mkForce {}; + systemd.services.display-manager.enable = false; services.xserver.enable = true; |