From 705093162b6797475bc2333788cb56c16d0ad5ef Mon Sep 17 00:00:00 2001 From: Ryan Lymburner Date: Mon, 11 Aug 2025 13:58:15 -0700 Subject: [PATCH] Correct additional target group creation when using the ExportedPorts field with HTTPRoutes --- pkg/gateway/model_build_targetgroup.go | 6 - pkg/gateway/model_build_targetgroup_test.go | 16 ++- .../httproute_serviceexport_test.go | 118 ++++++++++++++++++ 3 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 test/suites/integration/httproute_serviceexport_test.go diff --git a/pkg/gateway/model_build_targetgroup.go b/pkg/gateway/model_build_targetgroup.go index 37f0ddf9..35a12f4d 100644 --- a/pkg/gateway/model_build_targetgroup.go +++ b/pkg/gateway/model_build_targetgroup.go @@ -225,12 +225,6 @@ func (t *svcExportTargetGroupModelBuildTask) buildTargetGroupForExportedPort(ctx spec.K8SServiceNamespace = t.serviceExport.Namespace spec.K8SProtocolVersion = protocolVersion - // Add a tag for the route type to help with identification - // This is not used by the controller but can be helpful for debugging - if exportedPort.RouteType != "" { - spec.K8SProtocolVersion = exportedPort.RouteType - } - stackTG, err := model.NewTargetGroup(t.stack, spec) if err != nil { return nil, err diff --git a/pkg/gateway/model_build_targetgroup_test.go b/pkg/gateway/model_build_targetgroup_test.go index ff877c21..b71e0e26 100644 --- a/pkg/gateway/model_build_targetgroup_test.go +++ b/pkg/gateway/model_build_targetgroup_test.go @@ -693,7 +693,7 @@ func Test_TGModelByServiceExportWithExportedPorts(t *testing.T) { wantErrIsNil: true, wantTargetGroupCount: 3, wantPorts: []int32{80, 8081, 443}, - wantRouteTypes: []string{"HTTP", "GRPC", "TLS"}, + wantRouteTypes: []string{vpclattice.TargetGroupProtocolVersionHttp1, vpclattice.TargetGroupProtocolVersionGrpc, ""}, }, { name: "ServiceExport with single exportedPort", @@ -731,7 +731,7 @@ func Test_TGModelByServiceExportWithExportedPorts(t *testing.T) { wantErrIsNil: true, wantTargetGroupCount: 1, wantPorts: []int32{80}, - wantRouteTypes: []string{"HTTP"}, + wantRouteTypes: []string{vpclattice.TargetGroupProtocolVersionHttp1}, }, { name: "ServiceExport with no exportedPorts (legacy behavior)", @@ -812,20 +812,18 @@ func Test_TGModelByServiceExportWithExportedPorts(t *testing.T) { seenPorts[tg.Spec.Port] = true seenRouteTypes[tg.Spec.K8SProtocolVersion] = true - // Check protocol and protocolVersion based on route type + // Check protocol and protocolVersion based on K8SProtocolVersion switch tg.Spec.K8SProtocolVersion { - case "HTTP": + case vpclattice.TargetGroupProtocolVersionHttp1: assert.Equal(t, vpclattice.TargetGroupProtocolHttp, tg.Spec.Protocol) assert.Equal(t, vpclattice.TargetGroupProtocolVersionHttp1, tg.Spec.ProtocolVersion) - case "GRPC": + case vpclattice.TargetGroupProtocolVersionGrpc: assert.Equal(t, vpclattice.TargetGroupProtocolHttp, tg.Spec.Protocol) assert.Equal(t, vpclattice.TargetGroupProtocolVersionGrpc, tg.Spec.ProtocolVersion) - case "TLS": + case "": + // TLS case - no protocol version for TCP assert.Equal(t, vpclattice.TargetGroupProtocolTcp, tg.Spec.Protocol) assert.Equal(t, "", tg.Spec.ProtocolVersion) - case vpclattice.TargetGroupProtocolVersionHttp1: - // Legacy behavior - assert.Equal(t, vpclattice.TargetGroupProtocolHttp, tg.Spec.Protocol) } } diff --git a/test/suites/integration/httproute_serviceexport_test.go b/test/suites/integration/httproute_serviceexport_test.go new file mode 100644 index 00000000..7cd3c644 --- /dev/null +++ b/test/suites/integration/httproute_serviceexport_test.go @@ -0,0 +1,118 @@ +package integration + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/aws/aws-application-networking-k8s/pkg/model/core" + "github.com/aws/aws-application-networking-k8s/test/pkg/test" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/vpclattice" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + + anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" +) + +var _ = Describe("HTTPRoute Service Export/Import Test", Ordered, func() { + var ( + httpDeployment *appsv1.Deployment + httpSvc *v1.Service + httpRoute *gwv1.HTTPRoute + serviceExport *anv1alpha1.ServiceExport + serviceImport *anv1alpha1.ServiceImport + ) + + It("Create k8s resource", func() { + // Create an HTTP service and deployment + httpDeployment, httpSvc = testFramework.NewHttpApp(test.HTTPAppOptions{Name: "my-http-exportedports", Namespace: k8snamespace}) + testFramework.ExpectCreated(ctx, httpDeployment, httpSvc) + + // Create ServiceImport + serviceImport = testFramework.CreateServiceImport(httpSvc) + testFramework.ExpectCreated(ctx, serviceImport) + + // Create ServiceExport with exportedPorts field + serviceExport = &anv1alpha1.ServiceExport{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "application-networking.k8s.aws/v1alpha1", + Kind: "ServiceExport", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: httpSvc.Name, + Namespace: httpSvc.Namespace, + Annotations: map[string]string{ + "application-networking.k8s.aws/federation": "amazon-vpc-lattice", + }, + }, + Spec: anv1alpha1.ServiceExportSpec{ + ExportedPorts: []anv1alpha1.ExportedPort{ + { + Port: httpSvc.Spec.Ports[0].Port, + RouteType: "HTTP", + }, + }, + }, + } + testFramework.ExpectCreated(ctx, serviceExport) + + httpRoute = testFramework.NewHttpRoute(testGateway, httpSvc, "ServiceImport") + testFramework.ExpectCreated(ctx, httpRoute) + }) + + It("Verify lattice resource & traffic", func() { + route := core.NewHTTPRoute(*httpRoute) + vpcLatticeService := testFramework.GetVpcLatticeService(ctx, route) + fmt.Printf("vpcLatticeService: %v \n", vpcLatticeService) + + // Get the target group and verify it's configured for HTTP + tgSummary := testFramework.GetTargetGroupWithProtocol(ctx, httpSvc, "http", "http1") + tg, err := testFramework.LatticeClient.GetTargetGroup(&vpclattice.GetTargetGroupInput{ + TargetGroupIdentifier: aws.String(*tgSummary.Id), + }) + Expect(tg).To(Not(BeNil())) + Expect(err).To(BeNil()) + Expect(*tgSummary.VpcIdentifier).To(Equal(os.Getenv("CLUSTER_VPC_ID"))) + + // Verify the target group is configured for HTTP + Expect(*tgSummary.Protocol).To(Equal("HTTP")) + Expect(*tg.Config.ProtocolVersion).To(Equal("HTTP1")) + + // Verify targets are registered + Eventually(func(g Gomega) { + targets := testFramework.GetTargets(ctx, tgSummary, httpDeployment) + for _, target := range targets { + g.Expect(*target.Port).To(BeEquivalentTo(httpSvc.Spec.Ports[0].TargetPort.IntVal)) + } + }).WithTimeout(3 * time.Minute).WithOffset(1).Should(Succeed()) + + log.Println("Verifying traffic") + dnsName := testFramework.GetVpcLatticeServiceDns(httpRoute.Name, httpRoute.Namespace) + pods := testFramework.GetPodsByDeploymentName(httpDeployment.Name, httpDeployment.Namespace) + Expect(len(pods)).To(BeEquivalentTo(1)) + pod := pods[0] + + Eventually(func(g Gomega) { + cmd := fmt.Sprintf("curl %s", dnsName) + stdout, _, err := testFramework.PodExec(pod, cmd) + g.Expect(err).To(BeNil()) + g.Expect(stdout).To(ContainSubstring("handler pod")) + }).Should(Succeed()) + }) + + AfterAll(func() { + testFramework.ExpectDeletedThenNotFound(ctx, + httpRoute, + httpDeployment, + httpSvc, + serviceImport, + serviceExport, + ) + }) +})