Skip to content

Commit 17eddd1

Browse files
author
Elad Ben-Israel
authored
feat(route53): support CNAME records (#1487)
Adds support for CNAME records via the `CnameRecord` construct. Misc: - `TXTRecord` was renamed to `TxtRecord`. - `hostedZoneNameServers` attribute added to IHostedZone - Made `HostedZone` a concrete (non-abstract) class so it will be compatible with how CloudFormation represents this resource, but left PublicHostedZone and PrivateHostedZone to allow a more strongly-typed experience if you like. Credits for original PR (closes #1420): @MikeBild BREAKING CHANGE: The `route53.TXTRecord` class was renamed to `route53.TxtRecord`.
1 parent 471e8eb commit 17eddd1

12 files changed

+341
-117
lines changed

packages/@aws-cdk/aws-route53/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## AWS Route53 Constuct Library
1+
## AWS Route53 Construct Library
22

33
To add a public hosted zone:
44

@@ -34,7 +34,7 @@ To add a TXT record to your zone:
3434
```ts
3535
import route53 = require('@aws-cdk/aws-route53');
3636

37-
new route53.TXTRecord(zone, 'TXTRecord', {
37+
new route53.TxtRecord(zone, 'TXTRecord', {
3838
recordName: '_foo', // If the name ends with a ".", it will be used as-is;
3939
// if it ends with a "." followed by the zone name, a trailing "." will be added automatically;
4040
// otherwise, a ".", the zone name, and a trailing "." will be added automatically.

packages/@aws-cdk/aws-route53/lib/hosted-zone-ref.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import cdk = require('@aws-cdk/cdk');
55
*/
66
export interface IHostedZone extends cdk.IConstruct {
77
/**
8-
* ID of this hosted zone
8+
* ID of this hosted zone, such as "Z23ABC4XYZL05B"
99
*/
1010
readonly hostedZoneId: string;
1111

@@ -14,6 +14,14 @@ export interface IHostedZone extends cdk.IConstruct {
1414
*/
1515
readonly zoneName: string;
1616

17+
/**
18+
* Returns the set of name servers for the specific hosted zone. For example:
19+
* ns1.example.com.
20+
*
21+
* This attribute will be undefined for private hosted zones or hosted zones imported from another stack.
22+
*/
23+
readonly hostedZoneNameServers?: string[];
24+
1725
/**
1826
* Export the hosted zone
1927
*/

packages/@aws-cdk/aws-route53/lib/hosted-zone.ts

+74-85
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,125 @@
11
import ec2 = require('@aws-cdk/aws-ec2');
22
import cdk = require('@aws-cdk/cdk');
33
import { HostedZoneImportProps, IHostedZone } from './hosted-zone-ref';
4-
import { CfnHostedZone, HostedZoneNameServers } from './route53.generated';
4+
import { CfnHostedZone } from './route53.generated';
55
import { validateZoneName } from './util';
66

7-
/**
8-
* Properties of a new hosted zone
9-
*/
10-
export interface PublicHostedZoneProps {
7+
export interface CommonHostedZoneProps {
118
/**
12-
* The fully qualified domain name for the hosted zone
9+
* The name of the domain. For resource record types that include a domain
10+
* name, specify a fully qualified domain name.
1311
*/
1412
zoneName: string;
1513

1614
/**
1715
* Any comments that you want to include about the hosted zone.
1816
*
19-
* @default no comment
17+
* @default none
2018
*/
2119
comment?: string;
2220

2321
/**
2422
* The Amazon Resource Name (ARN) for the log group that you want Amazon Route 53 to send query logs to.
2523
*
26-
* @default no DNS query logging
24+
* @default disabled
2725
*/
2826
queryLogsLogGroupArn?: string;
2927
}
3028

31-
export abstract class HostedZone extends cdk.Construct implements IHostedZone {
32-
public static import(scope: cdk.Construct, id: string, props: HostedZoneImportProps): IHostedZone {
33-
return new ImportedHostedZone(scope, id, props);
34-
}
35-
36-
public abstract readonly hostedZoneId: string;
37-
public abstract readonly zoneName: string;
38-
39-
public export(): HostedZoneImportProps {
40-
return {
41-
hostedZoneId: new cdk.Output(this, 'HostedZoneId', { value: this.hostedZoneId }).makeImportValue().toString(),
42-
zoneName: this.zoneName,
43-
};
44-
}
45-
}
46-
4729
/**
48-
* Create a Route53 public hosted zone.
30+
* Properties of a new hosted zone
4931
*/
50-
export class PublicHostedZone extends HostedZone {
32+
export interface HostedZoneProps extends CommonHostedZoneProps {
5133
/**
52-
* Identifier of this hosted zone
34+
* A VPC that you want to associate with this hosted zone. When you specify
35+
* this property, a private hosted zone will be created.
36+
*
37+
* You can associate additional VPCs to this private zone using `addVpc(vpc)`.
38+
*
39+
* @default public (no VPCs associated)
5340
*/
54-
public readonly hostedZoneId: string;
41+
vpcs?: ec2.IVpcNetwork[];
42+
}
5543

44+
export class HostedZone extends cdk.Construct implements IHostedZone {
5645
/**
57-
* Fully qualified domain name for the hosted zone
46+
* Imports a hosted zone from another stack.
5847
*/
48+
public static import(scope: cdk.Construct, id: string, props: HostedZoneImportProps): IHostedZone {
49+
return new ImportedHostedZone(scope, id, props);
50+
}
51+
52+
public readonly hostedZoneId: string;
5953
public readonly zoneName: string;
54+
public readonly hostedZoneNameServers?: string[];
6055

6156
/**
62-
* Nameservers for this public hosted zone
57+
* VPCs to which this hosted zone will be added
6358
*/
64-
public readonly nameServers: HostedZoneNameServers;
59+
protected readonly vpcs = new Array<CfnHostedZone.VPCProperty>();
6560

66-
constructor(scope: cdk.Construct, id: string, props: PublicHostedZoneProps) {
61+
constructor(scope: cdk.Construct, id: string, props: HostedZoneProps) {
6762
super(scope, id);
6863

6964
validateZoneName(props.zoneName);
7065

7166
const hostedZone = new CfnHostedZone(this, 'Resource', {
72-
...determineHostedZoneProps(props)
67+
name: props.zoneName + '.',
68+
hostedZoneConfig: props.comment ? { comment: props.comment } : undefined,
69+
queryLoggingConfig: props.queryLogsLogGroupArn ? { cloudWatchLogsLogGroupArn: props.queryLogsLogGroupArn } : undefined,
70+
vpcs: new cdk.Token(() => this.vpcs.length === 0 ? undefined : this.vpcs)
7371
});
7472

7573
this.hostedZoneId = hostedZone.ref;
76-
this.nameServers = hostedZone.hostedZoneNameServers;
74+
this.hostedZoneNameServers = hostedZone.hostedZoneNameServers.toList();
7775
this.zoneName = props.zoneName;
76+
77+
for (const vpc of props.vpcs || []) {
78+
this.addVpc(vpc);
79+
}
80+
}
81+
82+
public export(): HostedZoneImportProps {
83+
return {
84+
hostedZoneId: new cdk.Output(this, 'HostedZoneId', { value: this.hostedZoneId }).makeImportValue(),
85+
zoneName: this.zoneName,
86+
};
87+
}
88+
89+
/**
90+
* Add another VPC to this private hosted zone.
91+
*
92+
* @param vpc the other VPC to add.
93+
*/
94+
public addVpc(vpc: ec2.IVpcNetwork) {
95+
this.vpcs.push({ vpcId: vpc.vpcId, vpcRegion: new cdk.AwsRegion() });
7896
}
7997
}
8098

99+
// tslint:disable-next-line:no-empty-interface
100+
export interface PublicHostedZoneProps extends CommonHostedZoneProps {
101+
102+
}
103+
81104
/**
82-
* Properties for a private hosted zone.
105+
* Create a Route53 public hosted zone.
83106
*/
84-
export interface PrivateHostedZoneProps extends PublicHostedZoneProps {
107+
export class PublicHostedZone extends HostedZone {
108+
constructor(scope: cdk.Construct, id: string, props: PublicHostedZoneProps) {
109+
super(scope, id, props);
110+
}
111+
112+
public addVpc(_vpc: ec2.IVpcNetwork) {
113+
throw new Error('Cannot associate public hosted zones with a VPC');
114+
}
115+
}
116+
117+
export interface PrivateHostedZoneProps extends CommonHostedZoneProps {
85118
/**
86-
* One VPC that you want to associate with this hosted zone.
119+
* A VPC that you want to associate with this hosted zone.
120+
*
121+
* Private hosted zones must be associated with at least one VPC. You can
122+
* associated additional VPCs using `addVpc(vpc)`.
87123
*/
88124
vpc: ec2.IVpcNetwork;
89125
}
@@ -95,65 +131,18 @@ export interface PrivateHostedZoneProps extends PublicHostedZoneProps {
95131
* for the VPC you're configuring for private hosted zones.
96132
*/
97133
export class PrivateHostedZone extends HostedZone {
98-
/**
99-
* Identifier of this hosted zone
100-
*/
101-
public readonly hostedZoneId: string;
102-
103-
/**
104-
* Fully qualified domain name for the hosted zone
105-
*/
106-
public readonly zoneName: string;
107-
108-
/**
109-
* VPCs to which this hosted zone will be added
110-
*/
111-
private readonly vpcs: CfnHostedZone.VPCProperty[] = [];
112-
113134
constructor(scope: cdk.Construct, id: string, props: PrivateHostedZoneProps) {
114-
super(scope, id);
115-
116-
validateZoneName(props.zoneName);
117-
118-
const hostedZone = new CfnHostedZone(this, 'Resource', {
119-
vpcs: new cdk.Token(() => this.vpcs ? this.vpcs : undefined),
120-
...determineHostedZoneProps(props)
121-
});
122-
123-
this.hostedZoneId = hostedZone.ref;
124-
this.zoneName = props.zoneName;
135+
super(scope, id, props);
125136

126137
this.addVpc(props.vpc);
127138
}
128-
129-
/**
130-
* Add another VPC to this private hosted zone.
131-
*
132-
* @param vpc the other VPC to add.
133-
*/
134-
public addVpc(vpc: ec2.IVpcNetwork) {
135-
this.vpcs.push(toVpcProperty(vpc));
136-
}
137-
}
138-
139-
function toVpcProperty(vpc: ec2.IVpcNetwork): CfnHostedZone.VPCProperty {
140-
return { vpcId: vpc.vpcId, vpcRegion: new cdk.AwsRegion() };
141-
}
142-
143-
function determineHostedZoneProps(props: PublicHostedZoneProps) {
144-
const name = props.zoneName + '.';
145-
const hostedZoneConfig = props.comment ? { comment: props.comment } : undefined;
146-
const queryLoggingConfig = props.queryLogsLogGroupArn ? { cloudWatchLogsLogGroupArn: props.queryLogsLogGroupArn } : undefined;
147-
148-
return { name, hostedZoneConfig, queryLoggingConfig };
149139
}
150140

151141
/**
152142
* Imported hosted zone
153143
*/
154144
class ImportedHostedZone extends cdk.Construct implements IHostedZone {
155145
public readonly hostedZoneId: string;
156-
157146
public readonly zoneName: string;
158147

159148
constructor(scope: cdk.Construct, name: string, private readonly props: HostedZoneImportProps) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Construct } from '@aws-cdk/cdk';
2+
import { IHostedZone } from '../hosted-zone-ref';
3+
import { CfnRecordSet } from '../route53.generated';
4+
import { determineFullyQualifiedDomainName } from './_util';
5+
6+
export interface CnameRecordProps {
7+
/**
8+
* The hosted zone in which to define the new TXT record.
9+
*/
10+
zone: IHostedZone;
11+
12+
/**
13+
* The domain name for this record set.
14+
*/
15+
recordName: string;
16+
17+
/**
18+
* The value for this record set.
19+
*/
20+
recordValue: string;
21+
22+
/**
23+
* The resource record cache time to live (TTL) in seconds.
24+
*
25+
* @default 1800 seconds
26+
*/
27+
ttl?: number;
28+
}
29+
30+
/**
31+
* A DNS CNAME record
32+
*/
33+
export class CnameRecord extends Construct {
34+
constructor(scope: Construct, id: string, props: CnameRecordProps) {
35+
super(scope, id);
36+
37+
const ttl = props.ttl === undefined ? 1800 : props.ttl;
38+
39+
new CfnRecordSet(this, 'Resource', {
40+
hostedZoneId: props.zone.hostedZoneId,
41+
name: determineFullyQualifiedDomainName(props.recordName, props.zone),
42+
type: 'CNAME',
43+
resourceRecords: [ props.recordValue ],
44+
ttl: ttl.toString(),
45+
});
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './alias';
22
export * from './txt';
3+
export * from './cname';
34
export * from './zone-delegation';

packages/@aws-cdk/aws-route53/lib/records/txt.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@ import { IHostedZone } from '../hosted-zone-ref';
33
import { CfnRecordSet } from '../route53.generated';
44
import { determineFullyQualifiedDomainName } from './_util';
55

6-
export interface TXTRecordProps {
6+
export interface TxtRecordProps {
7+
/**
8+
* The hosted zone in which to define the new TXT record.
9+
*/
710
zone: IHostedZone;
11+
12+
/**
13+
* The domain name for this record set.
14+
*/
815
recordName: string;
16+
17+
/**
18+
* The value for this record set.
19+
*/
920
recordValue: string;
10-
/** @default 1800 seconds */
21+
22+
/**
23+
* The resource record cache time to live (TTL) in seconds.
24+
*
25+
* @default 1800 seconds
26+
*/
1127
ttl?: number;
1228
}
1329

1430
/**
1531
* A DNS TXT record
1632
*/
17-
export class TXTRecord extends Construct {
18-
constructor(scope: Construct, id: string, props: TXTRecordProps) {
33+
export class TxtRecord extends Construct {
34+
constructor(scope: Construct, id: string, props: TxtRecordProps) {
1935
super(scope, id);
2036

2137
// JSON.stringify conveniently wraps strings in " and escapes ".

packages/@aws-cdk/aws-route53/package.json

-6
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,5 @@
7373
},
7474
"engines": {
7575
"node": ">= 8.10.0"
76-
},
77-
"awslint": {
78-
"exclude": [
79-
"resource-attribute:@aws-cdk/aws-route53.IHostedZone.hostedZoneNameServers",
80-
"resource-props:@aws-cdk/aws-route53.HostedZoneProps"
81-
]
8276
}
8377
}

0 commit comments

Comments
 (0)