1{ config, lib, pkgs, utils, ... }:
2
3with lib;
4
5let
6 cfg = config.services.cockroachdb;
7 crdb = cfg.package;
8
9 startupCommand = utils.escapeSystemdExecArgs
10 ([
11 # Basic startup
12 "${crdb}/bin/cockroach"
13 "start"
14 "--logtostderr"
15 "--store=/var/lib/cockroachdb"
16
17 # WebUI settings
18 "--http-addr=${cfg.http.address}:${toString cfg.http.port}"
19
20 # Cluster listen address
21 "--listen-addr=${cfg.listen.address}:${toString cfg.listen.port}"
22
23 # Cache and memory settings.
24 "--cache=${cfg.cache}"
25 "--max-sql-memory=${cfg.maxSqlMemory}"
26
27 # Certificate/security settings.
28 (if cfg.insecure then "--insecure" else "--certs-dir=${cfg.certsDir}")
29 ]
30 ++ lib.optional (cfg.join != null) "--join=${cfg.join}"
31 ++ lib.optional (cfg.locality != null) "--locality=${cfg.locality}"
32 ++ cfg.extraArgs);
33
34 addressOption = descr: defaultPort: {
35 address = mkOption {
36 type = types.str;
37 default = "localhost";
38 description = lib.mdDoc "Address to bind to for ${descr}";
39 };
40
41 port = mkOption {
42 type = types.port;
43 default = defaultPort;
44 description = lib.mdDoc "Port to bind to for ${descr}";
45 };
46 };
47in
48
49{
50 options = {
51 services.cockroachdb = {
52 enable = mkEnableOption (lib.mdDoc "CockroachDB Server");
53
54 listen = addressOption "intra-cluster communication" 26257;
55
56 http = addressOption "http-based Admin UI" 8080;
57
58 locality = mkOption {
59 type = types.nullOr types.str;
60 default = null;
61 description = lib.mdDoc ''
62 An ordered, comma-separated list of key-value pairs that describe the
63 topography of the machine. Topography might include country,
64 datacenter or rack designations. Data is automatically replicated to
65 maximize diversities of each tier. The order of tiers is used to
66 determine the priority of the diversity, so the more inclusive
67 localities like country should come before less inclusive localities
68 like datacenter. The tiers and order must be the same on all nodes.
69 Including more tiers is better than including fewer. For example:
70
71 ```
72 country=us,region=us-west,datacenter=us-west-1b,rack=12
73 country=ca,region=ca-east,datacenter=ca-east-2,rack=4
74
75 planet=earth,province=manitoba,colo=secondary,power=3
76 ```
77 '';
78 };
79
80 join = mkOption {
81 type = types.nullOr types.str;
82 default = null;
83 description = lib.mdDoc "The addresses for connecting the node to a cluster.";
84 };
85
86 insecure = mkOption {
87 type = types.bool;
88 default = false;
89 description = lib.mdDoc "Run in insecure mode.";
90 };
91
92 certsDir = mkOption {
93 type = types.nullOr types.path;
94 default = null;
95 description = lib.mdDoc "The path to the certificate directory.";
96 };
97
98 user = mkOption {
99 type = types.str;
100 default = "cockroachdb";
101 description = lib.mdDoc "User account under which CockroachDB runs";
102 };
103
104 group = mkOption {
105 type = types.str;
106 default = "cockroachdb";
107 description = lib.mdDoc "User account under which CockroachDB runs";
108 };
109
110 openPorts = mkOption {
111 type = types.bool;
112 default = false;
113 description = lib.mdDoc "Open firewall ports for cluster communication by default";
114 };
115
116 cache = mkOption {
117 type = types.str;
118 default = "25%";
119 description = lib.mdDoc ''
120 The total size for caches.
121
122 This can be a percentage, expressed with a fraction sign or as a
123 decimal-point number, or any bytes-based unit. For example,
124 `"25%"`, `"0.25"` both represent
125 25% of the available system memory. The values
126 `"1000000000"` and `"1GB"` both
127 represent 1 gigabyte of memory.
128
129 '';
130 };
131
132 maxSqlMemory = mkOption {
133 type = types.str;
134 default = "25%";
135 description = lib.mdDoc ''
136 The maximum in-memory storage capacity available to store temporary
137 data for SQL queries.
138
139 This can be a percentage, expressed with a fraction sign or as a
140 decimal-point number, or any bytes-based unit. For example,
141 `"25%"`, `"0.25"` both represent
142 25% of the available system memory. The values
143 `"1000000000"` and `"1GB"` both
144 represent 1 gigabyte of memory.
145 '';
146 };
147
148 package = mkOption {
149 type = types.package;
150 default = pkgs.cockroachdb;
151 defaultText = literalExpression "pkgs.cockroachdb";
152 description = lib.mdDoc ''
153 The CockroachDB derivation to use for running the service.
154
155 This would primarily be useful to enable Enterprise Edition features
156 in your own custom CockroachDB build (Nixpkgs CockroachDB binaries
157 only contain open source features and open source code).
158 '';
159 };
160
161 extraArgs = mkOption {
162 type = types.listOf types.str;
163 default = [];
164 example = [ "--advertise-addr" "[fe80::f6f2:::]" ];
165 description = lib.mdDoc ''
166 Extra CLI arguments passed to {command}`cockroach start`.
167 For the full list of supported arguments, check <https://www.cockroachlabs.com/docs/stable/cockroach-start.html#flags>
168 '';
169 };
170 };
171 };
172
173 config = mkIf config.services.cockroachdb.enable {
174 assertions = [
175 { assertion = !cfg.insecure -> cfg.certsDir != null;
176 message = "CockroachDB must have a set of SSL certificates (.certsDir), or run in Insecure Mode (.insecure = true)";
177 }
178 ];
179
180 environment.systemPackages = [ crdb ];
181
182 users.users = optionalAttrs (cfg.user == "cockroachdb") {
183 cockroachdb = {
184 description = "CockroachDB Server User";
185 uid = config.ids.uids.cockroachdb;
186 group = cfg.group;
187 };
188 };
189
190 users.groups = optionalAttrs (cfg.group == "cockroachdb") {
191 cockroachdb.gid = config.ids.gids.cockroachdb;
192 };
193
194 networking.firewall.allowedTCPPorts = lib.optionals cfg.openPorts
195 [ cfg.http.port cfg.listen.port ];
196
197 systemd.services.cockroachdb =
198 { description = "CockroachDB Server";
199 documentation = [ "man:cockroach(1)" "https://www.cockroachlabs.com" ];
200
201 after = [ "network.target" "time-sync.target" ];
202 requires = [ "time-sync.target" ];
203 wantedBy = [ "multi-user.target" ];
204
205 unitConfig.RequiresMountsFor = "/var/lib/cockroachdb";
206
207 serviceConfig =
208 { ExecStart = startupCommand;
209 Type = "notify";
210 User = cfg.user;
211 StateDirectory = "cockroachdb";
212 StateDirectoryMode = "0700";
213
214 Restart = "always";
215
216 # A conservative-ish timeout is alright here, because for Type=notify
217 # cockroach will send systemd pings during startup to keep it alive
218 TimeoutStopSec = 60;
219 RestartSec = 10;
220 };
221 };
222 };
223
224 meta.maintainers = with lib.maintainers; [ thoughtpolice ];
225}