at 18.09-beta 9.7 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.cassandra; 7 defaultUser = "cassandra"; 8 cassandraConfig = flip recursiveUpdate cfg.extraConfig 9 ({ commitlog_sync = "batch"; 10 commitlog_sync_batch_window_in_ms = 2; 11 partitioner = "org.apache.cassandra.dht.Murmur3Partitioner"; 12 endpoint_snitch = "SimpleSnitch"; 13 seed_provider = 14 [{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; 15 parameters = [ { seeds = "127.0.0.1"; } ]; 16 }]; 17 data_file_directories = [ "${cfg.homeDir}/data" ]; 18 commitlog_directory = "${cfg.homeDir}/commitlog"; 19 saved_caches_directory = "${cfg.homeDir}/saved_caches"; 20 } // (if builtins.compareVersions cfg.package.version "3" >= 0 21 then { hints_directory = "${cfg.homeDir}/hints"; } 22 else {}) 23 ); 24 cassandraConfigWithAddresses = cassandraConfig // 25 ( if isNull cfg.listenAddress 26 then { listen_interface = cfg.listenInterface; } 27 else { listen_address = cfg.listenAddress; } 28 ) // ( 29 if isNull cfg.rpcAddress 30 then { rpc_interface = cfg.rpcInterface; } 31 else { rpc_address = cfg.rpcAddress; } 32 ); 33 cassandraEtc = pkgs.stdenv.mkDerivation 34 { name = "cassandra-etc"; 35 cassandraYaml = builtins.toJSON cassandraConfigWithAddresses; 36 cassandraEnvPkg = "${cfg.package}/conf/cassandra-env.sh"; 37 buildCommand = '' 38 mkdir -p "$out" 39 40 echo "$cassandraYaml" > "$out/cassandra.yaml" 41 ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh" 42 ''; 43 }; 44in { 45 options.services.cassandra = { 46 enable = mkEnableOption '' 47 Apache Cassandra Scalable and highly available database. 48 ''; 49 user = mkOption { 50 type = types.str; 51 default = defaultUser; 52 description = "Run Apache Cassandra under this user."; 53 }; 54 group = mkOption { 55 type = types.str; 56 default = defaultUser; 57 description = "Run Apache Cassandra under this group."; 58 }; 59 homeDir = mkOption { 60 type = types.path; 61 default = "/var/lib/cassandra"; 62 description = '' 63 Home directory for Apache Cassandra. 64 ''; 65 }; 66 package = mkOption { 67 type = types.package; 68 default = pkgs.cassandra; 69 defaultText = "pkgs.cassandra"; 70 example = literalExample "pkgs.cassandra_3_11"; 71 description = '' 72 The Apache Cassandra package to use. 73 ''; 74 }; 75 jvmOpts = mkOption { 76 type = types.listOf types.str; 77 default = []; 78 description = '' 79 Populate the JVM_OPT environment variable. 80 ''; 81 }; 82 listenAddress = mkOption { 83 type = types.nullOr types.str; 84 default = "127.0.0.1"; 85 example = literalExample "null"; 86 description = '' 87 Address or interface to bind to and tell other Cassandra nodes 88 to connect to. You _must_ change this if you want multiple 89 nodes to be able to communicate! 90 91 Set listenAddress OR listenInterface, not both. 92 93 Leaving it blank leaves it up to 94 InetAddress.getLocalHost(). This will always do the Right 95 Thing _if_ the node is properly configured (hostname, name 96 resolution, etc), and the Right Thing is to use the address 97 associated with the hostname (it might not be). 98 99 Setting listen_address to 0.0.0.0 is always wrong. 100 ''; 101 }; 102 listenInterface = mkOption { 103 type = types.nullOr types.str; 104 default = null; 105 example = "eth1"; 106 description = '' 107 Set listenAddress OR listenInterface, not both. Interfaces 108 must correspond to a single address, IP aliasing is not 109 supported. 110 ''; 111 }; 112 rpcAddress = mkOption { 113 type = types.nullOr types.str; 114 default = "127.0.0.1"; 115 example = literalExample "null"; 116 description = '' 117 The address or interface to bind the native transport server to. 118 119 Set rpcAddress OR rpcInterface, not both. 120 121 Leaving rpcAddress blank has the same effect as on 122 listenAddress (i.e. it will be based on the configured hostname 123 of the node). 124 125 Note that unlike listenAddress, you can specify 0.0.0.0, but you 126 must also set extraConfig.broadcast_rpc_address to a value other 127 than 0.0.0.0. 128 129 For security reasons, you should not expose this port to the 130 internet. Firewall it if needed. 131 ''; 132 }; 133 rpcInterface = mkOption { 134 type = types.nullOr types.str; 135 default = null; 136 example = "eth1"; 137 description = '' 138 Set rpcAddress OR rpcInterface, not both. Interfaces must 139 correspond to a single address, IP aliasing is not supported. 140 ''; 141 }; 142 143 extraConfig = mkOption { 144 type = types.attrs; 145 default = {}; 146 example = 147 { commitlog_sync_batch_window_in_ms = 3; 148 }; 149 description = '' 150 Extra options to be merged into cassandra.yaml as nix attribute set. 151 ''; 152 }; 153 fullRepairInterval = mkOption { 154 type = types.nullOr types.str; 155 default = "3w"; 156 example = literalExample "null"; 157 description = '' 158 Set the interval how often full repairs are run, i.e. 159 `nodetool repair --full` is executed. See 160 https://cassandra.apache.org/doc/latest/operating/repair.html 161 for more information. 162 163 Set to `null` to disable full repairs. 164 ''; 165 }; 166 fullRepairOptions = mkOption { 167 type = types.listOf types.str; 168 default = []; 169 example = [ "--partitioner-range" ]; 170 description = '' 171 Options passed through to the full repair command. 172 ''; 173 }; 174 incrementalRepairInterval = mkOption { 175 type = types.nullOr types.str; 176 default = "3d"; 177 example = literalExample "null"; 178 description = '' 179 Set the interval how often incremental repairs are run, i.e. 180 `nodetool repair` is executed. See 181 https://cassandra.apache.org/doc/latest/operating/repair.html 182 for more information. 183 184 Set to `null` to disable incremental repairs. 185 ''; 186 }; 187 incrementalRepairOptions = mkOption { 188 type = types.listOf types.string; 189 default = []; 190 example = [ "--partitioner-range" ]; 191 description = '' 192 Options passed through to the incremental repair command. 193 ''; 194 }; 195 }; 196 197 config = mkIf cfg.enable { 198 assertions = 199 [ { assertion = 200 ((isNull cfg.listenAddress) 201 || (isNull cfg.listenInterface) 202 ) && !((isNull cfg.listenAddress) 203 && (isNull cfg.listenInterface) 204 ); 205 message = "You have to set either listenAddress or listenInterface"; 206 } 207 { assertion = 208 ((isNull cfg.rpcAddress) 209 || (isNull cfg.rpcInterface) 210 ) && !((isNull cfg.rpcAddress) 211 && (isNull cfg.rpcInterface) 212 ); 213 message = "You have to set either rpcAddress or rpcInterface"; 214 } 215 ]; 216 users = mkIf (cfg.user == defaultUser) { 217 extraUsers."${defaultUser}" = 218 { group = cfg.group; 219 home = cfg.homeDir; 220 createHome = true; 221 uid = config.ids.uids.cassandra; 222 description = "Cassandra service user"; 223 }; 224 extraGroups."${defaultUser}".gid = config.ids.gids.cassandra; 225 }; 226 227 systemd.services.cassandra = 228 { description = "Apache Cassandra service"; 229 after = [ "network.target" ]; 230 environment = 231 { CASSANDRA_CONF = "${cassandraEtc}"; 232 JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts; 233 }; 234 wantedBy = [ "multi-user.target" ]; 235 serviceConfig = 236 { User = cfg.user; 237 Group = cfg.group; 238 ExecStart = "${cfg.package}/bin/cassandra -f"; 239 SuccessExitStatus = 143; 240 }; 241 }; 242 243 systemd.services.cassandra-full-repair = 244 { description = "Perform a full repair on this Cassandra node"; 245 after = [ "cassandra.service" ]; 246 requires = [ "cassandra.service" ]; 247 serviceConfig = 248 { User = cfg.user; 249 Group = cfg.group; 250 ExecStart = 251 lib.concatStringsSep " " 252 ([ "${cfg.package}/bin/nodetool" "repair" "--full" 253 ] ++ cfg.fullRepairOptions); 254 }; 255 }; 256 systemd.timers.cassandra-full-repair = 257 mkIf (!isNull cfg.fullRepairInterval) { 258 description = "Schedule full repairs on Cassandra"; 259 wantedBy = [ "timers.target" ]; 260 timerConfig = 261 { OnBootSec = cfg.fullRepairInterval; 262 OnUnitActiveSec = cfg.fullRepairInterval; 263 Persistent = true; 264 }; 265 }; 266 267 systemd.services.cassandra-incremental-repair = 268 { description = "Perform an incremental repair on this cassandra node."; 269 after = [ "cassandra.service" ]; 270 requires = [ "cassandra.service" ]; 271 serviceConfig = 272 { User = cfg.user; 273 Group = cfg.group; 274 ExecStart = 275 lib.concatStringsSep " " 276 ([ "${cfg.package}/bin/nodetool" "repair" 277 ] ++ cfg.incrementalRepairOptions); 278 }; 279 }; 280 systemd.timers.cassandra-incremental-repair = 281 mkIf (!isNull cfg.incrementalRepairInterval) { 282 description = "Schedule incremental repairs on Cassandra"; 283 wantedBy = [ "timers.target" ]; 284 timerConfig = 285 { OnBootSec = cfg.incrementalRepairInterval; 286 OnUnitActiveSec = cfg.incrementalRepairInterval; 287 Persistent = true; 288 }; 289 }; 290 }; 291}