diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index d55a7db3915..820be5085de 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -1,79 +1,108 @@
{ config, lib, pkgs, ... }:
-with lib;
-
let
+ inherit (lib)
+ concatStringsSep
+ flip
+ literalExample
+ optionalAttrs
+ optionals
+ recursiveUpdate
+ mkEnableOption
+ mkIf
+ mkOption
+ types
+ versionAtLeast
+ ;
+
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"
+ 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";
+ } // optionalAttrs (cfg.seedAddresses != [ ]) {
+ seed_provider = [
+ {
+ class_name = "org.apache.cassandra.locator.SimpleSeedProvider";
+ parameters = [{ seeds = concatStringsSep "," cfg.seedAddresses; }];
+ }
+ ];
+ } // optionalAttrs (versionAtLeast cfg.package.version "3") {
+ hints_directory = "${cfg.homeDir}/hints";
+ }
+ );
- ( cat "$cassandraEnvPkg"
- echo "# lines from services.cassandra.extraEnvSh: "
- cat "$extraEnvShPath"
- ) > "$out/cassandra-env.sh"
+ 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; }
+ );
- # Delete default JMX Port, otherwise we can't set it using env variable
- sed -i '/JMX_PORT="7199"/d' "$out/cassandra-env.sh"
+ cassandraEtc = pkgs.stdenv.mkDerivation {
+ name = "cassandra-etc";
- # 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 != []) [
+ 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
+ ++ optionals (cfg.jmxRoles != [ ]) [
"-Dcom.sun.management.jmxremote.authenticate=true"
"-Dcom.sun.management.jmxremote.password.file=${cfg.jmxRolesFile}"
- ]
- ++ lib.optionals cfg.remoteJmx [
+ ] ++ 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 +112,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 +132,7 @@ in {
Home directory for Apache Cassandra.
'';
};
+
package = mkOption {
type = types.package;
default = pkgs.cassandra;
@@ -109,17 +142,19 @@ 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";
- example = literalExample "null";
+ example = null;
description = ''
Address or interface to bind to and tell other Cassandra nodes
to connect to. You _must_ change this if you want multiple
@@ -136,6 +171,7 @@ in {
Setting listen_address to 0.0.0.0 is always wrong.
'';
};
+
listenInterface = mkOption {
type = types.nullOr types.str;
default = null;
@@ -146,10 +182,11 @@ in {
supported.
'';
};
+
rpcAddress = mkOption {
type = types.nullOr types.str;
default = "127.0.0.1";
- example = literalExample "null";
+ example = null;
description = ''
The address or interface to bind the native transport server to.
@@ -167,6 +204,7 @@ in {
internet. Firewall it if needed.
'';
};
+
rpcInterface = mkOption {
type = types.nullOr types.str;
default = null;
@@ -176,6 +214,7 @@ in {
correspond to a single address, IP aliasing is not supported.
'';
};
+
logbackConfig = mkOption {
type = types.lines;
default = ''
@@ -197,6 +236,7 @@ in {
XML logback configuration for cassandra
'';
};
+
seedAddresses = mkOption {
type = types.listOf types.str;
default = [ "127.0.0.1" ];
@@ -207,6 +247,7 @@ in {
Set to 127.0.0.1 for a single node cluster.
'';
};
+
allowClients = mkOption {
type = types.bool;
default = true;
@@ -219,16 +260,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 +281,53 @@ in {
Extra shell lines to be appended onto cassandra-env.sh.
'';
};
+
fullRepairInterval = mkOption {
type = types.nullOr types.str;
default = "3w";
- example = literalExample "null";
+ example = 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";
+ example = 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 +348,7 @@ in {
expensive GC will be (usually).
'';
};
+
heapNewSize = mkOption {
type = types.nullOr types.str;
default = null;
@@ -322,6 +372,7 @@ in {
100 MB per physical CPU core.
'';
};
+
mallocArenaMax = mkOption {
type = types.nullOr types.int;
default = null;
@@ -330,6 +381,7 @@ in {
Set this to control the amount of arenas per-thread in glibc.
'';
};
+
remoteJmx = mkOption {
type = types.bool;
default = false;
@@ -341,6 +393,7 @@ in {
See: https://wiki.apache.org/cassandra/JmxSecurity
'';
};
+
jmxPort = mkOption {
type = types.int;
default = 7199;
@@ -351,8 +404,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 +429,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 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 +447,115 @@ 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";
- };
- extraGroups.${defaultUser}.gid = config.ids.gids.cassandra;
+ users.${defaultUser} = {
+ group = cfg.group;
+ home = cfg.homeDir;
+ createHome = true;
+ uid = config.ids.uids.cassandra;
+ description = "Cassandra service user";
+ };
+ groups.${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 =
+ 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 =
+ 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;
+ };
};
};
+
+ meta.maintainers = with lib.maintainers; [ roberth ];
}
diff --git a/pkgs/servers/nosql/cassandra/generic.nix b/pkgs/servers/nosql/cassandra/generic.nix
index ca2001817a3..cab21080a04 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 = 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;