From bdbff836b433e356fa6efc4fa616d38adb1dd442 Mon Sep 17 00:00:00 2001 From: lengrongfu <1275177125@qq.com> Date: Wed, 17 May 2023 22:00:08 +0800 Subject: [PATCH] add memory filter lable select Signed-off-by: lengrongfu <1275177125@qq.com> --- .../memorystorage/watchcache/watch_cache.go | 71 +++- .../watchcache/watch_cache_test.go | 304 ++++++++++++++++++ 2 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 pkg/storage/memorystorage/watchcache/watch_cache_test.go diff --git a/pkg/storage/memorystorage/watchcache/watch_cache.go b/pkg/storage/memorystorage/watchcache/watch_cache.go index 639452dab..a03677aff 100644 --- a/pkg/storage/memorystorage/watchcache/watch_cache.go +++ b/pkg/storage/memorystorage/watchcache/watch_cache.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/watch" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic/registry" @@ -112,6 +113,19 @@ func GetKeyFunc(gvr schema.GroupVersionResource, isNamespaced bool) keyFunc { return kc } +func GetAttrsFunc(obj runtime.Object) (labels.Set, fields.Set, error) { + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, nil, err + } + objLabels := accessor.GetLabels() + if objLabels == nil { + objLabels = make(map[string]string) + } + labelSet := labels.Set(objLabels) + return labelSet, nil, nil +} + func storeElementKey(obj interface{}) (string, error) { elem, ok := obj.(*StoreElement) if !ok { @@ -153,7 +167,7 @@ func NewWatchCache(capacity int, gvr schema.GroupVersionResource, isNamespaced b wc := &WatchCache{ capacity: capacity, KeyFunc: GetKeyFunc(gvr, isNamespaced), - getAttrsFunc: nil, + getAttrsFunc: GetAttrsFunc, cache: make([]*watch.Event, capacity), startIndex: 0, endIndex: 0, @@ -305,12 +319,67 @@ func (w *WatchCache) WaitUntilFreshAndList(opts *internal.ListOptions) ([]*Store continue } } + if filterLabelSelector(opts.LabelSelector, se.Labels) { + continue + } result = append(result, se) } } return result, w.resourceVersion, nil } +// filterLabelSelector returns true if the given label selector matches the given label set. else false. +func filterLabelSelector(labelSelector labels.Selector, label labels.Set) bool { + if labelSelector != nil && label != nil { + if requirements, selectable := labelSelector.Requirements(); selectable { + for _, requirement := range requirements { + values := requirement.Values().List() + switch requirement.Operator() { + case selection.Exists: + if !label.Has(requirement.Key()) { + return true + } + case selection.DoesNotExist: + if label.Has(requirement.Key()) { + return true + } + case selection.Equals, selection.DoubleEquals: + labelValue := label.Get(requirement.Key()) + if labelValue != values[0] { + return true + } + case selection.NotEquals: + labelValue := label.Get(requirement.Key()) + if labelValue == values[0] { + return true + } + case selection.In: + labelValue := label.Get(requirement.Key()) + valuesMap := make(map[string]struct{}) + for _, value := range values { + valuesMap[value] = struct{}{} + } + if _, ok := valuesMap[labelValue]; !ok { + return true + } + case selection.NotIn: + labelValue := label.Get(requirement.Key()) + valuesMap := make(map[string]struct{}) + for _, value := range values { + valuesMap[value] = struct{}{} + } + if _, ok := valuesMap[labelValue]; ok { + return true + } + default: + continue + } + } + } + } + return false +} + // WaitUntilFreshAndGet returns list of pointers to objects. func (w *WatchCache) WaitUntilFreshAndGet(cluster, namespace, name string) (*StoreElement, error) { w.RLock() diff --git a/pkg/storage/memorystorage/watchcache/watch_cache_test.go b/pkg/storage/memorystorage/watchcache/watch_cache_test.go new file mode 100644 index 000000000..db957ee7c --- /dev/null +++ b/pkg/storage/memorystorage/watchcache/watch_cache_test.go @@ -0,0 +1,304 @@ +package watchcache + +import ( + "testing" + + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" +) + +func Test_filterLabelSelector(t *testing.T) { + type request struct { + labelSelector labels.Selector + label labels.Set + } + var tests = []struct { + name string + req request + before func(req *request) + want bool + }{ + { + name: "empty label selector", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + labelSelector: nil, + label: labels.Set{}, + }, + want: false, + }, + { + name: "empty label", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + labelSelector: labels.Everything(), + label: nil, + }, + want: false, + }, + { + name: "lable exist operator,but exist", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.Exists, nil) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + { + name: "lable exist operator,but not exist", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("app", selection.Exists, nil) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable not exist operator,but exist", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.DoesNotExist, nil) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable not exist operator,but not exist", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("app", selection.DoesNotExist, nil) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + { + name: "lable Equals operator,but not equals", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.Equals, []string{"prod"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable Equals operator,but equals", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.Equals, []string{"dev"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + { + name: "lable NotEquals operator,but equals", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.NotEquals, []string{"dev"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable NotEquals operator,but not equals", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.NotEquals, []string{"prod"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + { + name: "lable IN operator,but not in", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.In, []string{"prod", "test"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable IN operator,but in", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.In, []string{"prod", "test", "dev"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + { + name: "lable NotIn operator,but in", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.NotIn, []string{"prod", "test", "dev"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: true, + }, + { + name: "lable NotIn operator,but not in", + req: struct { + labelSelector labels.Selector + label labels.Set + }{ + labelSelector: labels.NewSelector(), + label: labels.Set{ + "env": "dev", + }, + }, + before: func(req *request) { + requirement, err := labels.NewRequirement("env", selection.NotIn, []string{"prod", "test"}) + if err != nil { + t.Fatal(err) + } + req.labelSelector = req.labelSelector.Add(labels.Requirements{*requirement}...) + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.before != nil { + tt.before(&tt.req) + } + if got := filterLabelSelector(tt.req.labelSelector, tt.req.label); got != tt.want { + t.Errorf("filterLabelSelector() = %v, want %v", got, tt.want) + } + }) + } +}