From b38eabe77d07939fc8000c333319b041c978917b Mon Sep 17 00:00:00 2001 From: lubronzhan Date: Thu, 15 Feb 2024 23:59:15 -0800 Subject: [PATCH] E2E test Signed-off-by: lubronzhan --- internal/dag/gatewayapi_processor.go | 2 +- internal/dag/status_test.go | 2 +- test/e2e/gateway/gateway_test.go | 2 + .../gateway/http_route_conflict_match_test.go | 86 +++++++++++++++++++ test/e2e/gatewayapi_predicates.go | 28 ++++++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/e2e/gateway/http_route_conflict_match_test.go diff --git a/internal/dag/gatewayapi_processor.go b/internal/dag/gatewayapi_processor.go index da9d2cd31cb..fac8bea4deb 100644 --- a/internal/dag/gatewayapi_processor.go +++ b/internal/dag/gatewayapi_processor.go @@ -1433,7 +1433,7 @@ func (p *GatewayAPIProcessor) computeHTTPRouteForListener( routes = append(routes, routesPerRule...) } - // check all the routes at once in case there is conclict + // check all the routes at once in case there is conflict if p.hasConflictRoute(listener, hosts, routes) { // skip adding the route to svhost or vhost since it's marked as conflict route routeAccessor.AddCondition( diff --git a/internal/dag/status_test.go b/internal/dag/status_test.go index bc05b8aae76..bbeca9c010e 100644 --- a/internal/dag/status_test.go +++ b/internal/dag/status_test.go @@ -5511,7 +5511,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { "test.projectcontour.io", }, Rules: []gatewayapi_v1.HTTPRouteRule{{ - Matches: gatewayapi.HTTPRouteMatch(gatewayapi_v1.PathMatchExact, "/"), + Matches: gatewayapi.HTTPRouteMatch(gatewayapi_v1.PathMatchPathPrefix, "/"), BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), }}, }, diff --git a/test/e2e/gateway/gateway_test.go b/test/e2e/gateway/gateway_test.go index 9ffa6d4de59..6404270d835 100644 --- a/test/e2e/gateway/gateway_test.go +++ b/test/e2e/gateway/gateway_test.go @@ -181,6 +181,8 @@ var _ = Describe("Gateway API", func() { f.NamespacedTest("gateway-request-redirect-rule", testWithHTTPGateway(testRequestRedirectRule)) f.NamespacedTest("gateway-backend-tls-policy", testWithHTTPGateway(testBackendTLSPolicy)) + + f.NamespacedTest("gateway-httproute-conflict-match", testWithHTTPGateway(testHTTPRouteConflictMatch)) }) Describe("Gateway with one HTTP listener and one HTTPS listener", func() { diff --git a/test/e2e/gateway/http_route_conflict_match_test.go b/test/e2e/gateway/http_route_conflict_match_test.go new file mode 100644 index 00000000000..b62a539744a --- /dev/null +++ b/test/e2e/gateway/http_route_conflict_match_test.go @@ -0,0 +1,86 @@ +// Copyright Project Contour Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build e2e + +package gateway + +import ( + . "github.com/onsi/ginkgo/v2" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/projectcontour/contour/internal/gatewayapi" + "github.com/projectcontour/contour/test/e2e" +) + +func testHTTPRouteConflictMatch(namespace string, gateway types.NamespacedName) { + Specify("Creates two http routes, second one has conflict match condition as the first one", func() { + By("create httproute-1 first") + route1 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-1", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "dolphin"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-2", 80, 1), + }, + }, + }, + } + f.CreateHTTPRouteAndWaitFor(route1, e2e.HTTPRouteAccepted) + + By("create httproute-2 with conflicted matches") + route2 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-2", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + }, + }, + } + f.CreateHTTPRouteAndWaitFor(route2, e2e.HTTPRouteNotAcceptedDueToConflict) + }) +} diff --git a/test/e2e/gatewayapi_predicates.go b/test/e2e/gatewayapi_predicates.go index a9ac70d2ee6..34b32a3e8b7 100644 --- a/test/e2e/gatewayapi_predicates.go +++ b/test/e2e/gatewayapi_predicates.go @@ -19,6 +19,8 @@ import ( meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" gatewayapi_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/projectcontour/contour/internal/status" ) // GatewayClassAccepted returns true if the gateway has a .status.conditions @@ -119,6 +121,22 @@ func HTTPRouteAccepted(route *gatewayapi_v1.HTTPRoute) bool { return false } +// HTTPRouteNotAcceptedDueToConflict returns true if the route has a .status.conditions +// entry of "Accepted: false". +func HTTPRouteNotAcceptedDueToConflict(route *gatewayapi_v1.HTTPRoute) bool { + if route == nil { + return false + } + + for _, gw := range route.Status.Parents { + if conditionExistsWithAllKeys(gw.Conditions, string(gatewayapi_v1.RouteConditionAccepted), meta_v1.ConditionFalse, string(status.ReasonRouteConflict), "HTTPRoute's Match has conflict with other HTTPRoute's Match") { + return false + } + } + + return false +} + // HTTPRouteIgnoredByContour returns true if the route has an empty .status.parents.conditions list func HTTPRouteIgnoredByContour(route *gatewayapi_v1.HTTPRoute) bool { if route == nil { @@ -194,3 +212,13 @@ func conditionExists(conditions []meta_v1.Condition, conditionType string, condi return false } + +func conditionExistsWithAllKeys(conditions []meta_v1.Condition, conditionType string, conditionStatus meta_v1.ConditionStatus, reason, message string) bool { + for _, cond := range conditions { + if cond.Type == conditionType && cond.Status == conditionStatus && cond.Reason == reason && cond.Message == message { + return true + } + } + + return false +}