From 08fecc43b33e005312da37eaf3395a4c63621ead Mon Sep 17 00:00:00 2001 From: Guilherme Cassolato Date: Tue, 1 Oct 2024 12:28:40 +0200 Subject: [PATCH] controller: function to sort objects by timestamp Signed-off-by: Guilherme Cassolato --- controller/object.go | 26 +++++++++++++++++++++++++- controller/object_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 controller/object_test.go diff --git a/controller/object.go b/controller/object.go index fdf17f1..d4b27fc 100644 --- a/controller/object.go +++ b/controller/object.go @@ -1,9 +1,12 @@ package controller import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/ptr" "github.com/kuadrant/policy-machinery/machinery" ) @@ -13,11 +16,15 @@ type Object interface { metav1.Object } -// RuntimeObject is a cluster runtime object that implements machinery.Object interface +// RuntimeObject is a wrapper around a Kubernetes runtime object, that also implements the machinery.Object interface +// Use it for wrapping runtime objects that do not natively implement machinery.Object, so such object can be added to +// a machinery.Topology type RuntimeObject struct { Object } +var _ machinery.Object = &RuntimeObject{} + func (o *RuntimeObject) GroupVersionKind() schema.GroupVersionKind { return o.Object.GetObjectKind().GroupVersionKind() } @@ -41,3 +48,20 @@ func ObjectAs[T any](obj Object, _ int) T { o, _ := obj.(T) return o } + +// ObjectsByCreationTimestamp is a slice of RuntimeObject that can be sorted by creation timestamp +// RuntimeObjects with the oldest creation timestamp will appear first; if two objects have the same creation timestamp, +// the object appearing first in alphabetical order by namespace/name will appear first. +type ObjectsByCreationTimestamp []Object + +func (a ObjectsByCreationTimestamp) Len() int { return len(a) } +func (a ObjectsByCreationTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ObjectsByCreationTimestamp) Less(i, j int) bool { + p1Time := ptr.To(a[i].GetCreationTimestamp()) + p2Time := ptr.To(a[j].GetCreationTimestamp()) + if !p1Time.Equal(p2Time) { + return p1Time.Before(p2Time) + } + // The object appearing first in alphabetical order by "{namespace}/{name}". + return fmt.Sprintf("%s/%s", a[i].GetNamespace(), a[i].GetName()) < fmt.Sprintf("%s/%s", a[j].GetNamespace(), a[j].GetName()) +} diff --git a/controller/object_test.go b/controller/object_test.go new file mode 100644 index 0000000..2975e36 --- /dev/null +++ b/controller/object_test.go @@ -0,0 +1,37 @@ +package controller + +import ( + "sort" + "testing" + "time" + + "github.com/samber/lo" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestObjectsByCreationTimestamp(t *testing.T) { + pods := []*corev1.Pod{ + {ObjectMeta: metav1.ObjectMeta{Name: "pod1", CreationTimestamp: metav1.Time{Time: time.Unix(1, 0)}}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pod2", CreationTimestamp: metav1.Time{Time: time.Unix(2, 0)}}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pod3", CreationTimestamp: metav1.Time{Time: time.Unix(1, 0)}}}, + } + objs := lo.Map(pods, func(pod *corev1.Pod, _ int) Object { return pod }) + sort.Sort(ObjectsByCreationTimestamp(objs)) + if objs[0].GetName() != "pod1" { + t.Errorf("expected pod1, got %s", objs[0].GetName()) + } + if objs[1].GetName() != "pod3" { + t.Errorf("expected pod3, got %s", objs[1].GetName()) + } + if objs[2].GetName() != "pod2" { + t.Errorf("expected pod2, got %s", objs[2].GetName()) + } +} + +func TestObjectAs(t *testing.T) { + var obj Object = &corev1.Pod{} + if ObjectAs[*corev1.Pod](obj, 0) == nil { + t.Errorf("expected *corev1.Pod, got nil") + } +}