Introduce generic hashset to simplify code
Signed-off-by: RainbowMango <qdurenhongcai@gmail.com>
This commit is contained in:
parent
e47d6cb71b
commit
14423bf5d8
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
@ -415,22 +414,6 @@ func (d *DependenciesDistributor) syncScheduleResultToAttachedBindings(binding *
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DependenciesDistributor) recordDependenciesForIndependentBinding(binding *workv1alpha2.ResourceBinding, dependencies []configv1alpha1.DependentObjectReference) error {
|
func (d *DependenciesDistributor) recordDependenciesForIndependentBinding(binding *workv1alpha2.ResourceBinding, dependencies []configv1alpha1.DependentObjectReference) error {
|
||||||
sort.Slice(dependencies, func(i, j int) bool {
|
|
||||||
if dependencies[i].APIVersion != dependencies[j].APIVersion {
|
|
||||||
return dependencies[i].APIVersion < dependencies[j].APIVersion
|
|
||||||
}
|
|
||||||
if dependencies[i].Kind != dependencies[j].Kind {
|
|
||||||
return dependencies[i].Kind < dependencies[j].Kind
|
|
||||||
}
|
|
||||||
if dependencies[i].Namespace != dependencies[j].Namespace {
|
|
||||||
return dependencies[i].Namespace < dependencies[j].Namespace
|
|
||||||
}
|
|
||||||
if dependencies[i].Name != dependencies[j].Name {
|
|
||||||
return dependencies[i].Name < dependencies[j].Name
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
dependenciesBytes, err := json.Marshal(dependencies)
|
dependenciesBytes, err := json.Marshal(dependencies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("failed to marshal dependencies of binding(%s/%s): %v", binding.Namespace, binding.Name, err)
|
klog.Errorf("failed to marshal dependencies of binding(%s/%s): %v", binding.Namespace, binding.Name, err)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package configurableinterpreter
|
package configurableinterpreter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
@ -11,6 +13,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/configmanager"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/configmanager"
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/luavm"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/luavm"
|
||||||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/hashset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigurableInterpreter interprets resources with resource interpreter customizations.
|
// ConfigurableInterpreter interprets resources with resource interpreter customizations.
|
||||||
|
@ -150,7 +153,7 @@ func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructu
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
referenceSet := newDependencySet()
|
refs := hashset.Make[configv1alpha1.DependentObjectReference]()
|
||||||
for _, luaScript := range scripts {
|
for _, luaScript := range scripts {
|
||||||
var references []configv1alpha1.DependentObjectReference
|
var references []configv1alpha1.DependentObjectReference
|
||||||
references, err = c.luaVM.GetDependencies(object, luaScript)
|
references, err = c.luaVM.GetDependencies(object, luaScript)
|
||||||
|
@ -159,9 +162,23 @@ func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructu
|
||||||
object.GroupVersionKind(), object.GetNamespace(), object.GetName(), err)
|
object.GroupVersionKind(), object.GetNamespace(), object.GetName(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
referenceSet.insert(references...)
|
refs.Insert(references...)
|
||||||
}
|
}
|
||||||
dependencies = referenceSet.list()
|
dependencies = refs.List()
|
||||||
|
|
||||||
|
// keep returned items in the same order between each call.
|
||||||
|
sort.Slice(dependencies, func(i, j int) bool {
|
||||||
|
if dependencies[i].APIVersion != dependencies[j].APIVersion {
|
||||||
|
return dependencies[i].APIVersion < dependencies[j].APIVersion
|
||||||
|
}
|
||||||
|
if dependencies[i].Kind != dependencies[j].Kind {
|
||||||
|
return dependencies[i].Kind < dependencies[j].Kind
|
||||||
|
}
|
||||||
|
if dependencies[i].Namespace != dependencies[j].Namespace {
|
||||||
|
return dependencies[i].Namespace < dependencies[j].Namespace
|
||||||
|
}
|
||||||
|
return dependencies[i].Name < dependencies[j].Name
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package configurableinterpreter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dependencySet map[configv1alpha1.DependentObjectReference]struct{}
|
|
||||||
|
|
||||||
func newDependencySet(items ...configv1alpha1.DependentObjectReference) dependencySet {
|
|
||||||
s := make(dependencySet, len(items))
|
|
||||||
s.insert(items...)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s dependencySet) insert(items ...configv1alpha1.DependentObjectReference) dependencySet {
|
|
||||||
for _, item := range items {
|
|
||||||
s[item] = struct{}{}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s dependencySet) list() []configv1alpha1.DependentObjectReference {
|
|
||||||
keys := make([]configv1alpha1.DependentObjectReference, len(s))
|
|
||||||
index := 0
|
|
||||||
for key := range s {
|
|
||||||
keys[index] = key
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
sort.Slice(keys, func(i, j int) bool {
|
|
||||||
return fmt.Sprintf("%s", keys[i]) < fmt.Sprintf("%s", keys[j])
|
|
||||||
})
|
|
||||||
return keys
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package configurableinterpreter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_dependencySet_list(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
s dependencySet
|
|
||||||
want []configv1alpha1.DependentObjectReference
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty set",
|
|
||||||
s: newDependencySet(),
|
|
||||||
want: []configv1alpha1.DependentObjectReference{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new with items",
|
|
||||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
}...),
|
|
||||||
want: []configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "insert with different items",
|
|
||||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
}...).insert(
|
|
||||||
[]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
|
||||||
}...),
|
|
||||||
want: []configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "insert with same items",
|
|
||||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
}...).insert(
|
|
||||||
[]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
|
||||||
}...).insert(
|
|
||||||
[]configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
{APIVersion: "apps/v1", Kind: "Deployment", Namespace: "foo", Name: "foo"},
|
|
||||||
}...),
|
|
||||||
want: []configv1alpha1.DependentObjectReference{
|
|
||||||
{APIVersion: "apps/v1", Kind: "Deployment", Namespace: "foo", Name: "foo"},
|
|
||||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
|
||||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := tt.s.list(); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("dependencySet.list() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package hashset
|
||||||
|
|
||||||
|
// Set is a data structure that can store elements and has no repeated values.
|
||||||
|
// A set is backed by a hash table(Go's map), and is not safe for concurrent use
|
||||||
|
// by multiple goroutines without additional locking or coordination.
|
||||||
|
type Set[T comparable] map[T]struct{}
|
||||||
|
|
||||||
|
// Make builds a hash set.
|
||||||
|
func Make[T comparable]() Set[T] {
|
||||||
|
return make(Set[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert adds items to the set.
|
||||||
|
func (s Set[T]) Insert(items ...T) {
|
||||||
|
for _, item := range items {
|
||||||
|
s[item] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns the contents as a slice.
|
||||||
|
func (s Set[T]) List() []T {
|
||||||
|
res := make([]T, 0, len(s))
|
||||||
|
|
||||||
|
for key := range s {
|
||||||
|
res = append(res, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package hashset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleSet_List() {
|
||||||
|
s := Make[int]() // make a set for int
|
||||||
|
s.Insert(1, 3, 2)
|
||||||
|
s.Insert(4)
|
||||||
|
l := s.List()
|
||||||
|
sort.Slice(l, func(i, j int) bool {
|
||||||
|
return l[i] < l[j]
|
||||||
|
})
|
||||||
|
fmt.Println(l)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet_List_BuiltinType(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "input nothing should list nothing",
|
||||||
|
input: []string{},
|
||||||
|
expected: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should guarantee no repeated items",
|
||||||
|
input: []string{"Kevin", "Jim", "Kevin", "Kevin"}, // repeat "Kevin"
|
||||||
|
expected: []string{"Jim", "Kevin"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
set := Make[string]()
|
||||||
|
set.Insert(test.input...)
|
||||||
|
list := set.List()
|
||||||
|
sort.Slice(list, func(i, j int) bool {
|
||||||
|
return list[i] < list[j]
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(list, test.expected) {
|
||||||
|
t.Fatalf("expected: %v, but got: %v", test.expected, list)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet_List_CustomType(t *testing.T) {
|
||||||
|
type CustomType struct {
|
||||||
|
Kind string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []CustomType
|
||||||
|
expected []CustomType
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "input nothing should list nothing",
|
||||||
|
input: []CustomType{},
|
||||||
|
expected: []CustomType{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should guarantee no repeated items",
|
||||||
|
input: []CustomType{ // repeat {Kind: "k1", Name: "n1"}
|
||||||
|
{Kind: "k1", Name: "n1"},
|
||||||
|
{Kind: "k2", Name: "n2"},
|
||||||
|
{Kind: "k1", Name: "n1"},
|
||||||
|
},
|
||||||
|
expected: []CustomType{{Kind: "k1", Name: "n1"}, {Kind: "k2", Name: "n2"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
set := Make[CustomType]()
|
||||||
|
set.Insert(test.input...)
|
||||||
|
list := set.List()
|
||||||
|
sort.Slice(list, func(i, j int) bool {
|
||||||
|
if list[i].Kind != list[j].Kind {
|
||||||
|
return list[i].Kind < list[j].Kind
|
||||||
|
}
|
||||||
|
return list[i].Name < list[j].Name
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(list, test.expected) {
|
||||||
|
t.Fatalf("expected: %v, but got: %v", test.expected, list)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue