Skip to content

Commit 1163137

Browse files
committed
feat(elasticsearch): throw typed errors
1 parent 0f71ee1 commit 1163137

File tree

2 files changed

+38
-36
lines changed

2 files changed

+38
-36
lines changed

packages/aws-cdk-lib/.eslintrc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ baseConfig.rules['@cdklabs/no-throw-default-error'] = ['error'];
1919
const noThrowDefaultErrorNotYetSupported = [
2020
'aws-ecs-patterns',
2121
'aws-ecs',
22-
'aws-elasticsearch',
22+
'aws-eks',
23+
'aws-events-targets',
2324
'aws-globalaccelerator',
2425
'aws-globalaccelerator-endpoints',
2526
'aws-iam',

packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts

+36-35
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as logs from '../../aws-logs';
1414
import * as route53 from '../../aws-route53';
1515
import * as secretsmanager from '../../aws-secretsmanager';
1616
import * as cdk from '../../core';
17+
import { ValidationError } from '../../core';
1718
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
1819

1920
/**
@@ -1487,7 +1488,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
14871488
props.zoneAwareness?.availabilityZoneCount ?? 2;
14881489

14891490
if (![2, 3].includes(availabilityZoneCount)) {
1490-
throw new Error('Invalid zone awareness configuration; availabilityZoneCount must be 2 or 3');
1491+
throw new ValidationError('Invalid zone awareness configuration; availabilityZoneCount must be 2 or 3', this);
14911492
}
14921493

14931494
const zoneAwarenessEnabled =
@@ -1508,19 +1509,19 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
15081509

15091510
// If VPC options are supplied ensure that the number of subnets matches the number AZ
15101511
if (subnets && zoneAwarenessEnabled && new Set(subnets.map((subnet) => subnet.availabilityZone)).size < availabilityZoneCount) {
1511-
throw new Error('When providing vpc options you need to provide a subnet for each AZ you are using');
1512+
throw new ValidationError('When providing vpc options you need to provide a subnet for each AZ you are using', this);
15121513
}
15131514

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

15181519
if (!cdk.Token.isUnresolved(warmType) && !warmType.startsWith('ultrawarm')) {
1519-
throw new Error('UltraWarm node instance type must start with "ultrawarm".');
1520+
throw new ValidationError('UltraWarm node instance type must start with "ultrawarm".', this);
15201521
}
15211522

15221523
const elasticsearchVersion = props.version.version;
1523-
const elasticsearchVersionNum = parseVersion(props.version);
1524+
const elasticsearchVersionNum = parseVersion(this, props.version);
15241525

15251526
if (
15261527
elasticsearchVersionNum <= 7.7 &&
@@ -1530,20 +1531,20 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
15301531
7.7,
15311532
].includes(elasticsearchVersionNum)
15321533
) {
1533-
throw new Error(`Unknown Elasticsearch version: ${elasticsearchVersion}`);
1534+
throw new ValidationError(`Unknown Elasticsearch version: ${elasticsearchVersion}`, this);
15341535
}
15351536

15361537
const unsignedBasicAuthEnabled = props.useUnsignedBasicAuth ?? false;
15371538

15381539
if (unsignedBasicAuthEnabled) {
15391540
if (props.enforceHttps == false) {
1540-
throw new Error('You cannot disable HTTPS and use unsigned basic auth');
1541+
throw new ValidationError('You cannot disable HTTPS and use unsigned basic auth', this);
15411542
}
15421543
if (props.nodeToNodeEncryption == false) {
1543-
throw new Error('You cannot disable node to node encryption and use unsigned basic auth');
1544+
throw new ValidationError('You cannot disable node to node encryption and use unsigned basic auth', this);
15441545
}
15451546
if (props.encryptionAtRest?.enabled == false) {
1546-
throw new Error('You cannot disable encryption at rest and use unsigned basic auth');
1547+
throw new ValidationError('You cannot disable encryption at rest and use unsigned basic auth', this);
15471548
}
15481549
}
15491550

@@ -1562,7 +1563,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
15621563
: masterUserNameProps;
15631564

15641565
if (masterUserArn != null && masterUserName != null) {
1565-
throw new Error('Invalid fine grained access control settings. Only provide one of master user ARN or master user name. Not both.');
1566+
throw new ValidationError('Invalid fine grained access control settings. Only provide one of master user ARN or master user name. Not both.', this);
15661567
}
15671568

15681569
const advancedSecurityEnabled = (masterUserArn ?? masterUserName) != null;
@@ -1608,86 +1609,86 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
16081609
// https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-features-by-version.html
16091610
if (elasticsearchVersionNum < 5.1) {
16101611
if (props.logging?.appLogEnabled) {
1611-
throw new Error('Error logs publishing requires Elasticsearch version 5.1 or later.');
1612+
throw new ValidationError('Error logs publishing requires Elasticsearch version 5.1 or later.', this);
16121613
}
16131614
if (props.encryptionAtRest?.enabled) {
1614-
throw new Error('Encryption of data at rest requires Elasticsearch version 5.1 or later.');
1615+
throw new ValidationError('Encryption of data at rest requires Elasticsearch version 5.1 or later.', this);
16151616
}
16161617
if (props.cognitoKibanaAuth != null) {
1617-
throw new Error('Cognito authentication for Kibana requires Elasticsearch version 5.1 or later.');
1618+
throw new ValidationError('Cognito authentication for Kibana requires Elasticsearch version 5.1 or later.', this);
16181619
}
16191620
if (isSomeInstanceType('c5', 'i3', 'm5', 'r5')) {
1620-
throw new Error('C5, I3, M5, and R5 instance types require Elasticsearch version 5.1 or later.');
1621+
throw new ValidationError('C5, I3, M5, and R5 instance types require Elasticsearch version 5.1 or later.', this);
16211622
}
16221623
}
16231624

16241625
if (elasticsearchVersionNum < 6.0) {
16251626
if (props.nodeToNodeEncryption) {
1626-
throw new Error('Node-to-node encryption requires Elasticsearch version 6.0 or later.');
1627+
throw new ValidationError('Node-to-node encryption requires Elasticsearch version 6.0 or later.', this);
16271628
}
16281629
}
16291630

16301631
if (elasticsearchVersionNum < 6.7) {
16311632
if (unsignedBasicAuthEnabled) {
1632-
throw new Error('Using unsigned basic auth requires Elasticsearch version 6.7 or later.');
1633+
throw new ValidationError('Using unsigned basic auth requires Elasticsearch version 6.7 or later.', this);
16331634
}
16341635
if (advancedSecurityEnabled) {
1635-
throw new Error('Fine-grained access control requires Elasticsearch version 6.7 or later.');
1636+
throw new ValidationError('Fine-grained access control requires Elasticsearch version 6.7 or later.', this);
16361637
}
16371638
}
16381639

16391640
if (elasticsearchVersionNum < 6.8 && warmEnabled) {
1640-
throw new Error('UltraWarm requires Elasticsearch 6.8 or later.');
1641+
throw new ValidationError('UltraWarm requires Elasticsearch 6.8 or later.', this);
16411642
}
16421643

16431644
// Validate against instance type restrictions, per
16441645
// https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-supported-instance-types.html
16451646
if (isSomeInstanceType('i3', 'r6gd') && ebsEnabled) {
1646-
throw new Error('I3 and R6GD instance types do not support EBS storage volumes.');
1647+
throw new ValidationError('I3 and R6GD instance types do not support EBS storage volumes.', this);
16471648
}
16481649

16491650
if (isSomeInstanceType('m3', 'r3', 't2') && encryptionAtRestEnabled) {
1650-
throw new Error('M3, R3, and T2 instance types do not support encryption of data at rest.');
1651+
throw new ValidationError('M3, R3, and T2 instance types do not support encryption of data at rest.', this);
16511652
}
16521653

16531654
if (isInstanceType('t2.micro') && elasticsearchVersionNum > 2.3) {
1654-
throw new Error('The t2.micro.elasticsearch instance type supports only Elasticsearch 1.5 and 2.3.');
1655+
throw new ValidationError('The t2.micro.elasticsearch instance type supports only Elasticsearch 1.5 and 2.3.', this);
16551656
}
16561657

16571658
if (isSomeInstanceType('t2', 't3') && warmEnabled) {
1658-
throw new Error('T2 and T3 instance types do not support UltraWarm storage.');
1659+
throw new ValidationError('T2 and T3 instance types do not support UltraWarm storage.', this);
16591660
}
16601661

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

16671668
// Fine-grained access control requires node-to-node encryption, encryption at rest,
16681669
// and enforced HTTPS.
16691670
if (advancedSecurityEnabled) {
16701671
if (!nodeToNodeEncryptionEnabled) {
1671-
throw new Error('Node-to-node encryption is required when fine-grained access control is enabled.');
1672+
throw new ValidationError('Node-to-node encryption is required when fine-grained access control is enabled.', this);
16721673
}
16731674
if (!encryptionAtRestEnabled) {
1674-
throw new Error('Encryption-at-rest is required when fine-grained access control is enabled.');
1675+
throw new ValidationError('Encryption-at-rest is required when fine-grained access control is enabled.', this);
16751676
}
16761677
if (!enforceHttps) {
1677-
throw new Error('Enforce HTTPS is required when fine-grained access control is enabled.');
1678+
throw new ValidationError('Enforce HTTPS is required when fine-grained access control is enabled.', this);
16781679
}
16791680
}
16801681

16811682
// Validate fine grained access control enabled for audit logs, per
16821683
// https://aws.amazon.com/about-aws/whats-new/2020/09/elasticsearch-audit-logs-now-available-on-amazon-elasticsearch-service/
16831684
if (props.logging?.auditLogEnabled && !advancedSecurityEnabled) {
1684-
throw new Error('Fine-grained access control is required when audit logs publishing is enabled.');
1685+
throw new ValidationError('Fine-grained access control is required when audit logs publishing is enabled.', this);
16851686
}
16861687

16871688
// Validate UltraWarm requirement for dedicated master nodes, per
16881689
// https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/ultrawarm.html
16891690
if (warmEnabled && !dedicatedMasterEnabled) {
1690-
throw new Error('Dedicated master node is required when UltraWarm storage is enabled.');
1691+
throw new ValidationError('Dedicated master node is required when UltraWarm storage is enabled.', this);
16911692
}
16921693

16931694
let cfnVpcOptions: CfnDomain.VPCOptionsProperty | undefined;
@@ -1887,13 +1888,13 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
18871888
if (!cdk.Token.isUnresolved(props.domainName)) {
18881889
// https://docs.aws.amazon.com/opensearch-service/latest/developerguide/configuration-api.html#configuration-api-datatypes-domainname
18891890
if (!props.domainName.match(/^[a-z0-9\-]+$/)) {
1890-
throw new Error(`Invalid domainName '${props.domainName}'. Valid characters are a-z (lowercase only), 0-9, and – (hyphen).`);
1891+
throw new ValidationError(`Invalid domainName '${props.domainName}'. Valid characters are a-z (lowercase only), 0-9, and – (hyphen).`, this);
18911892
}
18921893
if (props.domainName.length < 3 || props.domainName.length > 28) {
1893-
throw new Error(`Invalid domainName '${props.domainName}'. It must be between 3 and 28 characters`);
1894+
throw new ValidationError(`Invalid domainName '${props.domainName}'. It must be between 3 and 28 characters`, this);
18941895
}
18951896
if (props.domainName[0] < 'a' || props.domainName[0] > 'z') {
1896-
throw new Error(`Invalid domainName '${props.domainName}'. It must start with a lowercase letter`);
1897+
throw new ValidationError(`Invalid domainName '${props.domainName}'. It must start with a lowercase letter`, this);
18971898
}
18981899
}
18991900
this.node.addMetadata('aws:cdk:hasPhysicalName', props.domainName);
@@ -1934,7 +1935,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
19341935
*/
19351936
public get connections(): ec2.Connections {
19361937
if (!this._connections) {
1937-
throw new Error("Connections are only available on VPC enabled domains. Use the 'vpc' property to place a domain inside a VPC");
1938+
throw new ValidationError("Connections are only available on VPC enabled domains. Use the 'vpc' property to place a domain inside a VPC", this);
19381939
}
19391940
return this._connections;
19401941
}
@@ -2001,12 +2002,12 @@ function extractNameFromEndpoint(domainEndpoint: string) {
20012002
*
20022003
* @param version The Elasticsearch version object
20032004
*/
2004-
function parseVersion(version: ElasticsearchVersion): number {
2005+
function parseVersion(scope: Construct, version: ElasticsearchVersion): number {
20052006
const versionStr = version.version;
20062007
const firstDot = versionStr.indexOf('.');
20072008

20082009
if (firstDot < 1) {
2009-
throw new Error(`Invalid Elasticsearch version: ${versionStr}. Version string needs to start with major and minor version (x.y).`);
2010+
throw new ValidationError(`Invalid Elasticsearch version: ${versionStr}. Version string needs to start with major and minor version (x.y).`, scope);
20102011
}
20112012

20122013
const secondDot = versionStr.indexOf('.', firstDot + 1);
@@ -2018,7 +2019,7 @@ function parseVersion(version: ElasticsearchVersion): number {
20182019
return parseFloat(versionStr.substring(0, secondDot));
20192020
}
20202021
} catch {
2021-
throw new Error(`Invalid Elasticsearch version: ${versionStr}. Version string needs to start with major and minor version (x.y).`);
2022+
throw new ValidationError(`Invalid Elasticsearch version: ${versionStr}. Version string needs to start with major and minor version (x.y).`, scope);
20222023
}
20232024
}
20242025

0 commit comments

Comments
 (0)