diff --git a/src/cluster/cluster-create/ask-cluster-def.ts b/src/cluster/cluster-create/ask-cluster-def.ts index 32fdb19..4ad4d3d 100644 --- a/src/cluster/cluster-create/ask-cluster-def.ts +++ b/src/cluster/cluster-create/ask-cluster-def.ts @@ -53,6 +53,12 @@ export interface AskClusterDefOptions { * manages the cluster topology, so no node count or service list is asked. */ capellaCloudProvider?: CapellaCloudProvider; + /** + * Enable the Capella Data API at cluster create time. Decided one level up + * (the definition builder's Data API prompt), so it is passed in. Only + * meaningful with {@link capellaCloudProvider}. + */ + capellaDataApi?: boolean; /** * Build a Capella Analytics (cloud) cluster — emits cbdinocluster's * `columnar: true` + `deployer: cloud`. Asks for cloud provider; cbdinocluster @@ -74,7 +80,14 @@ export async function askClusterDef(options: AskClusterDefOptions = {}): Promise }); // Capella manages cluster topology; no service list needed. // 3 nodes so cbdinocluster's cloud deployer (which always sends numReplicas=1) is valid. - return { nodeCount: 3, version, services: [], cng: false, capellaCloudProvider: options.capellaCloudProvider }; + return { + nodeCount: 3, + version, + services: [], + cng: false, + capellaCloudProvider: options.capellaCloudProvider, + ...(options.capellaDataApi ? { capellaDataApi: true } : {}), + }; } if (options.cng) { diff --git a/src/cluster/cluster-create/build-cluster-def.ts b/src/cluster/cluster-create/build-cluster-def.ts index 3af8927..08c94dd 100644 --- a/src/cluster/cluster-create/build-cluster-def.ts +++ b/src/cluster/cluster-create/build-cluster-def.ts @@ -48,6 +48,12 @@ export interface ClusterDef { enterpriseAnalytics?: boolean; /** Cloud provider for a Capella cluster; when set the `cloud` deployer is used. */ capellaCloudProvider?: CapellaCloudProvider; + /** + * Enable the Capella Data API when the cluster is created. Off by default: + * the Data API adds minutes to the allocation, so only runs that need it + * should request it. Capella Server clusters only; Capella Analytics rejects it. + */ + capellaDataApi?: boolean; /** Whether to build a Capella Analytics (cloud) cluster via cbdinocluster's cloud deployer. */ capellaAnalytics?: boolean; /** Cloud provider for a Capella Analytics cluster. */ @@ -127,8 +133,8 @@ export interface CbdinoclusterDef { columnar?: boolean; /** cbdinocluster deployer: "cloud" for Capella Analytics; "docker" (or absent) for Enterprise Analytics. */ deployer?: string; - /** Cloud configuration for Capella Analytics clusters. */ - cloud?: { "cloud-provider": string; region?: string }; + /** Cloud configuration for Capella clusters. `data-api` is valid for Capella Server only. */ + cloud?: { "cloud-provider": string; region?: string; "data-api"?: boolean }; /** Per-service RAM quotas for the docker deployer; omitted for other deployers. */ docker?: CbdinoclusterDockerDef; /** Pass-through: any other cbdinocluster cluster-def keys are forwarded as-is. */ @@ -164,7 +170,10 @@ export function buildClusterDefObject(def: ClusterDef): CbdinoclusterDef { // underlying infrastructure to use. No `docker` block — Capella manages resources. return { nodes: [{ count: def.nodeCount, version: def.version }], - cloud: { "cloud-provider": def.capellaCloudProvider }, + cloud: { + "cloud-provider": def.capellaCloudProvider, + ...(def.capellaDataApi ? { "data-api": true } : {}), + }, }; } if (def.capellaAnalytics) { diff --git a/src/cluster/cluster-create/tests/build-cluster-def.test.ts b/src/cluster/cluster-create/tests/build-cluster-def.test.ts index e5ee3b1..b5a50ac 100644 --- a/src/cluster/cluster-create/tests/build-cluster-def.test.ts +++ b/src/cluster/cluster-create/tests/build-cluster-def.test.ts @@ -102,6 +102,43 @@ test("buildClusterDefObject uses cao (not docker) for CNG clusters", () => { assert.equal(def.cao?.["operator-version"], CAO_OPERATOR_VERSION); }); +test("buildClusterDefObject emits data-api in the cloud section when capellaDataApi is set", () => { + const def = buildClusterDefObject({ + nodeCount: 3, + version: "7.6", + services: [], + cng: false, + capellaCloudProvider: "aws", + capellaDataApi: true, + }); + assert.deepEqual(def.cloud, { "cloud-provider": "aws", "data-api": true }); +}); + +test("buildClusterDefObject omits data-api by default for Capella clusters", () => { + const def = buildClusterDefObject({ + nodeCount: 3, + version: "7.6", + services: [], + cng: false, + capellaCloudProvider: "aws", + }); + assert.deepEqual(def.cloud, { "cloud-provider": "aws" }); +}); + +test("buildClusterDefObject never emits data-api for Capella Analytics clusters", () => { + // Capella Analytics rejects the data-api field, so it must not appear on that path. + const def = buildClusterDefObject({ + nodeCount: 2, + version: "", + services: [], + cng: false, + capellaAnalytics: true, + cloudProvider: "aws", + capellaDataApi: true, + }); + assert.deepEqual(def.cloud, { "cloud-provider": "aws" }); +}); + test("buildClusterDefObject emits cbdino columnar:true + an nginx load balancer for a self-managed Enterprise Analytics cluster", () => { const def = buildClusterDefObject({ nodeCount: 2, diff --git a/src/fit/shared/create-definition/create-definition.ts b/src/fit/shared/create-definition/create-definition.ts index 045c801..628cc5f 100644 --- a/src/fit/shared/create-definition/create-definition.ts +++ b/src/fit/shared/create-definition/create-definition.ts @@ -143,7 +143,11 @@ export function functionalInstanceConnectivity( return "operational"; } -async function chooseFunctionalDefinitionCluster(connectivity: FunctionalConnectivity, capellaCloudProvider?: CapellaCloudProvider): Promise { +async function chooseFunctionalDefinitionCluster( + connectivity: FunctionalConnectivity, + capellaCloudProvider?: CapellaCloudProvider, + capellaDataApi?: boolean, +): Promise { if (connectivity === "cng") { return { kind: "cbdinocluster", def: await askClusterDef({ cng: true }) }; } @@ -151,7 +155,7 @@ async function chooseFunctionalDefinitionCluster(connectivity: FunctionalConnect return { kind: "cbdinocluster", def: await askClusterDef({ enterpriseAnalytics: true }) }; } if (connectivity === "capella") { - return { kind: "cbdinocluster", def: await askClusterDef({ capellaCloudProvider }) }; + return { kind: "cbdinocluster", def: await askClusterDef({ capellaCloudProvider, ...(capellaDataApi ? { capellaDataApi } : {}) }) }; } if (connectivity === "capella-analytics") { return { kind: "cbdinocluster", def: await askClusterDef({ capellaAnalytics: true }) }; @@ -185,6 +189,19 @@ async function askCapellaPrivateEndpoint(promptIdPrefix: string): Promise { + return confirm({ + promptId: qualifyPromptId("capella.data-api", promptIdPrefix), + message: "Enable the Capella Data API on this cluster?", + default: false, + }); +} + async function ensureSharedRepoSetup(state: DefinitionBuilderState): Promise { if (state.gerritRefAsked) { return; @@ -356,9 +373,11 @@ async function addFunctionalRun( let capellaCloudProvider: CapellaCloudProvider | undefined; let capellaEnvironment: string | undefined; let capellaPrivateEndpoint = false; + let capellaDataApi = false; if (connectivity === "capella") { capellaCloudProvider = await chooseCapellaCloudProvider(promptIdPrefix); capellaEnvironment = await chooseCapellaEnvironment(promptIdPrefix); + capellaDataApi = await askCapellaDataApi(promptIdPrefix); if (capellaCloudProvider === "aws") { capellaPrivateEndpoint = await askCapellaPrivateEndpoint(promptIdPrefix); } @@ -380,7 +399,7 @@ async function addFunctionalRun( const instance = capellaPrivateEndpoint ? { aws: { privateEndpoint: {} } } : await chooseInstanceExecution(promptIdPrefix); - const cluster = await chooseFunctionalDefinitionCluster(connectivity, capellaCloudProvider); + const cluster = await chooseFunctionalDefinitionCluster(connectivity, capellaCloudProvider, capellaDataApi); const onClusterExists = cluster.kind === "cbdinocluster" ? await askClusterExistsPolicy() : undefined; const subDef = buildFitFunctionalDefinitionFrom({ cluster, @@ -430,6 +449,7 @@ function functionalDefinitionCluster(instance: InstanceLifetime, clusterConfigs: : { enterpriseAnalytics: true } : {}), ...(clusterData.cbdinocluster.capella ? { capellaCloudProvider: clusterData.cbdinocluster.capella.cloudProvider } : {}), + ...(clusterData.cbdinocluster.config.cloud?.["data-api"] === true ? { capellaDataApi: true } : {}), }, }; }