Skip to content

Commit

Permalink
fix(opensearch): domain doesn't handle tokens in capacity configurati…
Browse files Browse the repository at this point in the history
…on (aws#17131)

At the moment, tokens in OpenSearch capacity configuration lead to errors because they are not handled correctly for instance types. This fix provides the possibility to use tokens in instance types.

The changes are implemented in modules opensearch and elasticsearch.

Fixes aws#15014.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jumic authored and TikiTDO committed Feb 21, 2022
1 parent e20bf2e commit 222ed7e
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 28 deletions.
35 changes: 22 additions & 13 deletions packages/@aws-cdk/aws-elasticsearch/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1276,22 +1276,16 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
const defaultInstanceType = 'r5.large.elasticsearch';
const warmDefaultInstanceType = 'ultrawarm1.medium.elasticsearch';

const dedicatedMasterType =
props.capacity?.masterNodeInstanceType?.toLowerCase() ??
defaultInstanceType;
const dedicatedMasterType = initializeInstanceType(defaultInstanceType, props.capacity?.masterNodeInstanceType);
const dedicatedMasterCount = props.capacity?.masterNodes ?? 0;
const dedicatedMasterEnabled = dedicatedMasterCount > 0;
const dedicatedMasterEnabled = cdk.Token.isUnresolved(dedicatedMasterCount) ? true : dedicatedMasterCount > 0;

const instanceType =
props.capacity?.dataNodeInstanceType?.toLowerCase() ??
defaultInstanceType;
const instanceType = initializeInstanceType(defaultInstanceType, props.capacity?.dataNodeInstanceType);
const instanceCount = props.capacity?.dataNodes ?? 1;

const warmType =
props.capacity?.warmInstanceType?.toLowerCase() ??
warmDefaultInstanceType;
const warmType = initializeInstanceType(warmDefaultInstanceType, props.capacity?.warmInstanceType);
const warmCount = props.capacity?.warmNodes ?? 0;
const warmEnabled = warmCount > 0;
const warmEnabled = cdk.Token.isUnresolved(warmCount) ? true : warmCount > 0;

const availabilityZoneCount =
props.zoneAwareness?.availabilityZoneCount ?? 2;
Expand Down Expand Up @@ -1322,11 +1316,11 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
throw new Error('When providing vpc options you need to provide a subnet for each AZ you are using');
}

if ([dedicatedMasterType, instanceType, warmType].some(t => !t.endsWith('.elasticsearch'))) {
if ([dedicatedMasterType, instanceType, warmType].some(t => (!cdk.Token.isUnresolved(t) && !t.endsWith('.elasticsearch')))) {
throw new Error('Master, data and UltraWarm node instance types must end with ".elasticsearch".');
}

if (!warmType.startsWith('ultrawarm')) {
if (!cdk.Token.isUnresolved(warmType) && !warmType.startsWith('ultrawarm')) {
throw new Error('UltraWarm node instance type must start with "ultrawarm".');
}

Expand Down Expand Up @@ -1836,3 +1830,18 @@ function selectSubnets(vpc: ec2.IVpc, vpcSubnets: ec2.SubnetSelection[]): ec2.IS
}
return selected;
}

/**
* Initializes an instance type.
*
* @param defaultInstanceType Default instance type which is used if no instance type is provided
* @param instanceType Instance type
* @returns Instance type in lowercase (if provided) or default instance type
*/
function initializeInstanceType(defaultInstanceType: string, instanceType?: string): string {
if (instanceType) {
return cdk.Token.isUnresolved(instanceType) ? instanceType : instanceType.toLowerCase();
} else {
return defaultInstanceType;
}
}
41 changes: 40 additions & 1 deletion packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import * as logs from '@aws-cdk/aws-logs';
import * as route53 from '@aws-cdk/aws-route53';
import { App, Stack, Duration, SecretValue, CfnParameter } from '@aws-cdk/core';
import { App, Stack, Duration, SecretValue, CfnParameter, Token } from '@aws-cdk/core';
import { Domain, ElasticsearchVersion } from '../lib';

let app: App;
Expand Down Expand Up @@ -307,6 +307,45 @@ describe('UltraWarm instances', () => {

});

test('can use tokens in capacity configuration', () => {
new Domain(stack, 'Domain', {
version: ElasticsearchVersion.V7_10,
capacity: {
dataNodeInstanceType: Token.asString({ Ref: 'dataNodeInstanceType' }),
dataNodes: Token.asNumber({ Ref: 'dataNodes' }),
masterNodeInstanceType: Token.asString({ Ref: 'masterNodeInstanceType' }),
masterNodes: Token.asNumber({ Ref: 'masterNodes' }),
warmInstanceType: Token.asString({ Ref: 'warmInstanceType' }),
warmNodes: Token.asNumber({ Ref: 'warmNodes' }),
},
});

expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', {
ElasticsearchClusterConfig: {
InstanceCount: {
Ref: 'dataNodes',
},
InstanceType: {
Ref: 'dataNodeInstanceType',
},
DedicatedMasterEnabled: true,
DedicatedMasterCount: {
Ref: 'masterNodes',
},
DedicatedMasterType: {
Ref: 'masterNodeInstanceType',
},
WarmEnabled: true,
WarmCount: {
Ref: 'warmNodes',
},
WarmType: {
Ref: 'warmInstanceType',
},
},
});
});

describe('log groups', () => {

test('slowSearchLogEnabled should create a custom log group', () => {
Expand Down
35 changes: 22 additions & 13 deletions packages/@aws-cdk/aws-opensearchservice/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1204,22 +1204,16 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
const defaultInstanceType = 'r5.large.search';
const warmDefaultInstanceType = 'ultrawarm1.medium.search';

const dedicatedMasterType =
props.capacity?.masterNodeInstanceType?.toLowerCase() ??
defaultInstanceType;
const dedicatedMasterType = initializeInstanceType(defaultInstanceType, props.capacity?.masterNodeInstanceType);
const dedicatedMasterCount = props.capacity?.masterNodes ?? 0;
const dedicatedMasterEnabled = dedicatedMasterCount > 0;
const dedicatedMasterEnabled = cdk.Token.isUnresolved(dedicatedMasterCount) ? true : dedicatedMasterCount > 0;

const instanceType =
props.capacity?.dataNodeInstanceType?.toLowerCase() ??
defaultInstanceType;
const instanceType = initializeInstanceType(defaultInstanceType, props.capacity?.dataNodeInstanceType);
const instanceCount = props.capacity?.dataNodes ?? 1;

const warmType =
props.capacity?.warmInstanceType?.toLowerCase() ??
warmDefaultInstanceType;
const warmType = initializeInstanceType(warmDefaultInstanceType, props.capacity?.warmInstanceType);
const warmCount = props.capacity?.warmNodes ?? 0;
const warmEnabled = warmCount > 0;
const warmEnabled = cdk.Token.isUnresolved(warmCount) ? true : warmCount > 0;

const availabilityZoneCount =
props.zoneAwareness?.availabilityZoneCount ?? 2;
Expand Down Expand Up @@ -1250,11 +1244,11 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
throw new Error('When providing vpc options you need to provide a subnet for each AZ you are using');
}

if ([dedicatedMasterType, instanceType, warmType].some(t => !t.endsWith('.search'))) {
if ([dedicatedMasterType, instanceType, warmType].some(t => (!cdk.Token.isUnresolved(t) && !t.endsWith('.search')))) {
throw new Error('Master, data and UltraWarm node instance types must end with ".search".');
}

if (!warmType.startsWith('ultrawarm')) {
if (!cdk.Token.isUnresolved(warmType) && !warmType.startsWith('ultrawarm')) {
throw new Error('UltraWarm node instance type must start with "ultrawarm".');
}

Expand Down Expand Up @@ -1761,3 +1755,18 @@ function selectSubnets(vpc: ec2.IVpc, vpcSubnets: ec2.SubnetSelection[]): ec2.IS
}
return selected;
}

/**
* Initializes an instance type.
*
* @param defaultInstanceType Default instance type which is used if no instance type is provided
* @param instanceType Instance type
* @returns Instance type in lowercase (if provided) or default instance type
*/
function initializeInstanceType(defaultInstanceType: string, instanceType?: string): string {
if (instanceType) {
return cdk.Token.isUnresolved(instanceType) ? instanceType : instanceType.toLowerCase();
} else {
return defaultInstanceType;
}
}
41 changes: 40 additions & 1 deletion packages/@aws-cdk/aws-opensearchservice/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import * as logs from '@aws-cdk/aws-logs';
import * as route53 from '@aws-cdk/aws-route53';
import { App, Stack, Duration, SecretValue, CfnParameter } from '@aws-cdk/core';
import { App, Stack, Duration, SecretValue, CfnParameter, Token } from '@aws-cdk/core';
import { Domain, EngineVersion } from '../lib';

let app: App;
Expand Down Expand Up @@ -248,6 +248,45 @@ describe('UltraWarm instances', () => {

});

test('can use tokens in capacity configuration', () => {
new Domain(stack, 'Domain', {
version: defaultVersion,
capacity: {
dataNodeInstanceType: Token.asString({ Ref: 'dataNodeInstanceType' }),
dataNodes: Token.asNumber({ Ref: 'dataNodes' }),
masterNodeInstanceType: Token.asString({ Ref: 'masterNodeInstanceType' }),
masterNodes: Token.asNumber({ Ref: 'masterNodes' }),
warmInstanceType: Token.asString({ Ref: 'warmInstanceType' }),
warmNodes: Token.asNumber({ Ref: 'warmNodes' }),
},
});

expect(stack).toHaveResourceLike('AWS::OpenSearchService::Domain', {
ClusterConfig: {
InstanceCount: {
Ref: 'dataNodes',
},
InstanceType: {
Ref: 'dataNodeInstanceType',
},
DedicatedMasterEnabled: true,
DedicatedMasterCount: {
Ref: 'masterNodes',
},
DedicatedMasterType: {
Ref: 'masterNodeInstanceType',
},
WarmEnabled: true,
WarmCount: {
Ref: 'warmNodes',
},
WarmType: {
Ref: 'warmInstanceType',
},
},
});
});

describe('log groups', () => {

test('slowSearchLogEnabled should create a custom log group', () => {
Expand Down

0 comments on commit 222ed7e

Please sign in to comment.