parent
d10236eb20
commit
48c16e48aa
@ -0,0 +1,104 @@ |
||||
{ 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; |
||||
}) |
||||
]); |
||||
} |
@ -0,0 +1,89 @@ |
||||
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)); |
||||
}) |
Loading…
Reference in new issue