Skip to content

Commit

Permalink
add cluster name checks to compare func; add unit tests to sets v2; u…
Browse files Browse the repository at this point in the history
…pdate Delete to take ResourceId
  • Loading branch information
conradhanson committed Mar 26, 2024
1 parent d4fd8ee commit 2e6dfb4
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 94 deletions.
15 changes: 9 additions & 6 deletions contrib/pkg/sets/v2/sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ type ResourceSet[T client.Object] interface {
Insert(resource ...T)
// Compare the equality of the keys in two sets (not the resources themselves)
Equal(set ResourceSet[T]) bool
// Check if the set contains the resource. Uses the compare func to determine equality.
// Check if the set contains the resource.
Has(resource T) bool
// Delete the matching resource. Uses the compare func to determine equality.
Delete(resource T)
// Delete the matching resource.
Delete(resource ezkube.ResourceId)
// Return the union with the provided set
Union(set ResourceSet[T]) ResourceSet[T]
// Return the difference with the provided set
Expand Down Expand Up @@ -157,13 +157,16 @@ func (s *resourceSet[T]) Equal(
return s.Generic().Equal(set.Generic())
}

func (s *resourceSet[T]) Delete(resource T) {
func (s *resourceSet[T]) Delete(resource ezkube.ResourceId) {
s.lock.Lock()
defer s.lock.Unlock()

desired_key := sk_sets.Key(resource)
i := sort.Search(len(s.set), func(i int) bool {
return s.compareFunc(s.set[i], resource) >= 0
return sk_sets.Key(s.set[i]) >= desired_key
})
if i != len(s.set) && s.compareFunc(s.set[i], resource) == 0 {
found := i < len(s.set) && sk_sets.Key(s.set[i]) == desired_key
if found {
s.set = slices.Delete(s.set, i, i+1)
}
}
Expand Down
104 changes: 78 additions & 26 deletions contrib/tests/set_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (

var _ = FDescribe("PaintSetV2", func() {
var (
setA, setB sets_v2.ResourceSet[*v1.Paint]
paintA, paintB, paintC *v1.Paint
setA, setB sets_v2.ResourceSet[*v1.Paint]
paintA, paintBCluster2, paintC *v1.Paint
)

BeforeEach(func() {
Expand All @@ -22,7 +22,7 @@ var _ = FDescribe("PaintSetV2", func() {
paintA = &v1.Paint{
ObjectMeta: metav1.ObjectMeta{Name: "nameA", Namespace: "nsA"},
}
paintB = &v1.Paint{
paintBCluster2 = &v1.Paint{
ObjectMeta: metav1.ObjectMeta{Name: "nameB", Namespace: "nsB"},
}
paintC = &v1.Paint{
Expand All @@ -33,44 +33,44 @@ var _ = FDescribe("PaintSetV2", func() {
It("should insert", func() {
setA.Insert(paintA)
Expect(setA.Has(paintA)).To(BeTrue())
setA.Insert(paintB, paintC)
Expect(setA.Has(paintB)).To(BeTrue())
setA.Insert(paintBCluster2, paintC)
Expect(setA.Has(paintBCluster2)).To(BeTrue())
Expect(setA.Has(paintC)).To(BeTrue())
Expect(setA.Len()).To(Equal(3))
})

It("should return set existence", func() {
setA.Insert(paintA)
Expect(setA.Has(paintA)).To(BeTrue())
Expect(setA.Has(paintB)).To(BeFalse())
setA.Insert(paintB, paintC)
Expect(setA.Has(paintBCluster2)).To(BeFalse())
setA.Insert(paintBCluster2, paintC)
Expect(setA.Has(paintA)).To(BeTrue())
Expect(setA.Has(paintB)).To(BeTrue())
Expect(setA.Has(paintBCluster2)).To(BeTrue())
Expect(setA.Has(paintC)).To(BeTrue())
})

It("should return set equality", func() {
setB.Insert(paintA, paintB, paintC)
setB.Insert(paintA, paintBCluster2, paintC)
setA.Insert(paintA)
Expect(setA.Equal(setB)).To(BeFalse())
setA.Insert(paintC, paintB)
setA.Insert(paintC, paintBCluster2)
Expect(setA.Equal(setB)).To(BeTrue())
})

It("should delete", func() {
setA.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2, paintC)
Expect(setA.Has(paintA)).To(BeTrue())
setA.Delete(paintA)
Expect(setA.Has(paintA)).To(BeFalse())
})

It("should filter List", func() {
setA.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2, paintC)
Expect(setA.Has(paintA)).To(BeTrue())

for i, filtered := range setA.List(func(p *v1.Paint) bool { return p.GetName() == "nameA" }) {
if i == 1 {
Expect(filtered).To(Equal(paintB))
Expect(filtered).To(Equal(paintBCluster2))
}
if i == 2 {
Expect(filtered).To(Equal(paintC))
Expand All @@ -79,7 +79,7 @@ var _ = FDescribe("PaintSetV2", func() {
})

It("should double filter List", func() {
setA.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2, paintC)
Expect(setA.Has(paintA)).To(BeTrue())
for _, filtered := range setA.List(func(p *v1.Paint) bool {
return p.Name == "nameA"
Expand All @@ -91,20 +91,20 @@ var _ = FDescribe("PaintSetV2", func() {
})

It("should union two sets and return new set", func() {
setA.Insert(paintA, paintB)
setB.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2)
setB.Insert(paintA, paintBCluster2, paintC)
unionSet := setA.Union(setB)
Expect(unionSet.Len()).To(Equal(3))
Expect(unionSet.Has(paintA)).To(BeTrue())
Expect(unionSet.Has(paintB)).To(BeTrue())
Expect(unionSet.Has(paintBCluster2)).To(BeTrue())
Expect(unionSet.Has(paintC)).To(BeTrue())
Expect(unionSet).ToNot(BeIdenticalTo(setA))
Expect(unionSet).ToNot(BeIdenticalTo(setB))
})

It("should take the difference of two sets and return new set", func() {
setA.Insert(paintA, paintB)
setB.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2)
setB.Insert(paintA, paintBCluster2, paintC)
differenceA := setA.Difference(setB)
Expect(differenceA.Len()).To(Equal(0))
Expect(differenceA.Map()).To(BeEmpty())
Expand All @@ -117,27 +117,27 @@ var _ = FDescribe("PaintSetV2", func() {
})

It("should take the intersection of two sets and return new set", func() {
setA.Insert(paintA, paintB)
setB.Insert(paintA, paintB, paintC)
setA.Insert(paintA, paintBCluster2)
setB.Insert(paintA, paintBCluster2, paintC)
intersectionA := setA.Intersection(setB)
Expect(intersectionA.Has(paintA)).To(BeTrue())
Expect(intersectionA.Has(paintB)).To(BeTrue())
Expect(intersectionA.Has(paintBCluster2)).To(BeTrue())
Expect(intersectionA.Len()).To(Equal(2))
Expect(intersectionA.Map()).To(HaveKeyWithValue(sets.Key(paintA), paintA))
Expect(intersectionA.Map()).To(HaveKeyWithValue(sets.Key(paintB), paintB))
Expect(intersectionA.Map()).To(HaveKeyWithValue(sets.Key(paintBCluster2), paintBCluster2))
Expect(intersectionA).ToNot(BeIdenticalTo(setA))
})

// It("should correctly match two sets", func() {
// setA.Insert(paintA, paintB)
// setB.Insert(paintA, paintB)
// setA.Insert(paintA, paintBCluster2)
// setB.Insert(paintA, paintBCluster2)
// Expect(setA).To(Equal(setB))
// setB.Insert(paintC)
// Expect(setA).ToNot(Equal(setB))
// })

It("should return corrent length", func() {
setA.Insert(paintA, paintB)
setA.Insert(paintA, paintBCluster2)
Expect(setA.Len()).To(Equal(2))
})

Expand Down Expand Up @@ -224,4 +224,56 @@ var _ = FDescribe("PaintSetV2", func() {
Expect(found).To(Equal(paint))
}
})

It("should sort resources first by cluster, then by namespace, then by name", func() {
paintAAcluster1 := &v1.Paint{
ObjectMeta: metav1.ObjectMeta{
Name: "a",
Namespace: "a",
Annotations: map[string]string{ezkube.ClusterAnnotation: "cluster-1"},
},
}
paintABcluster1 := &v1.Paint{
ObjectMeta: metav1.ObjectMeta{
Name: "a",
Namespace: "b",
Annotations: map[string]string{ezkube.ClusterAnnotation: "cluster-1"},
},
}
paintBBcluster1 := &v1.Paint{
ObjectMeta: metav1.ObjectMeta{
Name: "b",
Namespace: "b",
Annotations: map[string]string{ezkube.ClusterAnnotation: "cluster-1"},
},
}
paintACluster2 := &v1.Paint{
ObjectMeta: metav1.ObjectMeta{
Name: "a",
Namespace: "c",
Annotations: map[string]string{ezkube.ClusterAnnotation: "cluster-2"},
},
}
paintBCluster2 := &v1.Paint{
ObjectMeta: metav1.ObjectMeta{
Name: "b",
Namespace: "c",
Annotations: map[string]string{ezkube.ClusterAnnotation: "cluster-2"},
},
}
expectedOrder := []*v1.Paint{
paintAAcluster1, paintABcluster1, paintBBcluster1,
paintACluster2, paintBCluster2,
}
setA.Insert(expectedOrder...)

var paintList []*v1.Paint
for _, paint := range setA.List() {
paintList = append(paintList, paint)
}

for i, paint := range expectedOrder {
Expect(paintList[i]).To(Equal(paint))
}
})
})
74 changes: 12 additions & 62 deletions pkg/ezkube/resource_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"sync"

"github.com/rotisserie/eris"
"github.com/solo-io/skv2/pkg/controllerutils"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -178,81 +177,32 @@ func ResourceIdFromKeyWithSeparator(key string, separator string) (ResourceId, e
// CompareResourceId returns an integer comparing two ResourceIds lexicographically.
// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
// a, b must be of type ResourceId
func CompareResourceIds(a, b interface{}) int {
aa, aok := a.(ResourceId)
bb, bok := b.(ResourceId)
if !aok {
panic("a is not a ResourceId")
}
if !bok {
panic("b is not a ResourceId")
}
if resourceIdsEqual(aa, bb) {
func ResourceIdsCompare(a, b client.Object) int {
if resourceIdsEqual(a, b) {
return 0
}
if resourceIdsLessThan(aa, bb) {
if resourceIdsLessThan(a, b) {
return -1
}
return 1
}

func ResourceIdsAscending(a, b interface{}) bool {
aa, aok := a.(ResourceId)
bb, bok := b.(ResourceId)
if !aok {
panic("a is not a ResourceId")
}
if !bok {
panic("b is not a ResourceId")
}
return resourceIdsLessThan(aa, bb)
}

func resourceIdsEqual(a, b ResourceId) bool {
return a.GetName() == b.GetName() && a.GetNamespace() == b.GetNamespace()
}

func resourceIdsLessThan(a, b ResourceId) bool {
// namespace is the primary sort key
if a.GetNamespace() > b.GetNamespace() {
return false
}
// name is the secondary sort key
if a.GetName() < b.GetName() {
return true
}
return false
func resourceIdsEqual(a, b client.Object) bool {
return a.GetName() == b.GetName() && a.GetNamespace() == b.GetNamespace() && GetClusterName(a) == GetClusterName(b)
}

// CompareResourceId returns an integer comparing two ResourceIds lexicographically.
// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
// a, b must be of type ResourceId
func CompareObjects(a, b client.Object) int {
if controllerutils.ObjectsEqual(a, b) {
return 0
}
if objectsLessThan(a, b) {
return -1
}
return 1
}

func ObjectsAscending(a, b client.Object) bool {
return objectsLessThan(a, b)
}

func objectsLessThan(a, b client.Object) bool {
func resourceIdsLessThan(a, b client.Object) bool {
// cluster name is the primary sort key
if GetClusterName(a) > GetClusterName(b) {
return false
}
// namespace is the secondary sort key
if a.GetNamespace() < b.GetNamespace() {
return true
if a.GetNamespace() > b.GetNamespace() {
return false
}
// name is the secondary sort key
if a.GetName() < b.GetName() {
return true
// name is the tertiary sort key
if a.GetName() > b.GetName() {
return false
}
return false
return true
}

0 comments on commit 2e6dfb4

Please sign in to comment.