From a0ecb7140fcffc82cb701823496d85e397791d18 Mon Sep 17 00:00:00 2001 From: wplucinsky Date: Thu, 6 Jan 2022 14:35:25 -0800 Subject: [PATCH] fix(appmesh): allow a Virtual Node have as a backend a Virtual Service 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* --- .../integ.all-service-addons.expected.json | 14 +--- packages/@aws-cdk/aws-appmesh/README.md | 18 +++++ .../aws-appmesh/lib/shared-interfaces.ts | 9 ++- .../aws-appmesh/test/integ.mesh.expected.json | 28 +------ .../@aws-cdk/aws-appmesh/test/mesh.test.ts | 4 +- .../aws-appmesh/test/virtual-node.test.ts | 81 ++++++++++++++++--- 6 files changed, 103 insertions(+), 51 deletions(-) diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.all-service-addons.expected.json b/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.all-service-addons.expected.json index bd9224e586933..04e3fb87689fa 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.all-service-addons.expected.json +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.all-service-addons.expected.json @@ -3185,22 +3185,12 @@ "Backends": [ { "VirtualService": { - "VirtualServiceName": { - "Fn::GetAtt": [ - "namevirtualservice3DDDDF1E", - "VirtualServiceName" - ] - } + "VirtualServiceName": "name.production" } }, { "VirtualService": { - "VirtualServiceName": { - "Fn::GetAtt": [ - "greetingvirtualservice60AD3AD9", - "VirtualServiceName" - ] - } + "VirtualServiceName": "greeting.production" } } ], diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 54bc79fade4c3..d168d56c73a7e 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -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. diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index 8e672ac535bdc..7598ac959e98b 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -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), 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 4b6c3e54f543e..3649195cf9c7e 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -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" } } ], @@ -1111,12 +1101,7 @@ "Backends": [ { "VirtualService": { - "VirtualServiceName": { - "Fn::GetAtt": [ - "service3859EB104", - "VirtualServiceName" - ] - } + "VirtualServiceName": "service3.domain.local" } } ], @@ -1241,12 +1226,7 @@ "Backends": [ { "VirtualService": { - "VirtualServiceName": { - "Fn::GetAtt": [ - "service4983B61EE", - "VirtualServiceName" - ] - } + "VirtualServiceName": "service4.domain.local" } } ], diff --git a/packages/@aws-cdk/aws-appmesh/test/mesh.test.ts b/packages/@aws-cdk/aws-appmesh/test/mesh.test.ts index f30d1562416da..294701e0d550a 100644 --- a/packages/@aws-cdk/aws-appmesh/test/mesh.test.ts +++ b/packages/@aws-cdk/aws-appmesh/test/mesh.test.ts @@ -314,9 +314,7 @@ describe('mesh', () => { Backends: [ { VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['service1A48078CF', 'VirtualServiceName'], - }, + VirtualServiceName: 'service1.domain.local', }, }, ], diff --git a/packages/@aws-cdk/aws-appmesh/test/virtual-node.test.ts b/packages/@aws-cdk/aws-appmesh/test/virtual-node.test.ts index 82bc98c6267ef..842c061f68a15 100644 --- a/packages/@aws-cdk/aws-appmesh/test/virtual-node.test.ts +++ b/packages/@aws-cdk/aws-appmesh/test/virtual-node.test.ts @@ -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, }); - - }); }); @@ -458,9 +452,7 @@ describe('virtual node', () => { Backends: [ { VirtualService: { - VirtualServiceName: { - 'Fn::GetAtt': ['service1A48078CF', 'VirtualServiceName'], - }, + VirtualServiceName: 'service1.domain.local', ClientPolicy: { TLS: { Ports: [8080, 8081], @@ -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', + }, + }, + ], + }, + }); }); });