Skip to content

Commit

Permalink
fix(opensearch): correctly validate ebs configuration against instanc…
Browse files Browse the repository at this point in the history
…e types (#16911)

Currently, the following valid cluster configurations are not possible to be created with CDK.

```ts
new es.Domain(stack, 'Domain1', {
    version: es.EngineVersion.OPENSEARCH_1_0,
    ebs: { enabled: false },
    capacity: {
        masterNodes: 3,
        masterNodeInstanceType: 'c5.2xlarge.search',
        dataNodeInstanceType: 'i3.2xlarge.search'
    }
});


new es.Domain(stack, 'Domain2', {
    version: es.EngineVersion.OPENSEARCH_1_0,
    ebs: { enabled: false, },
    capacity: {
        masterNodes: 3,
        masterNodeInstanceType: 'c6g.large.search',
        dataNodeInstanceType: 'r6gd.large.search'
    }
});
```
Throws error:
`EBS volumes are required when using instance types other than r3, i3 or r6gd.`

Here, EBS validation is being checked on master nodes instead of just the data nodes -  which causes the above failure.

This PR applies fix on both `elasticsearch` and `opensearchservice` modules

Fixes #11898 

----
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
nom3ad authored Nov 19, 2021
1 parent 5f1f476 commit 34af598
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 10 deletions.
7 changes: 3 additions & 4 deletions packages/@aws-cdk/aws-elasticsearch/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1403,9 +1403,8 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
return instanceTypes.some(isInstanceType);
};

function isEveryInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(t => dedicatedMasterType.startsWith(t))
&& instanceTypes.some(t => instanceType.startsWith(t));
function isEveryDatanodeInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(t => instanceType.startsWith(t));
};

// Validate feature support for the given Elasticsearch version, per
Expand Down Expand Up @@ -1464,7 +1463,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {

// Only R3, I3 and r6gd support instance storage, per
// https://aws.amazon.com/elasticsearch-service/pricing/
if (!ebsEnabled && !isEveryInstanceType('r3', 'i3', 'r6gd')) {
if (!ebsEnabled && !isEveryDatanodeInstanceType('r3', 'i3', 'r6gd')) {
throw new Error('EBS volumes are required when using instance types other than r3, i3 or r6gd.');
}

Expand Down
47 changes: 46 additions & 1 deletion packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,7 @@ describe('custom error responses', () => {
})).toThrow(/Node-to-node encryption requires Elasticsearch version 6.0 or later/);
});

test('error when i3 instance types are specified with EBS enabled', () => {
test('error when i3 or r6g instance types are specified with EBS enabled', () => {
expect(() => new Domain(stack, 'Domain1', {
version: ElasticsearchVersion.V7_4,
capacity: {
Expand All @@ -1448,6 +1448,16 @@ describe('custom error responses', () => {
volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD,
},
})).toThrow(/I3 and R6GD instance types do not support EBS storage volumes/);
expect(() => new Domain(stack, 'Domain2', {
version: ElasticsearchVersion.V7_4,
capacity: {
dataNodeInstanceType: 'r6gd.large.elasticsearch',
},
ebs: {
volumeSize: 100,
volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD,
},
})).toThrow(/I3 and R6GD instance types do not support EBS storage volumes/);
});

test('error when m3, r3, or t2 instance types are specified with encryption at rest enabled', () => {
Expand Down Expand Up @@ -1500,6 +1510,41 @@ describe('custom error responses', () => {
masterNodeInstanceType: 'm5.large.elasticsearch',
},
})).toThrow(/EBS volumes are required when using instance types other than r3, i3 or r6gd/);
expect(() => new Domain(stack, 'Domain2', {
version: ElasticsearchVersion.V7_4,
ebs: {
enabled: false,
},
capacity: {
dataNodeInstanceType: 'm5.large.elasticsearch',
},
})).toThrow(/EBS volumes are required when using instance types other than r3, i3 or r6gd/);
});

test('can use compatible master instance types that does not have local storage when data node type is i3 or r6gd', () => {
new Domain(stack, 'Domain1', {
version: ElasticsearchVersion.V7_4,
ebs: {
enabled: false,
},
capacity: {
masterNodeInstanceType: 'c5.2xlarge.elasticsearch',
dataNodeInstanceType: 'i3.2xlarge.elasticsearch',
},
});
new Domain(stack, 'Domain2', {
version: ElasticsearchVersion.V7_4,
ebs: {
enabled: false,
},
capacity: {
masterNodes: 3,
masterNodeInstanceType: 'c6g.large.elasticsearch',
dataNodeInstanceType: 'r6gd.large.elasticsearch',
},
});
// both configurations pass synth-time validation
expect(stack).toCountResources('AWS::Elasticsearch::Domain', 2);
});

test('error when availabilityZoneCount is not 2 or 3', () => {
Expand Down
7 changes: 3 additions & 4 deletions packages/@aws-cdk/aws-opensearchservice/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1320,9 +1320,8 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
return instanceTypes.some(isInstanceType);
};

function isEveryInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(t => dedicatedMasterType.startsWith(t))
&& instanceTypes.some(t => instanceType.startsWith(t));
function isEveryDatanodeInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(t => instanceType.startsWith(t));
};

// Validate feature support for the given Elasticsearch/OpenSearch version, per
Expand Down Expand Up @@ -1396,7 +1395,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {

// Only R3, I3 and r6gd support instance storage, per
// https://aws.amazon.com/opensearch-service/pricing/
if (!ebsEnabled && !isEveryInstanceType('r3', 'i3', 'r6gd')) {
if (!ebsEnabled && !isEveryDatanodeInstanceType('r3', 'i3', 'r6gd')) {
throw new Error('EBS volumes are required when using instance types other than r3, i3 or r6gd.');
}

Expand Down
47 changes: 46 additions & 1 deletion packages/@aws-cdk/aws-opensearchservice/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,7 +1439,7 @@ describe('custom error responses', () => {
})).toThrow(/Node-to-node encryption requires Elasticsearch version 6.0 or later or OpenSearch version 1.0 or later/);
});

test('error when i3 instance types are specified with EBS enabled', () => {
test('error when i3 or r6g instance types are specified with EBS enabled', () => {
expect(() => new Domain(stack, 'Domain1', {
version: defaultVersion,
capacity: {
Expand All @@ -1450,6 +1450,16 @@ describe('custom error responses', () => {
volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD,
},
})).toThrow(/I3 and R6GD instance types do not support EBS storage volumes/);
expect(() => new Domain(stack, 'Domain2', {
version: defaultVersion,
capacity: {
dataNodeInstanceType: 'r6gd.large.search',
},
ebs: {
volumeSize: 100,
volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD,
},
})).toThrow(/I3 and R6GD instance types do not support EBS storage volumes/);
});

test('error when m3, r3, or t2 instance types are specified with encryption at rest enabled', () => {
Expand Down Expand Up @@ -1502,6 +1512,41 @@ describe('custom error responses', () => {
masterNodeInstanceType: 'm5.large.search',
},
})).toThrow(/EBS volumes are required when using instance types other than r3, i3 or r6gd/);
expect(() => new Domain(stack, 'Domain2', {
version: defaultVersion,
ebs: {
enabled: false,
},
capacity: {
dataNodeInstanceType: 'm5.large.search',
},
})).toThrow(/EBS volumes are required when using instance types other than r3, i3 or r6gd/);
});

test('can use compatible master instance types that does not have local storage when data node type is i3 or r6gd', () => {
new Domain(stack, 'Domain1', {
version: defaultVersion,
ebs: {
enabled: false,
},
capacity: {
masterNodeInstanceType: 'c5.2xlarge.search',
dataNodeInstanceType: 'i3.2xlarge.search',
},
});
new Domain(stack, 'Domain2', {
version: defaultVersion,
ebs: {
enabled: false,
},
capacity: {
masterNodes: 3,
masterNodeInstanceType: 'c6g.large.search',
dataNodeInstanceType: 'r6gd.large.search',
},
});
// both configurations pass synth-time validation
expect(stack).toCountResources('AWS::OpenSearchService::Domain', 2);
});

test('error when availabilityZoneCount is not 2 or 3', () => {
Expand Down

0 comments on commit 34af598

Please sign in to comment.