diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json new file mode 100644 index 0000000000000..1b0f19292ce14 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json new file mode 100644 index 0000000000000..2b9f64b17cdbf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "Route53WeightedRecordInteg/DefaultTest": { + "stacks": [ + "weighted-record" + ], + "assertionStack": "Route53WeightedRecordInteg/DefaultTest/DeployAssert", + "assertionStackName": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json new file mode 100644 index 0000000000000..19af9778152e4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "36.0.0", + "artifacts": { + "weighted-record.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "weighted-record.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "weighted-record": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "weighted-record.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "weighted-record.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "weighted-record.assets" + ], + "metadata": { + "/weighted-record/HostedZone/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "HostedZoneDB99F866" + } + ], + "/weighted-record/WeightedRecord0/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord0183C6356" + } + ], + "/weighted-record/WeightedRecord1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord1901777B1" + } + ], + "/weighted-record/WeightedRecord2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "WeightedRecord2D4D415A2" + } + ], + "/weighted-record/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/weighted-record/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "weighted-record" + }, + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "Route53WeightedRecordIntegDefaultTestDeployAssert2D65F909.assets" + ], + "metadata": { + "/Route53WeightedRecordInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Route53WeightedRecordInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Route53WeightedRecordInteg/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/tree.json new file mode 100644 index 0000000000000..29e50fd5b0cd2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/tree.json @@ -0,0 +1,227 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "weighted-record": { + "id": "weighted-record", + "path": "weighted-record", + "children": { + "HostedZone": { + "id": "HostedZone", + "path": "weighted-record/HostedZone", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/HostedZone/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::HostedZone", + "aws:cdk:cloudformation:props": { + "name": "cdk.dev." + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnHostedZone", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.PublicHostedZone", + "version": "0.0.0" + } + }, + "WeightedRecord0": { + "id": "WeightedRecord0", + "path": "weighted-record/WeightedRecord0", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord0/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "1.2.3.4" + ], + "setIdentifier": "WEIGHT_20_ID_weightedrecordWeightedRecord036EC06FB", + "ttl": "10", + "type": "A", + "weight": 20 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "WeightedRecord1": { + "id": "WeightedRecord1", + "path": "weighted-record/WeightedRecord1", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "2.3.4.5" + ], + "setIdentifier": "WEIGHT_30_ID_weightedrecordWeightedRecord1AB70F3F7", + "ttl": "10", + "type": "A", + "weight": 30 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "WeightedRecord2": { + "id": "WeightedRecord2", + "path": "weighted-record/WeightedRecord2", + "children": { + "Resource": { + "id": "Resource", + "path": "weighted-record/WeightedRecord2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Route53::RecordSet", + "aws:cdk:cloudformation:props": { + "hostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "name": "www.cdk.dev.", + "resourceRecords": [ + "3.4.5.6" + ], + "setIdentifier": "WEIGHT_50_ID_weightedrecordWeightedRecord2896F8B34", + "ttl": "10", + "type": "A", + "weight": 50 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.CfnRecordSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_route53.ARecord", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "weighted-record/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "weighted-record/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Route53WeightedRecordInteg": { + "id": "Route53WeightedRecordInteg", + "path": "Route53WeightedRecordInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Route53WeightedRecordInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "Route53WeightedRecordInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "Route53WeightedRecordInteg/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Route53WeightedRecordInteg/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Route53WeightedRecordInteg/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json new file mode 100644 index 0000000000000..09cc2ad6e14b8 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021": { + "source": { + "path": "weighted-record.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "aaa3e08f1a3f807fd7d4163e5ad01d2400b1f162ead6be3e788281f64fb3f021.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json new file mode 100644 index 0000000000000..245ed234bbc91 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.js.snapshot/weighted-record.template.json @@ -0,0 +1,92 @@ +{ + "Resources": { + "HostedZoneDB99F866": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "cdk.dev." + } + }, + "WeightedRecord0183C6356": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "1.2.3.4" + ], + "SetIdentifier": "WEIGHT_20_ID_weightedrecordWeightedRecord036EC06FB", + "TTL": "10", + "Type": "A", + "Weight": 20 + } + }, + "WeightedRecord1901777B1": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "2.3.4.5" + ], + "SetIdentifier": "WEIGHT_30_ID_weightedrecordWeightedRecord1AB70F3F7", + "TTL": "10", + "Type": "A", + "Weight": 30 + } + }, + "WeightedRecord2D4D415A2": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + }, + "Name": "www.cdk.dev.", + "ResourceRecords": [ + "3.4.5.6" + ], + "SetIdentifier": "WEIGHT_50_ID_weightedrecordWeightedRecord2896F8B34", + "TTL": "10", + "Type": "A", + "Weight": 50 + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.ts new file mode 100644 index 0000000000000..7c6d9a3aa299b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-route53/test/integ.weighted-record.ts @@ -0,0 +1,36 @@ +import { App, Duration, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const hostedZone = new route53.PublicHostedZone(this, 'HostedZone', { + zoneName: 'cdk.dev', + }); + + [ + { target: '1.2.3.4', weight: 20 }, + { target: '2.3.4.5', weight: 30 }, + { target: '3.4.5.6', weight: 50 }, + ].forEach((data, index) => { + new route53.ARecord(this, `WeightedRecord${index}`, { + zone: hostedZone, + recordName: 'www', + weight: data.weight, + ttl: Duration.seconds(10), + target: route53.RecordTarget.fromIpAddresses(data.target), + }); + }); + } +} + +const app = new App(); +const stack = new TestStack(app, 'weighted-record'); + +new IntegTest(app, 'Route53WeightedRecordInteg', { + testCases: [stack], +}); +app.synth(); diff --git a/packages/aws-cdk-lib/aws-route53/README.md b/packages/aws-cdk-lib/aws-route53/README.md index ce92008765691..fb33c8f5e7fdf 100644 --- a/packages/aws-cdk-lib/aws-route53/README.md +++ b/packages/aws-cdk-lib/aws-route53/README.md @@ -151,6 +151,32 @@ new route53.ARecord(this, 'ARecordGeoLocationDefault', { }); ``` +To enable [weighted routing](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-weighted.html), use the `weight` parameter: + +```ts +declare const myZone: route53.HostedZone; + +new route53.ARecord(this, 'ARecordWeighted1', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('1.2.3.4'), + weight: 10, +}); +``` + +To specify a unique identifier to differentiate among multiple resource record sets that have the same combination of name and type, use the `setIdentifier` parameter: + +```ts +declare const myZone: route53.HostedZone; + +new route53.ARecord(this, 'ARecordWeighted1', { + zone: myZone, + target: route53.RecordTarget.fromIpAddresses('1.2.3.4'), + weight: 10, + setIdentifier: 'weighted-record-id', +}); +``` +**Warning** It is not possible to specify `setIdentifier` in a simple routing without one of `weight` or `geoLocation` defined. + Constructs are available for A, AAAA, CAA, CNAME, MX, NS, SRV and TXT records. Use the `CaaAmazonRecord` construct to easily restrict certificate authorities diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index 15b49c7e36cd5..02c1b2a8d3fac 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -5,7 +5,7 @@ import { IHostedZone } from './hosted-zone-ref'; import { CfnRecordSet } from './route53.generated'; import { determineFullyQualifiedDomainName } from './util'; import * as iam from '../../aws-iam'; -import { CustomResource, Duration, IResource, RemovalPolicy, Resource, Token } from '../../core'; +import { CustomResource, Duration, IResource, Names, RemovalPolicy, Resource, Token } from '../../core'; import { CrossAccountZoneDelegationProvider } from '../../custom-resource-handlers/dist/aws-route53/cross-account-zone-delegation-provider.generated'; import { DeleteExistingRecordSetProvider } from '../../custom-resource-handlers/dist/aws-route53/delete-existing-record-set-provider.generated'; @@ -184,6 +184,31 @@ export interface RecordSetOptions { * @default false */ readonly deleteExisting?: boolean; + + /** + * Among resource record sets that have the same combination of DNS name and type, + * a value that determines the proportion of DNS queries that Amazon Route 53 responds to using the current resource record set. + * + * Route 53 calculates the sum of the weights for the resource record sets that have the same combination of DNS name and type. + * Route 53 then responds to queries based on the ratio of a resource's weight to the total. + * + * This value can be a number between 0 and 255. + * + * @see https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-weighted.html + * + * @default - Do not set weighted routing + */ + readonly weight?: number; + + /** + * A string used to distinguish between different records with the same combination of DNS name and type. + * It can only be set when either weight or geoLocation is defined. + * + * This parameter must be between 1 and 128 characters in length. + * + * @default - Auto generated string + */ + readonly setIdentifier?: string; } /** @@ -241,10 +266,28 @@ export interface RecordSetProps extends RecordSetOptions { */ export class RecordSet extends Resource implements IRecordSet { public readonly domainName: string; + private readonly geoLocation?: GeoLocation; + private readonly weight?: number; constructor(scope: Construct, id: string, props: RecordSetProps) { super(scope, id); + if (props.weight && (props.weight < 0 || props.weight > 255)) { + throw new Error(`weight must be between 0 and 255 inclusive, got: ${props.weight}`); + } + if (props.setIdentifier && (props.setIdentifier.length < 1 || props.setIdentifier.length > 128)) { + throw new Error(`setIdentifier must be between 1 and 128 characters long, got: ${props.setIdentifier.length}`); + } + if (props.weight && props.geoLocation) { + throw new Error('Only one of weight or geoLocation can be specified, not both'); + } + if (props.setIdentifier && !props.weight && !props.geoLocation) { + throw new Error('setIdentifier can only be specified when either weight or geoLocation is defined'); + } + + this.geoLocation = props.geoLocation; + this.weight = props.weight; + const ttl = props.target.aliasTarget ? undefined : ((props.ttl && props.ttl.toSeconds()) ?? 1800).toString(); const recordName = determineFullyQualifiedDomainName(props.recordName || props.zone.zoneName, props.zone); @@ -262,7 +305,8 @@ export class RecordSet extends Resource implements IRecordSet { countryCode: props.geoLocation.countryCode, subdivisionCode: props.geoLocation.subdivisionCode, } : undefined, - setIdentifier: props.geoLocation ? this.configureSetIdentifer(props.geoLocation) : undefined, + setIdentifier: props.setIdentifier ?? this.configureSetIdentifier(), + weight: props.weight, }); this.domainName = recordSet.ref; @@ -308,18 +352,28 @@ export class RecordSet extends Resource implements IRecordSet { } } - private configureSetIdentifer(props: GeoLocation): string | undefined { - let identifier = 'GEO'; - if (props.continentCode) { - identifier = identifier.concat('_CONTINENT_', props.continentCode); - } - if (props.countryCode) { - identifier = identifier.concat('_COUNTRY_', props.countryCode); + private configureSetIdentifier(): string | undefined { + if (this.geoLocation) { + let identifier = 'GEO'; + if (this.geoLocation.continentCode) { + identifier = identifier.concat('_CONTINENT_', this.geoLocation.continentCode); + } + if (this.geoLocation.countryCode) { + identifier = identifier.concat('_COUNTRY_', this.geoLocation.countryCode); + } + if (this.geoLocation.subdivisionCode) { + identifier = identifier.concat('_SUBDIVISION_', this.geoLocation.subdivisionCode); + } + return identifier; } - if (props.subdivisionCode) { - identifier = identifier.concat('_SUBDIVISION_', props.subdivisionCode); + + if (this.weight) { + const idPrefix = `WEIGHT_${this.weight}_ID_`; + const identifier = `${idPrefix}${Names.uniqueResourceName(this, { maxLength: 64 - idPrefix.length })}`; + return identifier; } - return identifier; + + return undefined; } } diff --git a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts index 1fa9bf4eae2e1..871768a41893a 100644 --- a/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts +++ b/packages/aws-cdk-lib/aws-route53/test/record-set.test.ts @@ -1096,4 +1096,107 @@ describe('record set', () => { ], }); }); + + test('with weight', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { + zoneName: 'myzone', + }); + + // WHEN + new route53.RecordSet(stack, 'RecordSet', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 50, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { + Name: 'www.myzone.', + Type: 'CNAME', + HostedZoneId: { + Ref: 'HostedZoneDB99F866', + }, + ResourceRecords: [ + 'zzz', + ], + TTL: '1800', + Weight: 50, + SetIdentifier: 'WEIGHT_50_ID_RecordSet', + }); + }); + + test.each([ + [-1], + [256], + ])('throw error for invalid weight %s', (weight: number) => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight, + })).toThrow(`weight must be between 0 and 255 inclusive, got: ${weight}`); + }); + + test('throw error for invalid setIdentifier', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 20, + setIdentifier: 'a'.repeat(129), + })).toThrow('setIdentifier must be between 1 and 128 characters long, got: 129'); + }); + + test('throw error for the simultaneous definition of weight and geoLocation', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + weight: 50, + geoLocation: route53.GeoLocation.continent(route53.Continent.EUROPE), + setIdentifier: 'uniqueId', + })).toThrow('Only one of weight or geoLocation can be specified, not both'); + }); + + test('throw error for the definition of setIdentifier without weight or geoLocation', () => { + // GIVEN + const stack = new Stack(); + + const zone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'myzone' }); + + // THEN + expect(() => new route53.RecordSet(stack, 'Basic', { + zone, + recordName: 'www', + recordType: route53.RecordType.CNAME, + target: route53.RecordTarget.fromValues('zzz'), + setIdentifier: 'uniqueId', + })).toThrow('setIdentifier can only be specified when either weight or geoLocation is defined'); + }); });