Skip to content

Commit

Permalink
feat(opensearch): add node options configuration for coordinator nodes
Browse files Browse the repository at this point in the history
### Reason for this change
Introduced new interfaces and validation for configuring coordinator nodes in OpenSearch domains, allowing users to specify node options such as instance type and count.

### Description of changes
- Added `NodeOptions` and `NodeConfig` interfaces to define configurations for coordinator nodes.
- Updated the `Domain` class to handle `nodeOptions` in the capacity configuration, including validation for instance type and count.
- Enhanced unit tests to cover new configurations and validation scenarios for coordinator nodes.

### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
dubesar1708 committed Jan 15, 2025
1 parent c7d6fb6 commit e5bb801
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 2 deletions.
66 changes: 65 additions & 1 deletion packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ export interface CapacityConfig {
* is true, no multi-az with standby otherwise
*/
readonly multiAzWithStandbyEnabled?: boolean;

/**
* Additional node options for the domain
*
* @default - no additional node options
*/
readonly nodeOptions?: NodeOptions[];
}

/**
Expand Down Expand Up @@ -380,7 +387,7 @@ export interface AdvancedSecurityOptions {

/**
* Container for information about the SAML configuration for OpenSearch Dashboards.
* If set, `samlAuthenticationEnabled` will be enabled.
* If set, `samlAuthenticationEnabled` will be enabled.
*
* @default - no SAML authentication options
*/
Expand Down Expand Up @@ -439,6 +446,47 @@ export enum IpAddressType {
DUAL_STACK = 'dualstack',
}

/**
* Configuration for a specific node type in OpenSearch domain
*/
export interface NodeConfig {
/**
* Whether this node type is enabled
*
* @default - false
*/
readonly enabled?: boolean;

/**
* The instance type for the nodes
*
* @default - m5.large.search
*/
readonly type?: string;

/**
* The number of nodes of this type
*
* @default - 1
*/
readonly count?: number;
}

/**
* Configuration for node options in OpenSearch domain
*/
export interface NodeOptions {
/**
* The type of node. Currently only 'coordinator' is supported.
*/
readonly nodeType: 'coordinator';

/**
* Configuration for the node type
*/
readonly nodeConfig: NodeConfig;
}

/**
* Properties for an Amazon OpenSearch Service domain.
*/
Expand Down Expand Up @@ -1393,6 +1441,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {

const defaultInstanceType = 'r5.large.search';
const warmDefaultInstanceType = 'ultrawarm1.medium.search';
const defaultCoordinatorInstanceType = 'm5.large.search';

const dedicatedMasterType = initializeInstanceType(defaultInstanceType, props.capacity?.masterNodeInstanceType);
const dedicatedMasterCount = props.capacity?.masterNodes ?? 0;
Expand Down Expand Up @@ -1871,6 +1920,20 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
this.validateSamlAuthenticationOptions(props.fineGrainedAccessControl?.samlAuthenticationOptions);
}

if (props.capacity?.nodeOptions) {
// Validate coordinator node configuration
const coordinatorConfig = props.capacity.nodeOptions.find(opt => opt.nodeType === 'coordinator')?.nodeConfig;
if (coordinatorConfig?.enabled) {
const coordinatorType = initializeInstanceType(defaultCoordinatorInstanceType, coordinatorConfig.type);
if (!cdk.Token.isUnresolved(coordinatorType) && !coordinatorType.endsWith('.search')) {
throw new Error('Coordinator node instance type must end with ".search".');
}
if (coordinatorConfig.count !== undefined && coordinatorConfig.count < 1) {
throw new Error('Coordinator node count must be at least 1.');
}
}
}

// Create the domain
this.domain = new CfnDomain(this, 'Resource', {
domainName: this.physicalName,
Expand Down Expand Up @@ -1902,6 +1965,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
zoneAwarenessConfig: zoneAwarenessEnabled
? { availabilityZoneCount }
: undefined,
nodeOptions: props.capacity?.nodeOptions,
},
ebsOptions: {
ebsEnabled,
Expand Down
93 changes: 92 additions & 1 deletion packages/aws-cdk-lib/aws-opensearchservice/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as logs from '../../aws-logs';
import * as route53 from '../../aws-route53';
import { App, Stack, Duration, SecretValue, CfnParameter, Token } from '../../core';
import * as cxapi from '../../cx-api';
import { Domain, DomainProps, EngineVersion, IpAddressType } from '../lib';
import { Domain, DomainProps, EngineVersion, IpAddressType, NodeOptions } from '../lib';

let app: App;
let stack: Stack;
Expand Down Expand Up @@ -2747,3 +2747,94 @@ function testMetric(
});
expect(metric.dimensions).toHaveProperty('DomainName');
}

each(testedOpenSearchVersions).test('can configure coordinator nodes with nodeOptions', (engineVersion) => {
const coordinatorConfig: NodeOptions = {
nodeType: 'coordinator',
nodeConfig: {
enabled: true,
type: 'm5.large.search',
count: 2,
},
};

const domain = new Domain(stack, 'Domain', {
version: engineVersion,
capacity: {
nodeOptions: [coordinatorConfig],
},
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
ClusterConfig: {
NodeOptions: [{
NodeType: 'coordinator',
NodeConfig: {
Enabled: true,
Type: 'm5.large.search',
Count: 2,
},
}],
},
});
});

each(testedOpenSearchVersions).test('throws when coordinator node instance type does not end with .search', (engineVersion) => {
expect(() => {
new Domain(stack, 'Domain', {
version: engineVersion,
capacity: {
nodeOptions: [{
nodeType: 'coordinator' as const,
nodeConfig: {
enabled: true,
type: 'm5.large',
},
}],
},
});
}).toThrow('Coordinator node instance type must end with ".search".');
});

each(testedOpenSearchVersions).test('throws when coordinator node count is less than 1', (engineVersion) => {
expect(() => {
new Domain(stack, 'Domain', {
version: engineVersion,
capacity: {
nodeOptions: [{
nodeType: 'coordinator' as const,
nodeConfig: {
enabled: true,
count: 0,
type: 'm5.large.search',
},
}],
},
});
}).toThrow('Coordinator node count must be at least 1.');
});

each(testedOpenSearchVersions).test('can disable coordinator nodes', (engineVersion) => {
const domain = new Domain(stack, 'Domain', {
version: engineVersion,
capacity: {
nodeOptions: [{
nodeType: 'coordinator' as const,
nodeConfig: {
enabled: false,
},
}],
},
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
ClusterConfig: {
NodeOptions: [{
NodeType: 'coordinator',
NodeConfig: {
Enabled: false,
},
}],
},
});
});

0 comments on commit e5bb801

Please sign in to comment.