diff --git a/cluster-autoscaler/core/static_autoscaler.go b/cluster-autoscaler/core/static_autoscaler.go index c6fa9342b219..949649fadd76 100644 --- a/cluster-autoscaler/core/static_autoscaler.go +++ b/cluster-autoscaler/core/static_autoscaler.go @@ -23,7 +23,6 @@ import ( apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/uuid" schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" @@ -42,6 +41,7 @@ import ( "k8s.io/autoscaler/cluster-autoscaler/utils/deletetaint" "k8s.io/autoscaler/cluster-autoscaler/utils/errors" "k8s.io/autoscaler/cluster-autoscaler/utils/gpu" + scheduler_utils "k8s.io/autoscaler/cluster-autoscaler/utils/scheduler" "k8s.io/autoscaler/cluster-autoscaler/utils/taints" "k8s.io/autoscaler/cluster-autoscaler/utils/tpu" @@ -762,21 +762,6 @@ func allPodsAreNew(pods []*apiv1.Pod, currentTime time.Time) bool { return found && oldest.Add(unschedulablePodWithGpuTimeBuffer).After(currentTime) } -func deepCopyNodeInfo(nodeTemplate *schedulernodeinfo.NodeInfo, index int) *schedulernodeinfo.NodeInfo { - node := nodeTemplate.Node().DeepCopy() - node.Name = fmt.Sprintf("%s-%d", node.Name, index) - node.UID = uuid.NewUUID() - nodeInfo := schedulernodeinfo.NewNodeInfo() - nodeInfo.SetNode(node) - for _, podTemplate := range nodeTemplate.Pods() { - pod := podTemplate.DeepCopy() - pod.Name = fmt.Sprintf("%s-%d", podTemplate.Name, index) - pod.UID = uuid.NewUUID() - nodeInfo.AddPod(pod) - } - return nodeInfo -} - func getUpcomingNodeInfos(registry *clusterstate.ClusterStateRegistry, nodeInfos map[string]*schedulernodeinfo.NodeInfo) []*schedulernodeinfo.NodeInfo { upcomingNodes := make([]*schedulernodeinfo.NodeInfo, 0) for nodeGroup, numberOfNodes := range registry.GetUpcomingNodes() { @@ -789,7 +774,7 @@ func getUpcomingNodeInfos(registry *clusterstate.ClusterStateRegistry, nodeInfos // Ensure new nodes have different names because nodeName // will be used as a map key. Also deep copy pods (daemonsets & // any pods added by cloud provider on template). - upcomingNodes = append(upcomingNodes, deepCopyNodeInfo(nodeTemplate, i)) + upcomingNodes = append(upcomingNodes, scheduler_utils.DeepCopyTemplateNode(nodeTemplate, i)) } } return upcomingNodes diff --git a/cluster-autoscaler/estimator/binpacking_estimator.go b/cluster-autoscaler/estimator/binpacking_estimator.go index 111e6bf5dbd8..03cbf9fa0391 100644 --- a/cluster-autoscaler/estimator/binpacking_estimator.go +++ b/cluster-autoscaler/estimator/binpacking_estimator.go @@ -17,15 +17,14 @@ limitations under the License. package estimator import ( - "fmt" "sort" - "time" apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/autoscaler/cluster-autoscaler/simulator" "k8s.io/klog" schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" + "k8s.io/autoscaler/cluster-autoscaler/utils/scheduler" ) // podInfo contains Pod and score that corresponds to how important it is to handle the pod first. @@ -75,7 +74,6 @@ func (estimator *BinpackingNodeEstimator) Estimate( } }() - newNodeNameTimestamp := time.Now() newNodeNameIndex := 0 for _, podInfo := range podInfos { @@ -92,7 +90,7 @@ func (estimator *BinpackingNodeEstimator) Estimate( } if !found { // Add new node - newNodeName, err := estimator.addNewNodeToSnapshot(nodeTemplate, newNodeNameTimestamp, newNodeNameIndex) + newNodeName, err := estimator.addNewNodeToSnapshot(nodeTemplate, newNodeNameIndex) if err != nil { klog.Errorf("Error while adding new node for template to ClusterSnapshot; %v", err) return 0 @@ -111,19 +109,17 @@ func (estimator *BinpackingNodeEstimator) Estimate( func (estimator *BinpackingNodeEstimator) addNewNodeToSnapshot( template *schedulernodeinfo.NodeInfo, - nameTimestamp time.Time, nameIndex int) (string, error) { - newNode := template.Node().DeepCopy() - newNode.Name = fmt.Sprintf("%s-%d-%d", newNode.Name, nameTimestamp.Unix(), nameIndex) - if newNode.Labels == nil { - newNode.Labels = make(map[string]string) + newNodeInfo := scheduler.DeepCopyTemplateNode(template, nameIndex) + var pods []*apiv1.Pod + for _, podTemplate := range newNodeInfo.Pods() { + pods = append(pods, podTemplate) } - newNode.Labels["kubernetes.io/hostname"] = newNode.Name - if err := estimator.clusterSnapshot.AddNodeWithPods(newNode, template.Pods()); err != nil { + if err := estimator.clusterSnapshot.AddNodeWithPods(newNodeInfo.Node(), pods); err != nil { return "", err } - return newNode.Name, nil + return newNodeInfo.Node().Name, nil } // Calculates score for all pods and returns podInfo structure. diff --git a/cluster-autoscaler/utils/scheduler/scheduler.go b/cluster-autoscaler/utils/scheduler/scheduler.go index b44fbccc3eb7..8855d37d7d2c 100644 --- a/cluster-autoscaler/utils/scheduler/scheduler.go +++ b/cluster-autoscaler/utils/scheduler/scheduler.go @@ -17,8 +17,11 @@ limitations under the License. package scheduler import ( + "fmt" + apiv1 "k8s.io/api/core/v1" schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" + "k8s.io/apimachinery/pkg/util/uuid" ) // CreateNodeNameToInfoMap obtains a list of pods and pivots that list into a map where the keys are node names @@ -57,3 +60,25 @@ func CreateNodeNameToInfoMap(pods []*apiv1.Pod, nodes []*apiv1.Node) map[string] return nodeNameToNodeInfo } + +// DeepCopyTemplateNode copies NodeInfo object used as a template. It changes +// names of UIDs of both node and pods running on it, so that copies can be used +// to represent multiple nodes. +func DeepCopyTemplateNode(nodeTemplate *schedulernodeinfo.NodeInfo, index int) *schedulernodeinfo.NodeInfo { + node := nodeTemplate.Node().DeepCopy() + node.Name = fmt.Sprintf("%s-%d", node.Name, index) + node.UID = uuid.NewUUID() + if node.Labels == nil { + node.Labels = make(map[string]string) + } + node.Labels["kubernetes.io/hostname"] = node.Name + nodeInfo := schedulernodeinfo.NewNodeInfo() + nodeInfo.SetNode(node) + for _, podTemplate := range nodeTemplate.Pods() { + pod := podTemplate.DeepCopy() + pod.Name = fmt.Sprintf("%s-%d", podTemplate.Name, index) + pod.UID = uuid.NewUUID() + nodeInfo.AddPod(pod) + } + return nodeInfo +}