Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(httproute): add ReferenceGrant watch #3759

Merged
merged 4 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Adding a new version? You'll need three changes:
- Adding the `konghq.com/tags: csv,of,tags` annotation will add tags to
generated resources.
[#3778](https://github.com/Kong/kubernetes-ingress-controller/pull/3778)
- `HTTPRoute` reconciler now watches relevant `ReferenceGrant`s for changes.
[#3759](https://github.com/Kong/kubernetes-ingress-controller/pull/3759)
- Bumped Kong version in manifests to 3.2.
[#3804](https://github.com/Kong/kubernetes-ingress-controller/pull/3804)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func startKongAdminAPIServiceReconciler(ctx context.Context, t *testing.T, clien

mgr, err := ctrl.NewManager(cfg, ctrl.Options{
Logger: logrusr.New(logrus.New()),
Scheme: scheme.Scheme,
Scheme: client.Scheme(),
SyncPeriod: lo.ToPtr(2 * time.Second),
MetricsBindAddress: "0",
})
Expand Down Expand Up @@ -106,7 +106,7 @@ func TestKongAdminAPIController(t *testing.T) {
// In tests below we use a deferred cancel to stop the manager and not wait
// for its timeout.

cfg := envtest.Setup(t)
cfg := envtest.Setup(t, scheme.Scheme)
client, err := ctrlclient.New(cfg, ctrlclient.Options{})
require.NoError(t, err)

Expand Down
11 changes: 11 additions & 0 deletions internal/controllers/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package controllers

import (
"github.com/go-logr/logr"
ctrl "sigs.k8s.io/controller-runtime"
)

type Reconciler interface {
SetupWithManager(ctrl.Manager) error
SetLogger(logr.Logger)
}
26 changes: 26 additions & 0 deletions internal/controllers/gateway/dataplane_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package gateway

import (
"sigs.k8s.io/controller-runtime/pkg/client"

k8sobj "github.com/kong/kubernetes-ingress-controller/v2/internal/util/kubernetes/object"
)

// DataPlane is a common interface that is used by reconcilers to interact
// with the dataplane.
//
// TODO: This can probably be used in other reconcilers as well.
pmalek marked this conversation as resolved.
Show resolved Hide resolved
// Related issue: https://github.com/Kong/kubernetes-ingress-controller/issues/3794
type DataPlane interface {
DataPlaneClient

AreKubernetesObjectReportsEnabled() bool
KubernetesObjectConfigurationStatus(obj client.Object) k8sobj.ConfigurationStatus
}

// DataPlaneClient is a common client interface that is used by reconcilers to interact
// with the dataplane to perform CRUD operations on provided objects.
type DataPlaneClient interface {
UpdateObject(obj client.Object) error
DeleteObject(obj client.Object) error
}
32 changes: 32 additions & 0 deletions internal/controllers/gateway/dataplane_mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package gateway

import (
"sigs.k8s.io/controller-runtime/pkg/client"

k8sobj "github.com/kong/kubernetes-ingress-controller/v2/internal/util/kubernetes/object"
)

type DataplaneMock struct {
KubernetesObjectReportsEnabled bool
// Mapping namespace to name to status
// Note: this will come in useful when implementing
// https://github.com/Kong/kubernetes-ingress-controller/issues/3793
// which requires the status to be reported for route objects.
ObjectsStatuses map[string]map[string]k8sobj.ConfigurationStatus
pmalek marked this conversation as resolved.
Show resolved Hide resolved
}

func (d DataplaneMock) UpdateObject(_ client.Object) error {
return nil
}

func (d DataplaneMock) DeleteObject(_ client.Object) error {
return nil
}

func (d DataplaneMock) AreKubernetesObjectReportsEnabled() bool {
return d.KubernetesObjectReportsEnabled
}

func (d DataplaneMock) KubernetesObjectConfigurationStatus(obj client.Object) k8sobj.ConfigurationStatus {
return d.ObjectsStatuses[obj.GetNamespace()][obj.GetName()]
}
3 changes: 2 additions & 1 deletion internal/controllers/gateway/gateway_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ func isObjectUnmanaged(anns map[string]string) bool {
// isGatewayClassControlledAndUnmanaged returns boolean if the GatewayClass
// is controlled by this controller and is configured for unmanaged mode.
func isGatewayClassControlledAndUnmanaged(gatewayClass *GatewayClass) bool {
return gatewayClass.Spec.ControllerName == GetControllerName() && isObjectUnmanaged(gatewayClass.Annotations)
isUnamanaged := isObjectUnmanaged(gatewayClass.Annotations)
return gatewayClass.Spec.ControllerName == GetControllerName() && isUnamanaged
}

// getRefFromPublishService splits a publish service string in the format namespace/name into a types.NamespacedName
Expand Down
66 changes: 64 additions & 2 deletions internal/controllers/gateway/httproute_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane"
"github.com/kong/kubernetes-ingress-controller/v2/internal/util"
k8sobj "github.com/kong/kubernetes-ingress-controller/v2/internal/util/kubernetes/object"
)
Expand All @@ -38,7 +37,7 @@ type HTTPRouteReconciler struct {

Log logr.Logger
Scheme *runtime.Scheme
DataplaneClient *dataplane.KongClient
DataplaneClient DataPlane
// If EnableReferenceGrant is true, we will check for ReferenceGrant if backend in another
// namespace is in backendRefs.
// If it is false, referencing backend in different namespace will be rejected.
Expand Down Expand Up @@ -87,6 +86,16 @@ func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
return err
}

if r.EnableReferenceGrant {
if err := c.Watch(
&source.Kind{Type: &gatewayv1beta1.ReferenceGrant{}},
handler.EnqueueRequestsFromMapFunc(r.listReferenceGrantsForHTTPRoute),
predicate.NewPredicateFuncs(referenceGrantHasHTTPRouteFrom),
); err != nil {
return err
}
}

// because of the additional burden of having to manage reference data-plane
// configurations for HTTPRoute objects in the underlying Kong Gateway, we
// simply reconcile ALL HTTPRoute objects. This allows us to drop the backend
Expand All @@ -102,6 +111,54 @@ func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
// HTTPRoute Controller - Event Handlers
// -----------------------------------------------------------------------------

// listReferenceGrantsForHTTPRoute is a watch predicate which finds all HTTPRoutes
// mentioned in a From clause for a ReferenceGrant.
func (r *HTTPRouteReconciler) listReferenceGrantsForHTTPRoute(obj client.Object) []reconcile.Request {
grant, ok := obj.(*gatewayv1beta1.ReferenceGrant)
if !ok {
r.Log.Error(
fmt.Errorf("unexpected object type"),
"referencegrant watch predicate received unexpected object type",
"expected", "*gatewayv1beta1.ReferenceGrant", "found", reflect.TypeOf(obj),
)
return nil
}
httproutes := &gatewayv1beta1.HTTPRouteList{}
if err := r.Client.List(context.Background(), httproutes); err != nil {
r.Log.Error(err, "failed to list httproutes in watch", "referencegrant", grant.Name)
return nil
}
recs := []reconcile.Request{}
for _, gateway := range httproutes.Items {
for _, from := range grant.Spec.From {
if string(from.Namespace) == gateway.Namespace &&
from.Kind == gatewayv1beta1.Kind("HTTPRoute") &&
from.Group == gatewayv1beta1.Group("gateway.networking.k8s.io") {
recs = append(recs, reconcile.Request{
NamespacedName: types.NamespacedName{
Namespace: gateway.Namespace,
Name: gateway.Name,
},
})
}
}
}
return recs
}

func referenceGrantHasHTTPRouteFrom(obj client.Object) bool {
grant, ok := obj.(*gatewayv1beta1.ReferenceGrant)
if !ok {
return false
}
for _, from := range grant.Spec.From {
if from.Kind == "HTTPRoute" && from.Group == "gateway.networking.k8s.io" {
return true
}
}
return false
}

// listHTTPRoutesForGatewayClass is a controller-runtime event.Handler which
// produces a list of HTTPRoutes which were bound to a Gateway which is or was
// bound to this GatewayClass. This implementation effectively does a map-reduce
Expand Down Expand Up @@ -634,3 +691,8 @@ func (r *HTTPRouteReconciler) getHTTPRouteRuleReason(ctx context.Context, httpRo
}
return gatewayv1beta1.RouteReasonResolvedRefs, nil
}

// SetLogger sets the logger.
func (r *HTTPRouteReconciler) SetLogger(l logr.Logger) {
r.Log = l
}
Loading