Skip to content

Commit

Permalink
feat(appmesh): change VirtualService provider to a union-like class (#…
Browse files Browse the repository at this point in the history
…11978)

Fixes #9490

BREAKING CHANGE: the properties virtualRouter and virtualNode of VirtualServiceProps have been replaced with the union-like class VirtualServiceProvider 
* **appmesh**: the method `addVirtualService` has been removed from `IMesh`

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
Dominic Fezzie authored Jan 28, 2021
1 parent 2f6521a commit dfc765a
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,7 @@ export class AppMeshExtension extends ServiceExtension {
// Now create a virtual service. Relationship goes like this:
// virtual service -> virtual router -> virtual node
this.virtualService = new appmesh.VirtualService(this.scope, `${this.parentService.id}-virtual-service`, {
mesh: this.mesh,
virtualRouter: this.virtualRouter,
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualRouter(this.virtualRouter),
virtualServiceName: serviceName,
});
}
Expand Down
14 changes: 6 additions & 8 deletions packages/@aws-cdk/aws-appmesh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,21 @@ When creating a virtual service:
Adding a virtual router as the provider:

```ts
mesh.addVirtualService('virtual-service', {
virtualRouter: router,
virtualServiceName: 'my-service.default.svc.cluster.local',
new appmesh.VirtualService('virtual-service', {
virtualServiceName: 'my-service.default.svc.cluster.local', // optional
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualRouter(router),
});
```

Adding a virtual node as the provider:

```ts
mesh.addVirtualService('virtual-service', {
virtualNode: node,
virtualServiceName: `my-service.default.svc.cluster.local`,
new appmesh.VirtualService('virtual-service', {
virtualServiceName: `my-service.default.svc.cluster.local`, // optional
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualNode(node),
});
```

**Note** that only one must of `virtualNode` or `virtualRouter` must be chosen.

## Adding a VirtualNode

A `virtual node` acts as a logical pointer to a particular task group, such as an Amazon ECS service or a Kubernetes deployment.
Expand Down
16 changes: 0 additions & 16 deletions packages/@aws-cdk/aws-appmesh/lib/mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ 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';

/**
* A utility enum defined for the egressFilter type property, the default of DROP_ALL,
Expand Down Expand Up @@ -46,11 +45,6 @@ export interface IMesh extends cdk.IResource {
*/
addVirtualRouter(id: string, props?: VirtualRouterBaseProps): VirtualRouter;

/**
* Adds a VirtualService with the given id
*/
addVirtualService(id: string, props?: VirtualServiceBaseProps): VirtualService;

/**
* Adds a VirtualNode to the Mesh
*/
Expand Down Expand Up @@ -86,16 +80,6 @@ abstract class MeshBase extends cdk.Resource implements IMesh {
});
}

/**
* Adds a VirtualService with the given id
*/
public addVirtualService(id: string, props: VirtualServiceBaseProps = {}): VirtualService {
return new VirtualService(this, id, {
...props,
mesh: this,
});
}

/**
* Adds a VirtualNode to the Mesh
*/
Expand Down
157 changes: 101 additions & 56 deletions packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export interface IVirtualService extends cdk.IResource {
}

/**
* The base properties which all classes in VirtualService will inherit from
* The properties applied to the VirtualService being defined
*/
export interface VirtualServiceBaseProps {
export interface VirtualServiceProps {
/**
* The name of the VirtualService.
*
Expand All @@ -50,36 +50,17 @@ export interface VirtualServiceBaseProps {
*/
readonly virtualServiceName?: string;

/**
* The VirtualRouter which the VirtualService uses as provider
*
* @default - At most one of virtualRouter and virtualNode is allowed.
*/
readonly virtualRouter?: IVirtualRouter;

/**
* The VirtualNode attached to the virtual service
*
* @default - At most one of virtualRouter and virtualNode is allowed.
*/
readonly virtualNode?: IVirtualNode;

/**
* Client policy for this Virtual Service
*
* @default - none
*/
readonly clientPolicy?: ClientPolicy;
}

/**
* The properties applied to the VirtualService being define
*/
export interface VirtualServiceProps extends VirtualServiceBaseProps {
/**
* The Mesh which the VirtualService belongs to
* The VirtualNode or VirtualRouter which the VirtualService uses as its provider
*/
readonly mesh: IMesh;
readonly virtualServiceProvider: VirtualServiceProvider;
}

/**
Expand Down Expand Up @@ -135,59 +116,35 @@ export class VirtualService extends cdk.Resource implements IVirtualService {

public readonly clientPolicy?: ClientPolicy;

private readonly virtualServiceProvider?: CfnVirtualService.VirtualServiceProviderProperty;

constructor(scope: Construct, id: string, props: VirtualServiceProps) {
super(scope, id, {
physicalName: props.virtualServiceName || cdk.Lazy.string({ produce: () => cdk.Names.uniqueId(this) }),
});

if (props.virtualNode && props.virtualRouter) {
throw new Error('Must provide only one of virtualNode or virtualRouter for the provider');
}

this.mesh = props.mesh;
this.clientPolicy = props.clientPolicy;

// Check which provider to use node or router (or neither)
if (props.virtualRouter) {
this.virtualServiceProvider = this.addVirtualRouter(props.virtualRouter.virtualRouterName);
}
if (props.virtualNode) {
this.virtualServiceProvider = this.addVirtualNode(props.virtualNode.virtualNodeName);
}
const providerConfig = props.virtualServiceProvider.bind(this);
this.mesh = providerConfig.mesh;

const svc = new CfnVirtualService(this, 'Resource', {
meshName: this.mesh.meshName,
virtualServiceName: this.physicalName,
spec: {
provider: this.virtualServiceProvider,
provider: providerConfig.virtualNodeProvider || providerConfig.virtualRouterProvider
? {
virtualNode: providerConfig.virtualNodeProvider,
virtualRouter: providerConfig.virtualRouterProvider,
}
: undefined,
},
});

this.virtualServiceName = this.getResourceNameAttribute(svc.attrVirtualServiceName);
this.virtualServiceArn = this.getResourceArnAttribute(svc.ref, {
service: 'appmesh',
resource: `mesh/${props.mesh.meshName}/virtualService`,
resource: `mesh/${this.mesh.meshName}/virtualService`,
resourceName: this.physicalName,
});
}

private addVirtualRouter(name: string): CfnVirtualService.VirtualServiceProviderProperty {
return {
virtualRouter: {
virtualRouterName: name,
},
};
}

private addVirtualNode(name: string): CfnVirtualService.VirtualServiceProviderProperty {
return {
virtualNode: {
virtualNodeName: name,
},
};
}
}

/**
Expand All @@ -211,3 +168,91 @@ export interface VirtualServiceAttributes {
*/
readonly clientPolicy?: ClientPolicy;
}

/**
* Properties for a VirtualService provider
*/
export interface VirtualServiceProviderConfig {
/**
* Virtual Node based provider
*
* @default - none
*/
readonly virtualNodeProvider?: CfnVirtualService.VirtualNodeServiceProviderProperty;

/**
* Virtual Router based provider
*
* @default - none
*/
readonly virtualRouterProvider?: CfnVirtualService.VirtualRouterServiceProviderProperty;

/**
* Mesh the Provider is using
*
* @default - none
*/
readonly mesh: IMesh;
}

/**
* Represents the properties needed to define the provider for a VirtualService
*/
export abstract class VirtualServiceProvider {
/**
* Returns a VirtualNode based Provider for a VirtualService
*/
public static virtualNode(virtualNode: IVirtualNode): VirtualServiceProvider {
return new VirtualServiceProviderImpl(virtualNode, undefined);
}

/**
* Returns a VirtualRouter based Provider for a VirtualService
*/
public static virtualRouter(virtualRouter: IVirtualRouter): VirtualServiceProvider {
return new VirtualServiceProviderImpl(undefined, virtualRouter);
}

/**
* Returns an Empty Provider for a VirtualService. This provides no routing capabilities
* and should only be used as a placeholder
*/
public static none(mesh: IMesh): VirtualServiceProvider {
return new VirtualServiceProviderImpl(undefined, undefined, mesh);
}

/**
* Enforces mutual exclusivity for VirtualService provider types.
*/
public abstract bind(_construct: Construct): VirtualServiceProviderConfig;
}

class VirtualServiceProviderImpl extends VirtualServiceProvider {
private readonly virtualNode?: IVirtualNode;
private readonly virtualRouter?: IVirtualRouter;
private readonly mesh: IMesh;

constructor(virtualNode?: IVirtualNode, virtualRouter?: IVirtualRouter, mesh?: IMesh) {
super();
this.virtualNode = virtualNode;
this.virtualRouter = virtualRouter;
const providedMesh = this.virtualNode?.mesh ?? this.virtualRouter?.mesh ?? mesh!;
this.mesh = providedMesh;
}

public bind(_construct: Construct): VirtualServiceProviderConfig {
return {
mesh: this.mesh,
virtualNodeProvider: this.virtualNode
? {
virtualNodeName: this.virtualNode.virtualNodeName,
}
: undefined,
virtualRouterProvider: this.virtualRouter
? {
virtualRouterName: this.virtualRouter.virtualRouterName,
}
: undefined,
};
}
}
Loading

0 comments on commit dfc765a

Please sign in to comment.