-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Xabier Larrakoetxea <[email protected]>
- Loading branch information
Showing
13 changed files
with
591 additions
and
340 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,45 @@ | ||
package filter | ||
|
||
import ( | ||
"context" | ||
|
||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
// UpdatedObjectFilter knows when to ignore an object that has been updated. Ignoring | ||
// an object means that the object will not be processed by the handler. | ||
type UpdatedObjectFilter interface { | ||
// Ignore will ignore the object when returns true. | ||
Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool | ||
} | ||
|
||
// UpdatedObjectFilterFunc is a helper type to create filters wihout the need to declare a new type. | ||
type UpdatedObjectFilterFunc func(ctx context.Context, old runtime.Object, new runtime.Object) bool | ||
|
||
func (f UpdatedObjectFilterFunc) Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool { | ||
return f(ctx, old, new) | ||
} | ||
|
||
// NoopUpdatedObjectFilter is a UpdatedObjectFilter that doesn't filter anything. | ||
const NoopUpdatedObjectFilter = noopUpdatedObjectFilter(false) | ||
|
||
type noopUpdatedObjectFilter bool | ||
|
||
func (noopUpdatedObjectFilter) Ignore(_ context.Context, _ runtime.Object, _ runtime.Object) bool { | ||
return false | ||
} | ||
|
||
// NewUpdatedObjectFilterChain is a chain of filters, it will execute all received filters in order | ||
// until a filter returns true, in that moment the execution chain will be stopped and return true. | ||
func NewUpdatedObjectFilterChain(fs ...UpdatedObjectFilter) UpdatedObjectFilter { | ||
return UpdatedObjectFilterFunc(func(ctx context.Context, old, new runtime.Object) bool { | ||
for _, f := range fs { | ||
ignore := f.Ignore(ctx, old, new) | ||
if ignore { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
}) | ||
} |
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,81 @@ | ||
package filter | ||
|
||
import ( | ||
"context" | ||
"maps" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
// AnnotationChangedFilter will ignore the object update if the annotations did not change. | ||
const AnnotationChangedFilter = annotationChangedFilter(false) | ||
|
||
type annotationChangedFilter bool | ||
|
||
func (annotationChangedFilter) Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool { | ||
o, n, ok := getMetaV1Object(old, new) | ||
if !ok { | ||
return false | ||
} | ||
|
||
return maps.Equal(o.GetAnnotations(), n.GetAnnotations()) | ||
} | ||
|
||
// LabelChangedFilter will ignore the object update if the labels did not change. | ||
const LabelChangedFilter = labelChangedFilter(false) | ||
|
||
type labelChangedFilter bool | ||
|
||
func (labelChangedFilter) Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool { | ||
o, n, ok := getMetaV1Object(old, new) | ||
if !ok { | ||
return false | ||
} | ||
|
||
return maps.Equal(o.GetLabels(), n.GetLabels()) | ||
} | ||
|
||
// GenerationChangedFilter will ignore the object update if the generation did not change. | ||
const GenerationChangedFilter = generationChangedFilter(false) | ||
|
||
type generationChangedFilter bool | ||
|
||
func (generationChangedFilter) Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool { | ||
o, n, ok := getMetaV1Object(old, new) | ||
if !ok { | ||
return false | ||
} | ||
|
||
return o.GetGeneration() == n.GetGeneration() | ||
} | ||
|
||
// ResourceVersionChangedFilter will ignore the object update if the resource version did not change. | ||
const ResourceVersionChangedFilter = resourceVersionChangedFilter(false) | ||
|
||
type resourceVersionChangedFilter bool | ||
|
||
func (resourceVersionChangedFilter) Ignore(ctx context.Context, old runtime.Object, new runtime.Object) bool { | ||
o, n, ok := getMetaV1Object(old, new) | ||
if !ok { | ||
return false | ||
} | ||
|
||
return o.GetResourceVersion() == n.GetResourceVersion() | ||
} | ||
|
||
func getMetaV1Object(old runtime.Object, new runtime.Object) (o metav1.Object, n metav1.Object, ok bool) { | ||
if old == nil || new == nil { | ||
return nil, nil, false | ||
} | ||
o, ok = old.(metav1.Object) | ||
if !ok { | ||
return nil, nil, false | ||
} | ||
n, ok = new.(metav1.Object) | ||
if !ok { | ||
return nil, nil, false | ||
} | ||
|
||
return o, n, true | ||
} |
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,232 @@ | ||
package filter_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/spotahome/kooper/v2/controller/filter" | ||
"github.com/stretchr/testify/assert" | ||
corev1 "k8s.io/api/core/v1" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
func TestAnnotationChangedFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
oldObj runtime.Object | ||
newObj runtime.Object | ||
expIgnore bool | ||
}{ | ||
"If nil old object, should not ignore.": { | ||
oldObj: nil, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If nil new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: nil, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid old object, should not ignore.": { | ||
oldObj: &corev1.PodExecOptions{}, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: &corev1.PodExecOptions{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have different annotations, it should not be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{ | ||
"k1": "v2", | ||
}}}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have same annotations, it should be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
expIgnore: true, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
gotIgnore := filter.AnnotationChangedFilter.Ignore(context.TODO(), test.oldObj, test.newObj) | ||
assert.Equal(t, test.expIgnore, gotIgnore) | ||
}) | ||
} | ||
} | ||
|
||
func TestLabelChangedFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
oldObj runtime.Object | ||
newObj runtime.Object | ||
expIgnore bool | ||
}{ | ||
"If nil old object, should not ignore.": { | ||
oldObj: nil, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If nil new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: nil, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid old object, should not ignore.": { | ||
oldObj: &corev1.PodExecOptions{}, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: &corev1.PodExecOptions{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have different labels, it should not be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{ | ||
"k1": "v2", | ||
}}}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have same labels, it should be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{ | ||
"k1": "v1", | ||
}}}, | ||
expIgnore: true, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
gotIgnore := filter.LabelChangedFilter.Ignore(context.TODO(), test.oldObj, test.newObj) | ||
assert.Equal(t, test.expIgnore, gotIgnore) | ||
}) | ||
} | ||
} | ||
|
||
func TestGenerationChangedFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
oldObj runtime.Object | ||
newObj runtime.Object | ||
expIgnore bool | ||
}{ | ||
"If nil old object, should not ignore.": { | ||
oldObj: nil, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If nil new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: nil, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid old object, should not ignore.": { | ||
oldObj: &corev1.PodExecOptions{}, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: &corev1.PodExecOptions{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have different generation, it should not be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Generation: 42}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Generation: 43}}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have same generation, it should be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Generation: 42}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{Generation: 42}}, | ||
expIgnore: true, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
gotIgnore := filter.GenerationChangedFilter.Ignore(context.TODO(), test.oldObj, test.newObj) | ||
assert.Equal(t, test.expIgnore, gotIgnore) | ||
}) | ||
} | ||
} | ||
|
||
func TestResourceVersionChangedFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
oldObj runtime.Object | ||
newObj runtime.Object | ||
expIgnore bool | ||
}{ | ||
"If nil old object, should not ignore.": { | ||
oldObj: nil, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If nil new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: nil, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid old object, should not ignore.": { | ||
oldObj: &corev1.PodExecOptions{}, | ||
newObj: &corev1.Pod{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If no valid new object, should not ignore.": { | ||
oldObj: &corev1.Pod{}, | ||
newObj: &corev1.PodExecOptions{}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have different resource version, it should not be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{ResourceVersion: "123456"}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{ResourceVersion: "123457"}}, | ||
expIgnore: false, | ||
}, | ||
|
||
"If old and new have same resource version, it should be ignored.": { | ||
oldObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{ResourceVersion: "123456"}}, | ||
newObj: &corev1.Pod{ObjectMeta: v1.ObjectMeta{ResourceVersion: "123456"}}, | ||
expIgnore: true, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
gotIgnore := filter.ResourceVersionChangedFilter.Ignore(context.TODO(), test.oldObj, test.newObj) | ||
assert.Equal(t, test.expIgnore, gotIgnore) | ||
}) | ||
} | ||
} |
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
6 changes: 5 additions & 1 deletion
6
examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.