Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(route53): Convenience API for creating zone delegations #1853

Merged
merged 2 commits into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ec2 = require('@aws-cdk/aws-ec2');
import cdk = require('@aws-cdk/cdk');
import { HostedZoneImportProps, IHostedZone } from './hosted-zone-ref';
import { ZoneDelegationRecord } from './records';
import { CfnHostedZone } from './route53.generated';
import { validateZoneName } from './util';

Expand Down Expand Up @@ -112,6 +113,41 @@ export class PublicHostedZone extends HostedZone {
public addVpc(_vpc: ec2.IVpcNetwork) {
throw new Error('Cannot associate public hosted zones with a VPC');
}

/**
* Adds a delegation from this zone to a designated zone.
*
* @param delegate the zone being delegated to.
* @param opts options for creating the DNS record, if any.
*/
public addDelegation(delegate: PublicHostedZone, opts: ZoneDelegationOptions = {}): void {
new ZoneDelegationRecord(this, `${this.zoneName} -> ${delegate.zoneName}`, {
zone: this,
delegatedZoneName: delegate.zoneName,
nameServers: delegate.hostedZoneNameServers!, // PublicHostedZones always have name servers!
comment: opts.comment,
ttl: opts.ttl,
});
}
}

/**
* Options available when creating a delegation relationship from one PublicHostedZone to another.
*/
export interface ZoneDelegationOptions {
/**
* A comment to add on the DNS record created to incorporate the delegation.
*
* @default none
*/
comment?: string;

/**
* The TTL (Time To Live) of the DNS delegation record in DNS caches.
*
* @default 172800
*/
ttl?: number;
}

export interface PrivateHostedZoneProps extends CommonHostedZoneProps {
Expand Down
28 changes: 9 additions & 19 deletions packages/@aws-cdk/aws-route53/lib/records/zone-delegation.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Construct } from '@aws-cdk/cdk';
import cdk = require('@aws-cdk/cdk');
import { ZoneDelegationOptions } from '../hosted-zone';
import { IHostedZone } from '../hosted-zone-ref';
import { CfnRecordSet } from '../route53.generated';
import { determineFullyQualifiedDomainName } from './_util';

export interface ZoneDelegationRecordProps {
export interface ZoneDelegationRecordProps extends ZoneDelegationOptions {
/**
* The zone in which this delegate is defined.
*/
Expand All @@ -17,38 +18,27 @@ export interface ZoneDelegationRecordProps {
* The name servers to report in the delegation records.
*/
nameServers: string[];

/**
* The TTL of the zone delegation records.
*
* @default 172800 seconds.
*/
ttl?: number;

/**
* Any comments that you want to include about the zone delegation records.
*
* @default no comment.
*/
comment?: string;
}

/**
* A record to delegate further lookups to a different set of name servers
*/
export class ZoneDelegationRecord extends Construct {
constructor(scope: Construct, id: string, props: ZoneDelegationRecordProps) {
export class ZoneDelegationRecord extends cdk.Construct {
constructor(scope: cdk.Construct, id: string, props: ZoneDelegationRecordProps) {
super(scope, id);

const ttl = props.ttl === undefined ? 172_800 : props.ttl;
const resourceRecords = cdk.unresolved(props.nameServers)
? props.nameServers // Can't map a string-array token!
: props.nameServers.map(ns => (cdk.unresolved(ns) || ns.endsWith('.')) ? ns : `${ns}.`);

new CfnRecordSet(this, 'Resource', {
hostedZoneId: props.zone.hostedZoneId,
name: determineFullyQualifiedDomainName(props.delegatedZoneName, props.zone),
type: 'NS',
ttl: ttl.toString(),
comment: props.comment,
resourceRecords: props.nameServers.map(ns => ns.endsWith('.') ? ns : `${ns}.`)
resourceRecords,
});
}
}
25 changes: 24 additions & 1 deletion packages/@aws-cdk/aws-route53/test/integ.route53.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,29 @@
"Name": "cdk.test."
}
},
"PublicZonecdktestsubcdktest83558650": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
"Name": "sub.cdk.test.",
"Type": "NS",
"HostedZoneId": {
"Ref": "PublicZone2E1C4E34"
},
"ResourceRecords": {
"Fn::GetAtt": [
"PublicSubZoneDBD26A0A",
"NameServers"
]
},
"TTL": "172800"
}
},
"PublicSubZoneDBD26A0A": {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"Name": "sub.cdk.test."
}
},
"CNAMEC70A2D52": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
Expand Down Expand Up @@ -559,4 +582,4 @@
}
}
}
}
}
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-route53/test/integ.route53.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const privateZone = new PrivateHostedZone(stack, 'PrivateZone', {
const publicZone = new PublicHostedZone(stack, 'PublicZone', {
zoneName: 'cdk.test'
});
const publicSubZone = new PublicHostedZone(stack, 'PublicSubZone', {
zoneName: 'sub.cdk.test'
});
publicZone.addDelegation(publicSubZone);

new TxtRecord(privateZone, 'TXT', {
zone: privateZone,
Expand Down
22 changes: 21 additions & 1 deletion packages/@aws-cdk/aws-route53/test/test.route53.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,27 @@ export = {
// THEN
test.throws(() => zone.addVpc(vpc), /Cannot associate public hosted zones with a VPC/);
test.done();
}
},

'setting up zone delegation'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const zone = new PublicHostedZone(stack, 'TopZone', { zoneName: 'top.test' });
const delegate = new PublicHostedZone(stack, 'SubZone', { zoneName: 'sub.top.test' });

// WHEN
zone.addDelegation(delegate, { ttl: 1337 });

// THEN
expect(stack).to(haveResource('AWS::Route53::RecordSet', {
Type: 'NS',
Name: 'sub.top.test.',
HostedZoneId: zone.node.resolve(zone.hostedZoneId),
ResourceRecords: zone.node.resolve(delegate.hostedZoneNameServers),
TTL: '1337',
}));
test.done();
},
};

class TestApp {
Expand Down