Merge pull request #123828 from Lassulus/solanum2

nixos/solanum: init

Changed files
+195
nixos
modules
services
networking
tests
+1
nixos/modules/module-list.nix
···
./services/networking/smartdns.nix
./services/networking/smokeping.nix
./services/networking/softether.nix
+
./services/networking/solanum.nix
./services/networking/spacecookie.nix
./services/networking/spiped.nix
./services/networking/squid.nix
+104
nixos/modules/services/networking/solanum.nix
···
+
{ config, lib, pkgs, ... }:
+
+
let
+
inherit (lib) mkEnableOption mkIf mkOption types;
+
inherit (pkgs) solanum;
+
cfg = config.services.solanum;
+
+
configFile = pkgs.writeText "solanum.conf" cfg.config;
+
in
+
+
{
+
+
###### interface
+
+
options = {
+
+
services.solanum = {
+
+
enable = mkEnableOption "Solanum IRC daemon";
+
+
config = mkOption {
+
type = types.str;
+
default = ''
+
serverinfo {
+
name = "irc.example.com";
+
sid = "1ix";
+
description = "irc!";
+
+
vhost = "0.0.0.0";
+
vhost6 = "::";
+
};
+
+
listen {
+
host = "0.0.0.0";
+
port = 6667;
+
};
+
+
auth {
+
user = "*@*";
+
class = "users";
+
flags = exceed_limit;
+
};
+
channel {
+
default_split_user_count = 0;
+
};
+
'';
+
description = ''
+
Solanum IRC daemon configuration file.
+
check <link xlink:href="https://github.com/solanum-ircd/solanum/blob/main/doc/reference.conf"/> for all options.
+
'';
+
};
+
+
openFilesLimit = mkOption {
+
type = types.int;
+
default = 1024;
+
description = ''
+
Maximum number of open files. Limits the clients and server connections.
+
'';
+
};
+
+
motd = mkOption {
+
type = types.nullOr types.lines;
+
default = null;
+
description = ''
+
Solanum MOTD text.
+
+
Solanum will read its MOTD from <literal>/etc/solanum/ircd.motd</literal>.
+
If set, the value of this option will be written to this path.
+
'';
+
};
+
+
};
+
+
};
+
+
+
###### implementation
+
+
config = mkIf cfg.enable (lib.mkMerge [
+
{
+
systemd.services.solanum = {
+
description = "Solanum IRC daemon";
+
after = [ "network.target" ];
+
wantedBy = [ "multi-user.target" ];
+
environment = {
+
BANDB_DBPATH = "/var/lib/solanum/ban.db";
+
};
+
serviceConfig = {
+
ExecStart = "${solanum}/bin/solanum -foreground -logfile /dev/stdout -configfile ${configFile} -pidfile /run/solanum/ircd.pid";
+
DynamicUser = true;
+
User = "solanum";
+
StateDirectory = "solanum";
+
RuntimeDirectory = "solanum";
+
LimitNOFILE = "${toString cfg.openFilesLimit}";
+
};
+
};
+
+
}
+
+
(mkIf (cfg.motd != null) {
+
environment.etc."solanum/ircd.motd".text = cfg.motd;
+
})
+
]);
+
}
+1
nixos/tests/all-tests.nix
···
snapcast = handleTest ./snapcast.nix {};
snapper = handleTest ./snapper.nix {};
sogo = handleTest ./sogo.nix {};
+
solanum = handleTest ./solanum.nix {};
solr = handleTest ./solr.nix {};
sonarr = handleTest ./sonarr.nix {};
spacecookie = handleTest ./spacecookie.nix {};
+89
nixos/tests/solanum.nix
···
+
let
+
clients = [
+
"ircclient1"
+
"ircclient2"
+
];
+
server = "solanum";
+
ircPort = 6667;
+
channel = "nixos-cat";
+
iiDir = "/tmp/irc";
+
in
+
+
import ./make-test-python.nix ({ pkgs, lib, ... }: {
+
name = "solanum";
+
nodes = {
+
"${server}" = {
+
networking.firewall.allowedTCPPorts = [ ircPort ];
+
services.solanum = {
+
enable = true;
+
};
+
};
+
} // lib.listToAttrs (builtins.map (client: lib.nameValuePair client {
+
imports = [
+
./common/user-account.nix
+
];
+
+
systemd.services.ii = {
+
requires = [ "network.target" ];
+
wantedBy = [ "default.target" ];
+
+
serviceConfig = {
+
Type = "simple";
+
ExecPreStartPre = "mkdir -p ${iiDir}";
+
ExecStart = ''
+
${lib.getBin pkgs.ii}/bin/ii -n ${client} -s ${server} -i ${iiDir}
+
'';
+
User = "alice";
+
};
+
};
+
}) clients);
+
+
testScript =
+
let
+
msg = client: "Hello, my name is ${client}";
+
clientScript = client: [
+
''
+
${client}.wait_for_unit("network.target")
+
${client}.systemctl("start ii")
+
${client}.wait_for_unit("ii")
+
${client}.wait_for_file("${iiDir}/${server}/out")
+
''
+
# wait until first PING from server arrives before joining,
+
# so we don't try it too early
+
''
+
${client}.wait_until_succeeds("grep 'PING' ${iiDir}/${server}/out")
+
''
+
# join ${channel}
+
''
+
${client}.succeed("echo '/j #${channel}' > ${iiDir}/${server}/in")
+
${client}.wait_for_file("${iiDir}/${server}/#${channel}/in")
+
''
+
# send a greeting
+
''
+
${client}.succeed(
+
"echo '${msg client}' > ${iiDir}/${server}/#${channel}/in"
+
)
+
''
+
# check that all greetings arrived on all clients
+
] ++ builtins.map (other: ''
+
${client}.succeed(
+
"grep '${msg other}$' ${iiDir}/${server}/#${channel}/out"
+
)
+
'') clients;
+
+
# foldl', but requires a non-empty list instead of a start value
+
reduce = f: list:
+
builtins.foldl' f (builtins.head list) (builtins.tail list);
+
in ''
+
start_all()
+
${server}.systemctl("status solanum")
+
${server}.wait_for_open_port(${toString ircPort})
+
+
# run clientScript for all clients so that every list
+
# entry is executed by every client before advancing
+
# to the next one.
+
'' + lib.concatStrings
+
(reduce
+
(lib.zipListsWith (cs: c: cs + c))
+
(builtins.map clientScript clients));
+
})