diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index a642b37a0a..32475c6e7e 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -1039,7 +1039,7 @@ func (t *Translator) processRequestMirrorFilter( fmt.Errorf("failed to validate the RequestMirror filter: %w", err), err.Reason()).WithType(gwapiv1.RouteConditionResolvedRefs) } - destName := fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(filterContext.Route, filterContext.RuleIdx), filterIdx) + destName := fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(t.XDSNameSchemeV2, filterContext.Route, filterContext.RuleIdx), filterIdx) settingName := irDestinationSettingName(destName, -1 /*unused*/) ds, _, err := t.processDestination(settingName, mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources) if err != nil { diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index a6cde9133c..6d5ea2fb06 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -465,11 +465,21 @@ func irUDPRouteName(route RouteContext) string { return irTCPRouteName(route) } -func irRouteDestinationName(route RouteContext, ruleIdx int) string { +// irRouteDestinationName returns the name for a route destination in the IR. +// The format of the name depends on the xdsNameSchemeV2 flag: +// - If xdsNameSchemeV2 is true, the name will be "{routePrefix}rule/{ruleIdx}/backend/0" +// this will avoid naming changes for issue like https://github.com/envoyproxy/gateway/issues/6287. +// - If xdsNameSchemeV2 is false, the name will be "{routePrefix}rule/{ruleIdx}" +func irRouteDestinationName(xdsNameSchemeV2 bool, route RouteContext, ruleIdx int) string { + if xdsNameSchemeV2 { + return fmt.Sprintf("%srule/%d/backend/0", irRoutePrefix(route), ruleIdx) + } return fmt.Sprintf("%srule/%d", irRoutePrefix(route), ruleIdx) } func irDestinationSettingName(destName string, backendIdx int) string { + // Trim the "/backend/0" suffix if it exists to avoid duplication when appending the backend index + destName = strings.TrimSuffix(destName, "/backend/0") return fmt.Sprintf("%s/backend/%d", destName, backendIdx) } diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 36215ce253..a88dc3a87d 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -259,7 +259,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe } // process each backendRef, and calculate the destination settings for this rule - destName := irRouteDestinationName(httpRoute, ruleIdx) + destName := irRouteDestinationName(t.XDSNameSchemeV2, httpRoute, ruleIdx) allDs := make([]*ir.DestinationSetting, 0, len(rule.BackendRefs)) var processDestinationError error failedNoReadyEndpoints := false @@ -586,7 +586,7 @@ func (t *Translator) processHTTPRouteRule( // We generate a unique session name per route. // `/` isn't allowed in the header key, so we just replace it with `-`. - sessionName = strings.ReplaceAll(irRouteDestinationName(httpRoute, ruleIdx), "/", "-") + sessionName = strings.ReplaceAll(irRouteDestinationName(t.XDSNameSchemeV2, httpRoute, ruleIdx), "/", "-") } else { sessionName = *rule.SessionPersistence.SessionName } @@ -980,7 +980,7 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe } // process each backendRef, and calculate the destination settings for this rule - destName := irRouteDestinationName(grpcRoute, ruleIdx) + destName := irRouteDestinationName(t.XDSNameSchemeV2, grpcRoute, ruleIdx) allDs := make([]*ir.DestinationSetting, 0, len(rule.BackendRefs)) var processDestinationError error failedNoReadyEndpoints := false @@ -1365,7 +1365,7 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour var ( destSettings []*ir.DestinationSetting resolveErrs = &status.MultiStatusError{} - destName = irRouteDestinationName(tlsRoute, -1 /*rule index*/) + destName = irRouteDestinationName(t.XDSNameSchemeV2, tlsRoute, -1 /*rule index*/) ) // compute backends @@ -1544,7 +1544,7 @@ func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resour var ( destSettings []*ir.DestinationSetting resolveErrs = &status.MultiStatusError{} - destName = irRouteDestinationName(udpRoute, -1 /*rule index*/) + destName = irRouteDestinationName(t.XDSNameSchemeV2, udpRoute, -1 /*rule index*/) ) for i := range udpRoute.Spec.Rules[0].BackendRefs { @@ -1695,7 +1695,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour var ( destSettings []*ir.DestinationSetting resolveErrs = &status.MultiStatusError{} - destName = irRouteDestinationName(tcpRoute, -1 /*rule index*/) + destName = irRouteDestinationName(t.XDSNameSchemeV2, tcpRoute, -1 /*rule index*/) ) for i := range tcpRoute.Spec.Rules[0].BackendRefs { diff --git a/internal/gatewayapi/runner/runner.go b/internal/gatewayapi/runner/runner.go index 77dcf79481..89aef38aee 100644 --- a/internal/gatewayapi/runner/runner.go +++ b/internal/gatewayapi/runner/runner.go @@ -196,6 +196,7 @@ func (r *Runner) subscribeAndTranslate(sub <-chan watchable.Snapshot[string, *re RunningOnHost: r.EnvoyGateway.Provider != nil && r.EnvoyGateway.Provider.IsRunningOnHost(), Logger: traceLogger, LuaEnvoyExtensionPolicyDisabled: r.EnvoyGateway.ExtensionAPIs != nil && r.EnvoyGateway.ExtensionAPIs.DisableLua, + XDSNameSchemeV2: r.EnvoyGateway.RuntimeFlags != nil && r.EnvoyGateway.RuntimeFlags.IsEnabled(egv1a1.XDSNameSchemeV2), } // If an extension is loaded, pass its supported groups/kinds to the translator diff --git a/internal/gatewayapi/testdata/xds-name-scheme-v2.in.yaml b/internal/gatewayapi/testdata/xds-name-scheme-v2.in.yaml new file mode 100644 index 0000000000..1be8bb7227 --- /dev/null +++ b/internal/gatewayapi/testdata/xds-name-scheme-v2.in.yaml @@ -0,0 +1,76 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway2.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + - name: service-2 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: backend + value: service-2 diff --git a/internal/gatewayapi/testdata/xds-name-scheme-v2.out.yaml b/internal/gatewayapi/testdata/xds-name-scheme-v2.out.yaml new file mode 100644 index 0000000000..e1ace5443e --- /dev/null +++ b/internal/gatewayapi/testdata/xds-name-scheme-v2.out.yaml @@ -0,0 +1,373 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway2.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + - filters: + - responseHeaderModifier: + add: + - name: backend + value: service-2 + type: ResponseHeaderModifier + name: service-2 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system + envoy-gateway/gateway-2: + proxy: + listeners: + - name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-2 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/backend/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + envoy-gateway/gateway-2: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-2-4a0e4eb9 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-2 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-2-4a0e4eb9 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-2 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0/backend/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + filters: + addResponseHeaders: + - addIfAbsent: false + append: true + name: backend + value: + - service-2 + metadata: + kind: Service + name: service-2 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-2/rule/0/backend/1 + protocol: HTTP + weight: 1 + hostname: gateway2.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0/match/0/gateway2_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 08b72d9f1f..e6d3937289 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -126,6 +126,8 @@ type Translator struct { // Logger is the logger used by the translator. Logger logging.Logger + + XDSNameSchemeV2 bool } type TranslateResult struct { diff --git a/internal/gatewayapi/translator_test.go b/internal/gatewayapi/translator_test.go index 5f8c61a86d..5c96feb23a 100644 --- a/internal/gatewayapi/translator_test.go +++ b/internal/gatewayapi/translator_test.go @@ -56,6 +56,7 @@ func TestTranslate(t *testing.T) { GatewayNamespaceMode bool RunningOnHost bool LuaEnvoyExtensionPolicyDisabled bool + XDSNameSchemeV2 bool }{ { name: "envoypatchpolicy-invalid-feature-disabled", @@ -83,6 +84,10 @@ func TestTranslate(t *testing.T) { name: "envoyextensionpolicy-lua-feature-disabled", LuaEnvoyExtensionPolicyDisabled: true, }, + { + name: "xds-name-scheme-v2", + XDSNameSchemeV2: true, + }, } inputFiles, err := filepath.Glob(filepath.Join("testdata", "*.in.yaml")) @@ -107,6 +112,7 @@ func TestTranslate(t *testing.T) { gatewayNamespaceMode := false runningOnHost := false luaEnvoyExtensionPolicyDisabled := false + xdsNameSchemeV2 := false for _, config := range testCasesConfig { if config.name == strings.Split(filepath.Base(inputFile), ".")[0] { @@ -115,6 +121,7 @@ func TestTranslate(t *testing.T) { gatewayNamespaceMode = config.GatewayNamespaceMode runningOnHost = config.RunningOnHost luaEnvoyExtensionPolicyDisabled = config.LuaEnvoyExtensionPolicyDisabled + xdsNameSchemeV2 = config.XDSNameSchemeV2 } } @@ -131,6 +138,7 @@ func TestTranslate(t *testing.T) { RunningOnHost: runningOnHost, LuaEnvoyExtensionPolicyDisabled: luaEnvoyExtensionPolicyDisabled, Logger: logging.DefaultLogger(os.Stdout, egv1a1.LogLevelInfo), + XDSNameSchemeV2: xdsNameSchemeV2, } // Add common test fixtures diff --git a/release-notes/current.yaml b/release-notes/current.yaml index b4d0c5a0ad..2a8d01e2f6 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -4,6 +4,7 @@ date: Pending breaking changes: | The DirectResponse body in HTTPFilter now supports Envoy command operators for dynamic content. Existing configurations including the template syntax (%) will be interpolated. The `0s` timeout in SecurityPolicy is now treated as infinite timeout instead of immediate timeout. + The cluster naming changed from `httproute/default/httproute/rule/0` to `httproute/default/httproute/rule/0/backend/0` when `XDSNameSchemeV2` is enabled. # Updates addressing vulnerabilities, security flaws, or compliance requirements. security updates: | diff --git a/test/e2e/testdata/httproute-backend-filter.yaml b/test/e2e/testdata/httproute-backend-filter.yaml new file mode 100644 index 0000000000..00b2cc5684 --- /dev/null +++ b/test/e2e/testdata/httproute-backend-filter.yaml @@ -0,0 +1,23 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend-changed + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /backend-changed + backendRefs: + - kind: Service + name: infra-backend-v2 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: my-added-header + value: added-value diff --git a/test/e2e/testdata/httproute-backend.yaml b/test/e2e/testdata/httproute-backend.yaml new file mode 100644 index 0000000000..dbc480f82d --- /dev/null +++ b/test/e2e/testdata/httproute-backend.yaml @@ -0,0 +1,43 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backends + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /v1 + backendRefs: + - kind: Service + name: infra-backend-v1 + port: 8080 + - matches: + - path: + type: PathPrefix + value: /v2 + backendRefs: + - kind: Service + name: infra-backend-v2 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend-changed + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /backend-changed + backendRefs: + - kind: Service + name: infra-backend-v1 + port: 8080 diff --git a/test/e2e/tests/backend_health_check.go b/test/e2e/tests/backend_health_check.go index 6e950cf403..ebcc88581e 100644 --- a/test/e2e/tests/backend_health_check.go +++ b/test/e2e/tests/backend_health_check.go @@ -39,6 +39,9 @@ var BackendHealthCheckActiveHTTPTest = suite.ConformanceTest{ Description: "Resource with BackendHealthCheckActiveHTTP enabled", Manifests: []string{"testdata/backend-health-check-active-http.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + if XDSNameSchemeV2() { + t.Skip("cluster name format changed") + } t.Run("active http", func(t *testing.T) { ctx := context.Background() ns := "gateway-conformance-infra" @@ -152,6 +155,9 @@ var BackendHealthCheckWithOverrideTest = suite.ConformanceTest{ Description: "Test backend health check with override configuration", Manifests: []string{"testdata/backend-health-check-with-override.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + if XDSNameSchemeV2() { + t.Skip("cluster name format changed") + } ns := "gateway-conformance-infra" withOverrideRouteNN := types.NamespacedName{Name: "httproute-with-health-check-override", Namespace: ns} withoutOverrideRouteNN := types.NamespacedName{Name: "httproute-without-health-check-override", Namespace: ns} diff --git a/test/e2e/tests/backend_panic_threshold.go b/test/e2e/tests/backend_panic_threshold.go index ac08ce1295..0c97ff80a2 100644 --- a/test/e2e/tests/backend_panic_threshold.go +++ b/test/e2e/tests/backend_panic_threshold.go @@ -35,6 +35,9 @@ var BackendPanicThresholdHTTPTest = suite.ConformanceTest{ Description: "Resource with BackendPanicThreshold enabled", Manifests: []string{"testdata/backend-panic-threshold.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + if XDSNameSchemeV2() { + t.Skip("cluster name format changed") + } t.Run("active http", func(t *testing.T) { ctx := context.Background() ns := "gateway-conformance-infra" diff --git a/test/e2e/tests/httproute_backned_change.go b/test/e2e/tests/httproute_backned_change.go new file mode 100644 index 0000000000..ed920baf09 --- /dev/null +++ b/test/e2e/tests/httproute_backned_change.go @@ -0,0 +1,95 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e + +package tests + +import ( + "net/url" + "testing" + "time" + + "fortio.org/fortio/periodic" + "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/conformance/utils/tlog" +) + +func init() { + ConformanceTests = append(ConformanceTests, HTTPRouteBackendChangeTest) +} + +var HTTPRouteBackendChangeTest = suite.ConformanceTest{ + ShortName: "HTTPRouteBackendChange", + Description: "HTTPRoute with backend change", + Manifests: []string{ + "testdata/httproute-backend.yaml", + }, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + if !XDSNameSchemeV2() { + t.Skip("This test is only relevant for v2 xDS scheme, skipping") + } + + ns := ConformanceInfraNamespace + routeNN := types.NamespacedName{Name: "backend-changed", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, + kubernetes.NewGatewayRef(gwNN), &gwapiv1.HTTPRoute{}, false, + routeNN, types.NamespacedName{Name: "backends", Namespace: ns}, + ) + + // Make sure backend is ready. + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{ + Request: http.Request{ + Path: "/v1", + }, + Response: http.Response{ + StatusCodes: []int{200}, + }, + Backend: "infra-backend-v1", + Namespace: ns, + }) + + done := make(chan struct{}) + go func() { + for i := range 10 { + location := "" + if i%2 == 0 { + location = "testdata/httproute-backend-filter.yaml" + } else { + location = "testdata/httproute-backend.yaml" + } + tlog.Logf(t, "Apply file %s", location) + suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, location, false) + time.Sleep(time.Second) + } + done <- struct{}{} + }() + + reqURL := url.URL{Scheme: "http", Host: http.CalculateHost(t, gwAddr, "http"), Path: "/backend-changed"} + + // can be used to abort the test after deployment restart is complete or failed + aborter := periodic.NewAborter() + // will contain indication on success or failure of load test + loadSuccess := make(chan bool) + + tlog.Logf(t, "Starting load generation") + // Run load async and continue to restart deployment + go runLoadAndWait(t, &suite.TimeoutConfig, loadSuccess, aborter, reqURL.String(), 0) + <-done + tlog.Logf(t, "Stopping load generation and collecting results") + aborter.Abort(false) // abort the load either way + + // Wait for the goroutine to finish + result := <-loadSuccess + if !result { + tlog.Errorf(t, "Load test failed") + } + }, +} diff --git a/test/e2e/tests/retry.go b/test/e2e/tests/retry.go index 6ccaa5b768..2232b3d919 100644 --- a/test/e2e/tests/retry.go +++ b/test/e2e/tests/retry.go @@ -32,6 +32,10 @@ var RetryTest = suite.ConformanceTest{ Description: "Test that the BackendTrafficPolicy API implementation supports retry", Manifests: []string{"testdata/retry.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + if XDSNameSchemeV2() { + t.Skip("cluster name format changed") + } + promClient, err := prometheus.NewClient(suite.Client, types.NamespacedName{Name: "prometheus", Namespace: "monitoring"}) require.NoError(t, err)