diff --git a/internal/controller/gateway_controller.go b/internal/controller/gateway_controller.go index 76648a4a..e2806304 100644 --- a/internal/controller/gateway_controller.go +++ b/internal/controller/gateway_controller.go @@ -21,7 +21,6 @@ import ( "context" "errors" "fmt" - "reflect" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -35,6 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" "github.com/apache/apisix-ingress-controller/api/v1alpha1" @@ -62,13 +62,10 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { For( &gatewayv1.Gateway{}, builder.WithPredicates( - predicate.NewPredicateFuncs(r.checkGatewayClass), - ), - ). - WithEventFilter( - predicate.Or( - predicate.GenerationChangedPredicate{}, - predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()), + predicate.And( + predicate.NewPredicateFuncs(r.checkGatewayClass), + predicate.GenerationChangedPredicate{}, + ), ), ). Watches( @@ -80,7 +77,23 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { ). Watches( &gatewayv1.HTTPRoute{}, - handler.EnqueueRequestsFromMapFunc(r.listGatewaysForHTTPRoute), + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ). + Watches( + &gatewayv1.GRPCRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ). + Watches( + &gatewayv1alpha2.TCPRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ). + Watches( + &gatewayv1alpha2.TLSRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ). + Watches( + &gatewayv1alpha2.UDPRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), ). Watches( &v1alpha1.GatewayProxy{}, @@ -300,19 +313,11 @@ func (r *GatewayReconciler) listGatewaysForGatewayProxy(ctx context.Context, obj return recs } -func (r *GatewayReconciler) listGatewaysForHTTPRoute(ctx context.Context, obj client.Object) []reconcile.Request { - httpRoute, ok := obj.(*gatewayv1.HTTPRoute) - if !ok { - r.Log.Error( - fmt.Errorf("unexpected object type"), - "HTTPRoute watch predicate received unexpected object type", - "expected", "*gatewayapi.HTTPRoute", "found", reflect.TypeOf(obj), - ) - return nil - } - recs := []reconcile.Request{} - for _, routeParentStatus := range httpRoute.Status.Parents { - gatewayNamespace := httpRoute.GetNamespace() +func (r *GatewayReconciler) listGatewaysForStatusParentRefs(ctx context.Context, obj client.Object) []reconcile.Request { + route := internaltypes.NewRouteAdapter(obj) + reqs := []reconcile.Request{} + for _, routeParentStatus := range route.GetParentStatuses() { + gatewayNamespace := route.GetNamespace() parentRef := routeParentStatus.ParentRef if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName { continue @@ -336,14 +341,14 @@ func (r *GatewayReconciler) listGatewaysForHTTPRoute(ctx context.Context, obj cl continue } - recs = append(recs, reconcile.Request{ + reqs = append(reqs, reconcile.Request{ NamespacedName: client.ObjectKey{ Namespace: gatewayNamespace, Name: string(parentRef.Name), }, }) } - return recs + return reqs } func (r *GatewayReconciler) listGatewaysForSecret(ctx context.Context, obj client.Object) (requests []reconcile.Request) { diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 3803d531..f34f01a4 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -386,7 +386,7 @@ func ParseRouteParentRefs( } } - if !routeMatchesListenerType(route, listener) { + if ok, _ := routeMatchesListenerType(route, listener); !ok { continue } @@ -485,8 +485,8 @@ func checkRouteAcceptedByListener( return false, gatewayv1.RouteReasonNoMatchingParent, nil } } - if !routeMatchesListenerType(route, listener) { - return false, gatewayv1.RouteReasonNoMatchingParent, nil + if ok, err := routeMatchesListenerType(route, listener); !ok { + return false, gatewayv1.RouteReasonNoMatchingParent, err } if !routeHostnamesIntersectsWithListenerHostname(route, listener) { return false, gatewayv1.RouteReasonNoMatchingListenerHostname, nil @@ -659,71 +659,95 @@ func isRouteNamespaceAllowed( } } -func routeMatchesListenerType(route client.Object, listener gatewayv1.Listener) bool { +func routeMatchesListenerType(route client.Object, listener gatewayv1.Listener) (bool, error) { switch route.(type) { case *gatewayv1.HTTPRoute, *gatewayv1.GRPCRoute: if listener.Protocol != gatewayv1.HTTPProtocolType && listener.Protocol != gatewayv1.HTTPSProtocolType { - return false + return false, nil } if listener.Protocol == gatewayv1.HTTPSProtocolType { if listener.TLS == nil { - return false + return false, nil } if listener.TLS.Mode != nil && *listener.TLS.Mode != gatewayv1.TLSModeTerminate { - return false + return false, nil } } case *gatewayv1alpha2.TCPRoute: if listener.Protocol != gatewayv1.TCPProtocolType { - return false + return false, nil } case *gatewayv1alpha2.UDPRoute: if listener.Protocol != gatewayv1.UDPProtocolType { - return false + return false, nil } case *gatewayv1alpha2.TLSRoute: if listener.Protocol != gatewayv1.TLSProtocolType { - return false + return false, nil } default: - return false + return false, fmt.Errorf("unsupported route type %T", route) } - return true + return true, nil } func getAttachedRoutesForListener(ctx context.Context, mgrc client.Client, gateway gatewayv1.Gateway, listener gatewayv1.Listener) (int32, error) { - httpRouteList := gatewayv1.HTTPRouteList{} - if err := mgrc.List(ctx, &httpRouteList); err != nil { - return 0, err + routes := []types.RouteAdapter{} + routeList := []client.ObjectList{} + + listOption := client.MatchingFields{ + indexer.ParentRefs: indexer.GenIndexKey(gateway.Namespace, gateway.Name), } - var attachedRoutes int32 - for _, route := range httpRouteList.Items { - route := route - acceptedByGateway := lo.ContainsBy(route.Status.Parents, func(parentStatus gatewayv1.RouteParentStatus) bool { - parentRef := parentStatus.ParentRef - if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName { - return false - } - if parentRef.Kind != nil && *parentRef.Kind != KindGateway { - return false + if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil { + for _, rgk := range listener.AllowedRoutes.Kinds { + if rgk.Group != nil && *rgk.Group != gatewayv1.GroupName { + continue } - gatewayNamespace := route.Namespace - if parentRef.Namespace != nil { - gatewayNamespace = string(*parentRef.Namespace) + switch rgk.Kind { + case types.KindHTTPRoute: + routeList = append(routeList, &gatewayv1.HTTPRouteList{}) + case types.KindGRPCRoute: + routeList = append(routeList, &gatewayv1.GRPCRouteList{}) + case types.KindTCPRoute: + routeList = append(routeList, &gatewayv1alpha2.TCPRouteList{}) + case types.KindUDPRoute: + routeList = append(routeList, &gatewayv1alpha2.UDPRouteList{}) + case types.KindTLSRoute: + routeList = append(routeList, &gatewayv1alpha2.TLSRouteList{}) } - return gateway.Namespace == gatewayNamespace && gateway.Name == string(parentRef.Name) - }) - if !acceptedByGateway { - continue } + } else { + switch listener.Protocol { + case gatewayv1.HTTPProtocolType, gatewayv1.HTTPSProtocolType: + routeList = append(routeList, &gatewayv1.HTTPRouteList{}, &gatewayv1.GRPCRouteList{}) + case gatewayv1.TCPProtocolType: + routeList = append(routeList, &gatewayv1alpha2.TCPRouteList{}) + case gatewayv1.UDPProtocolType: + routeList = append(routeList, &gatewayv1alpha2.UDPRouteList{}) + case gatewayv1.TLSProtocolType: + routeList = append(routeList, &gatewayv1alpha2.TLSRouteList{}) + } + } + + for _, rl := range routeList { + if err := mgrc.List(ctx, rl, listOption); err != nil { + return 0, fmt.Errorf("failed to list %T: %w", rl, err) + } + routes = append(routes, types.NewRouteListAdapter(rl)...) + } - for _, parentRef := range route.Spec.ParentRefs { + var attachedRoutes int32 + for _, route := range routes { + if !checkStatusParent(route.GetParentStatuses(), route.GetNamespace(), gateway) { + continue + } + for _, parentRef := range route.GetParentRefs() { ok, _, err := checkRouteAcceptedByListener( ctx, mgrc, - &route, + route.GetObject(), gateway, listener, parentRef, @@ -739,13 +763,29 @@ func getAttachedRoutesForListener(ctx context.Context, mgrc client.Client, gatew return attachedRoutes, nil } +func checkStatusParent(parents []gatewayv1.RouteParentStatus, routeNamespace string, gateway gatewayv1.Gateway) bool { + return lo.ContainsBy(parents, func(parentStatus gatewayv1.RouteParentStatus) bool { + parentRef := parentStatus.ParentRef + if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName { + return false + } + if parentRef.Kind != nil && *parentRef.Kind != KindGateway { + return false + } + gatewayNamespace := routeNamespace + if parentRef.Namespace != nil { + gatewayNamespace = string(*parentRef.Namespace) + } + return gateway.Namespace == gatewayNamespace && gateway.Name == string(parentRef.Name) + }) +} + func getListenerStatus( ctx context.Context, mrgc client.Client, gateway *gatewayv1.Gateway, ) ([]gatewayv1.ListenerStatus, error) { - statuses := make(map[gatewayv1.SectionName]gatewayv1.ListenerStatus, len(gateway.Spec.Listeners)) - + statusArray := make([]gatewayv1.ListenerStatus, 0, len(gateway.Spec.Listeners)) for i, listener := range gateway.Spec.Listeners { attachedRoutes, err := getAttachedRoutesForListener(ctx, mrgc, *gateway, listener) if err != nil { @@ -786,10 +826,35 @@ func getListenerStatus( ) if listener.AllowedRoutes == nil || listener.AllowedRoutes.Kinds == nil { - supportedKinds = []gatewayv1.RouteGroupKind{ - { - Kind: KindHTTPRoute, - }, + group := gatewayv1.Group(gatewayv1.GroupName) + supportedKinds = []gatewayv1.RouteGroupKind{} + switch listener.Protocol { + case gatewayv1.TLSProtocolType: + supportedKinds = append(supportedKinds, gatewayv1.RouteGroupKind{ + Group: &group, + Kind: types.KindTLSRoute, + }) + case gatewayv1.TCPProtocolType: + supportedKinds = append(supportedKinds, gatewayv1.RouteGroupKind{ + Group: &group, + Kind: types.KindTCPRoute, + }) + case gatewayv1.UDPProtocolType: + supportedKinds = append(supportedKinds, gatewayv1.RouteGroupKind{ + Group: &group, + Kind: types.KindUDPRoute, + }) + case gatewayv1.HTTPProtocolType, gatewayv1.HTTPSProtocolType: + supportedKinds = append(supportedKinds, []gatewayv1.RouteGroupKind{ + { + Group: &group, + Kind: types.KindGRPCRoute, + }, + { + Group: &group, + Kind: types.KindHTTPRoute, + }, + }...) } } else { for _, kind := range listener.AllowedRoutes.Kinds { @@ -799,7 +864,7 @@ func getListenerStatus( continue } switch kind.Kind { - case KindHTTPRoute: + case KindHTTPRoute, types.KindGRPCRoute, types.KindTLSRoute, types.KindTCPRoute, types.KindUDPRoute: supportedKinds = append(supportedKinds, kind) default: conditionResolvedRefs.Status = metav1.ConditionFalse @@ -902,17 +967,9 @@ func getListenerStatus( changed = true } - if changed { - statuses[listener.Name] = status - } else { - statuses[listener.Name] = gateway.Status.Listeners[i] + if !changed { + status = gateway.Status.Listeners[i] } - } - - // check for conflicts - - statusArray := []gatewayv1.ListenerStatus{} - for _, status := range statuses { statusArray = append(statusArray, status) } diff --git a/internal/types/route_adapter.go b/internal/types/route_adapter.go new file mode 100644 index 00000000..11da083d --- /dev/null +++ b/internal/types/route_adapter.go @@ -0,0 +1,147 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +package types + +import ( + "github.com/samber/lo" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +type HTTPRouteAdapter struct { + *gatewayv1.HTTPRoute +} + +func (r HTTPRouteAdapter) GetParentStatuses() []gatewayv1.RouteParentStatus { + return r.Status.Parents +} +func (r HTTPRouteAdapter) GetParentRefs() []gatewayv1.ParentReference { + return r.Spec.ParentRefs +} +func (r HTTPRouteAdapter) GetObject() client.Object { + return r.HTTPRoute +} + +type GRPCRouteAdapter struct { + *gatewayv1.GRPCRoute +} + +func (r GRPCRouteAdapter) GetParentStatuses() []gatewayv1.RouteParentStatus { + return r.Status.Parents +} +func (r GRPCRouteAdapter) GetParentRefs() []gatewayv1.ParentReference { + return r.Spec.ParentRefs +} +func (r GRPCRouteAdapter) GetObject() client.Object { + return r.GRPCRoute +} + +type TCPRouteAdapter struct { + *gatewayv1alpha2.TCPRoute +} + +func (r TCPRouteAdapter) GetParentStatuses() []gatewayv1.RouteParentStatus { + return r.Status.Parents +} + +func (r TCPRouteAdapter) GetParentRefs() []gatewayv1.ParentReference { + return r.Spec.ParentRefs +} +func (r TCPRouteAdapter) GetObject() client.Object { + return r.TCPRoute +} + +type UDPRouteAdapter struct { + *gatewayv1alpha2.UDPRoute +} + +func (r UDPRouteAdapter) GetParentStatuses() []gatewayv1.RouteParentStatus { + return r.Status.Parents +} +func (r UDPRouteAdapter) GetParentRefs() []gatewayv1.ParentReference { + return r.Spec.ParentRefs +} +func (r UDPRouteAdapter) GetObject() client.Object { + return r.UDPRoute +} + +type TLSRouteAdapter struct { + *gatewayv1alpha2.TLSRoute +} + +func (r TLSRouteAdapter) GetParentStatuses() []gatewayv1.RouteParentStatus { + return r.Status.Parents +} +func (r TLSRouteAdapter) GetParentRefs() []gatewayv1.ParentReference { + return r.Spec.ParentRefs +} +func (r TLSRouteAdapter) GetObject() client.Object { + return r.TLSRoute +} + +type RouteAdapter interface { + client.Object + GetParentStatuses() []gatewayv1.RouteParentStatus + GetParentRefs() []gatewayv1.ParentReference + GetObject() client.Object +} + +func NewRouteAdapter(obj client.Object) RouteAdapter { + switch r := obj.(type) { + case *gatewayv1.HTTPRoute: + return &HTTPRouteAdapter{HTTPRoute: r} + case *gatewayv1.GRPCRoute: + return &GRPCRouteAdapter{GRPCRoute: r} + case *gatewayv1alpha2.TLSRoute: + return &TLSRouteAdapter{TLSRoute: r} + case *gatewayv1alpha2.TCPRoute: + return &TCPRouteAdapter{TCPRoute: r} + case *gatewayv1alpha2.UDPRoute: + return &UDPRouteAdapter{UDPRoute: r} + default: + return nil + } +} + +func NewRouteListAdapter(objList client.ObjectList) []RouteAdapter { + switch r := objList.(type) { + case *gatewayv1.HTTPRouteList: + return lo.Map(r.Items, func(item gatewayv1.HTTPRoute, _ int) RouteAdapter { + return &HTTPRouteAdapter{HTTPRoute: &item} + }) + case *gatewayv1.GRPCRouteList: + return lo.Map(r.Items, func(item gatewayv1.GRPCRoute, _ int) RouteAdapter { + return &GRPCRouteAdapter{GRPCRoute: &item} + }) + case *gatewayv1alpha2.TLSRouteList: + return lo.Map(r.Items, func(item gatewayv1alpha2.TLSRoute, _ int) RouteAdapter { + return &TLSRouteAdapter{TLSRoute: &item} + }) + case *gatewayv1alpha2.TCPRouteList: + return lo.Map(r.Items, func(item gatewayv1alpha2.TCPRoute, _ int) RouteAdapter { + return &TCPRouteAdapter{TCPRoute: &item} + }) + case *gatewayv1alpha2.UDPRouteList: + return lo.Map(r.Items, func(item gatewayv1alpha2.UDPRoute, _ int) RouteAdapter { + return &UDPRouteAdapter{UDPRoute: &item} + }) + default: + return nil + } +} diff --git a/test/e2e/gatewayapi/gateway.go b/test/e2e/gatewayapi/gateway.go index 97eee6ed..a5f19cd7 100644 --- a/test/e2e/gatewayapi/gateway.go +++ b/test/e2e/gatewayapi/gateway.go @@ -26,6 +26,9 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" + k8stypes "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/apache/apisix-ingress-controller/test/e2e/framework" "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" @@ -315,4 +318,225 @@ spec: }).WithTimeout(20 * time.Second).ProbeEvery(time.Second).Should(Equal(framework.TestCert)) }) }) + + Context("Gateway Status", func() { + var gatewaySpec = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: %s +spec: + gatewayClassName: %s + listeners: + - name: http + protocol: HTTP + port: 80 + - name: tcp + protocol: TCP + port: 9000 + allowedRoutes: + kinds: + - kind: TCPRoute + - name: udp + protocol: UDP + port: 80 + allowedRoutes: + kinds: + - kind: UDPRoute + infrastructure: + parametersRef: + group: apisix.apache.org + kind: GatewayProxy + name: apisix-proxy-config +` + var httprouteSpec = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %s +spec: + parentRefs: + - name: %s + hostnames: + - httpbin.org + rules: + - matches: + - path: + type: Exact + value: /get + backendRefs: + - name: httpbin-service-e2e-test + port: 80 +` + var grpcrouteSpec = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: GRPCRoute +metadata: + name: %s +spec: + parentRefs: + - name: %s + rules: + - backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 +` + var tcpRouteSpec = ` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: %s +spec: + parentRefs: + - name: %s + sectionName: tcp + rules: + - backendRefs: + - name: httpbin-service-e2e-test + port: 80 +` + var udpRoute = ` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: UDPRoute +metadata: + name: %s +spec: + parentRefs: + - name: %s + sectionName: udp + rules: + - backendRefs: + - name: %s + port: %d +` + var ( + dnsName string + dnsPort int32 + ) + + BeforeEach(func() { + dnsSvc := s.NewCoreDNSService() + dnsName = dnsSvc.Name + dnsPort = dnsSvc.Spec.Ports[0].Port + + By("deploy grpc backend") + s.DeployGRPCBackend() + + By("create GatewayProxy") + Expect(s.CreateResourceFromString(s.GetGatewayProxySpec())).NotTo(HaveOccurred(), "creating GatewayProxy") + + By("create GatewayClass") + Expect(s.CreateResourceFromString(s.GetGatewayClassYaml())).NotTo(HaveOccurred(), "creating GatewayClass") + + s.RetryAssertion(func() string { + gcyaml, _ := s.GetResourceYaml("GatewayClass", s.Namespace()) + return gcyaml + }).Should( + And( + ContainSubstring(`status: "True"`), + ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), + ), + "check GatewayClass condition", + ) + + By("create Gateway") + gateway := fmt.Sprintf(gatewaySpec, s.Namespace(), s.Namespace()) + Expect(s.CreateResourceFromString(gateway)).NotTo(HaveOccurred(), "creating Gateway") + + s.RetryAssertion(func() string { + gcyaml, _ := s.GetResourceYaml("Gateway", s.Namespace()) + return gcyaml + }).Should( + And( + ContainSubstring(`status: "True"`), + ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), + ), + "check Gateway condition status", + ) + }) + getStatusLintener := func(listenerName string) (*gatewayv1.ListenerStatus, error) { + var gateway gatewayv1.Gateway + if err := s.GetKubeClient().Get(context.Background(), k8stypes.NamespacedName{ + Name: s.Namespace(), + Namespace: s.Namespace(), + }, &gateway); err != nil { + return nil, err + } + for _, listener := range gateway.Status.Listeners { + if string(listener.Name) == listenerName { + return &listener, nil + } + } + return nil, fmt.Errorf("listener %s not found", listenerName) + } + expectListenerAttachedRoutes := func(listenerName string, accectedRoutes int) { + s.RetryAssertion(func() error { + listener, err := getStatusLintener(listenerName) + if err != nil { + return err + } + if listener.AttachedRoutes != int32(accectedRoutes) { + return fmt.Errorf("expected listener %s attached routes to be %d, got %d", listener.Name, accectedRoutes, listener.AttachedRoutes) + } + return nil + }).ShouldNot(HaveOccurred(), "check listener attached routes") + } + It("check attachedRoutes and supportedkinds to gateway status", func() { + By("check HTTPRoute/GRPCRoute attachedRoutes and supportedkinds to gateway status") + s.ResourceApplied("HTTPRoute", "httpbin", fmt.Sprintf(httprouteSpec, "httpbin", s.Namespace()), 1) + expectListenerAttachedRoutes("http", 1) + for i := 0; i < 10; i++ { + name := fmt.Sprintf("httpbin-%d", i) + s.ResourceApplied("HTTPRoute", name, fmt.Sprintf(httprouteSpec, name, s.Namespace()), 1) + } + expectListenerAttachedRoutes("http", 11) + for i := 0; i < 10; i++ { + name := fmt.Sprintf("grcproute-%d", i) + s.ResourceApplied("GRPCRoute", name, fmt.Sprintf(grpcrouteSpec, name, s.Namespace()), 1) + } + expectListenerAttachedRoutes("http", 21) + + listenner, err := getStatusLintener("http") + Expect(err).NotTo(HaveOccurred(), "get http listener status") + Expect(listenner.SupportedKinds).To(HaveLen(2), "http listener supported kinds length") + Expect(listenner.SupportedKinds).To(ContainElements( + gatewayv1.RouteGroupKind{ + Group: ptr.To(gatewayv1.Group(gatewayv1.GroupName)), + Kind: "HTTPRoute", + }, + gatewayv1.RouteGroupKind{ + Group: ptr.To(gatewayv1.Group(gatewayv1.GroupName)), + Kind: "GRPCRoute", + }, + ), "http listener supported kinds content") + + By("check TCPRoute attachedRoutes and supportedkinds to gateway status") + name := "tcp-route" + s.ResourceApplied("TCPRoute", name, fmt.Sprintf(tcpRouteSpec, name, s.Namespace()), 1) + expectListenerAttachedRoutes("tcp", 1) + for i := 0; i < 10; i++ { + name := fmt.Sprintf("tcp-route-%d", i) + s.ResourceApplied("TCPRoute", name, fmt.Sprintf(tcpRouteSpec, name, s.Namespace()), 1) + } + expectListenerAttachedRoutes("tcp", 11) + getListener, err := getStatusLintener("tcp") + Expect(err).NotTo(HaveOccurred(), "get tcp listener status") + Expect(getListener.SupportedKinds).To(HaveLen(1), "tcp listener supported kinds length") + Expect(string(getListener.SupportedKinds[0].Kind)).To(Equal("TCPRoute"), "tcp listener supported kind content") + + By("check UDPRoute attachedRoutes and supportedkinds to gateway status") + name = "udp-route" + s.ResourceApplied("UDPRoute", name, fmt.Sprintf(udpRoute, name, s.Namespace(), dnsName, dnsPort), 1) + expectListenerAttachedRoutes("udp", 1) + for i := 0; i < 10; i++ { + name := fmt.Sprintf("udp-route-%d", i) + s.ResourceApplied("UDPRoute", name, fmt.Sprintf(udpRoute, name, s.Namespace(), dnsName, dnsPort), 1) + } + expectListenerAttachedRoutes("udp", 11) + getListener, err = getStatusLintener("udp") + Expect(err).NotTo(HaveOccurred(), "get udp listener status") + Expect(getListener.SupportedKinds).To(HaveLen(1), "udp listener supported kinds length") + Expect(string(getListener.SupportedKinds[0].Kind)).To(Equal("UDPRoute"), "udp listener supported kind content") + }) + }) }) diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go index a3547b61..2694832a 100644 --- a/test/e2e/scaffold/k8s.go +++ b/test/e2e/scaffold/k8s.go @@ -36,12 +36,16 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + client "sigs.k8s.io/controller-runtime/pkg/client" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" + apiv2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/test/e2e/framework" ) @@ -430,3 +434,18 @@ func (s *Scaffold) SetupWebhookResources() error { return s.CreateResourceFromStringWithNamespace(buf.String(), "") } + +func (s *Scaffold) GetKubeClient() client.Client { + if s.client == nil { + scheme := runtime.NewScheme() + _ = apiv2.AddToScheme(scheme) + _ = corev1.AddToScheme(scheme) + _ = gatewayv1.Install(scheme) + _ = v1alpha2.Install(scheme) + cfg, err := clientcmd.BuildConfigFromFlags("", s.opts.Kubeconfig) + Expect(err).NotTo(HaveOccurred(), "building kubeconfig") + s.client, err = client.New(cfg, client.Options{Scheme: scheme}) + Expect(err).NotTo(HaveOccurred(), "building controller-runtime client") + } + return s.client +} diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go index 9256a97d..fc6c36c7 100644 --- a/test/e2e/scaffold/scaffold.go +++ b/test/e2e/scaffold/scaffold.go @@ -35,6 +35,7 @@ import ( . "github.com/onsi/gomega" //nolint:staticcheck corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/test/e2e/framework" @@ -79,6 +80,8 @@ type Scaffold struct { runtimeOpts Options Deployer Deployer + + client client.Client } type Tunnels struct {