Newer
Older
# 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;
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;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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}
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
'';
};
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}
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# 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 ''
set -eo pipefail
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