-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Trigger target status reconciliation for policies when the policy sta…
…tus changed only
- Loading branch information
1 parent
850b412
commit 26fd172
Showing
3 changed files
with
305 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | ||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters