Skip to content

Commit

Permalink
Trigger target status reconciliation for policies when the policy sta…
Browse files Browse the repository at this point in the history
…tus changed only
  • Loading branch information
guicassolato committed Apr 21, 2024
1 parent 850b412 commit 26fd172
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 4 deletions.
96 changes: 96 additions & 0 deletions controllers/policy_status_event_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package controllers

import (
"context"
"reflect"

"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"

kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
)

// PolicyStatusEventHandlerFromMapFunc returns a PolicyStatusEventHandler that handles events from a mapping function.
func PolicyStatusEventHandlerFromMapFunc(mapFunc handler.MapFunc) handler.EventHandler {
return NewPolicyStatusEventHandler(WithHandler(handler.EnqueueRequestsFromMapFunc(mapFunc)))
}

// NewPolicyStatusEventHandler returns a new PolicyStatusEventHandler.
func NewPolicyStatusEventHandler(opts ...policyStatusEventHandlerOption) handler.EventHandler {
h := &PolicyStatusEventHandler{}
for _, opt := range opts {
opt(h)
}
return h
}

type policyStatusEventHandlerOption func(*PolicyStatusEventHandler)

func WithHandler(h handler.EventHandler) policyStatusEventHandlerOption {

Check failure on line 30 in controllers/policy_status_event_handler.go

View workflow job for this annotation

GitHub Actions / Lint

unexported-return: exported func WithHandler returns unexported type controllers.policyStatusEventHandlerOption, which can be annoying to use (revive)
return func(p *PolicyStatusEventHandler) {
p.handler = h
}
}

var _ handler.EventHandler = &PolicyStatusEventHandler{}

// PolicyStatusEventHandler enqueues reconcile.Requests in response to events for Policy objects
// whose status blocks have changed.
// The handling of the events is delegated to the provided handler.
type PolicyStatusEventHandler struct {
handler handler.EventHandler
}

// Create implements EventHandler.
func (h *PolicyStatusEventHandler) Create(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
if h.handler == nil {
return
}
h.handler.Create(ctx, evt, q)
}

// Update implements EventHandler.
func (h *PolicyStatusEventHandler) Update(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
if h.handler == nil {
return
}
oldPolicy, ok := evt.ObjectOld.(kuadrantgatewayapi.Policy)
if !ok {
return
}
newPolicy, ok := evt.ObjectNew.(kuadrantgatewayapi.Policy)
if !ok {
return
}
if statusChanged(oldPolicy, newPolicy) {
h.handler.Update(ctx, evt, q)
}
}

// Delete implements EventHandler.
func (h *PolicyStatusEventHandler) Delete(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
if h.handler == nil {
return
}
h.handler.Delete(ctx, evt, q)
}

// Generic implements EventHandler.
func (h *PolicyStatusEventHandler) Generic(ctx context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
if h.handler == nil {
return
}
h.handler.Generic(ctx, evt, q)
}

func statusChanged(old, new kuadrantgatewayapi.Policy) bool {
if old == nil || new == nil {
return false
}

oldStatus := old.GetStatus()
newStatus := new.GetStatus()

return !reflect.DeepEqual(oldStatus, newStatus)
}
205 changes: 205 additions & 0 deletions controllers/policy_status_event_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
//go:build unit

package controllers

import (
"context"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
)

var _ handler.EventHandler = &testEventHandler{}

type testEventHandler struct {
lastEventFunc string
}

func (h *testEventHandler) Create(_ context.Context, _ event.CreateEvent, _ workqueue.RateLimitingInterface) {
h.lastEventFunc = "Create"
}
func (h *testEventHandler) Update(_ context.Context, _ event.UpdateEvent, _ workqueue.RateLimitingInterface) {
h.lastEventFunc = "Update"
}
func (h *testEventHandler) Delete(_ context.Context, _ event.DeleteEvent, _ workqueue.RateLimitingInterface) {
h.lastEventFunc = "Delete"
}
func (h *testEventHandler) Generic(_ context.Context, _ event.GenericEvent, _ workqueue.RateLimitingInterface) {
h.lastEventFunc = "Generic"
}

func TestPolicyStatusEventHandler(t *testing.T) {
tests := []struct {
name string
lastEventFunc func() string
expected string
}{
{
name: "Create event",
lastEventFunc: func() string {
testHandler := &testEventHandler{}
h := NewPolicyStatusEventHandler(WithHandler(testHandler))
h.Create(context.Background(), event.CreateEvent{}, nil)
return testHandler.lastEventFunc
},
expected: "Create",
},
{
name: "Update event with different status",
lastEventFunc: func() string {
testHandler := &testEventHandler{}
h := NewPolicyStatusEventHandler(WithHandler(testHandler))
ev := event.UpdateEvent{
ObjectOld: &TestPolicy{
Status: FakePolicyStatus{
Conditions: []metav1.Condition{},
},
},
ObjectNew: &TestPolicy{
Status: FakePolicyStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: metav1.ConditionTrue,
Reason: "ValidPolicy",
},
},
},
},
}
h.Update(context.Background(), ev, nil)
return testHandler.lastEventFunc
},
expected: "Update",
},
{
name: "Update event without different status",
lastEventFunc: func() string {
testHandler := &testEventHandler{}
h := NewPolicyStatusEventHandler(WithHandler(testHandler))
ev := event.UpdateEvent{
ObjectOld: &TestPolicy{
Status: FakePolicyStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: metav1.ConditionTrue,
Reason: "ValidPolicy",
},
},
},
},
ObjectNew: &TestPolicy{
Status: FakePolicyStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: metav1.ConditionTrue,
Reason: "ValidPolicy",
},
},
},
},
}
h.Update(context.Background(), ev, nil)
return testHandler.lastEventFunc
},
expected: "",
},
{
name: "Delete event",
lastEventFunc: func() string {
testHandler := &testEventHandler{}
h := NewPolicyStatusEventHandler(WithHandler(testHandler))
h.Delete(context.Background(), event.DeleteEvent{}, nil)
return testHandler.lastEventFunc
},
expected: "Delete",
},
{
name: "Generic event",
lastEventFunc: func() string {
testHandler := &testEventHandler{}
h := NewPolicyStatusEventHandler(WithHandler(testHandler))
h.Generic(context.Background(), event.GenericEvent{}, nil)
return testHandler.lastEventFunc
},
expected: "Generic",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.lastEventFunc()
if got != tt.expected {
t.Errorf("%s failed. Expected %s, got %s", tt.name, tt.expected, got)
}
})
}
}

// Test policy that implements kuadrantgatewayapi.Policy

var (
_ kuadrantgatewayapi.Policy = &TestPolicy{}
_ kuadrantgatewayapi.PolicyStatus = &FakePolicyStatus{}
)

type TestPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

TargetRef gatewayapiv1alpha2.PolicyTargetReference `json:"targetRef"`
Status FakePolicyStatus `json:"status,omitempty"`
}

func (p *TestPolicy) PolicyClass() kuadrantgatewayapi.PolicyClass {
return kuadrantgatewayapi.DirectPolicy
}

func (p *TestPolicy) GetTargetRef() gatewayapiv1alpha2.PolicyTargetReference {
return p.TargetRef
}

func (p *TestPolicy) GetStatus() kuadrantgatewayapi.PolicyStatus {
return &p.Status
}

func (p *TestPolicy) DeepCopyObject() runtime.Object {
if c := p.DeepCopy(); c != nil {
return c
}
return nil
}

func (p *TestPolicy) DeepCopy() *TestPolicy {
if p == nil {
return nil
}
out := new(TestPolicy)
p.DeepCopyInto(out)
return out
}

func (p *TestPolicy) DeepCopyInto(out *TestPolicy) {
*out = *p
out.TypeMeta = p.TypeMeta
p.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
p.TargetRef.DeepCopyInto(&out.TargetRef)
out.Status = p.Status
}

type FakePolicyStatus struct {
Conditions []metav1.Condition
}

func (s *FakePolicyStatus) GetConditions() []metav1.Condition {
return s.Conditions
}
8 changes: 4 additions & 4 deletions controllers/target_status_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,19 +327,19 @@ func (r *TargetStatusReconciler) SetupWithManager(mgr ctrl.Manager) error {
).
Watches(
&kuadrantv1beta2.AuthPolicy{},
handler.EnqueueRequestsFromMapFunc(policyToParentGatewaysEventMapper.Map),
PolicyStatusEventHandlerFromMapFunc(policyToParentGatewaysEventMapper.Map),
).
Watches(
&kuadrantv1alpha1.DNSPolicy{},
handler.EnqueueRequestsFromMapFunc(policyToParentGatewaysEventMapper.Map),
PolicyStatusEventHandlerFromMapFunc(policyToParentGatewaysEventMapper.Map),
).
Watches(
&kuadrantv1beta2.RateLimitPolicy{},
handler.EnqueueRequestsFromMapFunc(policyToParentGatewaysEventMapper.Map),
PolicyStatusEventHandlerFromMapFunc(policyToParentGatewaysEventMapper.Map),
).
Watches(
&kuadrantv1alpha1.TLSPolicy{},
handler.EnqueueRequestsFromMapFunc(policyToParentGatewaysEventMapper.Map),
PolicyStatusEventHandlerFromMapFunc(policyToParentGatewaysEventMapper.Map),
).
Complete(r)
}
Expand Down

0 comments on commit 26fd172

Please sign in to comment.