From f669dba24dd6a56c9148e8955a2b7d34b3da0d48 Mon Sep 17 00:00:00 2001 From: wangguan Date: Tue, 2 Jan 2024 21:20:07 +0800 Subject: [PATCH] feat: cache selector --- pkg/admin/cache/registry/universal/cache.go | 155 +++++++++++------- .../cache/selector/application_selector.go | 56 +++++++ pkg/admin/cache/selector/multi_selector.go | 124 ++++++++++++++ pkg/admin/cache/selector/selector.go | 37 +++-- pkg/admin/cache/selector/service_selector.go | 66 ++++++++ 5 files changed, 365 insertions(+), 73 deletions(-) create mode 100644 pkg/admin/cache/selector/application_selector.go create mode 100644 pkg/admin/cache/selector/multi_selector.go create mode 100644 pkg/admin/cache/selector/service_selector.go diff --git a/pkg/admin/cache/registry/universal/cache.go b/pkg/admin/cache/registry/universal/cache.go index 28eb42413..5208fb4f1 100644 --- a/pkg/admin/cache/registry/universal/cache.go +++ b/pkg/admin/cache/registry/universal/cache.go @@ -194,26 +194,28 @@ func (uc *UniversalCache) GetInstancesWithSelector(namespace string, selector se uc.providers.lock.RLock() for application, serviceMap := range uc.providers.data { - if targetApplication, ok := selector.ApplicationOption(); ok && targetApplication != application { + if !selectByApplication(selector, application) { continue - } else { - for serviceKey, instanceMap := range serviceMap { - for _, dubboModel := range instanceMap { - if _, ok := instanceSet[dubboModel.Ip+":"+dubboModel.Port]; ok { - continue - } else { - instanceSet[dubboModel.Ip+":"+dubboModel.Port] = struct{}{} - res = append(res, &cache.InstanceModel{ - Application: &cache.ApplicationModel{Name: application}, - Workload: nil, - Name: serviceKey + "#" + dubboModel.Ip + ":" + dubboModel.Port, - Ip: dubboModel.Ip, - Port: dubboModel.Port, - Status: "", - Node: "", - Labels: nil, - }) - } + } + for serviceKey, instanceMap := range serviceMap { + if !selectByServiceKey(selector, serviceKey) { + continue + } + for _, dubboModel := range instanceMap { + if _, ok := instanceSet[dubboModel.Ip+":"+dubboModel.Port]; ok { + continue + } else { + instanceSet[dubboModel.Ip+":"+dubboModel.Port] = struct{}{} + res = append(res, &cache.InstanceModel{ + Application: &cache.ApplicationModel{Name: application}, + Workload: nil, + Name: serviceKey + "#" + dubboModel.Ip + ":" + dubboModel.Port, + Ip: dubboModel.Ip, + Port: dubboModel.Port, + Status: "", + Node: "", + Labels: nil, + }) } } } @@ -222,26 +224,28 @@ func (uc *UniversalCache) GetInstancesWithSelector(namespace string, selector se uc.consumers.lock.RLock() for application, serviceMap := range uc.consumers.data { - if targetApplication, ok := selector.ApplicationOption(); ok && targetApplication != application { + if !selectByApplication(selector, application) { continue - } else { - for serviceKey, instanceMap := range serviceMap { - for _, dubboModel := range instanceMap { - if _, ok := instanceSet[dubboModel.Ip+":"+dubboModel.Port]; ok { - continue - } else { - instanceSet[dubboModel.Ip+":"+dubboModel.Port] = struct{}{} - res = append(res, &cache.InstanceModel{ - Application: &cache.ApplicationModel{Name: application}, - Workload: nil, - Name: serviceKey + "#" + dubboModel.Ip + ":" + dubboModel.Port, - Ip: dubboModel.Ip, - Port: dubboModel.Port, - Status: "", - Node: "", - Labels: nil, - }) - } + } + for serviceKey, instanceMap := range serviceMap { + if !selectByServiceKey(selector, serviceKey) { + continue + } + for _, dubboModel := range instanceMap { + if _, ok := instanceSet[dubboModel.Ip+":"+dubboModel.Port]; ok { + continue + } else { + instanceSet[dubboModel.Ip+":"+dubboModel.Port] = struct{}{} + res = append(res, &cache.InstanceModel{ + Application: &cache.ApplicationModel{Name: application}, + Workload: nil, + Name: serviceKey + "#" + dubboModel.Ip + ":" + dubboModel.Port, + Ip: dubboModel.Ip, + Port: dubboModel.Port, + Status: "", + Node: "", + Labels: nil, + }) } } } @@ -294,41 +298,46 @@ func (uc *UniversalCache) GetServicesWithSelector(namespace string, selector sel uc.providers.lock.RLock() for application, serviceMap := range uc.providers.data { - if targetApplication, ok := selector.ApplicationOption(); ok && targetApplication != application { + if !selectByApplication(selector, application) { continue - } else { - for serviceKey := range serviceMap { - res = append(res, &cache.ServiceModel{ - Application: &cache.ApplicationModel{Name: application}, - Category: constant.ProviderSide, - Name: util.GetInterface(serviceKey), - Labels: nil, - ServiceKey: serviceKey, - Group: util.GetGroup(serviceKey), - Version: util.GetVersion(serviceKey), - }) + } + for serviceKey := range serviceMap { + if !selectByServiceKey(selector, serviceKey) { + continue } + res = append(res, &cache.ServiceModel{ + Application: &cache.ApplicationModel{Name: application}, + Category: constant.ProviderSide, + Name: util.GetInterface(serviceKey), + Labels: nil, + ServiceKey: serviceKey, + Group: util.GetGroup(serviceKey), + Version: util.GetVersion(serviceKey), + }) } } uc.providers.lock.RUnlock() uc.consumers.lock.RLock() for application, serviceMap := range uc.consumers.data { - if targetApplication, ok := selector.ApplicationOption(); ok && targetApplication != application { + if !selectByApplication(selector, application) { continue - } else { - for serviceKey := range serviceMap { - res = append(res, &cache.ServiceModel{ - Application: &cache.ApplicationModel{Name: application}, - Category: constant.ConsumerSide, - Name: util.GetInterface(serviceKey), - Labels: nil, - ServiceKey: serviceKey, - Group: util.GetGroup(serviceKey), - Version: util.GetVersion(serviceKey), - }) + } + for serviceKey := range serviceMap { + if !selectByServiceKey(selector, serviceKey) { + continue } + res = append(res, &cache.ServiceModel{ + Application: &cache.ApplicationModel{Name: application}, + Category: constant.ConsumerSide, + Name: util.GetInterface(serviceKey), + Labels: nil, + ServiceKey: serviceKey, + Group: util.GetGroup(serviceKey), + Version: util.GetVersion(serviceKey), + }) } + } uc.consumers.lock.RUnlock() @@ -489,3 +498,25 @@ func (m *DubboModel) ToggleRegistryType(deleteType string) { m.RegistryType = constant.RegistryInstance } } + +// selectByServiceKey is used to determine whether the serviceKey matches the selector +func selectByServiceKey(selector selector.Selector, serviceKey string) bool { + if serviceNameOptions, ok := selector.ServiceNameOptions(); ok && !serviceNameOptions.Exist(util.GetInterface(serviceKey)) { + return false + } + if serviceGroupOptions, ok := selector.ServiceGroupOptions(); ok && !serviceGroupOptions.Exist(util.GetGroup(serviceKey)) { + return false + } + if serviceVersionOptions, ok := selector.ServiceVersionOptions(); ok && !serviceVersionOptions.Exist(util.GetVersion(serviceKey)) { + return false + } + return true +} + +// selectByApplication is used to determine whether the application matches the selector +func selectByApplication(selector selector.Selector, application string) bool { + if applicationOptions, ok := selector.ApplicationOptions(); ok && !applicationOptions.Exist(application) { + return false + } + return true +} diff --git a/pkg/admin/cache/selector/application_selector.go b/pkg/admin/cache/selector/application_selector.go new file mode 100644 index 000000000..43f580bcd --- /dev/null +++ b/pkg/admin/cache/selector/application_selector.go @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package selector + +import ( + "github.com/apache/dubbo-kubernetes/pkg/admin/constant" + "k8s.io/apimachinery/pkg/labels" +) + +type ApplicationSelector struct { + Name string +} + +func NewApplicationSelector(name string) *ApplicationSelector { + return &ApplicationSelector{ + Name: name, + } +} + +func (s *ApplicationSelector) AsLabelsSelector() labels.Selector { + selector := labels.Set{ + constant.ApplicationLabel: s.Name, + } + return selector.AsSelector() +} + +func (s *ApplicationSelector) ApplicationOptions() (Options, bool) { + return newOptions(s.Name), true +} + +func (s *ApplicationSelector) ServiceNameOptions() (Options, bool) { + return nil, false +} + +func (s *ApplicationSelector) ServiceGroupOptions() (Options, bool) { + return nil, false +} + +func (s *ApplicationSelector) ServiceVersionOptions() (Options, bool) { + return nil, false +} diff --git a/pkg/admin/cache/selector/multi_selector.go b/pkg/admin/cache/selector/multi_selector.go new file mode 100644 index 000000000..2648461c9 --- /dev/null +++ b/pkg/admin/cache/selector/multi_selector.go @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package selector + +import ( + "github.com/apache/dubbo-kubernetes/pkg/admin/constant" + "github.com/apache/dubbo-kubernetes/pkg/core/logger" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" +) + +// MultiSelectors is an implement of Selector to combine multiple selectors, use NewMultiSelector to create it, and use Add to build it +type MultiSelectors struct { + applicationNames []string + serviceNames []string + serviceGroups []string + serviceVersions []string +} + +func NewMultiSelector() *MultiSelectors { + return &MultiSelectors{ + applicationNames: make([]string, 0), + serviceNames: make([]string, 0), + serviceGroups: make([]string, 0), + serviceVersions: make([]string, 0), + } +} + +func (s *MultiSelectors) Add(selector Selector) *MultiSelectors { + switch selector.(type) { + case *ApplicationSelector: + s.applicationNames = append(s.applicationNames, selector.(*ApplicationSelector).Name) + case *ServiceSelector: + s.serviceNames = append(s.serviceNames, selector.(*ServiceSelector).Name) + if selector.(*ServiceSelector).Group != "" { + s.serviceGroups = append(s.serviceGroups, selector.(*ServiceSelector).Group) + } + if selector.(*ServiceSelector).Version != "" { + s.serviceVersions = append(s.serviceVersions, selector.(*ServiceSelector).Version) + } + } + return s +} + +func (s *MultiSelectors) AsLabelsSelector() labels.Selector { + requirements := make([]labels.Requirement, 0) + + if len(s.applicationNames) > 0 { + req, err := labels.NewRequirement(constant.ApplicationLabel, selection.In, s.applicationNames) + if err != nil { + logger.Errorf("failed to create requirement for application selector: %v", err) + } + requirements = append(requirements, *req) + } + + if len(s.serviceNames) > 0 { + req, err := labels.NewRequirement(constant.ServiceKeyLabel, selection.In, s.serviceNames) + if err != nil { + logger.Errorf("failed to create requirement for service selector: %v", err) + } + requirements = append(requirements, *req) + } + + if len(s.serviceGroups) > 0 { + req, err := labels.NewRequirement(constant.GroupLabel, selection.In, s.serviceGroups) + if err != nil { + logger.Errorf("failed to create requirement for group selector: %v", err) + } + requirements = append(requirements, *req) + } + + if len(s.serviceVersions) > 0 { + req, err := labels.NewRequirement(constant.VersionLabel, selection.In, s.serviceVersions) + if err != nil { + logger.Errorf("failed to create requirement for version selector: %v", err) + } + requirements = append(requirements, *req) + } + + return labels.NewSelector().Add(requirements...) +} + +func (s *MultiSelectors) ApplicationOptions() (Options, bool) { + if len(s.applicationNames) == 0 { + return nil, false + } + return newOptions(s.applicationNames...), true +} + +func (s *MultiSelectors) ServiceNameOptions() (Options, bool) { + if len(s.serviceNames) == 0 { + return nil, false + } + return newOptions(s.serviceNames...), true +} + +func (s *MultiSelectors) ServiceGroupOptions() (Options, bool) { + if len(s.serviceGroups) == 0 { + return nil, false + } + return newOptions(s.serviceGroups...), true +} + +func (s *MultiSelectors) ServiceVersionOptions() (Options, bool) { + if len(s.serviceVersions) == 0 { + return nil, false + } + return newOptions(s.serviceVersions...), true +} diff --git a/pkg/admin/cache/selector/selector.go b/pkg/admin/cache/selector/selector.go index 61271525b..0e48bdaac 100644 --- a/pkg/admin/cache/selector/selector.go +++ b/pkg/admin/cache/selector/selector.go @@ -18,26 +18,41 @@ package selector import ( - "github.com/apache/dubbo-kubernetes/pkg/admin/constant" "k8s.io/apimachinery/pkg/labels" ) +// Selector is an interface for selecting resources from cache type Selector interface { AsLabelsSelector() labels.Selector - ApplicationOption() (string, bool) + ApplicationOptions() (Options, bool) + ServiceNameOptions() (Options, bool) + ServiceGroupOptions() (Options, bool) + ServiceVersionOptions() (Options, bool) } -type ApplicationSelector struct { - Name string + +// Options is an interface to represent possible options of a selector at a certain level(e.g. application, service) +type Options interface { + Len() int + Exist(str string) bool } -func (s *ApplicationSelector) AsLabelsSelector() labels.Selector { - selector := labels.Set{ - constant.ApplicationLabel: s.Name, - } - return selector.AsSelector() +func newOptions(strs ...string) Options { + return options(strs) +} + +// options is a slice of string, it implements Options interface +type options []string + +func (o options) Len() int { + return len(o) } -func (s *ApplicationSelector) ApplicationOption() (string, bool) { - return s.Name, true +func (o options) Exist(str string) bool { + for _, s := range o { + if s == str { + return true + } + } + return false } diff --git a/pkg/admin/cache/selector/service_selector.go b/pkg/admin/cache/selector/service_selector.go new file mode 100644 index 000000000..fd534f83d --- /dev/null +++ b/pkg/admin/cache/selector/service_selector.go @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package selector + +import ( + "github.com/apache/dubbo-kubernetes/pkg/admin/constant" + "k8s.io/apimachinery/pkg/labels" +) + +type ServiceSelector struct { + Name string + Group string + Version string +} + +func NewServiceSelector(name, group, version string) *ServiceSelector { + return &ServiceSelector{ + Name: name, + Group: group, + Version: version, + } +} + +func (s *ServiceSelector) AsLabelsSelector() labels.Selector { + selector := labels.Set{ + constant.ServiceKeyLabel: s.Name, + } + if s.Group != "" { + selector[constant.GroupLabel] = s.Group + } + if s.Version != "" { + selector[constant.VersionLabel] = s.Version + } + return selector.AsSelector() +} + +func (s *ServiceSelector) ApplicationOptions() (Options, bool) { + return nil, false +} + +func (s *ServiceSelector) ServiceNameOptions() (Options, bool) { + return newOptions(s.Name), true +} + +func (s *ServiceSelector) ServiceGroupOptions() (Options, bool) { + return newOptions(s.Group), true +} + +func (s *ServiceSelector) ServiceVersionOptions() (Options, bool) { + return newOptions(s.Version), true +}