diff --git a/internal/controllers/gateway/gateway_controller.go b/internal/controllers/gateway/gateway_controller.go index d1bb773f5..c949744df 100644 --- a/internal/controllers/gateway/gateway_controller.go +++ b/internal/controllers/gateway/gateway_controller.go @@ -348,8 +348,10 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if gatewayToReconcile, ok := r.GatewayNN.Get(); ok { if req.Namespace != gatewayToReconcile.Namespace || req.Name != gatewayToReconcile.Name { - r.Log.V(util.DebugLevel).Info("The request does not match the specified Gateway and will be skipped.", "gateway", gatewayToReconcile.String()) + r.Log.Info("The request does not match the specified Gateway and will be skipped.", "gateway", gatewayToReconcile.String()) return ctrl.Result{}, nil + } else { + r.Log.Info("The request matches the specified Gateway and will be processed.", "gateway", gatewayToReconcile.String()) } } diff --git a/internal/controllers/gateway/httproute_controller.go b/internal/controllers/gateway/httproute_controller.go index f22896553..280c6f466 100644 --- a/internal/controllers/gateway/httproute_controller.go +++ b/internal/controllers/gateway/httproute_controller.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/google/uuid" "github.com/samber/lo" "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" @@ -219,6 +220,7 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGatewayClass(ctx context.Context, return nil } + track := uuid.NewString() // reduce for in-class Gateway objects gateways := make(map[string]map[string]struct{}) for _, gateway := range gatewayList.Items { @@ -226,6 +228,7 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGatewayClass(ctx context.Context, // If the flag `--gateway-to-reconcile` is set, KIC will only reconcile the specified gateway. // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 if gatewayToReconcile, ok := r.GatewayNN.Get(); ok { + r.Log.Info("limited to single gateway", "track", track, "expected", gatewayToReconcile.Name, "actual", gateway.Name) if gatewayToReconcile.Namespace != gateway.Namespace || gatewayToReconcile.Name != gateway.Name { continue } @@ -264,7 +267,9 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGatewayClass(ctx context.Context, // if the gateway matches one of our previously filtered gateways, enqueue the route if gatewaysForNamespace, ok := gateways[namespace]; ok { + r.Log.Info("checking httproute", "track", track, "httproute", httproute.Name, "parent", parentRef.Name) if _, ok := gatewaysForNamespace[string(parentRef.Name)]; ok { + r.Log.Info("allowed gw for httproute", "track", track, "httproute", httproute.Name, "parent", parentRef.Name) queue = append(queue, reconcile.Request{ NamespacedName: k8stypes.NamespacedName{ Namespace: httproute.Namespace, diff --git a/internal/controllers/gateway/route_utils.go b/internal/controllers/gateway/route_utils.go index d8a29e00e..e6e235265 100644 --- a/internal/controllers/gateway/route_utils.go +++ b/internal/controllers/gateway/route_utils.go @@ -151,9 +151,13 @@ func getSupportedGatewayForRoute[T gatewayapi.RouteT](ctx context.Context, logge // If the flag `--gateway-to-reconcile` is set, KIC will only reconcile the specified gateway. // https://github.com/Kong/kubernetes-ingress-controller/issues/5322 if gatewayToReconcile, ok := specifiedGW.Get(); ok { - namespace = gatewayToReconcile.Namespace - name = gatewayToReconcile.Name - + parentNamespace := route.GetNamespace() + if parentRef.Namespace != nil { + parentNamespace = string(*parentRef.Namespace) + } + if !(parentNamespace == gatewayToReconcile.Namespace && string(parentRef.Name) == gatewayToReconcile.Name) { + continue + } } // pull the Gateway object from the cached client diff --git a/test/integration/httproute_test.go b/test/integration/httproute_test.go index 79782d268..279ac4c17 100644 --- a/test/integration/httproute_test.go +++ b/test/integration/httproute_test.go @@ -675,3 +675,118 @@ func TestHTTPRouteFilterHosts(t *testing.T) { t.Logf("test host matched in httproute, but not in listeners") require.False(t, testGetByHost(t, "another.specific.io")) } + +func TestHTTPRouteMultiGateway(t *testing.T) { + ctx := context.Background() + + ns, cleaner := helpers.Setup(ctx, t, env) + + t.Log("getting a gatewayOne client") + gatewayClient, err := gatewayclient.NewForConfig(env.Cluster().Config()) + require.NoError(t, err) + + t.Log("deploying a new gatewayClass") + gatewayClassName := uuid.NewString() + gwc, err := helpers.DeployGatewayClass(ctx, gatewayClient, gatewayClassName) + require.NoError(t, err) + cleaner.Add(gwc) + + t.Log("deploying a new gateways") + gatewayOneName := "wungus" + gatewayOne, err := helpers.DeployGateway(ctx, gatewayClient, ns.Name, gatewayClassName, func(gw *gatewayapi.Gateway) { + gw.Name = gatewayOneName + }) + require.NoError(t, err) + cleaner.Add(gatewayOne) + gatewayTwoName := "blungus" + gatewayTwo, err := helpers.DeployGateway(ctx, gatewayClient, ns.Name, gatewayClassName, func(gw *gatewayapi.Gateway) { + gw.Name = gatewayTwoName + }) + require.NoError(t, err) + cleaner.Add(gatewayTwo) + + t.Log("deploying a minimal HTTP container deployment to test Ingress routes") + container := generators.NewContainer("httpbin", test.HTTPBinImage, test.HTTPBinPort) + deployment := generators.NewDeploymentForContainer(container) + deployment, err = env.Cluster().Client().AppsV1().Deployments(ns.Name).Create(ctx, deployment, metav1.CreateOptions{}) + require.NoError(t, err) + + t.Logf("exposing deployment %s via service", deployment.Name) + service := generators.NewServiceForDeployment(deployment, corev1.ServiceTypeLoadBalancer) + _, err = env.Cluster().Client().CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{}) + require.NoError(t, err) + + t.Logf("creating an httproute to access deployment %s via kong", deployment.Name) + httpPort := gatewayapi.PortNumber(80) + pathMatchPrefix := gatewayapi.PathMatchPathPrefix + httpRouteOne := &gatewayapi.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "one", + Annotations: map[string]string{ + annotations.AnnotationPrefix + annotations.StripPathKey: "true", + }, + }, + Spec: gatewayapi.HTTPRouteSpec{ + CommonRouteSpec: gatewayapi.CommonRouteSpec{ + ParentRefs: []gatewayapi.ParentReference{{ + Name: gatewayapi.ObjectName(gatewayOne.Name), + }}, + }, + Rules: []gatewayapi.HTTPRouteRule{{ + Matches: []gatewayapi.HTTPRouteMatch{ + { + Path: &gatewayapi.HTTPPathMatch{ + Type: &pathMatchPrefix, + Value: kong.String("/one/test-httproute"), + }, + }, + }, + BackendRefs: []gatewayapi.HTTPBackendRef{{ + BackendRef: gatewayapi.BackendRef{ + BackendObjectReference: gatewayapi.BackendObjectReference{ + Name: gatewayapi.ObjectName(service.Name), + Port: &httpPort, + Kind: util.StringToGatewayAPIKindPtr("Service"), + }, + }, + }}, + }}, + }, + } + httpRouteTwo := httpRouteOne.DeepCopy() + httpRouteTwo.Name = "two" + httpRouteTwo.Spec.CommonRouteSpec.ParentRefs[0].Name = gatewayapi.ObjectName(gatewayTwo.Name) + httpRouteTwo.Spec.Rules[0].Matches[0].Path.Value = kong.String("/two/test-httproute") + httpRouteOne, err = gatewayClient.GatewayV1().HTTPRoutes(ns.Name).Create(ctx, httpRouteOne, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(httpRouteOne) + httpRouteTwo, err = gatewayClient.GatewayV1().HTTPRoutes(ns.Name).Create(ctx, httpRouteTwo, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(httpRouteTwo) + + t.Log("verifying that the Gateway gets linked to the route via status") + callback := helpers.GetGatewayIsLinkedCallback(ctx, t, gatewayClient, gatewayapi.HTTPProtocolType, ns.Name, httpRouteOne.Name) + require.Eventually(t, callback, ingressWait, waitTick) + t.Log("verifying that the httproute contains 'Programmed' condition") + require.Eventually(t, + helpers.GetVerifyProgrammedConditionCallback(t, gatewayClient, gatewayapi.HTTPProtocolType, ns.Name, httpRouteOne.Name, metav1.ConditionTrue), + ingressWait, waitTick, + ) + + t.Log("waiting for routes from HTTPRoute to become operational") + + require.Eventually(t, func() bool { + req := helpers.MustHTTPRequest(t, http.MethodGet, proxyURL.Host, "/one/test-httproute", nil) + resp, err := helpers.DefaultHTTPClientWithProxy(proxyURL).Do(req) + if err != nil { + t.Logf("WARNING: http request failed for GET %s/%s: %v", proxyURL, "/one/test-httproute", err) + return false + } + defer resp.Body.Close() + if _, ok := resp.Header["Reqid"]; ok { + return true + } + return false + }, ingressWait, waitTick) + +} diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index ec6a3e9d8..af903895a 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -178,6 +178,7 @@ func TestMain(m *testing.M) { "--anonymous-reports=false", fmt.Sprintf("--feature-gates=%s", featureGates), fmt.Sprintf("--election-namespace=%s", kongAddon.Namespace()), + fmt.Sprintf("--gateway-to-reconcile=%s", "spoop/wungus"), } allControllerArgs := append(standardControllerArgs, extraControllerArgs...) cancel, err := testutils.DeployControllerManagerForCluster(ctx, logger, env.Cluster(), kongAddon, allControllerArgs...) diff --git a/test/internal/helpers/setup.go b/test/internal/helpers/setup.go index ff0355851..d7fd3d174 100644 --- a/test/internal/helpers/setup.go +++ b/test/internal/helpers/setup.go @@ -7,8 +7,8 @@ import ( "github.com/kong/kubernetes-testing-framework/pkg/clusters" "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Setup is a test helper function which: @@ -35,8 +35,13 @@ func Setup(ctx context.Context, t *testing.T, env environments.Environment) (*co }) t.Log("creating a testing namespace") - namespace, err := clusters.GenerateNamespace(ctx, cluster, LabelValueForTest(t)) - require.NoError(t, err) + //namespace, err := clusters.GenerateNamespace(ctx, cluster, LabelValueForTest(t)) + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "spoop", + }, + } + //require.NoError(t, err) cleaner.AddNamespace(namespace) t.Logf("created namespace %s for test case %s", namespace.Name, t.Name())