From 19ba3d97d29285b2e6c49bfa3f3445c679e73ef2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 29 Apr 2021 08:22:39 +0200 Subject: cassandra: format --- nixos/modules/services/databases/cassandra.nix | 383 ++++++++++++++----------- pkgs/servers/nosql/cassandra/generic.nix | 31 +- 2 files changed, 241 insertions(+), 173 deletions(-) diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix index d55a7db3915..4ce7a068aed 100644 --- a/nixos/modules/services/databases/cassandra.nix +++ b/nixos/modules/services/databases/cassandra.nix @@ -4,76 +4,93 @@ with lib; let cfg = config.services.cassandra; + defaultUser = "cassandra"; - 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"; - data_file_directories = [ "${cfg.homeDir}/data" ]; - commitlog_directory = "${cfg.homeDir}/commitlog"; - saved_caches_directory = "${cfg.homeDir}/saved_caches"; - } // (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 - then { listen_interface = cfg.listenInterface; } - else { listen_address = cfg.listenAddress; } - ) // ( - if cfg.rpcAddress == null - then { rpc_interface = cfg.rpcInterface; } - else { rpc_address = cfg.rpcAddress; } - ); - cassandraEtc = pkgs.stdenv.mkDerivation - { name = "cassandra-etc"; - cassandraYaml = builtins.toJSON cassandraConfigWithAddresses; - cassandraEnvPkg = "${cfg.package}/conf/cassandra-env.sh"; - cassandraLogbackConfig = pkgs.writeText "logback.xml" cfg.logbackConfig; - passAsFile = [ "extraEnvSh" ]; - inherit (cfg) extraEnvSh; - buildCommand = '' - mkdir -p "$out" - - echo "$cassandraYaml" > "$out/cassandra.yaml" - ln -s "$cassandraLogbackConfig" "$out/logback.xml" - - ( cat "$cassandraEnvPkg" - echo "# lines from services.cassandra.extraEnvSh: " - cat "$extraEnvShPath" - ) > "$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 != []) [ + + 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"; + data_file_directories = [ "${cfg.homeDir}/data" ]; + commitlog_directory = "${cfg.homeDir}/commitlog"; + saved_caches_directory = "${cfg.homeDir}/saved_caches"; + } // 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 + then { listen_interface = cfg.listenInterface; } + else { listen_address = cfg.listenAddress; } + ) // ( + if cfg.rpcAddress == null + then { rpc_interface = cfg.rpcInterface; } + else { rpc_address = cfg.rpcAddress; } + ); + + cassandraEtc = pkgs.stdenv.mkDerivation { + name = "cassandra-etc"; + + cassandraYaml = builtins.toJSON cassandraConfigWithAddresses; + cassandraEnvPkg = "${cfg.package}/conf/cassandra-env.sh"; + cassandraLogbackConfig = pkgs.writeText "logback.xml" cfg.logbackConfig; + + passAsFile = [ "extraEnvSh" ]; + inherit (cfg) extraEnvSh; + + buildCommand = '' + mkdir -p "$out" + + echo "$cassandraYaml" > "$out/cassandra.yaml" + ln -s "$cassandraLogbackConfig" "$out/logback.xml" + + ( cat "$cassandraEnvPkg" + echo "# lines from services.cassandra.extraEnvSh: " + cat "$extraEnvShPath" + ) > "$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 [ + ] ++ lib.optionals cfg.remoteJmx [ "-Djava.rmi.server.hostname=${cfg.rpcAddress}" ]; -in { + +in +{ options.services.cassandra = { + enable = mkEnableOption '' Apache Cassandra – Scalable and highly available database. ''; + clusterName = mkOption { type = types.str; default = "Test Cluster"; @@ -83,16 +100,19 @@ in { another. All nodes in a cluster must have the same value. ''; }; + user = mkOption { type = types.str; default = defaultUser; description = "Run Apache Cassandra under this user."; }; + group = mkOption { type = types.str; default = defaultUser; description = "Run Apache Cassandra under this group."; }; + homeDir = mkOption { type = types.path; default = "/var/lib/cassandra"; @@ -100,6 +120,7 @@ in { Home directory for Apache Cassandra. ''; }; + package = mkOption { type = types.package; default = pkgs.cassandra; @@ -109,13 +130,15 @@ in { The Apache Cassandra package to use. ''; }; + jvmOpts = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; description = '' Populate the JVM_OPT environment variable. ''; }; + listenAddress = mkOption { type = types.nullOr types.str; default = "127.0.0.1"; @@ -136,6 +159,7 @@ in { Setting listen_address to 0.0.0.0 is always wrong. ''; }; + listenInterface = mkOption { type = types.nullOr types.str; default = null; @@ -146,6 +170,7 @@ in { supported. ''; }; + rpcAddress = mkOption { type = types.nullOr types.str; default = "127.0.0.1"; @@ -167,6 +192,7 @@ in { internet. Firewall it if needed. ''; }; + rpcInterface = mkOption { type = types.nullOr types.str; default = null; @@ -176,6 +202,7 @@ in { correspond to a single address, IP aliasing is not supported. ''; }; + logbackConfig = mkOption { type = types.lines; default = '' @@ -197,6 +224,7 @@ in { XML logback configuration for cassandra ''; }; + seedAddresses = mkOption { type = types.listOf types.str; default = [ "127.0.0.1" ]; @@ -207,6 +235,7 @@ in { Set to 127.0.0.1 for a single node cluster. ''; }; + allowClients = mkOption { type = types.bool; default = true; @@ -219,16 +248,19 @@ in { extraConfig. ''; }; + extraConfig = mkOption { type = types.attrs; - default = {}; + default = { }; example = - { commitlog_sync_batch_window_in_ms = 3; + { + commitlog_sync_batch_window_in_ms = 3; }; description = '' Extra options to be merged into cassandra.yaml as nix attribute set. ''; }; + extraEnvSh = mkOption { type = types.lines; default = ""; @@ -237,48 +269,53 @@ in { Extra shell lines to be appended onto cassandra-env.sh. ''; }; + fullRepairInterval = mkOption { type = types.nullOr types.str; default = "3w"; example = literalExample "null"; description = '' - Set the interval how often full repairs are run, i.e. - nodetool repair --full is executed. See - https://cassandra.apache.org/doc/latest/operating/repair.html - for more information. + Set the interval how often full repairs are run, i.e. + nodetool repair --full is executed. See + https://cassandra.apache.org/doc/latest/operating/repair.html + for more information. - Set to null to disable full repairs. - ''; + Set to null to disable full repairs. + ''; }; + fullRepairOptions = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; example = [ "--partitioner-range" ]; description = '' - Options passed through to the full repair command. - ''; + Options passed through to the full repair command. + ''; }; + incrementalRepairInterval = mkOption { type = types.nullOr types.str; default = "3d"; example = literalExample "null"; description = '' - Set the interval how often incremental repairs are run, i.e. - nodetool repair is executed. See - https://cassandra.apache.org/doc/latest/operating/repair.html - for more information. + Set the interval how often incremental repairs are run, i.e. + nodetool repair is executed. See + https://cassandra.apache.org/doc/latest/operating/repair.html + for more information. - Set to null to disable incremental repairs. - ''; + Set to null to disable incremental repairs. + ''; }; + incrementalRepairOptions = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; example = [ "--partitioner-range" ]; description = '' - Options passed through to the incremental repair command. - ''; + Options passed through to the incremental repair command. + ''; }; + maxHeapSize = mkOption { type = types.nullOr types.str; default = null; @@ -299,6 +336,7 @@ in { expensive GC will be (usually). ''; }; + heapNewSize = mkOption { type = types.nullOr types.str; default = null; @@ -322,6 +360,7 @@ in { 100 MB per physical CPU core. ''; }; + mallocArenaMax = mkOption { type = types.nullOr types.int; default = null; @@ -330,6 +369,7 @@ in { Set this to control the amount of arenas per-thread in glibc. ''; }; + remoteJmx = mkOption { type = types.bool; default = false; @@ -341,6 +381,7 @@ in { See: https://wiki.apache.org/cassandra/JmxSecurity ''; }; + jmxPort = mkOption { type = types.int; default = 7199; @@ -351,8 +392,9 @@ in { Firewall it if needed. ''; }; + jmxRoles = mkOption { - default = []; + 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. @@ -375,11 +417,13 @@ in { }; }); }; + 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; + 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. @@ -391,102 +435,113 @@ in { }; config = mkIf cfg.enable { - assertions = - [ { assertion = (cfg.listenAddress == null) != (cfg.listenInterface == null); - message = "You have to set either listenAddress or listenInterface"; - } - { 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 - jmxRoles or jmxRolesFile if - using Cassandra older than v3.11. - ''; - } - ]; + assertions = [ + { + assertion = (cfg.listenAddress == null) != (cfg.listenInterface == null); + message = "You have to set either listenAddress or listenInterface"; + } + { + 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 + jmxRoles or jmxRolesFile if + using Cassandra older than v3.11. + ''; + } + ]; users = mkIf (cfg.user == defaultUser) { - extraUsers.${defaultUser} = - { group = cfg.group; - home = cfg.homeDir; - createHome = true; - uid = config.ids.uids.cassandra; - description = "Cassandra service user"; - }; + extraUsers.${defaultUser} = { + group = cfg.group; + home = cfg.homeDir; + createHome = true; + uid = config.ids.uids.cassandra; + description = "Cassandra service user"; + }; extraGroups.${defaultUser}.gid = config.ids.gids.cassandra; }; - systemd.services.cassandra = - { description = "Apache Cassandra service"; - after = [ "network.target" ]; - environment = - { CASSANDRA_CONF = "${cassandraEtc}"; - 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 = - { User = cfg.user; - Group = cfg.group; - ExecStart = "${cfg.package}/bin/cassandra -f"; - SuccessExitStatus = 143; - }; + systemd.services.cassandra = { + description = "Apache Cassandra service"; + after = [ "network.target" ]; + environment = { + CASSANDRA_CONF = "${cassandraEtc}"; + 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 = { + User = cfg.user; + Group = cfg.group; + ExecStart = "${cfg.package}/bin/cassandra -f"; + SuccessExitStatus = 143; }; + }; - systemd.services.cassandra-full-repair = - { description = "Perform a full repair on this Cassandra node"; - after = [ "cassandra.service" ]; - requires = [ "cassandra.service" ]; - serviceConfig = - { User = cfg.user; - Group = cfg.group; - ExecStart = - lib.concatStringsSep " " - ([ "${cfg.package}/bin/nodetool" "repair" "--full" - ] ++ cfg.fullRepairOptions); - }; + systemd.services.cassandra-full-repair = { + description = "Perform a full repair on this Cassandra node"; + after = [ "cassandra.service" ]; + requires = [ "cassandra.service" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = + lib.concatStringsSep " " + ([ + "${cfg.package}/bin/nodetool" + "repair" + "--full" + ] ++ cfg.fullRepairOptions); }; + }; + systemd.timers.cassandra-full-repair = mkIf (cfg.fullRepairInterval != null) { description = "Schedule full repairs on Cassandra"; wantedBy = [ "timers.target" ]; - timerConfig = - { OnBootSec = cfg.fullRepairInterval; - OnUnitActiveSec = cfg.fullRepairInterval; - Persistent = true; - }; + timerConfig = { + OnBootSec = cfg.fullRepairInterval; + OnUnitActiveSec = cfg.fullRepairInterval; + Persistent = true; + }; }; - systemd.services.cassandra-incremental-repair = - { description = "Perform an incremental repair on this cassandra node."; - after = [ "cassandra.service" ]; - requires = [ "cassandra.service" ]; - serviceConfig = - { User = cfg.user; - Group = cfg.group; - ExecStart = - lib.concatStringsSep " " - ([ "${cfg.package}/bin/nodetool" "repair" - ] ++ cfg.incrementalRepairOptions); - }; + systemd.services.cassandra-incremental-repair = { + description = "Perform an incremental repair on this cassandra node."; + after = [ "cassandra.service" ]; + requires = [ "cassandra.service" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = + lib.concatStringsSep " " + ([ + "${cfg.package}/bin/nodetool" + "repair" + ] ++ cfg.incrementalRepairOptions); }; + }; + systemd.timers.cassandra-incremental-repair = mkIf (cfg.incrementalRepairInterval != null) { description = "Schedule incremental repairs on Cassandra"; wantedBy = [ "timers.target" ]; - timerConfig = - { OnBootSec = cfg.incrementalRepairInterval; - OnUnitActiveSec = cfg.incrementalRepairInterval; - Persistent = true; - }; + timerConfig = { + OnBootSec = cfg.incrementalRepairInterval; + OnUnitActiveSec = cfg.incrementalRepairInterval; + Persistent = true; + }; }; }; } diff --git a/pkgs/servers/nosql/cassandra/generic.nix b/pkgs/servers/nosql/cassandra/generic.nix index ca2001817a3..029eb160545 100644 --- a/pkgs/servers/nosql/cassandra/generic.nix +++ b/pkgs/servers/nosql/cassandra/generic.nix @@ -1,22 +1,34 @@ -{ lib, stdenv, fetchurl, python, makeWrapper, gawk, bash, getopt, procps -, which, jre, coreutils, nixosTests -# generation is the attribute version suffix such as 3_11 in pkgs.cassandra_3_11 +{ lib +, stdenv +, fetchurl +, python +, makeWrapper +, gawk +, bash +, getopt +, procps +, which +, jre +, coreutils +, nixosTests + # generation is the attribute version suffix such as 3_11 in pkgs.cassandra_3_11 , generation -, version, sha256 -, extraMeta ? {} +, version +, sha256 +, extraMeta ? { } , ... }: let libPath = lib.makeLibraryPath [ stdenv.cc.cc ]; - binPath = with lib; makeBinPath ([ + binPath = with lib; makeBinPath [ bash getopt gawk which jre procps - ]); + ]; in stdenv.mkDerivation rec { @@ -90,13 +102,14 @@ stdenv.mkDerivation rec { wrapProgram $out/bin/cqlsh --prefix PATH : ${python}/bin runHook postInstall - ''; + ''; passthru = { tests = let test = nixosTests."cassandra_${generation}"; - in { + in + { nixos = assert test.testPackage.version == version; test; -- cgit 1.4.1