diff --git a/CHANGELOG.md b/CHANGELOG.md index ff97baf7cc..bf29a7eb4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,7 +136,8 @@ Adding a new version? You'll need three changes: plugins. Reference of using Kong vaults: [Kong vault] [#5354](https://github.com/Kong/kubernetes-ingress-controller/pull/5354) [#5384](https://github.com/Kong/kubernetes-ingress-controller/pull/5384) - +- Add new flag `--gateway-namespaced-name` KIC can only reconciling the specified Gateway. + [#5405](https://github.com/Kong/kubernetes-ingress-controller/pull/5405) ### Fixed diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index 86a0abec43..56c945b6e4 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -39,6 +39,7 @@ | `--feature-gates` | `list of string=bool` | A set of comma separated key=value pairs that describe feature gates for alpha/beta/experimental features. See the Feature Gates documentation for information and available options: https://github.com/Kong/kubernetes-ingress-controller/blob/main/FEATURE_GATES.md. | | | `--gateway-api-controller-name` | `string` | The controller name to match on Gateway API resources. | `konghq.com/kic-gateway-controller` | | `--gateway-discovery-dns-strategy` | `dns-strategy` | DNS strategy to use when creating Gateway's Admin API addresses. One of: ip, service, pod. | `"ip"` | +| `--gateway-namespaced-name` | `namespaced-name` | Gateway namespaced name in "namespace/name" format, to use for KIC can only reconciling the specified Gateway. | | | `--health-probe-bind-address` | `string` | The address the probe endpoint binds to. | `:10254` | | `--ingress-class` | `string` | Name of the ingress class to route through this controller. | `kong` | | `--init-cache-sync-duration` | `duration` | The initial delay to wait for Kubernetes object caches to be synced before the initial configuration. | `5s` | diff --git a/internal/controllers/gateway/gateway_controller.go b/internal/controllers/gateway/gateway_controller.go index 0a8f2e44bf..f6871e61f1 100644 --- a/internal/controllers/gateway/gateway_controller.go +++ b/internal/controllers/gateway/gateway_controller.go @@ -58,6 +58,10 @@ type GatewayReconciler struct { //nolint:revive // to invalidate or allow cross-namespace TLSConfigs in gateways. // It's resolved on SetupWithManager call. enableReferenceGrant bool + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -173,6 +177,16 @@ func (r *GatewayReconciler) gatewayHasMatchingGatewayClass(obj client.Object) bo ) return false } + + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gateway.Namespace != r.GatewayNN.Namespace || gateway.Name != r.GatewayNN.Name { + return false + } + } + gatewayClass := &gatewayapi.GatewayClass{} if err := r.Client.Get(context.Background(), client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, gatewayClass); err != nil { r.Log.Error(err, "Could not retrieve gatewayclass", "gatewayclass", gateway.Spec.GatewayClassName) @@ -327,6 +341,13 @@ func referenceGrantHasGatewayFrom(obj client.Object) bool { func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("GatewayV1Gateway", req.NamespacedName) + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if req.Namespace != r.GatewayNN.Namespace || req.Name != r.GatewayNN.Name { + r.Log.V(util.DebugLevel).Info("The request does not match the specified Gateway and will be skipped.", "gateway", r.GatewayNN.String()) + return ctrl.Result{}, nil + } + } + // gather the gateway object based on the reconciliation trigger. It's possible for the object // to be gone at this point in which case it will be ignored. gateway := new(gatewayapi.Gateway) diff --git a/internal/controllers/gateway/grpcroute_controller.go b/internal/controllers/gateway/grpcroute_controller.go index f0762693e3..1e759a81b4 100644 --- a/internal/controllers/gateway/grpcroute_controller.go +++ b/internal/controllers/gateway/grpcroute_controller.go @@ -47,6 +47,10 @@ type GRPCRouteReconciler struct { // If it is false, referencing backend in different namespace will be rejected. EnableReferenceGrant bool CacheSyncTimeout time.Duration + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -143,11 +147,22 @@ func (r *GRPCRouteReconciler) listGRPCRoutesForGatewayClass(ctx context.Context, gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { if string(gateway.Spec.GatewayClassName) == gwc.Name { - _, ok := gateways[gateway.Namespace] - if !ok { - gateways[gateway.Namespace] = make(map[string]struct{}) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + _, ok := gateways[r.GatewayNN.Namespace] + if !ok { + gateways[r.GatewayNN.Namespace] = make(map[string]struct{}) + } + gateways[r.GatewayNN.Namespace][r.GatewayNN.Name] = struct{}{} + } else { + _, ok := gateways[gateway.Namespace] + if !ok { + gateways[gateway.Namespace] = make(map[string]struct{}) + } + gateways[gateway.Namespace][gateway.Name] = struct{}{} } - gateways[gateway.Namespace][gateway.Name] = struct{}{} } } @@ -217,6 +232,15 @@ func (r *GRPCRouteReconciler) listGRPCRoutesForGateway(ctx context.Context, obj return nil } + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gw.Namespace != r.GatewayNN.Namespace || gw.Name != r.GatewayNN.Name { + return nil + } + } + // map all GRPCRoute objects grpcrouteList := gatewayapi.GRPCRouteList{} if err := r.Client.List(ctx, &grpcrouteList); err != nil { @@ -293,7 +317,7 @@ func (r *GRPCRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // we need to pull the Gateway parent objects for the grpcroute to verify // routing behavior and ensure compatibility with Gateway configurations. debug(log, grpcroute, "Retrieving GatewayClass and Gateway for route") - gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, grpcroute) + gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, grpcroute, r.GatewayNN) if err != nil { if err.Error() == unsupportedGW { debug(log, grpcroute, "Unsupported route found, processing to verify whether it was ever supported") diff --git a/internal/controllers/gateway/httproute_controller.go b/internal/controllers/gateway/httproute_controller.go index 75b045556d..f623964b72 100644 --- a/internal/controllers/gateway/httproute_controller.go +++ b/internal/controllers/gateway/httproute_controller.go @@ -52,6 +52,10 @@ type HTTPRouteReconciler struct { // If it is false, referencing backend in different namespace will be rejected. // It's resolved on SetupWithManager call. enableReferenceGrant bool + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -217,11 +221,22 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGatewayClass(ctx context.Context, gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { if string(gateway.Spec.GatewayClassName) == gwc.Name { - _, ok := gateways[gateway.Namespace] - if !ok { - gateways[gateway.Namespace] = make(map[string]struct{}) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + _, ok := gateways[r.GatewayNN.Namespace] + if !ok { + gateways[r.GatewayNN.Namespace] = make(map[string]struct{}) + } + gateways[r.GatewayNN.Namespace][r.GatewayNN.Name] = struct{}{} + } else { + _, ok := gateways[gateway.Namespace] + if !ok { + gateways[gateway.Namespace] = make(map[string]struct{}) + } + gateways[gateway.Namespace][gateway.Name] = struct{}{} } - gateways[gateway.Namespace][gateway.Name] = struct{}{} } } @@ -291,6 +306,15 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGateway(ctx context.Context, obj return nil } + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gw.Namespace != r.GatewayNN.Namespace || gw.Name != r.GatewayNN.Name { + return nil + } + } + // map all HTTPRoute objects httprouteList := gatewayapi.HTTPRouteList{} if err := r.Client.List(ctx, &httprouteList); err != nil { @@ -368,7 +392,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // we need to pull the Gateway parent objects for the HTTPRoute to verify // routing behavior and ensure compatibility with Gateway configurations. debug(log, httproute, "Retrieving GatewayClass and Gateway for route") - gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, httproute) + gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, httproute, r.GatewayNN) if err != nil { if err.Error() == unsupportedGW { debug(log, httproute, "Unsupported route found, processing to verify whether it was ever supported") diff --git a/internal/controllers/gateway/route_utils.go b/internal/controllers/gateway/route_utils.go index 736ff6b557..579237bd6a 100644 --- a/internal/controllers/gateway/route_utils.go +++ b/internal/controllers/gateway/route_utils.go @@ -14,6 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + k8stypes "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -125,7 +126,9 @@ func parentRefsForRoute[T gatewayapi.RouteT](route T) ([]gatewayapi.ParentRefere // Gateway APIs route object (e.g. HTTPRoute, TCPRoute, e.t.c.) from the provided cached // client if they match this controller. If there are no gateways present for this route // OR the present gateways are references to missing objects, this will return a unsupportedGW error. -func getSupportedGatewayForRoute[T gatewayapi.RouteT](ctx context.Context, logger logr.Logger, mgrc client.Client, route T) ([]supportedGatewayWithCondition, error) { +// +// There is a parameter `specifiedGW` here, which is used to specific the gateway. +func getSupportedGatewayForRoute[T gatewayapi.RouteT](ctx context.Context, logger logr.Logger, mgrc client.Client, route T, specifiedGW k8stypes.NamespacedName) ([]supportedGatewayWithCondition, error) { // gather the parentrefs for this route object parentRefs, err := parentRefsForRoute(route) if err != nil { @@ -145,6 +148,14 @@ func getSupportedGatewayForRoute[T gatewayapi.RouteT](ctx context.Context, logge } name := string(parentRef.Name) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if specifiedGW.Namespace != "" && specifiedGW.Name != "" { + namespace = specifiedGW.Namespace + name = specifiedGW.Name + } + // pull the Gateway object from the cached client gateway := gatewayapi.Gateway{} if err := mgrc.Get(ctx, client.ObjectKey{ diff --git a/internal/controllers/gateway/route_utils_test.go b/internal/controllers/gateway/route_utils_test.go index ef2e968a0f..e8dd8b3833 100644 --- a/internal/controllers/gateway/route_utils_test.go +++ b/internal/controllers/gateway/route_utils_test.go @@ -542,7 +542,7 @@ func TestGetSupportedGatewayForRoute(t *testing.T) { WithObjects(tt.objects...). Build() - got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route) + got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route, k8stypes.NamespacedName{}) require.NoError(t, err) require.Len(t, got, len(tt.expected)) @@ -813,7 +813,7 @@ func TestGetSupportedGatewayForRoute(t *testing.T) { WithObjects(tt.objects...). Build() - got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route) + got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route, k8stypes.NamespacedName{}) require.NoError(t, err) require.Len(t, got, 1) match := got[0] @@ -1048,7 +1048,7 @@ func TestGetSupportedGatewayForRoute(t *testing.T) { WithObjects(tt.objects...). Build() - got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route) + got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route, k8stypes.NamespacedName{}) require.NoError(t, err) require.Len(t, got, 1) match := got[0] @@ -1270,7 +1270,7 @@ func TestGetSupportedGatewayForRoute(t *testing.T) { WithObjects(tt.objects...). Build() - got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route) + got, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, tt.route, k8stypes.NamespacedName{}) require.NoError(t, err) require.Len(t, got, len(tt.expected)) @@ -1300,7 +1300,7 @@ func TestGetSupportedGatewayForRoute(t *testing.T) { WithScheme(scheme.Scheme). Build() - _, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, bustedParentHTTPRoute) + _, err := getSupportedGatewayForRoute(context.Background(), logr.Discard(), fakeClient, bustedParentHTTPRoute, k8stypes.NamespacedName{}) require.Equal(t, fmt.Errorf("unsupported parent kind %s/%s", string(badGroup), string(badKind)), err) }) } diff --git a/internal/controllers/gateway/tcproute_controller.go b/internal/controllers/gateway/tcproute_controller.go index 5318c67aee..9e01f15464 100644 --- a/internal/controllers/gateway/tcproute_controller.go +++ b/internal/controllers/gateway/tcproute_controller.go @@ -44,6 +44,10 @@ type TCPRouteReconciler struct { DataplaneClient controllers.DataPlane CacheSyncTimeout time.Duration StatusQueue *status.Queue + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -140,11 +144,22 @@ func (r *TCPRouteReconciler) listTCPRoutesForGatewayClass(ctx context.Context, o gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { if string(gateway.Spec.GatewayClassName) == gwc.Name { - _, ok := gateways[gateway.Namespace] - if !ok { - gateways[gateway.Namespace] = make(map[string]struct{}) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + _, ok := gateways[r.GatewayNN.Namespace] + if !ok { + gateways[r.GatewayNN.Namespace] = make(map[string]struct{}) + } + gateways[r.GatewayNN.Namespace][r.GatewayNN.Name] = struct{}{} + } else { + _, ok := gateways[gateway.Namespace] + if !ok { + gateways[gateway.Namespace] = make(map[string]struct{}) + } + gateways[gateway.Namespace][gateway.Name] = struct{}{} } - gateways[gateway.Namespace][gateway.Name] = struct{}{} } } @@ -214,6 +229,15 @@ func (r *TCPRouteReconciler) listTCPRoutesForGateway(ctx context.Context, obj cl return nil } + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gw.Namespace != r.GatewayNN.Namespace || gw.Name != r.GatewayNN.Name { + return nil + } + } + // map all TCPRoute objects tcprouteList := gatewayapi.TCPRouteList{} if err := r.Client.List(ctx, &tcprouteList); err != nil { @@ -290,7 +314,7 @@ func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // we need to pull the Gateway parent objects for the TCPRoute to verify // routing behavior and ensure compatibility with Gateway configurations. debug(log, tcproute, "Retrieving GatewayClass and Gateway for route") - gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, tcproute) + gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, tcproute, r.GatewayNN) if err != nil { if err.Error() == unsupportedGW { debug(log, tcproute, "Unsupported route found, processing to verify whether it was ever supported") diff --git a/internal/controllers/gateway/tlsroute_controller.go b/internal/controllers/gateway/tlsroute_controller.go index 36d9cf6d32..4d2541cc5f 100644 --- a/internal/controllers/gateway/tlsroute_controller.go +++ b/internal/controllers/gateway/tlsroute_controller.go @@ -43,6 +43,10 @@ type TLSRouteReconciler struct { DataplaneClient controllers.DataPlane CacheSyncTimeout time.Duration StatusQueue *status.Queue + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -139,11 +143,22 @@ func (r *TLSRouteReconciler) listTLSRoutesForGatewayClass(ctx context.Context, o gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { if string(gateway.Spec.GatewayClassName) == gwc.Name { - _, ok := gateways[gateway.Namespace] - if !ok { - gateways[gateway.Namespace] = make(map[string]struct{}) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + _, ok := gateways[r.GatewayNN.Namespace] + if !ok { + gateways[r.GatewayNN.Namespace] = make(map[string]struct{}) + } + gateways[r.GatewayNN.Namespace][r.GatewayNN.Name] = struct{}{} + } else { + _, ok := gateways[gateway.Namespace] + if !ok { + gateways[gateway.Namespace] = make(map[string]struct{}) + } + gateways[gateway.Namespace][gateway.Name] = struct{}{} } - gateways[gateway.Namespace][gateway.Name] = struct{}{} } } @@ -213,6 +228,15 @@ func (r *TLSRouteReconciler) listTLSRoutesForGateway(ctx context.Context, obj cl return nil } + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gw.Namespace != r.GatewayNN.Namespace || gw.Name != r.GatewayNN.Name { + return nil + } + } + // map all TLSRoute objects tlsrouteList := gatewayapi.TLSRouteList{} if err := r.Client.List(ctx, &tlsrouteList); err != nil { @@ -289,7 +313,7 @@ func (r *TLSRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // we need to pull the Gateway parent objects for the TLSRoute to verify // routing behavior and ensure compatibility with Gateway configurations. debug(log, tlsroute, "Retrieving GatewayClass and Gateway for route") - gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, tlsroute) + gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, tlsroute, r.GatewayNN) if err != nil { if err.Error() == unsupportedGW { debug(log, tlsroute, "Unsupported route found, processing to verify whether it was ever supported") diff --git a/internal/controllers/gateway/udproute_controller.go b/internal/controllers/gateway/udproute_controller.go index 7f88c284d0..8d3fcf4de8 100644 --- a/internal/controllers/gateway/udproute_controller.go +++ b/internal/controllers/gateway/udproute_controller.go @@ -43,6 +43,10 @@ type UDPRouteReconciler struct { DataplaneClient controllers.DataPlane CacheSyncTimeout time.Duration StatusQueue *status.Queue + + // If GatewayNN is set, + // only resources managed by the specified Gateway are reconciled. + GatewayNN k8stypes.NamespacedName } // SetupWithManager sets up the controller with the Manager. @@ -139,11 +143,22 @@ func (r *UDPRouteReconciler) listUDPRoutesForGatewayClass(ctx context.Context, o gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { if string(gateway.Spec.GatewayClassName) == gwc.Name { - _, ok := gateways[gateway.Namespace] - if !ok { - gateways[gateway.Namespace] = make(map[string]struct{}) + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + _, ok := gateways[r.GatewayNN.Namespace] + if !ok { + gateways[r.GatewayNN.Namespace] = make(map[string]struct{}) + } + gateways[r.GatewayNN.Namespace][r.GatewayNN.Name] = struct{}{} + } else { + _, ok := gateways[gateway.Namespace] + if !ok { + gateways[gateway.Namespace] = make(map[string]struct{}) + } + gateways[gateway.Namespace][gateway.Name] = struct{}{} } - gateways[gateway.Namespace][gateway.Name] = struct{}{} } } @@ -213,6 +228,15 @@ func (r *UDPRouteReconciler) listUDPRoutesForGateway(ctx context.Context, obj cl return nil } + // KIC introduced a new flag called `--gateway-namespaced-name`. + // If this flag is set, KIC will only reconcile the specified gateway. + // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 + if r.GatewayNN.Namespace != "" && r.GatewayNN.Name != "" { + if gw.Namespace != r.GatewayNN.Namespace || gw.Name != r.GatewayNN.Name { + return nil + } + } + // map all UDPRoute objects udprouteList := gatewayapi.UDPRouteList{} if err := r.Client.List(ctx, &udprouteList); err != nil { @@ -289,7 +313,7 @@ func (r *UDPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // we need to pull the Gateway parent objects for the UDPRoute to verify // routing behavior and ensure compatibility with Gateway configurations. debug(log, udproute, "Retrieving GatewayClass and Gateway for route") - gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, udproute) + gateways, err := getSupportedGatewayForRoute(ctx, log, r.Client, udproute, r.GatewayNN) if err != nil { if err.Error() == unsupportedGW { debug(log, udproute, "Unsupported route found, processing to verify whether it was ever supported") diff --git a/internal/manager/config.go b/internal/manager/config.go index ae40c81b9e..88621bb04f 100644 --- a/internal/manager/config.go +++ b/internal/manager/config.go @@ -116,6 +116,9 @@ type Config struct { GatewayAPIHTTPRouteController bool GatewayAPIReferenceGrantController bool + // KIC can only reconciling the specified Gateway. + GatewayNamespacedName OptionalNamespacedName + // Admission Webhook server config AdmissionServer admission.ServerConfig @@ -249,6 +252,8 @@ func (c *Config) FlagSet() *pflag.FlagSet { flagSet.BoolVar(&c.GatewayAPIGatewayController, "enable-controller-gwapi-gateway", true, "Enable the Gateway API Gateway controller.") flagSet.BoolVar(&c.GatewayAPIHTTPRouteController, "enable-controller-gwapi-httproute", true, "Enable the Gateway API HTTPRoute controller.") flagSet.BoolVar(&c.GatewayAPIReferenceGrantController, "enable-controller-gwapi-reference-grant", true, "Enable the Gateway API ReferenceGrant controller.") + flagSet.Var(flags.NewValidatedValue(&c.GatewayNamespacedName, namespacedNameFromFlagValueAllowEmpty, nnTypeNameOverride), "gateway-namespaced-name", + `Gateway namespaced name in "namespace/name" format, to use for KIC can only reconciling the specified Gateway.`) flagSet.BoolVar(&c.KongServiceFacadeEnabled, "enable-controller-kong-service-facade", true, "Enable the KongServiceFacade controller.") flagSet.BoolVar(&c.KongVaultEnabled, "enable-controller-kong-vault", true, "Enable the KongVault controller.") diff --git a/internal/manager/config_validation.go b/internal/manager/config_validation.go index 62e45f7f17..0c07212e38 100644 --- a/internal/manager/config_validation.go +++ b/internal/manager/config_validation.go @@ -37,6 +37,16 @@ func namespacedNameFromFlagValue(flagValue string) (OptionalNamespacedName, erro }), nil } +func namespacedNameFromFlagValueAllowEmpty(flagValue string) (OptionalNamespacedName, error) { + if flagValue == "" { + return mo.Some(k8stypes.NamespacedName{ + Namespace: "", + Name: "", + }), nil + } + return namespacedNameFromFlagValue(flagValue) +} + func gatewayAPIControllerNameFromFlagValue(flagValue string) (string, error) { if !gatewayAPIControllerNameRegex.MatchString(flagValue) { return "", errors.New("the expected format is example.com/controller-name") diff --git a/internal/manager/controllerdef.go b/internal/manager/controllerdef.go index ed201983ea..695d2d3776 100644 --- a/internal/manager/controllerdef.go +++ b/internal/manager/controllerdef.go @@ -318,6 +318,7 @@ func setupControllers( WatchNamespaces: c.WatchNamespaces, CacheSyncTimeout: c.CacheSyncTimeout, ReferenceIndexers: referenceIndexers, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, @@ -339,6 +340,7 @@ func setupControllers( DataplaneClient: dataplaneClient, CacheSyncTimeout: c.CacheSyncTimeout, StatusQueue: kubernetesStatusQueue, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, @@ -383,6 +385,7 @@ func setupControllers( DataplaneClient: dataplaneClient, CacheSyncTimeout: c.CacheSyncTimeout, StatusQueue: kubernetesStatusQueue, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, @@ -404,6 +407,7 @@ func setupControllers( DataplaneClient: dataplaneClient, CacheSyncTimeout: c.CacheSyncTimeout, StatusQueue: kubernetesStatusQueue, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, @@ -425,6 +429,7 @@ func setupControllers( DataplaneClient: dataplaneClient, CacheSyncTimeout: c.CacheSyncTimeout, StatusQueue: kubernetesStatusQueue, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, @@ -446,6 +451,7 @@ func setupControllers( DataplaneClient: dataplaneClient, CacheSyncTimeout: c.CacheSyncTimeout, StatusQueue: kubernetesStatusQueue, + GatewayNN: c.GatewayNamespacedName.OrEmpty(), }, }, }, diff --git a/test/envtest/run.go b/test/envtest/run.go index 849d1da732..869ff35b3d 100644 --- a/test/envtest/run.go +++ b/test/envtest/run.go @@ -3,6 +3,7 @@ package envtest import ( "context" "fmt" + "strings" "sync" "testing" "time" @@ -88,6 +89,19 @@ func WithGatewayAPIControllers() func(cfg *manager.Config) { } } +func WithGatewayNamespacedName(gatewayNN string) func(cfg *manager.Config) { + parts := strings.SplitN(gatewayNN, "/", 3) + if len(parts) != 2 { + panic("the expected format if namespace/name") + } + return func(cfg *manager.Config) { + cfg.GatewayNamespacedName = mo.Some(k8stypes.NamespacedName{ + Namespace: parts[0], + Name: parts[1], + }) + } +} + func WithPublishService(namespace string) func(cfg *manager.Config) { return func(cfg *manager.Config) { cfg.PublishStatusAddress = []string{"127.0.0.1"} diff --git a/test/envtest/specific_gateway_envtest_test.go b/test/envtest/specific_gateway_envtest_test.go new file mode 100644 index 0000000000..0486b15153 --- /dev/null +++ b/test/envtest/specific_gateway_envtest_test.go @@ -0,0 +1,77 @@ +//go:build envtest + +package envtest + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi" + "github.com/samber/lo" + "github.com/stretchr/testify/require" + k8stypes "k8s.io/apimachinery/pkg/types" +) + +func TestSpecificGatewayNN(t *testing.T) { + t.Parallel() + + const ( + waitTime = time.Minute + tickTime = 500 * time.Millisecond + ) + + scheme := Scheme(t, WithGatewayAPI, WithKong) + envcfg := Setup(t, scheme) + ctrlClient := NewControllerClient(t, scheme, envcfg) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + gw := deployGateway(ctx, t, ctrlClient) + ignoredGW := deployGateway(ctx, t, ctrlClient) + RunManager(ctx, t, envcfg, + AdminAPIOptFns(), + WithPublishService(gw.Namespace), + WithGatewayFeatureEnabled, + WithGatewayAPIControllers(), + WithGatewayNamespacedName(fmt.Sprintf("%s/%s", gw.Namespace, gw.Name)), + ) + + createHTTPRoutes(ctx, t, ctrlClient, gw, 1) + createHTTPRoutes(ctx, t, ctrlClient, ignoredGW, 1) + + require.Eventually(t, func() bool { + err := ctrlClient.Get(ctx, k8stypes.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}, &gw) + if err != nil { + t.Logf("Failed to get gateway %s/%s: %v", gw.Namespace, gw.Name, err) + return false + } + httpListener, ok := lo.Find(gw.Status.Listeners, func(listener gatewayapi.ListenerStatus) bool { + return listener.Name == "http" + }) + if !ok { + t.Logf("Failed to find http listener status in gateway %s/%s", gw.Namespace, gw.Name) + return false + } + if httpListener.AttachedRoutes != 1 { + t.Logf("Expected %d routes to be attached to the http listener, got %d", 1, httpListener.AttachedRoutes) + return false + } + + err = ctrlClient.Get(ctx, k8stypes.NamespacedName{Namespace: ignoredGW.Namespace, Name: ignoredGW.Name}, &ignoredGW) + if err != nil { + t.Logf("Failed to get gateway %s/%s: %v", ignoredGW.Namespace, ignoredGW.Name, err) + return false + } + + // ignoredGW.Status.Listeners should be [] + if len(ignoredGW.Status.Listeners) != 0 { + t.Logf("Expected %s/%s gateway should not be processed.", ignoredGW.Namespace, ignoredGW.Name) + return false + } + + return true + }, waitTime, tickTime, "failed to reconcile all HTTPRoutes") + +}