From 778166aa121e98cf434d43b5f38557bba13fd893 Mon Sep 17 00:00:00 2001 From: zirain Date: Sun, 18 May 2025 15:06:00 +0800 Subject: [PATCH 1/2] e2e: fix and refactor WeightedRoute Signed-off-by: zirain --- .../testdata/weighted-backend-all-equal.yaml | 2 +- .../testdata/weighted-backend-bluegreen.yaml | 2 +- ... weighted-backend-completing-rollout.yaml} | 2 +- test/e2e/tests/weighted_backend.go | 252 +++++------------- 4 files changed, 75 insertions(+), 183 deletions(-) rename test/e2e/testdata/{weight-backend-completing-rollout.yaml => weighted-backend-completing-rollout.yaml} (92%) diff --git a/test/e2e/testdata/weighted-backend-all-equal.yaml b/test/e2e/testdata/weighted-backend-all-equal.yaml index b81edeb4ae..c13085d69c 100644 --- a/test/e2e/testdata/weighted-backend-all-equal.yaml +++ b/test/e2e/testdata/weighted-backend-all-equal.yaml @@ -10,7 +10,7 @@ spec: - matches: - path: type: PathPrefix - value: / + value: /same-weight backendRefs: - name: infra-backend-v1 port: 8080 diff --git a/test/e2e/testdata/weighted-backend-bluegreen.yaml b/test/e2e/testdata/weighted-backend-bluegreen.yaml index 13be4ab26a..a180f856a0 100644 --- a/test/e2e/testdata/weighted-backend-bluegreen.yaml +++ b/test/e2e/testdata/weighted-backend-bluegreen.yaml @@ -10,7 +10,7 @@ spec: - matches: - path: type: PathPrefix - value: / + value: /blue-green backendRefs: - name: infra-backend-v1 port: 8080 diff --git a/test/e2e/testdata/weight-backend-completing-rollout.yaml b/test/e2e/testdata/weighted-backend-completing-rollout.yaml similarity index 92% rename from test/e2e/testdata/weight-backend-completing-rollout.yaml rename to test/e2e/testdata/weighted-backend-completing-rollout.yaml index 2a36b55712..560c91cbc1 100644 --- a/test/e2e/testdata/weight-backend-completing-rollout.yaml +++ b/test/e2e/testdata/weighted-backend-completing-rollout.yaml @@ -10,7 +10,7 @@ spec: - matches: - path: type: PathPrefix - value: / + value: /complete-rollout backendRefs: - name: infra-backend-v1 port: 8080 diff --git a/test/e2e/tests/weighted_backend.go b/test/e2e/tests/weighted_backend.go index 2ffb239c52..afb044b73d 100644 --- a/test/e2e/tests/weighted_backend.go +++ b/test/e2e/tests/weighted_backend.go @@ -18,208 +18,100 @@ import ( "sigs.k8s.io/gateway-api/conformance/utils/suite" ) +const sendRequests = 50 + func init() { - ConformanceTests = append(ConformanceTests, WeightEqualTest, WeightBlueGreenTest, WeightCompleteRolloutTest) + ConformanceTests = append(ConformanceTests, WeightedRouteTest) } -var WeightEqualTest = suite.ConformanceTest{ - ShortName: "WeightEqualBackend", - Description: "Resource with Weight Backend enabled, and use the all backend weight is equal", - Manifests: []string{"testdata/weighted-backend-all-equal.yaml"}, +var WeightedRouteTest = suite.ConformanceTest{ + ShortName: "WeightedRoute", + Description: "Resource with Weight Backend enabled, and worked as expected.", + Manifests: []string{ + "testdata/weighted-backend-all-equal.yaml", + "testdata/weighted-backend-bluegreen.yaml", + "testdata/weighted-backend-completing-rollout.yaml", + }, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - t.Run("all backends receive the same weight of traffic", func(t *testing.T) { - const sendRequests = 50 - - ns := "gateway-conformance-infra" - weightEqualRoute := types.NamespacedName{Name: "weight-equal-http-route", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, - suite.ControllerName, - kubernetes.NewGatewayRef(gwNN), weightEqualRoute) - - expectedResponse := http.ExpectedResponse{ - Request: http.Request{ - Path: "/", - }, - Response: http.Response{ - StatusCode: 200, - }, - Namespace: ns, - } - req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") - - // Since we only route to pods with "infra-backend-v 1" and "infra-backend-v 2" prefixes - // So here we use fixed weight values + t.Run("SameWeight", func(t *testing.T) { + // The received request is approximately 1:1 expected := map[string]int{ "infra-backend-v1": sendRequests * .5, "infra-backend-v2": sendRequests * .5, } - weightMap := make(map[string]int) - for i := 0; i < sendRequests; i++ { - cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) - if err != nil { - t.Errorf("failed to get expected response: %v", err) - } - - if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { - t.Errorf("failed to compare request and response: %v", err) - } - - podName := cReq.Pod - if len(podName) == 0 { - // it shouldn't be missing here - t.Errorf("failed to get pod header in response: %v", err) - } else { - // all we need is the pod Name prefix - podNamePrefix := ExtractPodNamePrefix(podName) - weightMap[podNamePrefix]++ - } - } - - // We iterate over the actual traffic Map with podNamePrefix as the key to get the actual traffic. - // Given an offset of 3, we expect the expected traffic to be within the actual traffic [actual-3,actual+3] interval. - for prefix, actual := range weightMap { - expect := expected[prefix] - if !AlmostEquals(actual, expect, 3) { - t.Errorf("The actual traffic weights are not consistent with the expected routing weights") - } - } + runWeightRouteTest(t, suite, "weight-equal-http-route", "/same-weight", expected) }) - }, -} - -var WeightBlueGreenTest = suite.ConformanceTest{ - ShortName: "WeightBlueGreenBackend", - Description: "Resource with Weight Backend enabled, and use the blue-green policy weight setting", - Manifests: []string{"testdata/weighted-backend-bluegreen.yaml"}, - Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - t.Run("all backends receive the blue green weight of traffic", func(t *testing.T) { - const sendRequests = 50 - - ns := "gateway-conformance-infra" - weightEqualRoute := types.NamespacedName{Name: "weight-bluegreen-http-route", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, - suite.ControllerName, - kubernetes.NewGatewayRef(gwNN), weightEqualRoute) - - expectedResponse := http.ExpectedResponse{ - Request: http.Request{ - Path: "/", - }, - Response: http.Response{ - StatusCode: 200, - }, - Namespace: ns, - } - req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") - - // Since we only route to pods with "infra-backend-v 1" and "infra-backend-v 2" prefixes - // So here we use fixed weight values + t.Run("BlueGreen", func(t *testing.T) { + // The received request is approximately 9:1 expected := map[string]int{ "infra-backend-v1": sendRequests * .9, "infra-backend-v2": sendRequests * .1, } - weightMap := make(map[string]int) - for i := 0; i < sendRequests; i++ { - cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) - if err != nil { - t.Errorf("failed to get expected response: %v", err) - } - - if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { - t.Errorf("failed to compare request and response: %v", err) - } - - podName := cReq.Pod - if len(podName) == 0 { - // it shouldn't be missing here - t.Errorf("failed to get pod header in response: %v", err) - } else { - // all we need is the pod Name prefix - podNamePrefix := ExtractPodNamePrefix(podName) - weightMap[podNamePrefix]++ - } - } - - // We iterate over the actual traffic Map with podNamePrefix as the key to get the actual traffic. - // Given an offset of 3, we expect the expected traffic to be within the actual traffic [actual-3,actual+3] interval. - for prefix, actual := range weightMap { - expect := expected[prefix] - if !AlmostEquals(actual, expect, 3) { - t.Errorf("The actual traffic weights are not consistent with the expected routing weights") - } - } + runWeightRouteTest(t, suite, "weight-bluegreen-http-route", "/blue-green", expected) }) - }, -} - -var WeightCompleteRolloutTest = suite.ConformanceTest{ - ShortName: "WeightCompleteRollout", - Description: "Resource with Weight Backend enabled, and use the completing rollout policy weight setting", - Manifests: []string{"testdata/weight-backend-completing-rollout.yaml"}, - Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - t.Run("all backends receive the complete rollout weight of traffic", func(t *testing.T) { - const sendRequests = 50 - - ns := "gateway-conformance-infra" - weightEqualRoute := types.NamespacedName{Name: "weight-complete-rollout-http-route", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, - suite.ControllerName, - kubernetes.NewGatewayRef(gwNN), weightEqualRoute) - - expectedResponse := http.ExpectedResponse{ - Request: http.Request{ - Path: "/", - }, - Response: http.Response{ - StatusCode: 200, - }, - Namespace: ns, - } - req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") - - // Since we only route to pods with "infra-backend-v 1" and "infra-backend-v 2" prefixes - // So here we use fixed weight values + t.Run("CompleteRollout", func(t *testing.T) { + // All the requests should be proxied to v1 expected := map[string]int{ "infra-backend-v1": sendRequests * 1, "infra-backend-v2": sendRequests * 0, } - weightMap := make(map[string]int) - for i := 0; i < sendRequests; i++ { - cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) - if err != nil { - t.Errorf("failed to get expected response: %v", err) - } - - if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { - t.Errorf("failed to compare request and response: %v", err) - } - - podName := cReq.Pod - if len(podName) == 0 { - // it shouldn't be missing here - t.Errorf("failed to get pod header in response: %v", err) - } else { - // all we need is the pod Name prefix - podNamePrefix := ExtractPodNamePrefix(podName) - weightMap[podNamePrefix]++ - } - } - - // We iterate over the actual traffic Map with podNamePrefix as the key to get the actual traffic. - // Given an offset of 3, we expect the expected traffic to be within the actual traffic [actual-3,actual+3] interval. - for prefix, actual := range weightMap { - expect := expected[prefix] - if !AlmostEquals(actual, expect, 3) { - t.Errorf("The actual traffic weights are not consistent with the expected routing weights") - } - } + runWeightRouteTest(t, suite, "weight-complete-rollout-http-route", "/complete-rollout", expected) }) }, } +func runWeightRouteTest(t *testing.T, suite *suite.ConformanceTestSuite, routeName, path string, expectedOutput map[string]int) { + weightEqualRoute := types.NamespacedName{Name: routeName, Namespace: ConformanceInfraNamespace} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, + suite.ControllerName, + kubernetes.NewGatewayRef(SameNamespaceGateway), weightEqualRoute) + + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: path, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ConformanceInfraNamespace, + } + // Make sure the backend is ready + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse) + + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + + weightMap := make(map[string]int) + for range sendRequests { + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response: %v", err) + } + + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response: %v", err) + } + + podName := cReq.Pod + if len(podName) == 0 { + // it shouldn't be missing here + t.Errorf("failed to get pod header in response: %v", err) + } else { + // all we need is the pod Name prefix + podNamePrefix := ExtractPodNamePrefix(podName) + weightMap[podNamePrefix]++ + } + } + + // We iterate over the actual traffic Map with podNamePrefix as the key to get the actual traffic. + // Given an offset of 3, we expect the expected traffic to be within the actual traffic [actual-3,actual+3] interval. + for prefix, actual := range weightMap { + expect := expectedOutput[prefix] + if !AlmostEquals(actual, expect, 3) { + t.Errorf("The actual traffic weights are not consistent with the expected routing weights") + } + } +} + // ExtractPodNamePrefix Extract the Pod Name prefix func ExtractPodNamePrefix(podName string) string { pattern := regexp.MustCompile(`infra-backend-(.+?)-`) From f3fd9c0b41583b1ddae59ab8f9c69af26556aa62 Mon Sep 17 00:00:00 2001 From: zirain Date: Sun, 18 May 2025 15:31:39 +0800 Subject: [PATCH 2/2] refactor Signed-off-by: zirain --- test/e2e/tests/weighted_backend.go | 23 +++++---- test/e2e/tests/zone_aware_routing.go | 71 +--------------------------- 2 files changed, 12 insertions(+), 82 deletions(-) diff --git a/test/e2e/tests/weighted_backend.go b/test/e2e/tests/weighted_backend.go index afb044b73d..bea61448fc 100644 --- a/test/e2e/tests/weighted_backend.go +++ b/test/e2e/tests/weighted_backend.go @@ -21,10 +21,10 @@ import ( const sendRequests = 50 func init() { - ConformanceTests = append(ConformanceTests, WeightedRouteTest) + ConformanceTests = append(ConformanceTests, WeightedBackendTest) } -var WeightedRouteTest = suite.ConformanceTest{ +var WeightedBackendTest = suite.ConformanceTest{ ShortName: "WeightedRoute", Description: "Resource with Weight Backend enabled, and worked as expected.", Manifests: []string{ @@ -39,7 +39,7 @@ var WeightedRouteTest = suite.ConformanceTest{ "infra-backend-v1": sendRequests * .5, "infra-backend-v2": sendRequests * .5, } - runWeightRouteTest(t, suite, "weight-equal-http-route", "/same-weight", expected) + runWeightedBackendTest(t, suite, "weight-equal-http-route", "/same-weight", "infra-backend", expected) }) t.Run("BlueGreen", func(t *testing.T) { // The received request is approximately 9:1 @@ -47,7 +47,7 @@ var WeightedRouteTest = suite.ConformanceTest{ "infra-backend-v1": sendRequests * .9, "infra-backend-v2": sendRequests * .1, } - runWeightRouteTest(t, suite, "weight-bluegreen-http-route", "/blue-green", expected) + runWeightedBackendTest(t, suite, "weight-bluegreen-http-route", "/blue-green", "infra-backend", expected) }) t.Run("CompleteRollout", func(t *testing.T) { // All the requests should be proxied to v1 @@ -55,12 +55,12 @@ var WeightedRouteTest = suite.ConformanceTest{ "infra-backend-v1": sendRequests * 1, "infra-backend-v2": sendRequests * 0, } - runWeightRouteTest(t, suite, "weight-complete-rollout-http-route", "/complete-rollout", expected) + runWeightedBackendTest(t, suite, "weight-complete-rollout-http-route", "/complete-rollout", "infra-backend", expected) }) }, } -func runWeightRouteTest(t *testing.T, suite *suite.ConformanceTestSuite, routeName, path string, expectedOutput map[string]int) { +func runWeightedBackendTest(t *testing.T, suite *suite.ConformanceTestSuite, routeName, path, backendName string, expectedOutput map[string]int) { weightEqualRoute := types.NamespacedName{Name: routeName, Namespace: ConformanceInfraNamespace} gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, @@ -97,7 +97,7 @@ func runWeightRouteTest(t *testing.T, suite *suite.ConformanceTestSuite, routeNa t.Errorf("failed to get pod header in response: %v", err) } else { // all we need is the pod Name prefix - podNamePrefix := ExtractPodNamePrefix(podName) + podNamePrefix := extractPodNamePrefix(podName, backendName) weightMap[podNamePrefix]++ } } @@ -107,18 +107,17 @@ func runWeightRouteTest(t *testing.T, suite *suite.ConformanceTestSuite, routeNa for prefix, actual := range weightMap { expect := expectedOutput[prefix] if !AlmostEquals(actual, expect, 3) { - t.Errorf("The actual traffic weights are not consistent with the expected routing weights") + t.Errorf("The actual traffic weights are not consistent with the expected routing weights, actual %s %d, expected %s %d", prefix, actual, prefix, expect) } } } -// ExtractPodNamePrefix Extract the Pod Name prefix -func ExtractPodNamePrefix(podName string) string { - pattern := regexp.MustCompile(`infra-backend-(.+?)-`) +func extractPodNamePrefix(podName, prefix string) string { + pattern := regexp.MustCompile(prefix + `-(.+?)-`) match := pattern.FindStringSubmatch(podName) if len(match) > 1 { version := match[1] - return fmt.Sprintf("infra-backend-%s", version) + return fmt.Sprintf("%s-%s", prefix, version) } return podName diff --git a/test/e2e/tests/zone_aware_routing.go b/test/e2e/tests/zone_aware_routing.go index 77ee63ed32..5ac6edc0fa 100644 --- a/test/e2e/tests/zone_aware_routing.go +++ b/test/e2e/tests/zone_aware_routing.go @@ -8,14 +8,8 @@ package tests import ( - "fmt" - "regexp" "testing" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/gateway-api/conformance/utils/http" - "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" "sigs.k8s.io/gateway-api/conformance/utils/suite" ) @@ -29,76 +23,13 @@ var ZoneAwareRoutingTest = suite.ConformanceTest{ Manifests: []string{"testdata/zone-aware-routing.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { t.Run("only local zone should get requests", func(t *testing.T) { - const sendRequests = 50 - - ns := "gateway-conformance-infra" - zoneAwareRoute := types.NamespacedName{Name: "zone-aware-http-route", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, - suite.ControllerName, - kubernetes.NewGatewayRef(gwNN), zoneAwareRoute) - - podReady := corev1.PodCondition{Type: corev1.PodReady, Status: corev1.ConditionTrue} - WaitForPods(t, suite.Client, ns, map[string]string{"app": "zone-aware-backend"}, corev1.PodRunning, podReady) - - expectedResponse := http.ExpectedResponse{ - Request: http.Request{ - Path: "/", - }, - Response: http.Response{ - StatusCode: 200, - }, - Namespace: ns, - } - req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") - // Pods from the backend-local deployment have affinity // for the Envoy Proxy pods so should receive all requests. expected := map[string]int{ "zone-aware-backend-local": sendRequests, "zone-aware-backend-nonlocal": 0, } - reqMap := make(map[string]int) - for i := 0; i < sendRequests; i++ { - cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) - if err != nil { - t.Errorf("failed to get expected response: %v", err) - } - - if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { - t.Errorf("failed to compare request and response: %v", err) - } - - podName := cReq.Pod - if len(podName) == 0 { - // it shouldn't be missing here - t.Errorf("failed to get pod header in response: %v", err) - } else { - // all we need is the pod Name prefix - podNamePrefix := ZoneAwareRoutingExtractPodNamePrefix(podName) - reqMap[podNamePrefix]++ - } - } - - // We iterate over the actual traffic Map with podNamePrefix as the key to get the actual traffic. - for prefix, actual := range reqMap { - expect := expected[prefix] - if actual != expect { - t.Errorf("The actual traffic distribution between zones is not consistent with the expected: %v", reqMap) - } - } + runWeightedBackendTest(t, suite, "zone-aware-http-route", "/", "zone-aware-backend", expected) }) }, } - -// ZoneAwareRoutingExtractPodNamePrefix extracts the Pod Name prefix -func ZoneAwareRoutingExtractPodNamePrefix(podName string) string { - pattern := regexp.MustCompile(`zone-aware-backend-(.+?)-`) - match := pattern.FindStringSubmatch(podName) - if len(match) > 1 { - version := match[1] - return fmt.Sprintf("zone-aware-backend-%s", version) - } - - return podName -}