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(konnect): manage finalizers for KongRoute in KonnectEntityPluginBindingFinalizerReconciler #683

Merged
merged 1 commit into from
Oct 2, 2024
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
41 changes: 41 additions & 0 deletions controller/konnect/reconciler_generic_pluginbindingfinalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ func enqueueKongServiceForKongPluginBinding() func(
}
}

func enqueueKongRouteForKongPluginBinding() func(
ctx context.Context, obj client.Object) []reconcile.Request {
return func(ctx context.Context, obj client.Object) []reconcile.Request {
kpb, ok := obj.(*configurationv1alpha1.KongPluginBinding)
if !ok {
return nil
}

if kpb.Spec.Targets.RouteReference == nil ||
kpb.Spec.Targets.RouteReference.Kind != "KongRoute" ||
kpb.Spec.Targets.RouteReference.Group != configurationv1alpha1.GroupVersion.Group {
return nil
}

return []ctrl.Request{
{
NamespacedName: types.NamespacedName{
Namespace: kpb.Namespace,
Name: kpb.Spec.Targets.RouteReference.Name,
},
},
}
}
}

// Reconcile reconciles the Konnect entity that can be set as KongPluginBinding target.
// Its purpose is to:
// - check if the entity is marked for deletion and mark KongPluginBindings
Expand Down Expand Up @@ -189,6 +214,8 @@ func (r *KonnectEntityPluginBindingFinalizerReconciler[T, TEnt]) getKongPluginBi
switch any(ent).(type) {
case *configurationv1alpha1.KongService:
return IndexFieldKongPluginBindingKongServiceReference
case *configurationv1alpha1.KongRoute:
return IndexFieldKongPluginBindingKongRouteReference
default:
panic(fmt.Sprintf("unsupported entity type %s", constraints.EntityTypeName[T]()))
}
Expand Down Expand Up @@ -217,6 +244,20 @@ func (r *KonnectEntityPluginBindingFinalizerReconciler[T, TEnt]) setControllerBu
enqueueKongServiceForKongPluginBinding(),
),
)
case *configurationv1alpha1.KongRoute:
b.
For(&configurationv1alpha1.KongRoute{},
builder.WithPredicates(
predicate.NewPredicateFuncs(objRefersToKonnectGatewayControlPlane[configurationv1alpha1.KongRoute]),
kongPluginsAnnotationChangedPredicate,
),
).
Watches(
&configurationv1alpha1.KongPluginBinding{},
handler.EnqueueRequestsFromMapFunc(
enqueueKongRouteForKongPluginBinding(),
),
)
default:
panic(fmt.Sprintf("unsupported entity type %s", constraints.EntityTypeName[T]()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ package konnect

//+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongservices,verbs=get;update

//+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongroutes,verbs=get;update

//+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongpluginbindings,verbs=delete;list
125 changes: 66 additions & 59 deletions test/envtest/kongplugincleanupfinalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"testing"

"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand All @@ -18,7 +16,6 @@ import (
"github.com/kong/gateway-operator/pkg/consts"
"github.com/kong/gateway-operator/test/helpers/deploy"

configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1"
configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

Expand All @@ -42,64 +39,74 @@ func TestKongPluginFinalizer(t *testing.T) {
cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth)

require.NoError(t, manager.SetupCacheIndicesForKonnectTypes(ctx, mgr, false))
reconcilers := []Reconciler{

StartReconcilers(ctx, t, mgr, logs,
konnect.NewKonnectEntityPluginReconciler[configurationv1alpha1.KongService](false, mgr.GetClient()),
}

StartReconcilers(ctx, t, mgr, logs, reconcilers...)

rateLimitingkongPlugin := &configurationv1.KongPlugin{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "rate-limiting-kp-",
},
PluginName: "rate-limiting",
Config: apiextensionsv1.JSON{
Raw: []byte(`{"minute": 5, "policy": "local"}`),
},
}
require.NoError(t, clientNamespaced.Create(ctx, rateLimitingkongPlugin))
t.Logf("deployed %s KongPlugin (%s) resource", client.ObjectKeyFromObject(rateLimitingkongPlugin), rateLimitingkongPlugin.PluginName)

wKongService := setupWatch[configurationv1alpha1.KongServiceList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp)
kpb := deploy.KongPluginBinding(t, ctx, clientNamespaced,
&configurationv1alpha1.KongPluginBinding{
Spec: configurationv1alpha1.KongPluginBindingSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: cp.Name,
},
},
PluginReference: configurationv1alpha1.PluginRef{
Name: rateLimitingkongPlugin.Name,
},
Targets: configurationv1alpha1.KongPluginBindingTargets{
ServiceReference: &configurationv1alpha1.TargetRefWithGroupKind{
Group: configurationv1alpha1.GroupVersion.Group,
Kind: "KongService",
Name: kongService.Name,
},
},
},
},
konnect.NewKonnectEntityPluginReconciler[configurationv1alpha1.KongRoute](false, mgr.GetClient()),
)

_ = watchFor(t, ctx, wKongService, watch.Modified,
func(svc *configurationv1alpha1.KongService) bool {
return svc.Name == kongService.Name &&
slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongService doesn't have the %s finalizer set", consts.CleanupPluginBindingFinalizer),
)
t.Run("KongService", func(t *testing.T) {
rateLimitingkongPlugin := deploy.RateLimitingPlugin(t, ctx, clientNamespaced)

wKongService = setupWatch[configurationv1alpha1.KongServiceList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
require.NoError(t, clientNamespaced.Delete(ctx, kpb))
_ = watchFor(t, ctx, wKongService, watch.Modified,
func(svc *configurationv1alpha1.KongService) bool {
return svc.Name == kongService.Name &&
!slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongService has the %s finalizer set but it shouldn't", consts.CleanupPluginBindingFinalizer),
)
wKongService := setupWatch[configurationv1alpha1.KongServiceList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp)
kpb := deploy.KongPluginBinding(t, ctx, clientNamespaced,
konnect.NewKongPluginBindingBuilder().
WithControlPlaneRefKonnectNamespaced(cp.Name).
WithPluginRef(rateLimitingkongPlugin.Name).
WithServiceTarget(kongService.Name).
Build(),
)

_ = watchFor(t, ctx, wKongService, watch.Modified,
func(svc *configurationv1alpha1.KongService) bool {
return svc.Name == kongService.Name &&
slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongService doesn't have the %s finalizer set", consts.CleanupPluginBindingFinalizer),
)

wKongService = setupWatch[configurationv1alpha1.KongServiceList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
require.NoError(t, clientNamespaced.Delete(ctx, kpb))
_ = watchFor(t, ctx, wKongService, watch.Modified,
func(svc *configurationv1alpha1.KongService) bool {
return svc.Name == kongService.Name &&
!slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongService has the %s finalizer set but it shouldn't", consts.CleanupPluginBindingFinalizer),
)
})

t.Run("KongRoute", func(t *testing.T) {
rateLimitingkongPlugin := deploy.RateLimitingPlugin(t, ctx, clientNamespaced)

kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp)
wKongRoute := setupWatch[configurationv1alpha1.KongRouteList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
kongRoute := deploy.KongRouteAttachedToService(t, ctx, clientNamespaced, kongService)
kpb := deploy.KongPluginBinding(t, ctx, clientNamespaced,
konnect.NewKongPluginBindingBuilder().
WithControlPlaneRefKonnectNamespaced(cp.Name).
WithPluginRef(rateLimitingkongPlugin.Name).
WithRouteTarget(kongRoute.Name).
Build(),
)

_ = watchFor(t, ctx, wKongRoute, watch.Modified,
func(svc *configurationv1alpha1.KongRoute) bool {
return svc.Name == kongRoute.Name &&
slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongRoute doesn't have the %s finalizer set", consts.CleanupPluginBindingFinalizer),
)

wKongRoute = setupWatch[configurationv1alpha1.KongRouteList](t, ctx, clientWithWatch, client.InNamespace(ns.Name))
require.NoError(t, clientNamespaced.Delete(ctx, kpb))
_ = watchFor(t, ctx, wKongRoute, watch.Modified,
func(svc *configurationv1alpha1.KongRoute) bool {
return svc.Name == kongRoute.Name &&
!slices.Contains(svc.GetFinalizers(), consts.CleanupPluginBindingFinalizer)
},
fmt.Sprintf("KongRoute has the %s finalizer set but it shouldn't", consts.CleanupPluginBindingFinalizer),
)
})
}
23 changes: 23 additions & 0 deletions test/helpers/deploy/deploy_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,29 @@ func ProxyCachePlugin(
return plugin
}

// RateLimitingPlugin deploys the rate-limiting KongPlugin resource and returns the resource.
// The provided client should be namespaced, i.e. created with `client.NewNamespacedClient(client, ns)`
func RateLimitingPlugin(
t *testing.T,
ctx context.Context,
cl client.Client,
) *configurationv1.KongPlugin {
t.Helper()

plugin := &configurationv1.KongPlugin{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "rate-limiting-kp-",
},
PluginName: "rate-limiting",
Config: apiextensionsv1.JSON{
Raw: []byte(`{"minute": 5, "policy": "local"}`),
},
}
require.NoError(t, cl.Create(ctx, plugin))
t.Logf("deployed new %s KongPlugin (%s)", client.ObjectKeyFromObject(plugin), plugin.PluginName)
return plugin
}

// KongKeySetAttachedToCP deploys a KongKeySet resource attached to a CP and returns the resource.
func KongKeySetAttachedToCP(
t *testing.T,
Expand Down
Loading