From ba77c5c5c4df3c8e33a498c65f93bfe8c0ffd7f7 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Tue, 6 Aug 2019 15:46:15 -0400 Subject: [PATCH] begin modifying upstream tahoe service for our purposes --- nixos/modules/private-storage.nix | 27 +++- nixos/modules/tahoe.nix | 251 ++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 nixos/modules/tahoe.nix diff --git a/nixos/modules/private-storage.nix b/nixos/modules/private-storage.nix index cf8cbca1..31b55006 100644 --- a/nixos/modules/private-storage.nix +++ b/nixos/modules/private-storage.nix @@ -7,7 +7,22 @@ let }; cfg = config.services.private-storage; in -{ imports = [ ]; +{ + + # Upstream tahoe-lafs module conflicts with ours (since ours is a + # copy/paste/edit of upstream's...). Disable + # it. + # + # https://nixos.org/nixos/manual/#sec-replace-modules + disabledModules = + [ "services/network-filesystems/tahoe.nix" + ]; + + # Load our tahoe-lafs module. + imports = + [ ./tahoe.nix + ]; + options = { services.private-storage.enable = lib.mkEnableOption "private storage service"; services.private-storage.tahoe.package = lib.mkOption @@ -22,8 +37,14 @@ in config = lib.mkIf cfg.enable { services.tahoe.nodes."alpha" = { package = config.services.private-storage.tahoe.package; - nickname = "alpha"; - storage.enable = true; + sections = + { node = + { nickname = "alpha"; + }; + storage = + { enable = true; + }; + }; }; }; } diff --git a/nixos/modules/tahoe.nix b/nixos/modules/tahoe.nix new file mode 100644 index 00000000..aa48a59a --- /dev/null +++ b/nixos/modules/tahoe.nix @@ -0,0 +1,251 @@ +# Copy/pasted from nixos/modules/services/network-filesystems/tahoe.nix :/ We +# require control over additional configuration options compared to upstream +# and it's not clear how to do this without duplicating everything. +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.tahoe; +in + { + options.services.tahoe = { + introducers = mkOption { + default = {}; + type = with types; attrsOf (submodule { + options = { + nickname = mkOption { + type = types.str; + description = '' + The nickname of this Tahoe introducer. + ''; + }; + tub.port = mkOption { + default = 3458; + type = types.int; + description = '' + The port on which the introducer will listen. + ''; + }; + tub.location = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The external location that the introducer should listen on. + + If specified, the port should be included. + ''; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; + }; + }); + description = '' + The Tahoe introducers. + ''; + }; + nodes = mkOption { + default = {}; + type = with types; attrsOf (submodule { + options = { + sections = mkOption { + type = types.attrs; + description = '' + Top-level configuration sections. + ''; + default = { + "node" = { }; + "client" = { }; + "storage" = { }; + }; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; + }; + }); + description = '' + The Tahoe nodes. + ''; + }; + }; + config = mkMerge [ + (mkIf (cfg.introducers != {}) { + environment = { + etc = flip mapAttrs' cfg.introducers (node: settings: + nameValuePair "tahoe-lafs/introducer-${node}.cfg" { + mode = "0444"; + text = '' + # This configuration is generated by Nix. Edit at your own + # peril; here be dragons. + + [node] + nickname = ${settings.nickname} + tub.port = ${toString settings.tub.port} + ${optionalString (settings.tub.location != null) + "tub.location = ${settings.tub.location}"} + ''; + }); + # Actually require Tahoe, so that we will have it installed. + systemPackages = flip mapAttrsToList cfg.introducers (node: settings: + settings.package + ); + }; + # Open up the firewall. + # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers + # (node: settings: settings.tub.port); + systemd.services = flip mapAttrs' cfg.introducers (node: settings: + let + pidfile = "/run/tahoe.introducer-${node}.pid"; + # This is a directory, but it has no trailing slash. Tahoe commands + # get antsy when there's a trailing slash. + nodedir = "/var/db/tahoe-lafs/introducer-${node}"; + in nameValuePair "tahoe.introducer-${node}" { + description = "Tahoe LAFS node ${node}"; + wantedBy = [ "multi-user.target" ]; + path = [ settings.package ]; + restartTriggers = [ + config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ]; + serviceConfig = { + Type = "simple"; + PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe start). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} + ''; + }; + preStart = '' + if [ ! -d ${lib.escapeShellArg nodedir} ]; then + mkdir -p /var/db/tahoe-lafs + tahoe create-introducer ${lib.escapeShellArg nodedir} + fi + + # Tahoe has created a predefined tahoe.cfg which we must now + # scribble over. + # XXX I thought that a symlink would work here, but it doesn't, so + # we must do this on every prestart. Fixes welcome. + # rm ${nodedir}/tahoe.cfg + # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg + cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg + ''; + }); + users.users = flip mapAttrs' cfg.introducers (node: _: + nameValuePair "tahoe.introducer-${node}" { + description = "Tahoe node user for introducer ${node}"; + isSystemUser = true; + }); + }) + (mkIf (cfg.nodes != {}) { + environment = { + etc = flip mapAttrs' cfg.nodes (node: settings: + let + # Map a function over an attrset and concatenate the string results. + # + # concatMapAttrsToList (n: v: "${n} = ${v}\n") { a = "b"; c = "d"; } -> "a = b\nc = d\n" + concatMapAttrsToList = f: a: + lib.strings.concatStrings (lib.attrsets.mapAttrsToList f a); + + # Generate one line of configuration defining one item in one section. + # + # oneConfigItemText "foo" "bar" -> "foo = bar\n" + oneConfigItemText = name: value: + "${name} = ${builtins.toString value}\n"; + + # Generate all lines of configuration defining all items in one section. + # + # allConfigItemsText { foo = "bar"; baz = "quux"; } -> "foo = bar\nbaz = quux" + allConfigItemsText = items: + concatMapAttrsToList oneConfigItemText items; + + # Generate all lines of configuration for one section, header + # and items included. + # + # oneConfigSectionText "foo" { bar = "baz"; } -> "[foo]\nbar = baz\n" + oneConfigSectionText = name: value: '' + [${name}] + ${allConfigItemsText value} + ''; + + # Generate all lines of configuration for all sections, headers + # and items included. + # + # allConfigSectionsText { foo = { bar = "baz"; }; } -> "[foo]\nbar = baz\n" + allConfigSectionsText = sections: + concatMapAttrsToList oneConfigSectionText sections; + in + nameValuePair "tahoe-lafs/${node}.cfg" { + mode = "0444"; + text = '' + # This configuration is generated by Nix. Edit at your own + # peril; here be dragons. + + ${allConfigSectionsText settings.sections} + ''; + }); + # Actually require Tahoe, so that we will have it installed. + systemPackages = flip mapAttrsToList cfg.nodes (node: settings: + settings.package + ); + }; + # Open up the firewall. + # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes + # (node: settings: settings.tub.port); + systemd.services = flip mapAttrs' cfg.nodes (node: settings: + let + pidfile = "/run/tahoe.${node}.pid"; + # This is a directory, but it has no trailing slash. Tahoe commands + # get antsy when there's a trailing slash. + nodedir = "/var/db/tahoe-lafs/${node}"; + in nameValuePair "tahoe.${node}" { + description = "Tahoe LAFS node ${node}"; + wantedBy = [ "multi-user.target" ]; + path = [ settings.package ]; + restartTriggers = [ + config.environment.etc."tahoe-lafs/${node}.cfg".source ]; + serviceConfig = { + Type = "simple"; + PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe start). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} + ''; + }; + preStart = '' + if [ ! -d ${lib.escapeShellArg nodedir} ]; then + mkdir -p /var/db/tahoe-lafs + tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir} + fi + + # Tahoe has created a predefined tahoe.cfg which we must now + # scribble over. + # XXX I thought that a symlink would work here, but it doesn't, so + # we must do this on every prestart. Fixes welcome. + # rm ${nodedir}/tahoe.cfg + # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg + cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg + ''; + }); + users.users = flip mapAttrs' cfg.nodes (node: _: + nameValuePair "tahoe.${node}" { + description = "Tahoe node user for node ${node}"; + isSystemUser = true; + }); + }) + ]; + } -- GitLab