From 5cab38ce8eb40cf53693c223bef1665f13f9fe4e Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 29 Sep 2020 13:14:07 -0700 Subject: [PATCH 01/34] Initial VGW commit --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 166 ++++++++++ packages/@aws-cdk/aws-appmesh/lib/index.ts | 2 + packages/@aws-cdk/aws-appmesh/lib/mesh.ts | 16 + .../aws-appmesh/lib/shared-interfaces.ts | 14 +- packages/@aws-cdk/aws-appmesh/lib/utils.ts | 41 +++ .../aws-appmesh/lib/virtual-gateway.ts | 294 ++++++++++++++++++ .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 37 +-- packages/@aws-cdk/aws-appmesh/package.json | 10 + .../aws-appmesh/test/integ.mesh.expected.json | 29 ++ .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 6 + .../aws-appmesh/test/test.gateway-route.ts | 38 +++ .../aws-appmesh/test/test.virtual-gateway.ts | 41 +++ 12 files changed, 660 insertions(+), 34 deletions(-) create mode 100644 packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts create mode 100644 packages/@aws-cdk/aws-appmesh/lib/utils.ts create mode 100644 packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts create mode 100644 packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts create mode 100644 packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts new file mode 100644 index 0000000000000..e502980c74b5d --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -0,0 +1,166 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnGatewayRoute } from './appmesh.generated'; +import { IMesh } from './mesh'; +import { IVirtualGateway } from './virtual-gateway'; + +/** + * Interface for which all Gateway Route based classes MUST implement + */ +export interface IGatewayRoute extends cdk.IResource { + /** + * The name of the Gateway Route + * + * @attribute + */ + readonly gatewayRouteName: string, + + /** + * The Amazon Resource Name (ARN) for the Gateway Route + * + * @attribute + */ + readonly gatewayRouteArn: string; +} + +/** + * Base interface properties for all Gateway Routes + */ +export interface GatewayRouteBaseProps { + /** + * The name of the Gateway Route + * + * @default - an automatically generated name + */ + readonly gatewayRouteName?: string; + + /** + * The Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGateway: IVirtualGateway; +} + +/** + * Properties to define new Gateway Routes + */ +export interface GatewayRouteProps extends GatewayRouteBaseProps { + /** + * The mesh to define the Gateway Route in + */ + readonly mesh: IMesh; +} + +/** + * Gateway Route represents a new or existing gateway route attached to a VirtualGateway and Mesh + * + * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html + */ +export class GatewayRoute extends cdk.Resource implements IGatewayRoute { + /** + * Import an existing Gateway Route given an ARN + */ + public static fromGatewayRouteArn(scope: cdk.Construct, id: string, gatewayRouteArn: string): IGatewayRoute { + return new ImportedGatewayRoute(scope, id, { gatewayRouteArn }); + } + + /** + * Import an existing Gateway Route given its name + */ + public static fromGatewayRouteName( + scope: cdk.Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { + return new ImportedGatewayRoute(scope, id, { meshName, virtualGatewayName, gatewayRouteName }); + } + + /** + * The name of the Gateway Route + */ + public readonly gatewayRouteName: string; + + /** + * The Amazon Resource Name (ARN) for the Gateway Route + */ + public readonly gatewayRouteArn: string; + + /** + * The VirtualGateway this GatewayRoute is a part of + */ + public readonly virtualGateway: IVirtualGateway; + + constructor(scope: cdk.Construct, id: string, props: GatewayRouteProps) { + super(scope, id, { + physicalName: props.gatewayRouteName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), + }); + + this.virtualGateway = props.virtualGateway; + + const gatewayRoute = new CfnGatewayRoute(this, 'Resource', { + gatewayRouteName: this.physicalName, + meshName: props.virtualGateway.mesh.meshName, + spec: {}, + virtualGatewayName: this.virtualGateway.virtualGatewayName, + }); + + this.gatewayRouteName = this.getResourceNameAttribute(gatewayRoute.attrGatewayRouteName); + this.gatewayRouteArn = this.getResourceArnAttribute(gatewayRoute.ref, { + service: 'appmesh', + resource: `mesh/${props.mesh.meshName}/virtualRouter/${this.virtualGateway.virtualGatewayName}/gatewayRoute`, + resourceName: this.physicalName, + }); + } +} + +/** + * Interface with properties necessary to import a reusable Gateway Route + */ +interface GatewayRouteAttributes { + /** + * The name of the Gateway Route + */ + readonly gatewayRouteName?: string; + + /** + * The Amazon Resource Name (ARN) for the Gateway Route + */ + readonly gatewayRouteArn?: string; + + /** + * The name of the mesh this Gateway Route is associated with + */ + readonly meshName?: string; + + /** + * The name of the Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGatewayName?: string; +} + +/** + * Represents an imported IGatewayRoute + */ +class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { + /** + * The name of the Gateway Route + */ + public gatewayRouteName: string; + + /** + * The Amazon Resource Name (ARN) for the Gateway Route + */ + public gatewayRouteArn: string; + + constructor(scope: cdk.Construct, id: string, props: GatewayRouteAttributes) { + super(scope, id); + if (props.gatewayRouteArn) { + this.gatewayRouteArn = props.gatewayRouteArn; + this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + } else if (props.gatewayRouteName && props.meshName && props.virtualGatewayName) { + this.gatewayRouteName = props.gatewayRouteName; + this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ + service: 'appmesh', + resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGatewayName}/gatewayRoute`, + resourceName: this.gatewayRouteName, + }); + } else { + throw new Error('Need either arn or three names'); + } + } +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/index.ts b/packages/@aws-cdk/aws-appmesh/lib/index.ts index ff36676e2ec1d..5b8e10105f08d 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/index.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/index.ts @@ -6,3 +6,5 @@ export * from './shared-interfaces'; export * from './virtual-node'; export * from './virtual-router'; export * from './virtual-service'; +export * from './virtual-gateway'; +export * from './gateway-route'; diff --git a/packages/@aws-cdk/aws-appmesh/lib/mesh.ts b/packages/@aws-cdk/aws-appmesh/lib/mesh.ts index 2aaded50f99bc..78b9c16e85af5 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/mesh.ts @@ -1,6 +1,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnMesh } from './appmesh.generated'; +import { VirtualGateway, VirtualGatewayBaseProps } from './virtual-gateway'; import { VirtualNode, VirtualNodeBaseProps } from './virtual-node'; import { VirtualRouter, VirtualRouterBaseProps } from './virtual-router'; import { VirtualService, VirtualServiceBaseProps } from './virtual-service'; @@ -54,6 +55,11 @@ export interface IMesh extends cdk.IResource { * Adds a VirtualNode to the Mesh */ addVirtualNode(id: string, props?: VirtualNodeBaseProps): VirtualNode; + + /** + * Adds a VirtualGateway to the Mesh + */ + addVirtualGateway(id: string, props: VirtualGatewayBaseProps): VirtualGateway; } /** @@ -99,6 +105,16 @@ abstract class MeshBase extends cdk.Resource implements IMesh { mesh: this, }); } + + /** + * Adds a VirtualGateway to the Mesh + */ + addVirtualGateway(id: string, props: VirtualGatewayBaseProps): VirtualGateway { + return new VirtualGateway(this, id, { + ...props, + mesh: this, + }); + } } /** diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 7cd55df23565d..62230fe569916 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -82,9 +82,9 @@ export interface PortMapping { } /** - * Represents the properties needed to define healthy and active listeners for nodes. + * Base properties all listeners share */ -export interface VirtualNodeListener { +export interface ListenerBase { /** * Array of PortMappingProps for the listener * @@ -100,6 +100,16 @@ export interface VirtualNodeListener { readonly healthCheck?: HealthCheck; } +/** + * Represents the properties needed to define healthy and active listeners for nodes + */ +export interface VirtualNodeListener extends ListenerBase {} + +/** + * Represents the properties needed to define listeners for Virtual Gateways + */ +export interface VirtualGatewayListener extends ListenerBase {} + /** * All Properties for Envoy Access logs for mesh endpoints */ diff --git a/packages/@aws-cdk/aws-appmesh/lib/utils.ts b/packages/@aws-cdk/aws-appmesh/lib/utils.ts new file mode 100644 index 0000000000000..7e07348ed79f0 --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/utils.ts @@ -0,0 +1,41 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated'; + +type AppMeshHealthCheck = CfnVirtualNode.HealthCheckProperty | CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty + +/** + * Validates health check properties, throws an error if they are misconfigured. + * + * @param healthCheck Healthcheck property from a Virtual Node or Virtual Gateway + */ +export function validateHealthChecks(healthCheck: AppMeshHealthCheck) { + (Object.keys(healthCheck) as Array) + .filter((key) => + HEALTH_CHECK_PROPERTY_THRESHOLDS[key] && + typeof healthCheck[key] === 'number' && + !cdk.Token.isUnresolved(healthCheck[key]), + ).map((key) => { + const [min, max] = HEALTH_CHECK_PROPERTY_THRESHOLDS[key]!; + const value = healthCheck[key]!; + + if (value < min) { + throw new Error(`The value of '${key}' is below the minimum threshold (expected >=${min}, got ${value})`); + } + if (value > max) { + throw new Error(`The value of '${key}' is above the maximum threshold (expected <=${max}, got ${value})`); + } + }); +} + +/** + * Minimum and maximum thresholds for HeathCheck numeric properties + * + * @see https://docs.aws.amazon.com/app-mesh/latest/APIReference/API_HealthCheckPolicy.html + */ +const HEALTH_CHECK_PROPERTY_THRESHOLDS: {[key in (keyof AppMeshHealthCheck)]?: [number, number]} = { + healthyThreshold: [2, 10], + intervalMillis: [5000, 300000], + port: [1, 65535], + timeoutMillis: [2000, 60000], + unhealthyThreshold: [2, 10], +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts new file mode 100644 index 0000000000000..3a3b4073b9788 --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -0,0 +1,294 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; +import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; +import { IMesh, Mesh } from './mesh'; +import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualGatewayListener } from './shared-interfaces'; +import { validateHealthChecks } from './utils'; + +/** + * Interface which all Virtual Gateway based classes must implement + */ +export interface IVirtualGateway extends cdk.IResource { + /** + * Name of the VirtualGateway + * + * @attribute + */ + readonly virtualGatewayName: string; + + /** + * The Amazon Resource Name (ARN) for the VirtualGateway + * + * @attribute + */ + readonly virtualGatewayArn: string; + + /** + * The mesh which the VirtualGateway belongs to + */ + readonly mesh: IMesh; + + /** + * Utility method to add a list of listeners to this VirtualGateway + */ + addListeners(listeners: VirtualGatewayListener[]): void; + + /** + * Utility method to add a new GatewayRoute to the VirtualGateway + */ + addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; +} + +/** + * Basic configuration properties for a VirtualGateway + */ +export interface VirtualGatewayBaseProps { + /** + * Name of the VirtualGateway + * + * @default - A name is automatically determined + */ + readonly virtualGatewayName?: string; + + /** + * Listeners for the VirtualGateway. Only one is supported. + */ + readonly listeners: VirtualGatewayListener[]; + + /** + * Access Logging Configuration for the VirtualGateway + * + * @default no access logging + */ + readonly accessLog?: AccessLog; +} + +/** + * Properties used when creating a new VirtualGateway + */ +export interface VirtualGatewayProps extends VirtualGatewayBaseProps { + /** + * The mesh which the VirtualGateway belongs to + */ + readonly mesh: IMesh; +} + +abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGateway { + /** + * Name of the VirtualGateway + */ + public abstract readonly virtualGatewayName: string; + + /** + * The Amazon Resource Name (ARN) for the VirtualGateway + */ + public abstract readonly virtualGatewayArn: string; + + /** + * The name of the mesh which the VirtualGateway belongs to + */ + public abstract readonly mesh: IMesh; + + protected readonly listeners = new Array(); + protected readonly routes = new Array(); + + /** + * Utility method to add a list of listeners to this VirtualGateway + */ + public addListeners(listeners: VirtualGatewayListener[]) { + if (this.listeners.length + this.listeners.length > 1) { + throw new Error('VirtualGateway may have at most one listener'); + } + for (const listener of listeners) { + const portMapping = listener.portMapping || { port: 8080, protocol: Protocol.HTTP }; + this.listeners.push({ + portMapping, + healthCheck: renderHealthCheck(listener.healthCheck, portMapping), + }); + } + } + + /** + * Utility method to add a new GatewayRoute to the VirtualGateway + */ + public addGatewayRoute(id: string, props: GatewayRouteBaseProps): GatewayRoute { + return new GatewayRoute(this, id, { + ...props, + mesh: this.mesh, + }); + } +} + +/** + * VirtualGateway represents a newly defined App Mesh Virtual Gateway + * + * A virtual gateway allows resources that are outside of your mesh to communicate to resources that + * are inside of your mesh. + * + * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/virtual_gateways.html + */ +export class VirtualGateway extends VirtualGatewayBase { + /** + * Import an existing VirtualGateway given an ARN + */ + public static fromVirtualGatewayArn(scope: cdk.Construct, id: string, virtualGatewayArn: string): IVirtualGateway { + return new ImportedVirtualGateway(scope, id, { virtualGatewayArn }); + } + + /** + * Import an existing VirtualGateway given its name + */ + public static fromVirtualNodeName(scope: cdk.Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { + return new ImportedVirtualGateway(scope, id, { + meshName, + virtualGatewayName, + }); + } + + /** + * The name of the VirtualGateway + */ + public readonly virtualGatewayName: string; + + /** + * The Amazon Resource Name (ARN) for the VirtualGateway + */ + public readonly virtualGatewayArn: string; + + /** + * The Mesh that the VirtualGateway belongs to + */ + public readonly mesh: IMesh; + + constructor(scope: cdk.Construct, id: string, props: VirtualGatewayProps) { + super(scope, id, { + physicalName: props.virtualGatewayName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), + }); + + this.mesh = props.mesh; + + this.addListeners(props.listeners ? props.listeners : []); + const accessLogging = props.accessLog?.bind(this); + + const node = new CfnVirtualGateway(this, 'Resource', { + virtualGatewayName: this.physicalName, + meshName: this.mesh.meshName, + spec: { + listeners: cdk.Lazy.anyValue({ produce: () => this.listeners }, { omitEmptyArray: true }), + logging: accessLogging !== undefined ? { + accessLog: { + file: accessLogging.filePath != undefined ? { + path: accessLogging.filePath, + } : undefined, + }, + } : undefined, + }, + }); + + this.virtualGatewayName = this.getResourceNameAttribute(node.attrVirtualGatewayName); + this.virtualGatewayArn = this.getResourceArnAttribute(node.ref, { + service: 'appmesh', + resource: `mesh/${props.mesh.meshName}/virtualGateway`, + resourceName: this.physicalName, + }); + } +} + +function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty | undefined { + if (hc === undefined) { return undefined; } + + if (hc.protocol === Protocol.TCP && hc.path) { + throw new Error('The path property cannot be set with Protocol.TCP'); + } + + if (hc.protocol === Protocol.GRPC && hc.path) { + throw new Error('The path property cannot be set with Protocol.GRPC'); + } + + const healthCheck: CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty = { + healthyThreshold: hc.healthyThreshold || 2, + intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min + path: hc.path || (hc.protocol === Protocol.HTTP ? '/' : undefined), + port: hc.port || pm.port, + protocol: hc.protocol || pm.protocol, + timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), + unhealthyThreshold: hc.unhealthyThreshold || 2, + }; + + validateHealthChecks(healthCheck); + + return healthCheck; +} + +/** + * Unterface with properties necessary to import a reusable VirtualGateway + */ +interface VirtualGatewayAttributes { + /** + * The name of the VirtualGateway + */ + readonly virtualGatewayName?: string; + + /** + * The Amazon Resource Name (ARN) belonging to the VirtualGateway + */ + readonly virtualGatewayArn?: string; + + /** + * The Mesh that the VirtualGateway belongs to + */ + readonly mesh?: IMesh; + + /** + * The name of the mesh that the VirtualGateway belongs to + */ + readonly meshName?: string; +} + +/** + * Used to import a VirtualGateway and read its properties + */ +class ImportedVirtualGateway extends VirtualGatewayBase { + /** + * The name of the VirtualGateway + */ + public readonly virtualGatewayName: string; + + /** + * The Amazon Resource Name (ARN) belonging to the VirtualGateway + */ + public readonly virtualGatewayArn: string; + + /** + * The Mesh that the VirtualGateway belongs to + */ + public readonly mesh: IMesh; + + constructor(scope: cdk.Construct, id: string, props: VirtualGatewayAttributes) { + super(scope, id); + + if (props.mesh) { + this.mesh = props.mesh; + } else if (props.meshName) { + if (props.mesh) { + throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); + } + this.mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); + } else { + throw new Error('Supply either \'mesh\' or \'meshName\''); + } + if (props.virtualGatewayArn) { + this.virtualGatewayArn = props.virtualGatewayArn; + this.virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); + } else if (props.virtualGatewayName && props.meshName) { + this.virtualGatewayName = props.virtualGatewayName; + this.virtualGatewayArn = cdk.Stack.of(this).formatArn({ + service: 'appmesh', + resource: `mesh/${props.meshName}/virtualGateway`, + resourceName: this.virtualGatewayName, + }); + } else { + throw new Error('Need either arn or both names'); + } + } +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index 233ad2d0f216c..2ffb108432e2d 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -4,6 +4,7 @@ import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; import { IMesh } from './mesh'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualNodeListener } from './shared-interfaces'; +import { validateHealthChecks } from './utils'; import { IVirtualService } from './virtual-service'; /** @@ -154,19 +155,6 @@ abstract class VirtualNodeBase extends cdk.Resource implements IVirtualNode { } } -/** - * Minimum and maximum thresholds for HeathCheck numeric properties - * - * @see https://docs.aws.amazon.com/app-mesh/latest/APIReference/API_HealthCheckPolicy.html - */ -const HEALTH_CHECK_PROPERTY_THRESHOLDS: {[key in (keyof CfnVirtualNode.HealthCheckProperty)]?: [number, number]} = { - healthyThreshold: [2, 10], - intervalMillis: [5000, 300000], - port: [1, 65535], - timeoutMillis: [2000, 60000], - unhealthyThreshold: [2, 10], -}; - function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVirtualNode.HealthCheckProperty | undefined { if (hc === undefined) { return undefined; } @@ -188,28 +176,13 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir unhealthyThreshold: hc.unhealthyThreshold || 2, }; - (Object.keys(healthCheck) as Array) - .filter((key) => - HEALTH_CHECK_PROPERTY_THRESHOLDS[key] && - typeof healthCheck[key] === 'number' && - !cdk.Token.isUnresolved(healthCheck[key]), - ).map((key) => { - const [min, max] = HEALTH_CHECK_PROPERTY_THRESHOLDS[key]!; - const value = healthCheck[key]!; - - if (value < min) { - throw new Error(`The value of '${key}' is below the minimum threshold (expected >=${min}, got ${value})`); - } - if (value > max) { - throw new Error(`The value of '${key}' is above the maximum threshold (expected <=${max}, got ${value})`); - } - }); + validateHealthChecks(healthCheck); return healthCheck; } /** - * VirtualNode represents a newly defined AppMesh VirtualNode + * VirtualNode represents a newly defined App Mesh VirtualNode * * Any inbound traffic that your virtual node expects should be specified as a * listener. Any outbound traffic that your virtual node expects to reach @@ -241,7 +214,7 @@ export class VirtualNode extends VirtualNodeBase { public readonly virtualNodeName: string; /** - * The Amazon Resource Name belonging to the VirtualNdoe + * The Amazon Resource Name belonging to the VirtualNode */ public readonly virtualNodeArn: string; @@ -316,7 +289,7 @@ interface VirtualNodeAttributes { } /** - * Used to import a VirtualNode and read it's properties + * Used to import a VirtualNode and read its properties */ class ImportedVirtualNode extends VirtualNodeBase { /** diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index 94a4d5d7fe0b5..35d3c448ba029 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -107,10 +107,16 @@ "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualNode", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualRouter", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualService", + "from-signature:@aws-cdk/aws-appmesh.GatewayRoute.fromGatewayRouteName", "from-signature:@aws-cdk/aws-appmesh.Route.fromRouteName", "from-signature:@aws-cdk/aws-appmesh.VirtualNode.fromVirtualNodeName", "from-signature:@aws-cdk/aws-appmesh.VirtualRouter.fromVirtualRouterName", "from-signature:@aws-cdk/aws-appmesh.VirtualService.fromVirtualServiceName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteMeshName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteMeshOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteResourceOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteVirtualGatewayName", "resource-attribute:@aws-cdk/aws-appmesh.IMesh.meshUid", "resource-attribute:@aws-cdk/aws-appmesh.IRoute.routeUid", "resource-attribute:@aws-cdk/aws-appmesh.IVirtualNode.virtualNodeUid", @@ -124,6 +130,10 @@ "resource-attribute:@aws-cdk/aws-appmesh.Route.routeResourceOwner", "resource-attribute:@aws-cdk/aws-appmesh.Route.routeUid", "resource-attribute:@aws-cdk/aws-appmesh.Route.routeVirtualRouterName", + "resource-attribute:@aws-cdk/aws-appmesh.VirtualGateway.virtualGatewayMeshName", + "resource-attribute:@aws-cdk/aws-appmesh.VirtualGateway.virtualGatewayMeshOwner", + "resource-attribute:@aws-cdk/aws-appmesh.VirtualGateway.virtualGatewayResourceOwner", + "resource-attribute:@aws-cdk/aws-appmesh.VirtualGateway.virtualGatewayUid", "resource-attribute:@aws-cdk/aws-appmesh.VirtualNode.virtualNodeMeshName", "resource-attribute:@aws-cdk/aws-appmesh.VirtualNode.virtualNodeMeshOwner", "resource-attribute:@aws-cdk/aws-appmesh.VirtualNode.virtualNodeResourceOwner", diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index 5b1b1657f0e20..6087c16472067 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -772,6 +772,35 @@ "VirtualNodeName": "meshstackmeshnode3C5835BCB" } }, + "meshgateway1B02387E8": { + "Type": "AWS::AppMesh::VirtualGateway", + "Properties": { + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "Listeners": [ + { + "PortMapping": { + "Port": 8080, + "Protocol": "http" + } + } + ], + "Logging": { + "AccessLog": { + "File": { + "Path": "/dev/stdout" + } + } + } + }, + "VirtualGatewayName": "gateway1" + } + }, "service27C65CF7D": { "Type": "AWS::AppMesh::VirtualService", "Properties": { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index df55025f1365c..3e8316ccd000b 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -116,3 +116,9 @@ router.addRoute('route-3', { }, ], }); + +mesh.addVirtualGateway('gateway1', { + accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), + listeners: [{}], + virtualGatewayName: 'gateway1', +}); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts new file mode 100644 index 0000000000000..826a827f4c80d --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -0,0 +1,38 @@ +import { expect, haveResourceLike } from '@aws-cdk/assert'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; + +import * as appmesh from '../lib'; + +export = { + 'When creating a GatewayRoute': { + 'should have expected defaults'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const virtualGateway = new appmesh.VirtualGateway(stack, 'gateway-1', { + listeners: [{}], + mesh: mesh, + }); + + virtualGateway.addGatewayRoute('gateway-route', { + virtualGateway: virtualGateway, + gatewayRouteName: 'gateway-route', + }); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route', + }), + ); + + test.done(); + }, + }, +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts new file mode 100644 index 0000000000000..7de2cecfe60bc --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -0,0 +1,41 @@ +import { expect, haveResourceLike } from '@aws-cdk/assert'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; + +import * as appmesh from '../lib'; + +export = { + 'When creating a VirtualGateway': { + 'should have expected defaults'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + new appmesh.VirtualGateway(stack, 'gateway-1', { + listeners: [{}], + mesh: mesh, + }); + + expect(stack).to( + haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + PortMapping: { + Port: 8080, + Protocol: appmesh.Protocol.HTTP, + }, + }, + ], + }, + VirtualGatewayName: 'gateway1', + }), + ); + test.done(); + }, + }, +}; \ No newline at end of file From 71ac7dd066052da332f52aa2596ed61f94b3c5d8 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 30 Sep 2020 21:50:49 -0700 Subject: [PATCH 02/34] Update with upstream changes --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 9 +++++---- .../aws-appmesh/lib/shared-interfaces.ts | 16 ++++++++++++++-- .../@aws-cdk/aws-appmesh/lib/virtual-gateway.ts | 15 ++++++--------- packages/@aws-cdk/aws-appmesh/package.json | 1 + 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index e502980c74b5d..6e86466975071 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -1,4 +1,5 @@ import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; import { IMesh } from './mesh'; import { IVirtualGateway } from './virtual-gateway'; @@ -58,7 +59,7 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** * Import an existing Gateway Route given an ARN */ - public static fromGatewayRouteArn(scope: cdk.Construct, id: string, gatewayRouteArn: string): IGatewayRoute { + public static fromGatewayRouteArn(scope: Construct, id: string, gatewayRouteArn: string): IGatewayRoute { return new ImportedGatewayRoute(scope, id, { gatewayRouteArn }); } @@ -66,7 +67,7 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { * Import an existing Gateway Route given its name */ public static fromGatewayRouteName( - scope: cdk.Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { + scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { return new ImportedGatewayRoute(scope, id, { meshName, virtualGatewayName, gatewayRouteName }); } @@ -85,7 +86,7 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { */ public readonly virtualGateway: IVirtualGateway; - constructor(scope: cdk.Construct, id: string, props: GatewayRouteProps) { + constructor(scope: Construct, id: string, props: GatewayRouteProps) { super(scope, id, { physicalName: props.gatewayRouteName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), }); @@ -147,7 +148,7 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { */ public gatewayRouteArn: string; - constructor(scope: cdk.Construct, id: string, props: GatewayRouteAttributes) { + constructor(scope: Construct, id: string, props: GatewayRouteAttributes) { super(scope, id); if (props.gatewayRouteArn) { this.gatewayRouteArn = props.gatewayRouteArn; diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 62230fe569916..2a193e26b3de8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -1,5 +1,5 @@ import * as cdk from '@aws-cdk/core'; -import { CfnVirtualNode } from './appmesh.generated'; +import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated'; /** * Enum of supported AppMesh protocols @@ -121,6 +121,13 @@ export interface AccessLogConfig { * @default - no access logging */ readonly virtualNodeAccessLog?: CfnVirtualNode.AccessLogProperty; + + /** + * VirtualGateway CFN configuration for Access Logging + * + * @default - no access logging + */ + readonly virtualGatewayAccessLog?: CfnVirtualGateway.VirtualGatewayAccessLogProperty; } /** @@ -166,6 +173,11 @@ class FileAccessLog extends AccessLog { path: this.filePath, }, }, + virtualGatewayAccessLog: { + file: { + path: this.filePath, + }, + }, }; } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 3a3b4073b9788..85a277ecb20df 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -1,4 +1,5 @@ import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; @@ -131,14 +132,14 @@ export class VirtualGateway extends VirtualGatewayBase { /** * Import an existing VirtualGateway given an ARN */ - public static fromVirtualGatewayArn(scope: cdk.Construct, id: string, virtualGatewayArn: string): IVirtualGateway { + public static fromVirtualGatewayArn(scope: Construct, id: string, virtualGatewayArn: string): IVirtualGateway { return new ImportedVirtualGateway(scope, id, { virtualGatewayArn }); } /** * Import an existing VirtualGateway given its name */ - public static fromVirtualNodeName(scope: cdk.Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { + public static fromVirtualGatewayName(scope: Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { return new ImportedVirtualGateway(scope, id, { meshName, virtualGatewayName, @@ -160,7 +161,7 @@ export class VirtualGateway extends VirtualGatewayBase { */ public readonly mesh: IMesh; - constructor(scope: cdk.Construct, id: string, props: VirtualGatewayProps) { + constructor(scope: Construct, id: string, props: VirtualGatewayProps) { super(scope, id, { physicalName: props.virtualGatewayName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), }); @@ -176,11 +177,7 @@ export class VirtualGateway extends VirtualGatewayBase { spec: { listeners: cdk.Lazy.anyValue({ produce: () => this.listeners }, { omitEmptyArray: true }), logging: accessLogging !== undefined ? { - accessLog: { - file: accessLogging.filePath != undefined ? { - path: accessLogging.filePath, - } : undefined, - }, + accessLog: accessLogging.virtualGatewayAccessLog, } : undefined, }, }); @@ -264,7 +261,7 @@ class ImportedVirtualGateway extends VirtualGatewayBase { */ public readonly mesh: IMesh; - constructor(scope: cdk.Construct, id: string, props: VirtualGatewayAttributes) { + constructor(scope: Construct, id: string, props: VirtualGatewayAttributes) { super(scope, id); if (props.mesh) { diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index 35d3c448ba029..dedc68ec9ec15 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -109,6 +109,7 @@ "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualService", "from-signature:@aws-cdk/aws-appmesh.GatewayRoute.fromGatewayRouteName", "from-signature:@aws-cdk/aws-appmesh.Route.fromRouteName", + "from-signature:@aws-cdk/aws-appmesh.VirtualGateway.fromVirtualGatewayName", "from-signature:@aws-cdk/aws-appmesh.VirtualNode.fromVirtualNodeName", "from-signature:@aws-cdk/aws-appmesh.VirtualRouter.fromVirtualRouterName", "from-signature:@aws-cdk/aws-appmesh.VirtualService.fromVirtualServiceName", From 0cf538af073e456e303857dc2839ef36935672b6 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Thu, 1 Oct 2020 22:29:24 -0700 Subject: [PATCH 03/34] Add initial round of tests --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 12 +- packages/@aws-cdk/aws-appmesh/lib/mesh.ts | 4 +- .../aws-appmesh/lib/virtual-gateway.ts | 16 +- .../aws-appmesh/test/test.gateway-route.ts | 1 - .../aws-appmesh/test/test.virtual-gateway.ts | 140 +++++++++++++++++- 5 files changed, 153 insertions(+), 20 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 6e86466975071..cbdb960d6acaa 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -1,7 +1,6 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; -import { IMesh } from './mesh'; import { IVirtualGateway } from './virtual-gateway'; /** @@ -33,11 +32,6 @@ export interface GatewayRouteBaseProps { * @default - an automatically generated name */ readonly gatewayRouteName?: string; - - /** - * The Virtual Gateway this Gateway Route is associated with - */ - readonly virtualGateway: IVirtualGateway; } /** @@ -45,9 +39,9 @@ export interface GatewayRouteBaseProps { */ export interface GatewayRouteProps extends GatewayRouteBaseProps { /** - * The mesh to define the Gateway Route in + * The Virtual Gateway this Gateway Route is associated with */ - readonly mesh: IMesh; + readonly virtualGateway: IVirtualGateway; } /** @@ -103,7 +97,7 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { this.gatewayRouteName = this.getResourceNameAttribute(gatewayRoute.attrGatewayRouteName); this.gatewayRouteArn = this.getResourceArnAttribute(gatewayRoute.ref, { service: 'appmesh', - resource: `mesh/${props.mesh.meshName}/virtualRouter/${this.virtualGateway.virtualGatewayName}/gatewayRoute`, + resource: `mesh/${props.virtualGateway.mesh.meshName}/virtualRouter/${this.virtualGateway.virtualGatewayName}/gatewayRoute`, resourceName: this.physicalName, }); } diff --git a/packages/@aws-cdk/aws-appmesh/lib/mesh.ts b/packages/@aws-cdk/aws-appmesh/lib/mesh.ts index 78b9c16e85af5..961128151b537 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/mesh.ts @@ -59,7 +59,7 @@ export interface IMesh extends cdk.IResource { /** * Adds a VirtualGateway to the Mesh */ - addVirtualGateway(id: string, props: VirtualGatewayBaseProps): VirtualGateway; + addVirtualGateway(id: string, props?: VirtualGatewayBaseProps): VirtualGateway; } /** @@ -109,7 +109,7 @@ abstract class MeshBase extends cdk.Resource implements IMesh { /** * Adds a VirtualGateway to the Mesh */ - addVirtualGateway(id: string, props: VirtualGatewayBaseProps): VirtualGateway { + addVirtualGateway(id: string, props?: VirtualGatewayBaseProps): VirtualGateway { return new VirtualGateway(this, id, { ...props, mesh: this, diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 85a277ecb20df..1296a73d61fb8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -53,8 +53,10 @@ export interface VirtualGatewayBaseProps { /** * Listeners for the VirtualGateway. Only one is supported. + * + * @default - Single HTTP listener on port 8080 */ - readonly listeners: VirtualGatewayListener[]; + readonly listeners?: VirtualGatewayListener[]; /** * Access Logging Configuration for the VirtualGateway @@ -115,7 +117,7 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa public addGatewayRoute(id: string, props: GatewayRouteBaseProps): GatewayRoute { return new GatewayRoute(this, id, { ...props, - mesh: this.mesh, + virtualGateway: this, }); } } @@ -168,14 +170,15 @@ export class VirtualGateway extends VirtualGatewayBase { this.mesh = props.mesh; - this.addListeners(props.listeners ? props.listeners : []); + // Use listener default of http listener port 8080 if no listener is defined + this.addListeners(props.listeners ? props.listeners : [{}]); const accessLogging = props.accessLog?.bind(this); const node = new CfnVirtualGateway(this, 'Resource', { virtualGatewayName: this.physicalName, meshName: this.mesh.meshName, spec: { - listeners: cdk.Lazy.anyValue({ produce: () => this.listeners }, { omitEmptyArray: true }), + listeners: this.listeners, logging: accessLogging !== undefined ? { accessLog: accessLogging.virtualGatewayAccessLog, } : undefined, @@ -271,8 +274,11 @@ class ImportedVirtualGateway extends VirtualGatewayBase { throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); } this.mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); + } else if (props.virtualGatewayArn) { + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); + this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); } else { - throw new Error('Supply either \'mesh\' or \'meshName\''); + throw new Error('Supply either \'mesh\' or \'meshName\' or \'virtualGatewayArn\''); } if (props.virtualGatewayArn) { this.virtualGatewayArn = props.virtualGatewayArn; diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 826a827f4c80d..24e6df8a0a417 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -21,7 +21,6 @@ export = { }); virtualGateway.addGatewayRoute('gateway-route', { - virtualGateway: virtualGateway, gatewayRouteName: 'gateway-route', }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 7de2cecfe60bc..f59052cf88a65 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -15,11 +15,11 @@ export = { meshName: 'test-mesh', }); - new appmesh.VirtualGateway(stack, 'gateway-1', { - listeners: [{}], + new appmesh.VirtualGateway(stack, 'testGateway', { mesh: mesh, }); + // THEN expect(stack).to( haveResourceLike('AWS::AppMesh::VirtualGateway', { Spec: { @@ -32,10 +32,144 @@ export = { }, ], }, - VirtualGatewayName: 'gateway1', + VirtualGatewayName: 'testGateway', }), ); test.done(); }, }, + 'should have expected features'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + listeners: [{ + healthCheck: {}, + portMapping: { + port: 80, + protocol: appmesh.Protocol.GRPC, + }, + }], + mesh: mesh, + accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), + }); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 5000, + Port: 80, + Protocol: appmesh.Protocol.GRPC, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, + }, + PortMapping: { + Port: 80, + Protocol: appmesh.Protocol.GRPC, + }, + }, + ], + Logging: { + AccessLog: { + File: { + Path: '/dev/stdout', + }, + }, + }, + }, + VirtualGatewayName: 'test-gateway', + }), + ); + test.done(); + }, + 'When adding a gateway route to existing VirtualGateway ': { + 'should create gateway route resource'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const virtualGateway = new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + mesh: mesh, + }); + + virtualGateway.addGatewayRoute('testGatewayRoute', { + gatewayRouteName: 'test-gateway-route', + }); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + }), + ); + test.done(); + }, + }, + 'When adding gateway routes to a VirtualGateway with existing gateway routes': { + 'should add gateway routes and not overwite'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const virtualGateway = mesh.addVirtualGateway('gateway'); + virtualGateway.addGatewayRoute('testGatewayRoute', { + gatewayRouteName: 'test-gateway-route', + }); + virtualGateway.addGatewayRoute('testGatewayRoute2', { + gatewayRouteName: 'test-gateway-route-2', + }); + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + }), + ); + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route-2', + }), + ); + test.done(); + }, + }, + + 'Can export and import VirtualGateway and perform actions'(test: Test) { + const app = new cdk.App(); + // GIVEN + const stack = new cdk.Stack(app, 'Imports', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + // WHEN + const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayName(stack, 'importedGateway', 'testMesh', 'test-gateway'); + // THEN + test.equal(virtualGateway.mesh.meshName, 'testMesh'); + test.equal(virtualGateway.virtualGatewayName, 'test-gateway'); + // Nothing to do with imported Virtual Gateways yet + const virtualGateway2 = appmesh.VirtualGateway.fromVirtualGatewayArn( + stack, 'importedGateway2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/testMesh/virtualGateway/test-gateway'); + test.equal(virtualGateway2.mesh.meshName, 'testMesh'); + test.equal(virtualGateway2.virtualGatewayName, 'test-gateway'); + test.done(); + }, }; \ No newline at end of file From 1849af97abc3c7ed4d6f3f7ebd790ab11d806b20 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Fri, 2 Oct 2020 12:32:39 -0700 Subject: [PATCH 04/34] Fixes a bug with default paths on health checks --- .../aws-appmesh/lib/virtual-gateway.ts | 4 +- .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 4 +- .../aws-appmesh/test/integ.mesh.expected.json | 31 +++++++++++++++ .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 14 ++++++- .../aws-appmesh/test/test.virtual-gateway.ts | 39 +++++++++++++++++++ 5 files changed, 89 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 1296a73d61fb8..1e2da836032bc 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -205,10 +205,12 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir throw new Error('The path property cannot be set with Protocol.GRPC'); } + const protocol = hc.protocol? hc.protocol : pm.protocol; + const healthCheck: CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty = { healthyThreshold: hc.healthyThreshold || 2, intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min - path: hc.path || (hc.protocol === Protocol.HTTP ? '/' : undefined), + path: hc.path || (protocol === Protocol.HTTP ? '/' : undefined), port: hc.port || pm.port, protocol: hc.protocol || pm.protocol, timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index 2ffb108432e2d..fb9ad5891ed22 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -166,10 +166,12 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir throw new Error('The path property cannot be set with Protocol.GRPC'); } + const protocol = hc.protocol? hc.protocol : pm.protocol; + const healthCheck: CfnVirtualNode.HealthCheckProperty = { healthyThreshold: hc.healthyThreshold || 2, intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min - path: hc.path || (hc.protocol === Protocol.HTTP ? '/' : undefined), + path: hc.path || (protocol === Protocol.HTTP ? '/' : undefined), port: hc.port || pm.port, protocol: hc.protocol || pm.protocol, timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index 6087c16472067..32d6cf57c2548 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -826,6 +826,37 @@ "Spec": {}, "VirtualServiceName": "service3.domain.local" } + }, + "gateway2BCE5C5E0": { + "Type": "AWS::AppMesh::VirtualGateway", + "Properties": { + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "Listeners": [ + { + "HealthCheck": { + "HealthyThreshold": 2, + "IntervalMillis": 10000, + "Path": "/", + "Port": 443, + "Protocol": "http", + "TimeoutMillis": 2000, + "UnhealthyThreshold": 2 + }, + "PortMapping": { + "Port": 443, + "Protocol": "http" + } + } + ] + }, + "VirtualGatewayName": "meshstackgateway2BEC62D7C" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 3e8316ccd000b..b2b3bbc4035db 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -119,6 +119,18 @@ router.addRoute('route-3', { mesh.addVirtualGateway('gateway1', { accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), - listeners: [{}], virtualGatewayName: 'gateway1', }); + +new appmesh.VirtualGateway(stack, 'gateway2', { + mesh: mesh, + listeners: [{ + healthCheck: { + interval: cdk.Duration.seconds(10), + }, + portMapping: { + port: 443, + protocol: appmesh.Protocol.HTTP, + }, + }], +}); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index f59052cf88a65..7628c38671109 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -19,6 +19,19 @@ export = { mesh: mesh, }); + new appmesh.VirtualGateway(stack, 'gateway2', { + mesh: mesh, + listeners: [{ + healthCheck: { + interval: cdk.Duration.seconds(10), + }, + portMapping: { + port: 443, + protocol: appmesh.Protocol.HTTP, + }, + }], + }); + // THEN expect(stack).to( haveResourceLike('AWS::AppMesh::VirtualGateway', { @@ -35,6 +48,32 @@ export = { VirtualGatewayName: 'testGateway', }), ); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 10000, + Port: 443, + Protocol: appmesh.Protocol.HTTP, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, + Path: '/', + }, + PortMapping: { + Port: 443, + Protocol: appmesh.Protocol.HTTP, + }, + }, + ], + }, + VirtualGatewayName: 'gateway2', + }), + ); test.done(); }, }, From 4e2679057730659fa5de9b7a949509cee2035391 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 13 Oct 2020 23:11:08 -0700 Subject: [PATCH 05/34] Implements Gateway Routes --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 205 +++++++++++++++++- .../aws-appmesh/lib/virtual-gateway.ts | 7 +- packages/@aws-cdk/aws-appmesh/package.json | 19 ++ .../aws-appmesh/test/test.gateway-route.ts | 7 + .../aws-appmesh/test/test.virtual-gateway.ts | 19 ++ 5 files changed, 248 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index cbdb960d6acaa..e9b216b1a0c69 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -1,7 +1,9 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; +import { Protocol } from './shared-interfaces'; import { IVirtualGateway } from './virtual-gateway'; +import { IVirtualService } from './virtual-service'; /** * Interface for which all Gateway Route based classes MUST implement @@ -22,16 +24,122 @@ export interface IGatewayRoute extends cdk.IResource { readonly gatewayRouteArn: string; } -/** - * Base interface properties for all Gateway Routes - */ -export interface GatewayRouteBaseProps { +interface GatewayRouteBaseProps { /** * The name of the Gateway Route * * @default - an automatically generated name */ readonly gatewayRouteName?: string; + + /** + * The VirtualService this Gateway Route directs traffic to + */ + readonly routeTarget: IVirtualService; +} + +/** + * Properties to define new Gateway Routes + */ +export interface GatewayRouteProps extends GatewayRouteBaseProps { + /** + * The Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGateway: IVirtualGateway; +} + +/** + * Base interface for HTTP Based Gateway Routes + */ +export interface GatewayHttpRouteBaseProps extends GatewayRouteBaseProps { + /** + * The criterion for determining a request match for this Gateway Route. + * + * @default - prefix match on "/" + */ + readonly match?: GatewayHttpRouteMatch; + /** + * HTTP Procol + */ + readonly routeType: Protocol.HTTP; +} + +/** + * Interface for HTTP Based Gateway Routes + */ +export interface GatewayHttpRouteProps extends GatewayHttpRouteBaseProps, GatewayRouteProps {} + +/** + * Base interface for HTTP2 Based Gateway Routes + */ +export interface GatewayHttp2RouteBaseProps extends GatewayRouteBaseProps { + /** + * The criterion for determining a request match for this Gateway Route. + * + * @default - prefix match on "/" + */ + readonly match?: GatewayHttp2RouteMatch; + /** + * HTTP2 Procol + */ + readonly routeType: Protocol.HTTP2; +} + +/** + * Interface for HTTP2 Based Gateway Routes + */ +export interface GatewayHttp2RouteProps extends GatewayHttp2RouteBaseProps, GatewayRouteProps {} + +/** + * Base interface for GRPC Based Gateway Routes + */ +export interface GatewayGrpcRouteBaseProps extends GatewayRouteBaseProps { + /** + * The criterion for determining a request match for this Gateway Route. + * + * @default - no default + */ + readonly match: GatewayGrpcRouteMatch; + /** + * GRPC Protocol + */ + readonly routeType: Protocol.GRPC; +} + +/** + * Interface for GRPC Based Gateway Routes + */ +export interface GatewayGrpcRouteProps extends GatewayGrpcRouteBaseProps, GatewayRouteProps {} + +interface GatewayHttpSharedRouteMatch { + /** + * Specifies the path to match requests with. + * This parameter must always start with /, which by itself matches all requests to the virtual service name. + * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local + * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. + * + */ + readonly prefixPath: string; +} + +/** + * The criterion for determining a request match for this Gateway Route + */ +export interface GatewayHttpRouteMatch extends GatewayHttpSharedRouteMatch {} + +/** + * The criterion for determining a request match for this Gateway Route + */ +export interface GatewayHttp2RouteMatch extends GatewayHttpSharedRouteMatch {} + +/** + * The criterion for determining a request match for this Gateway Route + */ +export interface GatewayGrpcRouteMatch { + /** + * The fully qualified domain name for the service to match from the request + */ + readonly serviceName: string; } /** @@ -80,17 +188,35 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { */ public readonly virtualGateway: IVirtualGateway; - constructor(scope: Construct, id: string, props: GatewayRouteProps) { + private readonly httpRoute?: CfnGatewayRoute.HttpGatewayRouteProperty; + private readonly http2Route?: CfnGatewayRoute.HttpGatewayRouteProperty; + private readonly grpcRoute?: CfnGatewayRoute.GrpcGatewayRouteProperty; + + constructor(scope: Construct, id: string, props: GatewayHttpRouteProps | GatewayHttp2RouteProps | GatewayGrpcRouteProps) { super(scope, id, { physicalName: props.gatewayRouteName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), }); this.virtualGateway = props.virtualGateway; + if (props.routeType === Protocol.HTTP) { + this.httpRoute = this.renderHttpRoute(props); + } + if (props.routeType === Protocol.HTTP2) { + this.http2Route = this.renderHttpRoute(props); + } + if (props.routeType === Protocol.GRPC) { + this.grpcRoute = this.renderGrpcRoute(props); + } + const gatewayRoute = new CfnGatewayRoute(this, 'Resource', { gatewayRouteName: this.physicalName, meshName: props.virtualGateway.mesh.meshName, - spec: {}, + spec: { + httpRoute: this.httpRoute, + http2Route: this.http2Route, + grpcRoute: this.grpcRoute, + }, virtualGatewayName: this.virtualGateway.virtualGatewayName, }); @@ -101,6 +227,73 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { resourceName: this.physicalName, }); } + + private renderHttpRoute(props: GatewayHttpRouteProps | GatewayHttp2RouteProps): CfnGatewayRoute.HttpGatewayRouteProperty { + const prefixPath = props.match ? props.match.prefixPath : '/'; + if (prefixPath[0] != '/') { + throw new Error('Prefix Path must start with \'/\''); + } + return { + action: { + target: { + virtualService: { + virtualServiceName: props.routeTarget.virtualServiceName, + }, + }, + }, + match: { + prefix: prefixPath, + }, + }; + } + + private renderGrpcRoute(props: GatewayGrpcRouteProps): CfnGatewayRoute.GrpcGatewayRouteProperty { + return { + action: { + target: { + virtualService: { + virtualServiceName: props.routeTarget.virtualServiceName, + }, + }, + }, + match: { + serviceName: props.match.serviceName, + }, + }; + } +} + +/** + * HTTP Gateway Route attached to a VirtualGateway and Mesh + * + * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html + */ +export class GatewayHttpRoute extends GatewayRoute { + constructor(scope: Construct, id: string, props: GatewayHttpRouteProps) { + super(scope, id, props); + } +} + +/** + * HTTP2 Gateway Route attached to a VirtualGateway and Mesh + * + * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html + */ +export class GatewayHttp2Route extends GatewayRoute { + constructor(scope: Construct, id: string, props: GatewayHttp2RouteProps) { + super(scope, id, props); + } +} + +/** + * GRPC Gateway Route attached to a VirtualGateway and Mesh + * + * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html + */ +export class GatewayGrpcRoute extends GatewayRoute { + constructor(scope: Construct, id: string, props: GatewayGrpcRouteProps) { + super(scope, id, props); + } } /** diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 1e2da836032bc..e25709eb44a07 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -1,7 +1,8 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; -import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; +import { GatewayRoute, GatewayGrpcRouteBaseProps, GatewayHttp2RouteBaseProps, GatewayHttpRouteBaseProps } from './gateway-route'; + import { IMesh, Mesh } from './mesh'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualGatewayListener } from './shared-interfaces'; import { validateHealthChecks } from './utils'; @@ -37,7 +38,7 @@ export interface IVirtualGateway extends cdk.IResource { /** * Utility method to add a new GatewayRoute to the VirtualGateway */ - addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; + addGatewayRoute(id: string, route: GatewayHttpRouteBaseProps | GatewayHttp2RouteBaseProps | GatewayGrpcRouteBaseProps): GatewayRoute; } /** @@ -114,7 +115,7 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa /** * Utility method to add a new GatewayRoute to the VirtualGateway */ - public addGatewayRoute(id: string, props: GatewayRouteBaseProps): GatewayRoute { + public addGatewayRoute(id: string, props: GatewayHttpRouteBaseProps | GatewayHttp2RouteBaseProps | GatewayGrpcRouteBaseProps): GatewayRoute { return new GatewayRoute(this, id, { ...props, virtualGateway: this, diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index dedc68ec9ec15..83b92307d8743 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -107,6 +107,7 @@ "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualNode", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualRouter", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualService", + "construct-ctor-props-type:@aws-cdk/aws-appmesh.GatewayRoute", "from-signature:@aws-cdk/aws-appmesh.GatewayRoute.fromGatewayRouteName", "from-signature:@aws-cdk/aws-appmesh.Route.fromRouteName", "from-signature:@aws-cdk/aws-appmesh.VirtualGateway.fromVirtualGatewayName", @@ -118,6 +119,24 @@ "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteResourceOwner", "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteUid", "resource-attribute:@aws-cdk/aws-appmesh.GatewayRoute.gatewayRouteVirtualGatewayName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteMeshName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteMeshOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteResourceOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayGrpcRoute.gatewayRouteVirtualGatewayName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteMeshName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteMeshOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteResourceOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttp2Route.gatewayRouteVirtualGatewayName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteMeshName", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteMeshOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteResourceOwner", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteUid", + "resource-attribute:@aws-cdk/aws-appmesh.GatewayHttpRoute.gatewayRouteVirtualGatewayName", "resource-attribute:@aws-cdk/aws-appmesh.IMesh.meshUid", "resource-attribute:@aws-cdk/aws-appmesh.IRoute.routeUid", "resource-attribute:@aws-cdk/aws-appmesh.IVirtualNode.virtualNodeUid", diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 24e6df8a0a417..e0b021ffaf2fe 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -20,8 +20,15 @@ export = { mesh: mesh, }); + const virtualService = new appmesh.VirtualService(stack, 'vs-1', { + mesh: mesh, + }); + + // Add an HTTP Route virtualGateway.addGatewayRoute('gateway-route', { gatewayRouteName: 'gateway-route', + routeType: appmesh.Protocol.HTTP, + routeTarget: virtualService, }); // THEN diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 7628c38671109..3f50d4c66aed9 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -147,8 +147,15 @@ export = { mesh: mesh, }); + const virtualService = mesh.addVirtualService('virtualService', {}); + virtualGateway.addGatewayRoute('testGatewayRoute', { gatewayRouteName: 'test-gateway-route', + routeTarget: virtualService, + routeType: appmesh.Protocol.GRPC, + match: { + serviceName: 'serviceName', + }, }); // THEN @@ -170,12 +177,24 @@ export = { meshName: 'test-mesh', }); + const virtualService = mesh.addVirtualService('virtualService', {}); + const virtualGateway = mesh.addVirtualGateway('gateway'); virtualGateway.addGatewayRoute('testGatewayRoute', { gatewayRouteName: 'test-gateway-route', + match: { + prefixPath: '/', + }, + routeTarget: virtualService, + routeType: appmesh.Protocol.HTTP, }); virtualGateway.addGatewayRoute('testGatewayRoute2', { gatewayRouteName: 'test-gateway-route-2', + match: { + serviceName: 'serviceName', + }, + routeTarget: virtualService, + routeType: appmesh.Protocol.GRPC, }); // THEN expect(stack).to( From 9c7dc48cb09ae3e1bc4012bf0704fb28df32c162 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 14 Oct 2020 15:31:41 -0700 Subject: [PATCH 06/34] Adds routeType to GatewayRouteBaseProps --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 30 ++++++++++++------- packages/@aws-cdk/aws-appmesh/package.json | 3 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index e9b216b1a0c69..8152a39fc38cf 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -36,16 +36,11 @@ interface GatewayRouteBaseProps { * The VirtualService this Gateway Route directs traffic to */ readonly routeTarget: IVirtualService; -} -/** - * Properties to define new Gateway Routes - */ -export interface GatewayRouteProps extends GatewayRouteBaseProps { /** - * The Virtual Gateway this Gateway Route is associated with + * What protocol the route uses */ - readonly virtualGateway: IVirtualGateway; + readonly routeType: Protocol.GRPC | Protocol.HTTP | Protocol.HTTP2; } /** @@ -67,7 +62,12 @@ export interface GatewayHttpRouteBaseProps extends GatewayRouteBaseProps { /** * Interface for HTTP Based Gateway Routes */ -export interface GatewayHttpRouteProps extends GatewayHttpRouteBaseProps, GatewayRouteProps {} +export interface GatewayHttpRouteProps extends GatewayHttpRouteBaseProps { + /** + * The Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGateway: IVirtualGateway; +} /** * Base interface for HTTP2 Based Gateway Routes @@ -88,7 +88,12 @@ export interface GatewayHttp2RouteBaseProps extends GatewayRouteBaseProps { /** * Interface for HTTP2 Based Gateway Routes */ -export interface GatewayHttp2RouteProps extends GatewayHttp2RouteBaseProps, GatewayRouteProps {} +export interface GatewayHttp2RouteProps extends GatewayHttp2RouteBaseProps { + /** + * The Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGateway: IVirtualGateway; +} /** * Base interface for GRPC Based Gateway Routes @@ -109,7 +114,12 @@ export interface GatewayGrpcRouteBaseProps extends GatewayRouteBaseProps { /** * Interface for GRPC Based Gateway Routes */ -export interface GatewayGrpcRouteProps extends GatewayGrpcRouteBaseProps, GatewayRouteProps {} +export interface GatewayGrpcRouteProps extends GatewayGrpcRouteBaseProps { + /** + * The Virtual Gateway this Gateway Route is associated with + */ + readonly virtualGateway: IVirtualGateway; +} interface GatewayHttpSharedRouteMatch { /** diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index 83b92307d8743..f6c46c60c29af 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -174,7 +174,8 @@ "props-default-doc:@aws-cdk/aws-appmesh.VirtualRouterAttributes.virtualRouterName", "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP", "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP2", - "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.GRPC" + "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.GRPC", + "no-unused-type:@aws-cdk/aws-appmesh.GatewayRouteProps" ] }, "stability": "experimental", From 0d47976ade1f7581a29a31bd95ff69241991b92e Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Thu, 15 Oct 2020 15:33:58 -0700 Subject: [PATCH 07/34] Update packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts Co-authored-by: Adam Ruka --- packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index fb9ad5891ed22..508c5520544b6 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -166,7 +166,7 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir throw new Error('The path property cannot be set with Protocol.GRPC'); } - const protocol = hc.protocol? hc.protocol : pm.protocol; + const protocol = hc.protocol ?? pm.protocol; const healthCheck: CfnVirtualNode.HealthCheckProperty = { healthyThreshold: hc.healthyThreshold || 2, From 463f507f621065d91d6695dd97a8801ba808ad5f Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Fri, 16 Oct 2020 12:51:15 -0700 Subject: [PATCH 08/34] Reworks Gateway Routes to use bind classes for protocol spec variants --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 344 +++++++++--------- .../aws-appmesh/lib/{ => private}/utils.ts | 2 +- .../aws-appmesh/lib/virtual-gateway.ts | 6 +- .../aws-appmesh/test/test.gateway-route.ts | 5 +- .../aws-appmesh/test/test.virtual-gateway.ts | 24 +- 5 files changed, 192 insertions(+), 189 deletions(-) rename packages/@aws-cdk/aws-appmesh/lib/{ => private}/utils.ts (95%) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 8152a39fc38cf..642934d5eee20 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -6,177 +6,265 @@ import { IVirtualGateway } from './virtual-gateway'; import { IVirtualService } from './virtual-service'; /** - * Interface for which all Gateway Route based classes MUST implement + * Interface for which all GatewayRoute based classes MUST implement */ export interface IGatewayRoute extends cdk.IResource { /** - * The name of the Gateway Route + * The name of the GatewayRoute * * @attribute */ readonly gatewayRouteName: string, /** - * The Amazon Resource Name (ARN) for the Gateway Route + * The Amazon Resource Name (ARN) for the GatewayRoute * * @attribute */ readonly gatewayRouteArn: string; } -interface GatewayRouteBaseProps { +/** + * Basic configuration properties for a GatewayRoute + */ +export interface GatewayRouteBaseProps { /** - * The name of the Gateway Route + * The name of the GatewayRoute * * @default - an automatically generated name */ readonly gatewayRouteName?: string; - /** - * The VirtualService this Gateway Route directs traffic to - */ - readonly routeTarget: IVirtualService; - /** * What protocol the route uses */ - readonly routeType: Protocol.GRPC | Protocol.HTTP | Protocol.HTTP2; + readonly routeSpec: GatewayRouteSpec; } /** - * Base interface for HTTP Based Gateway Routes + * The criterion for determining a request match for this GatewayRoute */ -export interface GatewayHttpRouteBaseProps extends GatewayRouteBaseProps { +export interface HttpGatewayRouteMatch { /** - * The criterion for determining a request match for this Gateway Route. + * Specifies the path to match requests with. + * This parameter must always start with /, which by itself matches all requests to the virtual service name. + * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local + * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. * - * @default - prefix match on "/" */ - readonly match?: GatewayHttpRouteMatch; + readonly prefixPath: string; +} + +/** + * The criterion for determining a request match for this GatewayRoute + */ +export interface GrpcGatewayRouteMatch { /** - * HTTP Procol + * The fully qualified domain name for the service to match from the request */ - readonly routeType: Protocol.HTTP; + readonly serviceName: string; } /** - * Interface for HTTP Based Gateway Routes + * Properties to define a new GatewayRoute */ -export interface GatewayHttpRouteProps extends GatewayHttpRouteBaseProps { +export interface GatewayRouteProps extends GatewayRouteBaseProps { /** - * The Virtual Gateway this Gateway Route is associated with + * The VirtualGateway this GatewayRoute is associated with */ readonly virtualGateway: IVirtualGateway; } /** - * Base interface for HTTP2 Based Gateway Routes + * Properties specific for HTTP Based GatewayRoutes */ -export interface GatewayHttp2RouteBaseProps extends GatewayRouteBaseProps { +export interface HttpRouteSpecProps { /** - * The criterion for determining a request match for this Gateway Route. + * The criterion for determining a request match for this GatewayRoute * - * @default - prefix match on "/" + * @default - matches on '/' */ - readonly match?: GatewayHttp2RouteMatch; + readonly match?: HttpGatewayRouteMatch; /** - * HTTP2 Procol + * The VirtualService this GatewayRoute directs traffic to */ - readonly routeType: Protocol.HTTP2; + readonly routeTarget: IVirtualService; } /** - * Interface for HTTP2 Based Gateway Routes + * Properties specific for a GRPC GatewayRoute */ -export interface GatewayHttp2RouteProps extends GatewayHttp2RouteBaseProps { +export interface GrpcRouteSpecProps { /** - * The Virtual Gateway this Gateway Route is associated with + * The criterion for determining a request match for this GatewayRoute */ - readonly virtualGateway: IVirtualGateway; + readonly match: GrpcGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; } /** - * Base interface for GRPC Based Gateway Routes + * All Properties for GatewayRoute Specs */ -export interface GatewayGrpcRouteBaseProps extends GatewayRouteBaseProps { +export interface GatewayRouteSpecConfig { /** - * The criterion for determining a request match for this Gateway Route. + * The spec for an http gateway route * - * @default - no default + * @default - no http spec */ - readonly match: GatewayGrpcRouteMatch; + readonly httpSpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; /** - * GRPC Protocol + * The spec for an http2 gateway route + * + * @default - no http2 spec */ - readonly routeType: Protocol.GRPC; + readonly http2SpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; + /** + * The spec for a grpc gateway route + * + * @default - no grpc spec + */ + readonly grpcSpecConfig?: CfnGatewayRoute.GrpcGatewayRouteProperty; } /** - * Interface for GRPC Based Gateway Routes + * Used to generate specs with different protocols for a GatewayRoute */ -export interface GatewayGrpcRouteProps extends GatewayGrpcRouteBaseProps { +export abstract class GatewayRouteSpec { /** - * The Virtual Gateway this Gateway Route is associated with + * Creates an HTTP Based GatewayRoute + * + * @param props - no http gateway route */ - readonly virtualGateway: IVirtualGateway; -} + public static httpRouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { + return new HttpGatewayRouteSpec(props, Protocol.HTTP); + } -interface GatewayHttpSharedRouteMatch { /** - * Specifies the path to match requests with. - * This parameter must always start with /, which by itself matches all requests to the virtual service name. - * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local - * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. + * Creates an HTTP2 Based GatewayRoute * + * @param props - no http2 gateway route */ - readonly prefixPath: string; -} + public static http2RouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { + return new HttpGatewayRouteSpec(props, Protocol.HTTP2); + } -/** - * The criterion for determining a request match for this Gateway Route - */ -export interface GatewayHttpRouteMatch extends GatewayHttpSharedRouteMatch {} + /** + * Creates an GRPC Based GatewayRoute + * + * @param props - no grpc gateway route + */ + public static grpcRouteSpec(props: GrpcRouteSpecProps): GatewayRouteSpec { + return new GrpcGatewayRouteSpec(props); + } -/** - * The criterion for determining a request match for this Gateway Route - */ -export interface GatewayHttp2RouteMatch extends GatewayHttpSharedRouteMatch {} + /** + * Called when the GatewayRouteSpec type is initialized. Can be used to enforce + * mutual exclusivity with future properties + */ + public abstract bind(scope: cdk.Construct): GatewayRouteSpecConfig; +} -/** - * The criterion for determining a request match for this Gateway Route - */ -export interface GatewayGrpcRouteMatch { +class HttpGatewayRouteSpec extends GatewayRouteSpec { /** - * The fully qualified domain name for the service to match from the request + * The criterion for determining a request match for this GatewayRoute. + * + * @default - matches on '/' */ - readonly serviceName: string; + readonly match?: HttpGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; + + /** + * Type of route you are creating + */ + readonly routeType: Protocol; + + constructor(props: HttpRouteSpecProps, protocol: Protocol.HTTP | Protocol.HTTP2) { + super(); + this.routeTarget = props.routeTarget; + this.routeType = protocol; + this.match = props.match; + } + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { + const prefixPath = this.match ? this.match.prefixPath : '/'; + if (prefixPath[0] != '/') { + throw new Error('Prefix Path must start with \'/\''); + } + const httpConfig: CfnGatewayRoute.HttpGatewayRouteProperty = { + match: { + prefix: prefixPath, + }, + action: { + target: { + virtualService: { + virtualServiceName: this.routeTarget.virtualServiceName, + }, + }, + }, + }; + return { + httpSpecConfig: this.routeType === Protocol.HTTP ? httpConfig : undefined, + http2SpecConfig: this.routeType === Protocol.HTTP2 ? httpConfig : undefined, + }; + } } -/** - * Properties to define new Gateway Routes - */ -export interface GatewayRouteProps extends GatewayRouteBaseProps { +class GrpcGatewayRouteSpec extends GatewayRouteSpec { /** - * The Virtual Gateway this Gateway Route is associated with + * The criterion for determining a request match for this GatewayRoute. + * + * @default - no default */ - readonly virtualGateway: IVirtualGateway; + readonly match: GrpcGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; + + constructor(props: GrpcRouteSpecProps) { + super(); + this.match = props.match; + this.routeTarget = props.routeTarget; + } + + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { + return { + grpcSpecConfig: { + action: { + target: { + virtualService: { + virtualServiceName: this.routeTarget.virtualServiceName, + }, + }, + }, + match: { + serviceName: this.match.serviceName, + }, + }, + }; + } } /** - * Gateway Route represents a new or existing gateway route attached to a VirtualGateway and Mesh + * GatewayRoute represents a new or existing gateway route attached to a VirtualGateway and Mesh * * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html */ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** - * Import an existing Gateway Route given an ARN + * Import an existing GatewayRoute given an ARN */ public static fromGatewayRouteArn(scope: Construct, id: string, gatewayRouteArn: string): IGatewayRoute { return new ImportedGatewayRoute(scope, id, { gatewayRouteArn }); } /** - * Import an existing Gateway Route given its name + * Import an existing GatewayRoute given its name */ public static fromGatewayRouteName( scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { @@ -184,12 +272,12 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { } /** - * The name of the Gateway Route + * The name of the GatewayRoute */ public readonly gatewayRouteName: string; /** - * The Amazon Resource Name (ARN) for the Gateway Route + * The Amazon Resource Name (ARN) for the GatewayRoute */ public readonly gatewayRouteArn: string; @@ -198,34 +286,21 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { */ public readonly virtualGateway: IVirtualGateway; - private readonly httpRoute?: CfnGatewayRoute.HttpGatewayRouteProperty; - private readonly http2Route?: CfnGatewayRoute.HttpGatewayRouteProperty; - private readonly grpcRoute?: CfnGatewayRoute.GrpcGatewayRouteProperty; - - constructor(scope: Construct, id: string, props: GatewayHttpRouteProps | GatewayHttp2RouteProps | GatewayGrpcRouteProps) { + constructor(scope: Construct, id: string, props: GatewayRouteProps) { super(scope, id, { physicalName: props.gatewayRouteName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), }); this.virtualGateway = props.virtualGateway; - - if (props.routeType === Protocol.HTTP) { - this.httpRoute = this.renderHttpRoute(props); - } - if (props.routeType === Protocol.HTTP2) { - this.http2Route = this.renderHttpRoute(props); - } - if (props.routeType === Protocol.GRPC) { - this.grpcRoute = this.renderGrpcRoute(props); - } + const routeSpec = props.routeSpec.bind(this); const gatewayRoute = new CfnGatewayRoute(this, 'Resource', { gatewayRouteName: this.physicalName, meshName: props.virtualGateway.mesh.meshName, spec: { - httpRoute: this.httpRoute, - http2Route: this.http2Route, - grpcRoute: this.grpcRoute, + httpRoute: routeSpec.httpSpecConfig, + http2Route: routeSpec.http2SpecConfig, + grpcRoute: routeSpec.grpcSpecConfig, }, virtualGatewayName: this.virtualGateway.virtualGatewayName, }); @@ -237,96 +312,29 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { resourceName: this.physicalName, }); } - - private renderHttpRoute(props: GatewayHttpRouteProps | GatewayHttp2RouteProps): CfnGatewayRoute.HttpGatewayRouteProperty { - const prefixPath = props.match ? props.match.prefixPath : '/'; - if (prefixPath[0] != '/') { - throw new Error('Prefix Path must start with \'/\''); - } - return { - action: { - target: { - virtualService: { - virtualServiceName: props.routeTarget.virtualServiceName, - }, - }, - }, - match: { - prefix: prefixPath, - }, - }; - } - - private renderGrpcRoute(props: GatewayGrpcRouteProps): CfnGatewayRoute.GrpcGatewayRouteProperty { - return { - action: { - target: { - virtualService: { - virtualServiceName: props.routeTarget.virtualServiceName, - }, - }, - }, - match: { - serviceName: props.match.serviceName, - }, - }; - } -} - -/** - * HTTP Gateway Route attached to a VirtualGateway and Mesh - * - * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html - */ -export class GatewayHttpRoute extends GatewayRoute { - constructor(scope: Construct, id: string, props: GatewayHttpRouteProps) { - super(scope, id, props); - } -} - -/** - * HTTP2 Gateway Route attached to a VirtualGateway and Mesh - * - * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html - */ -export class GatewayHttp2Route extends GatewayRoute { - constructor(scope: Construct, id: string, props: GatewayHttp2RouteProps) { - super(scope, id, props); - } -} - -/** - * GRPC Gateway Route attached to a VirtualGateway and Mesh - * - * @see https://docs.aws.amazon.com/app-mesh/latest/userguide/gateway-routes.html - */ -export class GatewayGrpcRoute extends GatewayRoute { - constructor(scope: Construct, id: string, props: GatewayGrpcRouteProps) { - super(scope, id, props); - } } /** - * Interface with properties necessary to import a reusable Gateway Route + * Interface with properties necessary to import a reusable GatewayRoute */ interface GatewayRouteAttributes { /** - * The name of the Gateway Route + * The name of the GatewayRoute */ readonly gatewayRouteName?: string; /** - * The Amazon Resource Name (ARN) for the Gateway Route + * The Amazon Resource Name (ARN) for the GatewayRoute */ readonly gatewayRouteArn?: string; /** - * The name of the mesh this Gateway Route is associated with + * The name of the mesh this GatewayRoute is associated with */ readonly meshName?: string; /** - * The name of the Virtual Gateway this Gateway Route is associated with + * The name of the Virtual Gateway this GatewayRoute is associated with */ readonly virtualGatewayName?: string; } @@ -336,12 +344,12 @@ interface GatewayRouteAttributes { */ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { /** - * The name of the Gateway Route + * The name of the GatewayRoute */ public gatewayRouteName: string; /** - * The Amazon Resource Name (ARN) for the Gateway Route + * The Amazon Resource Name (ARN) for the GatewayRoute */ public gatewayRouteArn: string; diff --git a/packages/@aws-cdk/aws-appmesh/lib/utils.ts b/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts similarity index 95% rename from packages/@aws-cdk/aws-appmesh/lib/utils.ts rename to packages/@aws-cdk/aws-appmesh/lib/private/utils.ts index 7e07348ed79f0..0f63fdaf05253 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/utils.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts @@ -1,5 +1,5 @@ import * as cdk from '@aws-cdk/core'; -import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated'; +import { CfnVirtualGateway, CfnVirtualNode } from '../appmesh.generated'; type AppMeshHealthCheck = CfnVirtualNode.HealthCheckProperty | CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index e25709eb44a07..ef16e466658dc 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -1,7 +1,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; -import { GatewayRoute, GatewayGrpcRouteBaseProps, GatewayHttp2RouteBaseProps, GatewayHttpRouteBaseProps } from './gateway-route'; +import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualGatewayListener } from './shared-interfaces'; @@ -38,7 +38,7 @@ export interface IVirtualGateway extends cdk.IResource { /** * Utility method to add a new GatewayRoute to the VirtualGateway */ - addGatewayRoute(id: string, route: GatewayHttpRouteBaseProps | GatewayHttp2RouteBaseProps | GatewayGrpcRouteBaseProps): GatewayRoute; + addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; } /** @@ -115,7 +115,7 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa /** * Utility method to add a new GatewayRoute to the VirtualGateway */ - public addGatewayRoute(id: string, props: GatewayHttpRouteBaseProps | GatewayHttp2RouteBaseProps | GatewayGrpcRouteBaseProps): GatewayRoute { + public addGatewayRoute(id: string, props: GatewayRouteBaseProps): GatewayRoute { return new GatewayRoute(this, id, { ...props, virtualGateway: this, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index e0b021ffaf2fe..63632fc2f06a7 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -26,9 +26,10 @@ export = { // Add an HTTP Route virtualGateway.addGatewayRoute('gateway-route', { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), gatewayRouteName: 'gateway-route', - routeType: appmesh.Protocol.HTTP, - routeTarget: virtualService, }); // THEN diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 3f50d4c66aed9..0882b029b48e5 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -151,11 +151,9 @@ export = { virtualGateway.addGatewayRoute('testGatewayRoute', { gatewayRouteName: 'test-gateway-route', - routeTarget: virtualService, - routeType: appmesh.Protocol.GRPC, - match: { - serviceName: 'serviceName', - }, + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), }); // THEN @@ -182,19 +180,15 @@ export = { const virtualGateway = mesh.addVirtualGateway('gateway'); virtualGateway.addGatewayRoute('testGatewayRoute', { gatewayRouteName: 'test-gateway-route', - match: { - prefixPath: '/', - }, - routeTarget: virtualService, - routeType: appmesh.Protocol.HTTP, + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), }); virtualGateway.addGatewayRoute('testGatewayRoute2', { gatewayRouteName: 'test-gateway-route-2', - match: { - serviceName: 'serviceName', - }, - routeTarget: virtualService, - routeType: appmesh.Protocol.GRPC, + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), }); // THEN expect(stack).to( From d2c59bb87b59eb07ff9a450d4e05840b18eb49d5 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Fri, 16 Oct 2020 14:06:09 -0700 Subject: [PATCH 09/34] Adds addListener to VGW --- .../aws-appmesh/lib/virtual-gateway.ts | 22 ++- .../aws-appmesh/test/test.virtual-gateway.ts | 128 +++++++++++------- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index ef16e466658dc..7dce65902ee77 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -100,16 +100,26 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa * Utility method to add a list of listeners to this VirtualGateway */ public addListeners(listeners: VirtualGatewayListener[]) { - if (this.listeners.length + this.listeners.length > 1) { + if (listeners.length + this.listeners.length > 1) { throw new Error('VirtualGateway may have at most one listener'); } for (const listener of listeners) { - const portMapping = listener.portMapping || { port: 8080, protocol: Protocol.HTTP }; - this.listeners.push({ - portMapping, - healthCheck: renderHealthCheck(listener.healthCheck, portMapping), - }); + this.addListener(listener); + } + } + + /** + * Utility method to add a single listener to this VirtualGateway + */ + public addListener(listener: VirtualGatewayListener) { + if (this.listeners.length > 0) { + throw new Error('VirtualGateway may have at most one listener'); } + const portMapping = listener.portMapping || { port: 8080, protocol: Protocol.HTTP }; + this.listeners.push({ + portMapping, + healthCheck: renderHealthCheck(listener.healthCheck, portMapping), + }); } /** diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 0882b029b48e5..782758db8c7dc 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -76,61 +76,93 @@ export = { ); test.done(); }, - }, - 'should have expected features'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); + 'should have expected features'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); - // WHEN - const mesh = new appmesh.Mesh(stack, 'mesh', { - meshName: 'test-mesh', - }); + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); - new appmesh.VirtualGateway(stack, 'testGateway', { - virtualGatewayName: 'test-gateway', - listeners: [{ - healthCheck: {}, - portMapping: { - port: 80, - protocol: appmesh.Protocol.GRPC, - }, - }], - mesh: mesh, - accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), - }); + new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + listeners: [{ + healthCheck: {}, + portMapping: { + port: 80, + protocol: appmesh.Protocol.GRPC, + }, + }], + mesh: mesh, + accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), + }); - // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::VirtualGateway', { - Spec: { - Listeners: [ - { - HealthCheck: { - HealthyThreshold: 2, - IntervalMillis: 5000, - Port: 80, - Protocol: appmesh.Protocol.GRPC, - TimeoutMillis: 2000, - UnhealthyThreshold: 2, - }, - PortMapping: { - Port: 80, - Protocol: appmesh.Protocol.GRPC, + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 5000, + Port: 80, + Protocol: appmesh.Protocol.GRPC, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, + }, + PortMapping: { + Port: 80, + Protocol: appmesh.Protocol.GRPC, + }, }, - }, - ], - Logging: { - AccessLog: { - File: { - Path: '/dev/stdout', + ], + Logging: { + AccessLog: { + File: { + Path: '/dev/stdout', + }, }, }, }, - }, - VirtualGatewayName: 'test-gateway', - }), - ); - test.done(); + VirtualGatewayName: 'test-gateway', + }), + ); + test.done(); + }, + }, + 'When adding listeners to a VirtualGateway ': { + 'should throw an exception if you attempt to add more than 1 listener'(test: Test) { + + // GIVEN + const stack = new cdk.Stack(); + + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const virtualGateway = new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + mesh: mesh, + }); + test.throws(() => { + virtualGateway.addListener({ + portMapping: { + port: 8080, + protocol: appmesh.Protocol.HTTP, + }, + }); + }, /VirtualGateway may have at most one listener/); + test.throws(() => { + virtualGateway.addListeners([{ + portMapping: { + port: 8080, + protocol: appmesh.Protocol.HTTP, + }, + }]); + }, /VirtualGateway may have at most one listener/); + test.done(); + }, }, 'When adding a gateway route to existing VirtualGateway ': { 'should create gateway route resource'(test: Test) { From 4976d8f42832f3de1654e4ca5b3b01250fdcf038 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Fri, 16 Oct 2020 16:04:16 -0700 Subject: [PATCH 10/34] Adds gateway listener protocol variant classes --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 11 +- .../aws-appmesh/lib/shared-interfaces.ts | 5 - .../aws-appmesh/lib/virtual-gateway.ts | 172 +++++++++++++++++- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 9 +- .../aws-appmesh/test/test.gateway-route.ts | 2 +- .../aws-appmesh/test/test.virtual-gateway.ts | 33 +--- 6 files changed, 183 insertions(+), 49 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 642934d5eee20..f7a1fb59eb523 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -2,7 +2,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; import { Protocol } from './shared-interfaces'; -import { IVirtualGateway } from './virtual-gateway'; +import { IVirtualGateway, VirtualGateway } from './virtual-gateway'; import { IVirtualService } from './virtual-service'; /** @@ -268,7 +268,8 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { */ public static fromGatewayRouteName( scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { - return new ImportedGatewayRoute(scope, id, { meshName, virtualGatewayName, gatewayRouteName }); + const virtualGateway = VirtualGateway.fromVirtualGatewayName(scope, 'VirtualGateway', meshName, virtualGatewayName); + return new ImportedGatewayRoute(scope, id, { meshName, virtualGateway, gatewayRouteName }); } /** @@ -336,7 +337,7 @@ interface GatewayRouteAttributes { /** * The name of the Virtual Gateway this GatewayRoute is associated with */ - readonly virtualGatewayName?: string; + readonly virtualGateway?: IVirtualGateway; } /** @@ -358,11 +359,11 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { if (props.gatewayRouteArn) { this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - } else if (props.gatewayRouteName && props.meshName && props.virtualGatewayName) { + } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { this.gatewayRouteName = props.gatewayRouteName; this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGatewayName}/gatewayRoute`, + resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, resourceName: this.gatewayRouteName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 2a193e26b3de8..7e10e6abf3cd4 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -105,11 +105,6 @@ export interface ListenerBase { */ export interface VirtualNodeListener extends ListenerBase {} -/** - * Represents the properties needed to define listeners for Virtual Gateways - */ -export interface VirtualGatewayListener extends ListenerBase {} - /** * All Properties for Envoy Access logs for mesh endpoints */ diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 7dce65902ee77..2069d92fb50bb 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -4,7 +4,7 @@ import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; -import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualGatewayListener } from './shared-interfaces'; +import { AccessLog, HealthCheck, PortMapping, Protocol } from './shared-interfaces'; import { validateHealthChecks } from './utils'; /** @@ -41,6 +41,168 @@ export interface IVirtualGateway extends cdk.IResource { addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; } +/** + * Represents the properties needed to define HTTP Listeners for a VirtualGateway + */ +export interface HttpGatewayListenerProps { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port?: number + + /** + * The health check information for the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; +} + +/** + * Represents the properties needed to define GRPC Listeners for a VirtualGateway + */ +export interface GrpcGatewayListenerProps { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port?: number + + /** + * The health check information for the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; +} + +/** + * Represents the properties needed to define listeners for Virtual Gateways + */ +export abstract class VirtualGatewayListener { + public static httpGatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { + return new HttpGatewayListener(props); + } + + public static http2GatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { + return new Http2GatewayListener(props); + } + + public static grpcGatewayListener(props?: GrpcGatewayListenerProps) { + return new GrpcGatewayListener(props); + } + + public abstract bind(scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty; +} + +export class HttpGatewayListener extends VirtualGatewayListener { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; + constructor(props?: HttpGatewayListenerProps) { + super(); + const checkedProps = props ?? {}; + this.port = checkedProps.port ? checkedProps.port : 8080; + this.healthCheck = checkedProps.healthCheck; + } + + public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + return { + portMapping: { + port: this.port, + protocol: Protocol.HTTP, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.HTTP, + }), + }; + } +} + +export class Http2GatewayListener extends VirtualGatewayListener { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; + constructor(props?: HttpGatewayListenerProps) { + super(); + const checkedProps = props ?? {}; + this.port = checkedProps.port ? checkedProps.port : 8080; + this.healthCheck = checkedProps.healthCheck; + } + + public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + return { + portMapping: { + port: this.port, + protocol: Protocol.HTTP2, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.HTTP2, + }), + }; + } +} + +export class GrpcGatewayListener extends VirtualGatewayListener { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; + constructor(props?: HttpGatewayListenerProps) { + super(); + const checkedProps = props ?? {}; + this.port = checkedProps.port ? checkedProps.port : 8080; + this.healthCheck = checkedProps.healthCheck; + } + + public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + return { + portMapping: { + port: this.port, + protocol: Protocol.GRPC, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.GRPC, + }), + }; + } +} + /** * Basic configuration properties for a VirtualGateway */ @@ -115,11 +277,7 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa if (this.listeners.length > 0) { throw new Error('VirtualGateway may have at most one listener'); } - const portMapping = listener.portMapping || { port: 8080, protocol: Protocol.HTTP }; - this.listeners.push({ - portMapping, - healthCheck: renderHealthCheck(listener.healthCheck, portMapping), - }); + this.listeners.push(listener.bind(this)); } /** @@ -182,7 +340,7 @@ export class VirtualGateway extends VirtualGatewayBase { this.mesh = props.mesh; // Use listener default of http listener port 8080 if no listener is defined - this.addListeners(props.listeners ? props.listeners : [{}]); + this.addListeners(props.listeners ? props.listeners : [VirtualGatewayListener.httpGatewayListener()]); const accessLogging = props.accessLog?.bind(this); const node = new CfnVirtualGateway(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index b2b3bbc4035db..89445ea748be7 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -124,13 +124,10 @@ mesh.addVirtualGateway('gateway1', { new appmesh.VirtualGateway(stack, 'gateway2', { mesh: mesh, - listeners: [{ + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ + port: 443, healthCheck: { interval: cdk.Duration.seconds(10), }, - portMapping: { - port: 443, - protocol: appmesh.Protocol.HTTP, - }, - }], + })], }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 63632fc2f06a7..8d0511bf5bf74 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -16,7 +16,7 @@ export = { }); const virtualGateway = new appmesh.VirtualGateway(stack, 'gateway-1', { - listeners: [{}], + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener()], mesh: mesh, }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 782758db8c7dc..0b4ec33fd5751 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -21,15 +21,12 @@ export = { new appmesh.VirtualGateway(stack, 'gateway2', { mesh: mesh, - listeners: [{ + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ + port: 443, healthCheck: { interval: cdk.Duration.seconds(10), }, - portMapping: { - port: 443, - protocol: appmesh.Protocol.HTTP, - }, - }], + })], }); // THEN @@ -87,13 +84,9 @@ export = { new appmesh.VirtualGateway(stack, 'testGateway', { virtualGatewayName: 'test-gateway', - listeners: [{ - healthCheck: {}, - portMapping: { - port: 80, - protocol: appmesh.Protocol.GRPC, - }, - }], + listeners: [appmesh.VirtualGatewayListener.grpcGatewayListener({ + port: 80, + })], mesh: mesh, accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), }); @@ -146,20 +139,10 @@ export = { mesh: mesh, }); test.throws(() => { - virtualGateway.addListener({ - portMapping: { - port: 8080, - protocol: appmesh.Protocol.HTTP, - }, - }); + virtualGateway.addListener(appmesh.VirtualGatewayListener.httpGatewayListener()); }, /VirtualGateway may have at most one listener/); test.throws(() => { - virtualGateway.addListeners([{ - portMapping: { - port: 8080, - protocol: appmesh.Protocol.HTTP, - }, - }]); + virtualGateway.addListeners([appmesh.VirtualGatewayListener.httpGatewayListener()]); }, /VirtualGateway may have at most one listener/); test.done(); }, From ce2f32586cc0476802bf390be5db7e4aad19490e Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Fri, 16 Oct 2020 16:08:50 -0700 Subject: [PATCH 11/34] Adds addListener method to IVirtualGateway --- packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 2069d92fb50bb..3337da2dfb60f 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -35,6 +35,11 @@ export interface IVirtualGateway extends cdk.IResource { */ addListeners(listeners: VirtualGatewayListener[]): void; + /** + * Utility method to add a single listener to this VirtualGateway + */ + addListener(listener: VirtualGatewayListener): void; + /** * Utility method to add a new GatewayRoute to the VirtualGateway */ From 2939e5ceea615b4a3954addc86215c4232d800f4 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 20 Oct 2020 20:52:03 -0700 Subject: [PATCH 12/34] Fixes some build issues --- .../aws-appmesh/lib/virtual-gateway.ts | 38 ++++++++++++++++++- .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 2 +- .../aws-appmesh/test/test.virtual-gateway.ts | 2 + 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 3337da2dfb60f..6cca579a9ff49 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -4,8 +4,8 @@ import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; +import { validateHealthChecks } from './private/utils'; import { AccessLog, HealthCheck, PortMapping, Protocol } from './shared-interfaces'; -import { validateHealthChecks } from './utils'; /** * Interface which all Virtual Gateway based classes must implement @@ -85,24 +85,40 @@ export interface GrpcGatewayListenerProps { } /** - * Represents the properties needed to define listeners for Virtual Gateways + * Represents the properties needed to define listeners for a VirtualGateway */ export abstract class VirtualGatewayListener { + /** + * Returns an HTTP Listener for a VirtualGateway + */ public static httpGatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { return new HttpGatewayListener(props); } + /** + * Returns an HTTP2 Listener for a VirtualGateway + */ public static http2GatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { return new Http2GatewayListener(props); } + /** + * Returns a GRPC Listener for a VirtualGateway + */ public static grpcGatewayListener(props?: GrpcGatewayListenerProps) { return new GrpcGatewayListener(props); } + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ public abstract bind(scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty; } +/** + * Represents the properties needed to define an HTTP Listener for a VirtualGateway + */ export class HttpGatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on @@ -124,6 +140,10 @@ export class HttpGatewayListener extends VirtualGatewayListener { this.healthCheck = checkedProps.healthCheck; } + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { return { portMapping: { @@ -138,6 +158,9 @@ export class HttpGatewayListener extends VirtualGatewayListener { } } +/** +* Represents the properties needed to define an HTTP2 Listener for a VirtualGateway +*/ export class Http2GatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on @@ -159,6 +182,10 @@ export class Http2GatewayListener extends VirtualGatewayListener { this.healthCheck = checkedProps.healthCheck; } + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { return { portMapping: { @@ -173,6 +200,9 @@ export class Http2GatewayListener extends VirtualGatewayListener { } } +/** +* Represents the properties needed to define a GRPC Listener for Virtual Gateway +*/ export class GrpcGatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on @@ -194,6 +224,10 @@ export class GrpcGatewayListener extends VirtualGatewayListener { this.healthCheck = checkedProps.healthCheck; } + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { return { portMapping: { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index 508c5520544b6..bfad388fa91ee 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -3,8 +3,8 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; import { IMesh } from './mesh'; +import { validateHealthChecks } from './private/utils'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualNodeListener } from './shared-interfaces'; -import { validateHealthChecks } from './utils'; import { IVirtualService } from './virtual-service'; /** diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 0b4ec33fd5751..87fd440c6c2c6 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -86,6 +86,8 @@ export = { virtualGatewayName: 'test-gateway', listeners: [appmesh.VirtualGatewayListener.grpcGatewayListener({ port: 80, + healthCheck: { + }, })], mesh: mesh, accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), From a242bfdfb151b3cb29b75a5f98a5dc6bc32be8b0 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 20 Oct 2020 21:51:38 -0700 Subject: [PATCH 13/34] adds utility method to add multiple gateway routes to a VirtualGateway --- .../aws-appmesh/lib/virtual-gateway.ts | 21 ++++ .../aws-appmesh/test/test.virtual-gateway.ts | 101 ++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 6cca579a9ff49..31a635b1f6d35 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -278,6 +278,20 @@ export interface VirtualGatewayProps extends VirtualGatewayBaseProps { readonly mesh: IMesh; } +/** + * Properties required to add multiple GatewayRoutes to a VirtualGateway + */ +export interface AddGatewayRouteProps { + /** + * CloudFormation Logical ID + */ + readonly id: string; + /** + * Properties to create a GatewayRoute + */ + readonly props: GatewayRouteBaseProps; +} + abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGateway { /** * Name of the VirtualGateway @@ -319,6 +333,13 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa this.listeners.push(listener.bind(this)); } + /** + * Utility method to add a list of GatewayRoutes to the VirtualGateway + */ + public addGatewayRoutes(props: AddGatewayRouteProps[]): GatewayRoute[] { + return props.map(idx => this.addGatewayRoute(idx.id, idx.props)); + } + /** * Utility method to add a new GatewayRoute to the VirtualGateway */ diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 87fd440c6c2c6..5782fecba6a98 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -177,6 +177,107 @@ export = { expect(stack).to( haveResourceLike('AWS::AppMesh::GatewayRoute', { GatewayRouteName: 'test-gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/', + }, + }, + }, + }), + ); + test.done(); + }, + 'should create multiple gateway route resources'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const virtualGateway = new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + mesh: mesh, + }); + + const virtualService = mesh.addVirtualService('virtualService', {}); + + virtualGateway.addGatewayRoutes([ + { + id: 'testGatewayRoute', + props: { + gatewayRouteName: 'test-gateway-route', + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), + }, + }, + { + id: 'testGatewayRoute2', + props: { + gatewayRouteName: 'test-gateway-route-2', + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + match: { + prefixPath: '/2', + }, + }), + }, + }, + ]); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/', + }, + }, + }, + }), + ); + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route-2', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/2', + }, + }, + }, }), ); test.done(); From a97a5c57a2439593380e1c8ef46b04cc7842bbb4 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 20 Oct 2020 23:23:04 -0700 Subject: [PATCH 14/34] adds more tests to gateway routes --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 17 ++++++ .../aws-appmesh/test/test.gateway-route.ts | 53 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index f7a1fb59eb523..dac4004197c91 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -22,6 +22,13 @@ export interface IGatewayRoute extends cdk.IResource { * @attribute */ readonly gatewayRouteArn: string; + + /** + * The VirtualGateway the GatewayRoute belongs to + * + * @attribute + */ + readonly virtualGateway: IVirtualGateway; } /** @@ -354,13 +361,23 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { */ public gatewayRouteArn: string; + /** + * The VirtualGateway the GatewayRoute belongs to + */ + public virtualGateway: IVirtualGateway; + constructor(scope: Construct, id: string, props: GatewayRouteAttributes) { super(scope, id); if (props.gatewayRouteArn) { + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + const virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + this.virtualGateway = VirtualGateway.fromVirtualGatewayName(this, 'virtualGateway', meshName, virtualGatewayName); } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { this.gatewayRouteName = props.gatewayRouteName; + this.virtualGateway = props.virtualGateway; this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 8d0511bf5bf74..f67ffc2178981 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -36,10 +36,63 @@ export = { expect(stack).to( haveResourceLike('AWS::AppMesh::GatewayRoute', { GatewayRouteName: 'gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/', + }, + }, + }, }), ); + test.done(); + }, + 'should throw an exception if you start an http prefix match not with a /'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const virtualService = mesh.addVirtualService('testVirtualService'); + test.throws(() => appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + match: { + prefixPath: 'wrong', + }, + }).bind(stack), + /Prefix Path must start with \'\/\'/); test.done(); }, }, + 'Can export and import GatewayRoutes and perform actions'(test: Test) { + const app = new cdk.App(); + // GIVEN + const stack = new cdk.Stack(app, 'Imports', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + // WHEN + const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteName(stack, 'importedGatewayRoute', 'test-mesh', 'test-gateway', 'test-gateway-route'); + // THEN + test.equal(gatewayRoute.gatewayRouteName, 'test-gateway-route'); + test.equal(gatewayRoute.virtualGateway.virtualGatewayName, 'test-gateway'); + test.equal(gatewayRoute.virtualGateway.mesh.meshName, 'test-mesh'); + const gatewayRoute2 = appmesh.GatewayRoute.fromGatewayRouteArn( + stack, 'importedGatewayRoute2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/test-mesh/virtualGateway/test-gateway/gatewayRoute/test-gateway-route'); + test.equal(gatewayRoute2.gatewayRouteName, 'test-gateway-route'); + test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, 'test-gateway'); + test.equal(gatewayRoute2.virtualGateway.mesh.meshName, 'test-mesh'); + test.done(); + }, }; \ No newline at end of file From 0016549a3802da72fcfad10d65944cb2ceba5fbf Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 20 Oct 2020 23:45:01 -0700 Subject: [PATCH 15/34] adds integ tests for gateway routes --- .../aws-appmesh/test/integ.mesh.expected.json | 116 ++++++++++++++++++ .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 23 +++- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index 32d6cf57c2548..e74a824311783 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -801,6 +801,122 @@ "VirtualGatewayName": "gateway1" } }, + "meshgateway1gateway1routehttpE8D6F433": { + "Type": "AWS::AppMesh::GatewayRoute", + "Properties": { + "GatewayRouteName": "meshstackmeshgateway1gateway1routehttpBA921D42", + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "HttpRoute": { + "Action": { + "Target": { + "VirtualService": { + "VirtualServiceName": { + "Fn::GetAtt": [ + "meshserviceE06ECED5", + "VirtualServiceName" + ] + } + } + } + }, + "Match": { + "Prefix": "/" + } + } + }, + "VirtualGatewayName": { + "Fn::GetAtt": [ + "meshgateway1B02387E8", + "VirtualGatewayName" + ] + } + } + }, + "meshgateway1gateway1routehttp2FD69C306": { + "Type": "AWS::AppMesh::GatewayRoute", + "Properties": { + "GatewayRouteName": "meshstackmeshgateway1gateway1routehttp255781963", + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "Http2Route": { + "Action": { + "Target": { + "VirtualService": { + "VirtualServiceName": { + "Fn::GetAtt": [ + "meshserviceE06ECED5", + "VirtualServiceName" + ] + } + } + } + }, + "Match": { + "Prefix": "/" + } + } + }, + "VirtualGatewayName": { + "Fn::GetAtt": [ + "meshgateway1B02387E8", + "VirtualGatewayName" + ] + } + } + }, + "meshgateway1gateway1routegrpc76486062": { + "Type": "AWS::AppMesh::GatewayRoute", + "Properties": { + "GatewayRouteName": "meshstackmeshgateway1gateway1routegrpcCD4D891D", + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "GrpcRoute": { + "Action": { + "Target": { + "VirtualService": { + "VirtualServiceName": { + "Fn::GetAtt": [ + "meshserviceE06ECED5", + "VirtualServiceName" + ] + } + } + } + }, + "Match": { + "ServiceName": { + "Fn::GetAtt": [ + "meshserviceE06ECED5", + "VirtualServiceName" + ] + } + } + } + }, + "VirtualGatewayName": { + "Fn::GetAtt": [ + "meshgateway1B02387E8", + "VirtualGatewayName" + ] + } + } + }, "service27C65CF7D": { "Type": "AWS::AppMesh::VirtualService", "Properties": { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 89445ea748be7..322ae1cc608d6 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -117,7 +117,7 @@ router.addRoute('route-3', { ], }); -mesh.addVirtualGateway('gateway1', { +const gateway = mesh.addVirtualGateway('gateway1', { accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), virtualGatewayName: 'gateway1', }); @@ -131,3 +131,24 @@ new appmesh.VirtualGateway(stack, 'gateway2', { }, })], }); + +gateway.addGatewayRoute('gateway1-route-http', { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), +}); + +gateway.addGatewayRoute('gateway1-route-http2', { + routeSpec: appmesh.GatewayRouteSpec.http2RouteSpec({ + routeTarget: virtualService, + }), +}); + +gateway.addGatewayRoute('gateway1-route-grpc', { + routeSpec: appmesh.GatewayRouteSpec.grpcRouteSpec({ + routeTarget: virtualService, + match: { + serviceName: virtualService.virtualServiceName, + }, + }), +}); From 5a54ab5048ba4772ebdf2c754f93c8ff05de310c Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 14:39:32 -0700 Subject: [PATCH 16/34] Updates README and adds tests for adding multiple gateway routes --- packages/@aws-cdk/aws-appmesh/README.md | 122 +++++++++++++----- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 1 - .../aws-appmesh/lib/virtual-gateway.ts | 2 +- .../aws-appmesh/test/test.gateway-route.ts | 92 +++++++++++++ 4 files changed, 186 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 1becd4cf81772..f4226ab233170 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -126,8 +126,6 @@ mesh.addVirtualService('virtual-service', { A `virtual node` acts as a logical pointer to a particular task group, such as an Amazon ECS service or a Kubernetes deployment. -![Virtual node logical diagram](https://docs.aws.amazon.com/app-mesh/latest/userguide/images/virtual_node.png) - When you create a `virtual node`, you must specify the DNS service discovery hostname for your task group. Any inbound traffic that your `virtual node` expects should be specified as a listener. Any outbound traffic that your `virtual node` expects to reach should be specified as a backend. The response metadata for your new `virtual node` contains the Amazon Resource Name (ARN) that is associated with the `virtual node`. Set this value (either the full ARN or the truncated resource name) as the APPMESH_VIRTUAL_NODE_NAME environment variable for your task group's Envoy proxy container in your task definition or pod spec. For example, the value could be mesh/default/virtualNode/simpleapp. This is then mapped to the node.id and node.cluster Envoy parameters. @@ -144,7 +142,6 @@ const namespace = new servicediscovery.PrivateDnsNamespace(this, 'test-namespace const service = namespace.createService('Svc'); const node = mesh.addVirtualNode('virtual-node', { - dnsHostName: 'node-a', cloudMapService: service, listener: { portMapping: { @@ -170,7 +167,6 @@ Create a `VirtualNode` with the the constructor and add tags. ```typescript const node = new VirtualNode(this, 'node', { mesh, - dnsHostName: 'node-1', cloudMapService: service, listener: { portMapping: { @@ -193,7 +189,7 @@ const node = new VirtualNode(this, 'node', { cdk.Tag.add(node, 'Environment', 'Dev'); ``` -The listeners property can be left blank and added later with the `mesh.addListeners()` method. The `healthcheck` property is optional but if specifying a listener, the `portMappings` must contain at least one property. +The listeners property can be left blank and added later with the `node.addListeners()` method. The `healthcheck` property is optional but if specifying a listener, the `portMappings` must contain at least one property. ## Adding a Route @@ -235,34 +231,102 @@ router.addRoute('route', { }); ``` -Multiple routes may also be added at once to different applications or targets. +## Adding a Virtual Gateway + +A `virtual gateway` allows resources outside your mesh to communicate to resources that are inside your mesh. The `virtual gateway` represents an Envoy proxy running in an Amazon ECS task, in a Kubernetes service, or on an Amazon EC2 instance. Unlike a `virtual node`, which represents an Envoy running with an application, a `virtual gateway` represents Envoy deployed by itself. + +A `virtual gateway` is similar to a `virtual node` in that it has a `listener` that accepts traffic for a particular port and protocol (HTTP, HTTP2, GRPC). The traffic that the `virtual gateway` receives, is directed to other services in your mesh, using rules defined in `gateway routes` which can be added to your `virtual gateway`. + +Create a `virtual gateway` with the constructor ```typescript -ratingsRouter.addRoutes( - ['route1', 'route2'], - [ - { - routeTargets: [ - { - virtualNode, - weight: 1, - }, - ], - prefix: `/path-to-app`, - routeType: RouteType.HTTP, +const gateway = new appmesh.VirtualGateway(stack, 'gateway', { + mesh: mesh, + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ + port: 443, + healthCheck: { + interval: cdk.Duration.seconds(10), }, - { - routeTargets: [ - { - virtualNode: virtualNode2, - weight: 1, + })], + accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), + virtualGatewayName: 'virtualGateway', +}); +``` + +Add a `virtual gateway` directly to the mesh + +```typescript +const gateway = mesh.addVirtualGateway('gateway', { + accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), + virtualGatewayName: 'virtualGateway', + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ + port: 443, + healthCheck: { + interval: cdk.Duration.seconds(10), + }, + })], +}); +``` + +The `listeners` field can be omitted which will default to an HTTP Listener on port 8080. A `gateway route` can be added using the `gateway.addGatewayRoute()` method. + +## Adding a Gateway Route + +A `gateway route` is attached to a virtual gateway and routes traffic to an existing virtual service. If a route matches a request, it can distribute traffic to a target virtual service. + +For `HTTP` based routes, the `match` field can be used to match on a route prefix. By default, an `HTTP` based route will match on `/`. All matches must start with a leading `/`. + +```typescript +gateway.addGatewayRoute('gateway-route-http', { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + match: { + prefixMatch: '/', + }, + }), +}); +``` + +For `GRPC` based routes, the `match` field can be used to match on service names. You cannot omit the field, and must specify a match for these routes. + +```typescript +gateway.addGatewayRoute('gateway-route-grpc', { + routeSpec: appmesh.GatewayRouteSpec.grpcRouteSpec({ + routeTarget: virtualService, + match: { + serviceName: 'my-service.default.svc.cluster.local', + }, + }), +}); +``` + +Multiple gateway routes may also be added at route incoming traffic to different `virtual services`. + +```typescript +// Add an HTTP Route +gateway.addGatewayRoutes([ + { + id: 'gateway-route', + props: { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), + gatewayRouteName: 'gateway-route', + }, + }, + { + id: 'gateway-route2', + props: { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService2, + match: { + prefixPath: '/echo', }, - ], - prefix: `/path-to-app2`, - routeType: RouteType.HTTP, + }), + gatewayRouteName: 'gateway-route2', }, - ] -); + }, +]); ``` -The number of `route ids` and `route targets` must match as each route needs to have a unique name per router. +The `id` field is the CloudFormation Logical ID for the new resource, and props are the configuration for the new route you are creating. diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index dac4004197c91..4e6a5e3d9a518 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -185,7 +185,6 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { * The VirtualService this GatewayRoute directs traffic to */ readonly routeTarget: IVirtualService; - /** * Type of route you are creating */ diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 31a635b1f6d35..09ef5ea7619f9 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -263,7 +263,7 @@ export interface VirtualGatewayBaseProps { /** * Access Logging Configuration for the VirtualGateway * - * @default no access logging + * @default - no access logging */ readonly accessLog?: AccessLog; } diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index f67ffc2178981..09ec1e153a243 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -56,6 +56,98 @@ export = { ); test.done(); }, + 'should be able to add multiple routes'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const virtualGateway = new appmesh.VirtualGateway(stack, 'gateway-1', { + listeners: [appmesh.VirtualGatewayListener.httpGatewayListener()], + mesh: mesh, + }); + + const virtualService = new appmesh.VirtualService(stack, 'vs-1', { + mesh: mesh, + }); + const virtualService2 = new appmesh.VirtualService(stack, 'vs-2', { + mesh: mesh, + }); + + virtualGateway.addGatewayRoutes([ + { + id: 'gateway-route', + props: { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService, + }), + gatewayRouteName: 'gateway-route', + }, + }, + { + id: 'gateway-route2', + props: { + routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ + routeTarget: virtualService2, + match: { + prefixPath: '/echo', + }, + }), + gatewayRouteName: 'gateway-route2', + }, + }, + ]); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/', + }, + }, + }, + }), + ); + + // THEN + expect(stack).to( + haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route2', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs2BB8859F6', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/echo', + }, + }, + }, + }), + ); + test.done(); + }, 'should throw an exception if you start an http prefix match not with a /'(test: Test) { // GIVEN const stack = new cdk.Stack(); From ca1d0b34893acff7ab9e15c6f324a1004b1c5203 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 15:14:32 -0700 Subject: [PATCH 17/34] Apply suggestions from code review Co-authored-by: Adam Ruka --- packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts | 2 +- packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 4e6a5e3d9a518..1ae3dcc323b64 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -199,7 +199,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { - throw new Error('Prefix Path must start with \'/\''); + throw new Error(`Prefix path must start with '/', got: ${prefixPath}`); } const httpConfig: CfnGatewayRoute.HttpGatewayRouteProperty = { match: { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 09ef5ea7619f9..fa5717de62f83 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -105,7 +105,7 @@ export abstract class VirtualGatewayListener { /** * Returns a GRPC Listener for a VirtualGateway */ - public static grpcGatewayListener(props?: GrpcGatewayListenerProps) { + public static grpcGatewayListener(props?: GrpcGatewayListenerProps): VirtualGatewayListener { return new GrpcGatewayListener(props); } From 92dd14763ae9c65738c1ef17dfa29bcc6de57164 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 14:58:09 -0700 Subject: [PATCH 18/34] refactor gateway route spec related code into a separate file --- .../aws-appmesh/lib/gateway-route-spec.ts | 202 ++++++++++++++++++ .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 201 +---------------- packages/@aws-cdk/aws-appmesh/lib/index.ts | 1 + 3 files changed, 204 insertions(+), 200 deletions(-) create mode 100644 packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts new file mode 100644 index 0000000000000..0e336e4ebb2ad --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -0,0 +1,202 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnGatewayRoute } from './appmesh.generated'; +import { Protocol } from './shared-interfaces'; +import { IVirtualService } from './virtual-service'; + +/** + * Properties specific for a GRPC GatewayRoute + */ +export interface GrpcRouteSpecProps { + /** + * The criterion for determining a request match for this GatewayRoute + */ + readonly match: GrpcGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; +} + +/** + * All Properties for GatewayRoute Specs + */ +export interface GatewayRouteSpecConfig { + /** + * The spec for an http gateway route + * + * @default - no http spec + */ + readonly httpSpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; + /** + * The spec for an http2 gateway route + * + * @default - no http2 spec + */ + readonly http2SpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; + /** + * The spec for a grpc gateway route + * + * @default - no grpc spec + */ + readonly grpcSpecConfig?: CfnGatewayRoute.GrpcGatewayRouteProperty; +} + +/** + * Used to generate specs with different protocols for a GatewayRoute + */ +export abstract class GatewayRouteSpec { + /** + * Creates an HTTP Based GatewayRoute + * + * @param props - no http gateway route + */ + public static httpRouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { + return new HttpGatewayRouteSpec(props, Protocol.HTTP); + } + + /** + * Creates an HTTP2 Based GatewayRoute + * + * @param props - no http2 gateway route + */ + public static http2RouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { + return new HttpGatewayRouteSpec(props, Protocol.HTTP2); + } + + /** + * Creates an GRPC Based GatewayRoute + * + * @param props - no grpc gateway route + */ + public static grpcRouteSpec(props: GrpcRouteSpecProps): GatewayRouteSpec { + return new GrpcGatewayRouteSpec(props); + } + + /** + * Called when the GatewayRouteSpec type is initialized. Can be used to enforce + * mutual exclusivity with future properties + */ + public abstract bind(scope: cdk.Construct): GatewayRouteSpecConfig; +} + +class HttpGatewayRouteSpec extends GatewayRouteSpec { + /** + * The criterion for determining a request match for this GatewayRoute. + * + * @default - matches on '/' + */ + readonly match?: HttpGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; + /** + * Type of route you are creating + */ + readonly routeType: Protocol; + + constructor(props: HttpRouteSpecProps, protocol: Protocol.HTTP | Protocol.HTTP2) { + super(); + this.routeTarget = props.routeTarget; + this.routeType = protocol; + this.match = props.match; + } + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { + const prefixPath = this.match ? this.match.prefixPath : '/'; + if (prefixPath[0] != '/') { + throw new Error('Prefix Path must start with \'/\''); + } + const httpConfig: CfnGatewayRoute.HttpGatewayRouteProperty = { + match: { + prefix: prefixPath, + }, + action: { + target: { + virtualService: { + virtualServiceName: this.routeTarget.virtualServiceName, + }, + }, + }, + }; + return { + httpSpecConfig: this.routeType === Protocol.HTTP ? httpConfig : undefined, + http2SpecConfig: this.routeType === Protocol.HTTP2 ? httpConfig : undefined, + }; + } +} + +class GrpcGatewayRouteSpec extends GatewayRouteSpec { + /** + * The criterion for determining a request match for this GatewayRoute. + * + * @default - no default + */ + readonly match: GrpcGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; + + constructor(props: GrpcRouteSpecProps) { + super(); + this.match = props.match; + this.routeTarget = props.routeTarget; + } + + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { + return { + grpcSpecConfig: { + action: { + target: { + virtualService: { + virtualServiceName: this.routeTarget.virtualServiceName, + }, + }, + }, + match: { + serviceName: this.match.serviceName, + }, + }, + }; + } +} + +/** + * The criterion for determining a request match for this GatewayRoute + */ +export interface HttpGatewayRouteMatch { + /** + * Specifies the path to match requests with. + * This parameter must always start with /, which by itself matches all requests to the virtual service name. + * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local + * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. + * + */ + readonly prefixPath: string; +} + +/** + * The criterion for determining a request match for this GatewayRoute + */ +export interface GrpcGatewayRouteMatch { + /** + * The fully qualified domain name for the service to match from the request + */ + readonly serviceName: string; +} + +/** + * Properties specific for HTTP Based GatewayRoutes + */ +export interface HttpRouteSpecProps { + /** + * The criterion for determining a request match for this GatewayRoute + * + * @default - matches on '/' + */ + readonly match?: HttpGatewayRouteMatch; + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 1ae3dcc323b64..ba27032b15624 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -1,9 +1,8 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; -import { Protocol } from './shared-interfaces'; +import { GatewayRouteSpec } from './gateway-route-spec'; import { IVirtualGateway, VirtualGateway } from './virtual-gateway'; -import { IVirtualService } from './virtual-service'; /** * Interface for which all GatewayRoute based classes MUST implement @@ -48,30 +47,6 @@ export interface GatewayRouteBaseProps { readonly routeSpec: GatewayRouteSpec; } -/** - * The criterion for determining a request match for this GatewayRoute - */ -export interface HttpGatewayRouteMatch { - /** - * Specifies the path to match requests with. - * This parameter must always start with /, which by itself matches all requests to the virtual service name. - * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local - * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. - * - */ - readonly prefixPath: string; -} - -/** - * The criterion for determining a request match for this GatewayRoute - */ -export interface GrpcGatewayRouteMatch { - /** - * The fully qualified domain name for the service to match from the request - */ - readonly serviceName: string; -} - /** * Properties to define a new GatewayRoute */ @@ -82,180 +57,6 @@ export interface GatewayRouteProps extends GatewayRouteBaseProps { readonly virtualGateway: IVirtualGateway; } -/** - * Properties specific for HTTP Based GatewayRoutes - */ -export interface HttpRouteSpecProps { - /** - * The criterion for determining a request match for this GatewayRoute - * - * @default - matches on '/' - */ - readonly match?: HttpGatewayRouteMatch; - /** - * The VirtualService this GatewayRoute directs traffic to - */ - readonly routeTarget: IVirtualService; -} - -/** - * Properties specific for a GRPC GatewayRoute - */ -export interface GrpcRouteSpecProps { - /** - * The criterion for determining a request match for this GatewayRoute - */ - readonly match: GrpcGatewayRouteMatch; - /** - * The VirtualService this GatewayRoute directs traffic to - */ - readonly routeTarget: IVirtualService; -} - -/** - * All Properties for GatewayRoute Specs - */ -export interface GatewayRouteSpecConfig { - /** - * The spec for an http gateway route - * - * @default - no http spec - */ - readonly httpSpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; - /** - * The spec for an http2 gateway route - * - * @default - no http2 spec - */ - readonly http2SpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; - /** - * The spec for a grpc gateway route - * - * @default - no grpc spec - */ - readonly grpcSpecConfig?: CfnGatewayRoute.GrpcGatewayRouteProperty; -} - -/** - * Used to generate specs with different protocols for a GatewayRoute - */ -export abstract class GatewayRouteSpec { - /** - * Creates an HTTP Based GatewayRoute - * - * @param props - no http gateway route - */ - public static httpRouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { - return new HttpGatewayRouteSpec(props, Protocol.HTTP); - } - - /** - * Creates an HTTP2 Based GatewayRoute - * - * @param props - no http2 gateway route - */ - public static http2RouteSpec(props: HttpRouteSpecProps): GatewayRouteSpec { - return new HttpGatewayRouteSpec(props, Protocol.HTTP2); - } - - /** - * Creates an GRPC Based GatewayRoute - * - * @param props - no grpc gateway route - */ - public static grpcRouteSpec(props: GrpcRouteSpecProps): GatewayRouteSpec { - return new GrpcGatewayRouteSpec(props); - } - - /** - * Called when the GatewayRouteSpec type is initialized. Can be used to enforce - * mutual exclusivity with future properties - */ - public abstract bind(scope: cdk.Construct): GatewayRouteSpecConfig; -} - -class HttpGatewayRouteSpec extends GatewayRouteSpec { - /** - * The criterion for determining a request match for this GatewayRoute. - * - * @default - matches on '/' - */ - readonly match?: HttpGatewayRouteMatch; - /** - * The VirtualService this GatewayRoute directs traffic to - */ - readonly routeTarget: IVirtualService; - /** - * Type of route you are creating - */ - readonly routeType: Protocol; - - constructor(props: HttpRouteSpecProps, protocol: Protocol.HTTP | Protocol.HTTP2) { - super(); - this.routeTarget = props.routeTarget; - this.routeType = protocol; - this.match = props.match; - } - public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { - const prefixPath = this.match ? this.match.prefixPath : '/'; - if (prefixPath[0] != '/') { - throw new Error(`Prefix path must start with '/', got: ${prefixPath}`); - } - const httpConfig: CfnGatewayRoute.HttpGatewayRouteProperty = { - match: { - prefix: prefixPath, - }, - action: { - target: { - virtualService: { - virtualServiceName: this.routeTarget.virtualServiceName, - }, - }, - }, - }; - return { - httpSpecConfig: this.routeType === Protocol.HTTP ? httpConfig : undefined, - http2SpecConfig: this.routeType === Protocol.HTTP2 ? httpConfig : undefined, - }; - } -} - -class GrpcGatewayRouteSpec extends GatewayRouteSpec { - /** - * The criterion for determining a request match for this GatewayRoute. - * - * @default - no default - */ - readonly match: GrpcGatewayRouteMatch; - /** - * The VirtualService this GatewayRoute directs traffic to - */ - readonly routeTarget: IVirtualService; - - constructor(props: GrpcRouteSpecProps) { - super(); - this.match = props.match; - this.routeTarget = props.routeTarget; - } - - public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { - return { - grpcSpecConfig: { - action: { - target: { - virtualService: { - virtualServiceName: this.routeTarget.virtualServiceName, - }, - }, - }, - match: { - serviceName: this.match.serviceName, - }, - }, - }; - } -} - /** * GatewayRoute represents a new or existing gateway route attached to a VirtualGateway and Mesh * diff --git a/packages/@aws-cdk/aws-appmesh/lib/index.ts b/packages/@aws-cdk/aws-appmesh/lib/index.ts index 5b8e10105f08d..e05d27ba4d76c 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/index.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/index.ts @@ -8,3 +8,4 @@ export * from './virtual-router'; export * from './virtual-service'; export * from './virtual-gateway'; export * from './gateway-route'; +export * from './gateway-route-spec'; From 4c9a69cfc124e453f1ef031bfe782d0975cffa96 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 15:04:17 -0700 Subject: [PATCH 19/34] add a newline between properties --- packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts | 6 ++++++ packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts | 6 ++++++ packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts | 1 + 3 files changed, 13 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index 0e336e4ebb2ad..7a342374c1e6b 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -11,6 +11,7 @@ export interface GrpcRouteSpecProps { * The criterion for determining a request match for this GatewayRoute */ readonly match: GrpcGatewayRouteMatch; + /** * The VirtualService this GatewayRoute directs traffic to */ @@ -27,12 +28,14 @@ export interface GatewayRouteSpecConfig { * @default - no http spec */ readonly httpSpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; + /** * The spec for an http2 gateway route * * @default - no http2 spec */ readonly http2SpecConfig?: CfnGatewayRoute.HttpGatewayRouteProperty; + /** * The spec for a grpc gateway route * @@ -90,6 +93,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { * The VirtualService this GatewayRoute directs traffic to */ readonly routeTarget: IVirtualService; + /** * Type of route you are creating */ @@ -131,6 +135,7 @@ class GrpcGatewayRouteSpec extends GatewayRouteSpec { * * @default - no default */ + readonly match: GrpcGatewayRouteMatch; /** * The VirtualService this GatewayRoute directs traffic to @@ -195,6 +200,7 @@ export interface HttpRouteSpecProps { * @default - matches on '/' */ readonly match?: HttpGatewayRouteMatch; + /** * The VirtualService this GatewayRoute directs traffic to */ diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 7e10e6abf3cd4..3181072076bfa 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -23,24 +23,28 @@ export interface HealthCheck { * @default 2 */ readonly healthyThreshold?: number; + /** * Interval in milliseconds to re-check * * @default 5 seconds */ readonly interval?: cdk.Duration; + /** * The path where the application expects any health-checks, this can also be the application path. * * @default / */ readonly path?: string; + /** * The TCP port number for the healthcheck * * @default - same as corresponding port mapping */ readonly port?: number; + /** * The protocol to use for the healthcheck, for convinience a const enum has been defined. * Protocol.HTTP or Protocol.TCP @@ -48,12 +52,14 @@ export interface HealthCheck { * @default - same as corresponding port mapping */ readonly protocol?: Protocol; + /** * Timeout in milli-seconds for the healthcheck to be considered a fail. * * @default 2 seconds */ readonly timeout?: cdk.Duration; + /** * Number of failed attempts before considering the node DOWN. * diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index fa5717de62f83..48b26b157ef9a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -286,6 +286,7 @@ export interface AddGatewayRouteProps { * CloudFormation Logical ID */ readonly id: string; + /** * Properties to create a GatewayRoute */ From 214177c69b6ea3c8985cd6a1db8b57229beb3888 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 15:17:44 -0700 Subject: [PATCH 20/34] do not export gateway listeners --- packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 48b26b157ef9a..4ad5f95cb475f 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -119,7 +119,7 @@ export abstract class VirtualGatewayListener { /** * Represents the properties needed to define an HTTP Listener for a VirtualGateway */ -export class HttpGatewayListener extends VirtualGatewayListener { +class HttpGatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on * @@ -161,7 +161,7 @@ export class HttpGatewayListener extends VirtualGatewayListener { /** * Represents the properties needed to define an HTTP2 Listener for a VirtualGateway */ -export class Http2GatewayListener extends VirtualGatewayListener { +class Http2GatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on * @@ -203,7 +203,7 @@ export class Http2GatewayListener extends VirtualGatewayListener { /** * Represents the properties needed to define a GRPC Listener for Virtual Gateway */ -export class GrpcGatewayListener extends VirtualGatewayListener { +class GrpcGatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on * From 93ded0e13b5bab1e600941ddc04501e2f06ee4cd Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 20:39:44 -0700 Subject: [PATCH 21/34] removes fromResourceName methods and replaces it with fromResourceAttributes --- .../aws-appmesh/lib/gateway-route-spec.ts | 3 +- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 32 +++++----- packages/@aws-cdk/aws-appmesh/lib/route.ts | 27 +++++---- .../aws-appmesh/lib/virtual-gateway.ts | 47 ++++++--------- .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 49 ++++++++++----- .../aws-appmesh/lib/virtual-router.ts | 60 +++++++------------ .../aws-appmesh/lib/virtual-service.ts | 35 +++++++---- .../aws-appmesh/test/test.gateway-route.ts | 21 ++++--- .../@aws-cdk/aws-appmesh/test/test.route.ts | 10 ++-- .../aws-appmesh/test/test.virtual-gateway.ts | 4 +- .../aws-appmesh/test/test.virtual-node.ts | 2 +- .../aws-appmesh/test/test.virtual-router.ts | 6 +- .../aws-appmesh/test/test.virtual-service.ts | 5 +- 13 files changed, 156 insertions(+), 145 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index 7a342374c1e6b..e0209e8405da9 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -105,6 +105,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { this.routeType = protocol; this.match = props.match; } + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { @@ -205,4 +206,4 @@ export interface HttpRouteSpecProps { * The VirtualService this GatewayRoute directs traffic to */ readonly routeTarget: IVirtualService; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index ba27032b15624..56dc4ee478e2b 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -2,6 +2,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; import { GatewayRouteSpec } from './gateway-route-spec'; +import { Mesh } from './mesh'; import { IVirtualGateway, VirtualGateway } from './virtual-gateway'; /** @@ -24,8 +25,6 @@ export interface IGatewayRoute extends cdk.IResource { /** * The VirtualGateway the GatewayRoute belongs to - * - * @attribute */ readonly virtualGateway: IVirtualGateway; } @@ -73,10 +72,8 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** * Import an existing GatewayRoute given its name */ - public static fromGatewayRouteName( - scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { - const virtualGateway = VirtualGateway.fromVirtualGatewayName(scope, 'VirtualGateway', meshName, virtualGatewayName); - return new ImportedGatewayRoute(scope, id, { meshName, virtualGateway, gatewayRouteName }); + public static fromGatewayRouteAttributes(scope: Construct, id: string, attrs: GatewayRouteAttributes): IGatewayRoute { + return new ImportedGatewayRoute(scope, id, attrs); } /** @@ -125,24 +122,25 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** * Interface with properties necessary to import a reusable GatewayRoute */ -interface GatewayRouteAttributes { +export interface GatewayRouteAttributes { /** * The name of the GatewayRoute + * + * @default - required if gatewayRouteArn is not specified */ readonly gatewayRouteName?: string; /** * The Amazon Resource Name (ARN) for the GatewayRoute + * + * @default - required if gatewayRouteName and virtualGateway are not specified */ readonly gatewayRouteArn?: string; - /** - * The name of the mesh this GatewayRoute is associated with - */ - readonly meshName?: string; - /** * The name of the Virtual Gateway this GatewayRoute is associated with + * + * @default - required if gatewayRouteArn is not specified */ readonly virtualGateway?: IVirtualGateway; } @@ -170,21 +168,21 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { super(scope, id); if (props.gatewayRouteArn) { const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + const mesh = Mesh.fromMeshName(this, 'Mesh', meshName); const virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - this.virtualGateway = VirtualGateway.fromVirtualGatewayName(this, 'virtualGateway', meshName, virtualGatewayName); - } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { + this.virtualGateway = VirtualGateway.fromVirtualGatewayAttributes(this, 'virtualGateway', { mesh, virtualGatewayName }); + } else if (props.gatewayRouteName && props.virtualGateway) { this.gatewayRouteName = props.gatewayRouteName; this.virtualGateway = props.virtualGateway; this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, + resource: `mesh/${props.virtualGateway.mesh.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, resourceName: this.gatewayRouteName, }); } else { - throw new Error('Need either arn or three names'); + throw new Error('Need either gatewayRouteArn or gatewayRouteName and virtualGateway'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/route.ts b/packages/@aws-cdk/aws-appmesh/lib/route.ts index 0dfeac897375a..c9a41e2b02cab 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route.ts @@ -120,8 +120,8 @@ export class Route extends cdk.Resource implements IRoute { /** * Import an existing route given its name */ - public static fromRouteName(scope: Construct, id: string, meshName: string, virtualRouterName: string, routeName: string): IRoute { - return new ImportedRoute(scope, id, { meshName, virtualRouterName, routeName }); + public static fromRouteAttributes(scope: Construct, id: string, attrs: RouteAttributes): IRoute { + return new ImportedRoute(scope, id, attrs); } /** @@ -215,26 +215,27 @@ export class Route extends cdk.Resource implements IRoute { /** * Interface with properties ncecessary to import a reusable Route */ -interface RouteAttributes { +export interface RouteAttributes { /** - * The name of the route + * The name of the Route + * + * @default - required if routeArn is not specified */ readonly routeName?: string; /** * The Amazon Resource Name (ARN) for the route + * + * @default - required if routeName and virtualRouter are not specified */ readonly routeArn?: string; /** - * The name of the mesh this route is associated with - */ - readonly meshName?: string; - - /** - * The name of the virtual router this route is associated with + * The VirtualRouter this Route is associated with + * + * @default - required if routeArn is not specified */ - readonly virtualRouterName?: string; + readonly virtualRouter?: IVirtualRouter; } /** @@ -257,11 +258,11 @@ class ImportedRoute extends cdk.Resource implements IRoute { if (props.routeArn) { this.routeArn = props.routeArn; this.routeName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.routeArn).resourceName!)); - } else if (props.routeName && props.meshName && props.virtualRouterName) { + } else if (props.routeName && props.virtualRouter) { this.routeName = props.routeName; this.routeArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualRouter/${props.virtualRouterName}/route`, + resource: `mesh/${props.virtualRouter.mesh}/virtualRouter/${props.virtualRouter.virtualRouterName}/route`, resourceName: this.routeName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 4ad5f95cb475f..1f931d2c6f74d 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -26,7 +26,7 @@ export interface IVirtualGateway extends cdk.IResource { readonly virtualGatewayArn: string; /** - * The mesh which the VirtualGateway belongs to + * The Mesh which the VirtualGateway belongs to */ readonly mesh: IMesh; @@ -273,7 +273,7 @@ export interface VirtualGatewayBaseProps { */ export interface VirtualGatewayProps extends VirtualGatewayBaseProps { /** - * The mesh which the VirtualGateway belongs to + * The Mesh which the VirtualGateway belongs to */ readonly mesh: IMesh; } @@ -371,11 +371,8 @@ export class VirtualGateway extends VirtualGatewayBase { /** * Import an existing VirtualGateway given its name */ - public static fromVirtualGatewayName(scope: Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { - return new ImportedVirtualGateway(scope, id, { - meshName, - virtualGatewayName, - }); + public static fromVirtualGatewayAttributes(scope: Construct, id: string, attrs: VirtualGatewayAttributes): IVirtualGateway { + return new ImportedVirtualGateway(scope, id, attrs); } /** @@ -455,26 +452,27 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir /** * Unterface with properties necessary to import a reusable VirtualGateway */ -interface VirtualGatewayAttributes { +export interface VirtualGatewayAttributes { /** * The name of the VirtualGateway + * + * @default - required if virtualGatewayArn is not specified */ readonly virtualGatewayName?: string; /** * The Amazon Resource Name (ARN) belonging to the VirtualGateway + * + * @default - required if virtualGatewayName and mesh are not specified */ readonly virtualGatewayArn?: string; /** * The Mesh that the VirtualGateway belongs to + * + * @default - required if virtualGatewayArn is not specified */ readonly mesh?: IMesh; - - /** - * The name of the mesh that the VirtualGateway belongs to - */ - readonly meshName?: string; } /** @@ -498,32 +496,21 @@ class ImportedVirtualGateway extends VirtualGatewayBase { constructor(scope: Construct, id: string, props: VirtualGatewayAttributes) { super(scope, id); - - if (props.mesh) { - this.mesh = props.mesh; - } else if (props.meshName) { - if (props.mesh) { - throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); - } - this.mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); - } else if (props.virtualGatewayArn) { - const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); - this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); - } else { - throw new Error('Supply either \'mesh\' or \'meshName\' or \'virtualGatewayArn\''); - } if (props.virtualGatewayArn) { this.virtualGatewayArn = props.virtualGatewayArn; + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); + this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); this.virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); - } else if (props.virtualGatewayName && props.meshName) { + } else if (props.mesh && props.virtualGatewayName) { + this.mesh = props.mesh; this.virtualGatewayName = props.virtualGatewayName; this.virtualGatewayArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualGateway`, + resource: `mesh/${this.mesh.meshName}/virtualGateway`, resourceName: this.virtualGatewayName, }); } else { - throw new Error('Need either arn or both names'); + throw new Error('Need either virtualGatewayArn or mesh and virtualGatewayName'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index bfad388fa91ee..b2a50aca4c02e 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -2,7 +2,7 @@ import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; -import { IMesh } from './mesh'; +import { IMesh, Mesh } from './mesh'; import { validateHealthChecks } from './private/utils'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualNodeListener } from './shared-interfaces'; import { IVirtualService } from './virtual-service'; @@ -29,6 +29,11 @@ export interface IVirtualNode extends cdk.IResource { */ readonly virtualNodeArn: string; + /** + * The Mesh that the VirtualNode belongs to + */ + readonly mesh: IMesh; + /** * Utility method to add backends for existing or new VirtualNodes */ @@ -105,7 +110,7 @@ export interface VirtualNodeBaseProps { */ export interface VirtualNodeProps extends VirtualNodeBaseProps { /** - * The name of the AppMesh which the virtual node belongs to + * The Mesh that the VirtualNode belongs to */ readonly mesh: IMesh; } @@ -121,6 +126,11 @@ abstract class VirtualNodeBase extends cdk.Resource implements IVirtualNode { */ public abstract readonly virtualNodeArn: string; + /** + * The Mesh that the VirtualNode belongs to + */ + public abstract readonly mesh: IMesh; + protected readonly backends = new Array(); protected readonly listeners = new Array(); @@ -203,11 +213,8 @@ export class VirtualNode extends VirtualNodeBase { /** * Import an existing VirtualNode given its name */ - public static fromVirtualNodeName(scope: Construct, id: string, meshName: string, virtualNodeName: string): IVirtualNode { - return new ImportedVirtualNode(scope, id, { - meshName, - virtualNodeName, - }); + public static fromVirtualNodeAttributes(scope: Construct, id: string, attrs: VirtualNodeAttributes): IVirtualNode { + return new ImportedVirtualNode(scope, id, attrs); } /** @@ -221,7 +228,7 @@ export class VirtualNode extends VirtualNodeBase { public readonly virtualNodeArn: string; /** - * The service mesh that the virtual node resides in + * The Mesh that the VirtualNode belongs to */ public readonly mesh: IMesh; @@ -273,21 +280,27 @@ function renderAttributes(attrs?: {[key: string]: string}) { /** * Interface with properties ncecessary to import a reusable VirtualNode */ -interface VirtualNodeAttributes { +export interface VirtualNodeAttributes { /** * The name of the VirtualNode + * + * @default - required when virtualNodeArn is not specified */ readonly virtualNodeName?: string; /** - * The Amazon Resource Name belonging to the VirtualNdoe + * The Amazon Resource Name belonging to the VirtualNode + * + * @default - required when virtualNodeName and mesh are not specified */ readonly virtualNodeArn?: string; /** - * The service mesh that the virtual node resides in + * The Mesh that the VirtualNode belongs to + * + * @default - required when virtualNodeArn is not specified */ - readonly meshName?: string; + readonly mesh?: IMesh; } /** @@ -304,17 +317,25 @@ class ImportedVirtualNode extends VirtualNodeBase { */ public readonly virtualNodeArn: string; + /** + * The Mesh that the VirtualNode belongs to + */ + public readonly mesh: IMesh; + constructor(scope: Construct, id: string, props: VirtualNodeAttributes) { super(scope, id); if (props.virtualNodeArn) { this.virtualNodeArn = props.virtualNodeArn; this.virtualNodeName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualNodeArn).resourceName!)); - } else if (props.virtualNodeName && props.meshName) { + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualNodeArn).resourceName!)); + this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); + } else if (props.virtualNodeName && props.mesh) { this.virtualNodeName = props.virtualNodeName; + this.mesh = props.mesh; this.virtualNodeArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualNode`, + resource: `mesh/${props.mesh.meshName}/virtualNode`, resourceName: this.virtualNodeName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts index 05fcbb29d5f71..63737cf00a0a1 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts @@ -24,7 +24,7 @@ export interface IVirtualRouter extends cdk.IResource { readonly virtualRouterArn: string; /** - * The service mesh that the virtual router resides in + * The Mesh that the VirtualRouter belongs to */ readonly mesh: IMesh; @@ -75,7 +75,7 @@ abstract class VirtualRouterBase extends cdk.Resource implements IVirtualRouter public abstract readonly virtualRouterArn: string; /** - * The AppMesh mesh the VirtualRouter belongs to + * The Mesh which the VirtualRouter belongs to */ public abstract readonly mesh: IMesh; @@ -112,13 +112,6 @@ export class VirtualRouter extends VirtualRouterBase { return new ImportedVirtualRouter(scope, id, { virtualRouterArn }); } - /** - * Import an existing VirtualRouter given names - */ - public static fromVirtualRouterName(scope: Construct, id: string, meshName: string, virtualRouterName: string): IVirtualRouter { - return new ImportedVirtualRouter(scope, id, { meshName, virtualRouterName }); - } - /** * Import an existing virtual router given attributes */ @@ -137,7 +130,7 @@ export class VirtualRouter extends VirtualRouterBase { public readonly virtualRouterArn: string; /** - * The AppMesh mesh the VirtualRouter belongs to + * The Mesh the VirtualRouter belongs to */ public readonly mesh: IMesh; @@ -184,23 +177,24 @@ export class VirtualRouter extends VirtualRouterBase { export interface VirtualRouterAttributes { /** * The name of the VirtualRouter + * + * @default - required if virtualRouterArn is not specified */ readonly virtualRouterName?: string; /** * The Amazon Resource Name (ARN) for the VirtualRouter + * + * @default - required if virtualRouterName and mesh are not specified */ readonly virtualRouterArn?: string; /** - * The AppMesh mesh the VirtualRouter belongs to + * The Mesh the VirtualRouter belongs to + * + * @default - required if virtualRouterArn is not specified */ readonly mesh?: IMesh; - - /** - * The name of the AppMesh mesh the VirtualRouter belongs to - */ - readonly meshName?: string; } /** @@ -217,43 +211,29 @@ class ImportedVirtualRouter extends VirtualRouterBase { */ public readonly virtualRouterArn: string; - private _mesh?: IMesh; + /** + * The Mesh which the VirtualRouter belongs to + */ + public readonly mesh: IMesh; constructor(scope: Construct, id: string, props: VirtualRouterAttributes) { super(scope, id); - if (props.mesh) { - this._mesh = props.mesh; - } - if (props.meshName) { - if (props.mesh) { - throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); - } - this._mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); - } - if (props.virtualRouterArn) { this.virtualRouterArn = props.virtualRouterArn; this.virtualRouterName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualRouterArn).resourceName!)); - } else if (props.virtualRouterName && props.meshName) { + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualRouterArn).resourceName!)); + this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); + } else if (props.virtualRouterName && props.mesh) { this.virtualRouterName = props.virtualRouterName; + this.mesh = props.mesh; this.virtualRouterArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualRouter`, + resource: `mesh/${props.mesh.meshName}/virtualRouter`, resourceName: this.virtualRouterName, }); } else { - throw new Error('Need either arn or both names'); - } - } - - /** - * The AppMesh mesh the VirtualRouter belongs to - */ - public get mesh(): IMesh { - if (!this._mesh) { - throw new Error('Please supply either \'mesh\' or \'meshName\' when calling \'fromVirtualRouterAttributes\''); + throw new Error('Need either arn or mesh and virtualRouterName'); } - return this._mesh; } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts index 00b48f2d39d80..9d6005ba240d0 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts @@ -1,7 +1,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualService } from './appmesh.generated'; -import { IMesh } from './mesh'; +import { IMesh, Mesh } from './mesh'; import { IVirtualNode } from './virtual-node'; import { IVirtualRouter } from './virtual-router'; @@ -22,6 +22,11 @@ export interface IVirtualService extends cdk.IResource { * @attribute */ readonly virtualServiceArn: string; + + /** + * The Mesh that the VirtualService belongs to + */ + readonly mesh: IMesh; } /** @@ -84,11 +89,8 @@ export class VirtualService extends cdk.Resource implements IVirtualService { /** * Import an existing VirtualService given mesh and service names */ - public static fromVirtualServiceName(scope: Construct, id: string, meshName: string, virtualServiceName: string): IVirtualService { - return new ImportedVirtualService(scope, id, { - meshName, - virtualServiceName, - }); + public static fromVirtualServiceAttributes(scope: Construct, id: string, attrs: VirtualServiceAttributes): IVirtualService { + return new ImportedVirtualService(scope, id, attrs); } /** @@ -101,8 +103,12 @@ export class VirtualService extends cdk.Resource implements IVirtualService { */ public readonly virtualServiceArn: string; + /** + * The Mesh that the VirtualService belongs to + */ + public readonly mesh: IMesh; + private readonly virtualServiceProvider?: CfnVirtualService.VirtualServiceProviderProperty; - private readonly mesh: IMesh; constructor(scope: Construct, id: string, props: VirtualServiceProps) { super(scope, id, { @@ -159,7 +165,7 @@ export class VirtualService extends cdk.Resource implements IVirtualService { /** * Interface with properties ncecessary to import a reusable VirtualService */ -interface VirtualServiceAttributes { +export interface VirtualServiceAttributes { /** * The Amazon Resource Name (ARN) for the virtual service * @@ -177,11 +183,9 @@ interface VirtualServiceAttributes { /** * The name of the service mesh that the virtual service resides in * - * Used to derive ARN from mesh name if ARN not provided - * * @default - Required if virtualServiceArn is not supplied. */ - readonly meshName?: string; + readonly mesh?: IMesh; } /** @@ -198,17 +202,22 @@ class ImportedVirtualService extends cdk.Resource implements IVirtualService { */ public readonly virtualServiceArn: string; + public readonly mesh: IMesh; + constructor(scope: Construct, id: string, props: VirtualServiceAttributes) { super(scope, id); if (props.virtualServiceArn) { this.virtualServiceArn = props.virtualServiceArn; this.virtualServiceName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualServiceArn).resourceName!)); - } else if (props.virtualServiceName && props.meshName) { + const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualServiceArn).resourceName!)); + this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); + } else if (props.virtualServiceName && props.mesh) { this.virtualServiceName = props.virtualServiceName; + this.mesh = props.mesh; this.virtualServiceArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.meshName}/virtualService`, + resource: `mesh/${props.mesh.meshName}/virtualService`, resourceName: this.virtualServiceName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 09ec1e153a243..ee9f23b8294e0 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -174,17 +174,22 @@ export = { env: { account: '123456789012', region: 'us-east-1' }, }); + const meshName = 'test-mesh'; + const mesh = appmesh.Mesh.fromMeshName(stack, 'mesh', meshName); + const virtualGatewayName = 'test-gateway'; + const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayAttributes(stack, 'virtualGateway', { mesh, virtualGatewayName }); // WHEN - const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteName(stack, 'importedGatewayRoute', 'test-mesh', 'test-gateway', 'test-gateway-route'); + const gatewayRouteName = 'test-gateway-route'; + const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteAttributes(stack, 'importedGatewayRoute', { virtualGateway, gatewayRouteName }); // THEN - test.equal(gatewayRoute.gatewayRouteName, 'test-gateway-route'); - test.equal(gatewayRoute.virtualGateway.virtualGatewayName, 'test-gateway'); - test.equal(gatewayRoute.virtualGateway.mesh.meshName, 'test-mesh'); + test.equal(gatewayRoute.gatewayRouteName, gatewayRouteName); + test.equal(gatewayRoute.virtualGateway.virtualGatewayName, virtualGatewayName); + test.equal(gatewayRoute.virtualGateway.mesh.meshName, meshName); const gatewayRoute2 = appmesh.GatewayRoute.fromGatewayRouteArn( - stack, 'importedGatewayRoute2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/test-mesh/virtualGateway/test-gateway/gatewayRoute/test-gateway-route'); - test.equal(gatewayRoute2.gatewayRouteName, 'test-gateway-route'); - test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, 'test-gateway'); - test.equal(gatewayRoute2.virtualGateway.mesh.meshName, 'test-mesh'); + stack, 'importedGatewayRoute2', `arn:aws:appmesh:us-east-1:123456789012:mesh/${meshName}/virtualGateway/${virtualGatewayName}/gatewayRoute/${gatewayRouteName}`); + test.equal(gatewayRoute2.gatewayRouteName, gatewayRouteName); + test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, virtualGatewayName); + test.equal(gatewayRoute2.virtualGateway.mesh.meshName, meshName); test.done(); }, }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 706a29a3552fc..7acadc16c49e2 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -13,7 +13,7 @@ export = { meshName: 'test-mesh', }); - const router = new appmesh.VirtualRouter(stack, 'router', { + const virtualRouter = new appmesh.VirtualRouter(stack, 'router', { mesh, }); @@ -36,9 +36,9 @@ export = { ], }); - const route = new appmesh.Route(stack, 'route-1', { + new appmesh.Route(stack, 'route-1', { mesh, - virtualRouter: router, + virtualRouter: virtualRouter, routeTargets: [ { virtualNode: node, @@ -49,9 +49,11 @@ export = { }); const stack2 = new cdk.Stack(); - appmesh.Route.fromRouteName(stack2, 'imported-route', mesh.meshName, router.virtualRouterName, route.routeName); + const routeName = 'imported-route'; + const importedRoute = appmesh.Route.fromRouteAttributes(stack2, 'imported-route', { virtualRouter, routeName }); // Nothing to do with imported route yet + test.equal(importedRoute.routeName, routeName); test.done(); }, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 5782fecba6a98..a1cb5417ba3fb 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -330,8 +330,10 @@ export = { env: { account: '123456789012', region: 'us-east-1' }, }); + const mesh = appmesh.Mesh.fromMeshName(stack, 'Mesh', 'testMesh'); + const virtualGatewayName = 'test-gateway'; // WHEN - const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayName(stack, 'importedGateway', 'testMesh', 'test-gateway'); + const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayAttributes(stack, 'importedGateway', { mesh, virtualGatewayName }); // THEN test.equal(virtualGateway.mesh.meshName, 'testMesh'); test.equal(virtualGateway.virtualGatewayName, 'test-gateway'); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts index 82589e2c52ac5..749cba00bfa40 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts @@ -118,7 +118,7 @@ export = { const stack2 = new cdk.Stack(); - const node2 = appmesh.VirtualNode.fromVirtualNodeName(stack2, 'imported-node', mesh.meshName, node.virtualNodeName); + const node2 = appmesh.VirtualNode.fromVirtualNodeAttributes(stack2, 'imported-node', { mesh, virtualNodeName: node.virtualNodeName }); node2.addListeners({ portMapping: { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts index 47b59e0e39962..cbe2e895e88a6 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts @@ -307,12 +307,14 @@ export = { 'can import a virtual router'(test: Test) { // GIVEN const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'Mesh'); // WHEN - const vr = appmesh.VirtualRouter.fromVirtualRouterName(stack, 'Router', 'MyMesh', 'MyRouter'); + const virtualRouterName = 'virtual-router'; + const importedVr = appmesh.VirtualRouter.fromVirtualRouterAttributes(stack, 'ImportedVr', { mesh, virtualRouterName }); // THEN - test.ok(vr.mesh !== undefined); + test.equal(importedVr.virtualRouterName, virtualRouterName); test.done(); }, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts index aa582c9376f19..4e84a45721e6b 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts @@ -30,9 +30,12 @@ export = { }); const stack2 = new cdk.Stack(); - appmesh.VirtualService.fromVirtualServiceName(stack2, 'imported-virtual-service', mesh.meshName, service.virtualServiceName); + const virtualServiceName = service.virtualServiceName; + const importedVirtualService = appmesh.VirtualService.fromVirtualServiceAttributes(stack2, 'imported-virtual-service', { mesh, virtualServiceName }); // Nothing to do with imported virtual service yet + test.equal(importedVirtualService.virtualServiceName, virtualServiceName); + test.done(); }, From b737de66dfc1583d6afc59a8a06fe5d1956390ba Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Wed, 21 Oct 2020 20:45:49 -0700 Subject: [PATCH 22/34] makes error message more specific for prefixPath --- packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts | 2 +- packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index e0209e8405da9..2cda46de8fdea 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -109,7 +109,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { - throw new Error('Prefix Path must start with \'/\''); + throw new Error(`Prefix Path must start with \'/\', got: ${prefixPath}`); } const httpConfig: CfnGatewayRoute.HttpGatewayRouteProperty = { match: { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index ee9f23b8294e0..1ac3e5b3cfe02 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -163,7 +163,7 @@ export = { prefixPath: 'wrong', }, }).bind(stack), - /Prefix Path must start with \'\/\'/); + /Prefix Path must start with \'\/\', got: wrong/); test.done(); }, }, From 48900efd5237f52d23aa1a91298b6fad4a2f34a2 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 12:32:26 -0700 Subject: [PATCH 23/34] Apply suggestions from code review Co-authored-by: Adam Ruka --- packages/@aws-cdk/aws-appmesh/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index f4226ab233170..75534bea38c15 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -237,7 +237,7 @@ A `virtual gateway` allows resources outside your mesh to communicate to resourc A `virtual gateway` is similar to a `virtual node` in that it has a `listener` that accepts traffic for a particular port and protocol (HTTP, HTTP2, GRPC). The traffic that the `virtual gateway` receives, is directed to other services in your mesh, using rules defined in `gateway routes` which can be added to your `virtual gateway`. -Create a `virtual gateway` with the constructor +Create a `virtual gateway` with the constructor: ```typescript const gateway = new appmesh.VirtualGateway(stack, 'gateway', { @@ -253,7 +253,7 @@ const gateway = new appmesh.VirtualGateway(stack, 'gateway', { }); ``` -Add a `virtual gateway` directly to the mesh +Add a `virtual gateway` directly to the mesh: ```typescript const gateway = mesh.addVirtualGateway('gateway', { From bcd5425b421406ccc9e24a4ccb1e6a8a968cb102 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 12:50:28 -0700 Subject: [PATCH 24/34] Revert "removes fromResourceName methods and replaces it with fromResourceAttributes" This reverts commit 93ded0e13b5bab1e600941ddc04501e2f06ee4cd. --- .../aws-appmesh/lib/gateway-route-spec.ts | 3 +- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 32 +++++----- packages/@aws-cdk/aws-appmesh/lib/route.ts | 27 ++++----- .../aws-appmesh/lib/virtual-gateway.ts | 47 +++++++++------ .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 49 +++++---------- .../aws-appmesh/lib/virtual-router.ts | 60 ++++++++++++------- .../aws-appmesh/lib/virtual-service.ts | 35 ++++------- .../aws-appmesh/test/test.gateway-route.ts | 21 +++---- .../@aws-cdk/aws-appmesh/test/test.route.ts | 10 ++-- .../aws-appmesh/test/test.virtual-gateway.ts | 4 +- .../aws-appmesh/test/test.virtual-node.ts | 2 +- .../aws-appmesh/test/test.virtual-router.ts | 6 +- .../aws-appmesh/test/test.virtual-service.ts | 5 +- 13 files changed, 145 insertions(+), 156 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index 2cda46de8fdea..78dd4ae6c54f5 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -105,7 +105,6 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { this.routeType = protocol; this.match = props.match; } - public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { @@ -206,4 +205,4 @@ export interface HttpRouteSpecProps { * The VirtualService this GatewayRoute directs traffic to */ readonly routeTarget: IVirtualService; -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 56dc4ee478e2b..ba27032b15624 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -2,7 +2,6 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnGatewayRoute } from './appmesh.generated'; import { GatewayRouteSpec } from './gateway-route-spec'; -import { Mesh } from './mesh'; import { IVirtualGateway, VirtualGateway } from './virtual-gateway'; /** @@ -25,6 +24,8 @@ export interface IGatewayRoute extends cdk.IResource { /** * The VirtualGateway the GatewayRoute belongs to + * + * @attribute */ readonly virtualGateway: IVirtualGateway; } @@ -72,8 +73,10 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** * Import an existing GatewayRoute given its name */ - public static fromGatewayRouteAttributes(scope: Construct, id: string, attrs: GatewayRouteAttributes): IGatewayRoute { - return new ImportedGatewayRoute(scope, id, attrs); + public static fromGatewayRouteName( + scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { + const virtualGateway = VirtualGateway.fromVirtualGatewayName(scope, 'VirtualGateway', meshName, virtualGatewayName); + return new ImportedGatewayRoute(scope, id, { meshName, virtualGateway, gatewayRouteName }); } /** @@ -122,25 +125,24 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { /** * Interface with properties necessary to import a reusable GatewayRoute */ -export interface GatewayRouteAttributes { +interface GatewayRouteAttributes { /** * The name of the GatewayRoute - * - * @default - required if gatewayRouteArn is not specified */ readonly gatewayRouteName?: string; /** * The Amazon Resource Name (ARN) for the GatewayRoute - * - * @default - required if gatewayRouteName and virtualGateway are not specified */ readonly gatewayRouteArn?: string; + /** + * The name of the mesh this GatewayRoute is associated with + */ + readonly meshName?: string; + /** * The name of the Virtual Gateway this GatewayRoute is associated with - * - * @default - required if gatewayRouteArn is not specified */ readonly virtualGateway?: IVirtualGateway; } @@ -168,21 +170,21 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { super(scope, id); if (props.gatewayRouteArn) { const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - const mesh = Mesh.fromMeshName(this, 'Mesh', meshName); const virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); + this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - this.virtualGateway = VirtualGateway.fromVirtualGatewayAttributes(this, 'virtualGateway', { mesh, virtualGatewayName }); - } else if (props.gatewayRouteName && props.virtualGateway) { + this.virtualGateway = VirtualGateway.fromVirtualGatewayName(this, 'virtualGateway', meshName, virtualGatewayName); + } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { this.gatewayRouteName = props.gatewayRouteName; this.virtualGateway = props.virtualGateway; this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.virtualGateway.mesh.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, + resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, resourceName: this.gatewayRouteName, }); } else { - throw new Error('Need either gatewayRouteArn or gatewayRouteName and virtualGateway'); + throw new Error('Need either arn or three names'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/route.ts b/packages/@aws-cdk/aws-appmesh/lib/route.ts index c9a41e2b02cab..0dfeac897375a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route.ts @@ -120,8 +120,8 @@ export class Route extends cdk.Resource implements IRoute { /** * Import an existing route given its name */ - public static fromRouteAttributes(scope: Construct, id: string, attrs: RouteAttributes): IRoute { - return new ImportedRoute(scope, id, attrs); + public static fromRouteName(scope: Construct, id: string, meshName: string, virtualRouterName: string, routeName: string): IRoute { + return new ImportedRoute(scope, id, { meshName, virtualRouterName, routeName }); } /** @@ -215,27 +215,26 @@ export class Route extends cdk.Resource implements IRoute { /** * Interface with properties ncecessary to import a reusable Route */ -export interface RouteAttributes { +interface RouteAttributes { /** - * The name of the Route - * - * @default - required if routeArn is not specified + * The name of the route */ readonly routeName?: string; /** * The Amazon Resource Name (ARN) for the route - * - * @default - required if routeName and virtualRouter are not specified */ readonly routeArn?: string; /** - * The VirtualRouter this Route is associated with - * - * @default - required if routeArn is not specified + * The name of the mesh this route is associated with + */ + readonly meshName?: string; + + /** + * The name of the virtual router this route is associated with */ - readonly virtualRouter?: IVirtualRouter; + readonly virtualRouterName?: string; } /** @@ -258,11 +257,11 @@ class ImportedRoute extends cdk.Resource implements IRoute { if (props.routeArn) { this.routeArn = props.routeArn; this.routeName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.routeArn).resourceName!)); - } else if (props.routeName && props.virtualRouter) { + } else if (props.routeName && props.meshName && props.virtualRouterName) { this.routeName = props.routeName; this.routeArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.virtualRouter.mesh}/virtualRouter/${props.virtualRouter.virtualRouterName}/route`, + resource: `mesh/${props.meshName}/virtualRouter/${props.virtualRouterName}/route`, resourceName: this.routeName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 1f931d2c6f74d..4ad5f95cb475f 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -26,7 +26,7 @@ export interface IVirtualGateway extends cdk.IResource { readonly virtualGatewayArn: string; /** - * The Mesh which the VirtualGateway belongs to + * The mesh which the VirtualGateway belongs to */ readonly mesh: IMesh; @@ -273,7 +273,7 @@ export interface VirtualGatewayBaseProps { */ export interface VirtualGatewayProps extends VirtualGatewayBaseProps { /** - * The Mesh which the VirtualGateway belongs to + * The mesh which the VirtualGateway belongs to */ readonly mesh: IMesh; } @@ -371,8 +371,11 @@ export class VirtualGateway extends VirtualGatewayBase { /** * Import an existing VirtualGateway given its name */ - public static fromVirtualGatewayAttributes(scope: Construct, id: string, attrs: VirtualGatewayAttributes): IVirtualGateway { - return new ImportedVirtualGateway(scope, id, attrs); + public static fromVirtualGatewayName(scope: Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { + return new ImportedVirtualGateway(scope, id, { + meshName, + virtualGatewayName, + }); } /** @@ -452,27 +455,26 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir /** * Unterface with properties necessary to import a reusable VirtualGateway */ -export interface VirtualGatewayAttributes { +interface VirtualGatewayAttributes { /** * The name of the VirtualGateway - * - * @default - required if virtualGatewayArn is not specified */ readonly virtualGatewayName?: string; /** * The Amazon Resource Name (ARN) belonging to the VirtualGateway - * - * @default - required if virtualGatewayName and mesh are not specified */ readonly virtualGatewayArn?: string; /** * The Mesh that the VirtualGateway belongs to - * - * @default - required if virtualGatewayArn is not specified */ readonly mesh?: IMesh; + + /** + * The name of the mesh that the VirtualGateway belongs to + */ + readonly meshName?: string; } /** @@ -496,21 +498,32 @@ class ImportedVirtualGateway extends VirtualGatewayBase { constructor(scope: Construct, id: string, props: VirtualGatewayAttributes) { super(scope, id); - if (props.virtualGatewayArn) { - this.virtualGatewayArn = props.virtualGatewayArn; + + if (props.mesh) { + this.mesh = props.mesh; + } else if (props.meshName) { + if (props.mesh) { + throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); + } + this.mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); + } else if (props.virtualGatewayArn) { const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); + } else { + throw new Error('Supply either \'mesh\' or \'meshName\' or \'virtualGatewayArn\''); + } + if (props.virtualGatewayArn) { + this.virtualGatewayArn = props.virtualGatewayArn; this.virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); - } else if (props.mesh && props.virtualGatewayName) { - this.mesh = props.mesh; + } else if (props.virtualGatewayName && props.meshName) { this.virtualGatewayName = props.virtualGatewayName; this.virtualGatewayArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${this.mesh.meshName}/virtualGateway`, + resource: `mesh/${props.meshName}/virtualGateway`, resourceName: this.virtualGatewayName, }); } else { - throw new Error('Need either virtualGatewayArn or mesh and virtualGatewayName'); + throw new Error('Need either arn or both names'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index b2a50aca4c02e..bfad388fa91ee 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -2,7 +2,7 @@ import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; -import { IMesh, Mesh } from './mesh'; +import { IMesh } from './mesh'; import { validateHealthChecks } from './private/utils'; import { AccessLog, HealthCheck, PortMapping, Protocol, VirtualNodeListener } from './shared-interfaces'; import { IVirtualService } from './virtual-service'; @@ -29,11 +29,6 @@ export interface IVirtualNode extends cdk.IResource { */ readonly virtualNodeArn: string; - /** - * The Mesh that the VirtualNode belongs to - */ - readonly mesh: IMesh; - /** * Utility method to add backends for existing or new VirtualNodes */ @@ -110,7 +105,7 @@ export interface VirtualNodeBaseProps { */ export interface VirtualNodeProps extends VirtualNodeBaseProps { /** - * The Mesh that the VirtualNode belongs to + * The name of the AppMesh which the virtual node belongs to */ readonly mesh: IMesh; } @@ -126,11 +121,6 @@ abstract class VirtualNodeBase extends cdk.Resource implements IVirtualNode { */ public abstract readonly virtualNodeArn: string; - /** - * The Mesh that the VirtualNode belongs to - */ - public abstract readonly mesh: IMesh; - protected readonly backends = new Array(); protected readonly listeners = new Array(); @@ -213,8 +203,11 @@ export class VirtualNode extends VirtualNodeBase { /** * Import an existing VirtualNode given its name */ - public static fromVirtualNodeAttributes(scope: Construct, id: string, attrs: VirtualNodeAttributes): IVirtualNode { - return new ImportedVirtualNode(scope, id, attrs); + public static fromVirtualNodeName(scope: Construct, id: string, meshName: string, virtualNodeName: string): IVirtualNode { + return new ImportedVirtualNode(scope, id, { + meshName, + virtualNodeName, + }); } /** @@ -228,7 +221,7 @@ export class VirtualNode extends VirtualNodeBase { public readonly virtualNodeArn: string; /** - * The Mesh that the VirtualNode belongs to + * The service mesh that the virtual node resides in */ public readonly mesh: IMesh; @@ -280,27 +273,21 @@ function renderAttributes(attrs?: {[key: string]: string}) { /** * Interface with properties ncecessary to import a reusable VirtualNode */ -export interface VirtualNodeAttributes { +interface VirtualNodeAttributes { /** * The name of the VirtualNode - * - * @default - required when virtualNodeArn is not specified */ readonly virtualNodeName?: string; /** - * The Amazon Resource Name belonging to the VirtualNode - * - * @default - required when virtualNodeName and mesh are not specified + * The Amazon Resource Name belonging to the VirtualNdoe */ readonly virtualNodeArn?: string; /** - * The Mesh that the VirtualNode belongs to - * - * @default - required when virtualNodeArn is not specified + * The service mesh that the virtual node resides in */ - readonly mesh?: IMesh; + readonly meshName?: string; } /** @@ -317,25 +304,17 @@ class ImportedVirtualNode extends VirtualNodeBase { */ public readonly virtualNodeArn: string; - /** - * The Mesh that the VirtualNode belongs to - */ - public readonly mesh: IMesh; - constructor(scope: Construct, id: string, props: VirtualNodeAttributes) { super(scope, id); if (props.virtualNodeArn) { this.virtualNodeArn = props.virtualNodeArn; this.virtualNodeName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualNodeArn).resourceName!)); - const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualNodeArn).resourceName!)); - this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); - } else if (props.virtualNodeName && props.mesh) { + } else if (props.virtualNodeName && props.meshName) { this.virtualNodeName = props.virtualNodeName; - this.mesh = props.mesh; this.virtualNodeArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.mesh.meshName}/virtualNode`, + resource: `mesh/${props.meshName}/virtualNode`, resourceName: this.virtualNodeName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts index 63737cf00a0a1..05fcbb29d5f71 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-router.ts @@ -24,7 +24,7 @@ export interface IVirtualRouter extends cdk.IResource { readonly virtualRouterArn: string; /** - * The Mesh that the VirtualRouter belongs to + * The service mesh that the virtual router resides in */ readonly mesh: IMesh; @@ -75,7 +75,7 @@ abstract class VirtualRouterBase extends cdk.Resource implements IVirtualRouter public abstract readonly virtualRouterArn: string; /** - * The Mesh which the VirtualRouter belongs to + * The AppMesh mesh the VirtualRouter belongs to */ public abstract readonly mesh: IMesh; @@ -112,6 +112,13 @@ export class VirtualRouter extends VirtualRouterBase { return new ImportedVirtualRouter(scope, id, { virtualRouterArn }); } + /** + * Import an existing VirtualRouter given names + */ + public static fromVirtualRouterName(scope: Construct, id: string, meshName: string, virtualRouterName: string): IVirtualRouter { + return new ImportedVirtualRouter(scope, id, { meshName, virtualRouterName }); + } + /** * Import an existing virtual router given attributes */ @@ -130,7 +137,7 @@ export class VirtualRouter extends VirtualRouterBase { public readonly virtualRouterArn: string; /** - * The Mesh the VirtualRouter belongs to + * The AppMesh mesh the VirtualRouter belongs to */ public readonly mesh: IMesh; @@ -177,24 +184,23 @@ export class VirtualRouter extends VirtualRouterBase { export interface VirtualRouterAttributes { /** * The name of the VirtualRouter - * - * @default - required if virtualRouterArn is not specified */ readonly virtualRouterName?: string; /** * The Amazon Resource Name (ARN) for the VirtualRouter - * - * @default - required if virtualRouterName and mesh are not specified */ readonly virtualRouterArn?: string; /** - * The Mesh the VirtualRouter belongs to - * - * @default - required if virtualRouterArn is not specified + * The AppMesh mesh the VirtualRouter belongs to */ readonly mesh?: IMesh; + + /** + * The name of the AppMesh mesh the VirtualRouter belongs to + */ + readonly meshName?: string; } /** @@ -211,29 +217,43 @@ class ImportedVirtualRouter extends VirtualRouterBase { */ public readonly virtualRouterArn: string; - /** - * The Mesh which the VirtualRouter belongs to - */ - public readonly mesh: IMesh; + private _mesh?: IMesh; constructor(scope: Construct, id: string, props: VirtualRouterAttributes) { super(scope, id); + if (props.mesh) { + this._mesh = props.mesh; + } + if (props.meshName) { + if (props.mesh) { + throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); + } + this._mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); + } + if (props.virtualRouterArn) { this.virtualRouterArn = props.virtualRouterArn; this.virtualRouterName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualRouterArn).resourceName!)); - const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualRouterArn).resourceName!)); - this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); - } else if (props.virtualRouterName && props.mesh) { + } else if (props.virtualRouterName && props.meshName) { this.virtualRouterName = props.virtualRouterName; - this.mesh = props.mesh; this.virtualRouterArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.mesh.meshName}/virtualRouter`, + resource: `mesh/${props.meshName}/virtualRouter`, resourceName: this.virtualRouterName, }); } else { - throw new Error('Need either arn or mesh and virtualRouterName'); + throw new Error('Need either arn or both names'); + } + } + + /** + * The AppMesh mesh the VirtualRouter belongs to + */ + public get mesh(): IMesh { + if (!this._mesh) { + throw new Error('Please supply either \'mesh\' or \'meshName\' when calling \'fromVirtualRouterAttributes\''); } + return this._mesh; } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts index 9d6005ba240d0..00b48f2d39d80 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts @@ -1,7 +1,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualService } from './appmesh.generated'; -import { IMesh, Mesh } from './mesh'; +import { IMesh } from './mesh'; import { IVirtualNode } from './virtual-node'; import { IVirtualRouter } from './virtual-router'; @@ -22,11 +22,6 @@ export interface IVirtualService extends cdk.IResource { * @attribute */ readonly virtualServiceArn: string; - - /** - * The Mesh that the VirtualService belongs to - */ - readonly mesh: IMesh; } /** @@ -89,8 +84,11 @@ export class VirtualService extends cdk.Resource implements IVirtualService { /** * Import an existing VirtualService given mesh and service names */ - public static fromVirtualServiceAttributes(scope: Construct, id: string, attrs: VirtualServiceAttributes): IVirtualService { - return new ImportedVirtualService(scope, id, attrs); + public static fromVirtualServiceName(scope: Construct, id: string, meshName: string, virtualServiceName: string): IVirtualService { + return new ImportedVirtualService(scope, id, { + meshName, + virtualServiceName, + }); } /** @@ -103,12 +101,8 @@ export class VirtualService extends cdk.Resource implements IVirtualService { */ public readonly virtualServiceArn: string; - /** - * The Mesh that the VirtualService belongs to - */ - public readonly mesh: IMesh; - private readonly virtualServiceProvider?: CfnVirtualService.VirtualServiceProviderProperty; + private readonly mesh: IMesh; constructor(scope: Construct, id: string, props: VirtualServiceProps) { super(scope, id, { @@ -165,7 +159,7 @@ export class VirtualService extends cdk.Resource implements IVirtualService { /** * Interface with properties ncecessary to import a reusable VirtualService */ -export interface VirtualServiceAttributes { +interface VirtualServiceAttributes { /** * The Amazon Resource Name (ARN) for the virtual service * @@ -183,9 +177,11 @@ export interface VirtualServiceAttributes { /** * The name of the service mesh that the virtual service resides in * + * Used to derive ARN from mesh name if ARN not provided + * * @default - Required if virtualServiceArn is not supplied. */ - readonly mesh?: IMesh; + readonly meshName?: string; } /** @@ -202,22 +198,17 @@ class ImportedVirtualService extends cdk.Resource implements IVirtualService { */ public readonly virtualServiceArn: string; - public readonly mesh: IMesh; - constructor(scope: Construct, id: string, props: VirtualServiceAttributes) { super(scope, id); if (props.virtualServiceArn) { this.virtualServiceArn = props.virtualServiceArn; this.virtualServiceName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualServiceArn).resourceName!)); - const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualServiceArn).resourceName!)); - this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); - } else if (props.virtualServiceName && props.mesh) { + } else if (props.virtualServiceName && props.meshName) { this.virtualServiceName = props.virtualServiceName; - this.mesh = props.mesh; this.virtualServiceArn = cdk.Stack.of(this).formatArn({ service: 'appmesh', - resource: `mesh/${props.mesh.meshName}/virtualService`, + resource: `mesh/${props.meshName}/virtualService`, resourceName: this.virtualServiceName, }); } else { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 1ac3e5b3cfe02..fa64febe7c2a0 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -174,22 +174,17 @@ export = { env: { account: '123456789012', region: 'us-east-1' }, }); - const meshName = 'test-mesh'; - const mesh = appmesh.Mesh.fromMeshName(stack, 'mesh', meshName); - const virtualGatewayName = 'test-gateway'; - const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayAttributes(stack, 'virtualGateway', { mesh, virtualGatewayName }); // WHEN - const gatewayRouteName = 'test-gateway-route'; - const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteAttributes(stack, 'importedGatewayRoute', { virtualGateway, gatewayRouteName }); + const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteName(stack, 'importedGatewayRoute', 'test-mesh', 'test-gateway', 'test-gateway-route'); // THEN - test.equal(gatewayRoute.gatewayRouteName, gatewayRouteName); - test.equal(gatewayRoute.virtualGateway.virtualGatewayName, virtualGatewayName); - test.equal(gatewayRoute.virtualGateway.mesh.meshName, meshName); + test.equal(gatewayRoute.gatewayRouteName, 'test-gateway-route'); + test.equal(gatewayRoute.virtualGateway.virtualGatewayName, 'test-gateway'); + test.equal(gatewayRoute.virtualGateway.mesh.meshName, 'test-mesh'); const gatewayRoute2 = appmesh.GatewayRoute.fromGatewayRouteArn( - stack, 'importedGatewayRoute2', `arn:aws:appmesh:us-east-1:123456789012:mesh/${meshName}/virtualGateway/${virtualGatewayName}/gatewayRoute/${gatewayRouteName}`); - test.equal(gatewayRoute2.gatewayRouteName, gatewayRouteName); - test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, virtualGatewayName); - test.equal(gatewayRoute2.virtualGateway.mesh.meshName, meshName); + stack, 'importedGatewayRoute2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/test-mesh/virtualGateway/test-gateway/gatewayRoute/test-gateway-route'); + test.equal(gatewayRoute2.gatewayRouteName, 'test-gateway-route'); + test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, 'test-gateway'); + test.equal(gatewayRoute2.virtualGateway.mesh.meshName, 'test-mesh'); test.done(); }, }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 7acadc16c49e2..706a29a3552fc 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -13,7 +13,7 @@ export = { meshName: 'test-mesh', }); - const virtualRouter = new appmesh.VirtualRouter(stack, 'router', { + const router = new appmesh.VirtualRouter(stack, 'router', { mesh, }); @@ -36,9 +36,9 @@ export = { ], }); - new appmesh.Route(stack, 'route-1', { + const route = new appmesh.Route(stack, 'route-1', { mesh, - virtualRouter: virtualRouter, + virtualRouter: router, routeTargets: [ { virtualNode: node, @@ -49,11 +49,9 @@ export = { }); const stack2 = new cdk.Stack(); - const routeName = 'imported-route'; - const importedRoute = appmesh.Route.fromRouteAttributes(stack2, 'imported-route', { virtualRouter, routeName }); + appmesh.Route.fromRouteName(stack2, 'imported-route', mesh.meshName, router.virtualRouterName, route.routeName); // Nothing to do with imported route yet - test.equal(importedRoute.routeName, routeName); test.done(); }, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index a1cb5417ba3fb..5782fecba6a98 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -330,10 +330,8 @@ export = { env: { account: '123456789012', region: 'us-east-1' }, }); - const mesh = appmesh.Mesh.fromMeshName(stack, 'Mesh', 'testMesh'); - const virtualGatewayName = 'test-gateway'; // WHEN - const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayAttributes(stack, 'importedGateway', { mesh, virtualGatewayName }); + const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayName(stack, 'importedGateway', 'testMesh', 'test-gateway'); // THEN test.equal(virtualGateway.mesh.meshName, 'testMesh'); test.equal(virtualGateway.virtualGatewayName, 'test-gateway'); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts index 749cba00bfa40..82589e2c52ac5 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts @@ -118,7 +118,7 @@ export = { const stack2 = new cdk.Stack(); - const node2 = appmesh.VirtualNode.fromVirtualNodeAttributes(stack2, 'imported-node', { mesh, virtualNodeName: node.virtualNodeName }); + const node2 = appmesh.VirtualNode.fromVirtualNodeName(stack2, 'imported-node', mesh.meshName, node.virtualNodeName); node2.addListeners({ portMapping: { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts index cbe2e895e88a6..47b59e0e39962 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-router.ts @@ -307,14 +307,12 @@ export = { 'can import a virtual router'(test: Test) { // GIVEN const stack = new cdk.Stack(); - const mesh = new appmesh.Mesh(stack, 'Mesh'); // WHEN - const virtualRouterName = 'virtual-router'; - const importedVr = appmesh.VirtualRouter.fromVirtualRouterAttributes(stack, 'ImportedVr', { mesh, virtualRouterName }); + const vr = appmesh.VirtualRouter.fromVirtualRouterName(stack, 'Router', 'MyMesh', 'MyRouter'); // THEN - test.equal(importedVr.virtualRouterName, virtualRouterName); + test.ok(vr.mesh !== undefined); test.done(); }, diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts index 4e84a45721e6b..aa582c9376f19 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-service.ts @@ -30,12 +30,9 @@ export = { }); const stack2 = new cdk.Stack(); - const virtualServiceName = service.virtualServiceName; - const importedVirtualService = appmesh.VirtualService.fromVirtualServiceAttributes(stack2, 'imported-virtual-service', { mesh, virtualServiceName }); + appmesh.VirtualService.fromVirtualServiceName(stack2, 'imported-virtual-service', mesh.meshName, service.virtualServiceName); // Nothing to do with imported virtual service yet - test.equal(importedVirtualService.virtualServiceName, virtualServiceName); - test.done(); }, From 94e79a8d75fcd3b13009560645c70309f88d4534 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 12:57:40 -0700 Subject: [PATCH 25/34] Update README with requested changes --- packages/@aws-cdk/aws-appmesh/README.md | 36 +++++++++++++++---------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 75534bea38c15..12f762ca5d603 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -233,11 +233,15 @@ router.addRoute('route', { ## Adding a Virtual Gateway -A `virtual gateway` allows resources outside your mesh to communicate to resources that are inside your mesh. The `virtual gateway` represents an Envoy proxy running in an Amazon ECS task, in a Kubernetes service, or on an Amazon EC2 instance. Unlike a `virtual node`, which represents an Envoy running with an application, a `virtual gateway` represents Envoy deployed by itself. +A _virtual gateway_ allows resources outside your mesh to communicate to resources that are inside your mesh. +The virtual gateway represents an Envoy proxy running in an Amazon ECS task, in a Kubernetes service, or on an Amazon EC2 instance. +Unlike a virtual node, which represents an Envoy running with an application, a virtual gateway represents Envoy deployed by itself. -A `virtual gateway` is similar to a `virtual node` in that it has a `listener` that accepts traffic for a particular port and protocol (HTTP, HTTP2, GRPC). The traffic that the `virtual gateway` receives, is directed to other services in your mesh, using rules defined in `gateway routes` which can be added to your `virtual gateway`. +A virtual gateway is similar to a virtual node in that it has a listener that accepts traffic for a particular port and protocol (HTTP, HTTP2, GRPC). +The traffic that the virtual gateway receives, is directed to other services in your mesh, +using rules defined in gateway routes which can be added to your virtual gateway. -Create a `virtual gateway` with the constructor: +Create a virtual gateway with the constructor: ```typescript const gateway = new appmesh.VirtualGateway(stack, 'gateway', { @@ -253,28 +257,31 @@ const gateway = new appmesh.VirtualGateway(stack, 'gateway', { }); ``` -Add a `virtual gateway` directly to the mesh: +Add a virtual gateway directly to the mesh: ```typescript const gateway = mesh.addVirtualGateway('gateway', { accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), virtualGatewayName: 'virtualGateway', listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ - port: 443, - healthCheck: { - interval: cdk.Duration.seconds(10), - }, + port: 443, + healthCheck: { + interval: cdk.Duration.seconds(10), + }, })], }); ``` -The `listeners` field can be omitted which will default to an HTTP Listener on port 8080. A `gateway route` can be added using the `gateway.addGatewayRoute()` method. +The listeners field can be omitted which will default to an HTTP Listener on port 8080. +A gateway route can be added using the `gateway.addGatewayRoute()` method. ## Adding a Gateway Route -A `gateway route` is attached to a virtual gateway and routes traffic to an existing virtual service. If a route matches a request, it can distribute traffic to a target virtual service. +A _gateway route_ is attached to a virtual gateway and routes traffic to an existing virtual service. +If a route matches a request, it can distribute traffic to a target virtual service. -For `HTTP` based routes, the `match` field can be used to match on a route prefix. By default, an `HTTP` based route will match on `/`. All matches must start with a leading `/`. +For HTTP based routes, the match field can be used to match on a route prefix. +By default, an HTTP based route will match on `/`. All matches must start with a leading `/`. ```typescript gateway.addGatewayRoute('gateway-route-http', { @@ -287,7 +294,8 @@ gateway.addGatewayRoute('gateway-route-http', { }); ``` -For `GRPC` based routes, the `match` field can be used to match on service names. You cannot omit the field, and must specify a match for these routes. +For GRPC based routes, the match field can be used to match on service names. +You cannot omit the field, and must specify a match for these routes. ```typescript gateway.addGatewayRoute('gateway-route-grpc', { @@ -300,7 +308,7 @@ gateway.addGatewayRoute('gateway-route-grpc', { }); ``` -Multiple gateway routes may also be added at route incoming traffic to different `virtual services`. +Multiple gateway routes may also be added at route incoming traffic to different virtual services. ```typescript // Add an HTTP Route @@ -329,4 +337,4 @@ gateway.addGatewayRoutes([ ]); ``` -The `id` field is the CloudFormation Logical ID for the new resource, and props are the configuration for the new route you are creating. +The id field is the CloudFormation Logical ID for the new resource, and props are the configuration for the new route you are creating. From 9cd6eb62dc28dd5a1dfc17ba859acba972793a56 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 13:28:29 -0700 Subject: [PATCH 26/34] Addresses PR comments --- .../aws-appmesh/lib/gateway-route-spec.ts | 83 ++++---- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 8 +- packages/@aws-cdk/aws-appmesh/package.json | 4 +- .../aws-appmesh/test/test.gateway-route.ts | 93 +++++---- .../aws-appmesh/test/test.virtual-gateway.ts | 190 +++++++++--------- 5 files changed, 182 insertions(+), 196 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index 78dd4ae6c54f5..72af46d79e445 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -3,6 +3,47 @@ import { CfnGatewayRoute } from './appmesh.generated'; import { Protocol } from './shared-interfaces'; import { IVirtualService } from './virtual-service'; +/** + * The criterion for determining a request match for this GatewayRoute + */ +export interface HttpGatewayRouteMatch { + /** + * Specifies the path to match requests with. + * This parameter must always start with /, which by itself matches all requests to the virtual service name. + * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local + * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. + * + */ + readonly prefixPath: string; +} + +/** + * The criterion for determining a request match for this GatewayRoute + */ +export interface GrpcGatewayRouteMatch { + /** + * The fully qualified domain name for the service to match from the request + */ + readonly serviceName: string; +} + +/** + * Properties specific for HTTP Based GatewayRoutes + */ +export interface HttpRouteSpecProps { + /** + * The criterion for determining a request match for this GatewayRoute + * + * @default - matches on '/' + */ + readonly match?: HttpGatewayRouteMatch; + + /** + * The VirtualService this GatewayRoute directs traffic to + */ + readonly routeTarget: IVirtualService; +} + /** * Properties specific for a GRPC GatewayRoute */ @@ -89,6 +130,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { * @default - matches on '/' */ readonly match?: HttpGatewayRouteMatch; + /** * The VirtualService this GatewayRoute directs traffic to */ @@ -165,44 +207,3 @@ class GrpcGatewayRouteSpec extends GatewayRouteSpec { }; } } - -/** - * The criterion for determining a request match for this GatewayRoute - */ -export interface HttpGatewayRouteMatch { - /** - * Specifies the path to match requests with. - * This parameter must always start with /, which by itself matches all requests to the virtual service name. - * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local - * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. - * - */ - readonly prefixPath: string; -} - -/** - * The criterion for determining a request match for this GatewayRoute - */ -export interface GrpcGatewayRouteMatch { - /** - * The fully qualified domain name for the service to match from the request - */ - readonly serviceName: string; -} - -/** - * Properties specific for HTTP Based GatewayRoutes - */ -export interface HttpRouteSpecProps { - /** - * The criterion for determining a request match for this GatewayRoute - * - * @default - matches on '/' - */ - readonly match?: HttpGatewayRouteMatch; - - /** - * The VirtualService this GatewayRoute directs traffic to - */ - readonly routeTarget: IVirtualService; -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index ba27032b15624..a5fb9ad8664b8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -100,15 +100,15 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { }); this.virtualGateway = props.virtualGateway; - const routeSpec = props.routeSpec.bind(this); + const routeSpecConfig = props.routeSpec.bind(this); const gatewayRoute = new CfnGatewayRoute(this, 'Resource', { gatewayRouteName: this.physicalName, meshName: props.virtualGateway.mesh.meshName, spec: { - httpRoute: routeSpec.httpSpecConfig, - http2Route: routeSpec.http2SpecConfig, - grpcRoute: routeSpec.grpcSpecConfig, + httpRoute: routeSpecConfig.httpSpecConfig, + http2Route: routeSpecConfig.http2SpecConfig, + grpcRoute: routeSpecConfig.grpcSpecConfig, }, virtualGatewayName: this.virtualGateway.virtualGatewayName, }); diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index 82136acaa9fb0..d82208df0cd2e 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -108,7 +108,6 @@ "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualNode", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualRouter", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualService", - "construct-ctor-props-type:@aws-cdk/aws-appmesh.GatewayRoute", "from-signature:@aws-cdk/aws-appmesh.GatewayRoute.fromGatewayRouteName", "from-signature:@aws-cdk/aws-appmesh.Route.fromRouteName", "from-signature:@aws-cdk/aws-appmesh.VirtualGateway.fromVirtualGatewayName", @@ -175,8 +174,7 @@ "props-default-doc:@aws-cdk/aws-appmesh.VirtualRouterAttributes.virtualRouterName", "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP", "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.HTTP2", - "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.GRPC", - "no-unused-type:@aws-cdk/aws-appmesh.GatewayRouteProps" + "docs-public-apis:@aws-cdk/aws-appmesh.Protocol.GRPC" ] }, "stability": "experimental", diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index fa64febe7c2a0..a715e58e55d87 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -33,29 +33,28 @@ export = { }); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/', - }, + }, + Match: { + Prefix: '/', }, }, - }), - ); + }, + })); test.done(); }, + 'should be able to add multiple routes'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -102,52 +101,49 @@ export = { ]); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/', - }, + }, + Match: { + Prefix: '/', }, }, - }), - ); + }, + })); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route2', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['vs2BB8859F6', 'VirtualServiceName'], - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-route2', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs2BB8859F6', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/echo', - }, + }, + Match: { + Prefix: '/echo', }, }, - }), - ); + }, + })); test.done(); }, + 'should throw an exception if you start an http prefix match not with a /'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -167,6 +163,7 @@ export = { test.done(); }, }, + 'Can export and import GatewayRoutes and perform actions'(test: Test) { const app = new cdk.App(); // GIVEN diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 5782fecba6a98..277c37d21f1f8 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -30,21 +30,19 @@ export = { }); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::VirtualGateway', { - Spec: { - Listeners: [ - { - PortMapping: { - Port: 8080, - Protocol: appmesh.Protocol.HTTP, - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + PortMapping: { + Port: 8080, + Protocol: appmesh.Protocol.HTTP, }, - ], - }, - VirtualGatewayName: 'testGateway', - }), - ); + }, + ], + }, + VirtualGatewayName: 'testGateway', + })); // THEN expect(stack).to( @@ -69,10 +67,10 @@ export = { ], }, VirtualGatewayName: 'gateway2', - }), - ); + })); test.done(); }, + 'should have expected features'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -94,39 +92,38 @@ export = { }); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::VirtualGateway', { - Spec: { - Listeners: [ - { - HealthCheck: { - HealthyThreshold: 2, - IntervalMillis: 5000, - Port: 80, - Protocol: appmesh.Protocol.GRPC, - TimeoutMillis: 2000, - UnhealthyThreshold: 2, - }, - PortMapping: { - Port: 80, - Protocol: appmesh.Protocol.GRPC, - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 5000, + Port: 80, + Protocol: appmesh.Protocol.GRPC, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, }, - ], - Logging: { - AccessLog: { - File: { - Path: '/dev/stdout', - }, + PortMapping: { + Port: 80, + Protocol: appmesh.Protocol.GRPC, + }, + }, + ], + Logging: { + AccessLog: { + File: { + Path: '/dev/stdout', }, }, }, - VirtualGatewayName: 'test-gateway', - }), - ); + }, + VirtualGatewayName: 'test-gateway', + })); test.done(); }, }, + 'When adding listeners to a VirtualGateway ': { 'should throw an exception if you attempt to add more than 1 listener'(test: Test) { @@ -149,6 +146,7 @@ export = { test.done(); }, }, + 'When adding a gateway route to existing VirtualGateway ': { 'should create gateway route resource'(test: Test) { // GIVEN @@ -174,29 +172,28 @@ export = { }); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/', - }, + }, + Match: { + Prefix: '/', }, }, - }), - ); + }, + })); test.done(); }, + 'should create multiple gateway route resources'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -238,51 +235,48 @@ export = { ]); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/', - }, + }, + Match: { + Prefix: '/', }, }, - }), - ); - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route-2', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], - }, + }, + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route-2', + Spec: { + HttpRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], }, }, }, - Match: { - Prefix: '/2', - }, + }, + Match: { + Prefix: '/2', }, }, - }), - ); + }, + })); test.done(); }, }, + 'When adding gateway routes to a VirtualGateway with existing gateway routes': { 'should add gateway routes and not overwite'(test: Test) { // GIVEN @@ -309,16 +303,12 @@ export = { }), }); // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route', - }), - ); - expect(stack).to( - haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route-2', - }), - ); + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route', + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'test-gateway-route-2', + })); test.done(); }, }, From 14df61ea89a03d958334658974246a8a0a8b23c1 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 13:47:10 -0700 Subject: [PATCH 27/34] bind method for gateway listeners no longer returns raw cfn spec --- .../aws-appmesh/lib/virtual-gateway.ts | 70 ++++++++++++------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 4ad5f95cb475f..0e8ca813ff432 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -84,6 +84,16 @@ export interface GrpcGatewayListenerProps { readonly healthCheck?: HealthCheck; } +/** + * Properties for a VirtualGateway listener + */ +export interface VirtualGatewayListenerConfig { + /** + * Single listener config for a VirtualGateway + */ + readonly listener: CfnVirtualGateway.VirtualGatewayListenerProperty, +} + /** * Represents the properties needed to define listeners for a VirtualGateway */ @@ -113,7 +123,7 @@ export abstract class VirtualGatewayListener { * Called when the GatewayListener type is initialized. Can be used to enforce * mutual exclusivity */ - public abstract bind(scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty; + public abstract bind(scope: cdk.Construct): VirtualGatewayListenerConfig; } /** @@ -144,16 +154,18 @@ class HttpGatewayListener extends VirtualGatewayListener { * Called when the GatewayListener type is initialized. Can be used to enforce * mutual exclusivity */ - public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { return { - portMapping: { - port: this.port, - protocol: Protocol.HTTP, + listener: { + portMapping: { + port: this.port, + protocol: Protocol.HTTP, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.HTTP, + }), }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: Protocol.HTTP, - }), }; } } @@ -186,16 +198,18 @@ class Http2GatewayListener extends VirtualGatewayListener { * Called when the GatewayListener type is initialized. Can be used to enforce * mutual exclusivity */ - public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { return { - portMapping: { - port: this.port, - protocol: Protocol.HTTP2, + listener: { + portMapping: { + port: this.port, + protocol: Protocol.HTTP2, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.HTTP2, + }), }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: Protocol.HTTP2, - }), }; } } @@ -228,16 +242,18 @@ class GrpcGatewayListener extends VirtualGatewayListener { * Called when the GatewayListener type is initialized. Can be used to enforce * mutual exclusivity */ - public bind(_scope: cdk.Construct): CfnVirtualGateway.VirtualGatewayListenerProperty { + public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { return { - portMapping: { - port: this.port, - protocol: Protocol.GRPC, + listener: { + portMapping: { + port: this.port, + protocol: Protocol.GRPC, + }, + healthCheck: renderHealthCheck(this.healthCheck, { + port: this.port, + protocol: Protocol.GRPC, + }), }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: Protocol.GRPC, - }), }; } } @@ -309,7 +325,7 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa */ public abstract readonly mesh: IMesh; - protected readonly listeners = new Array(); + protected readonly listeners = new Array(); protected readonly routes = new Array(); /** @@ -408,7 +424,7 @@ export class VirtualGateway extends VirtualGatewayBase { virtualGatewayName: this.physicalName, meshName: this.mesh.meshName, spec: { - listeners: this.listeners, + listeners: this.listeners.map(listener => listener.listener), logging: accessLogging !== undefined ? { accessLog: accessLogging.virtualGatewayAccessLog, } : undefined, From b30faafd55ab6f10026e59aa726261db952d35f4 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 15:10:36 -0700 Subject: [PATCH 28/34] uses inheritance for http2 gateway listener and removes fromName() methods for vgw/gwr --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 14 +- .../aws-appmesh/lib/virtual-gateway.ts | 121 ++++--------- .../aws-appmesh/test/test.gateway-route.ts | 95 +--------- .../aws-appmesh/test/test.virtual-gateway.ts | 168 ++++++------------ 4 files changed, 94 insertions(+), 304 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index a5fb9ad8664b8..02e0f49c89149 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -70,15 +70,6 @@ export class GatewayRoute extends cdk.Resource implements IGatewayRoute { return new ImportedGatewayRoute(scope, id, { gatewayRouteArn }); } - /** - * Import an existing GatewayRoute given its name - */ - public static fromGatewayRouteName( - scope: Construct, id: string, meshName: string, virtualGatewayName: string, gatewayRouteName: string): IGatewayRoute { - const virtualGateway = VirtualGateway.fromVirtualGatewayName(scope, 'VirtualGateway', meshName, virtualGatewayName); - return new ImportedGatewayRoute(scope, id, { meshName, virtualGateway, gatewayRouteName }); - } - /** * The name of the GatewayRoute */ @@ -169,12 +160,9 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { constructor(scope: Construct, id: string, props: GatewayRouteAttributes) { super(scope, id); if (props.gatewayRouteArn) { - const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - const virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); - this.virtualGateway = VirtualGateway.fromVirtualGatewayName(this, 'virtualGateway', meshName, virtualGatewayName); + this.virtualGateway = VirtualGateway.fromVirtualGatewayArn(this, 'virtualGateway', props.gatewayRouteArn); } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { this.gatewayRouteName = props.gatewayRouteName; this.virtualGateway = props.virtualGateway; diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 0e8ca813ff432..6116a0c426d2a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -101,24 +101,29 @@ export abstract class VirtualGatewayListener { /** * Returns an HTTP Listener for a VirtualGateway */ - public static httpGatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { + public static httpGatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { return new HttpGatewayListener(props); } /** * Returns an HTTP2 Listener for a VirtualGateway */ - public static http2GatewayListener(props?: HttpGatewayListenerProps): VirtualGatewayListener { + public static http2GatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { return new Http2GatewayListener(props); } /** * Returns a GRPC Listener for a VirtualGateway */ - public static grpcGatewayListener(props?: GrpcGatewayListenerProps): VirtualGatewayListener { + public static grpcGatewayListener(props: GrpcGatewayListenerProps = {}): VirtualGatewayListener { return new GrpcGatewayListener(props); } + /** + * Protocol the listener implements + */ + protected abstract protocol: Protocol; + /** * Called when the GatewayListener type is initialized. Can be used to enforce * mutual exclusivity @@ -143,11 +148,16 @@ class HttpGatewayListener extends VirtualGatewayListener { * @default - no healthcheck */ readonly healthCheck?: HealthCheck; - constructor(props?: HttpGatewayListenerProps) { + + /** + * Protocol the listener implements + */ + protected protocol: Protocol = Protocol.HTTP; + + constructor(props: HttpGatewayListenerProps = {}) { super(); - const checkedProps = props ?? {}; - this.port = checkedProps.port ? checkedProps.port : 8080; - this.healthCheck = checkedProps.healthCheck; + this.port = props.port ? props.port : 8080; + this.healthCheck = props.healthCheck; } /** @@ -159,11 +169,11 @@ class HttpGatewayListener extends VirtualGatewayListener { listener: { portMapping: { port: this.port, - protocol: Protocol.HTTP, + protocol: this.protocol, }, healthCheck: renderHealthCheck(this.healthCheck, { port: this.port, - protocol: Protocol.HTTP, + protocol: this.protocol, }), }, }; @@ -173,50 +183,16 @@ class HttpGatewayListener extends VirtualGatewayListener { /** * Represents the properties needed to define an HTTP2 Listener for a VirtualGateway */ -class Http2GatewayListener extends VirtualGatewayListener { - /** - * Port to listen for connections on - * - * @default - 8080 - */ - readonly port: number; - - /** - * Health checking strategy upstream nodes should use when communicating with the listener - * - * @default - no healthcheck - */ - readonly healthCheck?: HealthCheck; - constructor(props?: HttpGatewayListenerProps) { - super(); - const checkedProps = props ?? {}; - this.port = checkedProps.port ? checkedProps.port : 8080; - this.healthCheck = checkedProps.healthCheck; - } - - /** - * Called when the GatewayListener type is initialized. Can be used to enforce - * mutual exclusivity - */ - public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { - return { - listener: { - portMapping: { - port: this.port, - protocol: Protocol.HTTP2, - }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: Protocol.HTTP2, - }), - }, - }; +class Http2GatewayListener extends HttpGatewayListener { + constructor(props: HttpGatewayListenerProps = {}) { + super(props); + this.protocol = Protocol.HTTP2; } } /** -* Represents the properties needed to define a GRPC Listener for Virtual Gateway -*/ + * Represents the properties needed to define a GRPC Listener for Virtual Gateway + */ class GrpcGatewayListener extends VirtualGatewayListener { /** * Port to listen for connections on @@ -231,11 +207,16 @@ class GrpcGatewayListener extends VirtualGatewayListener { * @default - no healthcheck */ readonly healthCheck?: HealthCheck; - constructor(props?: HttpGatewayListenerProps) { + + /** + * Protocol the listener implements + */ + protected protocol: Protocol = Protocol.GRPC; + + constructor(props: HttpGatewayListenerProps = {}) { super(); - const checkedProps = props ?? {}; - this.port = checkedProps.port ? checkedProps.port : 8080; - this.healthCheck = checkedProps.healthCheck; + this.port = props.port ? props.port : 8080; + this.healthCheck = props.healthCheck; } /** @@ -294,21 +275,6 @@ export interface VirtualGatewayProps extends VirtualGatewayBaseProps { readonly mesh: IMesh; } -/** - * Properties required to add multiple GatewayRoutes to a VirtualGateway - */ -export interface AddGatewayRouteProps { - /** - * CloudFormation Logical ID - */ - readonly id: string; - - /** - * Properties to create a GatewayRoute - */ - readonly props: GatewayRouteBaseProps; -} - abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGateway { /** * Name of the VirtualGateway @@ -350,13 +316,6 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa this.listeners.push(listener.bind(this)); } - /** - * Utility method to add a list of GatewayRoutes to the VirtualGateway - */ - public addGatewayRoutes(props: AddGatewayRouteProps[]): GatewayRoute[] { - return props.map(idx => this.addGatewayRoute(idx.id, idx.props)); - } - /** * Utility method to add a new GatewayRoute to the VirtualGateway */ @@ -384,16 +343,6 @@ export class VirtualGateway extends VirtualGatewayBase { return new ImportedVirtualGateway(scope, id, { virtualGatewayArn }); } - /** - * Import an existing VirtualGateway given its name - */ - public static fromVirtualGatewayName(scope: Construct, id: string, meshName: string, virtualGatewayName: string): IVirtualGateway { - return new ImportedVirtualGateway(scope, id, { - meshName, - virtualGatewayName, - }); - } - /** * The name of the VirtualGateway */ @@ -456,7 +405,7 @@ function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVir const healthCheck: CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty = { healthyThreshold: hc.healthyThreshold || 2, intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min - path: hc.path || (protocol === Protocol.HTTP ? '/' : undefined), + path: hc.path || ((protocol === Protocol.HTTP || protocol === Protocol.HTTP2) ? '/' : undefined), port: hc.port || pm.port, protocol: hc.protocol || pm.protocol, timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index a715e58e55d87..81de005b48157 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -55,95 +55,6 @@ export = { test.done(); }, - 'should be able to add multiple routes'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - - // WHEN - const mesh = new appmesh.Mesh(stack, 'mesh', { - meshName: 'test-mesh', - }); - - const virtualGateway = new appmesh.VirtualGateway(stack, 'gateway-1', { - listeners: [appmesh.VirtualGatewayListener.httpGatewayListener()], - mesh: mesh, - }); - - const virtualService = new appmesh.VirtualService(stack, 'vs-1', { - mesh: mesh, - }); - const virtualService2 = new appmesh.VirtualService(stack, 'vs-2', { - mesh: mesh, - }); - - virtualGateway.addGatewayRoutes([ - { - id: 'gateway-route', - props: { - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService, - }), - gatewayRouteName: 'gateway-route', - }, - }, - { - id: 'gateway-route2', - props: { - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService2, - match: { - prefixPath: '/echo', - }, - }), - gatewayRouteName: 'gateway-route2', - }, - }, - ]); - - // THEN - expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], - }, - }, - }, - }, - Match: { - Prefix: '/', - }, - }, - }, - })); - - // THEN - expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route2', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['vs2BB8859F6', 'VirtualServiceName'], - }, - }, - }, - }, - Match: { - Prefix: '/echo', - }, - }, - }, - })); - test.done(); - }, - 'should throw an exception if you start an http prefix match not with a /'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -172,13 +83,9 @@ export = { }); // WHEN - const gatewayRoute = appmesh.GatewayRoute.fromGatewayRouteName(stack, 'importedGatewayRoute', 'test-mesh', 'test-gateway', 'test-gateway-route'); - // THEN - test.equal(gatewayRoute.gatewayRouteName, 'test-gateway-route'); - test.equal(gatewayRoute.virtualGateway.virtualGatewayName, 'test-gateway'); - test.equal(gatewayRoute.virtualGateway.mesh.meshName, 'test-mesh'); const gatewayRoute2 = appmesh.GatewayRoute.fromGatewayRouteArn( stack, 'importedGatewayRoute2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/test-mesh/virtualGateway/test-gateway/gatewayRoute/test-gateway-route'); + // THEN test.equal(gatewayRoute2.gatewayRouteName, 'test-gateway-route'); test.equal(gatewayRoute2.virtualGateway.virtualGatewayName, 'test-gateway'); test.equal(gatewayRoute2.virtualGateway.mesh.meshName, 'test-mesh'); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 277c37d21f1f8..506dc94d93f0b 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -19,7 +19,7 @@ export = { mesh: mesh, }); - new appmesh.VirtualGateway(stack, 'gateway2', { + new appmesh.VirtualGateway(stack, 'httpGateway', { mesh: mesh, listeners: [appmesh.VirtualGatewayListener.httpGatewayListener({ port: 443, @@ -29,6 +29,16 @@ export = { })], }); + new appmesh.VirtualGateway(stack, 'http2Gateway', { + mesh: mesh, + listeners: [appmesh.VirtualGatewayListener.http2GatewayListener({ + port: 443, + healthCheck: { + interval: cdk.Duration.seconds(10), + }, + })], + }); + // THEN expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualGateway', { Spec: { @@ -44,30 +54,51 @@ export = { VirtualGatewayName: 'testGateway', })); - // THEN - expect(stack).to( - haveResourceLike('AWS::AppMesh::VirtualGateway', { - Spec: { - Listeners: [ - { - HealthCheck: { - HealthyThreshold: 2, - IntervalMillis: 10000, - Port: 443, - Protocol: appmesh.Protocol.HTTP, - TimeoutMillis: 2000, - UnhealthyThreshold: 2, - Path: '/', - }, - PortMapping: { - Port: 443, - Protocol: appmesh.Protocol.HTTP, - }, + expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 10000, + Port: 443, + Protocol: appmesh.Protocol.HTTP, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, + Path: '/', }, - ], - }, - VirtualGatewayName: 'gateway2', - })); + PortMapping: { + Port: 443, + Protocol: appmesh.Protocol.HTTP, + }, + }, + ], + }, + VirtualGatewayName: 'httpGateway', + })); + + expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualGateway', { + Spec: { + Listeners: [ + { + HealthCheck: { + HealthyThreshold: 2, + IntervalMillis: 10000, + Port: 443, + Protocol: appmesh.Protocol.HTTP2, + TimeoutMillis: 2000, + UnhealthyThreshold: 2, + Path: '/', + }, + PortMapping: { + Port: 443, + Protocol: appmesh.Protocol.HTTP2, + }, + }, + ], + }, + VirtualGatewayName: 'http2Gateway', + })); test.done(); }, @@ -193,88 +224,6 @@ export = { })); test.done(); }, - - 'should create multiple gateway route resources'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - - // WHEN - const mesh = new appmesh.Mesh(stack, 'mesh', { - meshName: 'test-mesh', - }); - - const virtualGateway = new appmesh.VirtualGateway(stack, 'testGateway', { - virtualGatewayName: 'test-gateway', - mesh: mesh, - }); - - const virtualService = mesh.addVirtualService('virtualService', {}); - - virtualGateway.addGatewayRoutes([ - { - id: 'testGatewayRoute', - props: { - gatewayRouteName: 'test-gateway-route', - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService, - }), - }, - }, - { - id: 'testGatewayRoute2', - props: { - gatewayRouteName: 'test-gateway-route-2', - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService, - match: { - prefixPath: '/2', - }, - }), - }, - }, - ]); - - // THEN - expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], - }, - }, - }, - }, - Match: { - Prefix: '/', - }, - }, - }, - })); - expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'test-gateway-route-2', - Spec: { - HttpRoute: { - Action: { - Target: { - VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['meshvirtualService93460D43', 'VirtualServiceName'], - }, - }, - }, - }, - Match: { - Prefix: '/2', - }, - }, - }, - })); - test.done(); - }, }, 'When adding gateway routes to a VirtualGateway with existing gateway routes': { @@ -321,13 +270,10 @@ export = { }); // WHEN - const virtualGateway = appmesh.VirtualGateway.fromVirtualGatewayName(stack, 'importedGateway', 'testMesh', 'test-gateway'); - // THEN - test.equal(virtualGateway.mesh.meshName, 'testMesh'); - test.equal(virtualGateway.virtualGatewayName, 'test-gateway'); - // Nothing to do with imported Virtual Gateways yet const virtualGateway2 = appmesh.VirtualGateway.fromVirtualGatewayArn( stack, 'importedGateway2', 'arn:aws:appmesh:us-east-1:123456789012:mesh/testMesh/virtualGateway/test-gateway'); + + // THEN test.equal(virtualGateway2.mesh.meshName, 'testMesh'); test.equal(virtualGateway2.virtualGatewayName, 'test-gateway'); test.done(); From a86c57b6541ab02476a14022e261e10bcb1e73bf Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Mon, 26 Oct 2020 15:33:16 -0700 Subject: [PATCH 29/34] add protocol variant tests for gateway routes --- .../aws-appmesh/test/test.gateway-route.ts | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts index 81de005b48157..842e553dfa20c 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.gateway-route.ts @@ -22,19 +22,37 @@ export = { const virtualService = new appmesh.VirtualService(stack, 'vs-1', { mesh: mesh, + virtualServiceName: 'target.local', }); // Add an HTTP Route - virtualGateway.addGatewayRoute('gateway-route', { + virtualGateway.addGatewayRoute('gateway-http-route', { routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ routeTarget: virtualService, }), - gatewayRouteName: 'gateway-route', + gatewayRouteName: 'gateway-http-route', + }); + + virtualGateway.addGatewayRoute('gateway-http2-route', { + routeSpec: appmesh.GatewayRouteSpec.http2RouteSpec({ + routeTarget: virtualService, + }), + gatewayRouteName: 'gateway-http2-route', + }); + + virtualGateway.addGatewayRoute('gateway-grpc-route', { + routeSpec: appmesh.GatewayRouteSpec.grpcRouteSpec({ + routeTarget: virtualService, + match: { + serviceName: virtualService.virtualServiceName, + }, + }), + gatewayRouteName: 'gateway-grpc-route', }); // THEN expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { - GatewayRouteName: 'gateway-route', + GatewayRouteName: 'gateway-http-route', Spec: { HttpRoute: { Action: { @@ -52,6 +70,46 @@ export = { }, }, })); + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-http2-route', + Spec: { + Http2Route: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + Prefix: '/', + }, + }, + }, + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::GatewayRoute', { + GatewayRouteName: 'gateway-grpc-route', + Spec: { + GrpcRoute: { + Action: { + Target: { + VirtualService: { + VirtualServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], + }, + }, + }, + }, + Match: { + ServiceName: { + 'Fn::GetAtt': ['vs1732C2645', 'VirtualServiceName'], + }, + }, + }, + }, + })); test.done(); }, From 4e432c2ae231b23f2c71159527fc3f457e2aca64 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 27 Oct 2020 21:15:59 -0700 Subject: [PATCH 30/34] addresses smaller changes from review --- packages/@aws-cdk/aws-appmesh/README.md | 31 ------------------- .../aws-appmesh/lib/gateway-route-spec.ts | 4 +-- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 10 +----- .../aws-appmesh/lib/virtual-gateway.ts | 23 ++------------ packages/@aws-cdk/aws-appmesh/package.json | 2 -- 5 files changed, 5 insertions(+), 65 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 12f762ca5d603..e38a8ea49a9e9 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -307,34 +307,3 @@ gateway.addGatewayRoute('gateway-route-grpc', { }), }); ``` - -Multiple gateway routes may also be added at route incoming traffic to different virtual services. - -```typescript -// Add an HTTP Route -gateway.addGatewayRoutes([ - { - id: 'gateway-route', - props: { - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService, - }), - gatewayRouteName: 'gateway-route', - }, - }, - { - id: 'gateway-route2', - props: { - routeSpec: appmesh.GatewayRouteSpec.httpRouteSpec({ - routeTarget: virtualService2, - match: { - prefixPath: '/echo', - }, - }), - gatewayRouteName: 'gateway-route2', - }, - }, -]); -``` - -The id field is the CloudFormation Logical ID for the new resource, and props are the configuration for the new route you are creating. diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts index 72af46d79e445..b75890ece1453 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route-spec.ts @@ -12,7 +12,6 @@ export interface HttpGatewayRouteMatch { * This parameter must always start with /, which by itself matches all requests to the virtual service name. * You can also match for path-based routing of requests. For example, if your virtual service name is my-service.local * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. - * */ readonly prefixPath: string; } @@ -147,6 +146,7 @@ class HttpGatewayRouteSpec extends GatewayRouteSpec { this.routeType = protocol; this.match = props.match; } + public bind(_scope: cdk.Construct): GatewayRouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { @@ -177,8 +177,8 @@ class GrpcGatewayRouteSpec extends GatewayRouteSpec { * * @default - no default */ - readonly match: GrpcGatewayRouteMatch; + /** * The VirtualService this GatewayRoute directs traffic to */ diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 02e0f49c89149..2c7868a081428 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -163,16 +163,8 @@ class ImportedGatewayRoute extends cdk.Resource implements IGatewayRoute { this.gatewayRouteArn = props.gatewayRouteArn; this.gatewayRouteName = cdk.Fn.select(4, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.gatewayRouteArn).resourceName!)); this.virtualGateway = VirtualGateway.fromVirtualGatewayArn(this, 'virtualGateway', props.gatewayRouteArn); - } else if (props.gatewayRouteName && props.meshName && props.virtualGateway) { - this.gatewayRouteName = props.gatewayRouteName; - this.virtualGateway = props.virtualGateway; - this.gatewayRouteArn = cdk.Stack.of(this).formatArn({ - service: 'appmesh', - resource: `mesh/${props.meshName}/virtualGateway/${props.virtualGateway.virtualGatewayName}/gatewayRoute`, - resourceName: this.gatewayRouteName, - }); } else { - throw new Error('Need either arn or three names'); + throw new Error('Need gatewayRouteArn'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 6116a0c426d2a..16653801990a1 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -463,32 +463,13 @@ class ImportedVirtualGateway extends VirtualGatewayBase { constructor(scope: Construct, id: string, props: VirtualGatewayAttributes) { super(scope, id); - - if (props.mesh) { - this.mesh = props.mesh; - } else if (props.meshName) { - if (props.mesh) { - throw new Error('Supply either \'mesh\' or \'meshName\', but not both'); - } - this.mesh = Mesh.fromMeshName(this, 'Mesh', props.meshName); - } else if (props.virtualGatewayArn) { + if (props.virtualGatewayArn) { const meshName = cdk.Fn.select(0, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); this.mesh = Mesh.fromMeshName(this, 'Mesh', meshName); - } else { - throw new Error('Supply either \'mesh\' or \'meshName\' or \'virtualGatewayArn\''); - } - if (props.virtualGatewayArn) { this.virtualGatewayArn = props.virtualGatewayArn; this.virtualGatewayName = cdk.Fn.select(2, cdk.Fn.split('/', cdk.Stack.of(scope).parseArn(props.virtualGatewayArn).resourceName!)); - } else if (props.virtualGatewayName && props.meshName) { - this.virtualGatewayName = props.virtualGatewayName; - this.virtualGatewayArn = cdk.Stack.of(this).formatArn({ - service: 'appmesh', - resource: `mesh/${props.meshName}/virtualGateway`, - resourceName: this.virtualGatewayName, - }); } else { - throw new Error('Need either arn or both names'); + throw new Error('Need virtualGatewayArn'); } } } diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index d82208df0cd2e..36e6ba7906a97 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -108,9 +108,7 @@ "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualNode", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualRouter", "construct-ctor-props-type:@aws-cdk/aws-appmesh.ImportedVirtualService", - "from-signature:@aws-cdk/aws-appmesh.GatewayRoute.fromGatewayRouteName", "from-signature:@aws-cdk/aws-appmesh.Route.fromRouteName", - "from-signature:@aws-cdk/aws-appmesh.VirtualGateway.fromVirtualGatewayName", "from-signature:@aws-cdk/aws-appmesh.VirtualNode.fromVirtualNodeName", "from-signature:@aws-cdk/aws-appmesh.VirtualRouter.fromVirtualRouterName", "from-signature:@aws-cdk/aws-appmesh.VirtualService.fromVirtualServiceName", From e550d53b0f3126751cc3b994fcc0f99973cb4476 Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 27 Oct 2020 21:30:17 -0700 Subject: [PATCH 31/34] moves gateway listener classes to separate file and adds health check rendering as part of abstract class --- packages/@aws-cdk/aws-appmesh/lib/index.ts | 1 + .../lib/virtual-gateway-listener.ts | 227 ++++++++++++++++++ .../aws-appmesh/lib/virtual-gateway.ts | 225 +---------------- 3 files changed, 230 insertions(+), 223 deletions(-) create mode 100644 packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts diff --git a/packages/@aws-cdk/aws-appmesh/lib/index.ts b/packages/@aws-cdk/aws-appmesh/lib/index.ts index e05d27ba4d76c..69cdfd8d2e3c6 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/index.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/index.ts @@ -7,5 +7,6 @@ export * from './virtual-node'; export * from './virtual-router'; export * from './virtual-service'; export * from './virtual-gateway'; +export * from './virtual-gateway-listener'; export * from './gateway-route'; export * from './gateway-route-spec'; diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts new file mode 100644 index 0000000000000..6a40b39e1e19a --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts @@ -0,0 +1,227 @@ +import * as cdk from '@aws-cdk/core'; +import { CfnVirtualGateway } from './appmesh.generated'; +import { validateHealthChecks } from './private/utils'; +import { HealthCheck, Protocol } from './shared-interfaces'; + +/** + * Represents the properties needed to define HTTP Listeners for a VirtualGateway + */ +export interface HttpGatewayListenerProps { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port?: number + + /** + * The health check information for the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; +} + +/** + * Represents the properties needed to define GRPC Listeners for a VirtualGateway + */ +export interface GrpcGatewayListenerProps { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port?: number + + /** + * The health check information for the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; +} + +/** + * Properties for a VirtualGateway listener + */ +export interface VirtualGatewayListenerConfig { + /** + * Single listener config for a VirtualGateway + */ + readonly listener: CfnVirtualGateway.VirtualGatewayListenerProperty, +} + +/** + * Represents the properties needed to define listeners for a VirtualGateway + */ +export abstract class VirtualGatewayListener { + /** + * Returns an HTTP Listener for a VirtualGateway + */ + public static httpGatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { + return new HttpGatewayListener(props); + } + + /** + * Returns an HTTP2 Listener for a VirtualGateway + */ + public static http2GatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { + return new Http2GatewayListener(props); + } + + /** + * Returns a GRPC Listener for a VirtualGateway + */ + public static grpcGatewayListener(props: GrpcGatewayListenerProps = {}): VirtualGatewayListener { + return new GrpcGatewayListener(props); + } + + /** + * Protocol the listener implements + */ + protected abstract protocol: Protocol; + + /** + * Port to listen for connections on + */ + protected abstract port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + */ + protected abstract healthCheck?: HealthCheck; + + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ + public abstract bind(scope: cdk.Construct): VirtualGatewayListenerConfig; + + protected renderHealthCheck(hc: HealthCheck): CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty | undefined { + if (hc.protocol === Protocol.TCP) { + throw new Error('The path property cannot be set with Protocol.TCP'); + } + + if (hc.protocol === Protocol.GRPC && hc.path) { + throw new Error('The path property cannot be set with Protocol.GRPC'); + } + + const protocol = hc.protocol? hc.protocol : this.protocol; + + const healthCheck: CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty = { + healthyThreshold: hc.healthyThreshold || 2, + intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min + path: hc.path || ((protocol === Protocol.HTTP || protocol === Protocol.HTTP2) ? '/' : undefined), + port: hc.port || this.port, + protocol: hc.protocol || this.protocol, + timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), + unhealthyThreshold: hc.unhealthyThreshold || 2, + }; + + validateHealthChecks(healthCheck); + + return healthCheck; + } +} + +/** + * Represents the properties needed to define an HTTP Listener for a VirtualGateway + */ +class HttpGatewayListener extends VirtualGatewayListener { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; + + /** + * Protocol the listener implements + */ + protected protocol: Protocol = Protocol.HTTP; + + constructor(props: HttpGatewayListenerProps = {}) { + super(); + this.port = props.port ? props.port : 8080; + this.healthCheck = props.healthCheck; + } + + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ + public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { + return { + listener: { + portMapping: { + port: this.port, + protocol: this.protocol, + }, + healthCheck: this.healthCheck ? this.renderHealthCheck(this.healthCheck): undefined, + }, + }; + } +} + +/** +* Represents the properties needed to define an HTTP2 Listener for a VirtualGateway +*/ +class Http2GatewayListener extends HttpGatewayListener { + constructor(props: HttpGatewayListenerProps = {}) { + super(props); + this.protocol = Protocol.HTTP2; + } +} + +/** + * Represents the properties needed to define a GRPC Listener for Virtual Gateway + */ +class GrpcGatewayListener extends VirtualGatewayListener { + /** + * Port to listen for connections on + * + * @default - 8080 + */ + readonly port: number; + + /** + * Health checking strategy upstream nodes should use when communicating with the listener + * + * @default - no healthcheck + */ + readonly healthCheck?: HealthCheck; + + /** + * Protocol the listener implements + */ + protected protocol: Protocol = Protocol.GRPC; + + constructor(props: HttpGatewayListenerProps = {}) { + super(); + this.port = props.port ? props.port : 8080; + this.healthCheck = props.healthCheck; + } + + /** + * Called when the GatewayListener type is initialized. Can be used to enforce + * mutual exclusivity + */ + public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { + return { + listener: { + portMapping: { + port: this.port, + protocol: Protocol.GRPC, + }, + healthCheck: this.healthCheck? this.renderHealthCheck(this.healthCheck): undefined, + }, + }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 16653801990a1..4b06a26e6a200 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -4,8 +4,8 @@ import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; -import { validateHealthChecks } from './private/utils'; -import { AccessLog, HealthCheck, PortMapping, Protocol } from './shared-interfaces'; +import { AccessLog } from './shared-interfaces'; +import { VirtualGatewayListener, VirtualGatewayListenerConfig } from './virtual-gateway-listener'; /** * Interface which all Virtual Gateway based classes must implement @@ -46,199 +46,6 @@ export interface IVirtualGateway extends cdk.IResource { addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; } -/** - * Represents the properties needed to define HTTP Listeners for a VirtualGateway - */ -export interface HttpGatewayListenerProps { - /** - * Port to listen for connections on - * - * @default - 8080 - */ - readonly port?: number - - /** - * The health check information for the listener - * - * @default - no healthcheck - */ - readonly healthCheck?: HealthCheck; -} - -/** - * Represents the properties needed to define GRPC Listeners for a VirtualGateway - */ -export interface GrpcGatewayListenerProps { - /** - * Port to listen for connections on - * - * @default - 8080 - */ - readonly port?: number - - /** - * The health check information for the listener - * - * @default - no healthcheck - */ - readonly healthCheck?: HealthCheck; -} - -/** - * Properties for a VirtualGateway listener - */ -export interface VirtualGatewayListenerConfig { - /** - * Single listener config for a VirtualGateway - */ - readonly listener: CfnVirtualGateway.VirtualGatewayListenerProperty, -} - -/** - * Represents the properties needed to define listeners for a VirtualGateway - */ -export abstract class VirtualGatewayListener { - /** - * Returns an HTTP Listener for a VirtualGateway - */ - public static httpGatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { - return new HttpGatewayListener(props); - } - - /** - * Returns an HTTP2 Listener for a VirtualGateway - */ - public static http2GatewayListener(props: HttpGatewayListenerProps = {}): VirtualGatewayListener { - return new Http2GatewayListener(props); - } - - /** - * Returns a GRPC Listener for a VirtualGateway - */ - public static grpcGatewayListener(props: GrpcGatewayListenerProps = {}): VirtualGatewayListener { - return new GrpcGatewayListener(props); - } - - /** - * Protocol the listener implements - */ - protected abstract protocol: Protocol; - - /** - * Called when the GatewayListener type is initialized. Can be used to enforce - * mutual exclusivity - */ - public abstract bind(scope: cdk.Construct): VirtualGatewayListenerConfig; -} - -/** - * Represents the properties needed to define an HTTP Listener for a VirtualGateway - */ -class HttpGatewayListener extends VirtualGatewayListener { - /** - * Port to listen for connections on - * - * @default - 8080 - */ - readonly port: number; - - /** - * Health checking strategy upstream nodes should use when communicating with the listener - * - * @default - no healthcheck - */ - readonly healthCheck?: HealthCheck; - - /** - * Protocol the listener implements - */ - protected protocol: Protocol = Protocol.HTTP; - - constructor(props: HttpGatewayListenerProps = {}) { - super(); - this.port = props.port ? props.port : 8080; - this.healthCheck = props.healthCheck; - } - - /** - * Called when the GatewayListener type is initialized. Can be used to enforce - * mutual exclusivity - */ - public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { - return { - listener: { - portMapping: { - port: this.port, - protocol: this.protocol, - }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: this.protocol, - }), - }, - }; - } -} - -/** -* Represents the properties needed to define an HTTP2 Listener for a VirtualGateway -*/ -class Http2GatewayListener extends HttpGatewayListener { - constructor(props: HttpGatewayListenerProps = {}) { - super(props); - this.protocol = Protocol.HTTP2; - } -} - -/** - * Represents the properties needed to define a GRPC Listener for Virtual Gateway - */ -class GrpcGatewayListener extends VirtualGatewayListener { - /** - * Port to listen for connections on - * - * @default - 8080 - */ - readonly port: number; - - /** - * Health checking strategy upstream nodes should use when communicating with the listener - * - * @default - no healthcheck - */ - readonly healthCheck?: HealthCheck; - - /** - * Protocol the listener implements - */ - protected protocol: Protocol = Protocol.GRPC; - - constructor(props: HttpGatewayListenerProps = {}) { - super(); - this.port = props.port ? props.port : 8080; - this.healthCheck = props.healthCheck; - } - - /** - * Called when the GatewayListener type is initialized. Can be used to enforce - * mutual exclusivity - */ - public bind(_scope: cdk.Construct): VirtualGatewayListenerConfig { - return { - listener: { - portMapping: { - port: this.port, - protocol: Protocol.GRPC, - }, - healthCheck: renderHealthCheck(this.healthCheck, { - port: this.port, - protocol: Protocol.GRPC, - }), - }, - }; - } -} - /** * Basic configuration properties for a VirtualGateway */ @@ -389,34 +196,6 @@ export class VirtualGateway extends VirtualGatewayBase { } } -function renderHealthCheck(hc: HealthCheck | undefined, pm: PortMapping): CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty | undefined { - if (hc === undefined) { return undefined; } - - if (hc.protocol === Protocol.TCP && hc.path) { - throw new Error('The path property cannot be set with Protocol.TCP'); - } - - if (hc.protocol === Protocol.GRPC && hc.path) { - throw new Error('The path property cannot be set with Protocol.GRPC'); - } - - const protocol = hc.protocol? hc.protocol : pm.protocol; - - const healthCheck: CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty = { - healthyThreshold: hc.healthyThreshold || 2, - intervalMillis: (hc.interval || cdk.Duration.seconds(5)).toMilliseconds(), // min - path: hc.path || ((protocol === Protocol.HTTP || protocol === Protocol.HTTP2) ? '/' : undefined), - port: hc.port || pm.port, - protocol: hc.protocol || pm.protocol, - timeoutMillis: (hc.timeout || cdk.Duration.seconds(2)).toMilliseconds(), - unhealthyThreshold: hc.unhealthyThreshold || 2, - }; - - validateHealthChecks(healthCheck); - - return healthCheck; -} - /** * Unterface with properties necessary to import a reusable VirtualGateway */ From 7813bbcde9d3b1a8c957374953e552c123ef7aab Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 27 Oct 2020 21:43:42 -0700 Subject: [PATCH 32/34] removes addListener() methods from virtual gateways --- .../aws-appmesh/lib/virtual-gateway.ts | 44 +++++-------------- .../aws-appmesh/test/test.virtual-gateway.ts | 16 +++---- 2 files changed, 18 insertions(+), 42 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 4b06a26e6a200..bf5975a0dc5ee 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -30,16 +30,6 @@ export interface IVirtualGateway extends cdk.IResource { */ readonly mesh: IMesh; - /** - * Utility method to add a list of listeners to this VirtualGateway - */ - addListeners(listeners: VirtualGatewayListener[]): void; - - /** - * Utility method to add a single listener to this VirtualGateway - */ - addListener(listener: VirtualGatewayListener): void; - /** * Utility method to add a new GatewayRoute to the VirtualGateway */ @@ -101,28 +91,6 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa protected readonly listeners = new Array(); protected readonly routes = new Array(); - /** - * Utility method to add a list of listeners to this VirtualGateway - */ - public addListeners(listeners: VirtualGatewayListener[]) { - if (listeners.length + this.listeners.length > 1) { - throw new Error('VirtualGateway may have at most one listener'); - } - for (const listener of listeners) { - this.addListener(listener); - } - } - - /** - * Utility method to add a single listener to this VirtualGateway - */ - public addListener(listener: VirtualGatewayListener) { - if (this.listeners.length > 0) { - throw new Error('VirtualGateway may have at most one listener'); - } - this.listeners.push(listener.bind(this)); - } - /** * Utility method to add a new GatewayRoute to the VirtualGateway */ @@ -172,8 +140,16 @@ export class VirtualGateway extends VirtualGatewayBase { this.mesh = props.mesh; - // Use listener default of http listener port 8080 if no listener is defined - this.addListeners(props.listeners ? props.listeners : [VirtualGatewayListener.httpGatewayListener()]); + if (!props.listeners) { + // Use listener default of http listener port 8080 if no listener is defined + this.listeners.push(VirtualGatewayListener.httpGatewayListener().bind(this)); + } else { + if (props.listeners.length != 1) { + throw new Error('VirtualGateway may have at most one listener'); + } + props.listeners.forEach(listener => this.listeners.push(listener.bind(this))); + } + const accessLogging = props.accessLog?.bind(this); const node = new CfnVirtualGateway(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 506dc94d93f0b..21c313eb27861 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -164,15 +164,15 @@ export = { const mesh = new appmesh.Mesh(stack, 'mesh', { meshName: 'test-mesh', }); - const virtualGateway = new appmesh.VirtualGateway(stack, 'testGateway', { - virtualGatewayName: 'test-gateway', - mesh: mesh, - }); test.throws(() => { - virtualGateway.addListener(appmesh.VirtualGatewayListener.httpGatewayListener()); - }, /VirtualGateway may have at most one listener/); - test.throws(() => { - virtualGateway.addListeners([appmesh.VirtualGatewayListener.httpGatewayListener()]); + new appmesh.VirtualGateway(stack, 'testGateway', { + virtualGatewayName: 'test-gateway', + mesh: mesh, + listeners: [ + appmesh.VirtualGatewayListener.httpGatewayListener(), + appmesh.VirtualGatewayListener.http2GatewayListener(), + ], + }); }, /VirtualGateway may have at most one listener/); test.done(); }, From 8670dbd6aa68b817c1918a164c0e9aafc797c99a Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Tue, 27 Oct 2020 21:49:08 -0700 Subject: [PATCH 33/34] removes the ListenerBase interface --- packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 3181072076bfa..39111389b0a03 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -88,9 +88,9 @@ export interface PortMapping { } /** - * Base properties all listeners share + * Represents the properties needed to define healthy and active listeners for nodes */ -export interface ListenerBase { +export interface VirtualNodeListener { /** * Array of PortMappingProps for the listener * @@ -106,11 +106,6 @@ export interface ListenerBase { readonly healthCheck?: HealthCheck; } -/** - * Represents the properties needed to define healthy and active listeners for nodes - */ -export interface VirtualNodeListener extends ListenerBase {} - /** * All Properties for Envoy Access logs for mesh endpoints */ From 5efc19523c00b432f5b3571b78aced7e9f4bd50e Mon Sep 17 00:00:00 2001 From: Dominic Fezzie Date: Thu, 29 Oct 2020 12:05:16 -0700 Subject: [PATCH 34/34] remove validation for only having 1 listener on virtual gateways --- .../@aws-cdk/aws-appmesh/lib/gateway-route.ts | 2 -- .../lib/virtual-gateway-listener.ts | 2 +- .../aws-appmesh/lib/virtual-gateway.ts | 10 +++----- .../aws-appmesh/test/test.virtual-gateway.ts | 23 ------------------- 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts index 2c7868a081428..cc00c0f632ac3 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/gateway-route.ts @@ -24,8 +24,6 @@ export interface IGatewayRoute extends cdk.IResource { /** * The VirtualGateway the GatewayRoute belongs to - * - * @attribute */ readonly virtualGateway: IVirtualGateway; } diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts index 6a40b39e1e19a..a690c022f8c32 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts @@ -99,7 +99,7 @@ export abstract class VirtualGatewayListener { protected renderHealthCheck(hc: HealthCheck): CfnVirtualGateway.VirtualGatewayHealthCheckPolicyProperty | undefined { if (hc.protocol === Protocol.TCP) { - throw new Error('The path property cannot be set with Protocol.TCP'); + throw new Error('TCP health checks are not permitted for gateway listeners'); } if (hc.protocol === Protocol.GRPC && hc.path) { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index bf5975a0dc5ee..51950f82dbb13 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -1,6 +1,6 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnGatewayRoute, CfnVirtualGateway } from './appmesh.generated'; +import { CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; @@ -88,9 +88,6 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa */ public abstract readonly mesh: IMesh; - protected readonly listeners = new Array(); - protected readonly routes = new Array(); - /** * Utility method to add a new GatewayRoute to the VirtualGateway */ @@ -133,6 +130,8 @@ export class VirtualGateway extends VirtualGatewayBase { */ public readonly mesh: IMesh; + protected readonly listeners = new Array(); + constructor(scope: Construct, id: string, props: VirtualGatewayProps) { super(scope, id, { physicalName: props.virtualGatewayName || cdk.Lazy.stringValue({ produce: () => this.node.uniqueId }), @@ -144,9 +143,6 @@ export class VirtualGateway extends VirtualGatewayBase { // Use listener default of http listener port 8080 if no listener is defined this.listeners.push(VirtualGatewayListener.httpGatewayListener().bind(this)); } else { - if (props.listeners.length != 1) { - throw new Error('VirtualGateway may have at most one listener'); - } props.listeners.forEach(listener => this.listeners.push(listener.bind(this))); } diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 21c313eb27861..1dd75b938ab04 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -155,29 +155,6 @@ export = { }, }, - 'When adding listeners to a VirtualGateway ': { - 'should throw an exception if you attempt to add more than 1 listener'(test: Test) { - - // GIVEN - const stack = new cdk.Stack(); - - const mesh = new appmesh.Mesh(stack, 'mesh', { - meshName: 'test-mesh', - }); - test.throws(() => { - new appmesh.VirtualGateway(stack, 'testGateway', { - virtualGatewayName: 'test-gateway', - mesh: mesh, - listeners: [ - appmesh.VirtualGatewayListener.httpGatewayListener(), - appmesh.VirtualGatewayListener.http2GatewayListener(), - ], - }); - }, /VirtualGateway may have at most one listener/); - test.done(); - }, - }, - 'When adding a gateway route to existing VirtualGateway ': { 'should create gateway route resource'(test: Test) { // GIVEN