Skip to content

Commit

Permalink
feat(servicediscovery): Add construct lib
Browse files Browse the repository at this point in the history
Partially addresses aws#1554
  • Loading branch information
SoManyHs committed Mar 5, 2019
1 parent 7b3a30b commit df70967
Show file tree
Hide file tree
Showing 15 changed files with 1,622 additions and 11 deletions.
9 changes: 9 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
## The CDK Construct Library for AWS Service Discovery
This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project.

This package contains constructs for working with **AWS Cloud Map**

AWS Cloud Map is a fully managed service that you can use to create and
maintain a map of the backend services and resources that your applications
depend on.

For further information on AWS Cloud Map,
see the [AWS Cloud Map documentation](https://docs.aws.amazon.com/cloud-map)
41 changes: 41 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/lib/http-namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import cdk = require('@aws-cdk/cdk');
import { CfnHttpNamespace } from './servicediscovery.generated';

export interface HttpNamespaceProps {
/**
* A name for the HttpNamespace.
*/
name: string;

/**
* A description of the namespace.
*/
description?: string;
}

/**
* Define a Service Discovery HTTP Namespace
*/
export class HttpNamespace extends cdk.Construct {
/**
* A domain name
*/
public readonly name: string;

/**
* Namespace Id for the HttpNamespace.
*/
public readonly namespaceId: string;

constructor(scope: cdk.Construct, id: string, props: HttpNamespaceProps) {
super(scope, id);

const ns = new CfnHttpNamespace(this, 'Resource', {
name: props.name,
description: props.description
});

this.name = props.name;
this.namespaceId = ns.httpNamespaceId;
}
}
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './instance';
export * from './namespace';
export * from './service';
// AWS::ServiceDiscovery CloudFormation Resources:
export * from './servicediscovery.generated';
168 changes: 168 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/lib/instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import cdk = require('@aws-cdk/cdk');
import { NamespaceType } from './namespace';
import { DnsRecordType, Service } from './service';
import { CfnInstance } from './servicediscovery.generated';

/**
* Properties to define ServiceDiscovery Instance
*/
export interface InstanceProps {
/**
* The instance attributes.
* FIXME: add defaults/validations
*/
instanceAttributes: InstanceAttributes

/**
* The id of the instance resource
*/
instanceId: string;

/**
* The Cloudmap service this resource is registered to.
*/
service: Service;
}

/**
* Define a Service Discovery Instance
*/
export class Instance extends cdk.Construct {
/**
* The Id of the instance
*/
public readonly instanceId: string;
/**
* The Cloudmap service to which the instance is registered.
*/
public readonly service: Service;

constructor(scope: cdk.Construct, id: string, props: InstanceProps) {
super(scope, id);

const customAttributes = props.instanceAttributes.customAttributes || {};

const resource = new CfnInstance(this, 'Resource', {
instanceAttributes: { ...customAttributes, ...getInstanceAttributes(props) },
instanceId: props.instanceId,
serviceId: props.service.serviceId
});

this.service = props.service;
this.instanceId = resource.instanceId;
}
}

// NOTE: These are the 5 that seem to be supported in cloudformation, but the API docs indicate that you can also
// specify custom attributes. Not sure if CFN would support these? In the generated L1s instance attributes appears to
// just be an object.
export interface InstanceAttributes {
/**
* If you want AWS Cloud Map to create an Amazon Route 53 alias record that routes traffic to an Elastic Load
* Balancing load balancer, specify the DNS name that is associated with the load balancer.
*/
aliasDnsName?: string,

/**
* If the service configuration includes a CNAME record, the domain name that you want Route 53 to return in
* response to DNS queries, for example, example.com. This value is required if the service specified by ServiceId
* includes settings for an CNAME record.
*/
instanceCname?: string,

/** The port on the endpoint that you want AWS Cloud Map to perform health checks on. This value is also used for
* the port value in an SRV record if the service that you specify includes an SRV record. You can also specify a
* default port that is applied to all instances in the Service configuration.
*/
port?: string,
/**
* If the service that you specify contains a template for an A record, the IPv4 address that you want AWS Cloud
* Map to use for the value of the A record.
*/
ipv4?: string,
/**
* If the service that you specify contains a template for an AAAA record, the IPv6 address that you want AWS Cloud
* Map to use for the value of the AAAA record.
*/
ipv6?: string,

/**
* Custom attributes of the instance.
*/
customAttributes?: object;
}

/**
* Validates instance attributes and returns standard attributes based on the namespace/service type.
*
* @param props instance props
* @throws if the instance attributes are invalid
*/
function getInstanceAttributes(props: InstanceProps): object {
if (props.instanceAttributes.aliasDnsName && props.instanceAttributes.instanceCname) {
throw new Error('Cannot specify both `aliasDnsName` and `instanceCname`.');
}

if (props.service.namespace.type === NamespaceType.Http) {
if (props.instanceAttributes.aliasDnsName || props.instanceAttributes.instanceCname) {
throw new Error('Cannot specify `aliasDnsName` or `instanceCname` for an HTTP namespace.');
}

return {
AWS_INSTANCE_IPV4: props.instanceAttributes.ipv4,
AWS_INSTANCE_IPV6: props.instanceAttributes.ipv6,
AWS_INSTANCE_PORT: props.instanceAttributes.port
};
}

if (props.service.dnsRecordType === DnsRecordType.Cname) {
if (!props.instanceAttributes.instanceCname) {
throw new Error('A `instanceCname` must be specified for a service using a `CNAME` record.');
}

return {
AWS_INSTANCE_CNAME: props.instanceAttributes.instanceCname
};
}

if (props.service.dnsRecordType === DnsRecordType.Srv) {
if (!props.instanceAttributes.port) {
throw new Error('A `port` must be specified for a service using a `SRV` record.');
}

if (!props.instanceAttributes.ipv4 && !props.instanceAttributes.ipv6) {
throw new Error('At least `ipv4` or `ipv6` must be specified for a service using a `SRV` record.');
}

return {
AWS_INSTANCE_IPV4: props.instanceAttributes.ipv4,
AWS_INSTANCE_IPV6: props.instanceAttributes.ipv6,
AWS_INSTANCE_PORT: props.instanceAttributes.port
};
}

if (props.instanceAttributes.aliasDnsName) {
if (props.instanceAttributes.ipv4 || props.instanceAttributes.ipv6 || props.instanceAttributes.port) {
throw new Error('Cannot specify `ipv4`, `ipv6` or `port` when specifying `aliasDnsName`.');
}

return {
AWS_ALIAS_DNS_NAME: props.instanceAttributes.aliasDnsName
};
}

if (!props.instanceAttributes.ipv4 && (props.service.dnsRecordType === DnsRecordType.A || props.service.dnsRecordType === DnsRecordType.A_AAAA)) {
throw new Error('An `ipv4` must be specified for a service using a `A` record.');
}

if (!props.instanceAttributes.ipv6 &&
(props.service.dnsRecordType === DnsRecordType.AAAA || props.service.dnsRecordType === DnsRecordType.A_AAAA)) {
throw new Error('An `ipv6` must be specified for a service using a `AAAA` record.');
}

return {
AWS_INSTANCE_IPV4: props.instanceAttributes.ipv4,
AWS_INSTANCE_IPV6: props.instanceAttributes.ipv6,
AWS_INSTANCE_PORT: props.instanceAttributes.port
};
}
156 changes: 156 additions & 0 deletions packages/@aws-cdk/aws-servicediscovery/lib/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import ec2 = require('@aws-cdk/aws-ec2');
import cdk = require('@aws-cdk/cdk');
// import { BaseServiceProps, Service } from './service';
import { CfnHttpNamespace, CfnPrivateDnsNamespace, CfnPublicDnsNamespace} from './servicediscovery.generated';

export interface INamespace extends cdk.IConstruct {
/**
* A name for the Namespace.
*/
readonly namespaceName: string;

/**
* Namespace Id for the Namespace.
*/
readonly namespaceId: string;

/**
* Namespace ARN for the Namespace.
*/
readonly namespaceArn: string;

/**
* Type of Namespace. Valid values: HTTP, DNS_PUBLIC, or DNS_PRIVATE
*/
readonly type: NamespaceType;
}

export interface NamespaceProps {
/**
* A name for the Namespace.
*/
name: string;

/**
* A description of the Namespace.
*
* @default none
*/
description?: string;

/**
* Type of Namespace. Valid values: HTTP, DNS_PUBLIC, or DNS_PRIVATE
*
* @default HTTP
*/
type?: NamespaceType;

/**
* The Amazon VPC that you want to associate the namespace with.
* Only applies for Private DNS Namespaces.
*
* @default none
*/
vpc?: ec2.IVpcNetwork;

}

export interface NamespaceImportProps {
/**
* A name for the Namespace.
*/
readonly namespaceName: string;

/**
* Namespace Id for the Namespace.
*/
readonly namespaceId: string;

/**
* Namespace ARN for the Namespace.
*/
readonly namespaceArn: string;

/**
* Type of Namespace. Valid values: HTTP, DNS_PUBLIC, or DNS_PRIVATE
*/
readonly type: NamespaceType;
}

export enum NamespaceType {
Http = "HTTP",
DnsPublic = "DNS_PUBLIC",
DnsPrivate = "DNS_PRIVATE"
}

export class Namespace extends cdk.Construct implements INamespace {
public readonly namespaceName: string;
public readonly namespaceId: string;
public readonly namespaceArn: string;
public readonly type: NamespaceType;

constructor(scope: cdk.Construct, id: string, props: NamespaceProps) {
super(scope, id);

const namespaceName = props.name;
const description = props.description;

let namespaceType = props.type;
if (namespaceType === undefined) {
namespaceType = NamespaceType.Http;
}

this.namespaceName = namespaceName;
this.type = namespaceType;

switch (namespaceType) {
case NamespaceType.Http: {
const ns = new CfnHttpNamespace(this, 'Resource', {
name: namespaceName,
description
});

this.namespaceId = ns.httpNamespaceId;
this.namespaceArn = ns.httpNamespaceArn;
break;
}

case NamespaceType.DnsPrivate: {
if (props.vpc === undefined) {
throw new Error(`VPC must be specified for PrivateDNSNamespaces`);
}

const ns = new CfnPrivateDnsNamespace(this, 'Resource', {
name: namespaceName,
description,
vpc: props.vpc.vpcId
});

this.namespaceId = ns.privateDnsNamespaceId;
this.namespaceArn = ns.privateDnsNamespaceArn;
break;
}

case NamespaceType.DnsPublic: {
const ns = new CfnPublicDnsNamespace(this, 'Resource', {
name: namespaceName,
description
});

this.namespaceId = ns.publicDnsNamespaceId;
this.namespaceArn = ns.publicDnsNamespaceArn;
break;
}
}
}

/**
* Creates a new service in this namespace FIXME -- not setting namespace correctly
*/
// public createService(id: string, props?: BaseServiceProps): Service {
// return new Service(this, id, {
// namespace: this,
// ...props,
// });
// }
}
Loading

0 comments on commit df70967

Please sign in to comment.