Skip to content
Snippets Groups Projects
tahoe.nix 12.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # 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;
    
      ini = pkgs.callPackage ../lib/ini.nix { };
    
        # 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"
        ];
    
    
        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 run). The node directory must come first,
    
                  # and arguments which alter Twisted's behavior come afterwards.
                  ExecStart = ''
    
                    ${settings.package}/bin/tahoe run ${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:
                nameValuePair "tahoe-lafs/${node}.cfg" {
                  mode = "0444";
                  text = ''
                    # This configuration is generated by Nix. Edit at your own
                    # peril; here be dragons.
    
    
                    ${ini.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.${lib.escapeShellArg 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/${lib.escapeShellArg node}";
    
                eliotLog = "file:${nodedir}/logs/eliot.json,rotate_length=${toString (1024 * 1024 * 32)},max_rotated_files=32";
    
              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 run). The node directory must come first,
    
                  # and arguments which alter Twisted's behavior come afterwards.
                  ExecStart = ''
    
                    ${settings.package}/bin/tahoe --eliot-destination ${eliotLog} run ${nodedir} -n -l- --pidfile=${pidfile}
    
                  # The rlimit on number of open files controls how many
                  # connections a particular storage server can accept (factoring
                  # in the number of non-connection files the server needs open -
                  # eg for logging, reading and writing shares, etc).
                  #
                  # Once the maximum number of open files, as controlled by rlimit
                  # is reached, service suffers dramatically. New connections
                  # cannot be accepted. Shares cannot be read or written.
                  #
                  # The default limit on open files is fairly low, perhaps 1024
                  # (2^10) or 8192 (2^13). This can easily be raised. If it is
                  # raised to 2^16 then the rlimit is approximately equal to the
                  # limit imposed by TCP (which only has around 2^16 ports
                  # available per IP address). If we want each connection to also
                  # be able to read or write a share file, a limit of 2^15 would
                  # allow this. Then, we should scale the limit linearly with the
                  # number of IP addresses available. If the service can be
                  # reached on 2 IP addresses, allow twice as many files (2^15 * 2
                  # = 2^16). If it can be reached on 3 IP addresses, (2^16 *
                  # 3). etc.
                  #
                  # Python also sometimes wants to open files as a side effect of
                  # other things going.  For example, if there's a traceback, it
                  # opens the source files to read lines to put into the
                  # traceback.  If random numbers are generated, /dev/urandom
                  # might be opened, etc.  There is also some fixed overhead for
                  # listening ports and such.  This currently doesn't factor into
                  # our choice but perhaps it could somehow.
                  #
    
                  # Tahoe-LAFS has no logic to raise soft limit to hard limit so
                  # make it the same.
                  #
    
                  # There is only one IPv4 address assigned to each host right
                  # now. So it makes sense to have the limit be 2^15 right now.
                  LimitNOFILE = 32768;
    
                preStart =
                let
                  created = "${nodedir}.created";
                  atomic = "${nodedir}.atomic";
                in ''
    
                  if [ ! -e ${created} ]; then
                    mkdir -p /var/db/tahoe-lafs/
    
                    # Get rid of any prior partial efforts.  It might not exist.
                    # Don't let this tank us.
    
                    rm -rv ${atomic} || [ ! -e ${atomic} ]
    
    
                    # Really create the node.
                    tahoe create-node --hostname=localhost ${atomic}
    
    
                    # Get rid of any existing partially created node directory
                    # that might be in the way.
                    if [ -e ${nodedir} ]; then
                      for backup in $(seq 1 100); do
                        if [ ! -e ${nodedir}.$backup ]; then
                          mv ${nodedir} ${nodedir}.$backup
                          break
                        fi
                      done
                    fi
    
                    # Move the new thing to the real location.  We don't create it
                    # in-place because we might fail partway through and leave
                    # inconsistent state.  Also, systemd probably created
                    # logs/incidents/ already and `create-node` complains if it
                    # finds these exist already.
    
                    mv ${atomic} ${nodedir}
    
    
                    # Record our complete, consistent success.
    
                  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 ${nodedir}/tahoe.cfg
    
                '';
              });
            users.users = flip mapAttrs' cfg.nodes (node: _:
              nameValuePair "tahoe.${node}" {
                description = "Tahoe node user for node ${node}";
                isSystemUser = true;
              });
          })
        ];
      }