Skip to content

Commit

Permalink
fix(appmesh): allow a Virtual Node have as a backend a Virtual Servic…
Browse files Browse the repository at this point in the history
…e whose provider is that Node (#18265)

Addresses a circular dependency issue between Virtual Nodes and Virtual Services that works for Virtual Services created with a defined `virtualServiceName` and a randomly generated name. 

One such example of this problem was a case where a Virtual Node had a backend that is a Virtual Service whose provider was given as the same Virtual Node. This led to the Virtual Node being dependent on the creation of the Virtual Service, and the Virtual Service being dependent on the creation of the Virtual Node.

Fixes #17322

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
wplucinsky authored Jan 6, 2022
1 parent 9da0780 commit 272b6b1
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3185,22 +3185,12 @@
"Backends": [
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"namevirtualservice3DDDDF1E",
"VirtualServiceName"
]
}
"VirtualServiceName": "name.production"
}
},
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"greetingvirtualservice60AD3AD9",
"VirtualServiceName"
]
}
"VirtualServiceName": "greeting.production"
}
}
],
Expand Down
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-appmesh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,24 @@ The `backends` property can be added with `node.addBackend()`. In the example, w

The `backendDefaults` property is added to the node while creating the virtual node. These are the virtual node's default settings for all backends.

The `VirtualNode.addBackend()` method is especially useful if you want to create a circular traffic flow by having a Virtual Service as a backend whose provider is that same Virtual Node:

```ts
declare const mesh: appmesh.Mesh;

const node = new appmesh.VirtualNode(this, 'node', {
mesh,
serviceDiscovery: appmesh.ServiceDiscovery.dns('node'),
});

const virtualService = new appmesh.VirtualService(this, 'service-1', {
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualNode(node),
virtualServiceName: 'service1.domain.local',
});

node.addBackend(appmesh.Backend.virtualService(virtualService));
```

### Adding TLS to a listener

The `tls` property specifies TLS configuration when creating a listener for a virtual node or a virtual gateway.
Expand Down
9 changes: 8 additions & 1 deletion packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,14 @@ class VirtualServiceBackend extends Backend {
return {
virtualServiceBackend: {
virtualService: {
virtualServiceName: this.virtualService.virtualServiceName,
/**
* We want to use the name of the Virtual Service here directly instead of
* a `{ 'Fn::GetAtt' }` CFN expression. This avoids a circular dependency in
* the case where this Virtual Node is the Virtual Service's provider.
*/
virtualServiceName: cdk.Token.isUnresolved(this.virtualService.virtualServiceName)
? (this.virtualService as any).physicalName
: this.virtualService.virtualServiceName,
clientPolicy: this.tlsClientPolicy
? {
tls: renderTlsClientPolicy(scope, this.tlsClientPolicy),
Expand Down
28 changes: 4 additions & 24 deletions packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -1040,22 +1040,12 @@
"Backends": [
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"service6D174F83",
"VirtualServiceName"
]
}
"VirtualServiceName": "service1.domain.local"
}
},
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"service27C65CF7D",
"VirtualServiceName"
]
}
"VirtualServiceName": "service2.domain.local"
}
}
],
Expand Down Expand Up @@ -1111,12 +1101,7 @@
"Backends": [
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"service3859EB104",
"VirtualServiceName"
]
}
"VirtualServiceName": "service3.domain.local"
}
}
],
Expand Down Expand Up @@ -1241,12 +1226,7 @@
"Backends": [
{
"VirtualService": {
"VirtualServiceName": {
"Fn::GetAtt": [
"service4983B61EE",
"VirtualServiceName"
]
}
"VirtualServiceName": "service4.domain.local"
}
}
],
Expand Down
4 changes: 1 addition & 3 deletions packages/@aws-cdk/aws-appmesh/test/mesh.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,7 @@ describe('mesh', () => {
Backends: [
{
VirtualService: {
VirtualServiceName: {
'Fn::GetAtt': ['service1A48078CF', 'VirtualServiceName'],
},
VirtualServiceName: 'service1.domain.local',
},
},
],
Expand Down
81 changes: 70 additions & 11 deletions packages/@aws-cdk/aws-appmesh/test/virtual-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,18 @@ describe('virtual node', () => {
Backends: [
{
VirtualService: {
VirtualServiceName: {
'Fn::GetAtt': ['service1A48078CF', 'VirtualServiceName'],
},
VirtualServiceName: 'service1.domain.local',
},
},
{
VirtualService: {
VirtualServiceName: {
'Fn::GetAtt': ['service27C65CF7D', 'VirtualServiceName'],
},
VirtualServiceName: 'service2.domain.local',
},
},
],
},
MeshOwner: ABSENT,
});


});
});

Expand Down Expand Up @@ -458,9 +452,7 @@ describe('virtual node', () => {
Backends: [
{
VirtualService: {
VirtualServiceName: {
'Fn::GetAtt': ['service1A48078CF', 'VirtualServiceName'],
},
VirtualServiceName: 'service1.domain.local',
ClientPolicy: {
TLS: {
Ports: [8080, 8081],
Expand All @@ -478,8 +470,75 @@ describe('virtual node', () => {
],
},
});
});

test('you can add a Virtual Service as a backend to a Virtual Node which is the provider for that Virtual Service', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const mesh = new appmesh.Mesh(stack, 'mesh', {
meshName: 'test-mesh',
});

const node = new appmesh.VirtualNode(stack, 'test-node', {
mesh,
serviceDiscovery: appmesh.ServiceDiscovery.dns('test'),
});

const myVirtualService = new appmesh.VirtualService(stack, 'service-1', {
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualNode(node),
virtualServiceName: 'service1.domain.local',
});

node.addBackend(appmesh.Backend.virtualService(myVirtualService));

// THEN
expect(stack).toHaveResourceLike('AWS::AppMesh::VirtualNode', {
Spec: {
Backends: [
{
VirtualService: {
VirtualServiceName: 'service1.domain.local',
},
},
],
},
});
});

test('you can add a Virtual Service with an automated name as a backend to a Virtual Node which is the provider for that Virtual Service, ', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const mesh = new appmesh.Mesh(stack, 'mesh', {
meshName: 'test-mesh',
});

const node = new appmesh.VirtualNode(stack, 'test-node', {
mesh,
serviceDiscovery: appmesh.ServiceDiscovery.dns('test'),
});

const myVirtualService = new appmesh.VirtualService(stack, 'service-1', {
virtualServiceProvider: appmesh.VirtualServiceProvider.virtualNode(node),
});

node.addBackend(appmesh.Backend.virtualService(myVirtualService));

// THEN
expect(stack).toHaveResourceLike('AWS::AppMesh::VirtualNode', {
Spec: {
Backends: [
{
VirtualService: {
VirtualServiceName: 'service1',
},
},
],
},
});
});
});

Expand Down

0 comments on commit 272b6b1

Please sign in to comment.