From addbba912b642ae2b2026da4e9ed327560869fd1 Mon Sep 17 00:00:00 2001 From: Dave Hensley Date: Sun, 12 Jul 2020 14:57:53 +0000 Subject: [PATCH] Add an option to disable IPv6 (don't create the AAAA record in Route 53). --- README.md | 7 ++++- src/DomainConfig.ts | 2 ++ src/index.ts | 10 ++++++- src/types.ts | 1 + test/unit-tests/index.test.ts | 56 ++++++++++++++++++++++++++++++++++- 5 files changed, 73 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60082ff2..885c8e83 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ custom: basePath: api certificateName: '*.foo.com' createRoute53Record: true + createRoute53IPv6Record: true endpointType: 'regional' securityPolicy: tls_1_2 apiType: rest @@ -84,6 +85,7 @@ custom: basePath: api certificateName: '*.foo.com' createRoute53Record: true + createRoute53IPv6Record: true endpointType: 'regional' securityPolicy: tls_1_2 http: @@ -92,6 +94,7 @@ custom: basePath: api certificateName: '*.foo.com' createRoute53Record: true + createRoute53IPv6Record: true endpointType: 'regional' securityPolicy: tls_1_2 websocket: @@ -100,6 +103,7 @@ custom: basePath: api certificateName: '*.foo.com' createRoute53Record: true + createRoute53IPv6Record: true endpointType: 'regional' securityPolicy: tls_1_2 ``` @@ -111,7 +115,8 @@ custom: | stage | Value of `--stage`, or `provider.stage` (serverless will default to `dev` if unset) | The stage to create the domain name for. This parameter allows you to specify a different stage for the domain name than the stage specified for the serverless deployment. | | certificateName | Closest match | The name of a specific certificate from Certificate Manager to use with this API. If not specified, the closest match will be used (i.e. for a given domain name `api.example.com`, a certificate for `api.example.com` will take precedence over a `*.example.com` certificate).

Note: Edge-optimized endpoints require that the certificate be located in `us-east-1` to be used with the CloudFront distribution. | | certificateArn | `(none)` | The arn of a specific certificate from Certificate Manager to use with this API. | -| createRoute53Record | `true` | Toggles whether or not the plugin will create an A Alias and AAAA Alias records in Route53 mapping the `domainName` to the generated distribution domain name. If false, does not create a record. | +| createRoute53Record | `true` | Toggles whether or not the plugin will create A Alias and AAAA Alias records in Route53 mapping the `domainName` to the generated distribution domain name. If false, does not create a record. | +| createRoute53IPv6Record | `true` | Toggles whether or not the plugin will create an AAAA Alias record in Route53 mapping the `domainName` to the generated distribution domain name. If false, does not create a record. | | endpointType | edge | Defines the endpoint type, accepts `regional` or `edge`. | | apiType | rest | Defines the api type, accepts `rest`, `http` or `websocket`. | | hostedZoneId | | If hostedZoneId is set the route53 record set will be created in the matching zone, otherwise the hosted zone will be figured out from the domainName (hosted zone with matching domain). | diff --git a/src/DomainConfig.ts b/src/DomainConfig.ts index 09363675..24a68161 100644 --- a/src/DomainConfig.ts +++ b/src/DomainConfig.ts @@ -14,6 +14,7 @@ class DomainConfig { public certificateName: string | undefined; public certificateArn: string | undefined; public createRoute53Record: boolean | undefined; + public createRoute53IPv6Record: boolean | undefined; public endpointType: string | undefined; public apiType: string | undefined; public hostedZoneId: string | undefined; @@ -34,6 +35,7 @@ class DomainConfig { this.certificateArn = config.certificateArn; this.certificateName = config.certificateName; this.createRoute53Record = config.createRoute53Record; + this.createRoute53IPv6Record = config.createRoute53IPv6Record; this.hostedZoneId = config.hostedZoneId; this.hostedZonePrivate = config.hostedZonePrivate; this.allowPathMatching = config.allowPathMatching; diff --git a/src/index.ts b/src/index.ts index 40bd6787..4203dc92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -471,9 +471,17 @@ class ServerlessCustomDomain { this.serverless.cli.log(`Skipping ${action === "DELETE" ? "removal" : "creation"} of Route53 record.`); return; } + + const recordsToCreate = ["A"]; + const createRoute53IPv6Record = domain.createRoute53IPv6Record; + + if (createRoute53IPv6Record !== false) { + recordsToCreate.push("AAAA"); + } + // Set up parameters const route53HostedZoneId = await this.getRoute53HostedZoneId(domain); - const Changes = ["A", "AAAA"].map((Type) => ({ + const Changes = recordsToCreate.map((Type) => ({ Action: action, ResourceRecordSet: { AliasTarget: { diff --git a/src/types.ts b/src/types.ts index 0e8c089c..741ebbd3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ export interface ServerlessInstance { // tslint:disable-line certificateName: string | undefined, certificateArn: string | undefined, createRoute53Record: boolean | undefined, + createRoute53IPv6Record: boolean | undefined, endpointType: string | undefined, apiType: string | undefined, hostedZoneId: string | undefined, diff --git a/test/unit-tests/index.test.ts b/test/unit-tests/index.test.ts index e8412b31..3e8fdfbb 100644 --- a/test/unit-tests/index.test.ts +++ b/test/unit-tests/index.test.ts @@ -67,6 +67,7 @@ const constructPlugin = (customDomainOptions) => { basePath: customDomainOptions.basePath, certificateArn: customDomainOptions.certificateArn, certificateName: customDomainOptions.certificateName, + createRoute53IPv6Record: customDomainOptions.createRoute53IPv6Record, createRoute53Record: customDomainOptions.createRoute53Record, domainName: customDomainOptions.domainName, enabled: customDomainOptions.enabled, @@ -604,7 +605,7 @@ describe("Custom Domain Plugin", () => { expect(dc.domainInfo.securityPolicy).to.equal("TLS_1_2"); }); - it("Create a new A Alias Record", async () => { + it("Create new A and AAAA Alias Records", async () => { AWS.mock("Route53", "listHostedZones", (params, callback) => { callback(null, { HostedZones: [{ Name: "test_domain", Id: "test_host_id", Config: { PrivateZone: false } }] }); }); @@ -664,6 +665,59 @@ describe("Custom Domain Plugin", () => { expect(spy).to.have.been.called.with(expectedParams); }); + it("Create new A Alias Record Only", async () => { + AWS.mock("Route53", "listHostedZones", (params, callback) => { + callback(null, { HostedZones: [{ Name: "test_domain", Id: "test_host_id", Config: { PrivateZone: false } }] }); + }); + + AWS.mock("Route53", "changeResourceRecordSets", (params, callback) => { + callback(null, params); + }); + + const plugin = constructPlugin({ + basePath: "test_basepath", + createRoute53IPv6Record: false, + domainName: "test_domain", + }); + + plugin.route53 = new aws.Route53(); + + const dc: DomainConfig = new DomainConfig(plugin.serverless.service.custom.customDomain); + + dc.domainInfo = new DomainInfo( + { + distributionDomainName: "test_distribution_name", + distributionHostedZoneId: "test_id", + }, + ); + + const spy = chai.spy.on(plugin.route53, "changeResourceRecordSets"); + + await plugin.changeResourceRecordSet("UPSERT", dc); + + const expectedParams = { + ChangeBatch: { + Changes: [ + { + Action: "UPSERT", + ResourceRecordSet: { + AliasTarget: { + DNSName: "test_distribution_name", + EvaluateTargetHealth: false, + HostedZoneId: "test_id", + }, + Name: "test_domain", + Type: "A", + }, + }, + ], + Comment: "Record created by serverless-domain-manager", + }, + HostedZoneId: "est_host_id", // getRoute53HostedZoneId strips first character + }; + expect(spy).to.have.been.called.with(expectedParams); + }); + it("Do not create a Route53 record", async () => { const plugin = constructPlugin({ createRoute53Record: false,