cassandra: format

This commit is contained in:
Robert Hensing 2021-04-29 08:22:39 +02:00
parent 1c65a509fb
commit 19ba3d97d2
2 changed files with 237 additions and 169 deletions

View file

@ -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"
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";
}
);
( 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
++ 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 {
<literal>extraConfig</literal>.
'';
};
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.
<literal>nodetool repair --full</literal> 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.
<literal>nodetool repair --full</literal> is executed. See
https://cassandra.apache.org/doc/latest/operating/repair.html
for more information.
Set to <literal>null</literal> to disable full repairs.
'';
Set to <literal>null</literal> 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.
<literal>nodetool repair</literal> 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.
<literal>nodetool repair</literal> is executed. See
https://cassandra.apache.org/doc/latest/operating/repair.html
for more information.
Set to <literal>null</literal> to disable incremental repairs.
'';
Set to <literal>null</literal> 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
<literal>jmxRoles</literal> or <literal>jmxRolesFile</literal> 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
<literal>jmxRoles</literal> or <literal>jmxRolesFile</literal> 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;
};
};
};
}

View file

@ -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;