diff options
author | Frederik Rietdijk <fridh@fridh.nl> | 2019-06-15 08:43:33 +0200 |
---|---|---|
committer | Frederik Rietdijk <fridh@fridh.nl> | 2019-06-15 08:43:33 +0200 |
commit | 31f22a5bb30bc5885123225e14ea968384c9139b (patch) | |
tree | 43b0b7eb65fc6eb38814b84ff6e21de9ab342732 /nixos | |
parent | af1fca07e0d4ee19c3116f2b1032d7f659a4eb7f (diff) | |
parent | 3cf9c7163fc1becb1f08906967110891e208f1fc (diff) |
Merge staging-next into staging
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/programs/captive-browser.nix | 122 | ||||
-rw-r--r-- | nixos/modules/services/databases/cassandra.nix | 210 | ||||
-rw-r--r-- | nixos/modules/services/misc/gitea.nix | 90 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd.nix | 3 | ||||
-rw-r--r-- | nixos/modules/virtualisation/containers.nix | 32 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/cassandra.nix | 96 | ||||
-rw-r--r-- | nixos/tests/gitea.nix | 16 |
9 files changed, 457 insertions, 114 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d30700b4dd6a5..bb33d8e6d95a3 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -92,6 +92,7 @@ ./programs/bcc.nix ./programs/blcr.nix ./programs/browserpass.nix + ./programs/captive-browser.nix ./programs/ccache.nix ./programs/cdemu.nix ./programs/chromium.nix diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix new file mode 100644 index 0000000000000..55d474e5c9db0 --- /dev/null +++ b/nixos/modules/programs/captive-browser.nix @@ -0,0 +1,122 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.captive-browser; +in +{ + ###### interface + + options = { + programs.captive-browser = { + enable = mkEnableOption "captive browser"; + + package = mkOption { + type = types.package; + default = pkgs.captive-browser; + defaultText = "pkgs.captive-browser"; + description = "Which package to use for captive-browser"; + }; + + interface = mkOption { + type = types.str; + description = "your public network interface (wlp3s0, wlan0, eth0, ...)"; + }; + + # the options below are the same as in "captive-browser.toml" + browser = mkOption { + type = types.str; + default = concatStringsSep " " [ ''${pkgs.chromium}/bin/chromium'' + ''--user-data-dir=$HOME/.chromium-captive'' + ''--proxy-server="socks5://$PROXY"'' + ''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"'' + ''--no-first-run'' + ''--new-window'' + ''--incognito'' + ''http://cache.nixos.org/'' + ]; + description = '' + The shell (/bin/sh) command executed once the proxy starts. + When browser exits, the proxy exits. An extra env var PROXY is available. + + Here, we use a separate Chrome instance in Incognito mode, so that + it can run (and be waited for) alongside the default one, and that + it maintains no state across runs. To configure this browser open a + normal window in it, settings will be preserved. + + @volth: chromium is to open a plain HTTP (not HTTPS nor redirect to HTTPS!) website. + upstream uses http://example.com but I have seen captive portals whose DNS server resolves "example.com" to 127.0.0.1 + ''; + }; + + dhcp-dns = mkOption { + type = types.str; + description = '' + The shell (/bin/sh) command executed to obtain the DHCP + DNS server address. The first match of an IPv4 regex is used. + IPv4 only, because let's be real, it's a captive portal. + ''; + }; + + socks5-addr = mkOption { + type = types.str; + default = "localhost:1666"; + description = ''the listen address for the SOCKS5 proxy server''; + }; + + bindInterface = mkOption { + default = true; + type = types.bool; + description = '' + Binds <package>captive-browser</package> to the network interface declared in + <literal>cfg.interface</literal>. This can be used to avoid collisions + with private subnets. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + programs.captive-browser.dhcp-dns = mkOptionDefault ( + if config.networking.networkmanager.enable then + "${pkgs.networkmanager}/bin/nmcli dev show ${escapeShellArg cfg.interface} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS" + else if config.networking.dhcpcd.enable then + "${pkgs.dhcpcd}/bin/dhcpcd -U ${escapeShellArg cfg.interface} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers" + else if config.networking.useNetworkd then + "${cfg.package}/bin/systemd-networkd-dns ${escapeShellArg cfg.interface}" + else + "${config.security.wrapperDir}/udhcpc --quit --now -f -i ${escapeShellArg cfg.interface} -O dns --script ${ + pkgs.writeScript "udhcp-script" '' + #!/bin/sh + if [ "$1" = bound ]; then + echo "$dns" + fi + ''}" + ); + + security.wrappers.udhcpc = { + capabilities = "cap_net_raw+p"; + source = "${pkgs.busybox}/bin/udhcpc"; + }; + + security.wrappers.captive-browser = { + capabilities = "cap_net_raw+p"; + source = pkgs.writeScript "captive-browser" '' + #!${pkgs.bash}/bin/bash + export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" '' + browser = """${cfg.browser}""" + dhcp-dns = """${cfg.dhcp-dns}""" + socks5-addr = """${cfg.socks5-addr}""" + ${optionalString cfg.bindInterface '' + bind-device = """${cfg.interface}""" + ''} + ''} + exec ${cfg.package}/bin/captive-browser + ''; + }; + }; +} diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix index 688938868020a..e2ea9fcda6b0c 100644 --- a/nixos/modules/services/databases/cassandra.nix +++ b/nixos/modules/services/databases/cassandra.nix @@ -8,18 +8,21 @@ let cassandraConfig = flip recursiveUpdate cfg.extraConfig ({ commitlog_sync = "batch"; commitlog_sync_batch_window_in_ms = 2; + start_native_transport = cfg.allowClients; + cluster_name = cfg.clusterName; partitioner = "org.apache.cassandra.dht.Murmur3Partitioner"; endpoint_snitch = "SimpleSnitch"; - seed_provider = - [{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; - parameters = [ { seeds = "127.0.0.1"; } ]; - }]; data_file_directories = [ "${cfg.homeDir}/data" ]; commitlog_directory = "${cfg.homeDir}/commitlog"; saved_caches_directory = "${cfg.homeDir}/saved_caches"; - } // (if builtins.compareVersions cfg.package.version "3" >= 0 - then { hints_directory = "${cfg.homeDir}/hints"; } - else {}) + } // (lib.optionalAttrs (cfg.seedAddresses != []) { + seed_provider = [{ + class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; + parameters = [ { seeds = concatStringsSep "," cfg.seedAddresses; } ]; + }]; + }) // (lib.optionalAttrs (lib.versionAtLeast cfg.package.version "3") { + hints_directory = "${cfg.homeDir}/hints"; + }) ); cassandraConfigWithAddresses = cassandraConfig // ( if cfg.listenAddress == null @@ -39,15 +42,42 @@ let mkdir -p "$out" echo "$cassandraYaml" > "$out/cassandra.yaml" - ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh" ln -s "$cassandraLogbackConfig" "$out/logback.xml" + + cp "$cassandraEnvPkg" "$out/cassandra-env.sh" + + # Delete default JMX Port, otherwise we can't set it using env variable + sed -i '/JMX_PORT="7199"/d' "$out/cassandra-env.sh" + + # Delete default password file + sed -i '/-Dcom.sun.management.jmxremote.password.file=\/etc\/cassandra\/jmxremote.password/d' "$out/cassandra-env.sh" ''; }; + defaultJmxRolesFile = builtins.foldl' + (left: right: left + right) "" + (map (role: "${role.username} ${role.password}") cfg.jmxRoles); + fullJvmOptions = cfg.jvmOpts + ++ lib.optionals (cfg.jmxRoles != []) [ + "-Dcom.sun.management.jmxremote.authenticate=true" + "-Dcom.sun.management.jmxremote.password.file=${cfg.jmxRolesFile}" + ] + ++ lib.optionals cfg.remoteJmx [ + "-Djava.rmi.server.hostname=${cfg.rpcAddress}" + ]; in { options.services.cassandra = { enable = mkEnableOption '' Apache Cassandra – Scalable and highly available database. ''; + clusterName = mkOption { + type = types.str; + default = "NixOS Test Cluster"; + description = '' + The name of the cluster. + This setting prevents nodes in one logical cluster from joining + another. All nodes in a cluster must have the same value. + ''; + }; user = mkOption { type = types.str; default = defaultUser; @@ -162,6 +192,28 @@ in { XML logback configuration for cassandra ''; }; + seedAddresses = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" ]; + description = '' + The addresses of hosts designated as contact points in the cluster. A + joining node contacts one of the nodes in the seeds list to learn the + topology of the ring. + Set to 127.0.0.1 for a single node cluster. + ''; + }; + allowClients = mkOption { + type = types.bool; + default = true; + description = '' + Enables or disables the native transport server (CQL binary protocol). + This server uses the same address as the <literal>rpcAddress</literal>, + but the port it uses is not <literal>rpc_port</literal> but + <literal>native_transport_port</literal>. See the official Cassandra + docs for more information on these variables and set them using + <literal>extraConfig</literal>. + ''; + }; extraConfig = mkOption { type = types.attrs; default = {}; @@ -178,11 +230,11 @@ in { example = literalExample "null"; description = '' Set the interval how often full repairs are run, i.e. - `nodetool repair --full` is executed. See + <literal>nodetool repair --full</literal> is executed. See https://cassandra.apache.org/doc/latest/operating/repair.html for more information. - Set to `null` to disable full repairs. + Set to <literal>null</literal> to disable full repairs. ''; }; fullRepairOptions = mkOption { @@ -199,11 +251,11 @@ in { example = literalExample "null"; description = '' Set the interval how often incremental repairs are run, i.e. - `nodetool repair` is executed. See + <literal>nodetool repair</literal> is executed. See https://cassandra.apache.org/doc/latest/operating/repair.html for more information. - Set to `null` to disable incremental repairs. + Set to <literal>null</literal> to disable incremental repairs. ''; }; incrementalRepairOptions = mkOption { @@ -214,20 +266,135 @@ in { Options passed through to the incremental repair command. ''; }; + maxHeapSize = mkOption { + type = types.nullOr types.string; + default = null; + example = "4G"; + description = '' + Must be left blank or set together with heapNewSize. + If left blank a sensible value for the available amount of RAM and CPU + cores is calculated. + + Override to set the amount of memory to allocate to the JVM at + start-up. For production use you may wish to adjust this for your + environment. MAX_HEAP_SIZE is the total amount of memory dedicated + to the Java heap. HEAP_NEWSIZE refers to the size of the young + generation. + + The main trade-off for the young generation is that the larger it + is, the longer GC pause times will be. The shorter it is, the more + expensive GC will be (usually). + ''; + }; + heapNewSize = mkOption { + type = types.nullOr types.string; + default = null; + example = "800M"; + description = '' + Must be left blank or set together with heapNewSize. + If left blank a sensible value for the available amount of RAM and CPU + cores is calculated. + + Override to set the amount of memory to allocate to the JVM at + start-up. For production use you may wish to adjust this for your + environment. HEAP_NEWSIZE refers to the size of the young + generation. + + The main trade-off for the young generation is that the larger it + is, the longer GC pause times will be. The shorter it is, the more + expensive GC will be (usually). + + The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent pause + times. If in doubt, and if you do not particularly want to tweak, go with + 100 MB per physical CPU core. + ''; + }; + mallocArenaMax = mkOption { + type = types.nullOr types.int; + default = null; + example = 4; + description = '' + Set this to control the amount of arenas per-thread in glibc. + ''; + }; + remoteJmx = mkOption { + type = types.bool; + default = false; + description = '' + Cassandra ships with JMX accessible *only* from localhost. + To enable remote JMX connections set to true. + + Be sure to also enable authentication and/or TLS. + See: https://wiki.apache.org/cassandra/JmxSecurity + ''; + }; + jmxPort = mkOption { + type = types.int; + default = 7199; + description = '' + Specifies the default port over which Cassandra will be available for + JMX connections. + For security reasons, you should not expose this port to the internet. + Firewall it if needed. + ''; + }; + jmxRoles = mkOption { + default = []; + description = '' + Roles that are allowed to access the JMX (e.g. nodetool) + BEWARE: The passwords will be stored world readable in the nix-store. + It's recommended to use your own protected file using + <literal>jmxRolesFile</literal> + + Doesn't work in versions older than 3.11 because they don't like that + it's world readable. + ''; + type = types.listOf (types.submodule { + options = { + username = mkOption { + type = types.string; + description = "Username for JMX"; + }; + password = mkOption { + type = types.string; + description = "Password for JMX"; + }; + }; + }); + }; + jmxRolesFile = mkOption { + type = types.nullOr types.path; + default = if (lib.versionAtLeast cfg.package.version "3.11") + then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile + else null; + example = "/var/lib/cassandra/jmx.password"; + description = '' + Specify your own jmx roles file. + + Make sure the permissions forbid "others" from reading the file if + you're using Cassandra below version 3.11. + ''; + }; }; config = mkIf cfg.enable { assertions = - [ { assertion = - (cfg.listenAddress == null || cfg.listenInterface == null) - && !(cfg.listenAddress == null && cfg.listenInterface == null); + [ { assertion = (cfg.listenAddress == null) != (cfg.listenInterface == null); message = "You have to set either listenAddress or listenInterface"; } - { assertion = - (cfg.rpcAddress == null || cfg.rpcInterface == null) - && !(cfg.rpcAddress == null && cfg.rpcInterface == null); + { assertion = (cfg.rpcAddress == null) != (cfg.rpcInterface == null); message = "You have to set either rpcAddress or rpcInterface"; } + { assertion = (cfg.maxHeapSize == null) == (cfg.heapNewSize == null); + message = "If you set either of maxHeapSize or heapNewSize you have to set both"; + } + { assertion = cfg.remoteJmx -> cfg.jmxRolesFile != null; + message = '' + If you want JMX available remotely you need to set a password using + <literal>jmxRoles</literal> or <literal>jmxRolesFile</literal> if + using Cassandra older than v3.11. + ''; + } ]; users = mkIf (cfg.user == defaultUser) { extraUsers."${defaultUser}" = @@ -245,7 +412,12 @@ in { after = [ "network.target" ]; environment = { CASSANDRA_CONF = "${cassandraEtc}"; - JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts; + JVM_OPTS = builtins.concatStringsSep " " fullJvmOptions; + MAX_HEAP_SIZE = toString cfg.maxHeapSize; + HEAP_NEWSIZE = toString cfg.heapNewSize; + MALLOC_ARENA_MAX = toString cfg.mallocArenaMax; + LOCAL_JMX = if cfg.remoteJmx then "no" else "yes"; + JMX_PORT = toString cfg.jmxPort; }; wantedBy = [ "multi-user.target" ]; serviceConfig = diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix index 6fd4183bd6b4f..7daa2dd0d4cc9 100644 --- a/nixos/modules/services/misc/gitea.nix +++ b/nixos/modules/services/misc/gitea.nix @@ -159,7 +159,8 @@ in socket = mkOption { type = types.nullOr types.path; - default = null; + default = if (cfg.database.createDatabase && usePostgresql) then "/run/postgresql" else if (cfg.database.createDatabase && useMysql) then "/run/mysqld/mysqld.sock" else null; + defaultText = "null"; example = "/run/mysqld/mysqld.sock"; description = "Path to the unix socket file to use for authentication."; }; @@ -173,10 +174,7 @@ in createDatabase = mkOption { type = types.bool; default = true; - description = '' - Whether to create a local postgresql database automatically. - This only applies if database type "postgres" is selected. - ''; + description = "Whether to create a local database automatically."; }; }; @@ -277,7 +275,46 @@ in }; config = mkIf cfg.enable { - services.postgresql.enable = mkIf usePostgresql (mkDefault true); + assertions = [ + { assertion = cfg.database.createDatabase -> cfg.database.user == cfg.user; + message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned"; + } + ]; + + services.postgresql = optionalAttrs (usePostgresql && cfg.database.createDatabase) { + enable = mkDefault true; + + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.mysql = optionalAttrs (useMysql && cfg.database.createDatabase) { + enable = mkDefault true; + package = mkDefault pkgs.mariadb; + + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' - ${cfg.user} gitea - -" + "d '${cfg.stateDir}/conf' - ${cfg.user} gitea - -" + "d '${cfg.stateDir}/custom/conf' - ${cfg.user} gitea - -" + "d '${cfg.repositoryRoot}' - ${cfg.user} gitea - -" + "Z '${cfg.stateDir}' - ${cfg.user} gitea - -" + + # If we have a folder or symlink with gitea locales, remove it + # And symlink the current gitea locales in place + "L+ '${cfg.stateDir}/conf/locale' - - - - ${gitea.out}/locale" + ]; systemd.services.gitea = { description = "gitea"; @@ -289,12 +326,8 @@ in runConfig = "${cfg.stateDir}/custom/conf/app.ini"; secretKey = "${cfg.stateDir}/custom/conf/secret_key"; in '' - # Make sure that the stateDir exists, as well as the conf dir in there - mkdir -p ${cfg.stateDir}/conf - # copy custom configuration and generate a random secret key if needed ${optionalString (cfg.useWizard == false) '' - mkdir -p ${cfg.stateDir}/custom/conf cp -f ${configFile} ${runConfig} if [ ! -e ${secretKey} ]; then @@ -309,7 +342,6 @@ in chmod 640 ${runConfig} ${secretKey} ''} - mkdir -p ${cfg.repositoryRoot} # update all hooks' binary paths HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*") if [ "$HOOKS" ] @@ -319,43 +351,19 @@ in sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS fi - # If we have a folder or symlink with gitea locales, remove it - if [ -e ${cfg.stateDir}/conf/locale ] - then - rm -r ${cfg.stateDir}/conf/locale - fi - # And symlink the current gitea locales in place - ln -s ${gitea.out}/locale ${cfg.stateDir}/conf/locale + # update command option in authorized_keys if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] then sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea.bin}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys fi - '' + optionalString (usePostgresql && cfg.database.createDatabase) '' - if ! test -e "${cfg.stateDir}/db-created"; then - echo "CREATE ROLE ${cfg.database.user} - WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})' - NOCREATEDB NOCREATEROLE LOGIN" | - ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql - ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ - ${pg.package}/bin/createdb \ - --owner=${cfg.database.user} \ - --encoding=UTF8 \ - --lc-collate=C \ - --lc-ctype=C \ - --template=template0 \ - ${cfg.database.name} - touch "${cfg.stateDir}/db-created" - fi - '' + '' - chown ${cfg.user} -R ${cfg.stateDir} ''; serviceConfig = { Type = "simple"; User = cfg.user; + Group = "gitea"; WorkingDirectory = cfg.stateDir; - PermissionsStartOnly = true; ExecStart = "${gitea.bin}/bin/gitea web"; Restart = "always"; }; @@ -367,15 +375,17 @@ in }; }; - users = mkIf (cfg.user == "gitea") { - users.gitea = { + users.users = mkIf (cfg.user == "gitea") { + gitea = { description = "Gitea Service"; home = cfg.stateDir; - createHome = true; useDefaultShell = true; + group = "gitea"; }; }; + users.groups.gitea = {}; + warnings = optional (cfg.database.password != "") ''config.services.gitea.database.password will be stored as plaintext in the Nix store. Use database.passwordFile instead.''; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 8499b700d3eaf..cf35504e5182d 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -186,6 +186,9 @@ let "sockets.target" "sound.target" "systemd-exit.service" + "systemd-tmpfiles-clean.service" + "systemd-tmpfiles-clean.timer" + "systemd-tmpfiles-setup.service" "timers.target" ]; diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix index 97243e3304eea..2235eec9d95a2 100644 --- a/nixos/modules/virtualisation/containers.nix +++ b/nixos/modules/virtualisation/containers.nix @@ -465,20 +465,24 @@ in merge = loc: defs: (import ../../lib/eval-config.nix { inherit system; modules = - let extraConfig = - { boot.isContainer = true; - networking.hostName = mkDefault name; - networking.useDHCP = false; - assertions = [ - { - assertion = config.privateNetwork -> stringLength name < 12; - message = '' - Container name `${name}` is too long: When `privateNetwork` is enabled, container names can - not be longer than 11 characters, because the container's interface name is derived from it. - This might be fixed in the future. See https://github.com/NixOS/nixpkgs/issues/38509 - ''; - } - ]; + let + extraConfig = { + _file = "module at ${__curPos.file}:${toString __curPos.line}"; + config = { + boot.isContainer = true; + networking.hostName = mkDefault name; + networking.useDHCP = false; + assertions = [ + { + assertion = config.privateNetwork -> stringLength name < 12; + message = '' + Container name `${name}` is too long: When `privateNetwork` is enabled, container names can + not be longer than 11 characters, because the container's interface name is derived from it. + This might be fixed in the future. See https://github.com/NixOS/nixpkgs/issues/38509 + ''; + } + ]; + }; }; in [ extraConfig ] ++ (map (x: x.value) defs); prefix = [ "containers" name ]; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 7ec3ede383295..3872970343a97 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -36,6 +36,7 @@ in borgbackup = handleTest ./borgbackup.nix {}; buildbot = handleTest ./buildbot.nix {}; cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {}; + cassandra = handleTest ./cassandra.nix {}; ceph = handleTestOn ["x86_64-linux"] ./ceph.nix {}; certmgr = handleTest ./certmgr.nix {}; cfssl = handleTestOn ["x86_64-linux"] ./cfssl.nix {}; diff --git a/nixos/tests/cassandra.nix b/nixos/tests/cassandra.nix index 60d0c6d760682..aea4fa4d1c957 100644 --- a/nixos/tests/cassandra.nix +++ b/nixos/tests/cassandra.nix @@ -1,26 +1,43 @@ -import ./make-test.nix ({ pkgs, ...}: +import ./make-test.nix ({ pkgs, lib, ... }: let # Change this to test a different version of Cassandra: testPackage = pkgs.cassandra; - cassandraCfg = + clusterName = "NixOS Automated-Test Cluster"; + + testRemoteAuth = lib.versionAtLeast testPackage.version "3.11"; + jmxRoles = [{ username = "me"; password = "password"; }]; + jmxRolesFile = ./cassandra-jmx-roles; + jmxAuthArgs = "-u ${(builtins.elemAt jmxRoles 0).username} -pw ${(builtins.elemAt jmxRoles 0).password}"; + + # Would usually be assigned to 512M + numMaxHeapSize = "400"; + getHeapLimitCommand = '' + nodetool info | grep "^Heap Memory" | awk \'{print $NF}\' + ''; + checkHeapLimitCommand = '' + [ 1 -eq "$(echo "$(${getHeapLimitCommand}) < ${numMaxHeapSize}" | ${pkgs.bc}/bin/bc)" ] + ''; + + cassandraCfg = ipAddress: { enable = true; - listenAddress = null; - listenInterface = "eth1"; - rpcAddress = null; - rpcInterface = "eth1"; - extraConfig = - { start_native_transport = true; - seed_provider = - [{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; - parameters = [ { seeds = "cass0"; } ]; - }]; - }; + inherit clusterName; + listenAddress = ipAddress; + rpcAddress = ipAddress; + seedAddresses = [ "192.168.1.1" ]; package = testPackage; + maxHeapSize = "${numMaxHeapSize}M"; + heapNewSize = "100M"; }; - nodeCfg = extra: {pkgs, config, ...}: + nodeCfg = ipAddress: extra: {pkgs, config, ...}: { environment.systemPackages = [ testPackage ]; - networking.firewall.enable = false; - services.cassandra = cassandraCfg // extra; + networking = { + firewall.allowedTCPPorts = [ 7000 7199 9042 ]; + useDHCP = false; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [ + { address = ipAddress; prefixLength = 24; } + ]; + }; + services.cassandra = cassandraCfg ipAddress // extra; virtualisation.memorySize = 1024; }; in @@ -28,40 +45,65 @@ in name = "cassandra-ci"; nodes = { - cass0 = nodeCfg {}; - cass1 = nodeCfg {}; - cass2 = nodeCfg { jvmOpts = [ "-Dcassandra.replace_address=cass1" ]; }; + cass0 = nodeCfg "192.168.1.1" {}; + cass1 = nodeCfg "192.168.1.2" (lib.optionalAttrs testRemoteAuth { inherit jmxRoles; remoteJmx = true; }); + cass2 = nodeCfg "192.168.1.3" { jvmOpts = [ "-Dcassandra.replace_address=cass1" ]; }; }; testScript = '' - subtest "timers exist", sub { + # Check configuration + subtest "Timers exist", sub { $cass0->succeed("systemctl list-timers | grep cassandra-full-repair.timer"); $cass0->succeed("systemctl list-timers | grep cassandra-incremental-repair.timer"); }; - subtest "can connect via cqlsh", sub { + subtest "Can connect via cqlsh", sub { $cass0->waitForUnit("cassandra.service"); $cass0->waitUntilSucceeds("nc -z cass0 9042"); $cass0->succeed("echo 'show version;' | cqlsh cass0"); }; - subtest "nodetool is operational", sub { + subtest "Nodetool is operational", sub { $cass0->waitForUnit("cassandra.service"); $cass0->waitUntilSucceeds("nc -z localhost 7199"); $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass0'"); }; - subtest "bring up cluster", sub { + subtest "Cluster name was set", sub { + $cass0->waitForUnit("cassandra.service"); + $cass0->waitUntilSucceeds("nc -z localhost 7199"); + $cass0->waitUntilSucceeds("nodetool describecluster | grep 'Name: ${clusterName}'"); + }; + subtest "Heap limit set correctly", sub { + # Nodetool takes a while until it can display info + $cass0->waitUntilSucceeds('nodetool info'); + $cass0->succeed('${checkHeapLimitCommand}'); + }; + + # Check cluster interaction + subtest "Bring up cluster", sub { $cass1->waitForUnit("cassandra.service"); - $cass1->waitUntilSucceeds("nodetool status | egrep -c '^UN' | grep 2"); + $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN' | grep 2"); $cass0->succeed("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'"); }; - subtest "break and fix node", sub { + '' + lib.optionalString testRemoteAuth '' + subtest "Remote authenticated jmx", sub { + # Doesn't work if not enabled + $cass0->waitUntilSucceeds("nc -z localhost 7199"); + $cass1->fail("nc -z 192.168.1.1 7199"); + $cass1->fail("nodetool -h 192.168.1.1 status"); + + # Works if enabled + $cass1->waitUntilSucceeds("nc -z localhost 7199"); + $cass0->succeed("nodetool -h 192.168.1.2 ${jmxAuthArgs} status"); + }; + '' + '' + subtest "Break and fix node", sub { $cass1->block; $cass0->waitUntilSucceeds("nodetool status --resolve-ip | egrep -c '^DN[[:space:]]+cass1'"); $cass0->succeed("nodetool status | egrep -c '^UN' | grep 1"); $cass1->unblock; - $cass1->waitUntilSucceeds("nodetool status | egrep -c '^UN' | grep 2"); + $cass1->waitUntilSucceeds("nodetool ${jmxAuthArgs} status | egrep -c '^UN' | grep 2"); $cass0->succeed("nodetool status | egrep -c '^UN' | grep 2"); }; - subtest "replace crashed node", sub { + subtest "Replace crashed node", sub { $cass1->crash; $cass2->waitForUnit("cassandra.service"); $cass0->waitUntilFails("nodetool status --resolve-ip | egrep '^UN[[:space:]]+cass1'"); diff --git a/nixos/tests/gitea.nix b/nixos/tests/gitea.nix index cccf8c7cd44fe..b8ab6dabc8c1f 100644 --- a/nixos/tests/gitea.nix +++ b/nixos/tests/gitea.nix @@ -13,18 +13,8 @@ with pkgs.lib; machine = { config, pkgs, ... }: - { services.mysql.enable = true; - services.mysql.package = pkgs.mariadb; - services.mysql.ensureDatabases = [ "gitea" ]; - services.mysql.ensureUsers = [ - { name = "gitea"; - ensurePermissions = { "gitea.*" = "ALL PRIVILEGES"; }; - } - ]; - - services.gitea.enable = true; + { services.gitea.enable = true; services.gitea.database.type = "mysql"; - services.gitea.database.socket = "/run/mysqld/mysqld.sock"; }; testScript = '' @@ -42,10 +32,8 @@ with pkgs.lib; machine = { config, pkgs, ... }: - { - services.gitea.enable = true; + { services.gitea.enable = true; services.gitea.database.type = "postgres"; - services.gitea.database.passwordFile = pkgs.writeText "db-password" "secret"; }; testScript = '' |