From 17534b37ea58fa39eace9802263f77560f8039a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20Ruiz=20Fern=C3=A1ndez?= Date: Fri, 7 Jul 2023 16:59:49 +0200 Subject: [PATCH] feat(app-mesh): support port property on weighted targets (#26114) As described in the related issue, `WeightedTarget` L2 construct was missing `port` property which is already present in the L1 construct `CfnRoute`. This PR adds the missing `port` property to `WeightedTarget` L2 construct. The PR includes unit tests expansion to cover this new property appearance or absence. Closes #26083. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../manifest.json | 2 +- .../mesh-stack.assets.json | 4 +- .../mesh-stack.template.json | 1 + .../tree.json | 7 +- .../aws-appmesh/test/integ.mesh-port-match.ts | 2 +- .../aws-cdk-lib/aws-appmesh/lib/route-spec.ts | 8 ++ .../aws-appmesh/test/route.test.ts | 117 ++++++++++++++++++ 7 files changed, 134 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/manifest.json index 00a9fa201822e..8b2ec12709258 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/22240821b1bd2dffca97a9e0b581bf2d1a2dc32765da9872c066c10f315cd391.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/7eca470b64972db3f33a642114983901fa5e45a5c40d071fe328451294b6f3db.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.assets.json index df0d9477bb792..0d9aa86bc3b2f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.assets.json @@ -1,7 +1,7 @@ { "version": "32.0.0", "files": { - "22240821b1bd2dffca97a9e0b581bf2d1a2dc32765da9872c066c10f315cd391": { + "7eca470b64972db3f33a642114983901fa5e45a5c40d071fe328451294b6f3db": { "source": { "path": "mesh-stack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "22240821b1bd2dffca97a9e0b581bf2d1a2dc32765da9872c066c10f315cd391.json", + "objectKey": "7eca470b64972db3f33a642114983901fa5e45a5c40d071fe328451294b6f3db.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.template.json index 6f1dbba1aee62..7453610fae0fc 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/mesh-stack.template.json @@ -468,6 +468,7 @@ "Action": { "WeightedTargets": [ { + "Port": 1234, "VirtualNode": { "Fn::GetAtt": [ "meshgrpcnode5DE90B75", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/tree.json index 8e7f15fecfb80..faa2cbfeb5860 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.js.snapshot/tree.json @@ -807,7 +807,8 @@ "VirtualNodeName" ] }, - "weight": 1 + "weight": 1, + "port": 1234 } ] }, @@ -1264,7 +1265,7 @@ "path": "appmesh-routes-port-matchers/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.26" + "version": "10.2.55" } }, "DeployAssert": { @@ -1310,7 +1311,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.26" + "version": "10.2.55" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.ts index 083fb9f72240a..b879cc0a04176 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appmesh/test/integ.mesh-port-match.ts @@ -65,7 +65,7 @@ httpRouter.addRoute('http-route', { grpcRouter.addRoute('grpc-route', { routeSpec: appmesh.RouteSpec.grpc({ - weightedTargets: [{ virtualNode: grpcNode }], + weightedTargets: [{ virtualNode: grpcNode, port: 1234 }], match: { port: 1234, }, diff --git a/packages/aws-cdk-lib/aws-appmesh/lib/route-spec.ts b/packages/aws-cdk-lib/aws-appmesh/lib/route-spec.ts index e6080c25801e6..2204e8aa30a9f 100644 --- a/packages/aws-cdk-lib/aws-appmesh/lib/route-spec.ts +++ b/packages/aws-cdk-lib/aws-appmesh/lib/route-spec.ts @@ -24,6 +24,13 @@ export interface WeightedTarget { * @default 1 */ readonly weight?: number; + + /** + * The port to match from the request. + * + * @default - do not match on port + */ + readonly port?: number; } /** @@ -603,6 +610,7 @@ function renderWeightedTargets(weightedTargets: WeightedTarget[]): CfnRoute.Weig renderedTargets.push({ virtualNode: t.virtualNode.virtualNodeName, weight: t.weight == undefined ? 1 : t.weight, + port: t.port, }); } return renderedTargets; diff --git a/packages/aws-cdk-lib/aws-appmesh/test/route.test.ts b/packages/aws-cdk-lib/aws-appmesh/test/route.test.ts index 4bc11ff32bd15..05c467f674c90 100644 --- a/packages/aws-cdk-lib/aws-appmesh/test/route.test.ts +++ b/packages/aws-cdk-lib/aws-appmesh/test/route.test.ts @@ -280,6 +280,123 @@ describe('route', () => { }); + test('should allow weighted targets with port specified', () => { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + // WHEN + const node = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + router.addRoute('test-http-route', { + routeSpec: appmesh.RouteSpec.http({ + weightedTargets: [ + { + virtualNode: node, + port: 1234, + }, + ], + match: { + path: appmesh.HttpRoutePathMatch.startsWith('/node'), + }, + timeout: { + idle: cdk.Duration.seconds(10), + perRequest: cdk.Duration.seconds(11), + }, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppMesh::Route', { + Spec: { + HttpRoute: { + Action: { + WeightedTargets: [ + { + VirtualNode: { + 'Fn::GetAtt': [ + 'meshtestnodeF93946D4', + 'VirtualNodeName', + ], + }, + Weight: 1, + Port: 1234, + }, + ], + }, + }, + }, + RouteName: 'test-http-route', + }); + + }); + + test('should not have weighted targets port when not specified', () => { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + // WHEN + const node = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + router.addRoute('test-http-route', { + routeSpec: appmesh.RouteSpec.http({ + weightedTargets: [ + { + virtualNode: node, + }, + ], + match: { + path: appmesh.HttpRoutePathMatch.startsWith('/node'), + }, + timeout: { + idle: cdk.Duration.seconds(10), + perRequest: cdk.Duration.seconds(11), + }, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppMesh::Route', { + Spec: { + HttpRoute: { + Action: { + WeightedTargets: [ + { + VirtualNode: { + 'Fn::GetAtt': [ + 'meshtestnodeF93946D4', + 'VirtualNodeName', + ], + }, + Weight: 1, + Port: Match.absent(), + }, + ], + }, + }, + }, + RouteName: 'test-http-route', + }); + + }); + test('should allow http retries', () => { // GIVEN const stack = new cdk.Stack();