diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/databases/cassandra.nix')
-rw-r--r-- | nixpkgs/nixos/modules/services/databases/cassandra.nix | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/databases/cassandra.nix b/nixpkgs/nixos/modules/services/databases/cassandra.nix new file mode 100644 index 000000000000..d741ee48c48f --- /dev/null +++ b/nixpkgs/nixos/modules/services/databases/cassandra.nix @@ -0,0 +1,313 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.cassandra; + defaultUser = "cassandra"; + cassandraConfig = flip recursiveUpdate cfg.extraConfig + ({ commitlog_sync = "batch"; + commitlog_sync_batch_window_in_ms = 2; + 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 {}) + ); + cassandraConfigWithAddresses = cassandraConfig // + ( if isNull cfg.listenAddress + then { listen_interface = cfg.listenInterface; } + else { listen_address = cfg.listenAddress; } + ) // ( + if isNull cfg.rpcAddress + 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; + buildCommand = '' + mkdir -p "$out" + + echo "$cassandraYaml" > "$out/cassandra.yaml" + ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh" + ln -s "$cassandraLogbackConfig" "$out/logback.xml" + ''; + }; +in { + options.services.cassandra = { + enable = mkEnableOption '' + Apache Cassandra – Scalable and highly available database. + ''; + 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"; + description = '' + Home directory for Apache Cassandra. + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.cassandra; + defaultText = "pkgs.cassandra"; + example = literalExample "pkgs.cassandra_3_11"; + description = '' + The Apache Cassandra package to use. + ''; + }; + jvmOpts = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Populate the JVM_OPT environment variable. + ''; + }; + listenAddress = mkOption { + type = types.nullOr types.str; + default = "127.0.0.1"; + example = literalExample "null"; + description = '' + Address or interface to bind to and tell other Cassandra nodes + to connect to. You _must_ change this if you want multiple + nodes to be able to communicate! + + Set listenAddress OR listenInterface, not both. + + Leaving it blank leaves it up to + InetAddress.getLocalHost(). This will always do the Right + Thing _if_ the node is properly configured (hostname, name + resolution, etc), and the Right Thing is to use the address + associated with the hostname (it might not be). + + Setting listen_address to 0.0.0.0 is always wrong. + ''; + }; + listenInterface = mkOption { + type = types.nullOr types.str; + default = null; + example = "eth1"; + description = '' + Set listenAddress OR listenInterface, not both. Interfaces + must correspond to a single address, IP aliasing is not + supported. + ''; + }; + rpcAddress = mkOption { + type = types.nullOr types.str; + default = "127.0.0.1"; + example = literalExample "null"; + description = '' + The address or interface to bind the native transport server to. + + Set rpcAddress OR rpcInterface, not both. + + Leaving rpcAddress blank has the same effect as on + listenAddress (i.e. it will be based on the configured hostname + of the node). + + Note that unlike listenAddress, you can specify 0.0.0.0, but you + must also set extraConfig.broadcast_rpc_address to a value other + than 0.0.0.0. + + For security reasons, you should not expose this port to the + internet. Firewall it if needed. + ''; + }; + rpcInterface = mkOption { + type = types.nullOr types.str; + default = null; + example = "eth1"; + description = '' + Set rpcAddress OR rpcInterface, not both. Interfaces must + correspond to a single address, IP aliasing is not supported. + ''; + }; + logbackConfig = mkOption { + type = types.lines; + default = '' + <configuration scan="false"> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%-5level %date{HH:mm:ss,SSS} %msg%n</pattern> + </encoder> + </appender> + + <root level="INFO"> + <appender-ref ref="STDOUT" /> + </root> + + <logger name="com.thinkaurelius.thrift" level="ERROR"/> + </configuration> + ''; + description = '' + XML logback configuration for cassandra + ''; + }; + extraConfig = mkOption { + type = types.attrs; + default = {}; + example = + { commitlog_sync_batch_window_in_ms = 3; + }; + description = '' + Extra options to be merged into cassandra.yaml as nix attribute set. + ''; + }; + 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 to `null` to disable full repairs. + ''; + }; + fullRepairOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ "--partitioner-range" ]; + description = '' + 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 to `null` to disable incremental repairs. + ''; + }; + incrementalRepairOptions = mkOption { + type = types.listOf types.string; + default = []; + example = [ "--partitioner-range" ]; + description = '' + Options passed through to the incremental repair command. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = + [ { assertion = + ((isNull cfg.listenAddress) + || (isNull cfg.listenInterface) + ) && !((isNull cfg.listenAddress) + && (isNull cfg.listenInterface) + ); + message = "You have to set either listenAddress or listenInterface"; + } + { assertion = + ((isNull cfg.rpcAddress) + || (isNull cfg.rpcInterface) + ) && !((isNull cfg.rpcAddress) + && (isNull cfg.rpcInterface) + ); + message = "You have to set either rpcAddress or rpcInterface"; + } + ]; + 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; + }; + + systemd.services.cassandra = + { description = "Apache Cassandra service"; + after = [ "network.target" ]; + environment = + { CASSANDRA_CONF = "${cassandraEtc}"; + JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts; + }; + 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.timers.cassandra-full-repair = + mkIf (!isNull cfg.fullRepairInterval) { + description = "Schedule full repairs on Cassandra"; + wantedBy = [ "timers.target" ]; + 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.timers.cassandra-incremental-repair = + mkIf (!isNull cfg.incrementalRepairInterval) { + description = "Schedule incremental repairs on Cassandra"; + wantedBy = [ "timers.target" ]; + timerConfig = + { OnBootSec = cfg.incrementalRepairInterval; + OnUnitActiveSec = cfg.incrementalRepairInterval; + Persistent = true; + }; + }; + }; +} |