diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 07774dd1d29..4a63a09ab84 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -682,6 +682,7 @@ ./services/networking/i2p.nix ./services/networking/icecream/scheduler.nix ./services/networking/icecream/daemon.nix + ./services/networking/inspircd.nix ./services/networking/iodine.nix ./services/networking/iperf3.nix ./services/networking/ircd-hybrid/default.nix diff --git a/nixos/modules/services/networking/inspircd.nix b/nixos/modules/services/networking/inspircd.nix new file mode 100644 index 00000000000..fcac8d34cce --- /dev/null +++ b/nixos/modules/services/networking/inspircd.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.inspircd; + + configFile = pkgs.writeText "inspircd.conf" cfg.config; + +in { + options = { + services.inspircd = { + enable = lib.mkEnableOption "InspIRCd"; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.inspircd; + defaultText = lib.literalExample "pkgs.inspircd"; + example = lib.literalExample "pkgs.inspircdMinimal"; + description = '' + The InspIRCd package to use. This is mainly useful + to specify an overridden version of the + pkgs.inspircd dervivation, for + example if you want to use a more minimal InspIRCd + distribution with less modules enabled or with + modules enabled which can't be distributed in binary + form due to licensing issues. + ''; + }; + + config = lib.mkOption { + type = lib.types.lines; + description = '' + Verbatim inspircd.conf file. + For a list of options, consult the + InspIRCd documentation, the + Module documentation + and the example configuration files distributed + with pkgs.inspircd.doc + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.inspircd = { + description = "InspIRCd - the stable, high-performance and modular Internet Relay Chat Daemon"; + wantedBy = [ "multi-user.target" ]; + requires = [ "network.target" ]; + + serviceConfig = { + Type = "simple"; + ExecStart = '' + ${lib.getBin cfg.package}/bin/inspircd start --config ${configFile} --nofork --nopid + ''; + DynamicUser = true; + }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3ce71b0abe6..7d19e7309bc 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -176,6 +176,7 @@ in initrd-network-ssh = handleTest ./initrd-network-ssh {}; initrdNetwork = handleTest ./initrd-network.nix {}; initrd-secrets = handleTest ./initrd-secrets.nix {}; + inspircd = handleTest ./inspircd.nix {}; installer = handleTest ./installer.nix {}; iodine = handleTest ./iodine.nix {}; ipfs = handleTest ./ipfs.nix {}; diff --git a/nixos/tests/inspircd.nix b/nixos/tests/inspircd.nix new file mode 100644 index 00000000000..b2975091d36 --- /dev/null +++ b/nixos/tests/inspircd.nix @@ -0,0 +1,96 @@ +let + clients = [ + "ircclient1" + "ircclient2" + ]; + server = "inspircd"; + ircPort = 6667; + channel = "nixos-cat"; + iiDir = "/tmp/irc"; +in + +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "inspircd"; + nodes = { + "${server}" = { + networking.firewall.allowedTCPPorts = [ ircPort ]; + services.inspircd = { + enable = true; + package = pkgs.inspircdMinimal; + config = '' + + + ''; + }; + }; + } // 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 ++ [ + "# trailing comment to please reformatter :)" + ]; + + # 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}.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.concatStringsSep "\n" + (reduce + (a: b: lib.zipListsWith (cs: c: cs + "\n" + c) a b) + (builtins.map clientScript clients))} + ''; +})