+25
-25
flake.lock
+25
-25
flake.lock
···
26
26
"nixpkgs": "nixpkgs_2"
27
27
},
28
28
"locked": {
29
-
"lastModified": 1751021896,
30
-
"narHash": "sha256-L9u68mNPPiuW7+OV5BKbXaj/AENTiiuEx8+QnMBjRlU=",
29
+
"lastModified": 1751705516,
30
+
"narHash": "sha256-Y099OGYWYHtpYFP4offuV6rldBnpUv4CYk+HwuaQwLU=",
31
31
"owner": "catppuccin",
32
32
"repo": "nix",
33
-
"rev": "a6b0e34d083c79f08efabb1fd6ccf12b882daae6",
33
+
"rev": "719bb50ca2c99bc9c077669a48bfd9815493a11d",
34
34
"type": "github"
35
35
},
36
36
"original": {
···
158
158
]
159
159
},
160
160
"locked": {
161
-
"lastModified": 1751384836,
162
-
"narHash": "sha256-7xRbl/VLXxE5DzJmk1wdKWJmPx8rAfNC/a6mXtqp5cc=",
161
+
"lastModified": 1751824240,
162
+
"narHash": "sha256-aDDC0CHTlL7QDKWWhdbEgVPK6KwWt+ca0QkmHYZxMzI=",
163
163
"owner": "nix-community",
164
164
"repo": "home-manager",
165
-
"rev": "479f8889675770881033878a1c114fbfc6de7a4d",
165
+
"rev": "fd9e55f5fac45a26f6169310afca64d56b681935",
166
166
"type": "github"
167
167
},
168
168
"original": {
···
236
236
"spectrum": "spectrum"
237
237
},
238
238
"locked": {
239
-
"lastModified": 1750358184,
240
-
"narHash": "sha256-17EYMeY5v8KRk9HW6Z4dExY8Wg4y/zM2eM2wbbx+vMs=",
239
+
"lastModified": 1751732733,
240
+
"narHash": "sha256-MuaFFGHdShvGdHKrd3PUI2om+njixdG/1dGlglRdK8Q=",
241
241
"owner": "astro",
242
242
"repo": "microvm.nix",
243
-
"rev": "fd9f5dba1ffee5ad6f29394b2a9e4c66c1ce77dc",
243
+
"rev": "9d3d845ccb1a3f81747d027e95b110d4637468d0",
244
244
"type": "github"
245
245
},
246
246
"original": {
···
251
251
},
252
252
"nixos-hardware": {
253
253
"locked": {
254
-
"lastModified": 1751393906,
255
-
"narHash": "sha256-I1x6K61ZcdFlqc07weRBy3erCAB0lVkX10i0c9eXjDI=",
254
+
"lastModified": 1751432711,
255
+
"narHash": "sha256-136MeWtckSHTN9Z2WRNRdZ8oRP3vyx3L8UxeBYE+J9w=",
256
256
"owner": "nixos",
257
257
"repo": "nixos-hardware",
258
-
"rev": "f49bb3b4107a0917ee144337bb02d311033ee1ba",
258
+
"rev": "497ae1357f1ac97f1aea31a4cb74ad0d534ef41f",
259
259
"type": "github"
260
260
},
261
261
"original": {
···
283
283
},
284
284
"nixpkgs_2": {
285
285
"locked": {
286
-
"lastModified": 1744463964,
287
-
"narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
286
+
"lastModified": 1750776420,
287
+
"narHash": "sha256-/CG+w0o0oJ5itVklOoLbdn2dGB0wbZVOoDm4np6w09A=",
288
288
"owner": "NixOS",
289
289
"repo": "nixpkgs",
290
-
"rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
290
+
"rev": "30a61f056ac492e3b7cdcb69c1e6abdcf00e39cf",
291
291
"type": "github"
292
292
},
293
293
"original": {
···
299
299
},
300
300
"nixpkgs_3": {
301
301
"locked": {
302
-
"lastModified": 1751271578,
303
-
"narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=",
302
+
"lastModified": 1751637120,
303
+
"narHash": "sha256-xVNy/XopSfIG9c46nRmPaKfH1Gn/56vQ8++xWA8itO4=",
304
304
"owner": "nixos",
305
305
"repo": "nixpkgs",
306
-
"rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df",
306
+
"rev": "5c724ed1388e53cc231ed98330a60eb2f7be4be3",
307
307
"type": "github"
308
308
},
309
309
"original": {
···
343
343
"spectrum": {
344
344
"flake": false,
345
345
"locked": {
346
-
"lastModified": 1746869549,
347
-
"narHash": "sha256-BKZ/yZO/qeLKh9YqVkKB6wJiDQJAZNN5rk5NsMImsWs=",
346
+
"lastModified": 1751265943,
347
+
"narHash": "sha256-XoHSo6GEElzRUOYAEg/jlh5c8TDsyDESFIux3nU/NMc=",
348
348
"ref": "refs/heads/main",
349
-
"rev": "d927e78530892ec8ed389e8fae5f38abee00ad87",
350
-
"revCount": 862,
349
+
"rev": "37c8663fab86fdb202fece339ef7ac7177ffc201",
350
+
"revCount": 904,
351
351
"type": "git",
352
352
"url": "https://spectrum-os.org/git/spectrum"
353
353
},
···
443
443
]
444
444
},
445
445
"locked": {
446
-
"lastModified": 1751383329,
447
-
"narHash": "sha256-52dUY8jEkuXEIZINYb+AVsrmw6FxMhBAG3K9J/2qiSo=",
446
+
"lastModified": 1751779188,
447
+
"narHash": "sha256-o1PidAPLtSSqI6isos6v/e6s7t3zQ56NBYhXVaUesXc=",
448
448
"owner": "0xc000022070",
449
449
"repo": "zen-browser-flake",
450
-
"rev": "f29a4fece3b76c3e4579d67e2cf0cb8037f6a351",
450
+
"rev": "b3200f40877a3e0a679070d96f8793a06105c06e",
451
451
"type": "github"
452
452
},
453
453
"original": {
+1
-1
home/regent/home.nix
+1
-1
home/regent/home.nix
+15
hosts/buer/default.nix
+15
hosts/buer/default.nix
···
32
32
# CUSTOM MODULES
33
33
# =============================================================================
34
34
modules.garage.enable = true;
35
+
modules.seaweedfs.clusters.default = {
36
+
package = pkgs.seaweedfs;
37
+
38
+
masters.main = {
39
+
openFirewall = true;
40
+
ip = "fs.nkp.pet";
41
+
volumePreallocate = true;
42
+
43
+
defaultReplication = {
44
+
dataCenter = 0;
45
+
rack = 0;
46
+
server = 0;
47
+
};
48
+
};
49
+
};
35
50
36
51
# =============================================================================
37
52
# BOOT CONFIGURATION
+2
hosts/focalor/default.nix
+2
hosts/focalor/default.nix
+858
modules/seaweedfs/default.nix
+858
modules/seaweedfs/default.nix
···
1
+
/*https://hg.sr.ht/~dermetfan/seaweedfs-nixos/browse/seaweedfs.nix?rev=tip*/
2
+
3
+
{ config, lib, pkgs, ... }:
4
+
5
+
with lib;
6
+
7
+
let
8
+
cfg = config.modules.seaweedfs;
9
+
10
+
clusterModule = cluster: {
11
+
options = {
12
+
package = mkOption {
13
+
type = types.package;
14
+
default = pkgs.seaweedfs;
15
+
};
16
+
17
+
security.grpc = let
18
+
auth = mkOption {
19
+
type = with types; nullOr (submodule {
20
+
options = {
21
+
cert = mkOption { type = path; };
22
+
key = mkOption { type = path; };
23
+
};
24
+
});
25
+
default = null;
26
+
};
27
+
in {
28
+
ca = mkOption {
29
+
type = with types; nullOr str;
30
+
default = null;
31
+
};
32
+
33
+
master = auth;
34
+
volume = auth;
35
+
filer = auth;
36
+
client = auth;
37
+
msgBroker = auth;
38
+
};
39
+
40
+
masters = mkOption {
41
+
type = with types; attrsOf (submodule (masterModule cluster.config));
42
+
default = {};
43
+
description = "SeaweedFS masters";
44
+
};
45
+
46
+
volumes = mkOption {
47
+
type = with types; attrsOf (submodule (volumeModule cluster.config));
48
+
default = {};
49
+
description = "SeaweedFS volumes";
50
+
};
51
+
52
+
filers = mkOption {
53
+
type = with types; attrsOf (submodule (filerModule cluster.config));
54
+
default = {};
55
+
description = "SeaweedFS filers";
56
+
};
57
+
58
+
webdavs = mkOption {
59
+
type = with types; attrsOf (submodule (webdavModule cluster.config));
60
+
default = {};
61
+
description = "SeaweedFS WebDAV servers";
62
+
};
63
+
64
+
instances = mkOption {
65
+
type = with types; attrsOf (submodule instanceModule);
66
+
description = "SeaweedFS instances";
67
+
default =
68
+
mapAttrs' (name: master: nameValuePair
69
+
"master-${name}"
70
+
{
71
+
inherit (master) cluster configs;
72
+
73
+
command = "master";
74
+
75
+
args = with master;
76
+
[
77
+
"-port=${toString port}"
78
+
"-volumeSizeLimitMB=${toString volumeSizeLimitMB}"
79
+
] ++
80
+
optional (cpuprofile != "") "-cpuprofile=${cpuprofile}" ++
81
+
optional (defaultReplication != null) ("-defaultReplication=${defaultReplication.code}") ++
82
+
optional disableHttp "-disableHttp" ++
83
+
optional (garbageThreshold != "") "-garbageThreshold=${garbageThreshold}" ++
84
+
optional (ip != "") "-ip=${ip}" ++
85
+
optional (master."ip.bind" != "") "-ip.bind=${master."ip.bind"}" ++
86
+
optional (mdir != "") "-mdir=${mdir}" ++
87
+
optional (memprofile != "") "-memprofile=${memprofile}" ++
88
+
optional metrics.enable "-metrics.address=${metrics.address.text}" ++
89
+
optional (metrics.intervalSeconds != null) "-metrics.intervalSeconds=${toString metrics.intervalSeconds}" ++
90
+
optional (peers != []) ("-peers=" + (concatStringsSep "," (map (peer: peer.text) peers))) ++
91
+
optional resumeState "-resumeState" ++
92
+
optional volumePreallocate "-volumePreallocate" ++
93
+
optional (whiteList != []) ("-whiteList=" + (concatStringsSep "," whiteList));
94
+
}
95
+
) cluster.config.masters //
96
+
mapAttrs' (name: volume: nameValuePair
97
+
"volume-${name}"
98
+
{
99
+
inherit (volume) cluster configs;
100
+
101
+
command = "volume";
102
+
103
+
args = with volume;
104
+
[
105
+
"-port=${toString port}"
106
+
"-dir=${concatStringsSep "," dir}"
107
+
"-fileSizeLimitMB=${toString fileSizeLimitMB}"
108
+
"-idleTimeout=${toString idleTimeout}"
109
+
"-index=${index}"
110
+
"-minFreeSpacePercent=${toString minFreeSpacePercent}"
111
+
"-preStopSeconds=${toString preStopSeconds}"
112
+
] ++
113
+
optional (compactionMBps != null) ("-compactionMBps=${compactionMBps}") ++
114
+
optional (cpuprofile != "") "-cpuprofile=${cpuprofile}" ++
115
+
optional (dataCenter != "") "-dataCenter=${dataCenter}" ++
116
+
optional volume."images.fix.orientation" "-images.fix.orientation" ++
117
+
optional (ip != "") "-ip=${ip}" ++
118
+
optional (volume."ip.bind" != "") "-ip.bind=${volume."ip.bind"}" ++
119
+
optional (max != []) "-max=${concatStringsSep "," (map toString max)}" ++
120
+
optional (memprofile != "") "-memprofile=${memprofile}" ++
121
+
optional (metricsPort != null) "-metricsPort=${toString metricsPort}" ++
122
+
optional (mserver != []) ("-mserver=" + (concatStringsSep "," (map (mserver: mserver.text) mserver))) ++
123
+
optional (volume."port.public" != null) "-port.public=${toString volume."port.public"}" ++
124
+
optional pprof "-pprof" ++
125
+
optional (publicUrl != "") "-publicUrl=${publicUrl}" ++
126
+
optional (rack != "") "-rack=${rack}" ++
127
+
optional (!volume."read.redirect") "-read.redirect=false" ++
128
+
optional (whiteList != []) ("-whiteList=" + (concatStringsSep "," whiteList));
129
+
130
+
systemdService.preStart = "mkdir -p ${concatStringsSep " " volume.dir}";
131
+
}
132
+
) cluster.config.volumes //
133
+
mapAttrs' (name: filer: nameValuePair
134
+
"filer-${name}"
135
+
{
136
+
inherit (filer) cluster configs;
137
+
138
+
command = "filer";
139
+
140
+
args = with filer;
141
+
[
142
+
"-port=${toString port}"
143
+
"-dirListLimit=${toString dirListLimit}"
144
+
"-maxMB=${toString maxMB}"
145
+
] ++
146
+
optional (collection != "") "-collection=${collection}" ++
147
+
optional (dataCenter != "") "-dataCenter=${dataCenter}" ++
148
+
optional (defaultReplicaPlacement != null) ("-defaultReplicaPlacement=${defaultReplicaPlacement.code}") ++
149
+
optional disableDirListing "-disableDirListing" ++
150
+
optional disableHttp "-disableHttp" ++
151
+
optional encryptVolumeData "-encryptVolumeData" ++
152
+
optional (ip != "") "-ip=${ip}" ++
153
+
optional (filer."ip.bind" != "") "-ip.bind=${filer."ip.bind"}" ++
154
+
optional (master != []) ("-master=" + (concatStringsSep "," (map (master: master.text) master))) ++
155
+
optional (metricsPort != null) "-metricsPort=${toString metricsPort}" ++
156
+
optional (peers != []) ("-peers=" + (concatStringsSep "," (map (peer: peer.text) peers))) ++
157
+
optional (filer."port.readonly" != null) "-port.readonly=${toString filer."port.readonly"}" ++
158
+
optional (rack != "") "-rack=${rack}" ++
159
+
optionals s3.enable [
160
+
"-s3"
161
+
"-s3.port=${toString filer.s3.port}"
162
+
] ++
163
+
optional (s3.enable && s3."cert.file" != "") "-s3.cert.file=${s3."cert.file"}" ++
164
+
optional (s3.enable && s3."key.file" != "") "-s3.key.file=${s3."key.file"}" ++
165
+
optional (s3.enable && s3.config != "") "-s3.config=${s3.config}" ++
166
+
optional (s3.enable && s3.domainName != []) "-s3.domainName=${concatStringsSep "," s3.domainName}";
167
+
168
+
systemdService.preStart = let
169
+
conf = filer.configs.filer.leveldb2 or {};
170
+
in optionalString (conf ? "dir") "mkdir -p ${conf.dir}";
171
+
}
172
+
) cluster.config.filers //
173
+
mapAttrs' (name: webdav: nameValuePair
174
+
"webdav-${name}"
175
+
{
176
+
inherit (webdav) cluster;
177
+
178
+
command = "webdav";
179
+
180
+
args = with webdav;
181
+
[
182
+
"-port=${toString port}"
183
+
"-filer=${filer.text}"
184
+
"-cacheCapacityMB=${toString cacheCapacityMB}"
185
+
] ++
186
+
optional (collection != "") "-collection=${collection}" ++
187
+
optional (cacheDir != "") "-cacheDir=${cacheDir}";
188
+
}
189
+
) cluster.config.webdavs;
190
+
};
191
+
};
192
+
};
193
+
194
+
commonModule = cluster: common: {
195
+
options = {
196
+
cluster = mkOption {
197
+
type = types.submodule clusterModule;
198
+
internal = true;
199
+
};
200
+
201
+
openFirewall = mkEnableOption "open the firewall";
202
+
};
203
+
204
+
config = { inherit cluster; };
205
+
};
206
+
207
+
masterModule = cluster: master: {
208
+
imports = [ (commonModule cluster) ];
209
+
210
+
options = {
211
+
configs = mkOption {
212
+
type = with types; attrsOf attrs;
213
+
default.master.maintenance = {
214
+
scripts = ''
215
+
ec.encode -fullPercent=95 -quietFor=1h
216
+
ec.rebuild -force
217
+
ec.balance -force
218
+
volume.balance -force
219
+
volume.fix.replication
220
+
'';
221
+
sleep_minutes = 17;
222
+
};
223
+
};
224
+
225
+
cpuprofile = mkOption {
226
+
type = types.str;
227
+
default = "";
228
+
};
229
+
230
+
defaultReplication = mkOption {
231
+
type = types.submodule replicationModule;
232
+
default = {};
233
+
};
234
+
235
+
disableHttp = mkEnableOption "disable HTTP requests, gRPC only";
236
+
237
+
garbageThreshold = mkOption {
238
+
type = types.str;
239
+
default = "";
240
+
};
241
+
242
+
ip = mkOption {
243
+
type = types.str;
244
+
default = config.networking.hostName;
245
+
};
246
+
247
+
"ip.bind" = mkOption {
248
+
type = types.str;
249
+
default = "0.0.0.0";
250
+
};
251
+
252
+
mdir = mkOption {
253
+
type = types.str;
254
+
default = ".";
255
+
};
256
+
257
+
memprofile = mkOption {
258
+
type = types.str;
259
+
default = "";
260
+
};
261
+
262
+
metrics = {
263
+
enable = mkEnableOption "Prometheus";
264
+
265
+
address = mkOption {
266
+
type = types.submodule ipPortModule;
267
+
default = {};
268
+
};
269
+
270
+
intervalSeconds = mkOption {
271
+
type = types.ints.unsigned;
272
+
default = 15;
273
+
};
274
+
};
275
+
276
+
peers = mkOption {
277
+
type = peersType;
278
+
default = mapAttrsIpPort master.config.cluster.masters;
279
+
};
280
+
281
+
port = mkOption {
282
+
type = types.port;
283
+
default = 9333;
284
+
};
285
+
286
+
resumeState = mkEnableOption "resume previous state on master server";
287
+
288
+
volumePreallocate = mkEnableOption "preallocate disk space for volumes";
289
+
290
+
volumeSizeLimitMB = mkOption {
291
+
type = types.ints.unsigned;
292
+
default = 30000;
293
+
};
294
+
295
+
whiteList = mkOption {
296
+
type = with types; listOf str;
297
+
default = [];
298
+
};
299
+
};
300
+
};
301
+
302
+
volumeModule = cluster: volume: {
303
+
imports = [ (commonModule cluster) ];
304
+
305
+
options = {
306
+
configs = mkOption {
307
+
type = with types; attrsOf attrs;
308
+
default = {};
309
+
};
310
+
311
+
compactionMBps = mkOption {
312
+
type = with types; nullOr ints.unsigned;
313
+
default = null;
314
+
};
315
+
316
+
cpuprofile = mkOption {
317
+
type = types.str;
318
+
default = "";
319
+
};
320
+
321
+
dataCenter = mkOption {
322
+
type = types.str;
323
+
default = "";
324
+
};
325
+
326
+
dir = mkOption {
327
+
type = with types; listOf str;
328
+
default = [ "/var/lib/seaweedfs/${cluster._module.args.name}/volume-${volume.config._module.args.name}" ];
329
+
};
330
+
331
+
fileSizeLimitMB = mkOption {
332
+
type = types.ints.unsigned;
333
+
default = 256;
334
+
};
335
+
336
+
idleTimeout = mkOption{
337
+
type = types.ints.unsigned;
338
+
default = 30;
339
+
};
340
+
341
+
"images.fix.orientation" = mkEnableOption "adjustment of jpg orientation when uploading";
342
+
343
+
index = mkOption {
344
+
type = types.enum [
345
+
"memory"
346
+
"leveldb"
347
+
"leveldbMedium"
348
+
"leveldbLarge"
349
+
];
350
+
default = "memory";
351
+
};
352
+
353
+
ip = mkOption {
354
+
type = types.str;
355
+
default = config.networking.hostName;
356
+
};
357
+
358
+
"ip.bind" = mkOption {
359
+
type = types.str;
360
+
default = "0.0.0.0";
361
+
};
362
+
363
+
max = mkOption {
364
+
type = with types; listOf ints.unsigned;
365
+
default = [ 8 ];
366
+
};
367
+
368
+
memprofile = mkOption {
369
+
type = types.str;
370
+
default = "";
371
+
};
372
+
373
+
metricsPort = mkOption {
374
+
type = with types; nullOr port;
375
+
default = null;
376
+
};
377
+
378
+
minFreeSpacePercent = mkOption {
379
+
type = types.ints.unsigned;
380
+
default = 1;
381
+
};
382
+
383
+
mserver = mkOption {
384
+
type = peersType;
385
+
default = mapAttrsIpPort volume.config.cluster.masters;
386
+
};
387
+
388
+
port = mkOption {
389
+
type = types.port;
390
+
default = 8080;
391
+
};
392
+
393
+
"port.public" = mkOption {
394
+
type = with types; nullOr port;
395
+
default = null;
396
+
};
397
+
398
+
pprof = mkEnableOption "pprof http handlers. precludes -memprofile and -cpuprofile";
399
+
400
+
preStopSeconds = mkOption {
401
+
type = types.int;
402
+
default = 10;
403
+
};
404
+
405
+
publicUrl = mkOption {
406
+
type = types.str;
407
+
default = "";
408
+
};
409
+
410
+
rack = mkOption {
411
+
type = types.str;
412
+
default = "";
413
+
};
414
+
415
+
"read.redirect" = mkOption {
416
+
type = types.bool;
417
+
default = true;
418
+
};
419
+
420
+
whiteList = mkOption {
421
+
type = with types; listOf str;
422
+
default = [];
423
+
};
424
+
};
425
+
};
426
+
427
+
filerModule = cluster: filer: {
428
+
imports = [ (commonModule cluster) ];
429
+
430
+
options = {
431
+
configs = mkOption {
432
+
type = with types; attrsOf attrs;
433
+
default.filer.leveldb2 = {
434
+
enabled = true;
435
+
dir = "/var/lib/seaweedfs/${cluster._module.args.name}/filer-${filer.config._module.args.name}/filerldb2";
436
+
};
437
+
};
438
+
439
+
collection = mkOption {
440
+
type = types.str;
441
+
default = "";
442
+
};
443
+
444
+
dataCenter = mkOption {
445
+
type = types.str;
446
+
default = "";
447
+
};
448
+
449
+
defaultReplicaPlacement = mkOption {
450
+
type = with types; nullOr (submodule replicationModule);
451
+
default = null;
452
+
};
453
+
454
+
dirListLimit = mkOption {
455
+
type = types.ints.unsigned;
456
+
default = 100000;
457
+
};
458
+
459
+
disableDirListing = mkEnableOption "turn off directory listing";
460
+
461
+
disableHttp = mkEnableOption "disable http request, only gRpc operations are allowed";
462
+
463
+
encryptVolumeData = mkEnableOption "encrypt data on volume servers";
464
+
465
+
ip = mkOption {
466
+
type = types.str;
467
+
default = config.networking.hostName;
468
+
};
469
+
470
+
"ip.bind" = mkOption {
471
+
type = types.str;
472
+
default = "0.0.0.0";
473
+
};
474
+
475
+
master = mkOption {
476
+
type = peersType;
477
+
default = mapAttrsIpPort filer.config.cluster.masters;
478
+
};
479
+
480
+
maxMB = mkOption {
481
+
type = types.ints.unsigned;
482
+
default = 32;
483
+
};
484
+
485
+
metricsPort = mkOption {
486
+
type = with types; nullOr port;
487
+
default = null;
488
+
};
489
+
490
+
peers = mkOption {
491
+
type = peersType;
492
+
default = mapAttrsIpPort filer.config.cluster.filers;
493
+
};
494
+
495
+
port = mkOption {
496
+
type = types.port;
497
+
default = 8888;
498
+
};
499
+
500
+
"port.readonly" = mkOption {
501
+
type = with types; nullOr port;
502
+
default = null;
503
+
};
504
+
505
+
rack = mkOption {
506
+
type = types.str;
507
+
default = "";
508
+
};
509
+
510
+
s3 = {
511
+
enable = mkEnableOption "whether to start S3 gateway";
512
+
513
+
"cert.file" = mkOption {
514
+
type = types.path;
515
+
default = "";
516
+
};
517
+
518
+
config = mkOption {
519
+
type = types.path;
520
+
default = "";
521
+
};
522
+
523
+
domainName = mkOption {
524
+
type = with types; listOf str;
525
+
default = [];
526
+
};
527
+
528
+
"key.file" = mkOption {
529
+
type = types.path;
530
+
default = "";
531
+
};
532
+
533
+
port = mkOption {
534
+
type = types.port;
535
+
default = 8333;
536
+
};
537
+
};
538
+
};
539
+
};
540
+
541
+
webdavModule = cluster: webdav: {
542
+
imports = [ (commonModule cluster) ];
543
+
544
+
options = {
545
+
cacheCapacityMB = mkOption {
546
+
type = types.int;
547
+
default = 1000;
548
+
};
549
+
550
+
cacheDir = mkOption {
551
+
type = types.str;
552
+
default = ".";
553
+
};
554
+
555
+
collection = mkOption {
556
+
type = types.str;
557
+
default = "";
558
+
};
559
+
560
+
filer = mkOption {
561
+
type = types.submodule ipPortModule;
562
+
default = {
563
+
ip = "127.0.0.1";
564
+
port = 8888;
565
+
};
566
+
};
567
+
568
+
port = mkOption {
569
+
type = types.port;
570
+
default = 7333;
571
+
};
572
+
};
573
+
};
574
+
575
+
instanceModule = instance: {
576
+
options = {
577
+
cluster = mkOption {
578
+
type = types.submodule clusterModule;
579
+
internal = true;
580
+
};
581
+
582
+
command = mkOption {
583
+
type = types.enum [
584
+
"server"
585
+
"master"
586
+
"volume"
587
+
"mount"
588
+
"filer"
589
+
"filer.replicate"
590
+
"filer.sync"
591
+
"s3"
592
+
"msgBroker"
593
+
"watch"
594
+
"webdav"
595
+
];
596
+
};
597
+
598
+
logArgs = mkOption {
599
+
type = with types; listOf str;
600
+
default = [];
601
+
};
602
+
603
+
args = mkOption {
604
+
type = with types; listOf str;
605
+
default = [];
606
+
};
607
+
608
+
configs = mkOption {
609
+
type = with types; attrsOf attrs;
610
+
default = {};
611
+
};
612
+
613
+
package = mkOption {
614
+
type = types.package;
615
+
default = instance.config.cluster.package;
616
+
};
617
+
618
+
systemdService = mkOption {
619
+
type = types.attrs;
620
+
default = {};
621
+
};
622
+
};
623
+
624
+
config = {
625
+
logArgs = [ "-logtostderr" ];
626
+
627
+
systemdService.path = optional (instance.config.command == "mount") pkgs.fuse;
628
+
};
629
+
};
630
+
631
+
replicationModule = replication: {
632
+
options = {
633
+
dataCenter = mkOption {
634
+
type = types.ints.between 0 9;
635
+
default = 0;
636
+
};
637
+
638
+
rack = mkOption {
639
+
type = types.ints.between 0 9;
640
+
default = 0;
641
+
};
642
+
643
+
server = mkOption {
644
+
type = types.ints.between 0 9;
645
+
default = 0;
646
+
};
647
+
648
+
code = mkOption {
649
+
readOnly = true;
650
+
internal = true;
651
+
type = types.str;
652
+
default = with replication.config; "${toString dataCenter}${toString rack}${toString server}";
653
+
};
654
+
};
655
+
};
656
+
657
+
peersType = with types; listOf (submodule ipPortModule);
658
+
659
+
ipPortModule = ipPort: {
660
+
options = {
661
+
ip = mkOption {
662
+
type = types.str;
663
+
};
664
+
665
+
port = mkOption {
666
+
type = types.port;
667
+
};
668
+
669
+
text = mkOption {
670
+
internal = true;
671
+
readOnly = true;
672
+
type = types.str;
673
+
default = with ipPort.config; "${ip}:${toString port}";
674
+
};
675
+
};
676
+
};
677
+
678
+
mapAttrsIpPort = attrs: mapAttrsToList (name: value: { inherit (value) ip port; }) attrs;
679
+
680
+
toTOML = with generators; toINI {
681
+
mkKeyValue = mkKeyValueDefault {
682
+
mkValueString = v:
683
+
if isString v
684
+
then (
685
+
if hasInfix "\n" v
686
+
then ''
687
+
"""
688
+
${removeSuffix "\n" v}
689
+
"""
690
+
''
691
+
else ''"${v}"''
692
+
)
693
+
else mkValueStringDefault {} v;
694
+
} "=";
695
+
};
696
+
697
+
flattenAttrs = separator: attrs: let
698
+
/*
699
+
attrs = {
700
+
a = {
701
+
m1 = {};
702
+
m2 = {};
703
+
};
704
+
b = {
705
+
m1 = {};
706
+
};
707
+
}
708
+
*/
709
+
710
+
/*
711
+
step1 = {
712
+
a = [
713
+
{ name = "a-m1"; value = {}; }
714
+
{ name = "a-m2"; value = {}; }
715
+
];
716
+
b = [
717
+
{ name = "b-m1"; value = {}; }
718
+
];
719
+
};
720
+
*/
721
+
step1 = mapAttrs (outerName: outerValues:
722
+
mapAttrsToList (innerName: innerValues: nameValuePair
723
+
"${outerName}${separator}${innerName}"
724
+
innerValues
725
+
) outerValues
726
+
) attrs;
727
+
728
+
/*
729
+
step2 = [
730
+
[
731
+
{ name = "a-m1"; value = {}; }
732
+
{ name = "a-m2"; value = {}; }
733
+
]
734
+
[
735
+
{ name = "b-m1"; value = {}; }
736
+
]
737
+
];
738
+
*/
739
+
step2 = mapAttrsToList (name: value: value) step1;
740
+
741
+
/*
742
+
step3 = [
743
+
{ name = "a-m1"; value = {}; }
744
+
{ name = "a-m2"; value = {}; }
745
+
{ name = "b-m1"; value = {}; }
746
+
];
747
+
*/
748
+
step3 = flatten step2;
749
+
in
750
+
/*
751
+
{
752
+
a-m1 = {};
753
+
a-m2 = {};
754
+
b-m1 = {};
755
+
};
756
+
*/
757
+
builtins.listToAttrs step3;
758
+
in {
759
+
options.modules.seaweedfs = {
760
+
clusters = mkOption {
761
+
type = with types; attrsOf (submodule clusterModule);
762
+
default = {};
763
+
description = "SeaweedFS clusters";
764
+
};
765
+
};
766
+
767
+
config = {
768
+
systemd.services = mapAttrs'
769
+
(name: instance: nameValuePair "seaweedfs-${name}" instance)
770
+
(flattenAttrs "-" (
771
+
mapAttrs (clusterName: cluster:
772
+
mapAttrs (instanceName: instance: with instance; recursiveUpdate systemdService rec {
773
+
description = "SeaweedFS ${clusterName} ${instanceName}";
774
+
wants = [ "network.target" ];
775
+
after = wants;
776
+
wantedBy = [ "multi-user.target" ];
777
+
preStart = with serviceConfig; ''
778
+
${
779
+
let securityFile = config.environment.etc."seaweedfs/${clusterName}/security.toml";
780
+
in optionalString securityFile.enable "ln -s /etc/${securityFile.target} ${WorkingDirectory}/"
781
+
}
782
+
783
+
# TODO replace find usage with statically known condition
784
+
find -L /etc/${ConfigurationDirectory} -type f -exec ln -s '{}' ${WorkingDirectory}/ \;
785
+
786
+
${optionalString (systemdService ? preStart) systemdService.preStart}
787
+
'';
788
+
serviceConfig = rec {
789
+
ExecStart = "${package}/bin/weed ${concatStringsSep " " logArgs} ${command} ${concatStringsSep " " args}";
790
+
Restart = "on-failure";
791
+
Type = "exec";
792
+
ConfigurationDirectory = "seaweedfs/${clusterName}/${instanceName}";
793
+
RuntimeDirectory = ConfigurationDirectory;
794
+
RuntimeDirectoryPreserve = "restart";
795
+
WorkingDirectory = "/run/${RuntimeDirectory}";
796
+
};
797
+
}) cluster.instances
798
+
) cfg.clusters
799
+
));
800
+
801
+
environment.etc =
802
+
(mapAttrs' (name: cluster:
803
+
let file = "seaweedfs/${name}/security.toml";
804
+
in nameValuePair file {
805
+
enable = config.environment.etc.${file}.text != "";
806
+
text = with cluster.security.grpc; toTOML (
807
+
(if ca == null then {} else { grpc.ca = ca; }) //
808
+
(if master == null then {} else { "grpc.master" = { inherit (master) cert key; }; }) //
809
+
(if volume == null then {} else { "grpc.volume" = { inherit (volume) cert key; }; }) //
810
+
(if filer == null then {} else { "grpc.filer" = { inherit (filer) cert key; }; }) //
811
+
(if client == null then {} else { "grpc.client" = { inherit (client) cert key; }; }) //
812
+
(if msgBroker == null then {} else { "grpc.msg_broker" = { inherit (msgBroker) cert key; }; })
813
+
);
814
+
}
815
+
) cfg.clusters) //
816
+
(mapAttrs'
817
+
(name: config: nameValuePair
818
+
"seaweedfs/${name}.toml"
819
+
{ text = toTOML config; }
820
+
)
821
+
(flattenAttrs "/" (
822
+
mapAttrs (clusterName: cluster:
823
+
flattenAttrs "/" (
824
+
mapAttrs
825
+
(instanceName: instance: instance.configs)
826
+
cluster.instances
827
+
)
828
+
) cfg.clusters
829
+
))
830
+
);
831
+
832
+
networking.firewall.allowedTCPPorts = let
833
+
modulesToPorts = extraPorts: mapAttrsToList (name: module:
834
+
with module;
835
+
optionals openFirewall (
836
+
[ port (port + 10000) ] ++
837
+
(filter (p: p != null) (extraPorts module))
838
+
)
839
+
);
840
+
in flatten (mapAttrsToList (clusterName: cluster:
841
+
modulesToPorts
842
+
(master: [])
843
+
cluster.masters ++
844
+
845
+
modulesToPorts
846
+
(volume: with volume; [ metricsPort volume."port.public" ])
847
+
cluster.volumes ++
848
+
849
+
modulesToPorts
850
+
(filer: with filer; [ metricsPort filer."port.readonly" s3.port])
851
+
cluster.filers ++
852
+
853
+
modulesToPorts
854
+
(webdav: [])
855
+
cluster.webdavs
856
+
) cfg.clusters);
857
+
};
858
+
}