@intentius/chant-lexicon-k8s 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/integrity.json +9 -4
  2. package/dist/manifest.json +1 -1
  3. package/dist/skills/chant-k8s-aks.md +146 -0
  4. package/{src/skills/kubernetes-patterns.md → dist/skills/chant-k8s-deployment-strategies.md} +1 -1
  5. package/dist/skills/chant-k8s-eks.md +156 -0
  6. package/dist/skills/chant-k8s-gke.md +246 -0
  7. package/{src/skills/kubernetes-security.md → dist/skills/chant-k8s-security.md} +1 -1
  8. package/dist/skills/chant-k8s.md +65 -2
  9. package/package.json +1 -1
  10. package/src/composites/adot-collector.ts +34 -22
  11. package/src/composites/agic-ingress.ts +14 -6
  12. package/src/composites/aks-external-dns-agent.ts +29 -18
  13. package/src/composites/alb-ingress.ts +14 -6
  14. package/src/composites/autoscaled-service.ts +25 -20
  15. package/src/composites/azure-disk-storage-class.ts +14 -6
  16. package/src/composites/azure-file-storage-class.ts +14 -6
  17. package/src/composites/azure-monitor-collector.ts +34 -22
  18. package/src/composites/batch-job.ts +25 -17
  19. package/src/composites/cockroachdb-cluster.ts +148 -58
  20. package/src/composites/composites.test.ts +369 -363
  21. package/src/composites/config-connector-context.ts +15 -8
  22. package/src/composites/configured-app.ts +21 -15
  23. package/src/composites/cron-workload.ts +25 -20
  24. package/src/composites/ebs-storage-class.ts +14 -6
  25. package/src/composites/efs-storage-class.ts +14 -6
  26. package/src/composites/external-dns-agent.ts +26 -20
  27. package/src/composites/filestore-storage-class.ts +14 -6
  28. package/src/composites/fluent-bit-agent.ts +30 -24
  29. package/src/composites/gce-ingress.ts +14 -6
  30. package/src/composites/gce-pd-storage-class.ts +14 -6
  31. package/src/composites/gke-external-dns-agent.ts +34 -21
  32. package/src/composites/gke-fluent-bit-agent.ts +34 -22
  33. package/src/composites/gke-gateway.ts +19 -12
  34. package/src/composites/gke-otel-collector.ts +34 -22
  35. package/src/composites/irsa-service-account.ts +22 -14
  36. package/src/composites/metrics-server.ts +41 -26
  37. package/src/composites/monitored-service.ts +26 -19
  38. package/src/composites/namespace-env.ts +26 -17
  39. package/src/composites/network-isolated-app.ts +21 -16
  40. package/src/composites/node-agent.ts +33 -22
  41. package/src/composites/secure-ingress.ts +19 -11
  42. package/src/composites/sidecar-app.ts +17 -12
  43. package/src/composites/stateful-app.ts +21 -12
  44. package/src/composites/web-app.ts +25 -21
  45. package/src/composites/worker-pool.ts +40 -26
  46. package/src/composites/workload-identity-sa.ts +22 -14
  47. package/src/composites/workload-identity-service-account.ts +22 -16
  48. package/src/plugin.ts +40 -614
  49. package/src/serializer.ts +3 -0
  50. package/src/skills/chant-k8s-deployment-strategies.md +183 -0
  51. package/src/skills/chant-k8s-gke.md +55 -0
  52. package/src/skills/chant-k8s-patterns.md +245 -0
  53. package/src/skills/chant-k8s-security.md +237 -0
  54. package/src/skills/chant-k8s.md +305 -0
@@ -7,6 +7,9 @@
7
7
  * CockroachDbCluster per cloud, sharing joinAddresses across clouds.
8
8
  */
9
9
 
10
+ import { Composite, mergeDefaults } from "@intentius/chant";
11
+ import { StatefulSet, Service, ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding, PodDisruptionBudget, Job } from "../generated";
12
+
10
13
  export interface CockroachDbClusterProps {
11
14
  /** Cluster name — used in metadata, labels, and service names. */
12
15
  name: string;
@@ -34,26 +37,69 @@ export interface CockroachDbClusterProps {
34
37
  joinAddresses?: string[];
35
38
  /** Enable TLS via self-signed CA certs (default: true). */
36
39
  secure?: boolean;
40
+ /**
41
+ * Skip the cockroach init Job (default: false).
42
+ * Set true for secondary regions in a multi-region cluster — only ONE region
43
+ * should run `cockroach init` to bootstrap the unified cluster. Secondary
44
+ * nodes start fresh and auto-join the cluster via the joinAddresses.
45
+ */
46
+ skipInit?: boolean;
47
+ /**
48
+ * Skip the cert-gen Job (default: false).
49
+ * Set true for multi-region deployments where a shared CA and node certs are
50
+ * pre-generated externally (e.g. via generate-certs.sh) and distributed via
51
+ * External Secrets Operator or direct kubectl. Each region's cert-gen creates
52
+ * a separate CA, breaking cross-region TLS — so all regions must use the same CA.
53
+ */
54
+ skipCertGen?: boolean;
55
+ /**
56
+ * Extra DNS names / IPs to include in node cert SANs beyond the auto-generated
57
+ * cluster-local names. Use for cross-region or external hostnames.
58
+ * E.g. ["cockroachdb-0.east.crdb.internal", "cockroachdb-1.east.crdb.internal"].
59
+ */
60
+ extraCertNodeAddresses?: string[];
61
+ /**
62
+ * Domain suffix for cross-cluster advertise address (e.g. "east.crdb.internal").
63
+ * When set, nodes advertise "${HOSTNAME}.${advertiseHostDomain}" instead of the
64
+ * cluster-local FQDN from $(hostname -f). Required for multi-region clusters so
65
+ * that gossip addresses are resolvable from other Kubernetes clusters.
66
+ * E.g. pod "cockroachdb-0" in east advertises "cockroachdb-0.east.crdb.internal".
67
+ */
68
+ advertiseHostDomain?: string;
37
69
  /** Additional labels to apply to all resources. */
38
70
  labels?: Record<string, string>;
71
+ /** Per-member defaults for fine-grained overrides. */
72
+ defaults?: {
73
+ serviceAccount?: Partial<Record<string, unknown>>;
74
+ role?: Partial<Record<string, unknown>>;
75
+ roleBinding?: Partial<Record<string, unknown>>;
76
+ clusterRole?: Partial<Record<string, unknown>>;
77
+ clusterRoleBinding?: Partial<Record<string, unknown>>;
78
+ publicService?: Partial<Record<string, unknown>>;
79
+ headlessService?: Partial<Record<string, unknown>>;
80
+ pdb?: Partial<Record<string, unknown>>;
81
+ statefulSet?: Partial<Record<string, unknown>>;
82
+ initJob?: Partial<Record<string, unknown>>;
83
+ certGenJob?: Partial<Record<string, unknown>>;
84
+ };
39
85
  }
40
86
 
41
87
  export interface CockroachDbClusterResult {
42
- serviceAccount: Record<string, unknown>;
43
- role: Record<string, unknown>;
44
- roleBinding: Record<string, unknown>;
45
- clusterRole: Record<string, unknown>;
46
- clusterRoleBinding: Record<string, unknown>;
88
+ serviceAccount: InstanceType<typeof ServiceAccount>;
89
+ role: InstanceType<typeof Role>;
90
+ roleBinding: InstanceType<typeof RoleBinding>;
91
+ clusterRole: InstanceType<typeof ClusterRole>;
92
+ clusterRoleBinding: InstanceType<typeof ClusterRoleBinding>;
47
93
  /** Client-facing service (ClusterIP, ports 26257+8080). */
48
- publicService: Record<string, unknown>;
94
+ publicService: InstanceType<typeof Service>;
49
95
  /** Pod discovery service (headless, publishNotReadyAddresses). */
50
- headlessService: Record<string, unknown>;
51
- pdb: Record<string, unknown>;
52
- statefulSet: Record<string, unknown>;
53
- /** One-shot cockroach init job. */
54
- initJob: Record<string, unknown>;
55
- /** Generates self-signed CA + node certs, stores in Secrets. */
56
- certGenJob: Record<string, unknown>;
96
+ headlessService: InstanceType<typeof Service>;
97
+ pdb: InstanceType<typeof PodDisruptionBudget>;
98
+ statefulSet: InstanceType<typeof StatefulSet>;
99
+ /** One-shot cockroach init job. Absent when skipInit is true. */
100
+ initJob?: InstanceType<typeof Job>;
101
+ /** Generates self-signed CA + node certs, stores in Secrets. Absent when skipCertGen is true. */
102
+ certGenJob?: InstanceType<typeof Job>;
57
103
  }
58
104
 
59
105
  /**
@@ -77,7 +123,7 @@ export interface CockroachDbClusterResult {
77
123
  * });
78
124
  * ```
79
125
  */
80
- export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbClusterResult {
126
+ export const CockroachDbCluster = Composite<CockroachDbClusterProps>((props) => {
81
127
  const {
82
128
  name,
83
129
  namespace,
@@ -92,7 +138,12 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
92
138
  locality,
93
139
  joinAddresses = [],
94
140
  secure = true,
141
+ skipInit = false,
142
+ skipCertGen = false,
143
+ extraCertNodeAddresses = [],
144
+ advertiseHostDomain,
95
145
  labels: extraLabels = {},
146
+ defaults: defs,
96
147
  } = props;
97
148
 
98
149
  const saName = name;
@@ -105,28 +156,28 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
105
156
  ...extraLabels,
106
157
  };
107
158
 
108
- // ── RBAC ─────────────────────────────────────────────────────────
159
+ // -- RBAC --
109
160
 
110
- const serviceAccount: Record<string, unknown> = {
161
+ const serviceAccount = new ServiceAccount(mergeDefaults({
111
162
  metadata: {
112
163
  name: saName,
113
164
  ...(namespace && { namespace }),
114
165
  labels: { ...commonLabels, "app.kubernetes.io/component": "database" },
115
166
  },
116
- };
167
+ }, defs?.serviceAccount));
117
168
 
118
- const role: Record<string, unknown> = {
169
+ const role = new Role(mergeDefaults({
119
170
  metadata: {
120
171
  name,
121
172
  ...(namespace && { namespace }),
122
173
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
123
174
  },
124
175
  rules: [
125
- { apiGroups: [""], resources: ["secrets"], verbs: ["get", "create", "patch"] },
176
+ { apiGroups: [""], resources: ["secrets"], verbs: ["get", "create", "update", "patch"] },
126
177
  ],
127
- };
178
+ }, defs?.role));
128
179
 
129
- const roleBinding: Record<string, unknown> = {
180
+ const roleBinding = new RoleBinding(mergeDefaults({
130
181
  metadata: {
131
182
  name,
132
183
  ...(namespace && { namespace }),
@@ -140,9 +191,9 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
140
191
  subjects: [
141
192
  { kind: "ServiceAccount", name: saName, ...(namespace && { namespace }) },
142
193
  ],
143
- };
194
+ }, defs?.roleBinding));
144
195
 
145
- const clusterRole: Record<string, unknown> = {
196
+ const clusterRole = new ClusterRole(mergeDefaults({
146
197
  metadata: {
147
198
  name,
148
199
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
@@ -150,9 +201,9 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
150
201
  rules: [
151
202
  { apiGroups: ["certificates.k8s.io"], resources: ["certificatesigningrequests"], verbs: ["get", "create", "watch"] },
152
203
  ],
153
- };
204
+ }, defs?.clusterRole));
154
205
 
155
- const clusterRoleBinding: Record<string, unknown> = {
206
+ const clusterRoleBinding = new ClusterRoleBinding(mergeDefaults({
156
207
  metadata: {
157
208
  name,
158
209
  labels: { ...commonLabels, "app.kubernetes.io/component": "rbac" },
@@ -165,11 +216,11 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
165
216
  subjects: [
166
217
  { kind: "ServiceAccount", name: saName, ...(namespace && { namespace }) },
167
218
  ],
168
- };
219
+ }, defs?.clusterRoleBinding));
169
220
 
170
- // ── Services ────────────────────────────────────────────────────
221
+ // -- Services --
171
222
 
172
- const publicService: Record<string, unknown> = {
223
+ const publicService = new Service(mergeDefaults({
173
224
  metadata: {
174
225
  name: `${name}-public`,
175
226
  ...(namespace && { namespace }),
@@ -183,9 +234,9 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
183
234
  ],
184
235
  type: "ClusterIP",
185
236
  },
186
- };
237
+ }, defs?.publicService));
187
238
 
188
- const headlessService: Record<string, unknown> = {
239
+ const headlessService = new Service(mergeDefaults({
189
240
  metadata: {
190
241
  name,
191
242
  ...(namespace && { namespace }),
@@ -203,11 +254,11 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
203
254
  clusterIP: "None",
204
255
  publishNotReadyAddresses: true,
205
256
  },
206
- };
257
+ }, defs?.headlessService));
207
258
 
208
- // ── PodDisruptionBudget ─────────────────────────────────────────
259
+ // -- PodDisruptionBudget --
209
260
 
210
- const pdb: Record<string, unknown> = {
261
+ const pdb = new PodDisruptionBudget(mergeDefaults({
211
262
  metadata: {
212
263
  name,
213
264
  ...(namespace && { namespace }),
@@ -217,22 +268,31 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
217
268
  maxUnavailable: 1,
218
269
  selector: { matchLabels: { "app.kubernetes.io/name": name } },
219
270
  },
220
- };
271
+ }, defs?.pdb));
272
+
273
+ // -- StatefulSet --
221
274
 
222
- // ── StatefulSet ─────────────────────────────────────────────────
275
+ // Build the cockroach start command as a shell string so that $(hostname -f)
276
+ // is expanded at runtime. K8s exec-form args do NOT invoke a shell, so
277
+ // $(...) substitutions are never evaluated unless we wrap with sh -c.
278
+ // When advertiseHostDomain is set, advertise the cross-cluster ExternalDNS name
279
+ // (e.g. "cockroachdb-0.east.crdb.internal") so gossip addresses are resolvable
280
+ // from other Kubernetes clusters. Otherwise fall back to the cluster-local FQDN.
281
+ const advertiseHostFlag = advertiseHostDomain
282
+ ? `--advertise-host=\${HOSTNAME}.${advertiseHostDomain}`
283
+ : `--advertise-host=$(hostname -f)`;
223
284
 
224
- const cockroachArgs = [
225
- "start",
285
+ const cockroachFlags = [
226
286
  `--logtostderr=WARNING`,
227
- `--certs-dir=${secure ? certsDir : ""}`,
228
- ...(secure ? [] : ["--insecure"]),
229
- `--advertise-host=$(hostname -f)`,
287
+ ...(secure ? [`--certs-dir=${certsDir}`] : ["--insecure"]),
288
+ advertiseHostFlag,
230
289
  `--http-addr=0.0.0.0`,
231
290
  `--cache=${cachePercent}`,
232
291
  `--max-sql-memory=${sqlMemoryPercent}`,
233
292
  ...(joinAddresses.length > 0 ? [`--join=${joinAddresses.join(",")}`] : []),
234
293
  ...(locality ? [`--locality=${locality}`] : []),
235
294
  ];
295
+ const cockroachStartCmd = `/cockroach/cockroach start ${cockroachFlags.join(" ")}`;
236
296
 
237
297
  const volumes: Record<string, unknown>[] = [];
238
298
  const volumeMounts: Record<string, unknown>[] = [
@@ -251,8 +311,8 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
251
311
  { containerPort: 26257, name: "grpc" },
252
312
  { containerPort: 8080, name: "http" },
253
313
  ],
254
- command: ["/cockroach/cockroach"],
255
- args: cockroachArgs,
314
+ command: ["/bin/sh", "-c"],
315
+ args: [cockroachStartCmd],
256
316
  resources: {
257
317
  limits: { cpu: cpuLimit, memory: memoryLimit },
258
318
  requests: { cpu: cpuLimit, memory: memoryLimit },
@@ -274,7 +334,7 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
274
334
  },
275
335
  };
276
336
 
277
- const statefulSet: Record<string, unknown> = {
337
+ const statefulSet = new StatefulSet(mergeDefaults({
278
338
  metadata: {
279
339
  name,
280
340
  ...(namespace && { namespace }),
@@ -318,26 +378,56 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
318
378
  },
319
379
  ],
320
380
  },
321
- };
381
+ }, defs?.statefulSet));
322
382
 
323
- // ── cert-gen Job ─────────────────────────────────────────────────
383
+ // -- cert-gen Job --
324
384
 
325
385
  // Generates self-signed CA and node certs, stores them in K8s Secrets.
326
386
  // Each node's cert includes the pod DNS names (pod-N.svc.namespace.svc.cluster.local).
387
+ // NOTE: The cert-gen Job uses `curl` to create K8s Secrets via the API server.
388
+ // CockroachDB UBI images (v24+) include curl. For multi-region deployments,
389
+ // use an external cert generation script instead (see generate-certs.sh).
327
390
  const nodeNames = Array.from({ length: replicas }, (_, i) => `${name}-${i}.${name}`);
391
+ // Include both short names (for in-cluster init job) and FQDNs (for cross-namespace/init connections).
328
392
  const nodeAddresses = namespace
329
- ? nodeNames.map((n) => `${n}.${namespace}.svc.cluster.local`)
330
- : nodeNames.map((n) => `${n}.default.svc.cluster.local`);
393
+ ? [...nodeNames, ...nodeNames.map((n) => `${n}.${namespace}.svc.cluster.local`)]
394
+ : [...nodeNames, ...nodeNames.map((n) => `${n}.default.svc.cluster.local`)];
395
+
396
+ const ns = namespace ?? "default";
397
+ const nodeSecretName = `${name}-node-certs`;
398
+ const clientSecretName = `${name}-client-certs`;
399
+
400
+ const allCertNodeAddresses = [...nodeAddresses, ...extraCertNodeAddresses];
331
401
 
332
402
  const certGenScript = [
333
403
  "set -ex",
334
404
  "cd /cockroach",
335
405
  "cockroach cert create-ca --certs-dir=certs --ca-key=certs/ca.key",
336
- `cockroach cert create-node ${nodeAddresses.join(" ")} localhost 127.0.0.1 --certs-dir=certs --ca-key=certs/ca.key`,
406
+ `cockroach cert create-node ${allCertNodeAddresses.join(" ")} localhost 127.0.0.1 --certs-dir=certs --ca-key=certs/ca.key`,
337
407
  "cockroach cert create-client root --certs-dir=certs --ca-key=certs/ca.key",
338
- ].join(" && ");
408
+ // Store certs in K8s Secrets via the API using the ServiceAccount token.
409
+ `TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)`,
410
+ `K8S_CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`,
411
+ `API=https://kubernetes.default.svc`,
412
+ `NS=${ns}`,
413
+ `encode() { base64 | tr -d '\\n'; }`,
414
+ // Build JSON payloads for the two secrets.
415
+ `NODE_PAYLOAD=$(printf '{"apiVersion":"v1","kind":"Secret","metadata":{"name":"${nodeSecretName}","namespace":"%s"},"type":"Opaque","data":{"ca.crt":"%s","node.crt":"%s","node.key":"%s"}}' "$NS" "$(cat certs/ca.crt | encode)" "$(cat certs/node.crt | encode)" "$(cat certs/node.key | encode)")`,
416
+ `CLIENT_PAYLOAD=$(printf '{"apiVersion":"v1","kind":"Secret","metadata":{"name":"${clientSecretName}","namespace":"%s"},"type":"Opaque","data":{"ca.crt":"%s","client.root.crt":"%s","client.root.key":"%s"}}' "$NS" "$(cat certs/ca.crt | encode)" "$(cat certs/client.root.crt | encode)" "$(cat certs/client.root.key | encode)")`,
417
+ // Create or update each secret.
418
+ `for SECRET_INFO in "${nodeSecretName}:$NODE_PAYLOAD" "${clientSecretName}:$CLIENT_PAYLOAD"; do`,
419
+ ` SECRET_NAME=\${SECRET_INFO%%:*}`,
420
+ ` PAYLOAD=\${SECRET_INFO#*:}`,
421
+ ` STATUS=$(curl -s --cacert $K8S_CA -H "Authorization: Bearer $TOKEN" -o /dev/null -w '%{http_code}' $API/api/v1/namespaces/$NS/secrets/$SECRET_NAME)`,
422
+ ` if [ "$STATUS" = "200" ]; then`,
423
+ ` curl -sf --cacert $K8S_CA -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -X PUT $API/api/v1/namespaces/$NS/secrets/$SECRET_NAME -d "$PAYLOAD"`,
424
+ ` else`,
425
+ ` curl -sf --cacert $K8S_CA -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -X POST $API/api/v1/namespaces/$NS/secrets -d "$PAYLOAD"`,
426
+ ` fi`,
427
+ `done`,
428
+ ].join("\n");
339
429
 
340
- const certGenJob: Record<string, unknown> = {
430
+ const certGenJob = skipCertGen ? undefined : new Job(mergeDefaults({
341
431
  metadata: {
342
432
  name: `${name}-cert-gen`,
343
433
  ...(namespace && { namespace }),
@@ -361,9 +451,9 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
361
451
  },
362
452
  },
363
453
  },
364
- };
454
+ }, defs?.certGenJob));
365
455
 
366
- // ── init Job ────────────────────────────────────────────────────
456
+ // -- init Job (only when skipCertGen is false) --
367
457
 
368
458
  const initArgs = secure
369
459
  ? [`--certs-dir=${certsDir}`, `--host=${name}-0.${name}`]
@@ -372,11 +462,11 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
372
462
  const initVolumes: Record<string, unknown>[] = [];
373
463
  const initVolumeMounts: Record<string, unknown>[] = [];
374
464
  if (secure) {
375
- initVolumes.push({ name: "client-certs", secret: { secretName: `${name}-node-certs`, defaultMode: 0o400 } });
465
+ initVolumes.push({ name: "client-certs", secret: { secretName: `${name}-client-certs`, defaultMode: 0o400 } });
376
466
  initVolumeMounts.push({ name: "client-certs", mountPath: certsDir });
377
467
  }
378
468
 
379
- const initJob: Record<string, unknown> = {
469
+ const initJob = skipInit ? undefined : new Job(mergeDefaults({
380
470
  metadata: {
381
471
  name: `${name}-init`,
382
472
  ...(namespace && { namespace }),
@@ -403,7 +493,7 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
403
493
  },
404
494
  },
405
495
  },
406
- };
496
+ }, defs?.initJob));
407
497
 
408
498
  return {
409
499
  serviceAccount,
@@ -415,7 +505,7 @@ export function CockroachDbCluster(props: CockroachDbClusterProps): CockroachDbC
415
505
  headlessService,
416
506
  pdb,
417
507
  statefulSet,
418
- initJob,
419
- certGenJob,
508
+ ...(initJob && { initJob }),
509
+ ...(certGenJob && { certGenJob }),
420
510
  };
421
- }
511
+ }, "CockroachDbCluster");