82 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			82 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2022 The Kubernetes Authors.
 | |
| 
 | |
| Licensed 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 estimator
 | |
| 
 | |
| import (
 | |
| 	"time"
 | |
| 
 | |
| 	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
 | |
| 	klog "k8s.io/klog/v2"
 | |
| )
 | |
| 
 | |
| type thresholdBasedEstimationLimiter struct {
 | |
| 	maxDuration time.Duration
 | |
| 	maxNodes    int
 | |
| 	nodes       int
 | |
| 	start       time.Time
 | |
| 	thresholds  []Threshold
 | |
| }
 | |
| 
 | |
| func (tbel *thresholdBasedEstimationLimiter) StartEstimation(_ []PodEquivalenceGroup, nodeGroup cloudprovider.NodeGroup, context EstimationContext) {
 | |
| 	tbel.start = time.Now()
 | |
| 	tbel.nodes = 0
 | |
| 	tbel.maxNodes = 0
 | |
| 	tbel.maxDuration = time.Duration(0)
 | |
| 	for _, threshold := range tbel.thresholds {
 | |
| 		tbel.maxNodes = getMinLimit(tbel.maxNodes, threshold.NodeLimit(nodeGroup, context))
 | |
| 		tbel.maxDuration = getMinLimit(tbel.maxDuration, threshold.DurationLimit(nodeGroup, context))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getMinLimit[V int | time.Duration](baseLimit V, targetLimit V) V {
 | |
| 	if baseLimit < 0 || targetLimit < 0 {
 | |
| 		return -1
 | |
| 	}
 | |
| 	if (baseLimit == 0 || baseLimit > targetLimit) && targetLimit > 0 {
 | |
| 		return targetLimit
 | |
| 	}
 | |
| 	return baseLimit
 | |
| }
 | |
| 
 | |
| func (*thresholdBasedEstimationLimiter) EndEstimation() {}
 | |
| 
 | |
| func (tbel *thresholdBasedEstimationLimiter) PermissionToAddNode() bool {
 | |
| 	if tbel.maxNodes < 0 || (tbel.maxNodes > 0 && tbel.nodes >= tbel.maxNodes) {
 | |
| 		klog.V(4).Infof("Capping binpacking after exceeding threshold of %d nodes", tbel.maxNodes)
 | |
| 		return false
 | |
| 	}
 | |
| 	timeDefined := tbel.maxDuration > 0 && tbel.start != time.Time{}
 | |
| 	if tbel.maxDuration < 0 || (timeDefined && time.Now().After(tbel.start.Add(tbel.maxDuration))) {
 | |
| 		klog.V(4).Infof("Capping binpacking after exceeding max duration of %v", tbel.maxDuration)
 | |
| 		return false
 | |
| 	}
 | |
| 	tbel.nodes++
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // NewThresholdBasedEstimationLimiter returns an EstimationLimiter that will prevent estimation
 | |
| // after either a node count of time-based threshold is reached. This is meant to prevent cases
 | |
| // where binpacking of hundreds or thousands of nodes takes extremely long time rendering CA
 | |
| // incredibly slow or even completely crashing it.
 | |
| // Thresholds may return:
 | |
| //   - negative value: no new nodes are allowed to be added if at least one threshold returns negative limit
 | |
| //   - 0: no limit, thresholds with no limits will be ignored in favor of thresholds with positive or negative limits
 | |
| //   - positive value: new nodes can be added and this value represents the limit
 | |
| func NewThresholdBasedEstimationLimiter(thresholds []Threshold) EstimationLimiter {
 | |
| 	return &thresholdBasedEstimationLimiter{thresholds: thresholds}
 | |
| }
 |