Skip to content

Commit 1187366

Browse files
SoManyHsrix0rrr
authored andcommitted
feat(servicediscovery): AWS Cloud Map construct library (#1804)
Add a construct library for AWS Cloud Map. Partially based on work by @jogold.
1 parent c6c66e9 commit 1187366

25 files changed

+2858
-11
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,34 @@
11
## The CDK Construct Library for AWS Service Discovery
22
This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project.
3+
4+
This package contains constructs for working with **AWS Cloud Map**
5+
6+
AWS Cloud Map is a fully managed service that you can use to create and
7+
maintain a map of the backend services and resources that your applications
8+
depend on.
9+
10+
For further information on AWS Cloud Map,
11+
see the [AWS Cloud Map documentation](https://docs.aws.amazon.com/cloud-map)
12+
13+
The following example creates an AWS Cloud Map namespace that
14+
supports API calls, creates a service in that namespace, and
15+
registers an instance to it:
16+
17+
[Creating a Cloud Map service within an HTTP namespace](test/integ.service-with-http-namespace.lit.ts)
18+
19+
The following example creates an AWS Cloud Map namespace that
20+
supports both API calls and DNS queries within a vpc, creates a
21+
service in that namespace, and registers a loadbalancer as an
22+
instance:
23+
24+
[Creating a Cloud Map service within a Private DNS namespace](test/integ.service-with-private-dns-namespace.lit.ts)
25+
26+
The following example creates an AWS Cloud Map namespace that
27+
supports both API calls and public DNS queries, creates a service in
28+
that namespace, and registers an IP instance:
29+
30+
[Creating a Cloud Map service within a Public namespace](test/integ.service-with-public-dns-namespace.lit.ts)
31+
32+
For DNS namespaces, you can also register instances to services with CNAME records:
33+
34+
[Creating a Cloud Map service within a Public namespace](test/integ.service-with-cname-record.lit.ts)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import cdk = require('@aws-cdk/cdk');
2+
import { BaseInstanceProps, InstanceBase } from './instance';
3+
import { NamespaceType } from './namespace';
4+
import { DnsRecordType, IService, RoutingPolicy } from './service';
5+
import { CfnInstance } from './servicediscovery.generated';
6+
7+
/*
8+
* Properties for an AliasTargetInstance
9+
*/
10+
export interface AliasTargetInstanceProps extends BaseInstanceProps {
11+
/**
12+
* DNS name of the target
13+
*/
14+
dnsName: string;
15+
16+
/**
17+
* The Cloudmap service this resource is registered to.
18+
*/
19+
service: IService;
20+
}
21+
22+
/*
23+
* Instance that uses Route 53 Alias record type. Currently, the only resource types supported are Elastic Load
24+
* Balancers.
25+
*/
26+
export class AliasTargetInstance extends InstanceBase {
27+
/**
28+
* The Id of the instance
29+
*/
30+
public readonly instanceId: string;
31+
32+
/**
33+
* The Cloudmap service to which the instance is registered.
34+
*/
35+
public readonly service: IService;
36+
37+
/**
38+
* The Route53 DNS name of the alias target
39+
*/
40+
public readonly dnsName: string;
41+
42+
constructor(scope: cdk.Construct, id: string, props: AliasTargetInstanceProps) {
43+
super(scope, id);
44+
45+
if (props.service.namespace.type === NamespaceType.Http) {
46+
throw new Error('Namespace associated with Service must be a DNS Namespace.');
47+
}
48+
49+
// Should already be enforced when creating service, but validates if service is not instantiated with #createService
50+
const dnsRecordType = props.service.dnsRecordType;
51+
if (dnsRecordType !== DnsRecordType.A
52+
&& dnsRecordType !== DnsRecordType.AAAA
53+
&& dnsRecordType !== DnsRecordType.A_AAAA) {
54+
throw new Error('Service must use `A` or `AAAA` records to register an AliasRecordTarget.');
55+
}
56+
57+
if (props.service.routingPolicy !== RoutingPolicy.Weighted) {
58+
throw new Error('Service must use `WEIGHTED` routing policy.');
59+
}
60+
61+
const resource = new CfnInstance(this, 'Resource', {
62+
instanceAttributes: {
63+
AWS_ALIAS_DNS_NAME: props.dnsName,
64+
...props.customAttributes
65+
},
66+
instanceId: props.instanceId || this.node.uniqueId,
67+
serviceId: props.service.serviceId
68+
});
69+
70+
this.service = props.service;
71+
this.instanceId = resource.instanceId;
72+
this.dnsName = props.dnsName;
73+
}
74+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import cdk = require('@aws-cdk/cdk');
2+
import { BaseInstanceProps, InstanceBase } from './instance';
3+
import { NamespaceType } from './namespace';
4+
import { DnsRecordType, IService } from './service';
5+
import { CfnInstance } from './servicediscovery.generated';
6+
7+
/*
8+
* Properties for a CnameInstance used for service#registerCnameInstance
9+
*/
10+
export interface CnameInstanceBaseProps extends BaseInstanceProps {
11+
/**
12+
* If the service configuration includes a CNAME record, the domain name that you want Route 53 to
13+
* return in response to DNS queries, for example, example.com. This value is required if the
14+
* service specified by ServiceId includes settings for an CNAME record.
15+
*/
16+
instanceCname: string;
17+
}
18+
19+
/*
20+
* Properties for a CnameInstance
21+
*/
22+
export interface CnameInstanceProps extends CnameInstanceBaseProps {
23+
/**
24+
* The Cloudmap service this resource is registered to.
25+
*/
26+
service: IService;
27+
}
28+
29+
/*
30+
* Instance that is accessible using a domain name (CNAME).
31+
*/
32+
export class CnameInstance extends InstanceBase {
33+
/**
34+
* The Id of the instance
35+
*/
36+
public readonly instanceId: string;
37+
38+
/**
39+
* The Cloudmap service to which the instance is registered.
40+
*/
41+
public readonly service: IService;
42+
43+
/**
44+
* The domain name returned by DNS queries for the instance
45+
*/
46+
public readonly instanceCname: string;
47+
48+
constructor(scope: cdk.Construct, id: string, props: CnameInstanceProps) {
49+
super(scope, id);
50+
51+
if (props.service.namespace.type === NamespaceType.Http) {
52+
throw new Error('Namespace associated with Service must be a DNS Namespace.');
53+
}
54+
55+
if (props.service.dnsRecordType !== DnsRecordType.CNAME) {
56+
throw new Error('A `CnameIntance` can only be used with a service using a `CNAME` record.');
57+
}
58+
59+
const resource = new CfnInstance(this, 'Resource', {
60+
instanceId: props.instanceId || this.uniqueInstanceId(),
61+
serviceId: props.service.serviceId,
62+
instanceAttributes: {
63+
AWS_INSTANCE_CNAME: props.instanceCname,
64+
...props.customAttributes
65+
}
66+
});
67+
68+
this.service = props.service;
69+
this.instanceId = resource.instanceId;
70+
this.instanceCname = props.instanceCname;
71+
}
72+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import cdk = require('@aws-cdk/cdk');
2+
import { BaseNamespaceProps, NamespaceBase, NamespaceType } from './namespace';
3+
import { BaseServiceProps, Service } from './service';
4+
import { CfnHttpNamespace } from './servicediscovery.generated';
5+
6+
// tslint:disable:no-empty-interface
7+
export interface HttpNamespaceProps extends BaseNamespaceProps {}
8+
9+
/**
10+
* Define an HTTP Namespace
11+
*/
12+
export class HttpNamespace extends NamespaceBase {
13+
/**
14+
* A name for the namespace.
15+
*/
16+
public readonly namespaceName: string;
17+
18+
/**
19+
* Namespace Id for the namespace.
20+
*/
21+
public readonly namespaceId: string;
22+
23+
/**
24+
* Namespace Arn for the namespace.
25+
*/
26+
public readonly namespaceArn: string;
27+
28+
/**
29+
* Type of the namespace.
30+
*/
31+
public readonly type: NamespaceType;
32+
33+
constructor(scope: cdk.Construct, id: string, props: HttpNamespaceProps) {
34+
super(scope, id);
35+
36+
const ns = new CfnHttpNamespace(this, 'Resource', {
37+
name: props.name,
38+
description: props.description
39+
});
40+
41+
this.namespaceName = props.name;
42+
this.namespaceId = ns.httpNamespaceId;
43+
this.namespaceArn = ns.httpNamespaceArn;
44+
this.type = NamespaceType.Http;
45+
}
46+
47+
/**
48+
* Creates a service within the namespace
49+
*/
50+
public createService(id: string, props?: BaseServiceProps): Service {
51+
return new Service(this, id, {
52+
namespace: this,
53+
...props
54+
});
55+
}
56+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1+
export * from './instance';
2+
export * from './alias-target-instance';
3+
export * from './cname-instance';
4+
export * from './ip-instance';
5+
export * from './non-ip-instance';
6+
export * from './namespace';
7+
export * from './http-namespace';
8+
export * from './private-dns-namespace';
9+
export * from './public-dns-namespace';
10+
export * from './service';
111
// AWS::ServiceDiscovery CloudFormation Resources:
212
export * from './servicediscovery.generated';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import cdk = require('@aws-cdk/cdk');
2+
import { IService } from './service';
3+
4+
export interface IInstance extends cdk.IConstruct {
5+
/**
6+
* The id of the instance resource
7+
*/
8+
readonly instanceId: string;
9+
10+
/**
11+
* The Cloudmap service this resource is registered to.
12+
*/
13+
readonly service: IService;
14+
}
15+
16+
/**
17+
* Used when the resource that's associated with the service instance is accessible using values other than an IP
18+
* address or a domain name (CNAME), i.e. for non-ip-instances
19+
*/
20+
export interface BaseInstanceProps {
21+
/**
22+
* The id of the instance resource
23+
*
24+
* @default Automatically generated name
25+
*/
26+
instanceId?: string;
27+
28+
/**
29+
* Custom attributes of the instance.
30+
*
31+
* @default none
32+
*/
33+
customAttributes?: { [key: string]: string };
34+
}
35+
36+
export abstract class InstanceBase extends cdk.Construct implements IInstance {
37+
/**
38+
* The Id of the instance
39+
*/
40+
public abstract readonly instanceId: string;
41+
42+
/**
43+
* The Cloudmap service to which the instance is registered.
44+
*/
45+
public abstract readonly service: IService;
46+
47+
/**
48+
* Generate a unique instance Id that is safe to pass to CloudMap
49+
*/
50+
protected uniqueInstanceId() {
51+
// Max length of 64 chars, get the last 64 chars
52+
const id = this.node.uniqueId;
53+
return id.substring(Math.max(id.length - 64, 0), id.length);
54+
}
55+
}

0 commit comments

Comments
 (0)