diff --git a/pkg/descheduler/core/helper.go b/pkg/descheduler/core/helper.go index c5e257534a78..201124620875 100644 --- a/pkg/descheduler/core/helper.go +++ b/pkg/descheduler/core/helper.go @@ -62,26 +62,60 @@ func NewSchedulingResultHelper(binding *workv1alpha2.ResourceBinding) *Schedulin func (h *SchedulingResultHelper) FillUnschedulableReplicas(unschedulableThreshold time.Duration) { reference := &h.Spec.Resource undesiredClusters, undesiredClusterNames := h.GetUndesiredClusters() + // clusterIndexToEstimatorPriority key refers to index of cluster slice, + // value refers to the EstimatorPriority of who gave its estimated result. + clusterIndexToEstimatorPriority := make(map[int]estimatorclient.EstimatorPriority) + // Set the boundary. for i := range undesiredClusters { undesiredClusters[i].Unschedulable = math.MaxInt32 } - // Get the minimum value of MaxAvailableReplicas in terms of all estimators. + + // Get all replicaEstimators, which are stored in TreeMap. estimators := estimatorclient.GetUnschedulableReplicaEstimators() ctx := context.WithValue(context.TODO(), util.ContextKeyObject, fmt.Sprintf("kind=%s, name=%s/%s", reference.Kind, reference.Namespace, reference.Name)) - for _, estimator := range estimators { - res, err := estimator.GetUnschedulableReplicas(ctx, undesiredClusterNames, reference, unschedulableThreshold) - if err != nil { - klog.Errorf("Max cluster unschedulable replicas error: %v", err) - continue + + // List all unschedulableReplicaEstimators in order of descending priority. The estimators are grouped with different + // priorities, e.g: [priority:20, {estimators:[es1, es3]}, {priority:10, estimators:[es2, es4]}, ...] + estimatorGroups := estimators.Values() + + // Iterate the estimator groups in order of descending priority + for _, estimatorGroup := range estimatorGroups { + // if higher-priority estimators have formed a full result of member clusters, no longer to call lower-priority estimator. + if len(clusterIndexToEstimatorPriority) == len(undesiredClusterNames) { + break } - for i := range res { - if res[i].Replicas == estimatorclient.UnauthenticReplica { + estimatorsWithSamePriority := estimatorGroup.(map[string]estimatorclient.UnschedulableReplicaEstimator) + // iterate through these estimators with the same priority. + for _, estimator := range estimatorsWithSamePriority { + res, err := estimator.GetUnschedulableReplicas(ctx, undesiredClusterNames, reference, unschedulableThreshold) + if err != nil { + klog.Errorf("Max cluster unschedulable replicas error: %v", err) continue } - if undesiredClusters[i].ClusterName == res[i].Name && undesiredClusters[i].Unschedulable > res[i].Replicas { - undesiredClusters[i].Unschedulable = res[i].Replicas + for i := range res { + // the result of this cluster estimated failed, ignore the corresponding result + if res[i].Replicas == estimatorclient.UnauthenticReplica { + continue + } + // the cluster name not match, ignore, which hardly ever happens + if res[i].Name != undesiredClusters[i].ClusterName { + klog.Errorf("unexpected cluster name in the result of estimator with %d priority, "+ + "expected: %s, got: %s", estimator.Priority(), undesiredClusters[i].ClusterName, res[i].Name) + continue + } + // the result of this cluster has already been estimated by higher-priority estimator, + // ignore the corresponding result by this estimator + if priority, ok := clusterIndexToEstimatorPriority[i]; ok && estimator.Priority() < priority { + continue + } + // if multiple estimators are called, choose the minimum value of each estimated result, + // record the priority of result provider. + if res[i].Replicas < undesiredClusters[i].Unschedulable { + undesiredClusters[i].Unschedulable = res[i].Replicas + clusterIndexToEstimatorPriority[i] = estimator.Priority() + } } } } diff --git a/pkg/descheduler/descheduler.go b/pkg/descheduler/descheduler.go index f5689672fa81..7bb4ac4410ae 100644 --- a/pkg/descheduler/descheduler.go +++ b/pkg/descheduler/descheduler.go @@ -100,7 +100,8 @@ func NewDescheduler(karmadaClient karmadaclientset.Interface, kubeClient kuberne ReconcileFunc: desched.reconcileEstimatorConnection, } desched.schedulerEstimatorWorker = util.NewAsyncWorker(schedulerEstimatorWorkerOptions) - schedulerEstimator := estimatorclient.NewSchedulerEstimator(desched.schedulerEstimatorCache, opts.SchedulerEstimatorTimeout.Duration) + schedulerEstimator := estimatorclient.NewSchedulerEstimator(desched.schedulerEstimatorCache, + opts.SchedulerEstimatorTimeout.Duration, estimatorclient.Accurate) estimatorclient.RegisterSchedulerEstimator(schedulerEstimator) deschedulerWorkerOptions := util.Options{ Name: "descheduler", diff --git a/pkg/descheduler/descheduler_test.go b/pkg/descheduler/descheduler_test.go index 0b70bd2fb98e..a96558aa6efb 100644 --- a/pkg/descheduler/descheduler_test.go +++ b/pkg/descheduler/descheduler_test.go @@ -485,7 +485,7 @@ func TestDescheduler_worker(t *testing.T) { unschedulableThreshold: 5 * time.Minute, eventRecorder: record.NewFakeRecorder(1024), } - schedulerEstimator := estimatorclient.NewSchedulerEstimator(desched.schedulerEstimatorCache, 5*time.Second) + schedulerEstimator := estimatorclient.NewSchedulerEstimator(desched.schedulerEstimatorCache, 5*time.Second, estimatorclient.Accurate) estimatorclient.RegisterSchedulerEstimator(schedulerEstimator) for _, c := range tt.args.unschedulable { diff --git a/pkg/estimator/client/accurate.go b/pkg/estimator/client/accurate.go index e5390fe0c200..2e178de0a1c5 100644 --- a/pkg/estimator/client/accurate.go +++ b/pkg/estimator/client/accurate.go @@ -32,23 +32,25 @@ import ( // RegisterSchedulerEstimator will register a SchedulerEstimator. func RegisterSchedulerEstimator(se *SchedulerEstimator) { - replicaEstimators["scheduler-estimator"] = se - unschedulableReplicaEstimators["scheduler-estimator"] = se + registerReplicaEstimator("scheduler-estimator", se) + registerUnschedulableReplicaEstimator("scheduler-estimator", se) } type getClusterReplicasFunc func(ctx context.Context, cluster string) (int32, error) // SchedulerEstimator is an estimator that calls karmada-scheduler-estimator for estimation. type SchedulerEstimator struct { - cache *SchedulerEstimatorCache - timeout time.Duration + cache *SchedulerEstimatorCache + timeout time.Duration + priority EstimatorPriority } // NewSchedulerEstimator builds a new SchedulerEstimator. -func NewSchedulerEstimator(cache *SchedulerEstimatorCache, timeout time.Duration) *SchedulerEstimator { +func NewSchedulerEstimator(cache *SchedulerEstimatorCache, timeout time.Duration, priority EstimatorPriority) *SchedulerEstimator { return &SchedulerEstimator{ - cache: cache, - timeout: timeout, + cache: cache, + timeout: timeout, + priority: priority, } } @@ -67,6 +69,11 @@ func (se *SchedulerEstimator) MaxAvailableReplicas( }) } +// Priority provides the priority of this estimator. +func (se *SchedulerEstimator) Priority() EstimatorPriority { + return se.priority +} + // GetUnschedulableReplicas gets the unschedulable replicas which belong to a specified workload by calling karmada-scheduler-estimator. func (se *SchedulerEstimator) GetUnschedulableReplicas( parentCtx context.Context, diff --git a/pkg/estimator/client/general.go b/pkg/estimator/client/general.go index 6023c89f528a..9f13440c4189 100644 --- a/pkg/estimator/client/general.go +++ b/pkg/estimator/client/general.go @@ -32,15 +32,17 @@ import ( // GeneralEstimator is the default replica estimator. func init() { - replicaEstimators["general-estimator"] = NewGeneralEstimator() + registerReplicaEstimator("general-estimator", NewGeneralEstimator(General)) } // GeneralEstimator is a normal estimator in terms of cluster ResourceSummary. -type GeneralEstimator struct{} +type GeneralEstimator struct { + priority EstimatorPriority +} // NewGeneralEstimator builds a new GeneralEstimator. -func NewGeneralEstimator() *GeneralEstimator { - return &GeneralEstimator{} +func NewGeneralEstimator(priority EstimatorPriority) *GeneralEstimator { + return &GeneralEstimator{priority: priority} } // MaxAvailableReplicas estimates the maximum replicas that can be applied to the target cluster by cluster ResourceSummary. @@ -53,6 +55,11 @@ func (ge *GeneralEstimator) MaxAvailableReplicas(_ context.Context, clusters []* return availableTargetClusters, nil } +// Priority provides the priority of this estimator. +func (ge *GeneralEstimator) Priority() EstimatorPriority { + return ge.priority +} + func (ge *GeneralEstimator) maxAvailableReplicas(cluster *clusterv1alpha1.Cluster, replicaRequirements *workv1alpha2.ReplicaRequirements) int32 { resourceSummary := cluster.Status.ResourceSummary if resourceSummary == nil { diff --git a/pkg/estimator/client/interface.go b/pkg/estimator/client/interface.go index 2eab32a90ef7..8d1e55e89c48 100644 --- a/pkg/estimator/client/interface.go +++ b/pkg/estimator/client/interface.go @@ -20,6 +20,8 @@ import ( "context" "time" + "github.com/emirpasic/gods/maps/treemap" + clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" ) @@ -30,26 +32,83 @@ import ( const UnauthenticReplica = -1 var ( - replicaEstimators = map[string]ReplicaEstimator{} - unschedulableReplicaEstimators = map[string]UnschedulableReplicaEstimator{} + // replicaEstimators are organized in a TreeMap, sorted by descending priority. + // The key is of type EstimatorPriority, indicating the estimator's priority level. + // The value is a map with string keys and ReplicaEstimator values, grouping estimators by their respective priorities. + replicaEstimators = treemap.NewWith(estimatorPriorityComparator) + + // unschedulableReplicaEstimators are organized in a TreeMap, sorted by descending priority. + // The key is of type EstimatorPriority, indicating the estimator's priority level. + // The value is a map with string keys and UnschedulableReplicaEstimator values, grouping estimators by their respective priorities. + unschedulableReplicaEstimators = treemap.NewWith(estimatorPriorityComparator) ) +// registerReplicaEstimator add a estimator to replicaEstimators +func registerReplicaEstimator(estimatorName string, estimator ReplicaEstimator) { + if val, ok := replicaEstimators.Get(estimator.Priority()); !ok { + replicaEstimators.Put(estimator.Priority(), map[string]ReplicaEstimator{estimatorName: estimator}) + } else { + estimatorsWithSamePriority := val.(map[string]ReplicaEstimator) + estimatorsWithSamePriority[estimatorName] = estimator + } +} + +// registerUnschedulableReplicaEstimator add a estimator to unschedulableReplicaEstimators +func registerUnschedulableReplicaEstimator(estimatorName string, estimator UnschedulableReplicaEstimator) { + if val, ok := unschedulableReplicaEstimators.Get(estimator.Priority()); !ok { + unschedulableReplicaEstimators.Put(estimator.Priority(), map[string]UnschedulableReplicaEstimator{estimatorName: estimator}) + } else { + estimatorsWithSamePriority := val.(map[string]UnschedulableReplicaEstimator) + estimatorsWithSamePriority[estimatorName] = estimator + } +} + // ReplicaEstimator is an estimator which estimates the maximum replicas that can be applied to the target cluster. type ReplicaEstimator interface { MaxAvailableReplicas(ctx context.Context, clusters []*clusterv1alpha1.Cluster, replicaRequirements *workv1alpha2.ReplicaRequirements) ([]workv1alpha2.TargetCluster, error) + Priority() EstimatorPriority } // UnschedulableReplicaEstimator is an estimator which estimates the unschedulable replicas which belong to a specified workload. type UnschedulableReplicaEstimator interface { GetUnschedulableReplicas(ctx context.Context, clusters []string, reference *workv1alpha2.ObjectReference, unschedulableThreshold time.Duration) ([]workv1alpha2.TargetCluster, error) + Priority() EstimatorPriority } // GetReplicaEstimators returns all replica estimators. -func GetReplicaEstimators() map[string]ReplicaEstimator { +func GetReplicaEstimators() *treemap.Map { return replicaEstimators } // GetUnschedulableReplicaEstimators returns all unschedulable replica estimators. -func GetUnschedulableReplicaEstimators() map[string]UnschedulableReplicaEstimator { +func GetUnschedulableReplicaEstimators() *treemap.Map { return unschedulableReplicaEstimators } + +// EstimatorPriority the priority of estimator +// 1. If two estimators are of the same priority, call both and choose the minimum value of each estimated result. +// 2. If higher-priority estimators have formed a full result of member clusters, no longer to call lower-priority estimator. +// 3. If higher-priority estimators haven't given the result for certain member clusters, lower-priority estimator will +// continue to estimate for such clusters haven't got a result. +type EstimatorPriority int32 + +const ( + // General general priority, e.g: ResourceModel + General EstimatorPriority = 10 + // Accurate accurate priority, e.g: SchedulerEstimator + Accurate EstimatorPriority = 20 +) + +// estimatorPriorityComparator provides a basic comparison on EstimatorPriority. +func estimatorPriorityComparator(a, b interface{}) int { + aAsserted := a.(EstimatorPriority) + bAsserted := b.(EstimatorPriority) + switch { + case aAsserted > bAsserted: + return -1 + case aAsserted < bAsserted: + return 1 + default: + return 0 + } +} diff --git a/pkg/scheduler/core/util.go b/pkg/scheduler/core/util.go index 80dcfca0d153..e812c57f89c1 100644 --- a/pkg/scheduler/core/util.go +++ b/pkg/scheduler/core/util.go @@ -52,7 +52,11 @@ func getDefaultWeightPreference(clusters []*clusterv1alpha1.Cluster) *policyv1al } func calAvailableReplicas(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha2.ResourceBindingSpec) []workv1alpha2.TargetCluster { + // availableTargetClusters stores the result of estimated replicas for each clusters availableTargetClusters := make([]workv1alpha2.TargetCluster, len(clusters)) + // clusterIndexToEstimatorPriority key refers to index of cluster slice, + // value refers to the EstimatorPriority of who gave its estimated result. + clusterIndexToEstimatorPriority := make(map[int]estimatorclient.EstimatorPriority) // Set the boundary. for i := range availableTargetClusters { @@ -68,22 +72,51 @@ func calAvailableReplicas(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha return availableTargetClusters } - // Get the minimum value of MaxAvailableReplicas in terms of all estimators. - estimators := estimatorclient.GetReplicaEstimators() + // Get all replicaEstimators, which are stored in TreeMap. + replicaEstimators := estimatorclient.GetReplicaEstimators() ctx := context.WithValue(context.TODO(), util.ContextKeyObject, fmt.Sprintf("kind=%s, name=%s/%s", spec.Resource.Kind, spec.Resource.Namespace, spec.Resource.Name)) - for _, estimator := range estimators { - res, err := estimator.MaxAvailableReplicas(ctx, clusters, spec.ReplicaRequirements) - if err != nil { - klog.Errorf("Max cluster available replicas error: %v", err) - continue + + // List all replicaEstimators in order of descending priority. The estimators are grouped with different priorities, + // e.g: [priority:20, {estimators:[es1, es3]}, {priority:10, estimators:[es2, es4]}, ...] + estimatorGroups := replicaEstimators.Values() + + // Iterate the estimator groups in order of descending priority + for _, estimatorGroup := range estimatorGroups { + // if higher-priority estimators have formed a full result of member clusters, no longer to call lower-priority estimator. + if len(clusterIndexToEstimatorPriority) == len(clusters) { + break } - for i := range res { - if res[i].Replicas == estimatorclient.UnauthenticReplica { + estimatorsWithSamePriority := estimatorGroup.(map[string]estimatorclient.ReplicaEstimator) + // iterate through these estimators with the same priority. + for _, estimator := range estimatorsWithSamePriority { + res, err := estimator.MaxAvailableReplicas(ctx, clusters, spec.ReplicaRequirements) + if err != nil { + klog.Errorf("Max cluster available replicas error: %v", err) continue } - if availableTargetClusters[i].Name == res[i].Name && availableTargetClusters[i].Replicas > res[i].Replicas { - availableTargetClusters[i].Replicas = res[i].Replicas + for i := range res { + // the result of this cluster estimated failed, ignore the corresponding result + if res[i].Replicas == estimatorclient.UnauthenticReplica { + continue + } + // the cluster name not match, ignore, which hardly ever happens + if res[i].Name != availableTargetClusters[i].Name { + klog.Errorf("unexpected cluster name in the result of estimator with %d priority, "+ + "expected: %s, got: %s", estimator.Priority(), availableTargetClusters[i].Name, res[i].Name) + continue + } + // the result of this cluster has already been estimated by higher-priority estimator, + // ignore the corresponding result by this estimator + if priority, ok := clusterIndexToEstimatorPriority[i]; ok && estimator.Priority() < priority { + continue + } + // if multiple estimators are called, choose the minimum value of each estimated result, + // record the priority of result provider. + if res[i].Replicas < availableTargetClusters[i].Replicas { + availableTargetClusters[i].Replicas = res[i].Replicas + clusterIndexToEstimatorPriority[i] = estimator.Priority() + } } } } diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index 1bd2c6af58bf..c29422ad17f1 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -262,7 +262,8 @@ func NewScheduler(dynamicClient dynamic.Interface, karmadaClient karmadaclientse ReconcileFunc: sched.reconcileEstimatorConnection, } sched.schedulerEstimatorWorker = util.NewAsyncWorker(schedulerEstimatorWorkerOptions) - schedulerEstimator := estimatorclient.NewSchedulerEstimator(sched.schedulerEstimatorCache, options.schedulerEstimatorTimeout.Duration) + schedulerEstimator := estimatorclient.NewSchedulerEstimator(sched.schedulerEstimatorCache, + options.schedulerEstimatorTimeout.Duration, estimatorclient.Accurate) estimatorclient.RegisterSchedulerEstimator(schedulerEstimator) } sched.enableEmptyWorkloadPropagation = options.enableEmptyWorkloadPropagation diff --git a/vendor/github.com/emirpasic/gods/maps/maps.go b/vendor/github.com/emirpasic/gods/maps/maps.go new file mode 100644 index 000000000000..cdce9f7b1e93 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/maps/maps.go @@ -0,0 +1,40 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package maps provides an abstract Map interface. +// +// In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears just once in the collection. +// +// Operations associated with this data type allow: +// - the addition of a pair to the collection +// - the removal of a pair from the collection +// - the modification of an existing pair +// - the lookup of a value associated with a particular key +// +// Reference: https://en.wikipedia.org/wiki/Associative_array +package maps + +import "github.com/emirpasic/gods/containers" + +// Map interface that all maps implement +type Map interface { + Put(key interface{}, value interface{}) + Get(key interface{}) (value interface{}, found bool) + Remove(key interface{}) + Keys() []interface{} + + containers.Container + // Empty() bool + // Size() int + // Clear() + // Values() []interface{} + // String() string +} + +// BidiMap interface that all bidirectional maps implement (extends the Map interface) +type BidiMap interface { + GetKey(value interface{}) (key interface{}, found bool) + + Map +} diff --git a/vendor/github.com/emirpasic/gods/maps/treemap/enumerable.go b/vendor/github.com/emirpasic/gods/maps/treemap/enumerable.go new file mode 100644 index 000000000000..34b3704d050c --- /dev/null +++ b/vendor/github.com/emirpasic/gods/maps/treemap/enumerable.go @@ -0,0 +1,82 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package treemap + +import ( + "github.com/emirpasic/gods/containers" + rbt "github.com/emirpasic/gods/trees/redblacktree" +) + +// Assert Enumerable implementation +var _ containers.EnumerableWithKey = (*Map)(nil) + +// Each calls the given function once for each element, passing that element's key and value. +func (m *Map) Each(f func(key interface{}, value interface{})) { + iterator := m.Iterator() + for iterator.Next() { + f(iterator.Key(), iterator.Value()) + } +} + +// Map invokes the given function once for each element and returns a container +// containing the values returned by the given function as key/value pairs. +func (m *Map) Map(f func(key1 interface{}, value1 interface{}) (interface{}, interface{})) *Map { + newMap := &Map{tree: rbt.NewWith(m.tree.Comparator)} + iterator := m.Iterator() + for iterator.Next() { + key2, value2 := f(iterator.Key(), iterator.Value()) + newMap.Put(key2, value2) + } + return newMap +} + +// Select returns a new container containing all elements for which the given function returns a true value. +func (m *Map) Select(f func(key interface{}, value interface{}) bool) *Map { + newMap := &Map{tree: rbt.NewWith(m.tree.Comparator)} + iterator := m.Iterator() + for iterator.Next() { + if f(iterator.Key(), iterator.Value()) { + newMap.Put(iterator.Key(), iterator.Value()) + } + } + return newMap +} + +// Any passes each element of the container to the given function and +// returns true if the function ever returns true for any element. +func (m *Map) Any(f func(key interface{}, value interface{}) bool) bool { + iterator := m.Iterator() + for iterator.Next() { + if f(iterator.Key(), iterator.Value()) { + return true + } + } + return false +} + +// All passes each element of the container to the given function and +// returns true if the function returns true for all elements. +func (m *Map) All(f func(key interface{}, value interface{}) bool) bool { + iterator := m.Iterator() + for iterator.Next() { + if !f(iterator.Key(), iterator.Value()) { + return false + } + } + return true +} + +// Find passes each element of the container to the given function and returns +// the first (key,value) for which the function is true or nil,nil otherwise if no element +// matches the criteria. +func (m *Map) Find(f func(key interface{}, value interface{}) bool) (interface{}, interface{}) { + iterator := m.Iterator() + for iterator.Next() { + if f(iterator.Key(), iterator.Value()) { + return iterator.Key(), iterator.Value() + } + } + return nil, nil +} diff --git a/vendor/github.com/emirpasic/gods/maps/treemap/iterator.go b/vendor/github.com/emirpasic/gods/maps/treemap/iterator.go new file mode 100644 index 000000000000..becb56dbe467 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/maps/treemap/iterator.go @@ -0,0 +1,104 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package treemap + +import ( + "github.com/emirpasic/gods/containers" + rbt "github.com/emirpasic/gods/trees/redblacktree" +) + +// Assert Iterator implementation +var _ containers.ReverseIteratorWithKey = (*Iterator)(nil) + +// Iterator holding the iterator's state +type Iterator struct { + iterator rbt.Iterator +} + +// Iterator returns a stateful iterator whose elements are key/value pairs. +func (m *Map) Iterator() Iterator { + return Iterator{iterator: m.tree.Iterator()} +} + +// Next moves the iterator to the next element and returns true if there was a next element in the container. +// If Next() returns true, then next element's key and value can be retrieved by Key() and Value(). +// If Next() was called for the first time, then it will point the iterator to the first element if it exists. +// Modifies the state of the iterator. +func (iterator *Iterator) Next() bool { + return iterator.iterator.Next() +} + +// Prev moves the iterator to the previous element and returns true if there was a previous element in the container. +// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Prev() bool { + return iterator.iterator.Prev() +} + +// Value returns the current element's value. +// Does not modify the state of the iterator. +func (iterator *Iterator) Value() interface{} { + return iterator.iterator.Value() +} + +// Key returns the current element's key. +// Does not modify the state of the iterator. +func (iterator *Iterator) Key() interface{} { + return iterator.iterator.Key() +} + +// Begin resets the iterator to its initial state (one-before-first) +// Call Next() to fetch the first element if any. +func (iterator *Iterator) Begin() { + iterator.iterator.Begin() +} + +// End moves the iterator past the last element (one-past-the-end). +// Call Prev() to fetch the last element if any. +func (iterator *Iterator) End() { + iterator.iterator.End() +} + +// First moves the iterator to the first element and returns true if there was a first element in the container. +// If First() returns true, then first element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator +func (iterator *Iterator) First() bool { + return iterator.iterator.First() +} + +// Last moves the iterator to the last element and returns true if there was a last element in the container. +// If Last() returns true, then last element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Last() bool { + return iterator.iterator.Last() +} + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// passed function, and returns true if there was a next element in the container. +// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// passed function, and returns true if there was a next element in the container. +// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/vendor/github.com/emirpasic/gods/maps/treemap/serialization.go b/vendor/github.com/emirpasic/gods/maps/treemap/serialization.go new file mode 100644 index 000000000000..415a77ddd948 --- /dev/null +++ b/vendor/github.com/emirpasic/gods/maps/treemap/serialization.go @@ -0,0 +1,33 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package treemap + +import ( + "github.com/emirpasic/gods/containers" +) + +// Assert Serialization implementation +var _ containers.JSONSerializer = (*Map)(nil) +var _ containers.JSONDeserializer = (*Map)(nil) + +// ToJSON outputs the JSON representation of the map. +func (m *Map) ToJSON() ([]byte, error) { + return m.tree.ToJSON() +} + +// FromJSON populates the map from the input JSON representation. +func (m *Map) FromJSON(data []byte) error { + return m.tree.FromJSON(data) +} + +// UnmarshalJSON @implements json.Unmarshaler +func (m *Map) UnmarshalJSON(bytes []byte) error { + return m.FromJSON(bytes) +} + +// MarshalJSON @implements json.Marshaler +func (m *Map) MarshalJSON() ([]byte, error) { + return m.ToJSON() +} diff --git a/vendor/github.com/emirpasic/gods/maps/treemap/treemap.go b/vendor/github.com/emirpasic/gods/maps/treemap/treemap.go new file mode 100644 index 000000000000..a77d16d8541f --- /dev/null +++ b/vendor/github.com/emirpasic/gods/maps/treemap/treemap.go @@ -0,0 +1,150 @@ +// Copyright (c) 2015, Emir Pasic. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package treemap implements a map backed by red-black tree. +// +// Elements are ordered by key in the map. +// +// Structure is not thread safe. +// +// Reference: http://en.wikipedia.org/wiki/Associative_array +package treemap + +import ( + "fmt" + "github.com/emirpasic/gods/maps" + rbt "github.com/emirpasic/gods/trees/redblacktree" + "github.com/emirpasic/gods/utils" + "strings" +) + +// Assert Map implementation +var _ maps.Map = (*Map)(nil) + +// Map holds the elements in a red-black tree +type Map struct { + tree *rbt.Tree +} + +// NewWith instantiates a tree map with the custom comparator. +func NewWith(comparator utils.Comparator) *Map { + return &Map{tree: rbt.NewWith(comparator)} +} + +// NewWithIntComparator instantiates a tree map with the IntComparator, i.e. keys are of type int. +func NewWithIntComparator() *Map { + return &Map{tree: rbt.NewWithIntComparator()} +} + +// NewWithStringComparator instantiates a tree map with the StringComparator, i.e. keys are of type string. +func NewWithStringComparator() *Map { + return &Map{tree: rbt.NewWithStringComparator()} +} + +// Put inserts key-value pair into the map. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (m *Map) Put(key interface{}, value interface{}) { + m.tree.Put(key, value) +} + +// Get searches the element in the map by key and returns its value or nil if key is not found in tree. +// Second return parameter is true if key was found, otherwise false. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (m *Map) Get(key interface{}) (value interface{}, found bool) { + return m.tree.Get(key) +} + +// Remove removes the element from the map by key. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (m *Map) Remove(key interface{}) { + m.tree.Remove(key) +} + +// Empty returns true if map does not contain any elements +func (m *Map) Empty() bool { + return m.tree.Empty() +} + +// Size returns number of elements in the map. +func (m *Map) Size() int { + return m.tree.Size() +} + +// Keys returns all keys in-order +func (m *Map) Keys() []interface{} { + return m.tree.Keys() +} + +// Values returns all values in-order based on the key. +func (m *Map) Values() []interface{} { + return m.tree.Values() +} + +// Clear removes all elements from the map. +func (m *Map) Clear() { + m.tree.Clear() +} + +// Min returns the minimum key and its value from the tree map. +// Returns nil, nil if map is empty. +func (m *Map) Min() (key interface{}, value interface{}) { + if node := m.tree.Left(); node != nil { + return node.Key, node.Value + } + return nil, nil +} + +// Max returns the maximum key and its value from the tree map. +// Returns nil, nil if map is empty. +func (m *Map) Max() (key interface{}, value interface{}) { + if node := m.tree.Right(); node != nil { + return node.Key, node.Value + } + return nil, nil +} + +// Floor finds the floor key-value pair for the input key. +// In case that no floor is found, then both returned values will be nil. +// It's generally enough to check the first value (key) for nil, which determines if floor was found. +// +// Floor key is defined as the largest key that is smaller than or equal to the given key. +// A floor key may not be found, either because the map is empty, or because +// all keys in the map are larger than the given key. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (m *Map) Floor(key interface{}) (foundKey interface{}, foundValue interface{}) { + node, found := m.tree.Floor(key) + if found { + return node.Key, node.Value + } + return nil, nil +} + +// Ceiling finds the ceiling key-value pair for the input key. +// In case that no ceiling is found, then both returned values will be nil. +// It's generally enough to check the first value (key) for nil, which determines if ceiling was found. +// +// Ceiling key is defined as the smallest key that is larger than or equal to the given key. +// A ceiling key may not be found, either because the map is empty, or because +// all keys in the map are smaller than the given key. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (m *Map) Ceiling(key interface{}) (foundKey interface{}, foundValue interface{}) { + node, found := m.tree.Ceiling(key) + if found { + return node.Key, node.Value + } + return nil, nil +} + +// String returns a string representation of container +func (m *Map) String() string { + str := "TreeMap\nmap[" + it := m.Iterator() + for it.Next() { + str += fmt.Sprintf("%v:%v ", it.Key(), it.Value()) + } + return strings.TrimRight(str, " ") + "]" + +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d6a2d48e96cc..8f990e76dcc1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -68,6 +68,8 @@ github.com/emicklei/go-restful/v3/log # github.com/emirpasic/gods v1.18.1 ## explicit; go 1.2 github.com/emirpasic/gods/containers +github.com/emirpasic/gods/maps +github.com/emirpasic/gods/maps/treemap github.com/emirpasic/gods/trees github.com/emirpasic/gods/trees/redblacktree github.com/emirpasic/gods/utils