Skip to content

Commit d3f4284

Browse files
authored
fix(appmesh): Move Client Policy from Virtual Service to backend structure (#12943)
@sshver: > Client Policies are inherently not related to the Virtual Service. It should be thought of as the client (the VN) telling envoy what connections they want to allow to the server (the Virtual Service). The server shouldn't be the one to define what policies are used to enforce connections with itself. ## Description of changes I refactored the client policy from Virtual Service to a separate backend structure. This mirrors how our API is designed. Also ran `npm run lint -- --fix` and removed some comments to fix lint warnings. ```ts /* Old backend defaults */ backendsDefaultClientPolicy: appmesh.ClientPolicy.fileTrust({ certificateChain: 'path-to-certificate', }), /* result of this PR */ backendDefaults: { clientPolicy: appmesh.ClientPolicy.fileTrust({ certificateChain: 'path-to-certificate', }), }, ``` ```ts /* Old Virtual Service with client policy */ const service1 = new appmesh.VirtualService(stack, 'service-1', { virtualServiceName: 'service1.domain.local', virtualServiceProvider: appmesh.VirtualServiceProvider.none(mesh), clientPolicy: appmesh.ClientPolicy.fileTrust({ certificateChain: 'path-to-certificate', ports: [8080, 8081], }), }); /* result of this PR; client policy is defined in the Virtual Node */ const service1 = new appmesh.VirtualService(stack, 'service-1', { virtualServiceName: 'service1.domain.local', virtualServiceProvider: appmesh.VirtualServiceProvider.none(mesh), }); const node = new appmesh.VirtualNode(stack, 'test-node', { mesh, serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), }); node.addBackend({ virtualService: service1, clientPolicy: appmesh.ClientPolicy.fileTrust({ certificateChain: 'path-to-certificate', ports: [8080, 8081], }), }); ``` BREAKING CHANGE: Backend, backend default and Virtual Service client policies structures are being altered * **appmesh**: you must use the backend default interface to define backend defaults in `VirtualGateway`. The property name also changed from `backendsDefaultClientPolicy` to `backendDefaults` * **appmesh**: you must use the backend default interface to define backend defaults in `VirtualNode`, (the property name also changed from `backendsDefaultClientPolicy` to `backendDefaults`), and the `Backend` class to define a backend * **appmesh**: you can no longer attach a client policy to a `VirtualService` Resolves #11996 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent b71efd9 commit d3f4284

File tree

12 files changed

+153
-98
lines changed

12 files changed

+153
-98
lines changed

packages/@aws-cdk-containers/ecs-service-extensions/lib/extensions/appmesh.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ export class AppMeshExtension extends ServiceExtension {
347347
// Next update the app mesh config so that the local Envoy
348348
// proxy on this service knows how to route traffic to
349349
// nodes from the other service.
350-
this.virtualNode.addBackend(otherAppMesh.virtualService);
350+
this.virtualNode.addBackend(appmesh.Backend.virtualService(otherAppMesh.virtualService));
351351
}
352352

353353
private routeSpec(weightedTargets: appmesh.WeightedTarget[], serviceName: string): appmesh.RouteSpec {

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

+14-10
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,11 @@ const node = new VirtualNode(this, 'node', {
186186
idle: cdk.Duration.seconds(5),
187187
},
188188
})],
189-
backendsDefaultClientPolicy: appmesh.ClientPolicy.fileTrust({
190-
certificateChain: '/keys/local_cert_chain.pem',
191-
}),
189+
backendDefaults: {
190+
clientPolicy: appmesh.ClientPolicy.fileTrust({
191+
certificateChain: '/keys/local_cert_chain.pem',
192+
}),
193+
},
192194
accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'),
193195
});
194196

@@ -230,14 +232,14 @@ const virtualService = new appmesh.VirtualService(stack, 'service-1', {
230232
}),
231233
});
232234

233-
node.addBackend(virtualService);
235+
node.addBackend(appmesh.Backend.virtualService(virtualService));
234236
```
235237

236238
The `listeners` property can be left blank and added later with the `node.addListener()` method. The `healthcheck` and `timeout` properties are optional but if specifying a listener, the `port` must be added.
237239

238240
The `backends` property can be added with `node.addBackend()`. We define a virtual service and add it to the virtual node to allow egress traffic to other node.
239241

240-
The `backendsDefaultClientPolicy` property are added to the node while creating the virtual node. These are virtual node's service backends client policy defaults.
242+
The `backendDefaults` property are added to the node while creating the virtual node. These are virtual node's default settings for all backends.
241243

242244
## Adding TLS to a listener
243245

@@ -437,10 +439,12 @@ const gateway = new appmesh.VirtualGateway(stack, 'gateway', {
437439
interval: cdk.Duration.seconds(10),
438440
},
439441
})],
440-
backendsDefaultClientPolicy: appmesh.ClientPolicy.acmTrust({
441-
certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)],
442-
ports: [8080, 8081],
443-
}),
442+
backendDefaults: {
443+
clientPolicy: appmesh.ClientPolicy.acmTrust({
444+
certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)],
445+
ports: [8080, 8081],
446+
}),
447+
},
444448
accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'),
445449
virtualGatewayName: 'virtualGateway',
446450
});
@@ -464,7 +468,7 @@ const gateway = mesh.addVirtualGateway('gateway', {
464468
The listeners field can be omitted which will default to an HTTP Listener on port 8080.
465469
A gateway route can be added using the `gateway.addGatewayRoute()` method.
466470

467-
The `backendsDefaultClientPolicy` property are added to the node while creating the virtual gateway. These are virtual gateway's service backends client policy defaults.
471+
The `backendDefaults` property is added to the node while creating the virtual gateway. These are virtual gateway's default settings for all backends.
468472

469473
## Adding a Gateway Route
470474

packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts

+79
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as cdk from '@aws-cdk/core';
22
import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated';
3+
import { ClientPolicy } from './client-policy';
4+
import { IVirtualService } from './virtual-service';
35

46
// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
57
// eslint-disable-next-line no-duplicate-imports, import/order
@@ -194,3 +196,80 @@ class FileAccessLog extends AccessLog {
194196
}
195197
}
196198

199+
/**
200+
* Represents the properties needed to define backend defaults
201+
*/
202+
export interface BackendDefaults {
203+
/**
204+
* Client policy for backend defaults
205+
*
206+
* @default none
207+
*/
208+
readonly clientPolicy?: ClientPolicy;
209+
}
210+
211+
/**
212+
* Represents the properties needed to define a Virtual Service backend
213+
*/
214+
export interface VirtualServiceBackendOptions {
215+
216+
/**
217+
* Client policy for the backend
218+
*
219+
* @default none
220+
*/
221+
readonly clientPolicy?: ClientPolicy;
222+
}
223+
224+
/**
225+
* Properties for a backend
226+
*/
227+
export interface BackendConfig {
228+
/**
229+
* Config for a Virtual Service backend
230+
*/
231+
readonly virtualServiceBackend: CfnVirtualNode.BackendProperty;
232+
}
233+
234+
235+
/**
236+
* Contains static factory methods to create backends
237+
*/
238+
export abstract class Backend {
239+
/**
240+
* Construct a Virtual Service backend
241+
*/
242+
public static virtualService(virtualService: IVirtualService, props: VirtualServiceBackendOptions = {}): Backend {
243+
return new VirtualServiceBackend(virtualService, props.clientPolicy);
244+
}
245+
246+
/**
247+
* Return backend config
248+
*/
249+
public abstract bind(_scope: Construct): BackendConfig;
250+
}
251+
252+
/**
253+
* Represents the properties needed to define a Virtual Service backend
254+
*/
255+
class VirtualServiceBackend extends Backend {
256+
257+
constructor (private readonly virtualService: IVirtualService,
258+
private readonly clientPolicy: ClientPolicy | undefined) {
259+
super();
260+
}
261+
262+
/**
263+
* Return config for a Virtual Service backend
264+
*/
265+
public bind(_scope: Construct): BackendConfig {
266+
return {
267+
virtualServiceBackend: {
268+
virtualService: {
269+
virtualServiceName: this.virtualService.virtualServiceName,
270+
clientPolicy: this.clientPolicy?.bind(_scope).clientPolicy,
271+
},
272+
},
273+
};
274+
}
275+
}

packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import * as cdk from '@aws-cdk/core';
22
import { Construct } from 'constructs';
33
import { CfnVirtualGateway } from './appmesh.generated';
4-
import { ClientPolicy } from './client-policy';
54
import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route';
65
import { IMesh, Mesh } from './mesh';
7-
import { AccessLog } from './shared-interfaces';
6+
import { AccessLog, BackendDefaults } from './shared-interfaces';
87
import { VirtualGatewayListener, VirtualGatewayListenerConfig } from './virtual-gateway-listener';
98

109
/**
@@ -66,7 +65,7 @@ export interface VirtualGatewayBaseProps {
6665
*
6766
* @default - No Config
6867
*/
69-
readonly backendsDefaultClientPolicy?: ClientPolicy;
68+
readonly backendDefaults?: BackendDefaults;
7069
}
7170

7271
/**
@@ -180,7 +179,11 @@ export class VirtualGateway extends VirtualGatewayBase {
180179
meshName: this.mesh.meshName,
181180
spec: {
182181
listeners: this.listeners.map(listener => listener.listener),
183-
backendDefaults: props.backendsDefaultClientPolicy?.bind(this),
182+
backendDefaults: props.backendDefaults !== undefined
183+
? {
184+
clientPolicy: props.backendDefaults?.clientPolicy?.bind(this).clientPolicy,
185+
}
186+
: undefined,
184187
logging: accessLogging !== undefined ? {
185188
accessLog: accessLogging.virtualGatewayAccessLog,
186189
} : undefined,

packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts

+10-13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import * as cdk from '@aws-cdk/core';
22
import { Construct } from 'constructs';
33
import { CfnVirtualNode } from './appmesh.generated';
4-
import { ClientPolicy } from './client-policy';
54
import { IMesh, Mesh } from './mesh';
65
import { ServiceDiscovery } from './service-discovery';
7-
import { AccessLog } from './shared-interfaces';
6+
import { AccessLog, BackendDefaults, Backend } from './shared-interfaces';
87
import { VirtualNodeListener, VirtualNodeListenerConfig } from './virtual-node-listener';
9-
import { IVirtualService } from './virtual-service';
108

119
/**
1210
* Interface which all VirtualNode based classes must implement
@@ -61,7 +59,7 @@ export interface VirtualNodeBaseProps {
6159
*
6260
* @default - No backends
6361
*/
64-
readonly backends?: IVirtualService[];
62+
readonly backends?: Backend[];
6563

6664
/**
6765
* Initial listener for the virtual node
@@ -82,7 +80,7 @@ export interface VirtualNodeBaseProps {
8280
*
8381
* @default - No Config
8482
*/
85-
readonly backendsDefaultClientPolicy?: ClientPolicy;
83+
readonly backendDefaults?: BackendDefaults;
8684
}
8785

8886
/**
@@ -185,7 +183,11 @@ export class VirtualNode extends VirtualNodeBase {
185183
spec: {
186184
backends: cdk.Lazy.anyValue({ produce: () => this.backends }, { omitEmptyArray: true }),
187185
listeners: cdk.Lazy.anyValue({ produce: () => this.listeners.map(listener => listener.listener) }, { omitEmptyArray: true }),
188-
backendDefaults: props.backendsDefaultClientPolicy?.bind(this),
186+
backendDefaults: props.backendDefaults !== undefined
187+
? {
188+
clientPolicy: props.backendDefaults?.clientPolicy?.bind(this).clientPolicy,
189+
}
190+
: undefined,
189191
serviceDiscovery: {
190192
dns: serviceDiscovery?.dns,
191193
awsCloudMap: serviceDiscovery?.cloudmap,
@@ -214,13 +216,8 @@ export class VirtualNode extends VirtualNodeBase {
214216
/**
215217
* Add a Virtual Services that this node is expected to send outbound traffic to
216218
*/
217-
public addBackend(virtualService: IVirtualService) {
218-
this.backends.push({
219-
virtualService: {
220-
virtualServiceName: virtualService.virtualServiceName,
221-
clientPolicy: virtualService.clientPolicy?.bind(this).clientPolicy,
222-
},
223-
});
219+
public addBackend(backend: Backend) {
220+
this.backends.push(backend.bind(this).virtualServiceBackend);
224221
}
225222
}
226223

packages/@aws-cdk/aws-appmesh/lib/virtual-service.ts

-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as cdk from '@aws-cdk/core';
22
import { Construct } from 'constructs';
33
import { CfnVirtualService } from './appmesh.generated';
4-
import { ClientPolicy } from './client-policy';
54
import { IMesh, Mesh } from './mesh';
65
import { IVirtualNode } from './virtual-node';
76
import { IVirtualRouter } from './virtual-router';
@@ -28,11 +27,6 @@ export interface IVirtualService extends cdk.IResource {
2827
* The Mesh which the VirtualService belongs to
2928
*/
3029
readonly mesh: IMesh;
31-
32-
/**
33-
* Client policy for this Virtual Service
34-
*/
35-
readonly clientPolicy?: ClientPolicy;
3630
}
3731

3832
/**
@@ -50,13 +44,6 @@ export interface VirtualServiceProps {
5044
*/
5145
readonly virtualServiceName?: string;
5246

53-
/**
54-
* Client policy for this Virtual Service
55-
*
56-
* @default - none
57-
*/
58-
readonly clientPolicy?: ClientPolicy;
59-
6047
/**
6148
* The VirtualNode or VirtualRouter which the VirtualService uses as its provider
6249
*/
@@ -90,7 +77,6 @@ export class VirtualService extends cdk.Resource implements IVirtualService {
9077
return new class extends cdk.Resource implements IVirtualService {
9178
readonly virtualServiceName = attrs.virtualServiceName;
9279
readonly mesh = attrs.mesh;
93-
readonly clientPolicy = attrs.clientPolicy;
9480
readonly virtualServiceArn = cdk.Stack.of(this).formatArn({
9581
service: 'appmesh',
9682
resource: `mesh/${attrs.mesh.meshName}/virtualService`,
@@ -114,14 +100,11 @@ export class VirtualService extends cdk.Resource implements IVirtualService {
114100
*/
115101
public readonly mesh: IMesh;
116102

117-
public readonly clientPolicy?: ClientPolicy;
118-
119103
constructor(scope: Construct, id: string, props: VirtualServiceProps) {
120104
super(scope, id, {
121105
physicalName: props.virtualServiceName || cdk.Lazy.string({ produce: () => cdk.Names.uniqueId(this) }),
122106
});
123107

124-
this.clientPolicy = props.clientPolicy;
125108
const providerConfig = props.virtualServiceProvider.bind(this);
126109
this.mesh = providerConfig.mesh;
127110

@@ -160,13 +143,6 @@ export interface VirtualServiceAttributes {
160143
* The Mesh which the VirtualService belongs to
161144
*/
162145
readonly mesh: IMesh;
163-
164-
/**
165-
* Client policy for this Virtual Service
166-
*
167-
* @default - none
168-
*/
169-
readonly clientPolicy?: ClientPolicy;
170146
}
171147

172148
/**

packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts

+19-16
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,15 @@ const node = mesh.addVirtualNode('node', {
3636
path: '/check-path',
3737
},
3838
})],
39-
backends: [
40-
virtualService,
41-
],
39+
backends: [appmesh.Backend.virtualService(virtualService)],
4240
});
4341

44-
node.addBackend(new appmesh.VirtualService(stack, 'service-2', {
45-
virtualServiceName: 'service2.domain.local',
46-
virtualServiceProvider: appmesh.VirtualServiceProvider.none(mesh),
47-
}),
48-
);
42+
node.addBackend(appmesh.Backend.virtualService(
43+
new appmesh.VirtualService(stack, 'service-2', {
44+
virtualServiceName: 'service2.domain.local',
45+
virtualServiceProvider: appmesh.VirtualServiceProvider.none(mesh),
46+
}),
47+
));
4948

5049
router.addRoute('route-1', {
5150
routeSpec: appmesh.RouteSpec.http({
@@ -78,15 +77,17 @@ const node2 = mesh.addVirtualNode('node2', {
7877
unhealthyThreshold: 2,
7978
},
8079
})],
81-
backendsDefaultClientPolicy: appmesh.ClientPolicy.fileTrust({
82-
certificateChain: 'path/to/cert',
83-
}),
84-
backends: [
80+
backendDefaults: {
81+
clientPolicy: appmesh.ClientPolicy.fileTrust({
82+
certificateChain: 'path/to/cert',
83+
}),
84+
},
85+
backends: [appmesh.Backend.virtualService(
8586
new appmesh.VirtualService(stack, 'service-3', {
8687
virtualServiceName: 'service3.domain.local',
8788
virtualServiceProvider: appmesh.VirtualServiceProvider.none(mesh),
8889
}),
89-
],
90+
)],
9091
});
9192

9293
const node3 = mesh.addVirtualNode('node3', {
@@ -102,9 +103,11 @@ const node3 = mesh.addVirtualNode('node3', {
102103
unhealthyThreshold: 2,
103104
},
104105
})],
105-
backendsDefaultClientPolicy: appmesh.ClientPolicy.fileTrust({
106-
certificateChain: 'path-to-certificate',
107-
}),
106+
backendDefaults: {
107+
clientPolicy: appmesh.ClientPolicy.fileTrust({
108+
certificateChain: 'path-to-certificate',
109+
}),
110+
},
108111
accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'),
109112
});
110113

packages/@aws-cdk/aws-appmesh/test/test.health-check.ts

-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ export = {
6666
// THEN
6767
test.doesNotThrow(() => toThrow(min));
6868
test.doesNotThrow(() => toThrow(max));
69-
// falsy, falls back to portMapping.port
70-
// test.throws(() => toThrow(min - 1), /below the minimum threshold/);
7169
test.throws(() => toThrow(max + 1), /above the maximum threshold/);
7270

7371
test.done();

0 commit comments

Comments
 (0)