From b87fd2c9c388f67c41072a096f01a177dbc21765 Mon Sep 17 00:00:00 2001 From: elmiko Date: Wed, 22 Oct 2025 11:06:49 -0400 Subject: [PATCH 1/2] update createSanitizedNode to set unschedulable This change ensures that a sanitized node has its .spec.unschedulable field set to false. --- cluster-autoscaler/simulator/node_info_utils.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster-autoscaler/simulator/node_info_utils.go b/cluster-autoscaler/simulator/node_info_utils.go index ffa8f33be53a..a842654de0fc 100644 --- a/cluster-autoscaler/simulator/node_info_utils.go +++ b/cluster-autoscaler/simulator/node_info_utils.go @@ -115,6 +115,8 @@ func createSanitizedNode(node *apiv1.Node, newName string, taintConfig *taints.T } newNode.Labels[apiv1.LabelHostname] = newName + newNode.Spec.Unschedulable = false + if taintConfig != nil { newNode.Spec.Taints = taints.SanitizeTaints(newNode.Spec.Taints, *taintConfig) } From 5244a8f25cf29bda758c0f9b30daaa827e390135 Mon Sep 17 00:00:00 2001 From: elmiko Date: Wed, 22 Oct 2025 11:28:09 -0400 Subject: [PATCH 2/2] pass allNodes to node info provider Process This change passes all the nodes to the mixed node info provider processor that is called from `RunOnce`. The change is to allow unschedulable and unready nodes to be processed as bad canidates during the node info template generation. The Process function has been updated to separate nodes into good and bad candidates to make the filtering match the original intent. --- cluster-autoscaler/core/static_autoscaler.go | 2 +- .../mixed_nodeinfos_processor.go | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cluster-autoscaler/core/static_autoscaler.go b/cluster-autoscaler/core/static_autoscaler.go index 86d4ab9826a4..9b089a319c71 100644 --- a/cluster-autoscaler/core/static_autoscaler.go +++ b/cluster-autoscaler/core/static_autoscaler.go @@ -348,7 +348,7 @@ func (a *StaticAutoscaler) RunOnce(currentTime time.Time) caerrors.AutoscalerErr return typedErr.AddPrefix("failed to initialize RemainingPdbTracker: ") } - nodeInfosForGroups, autoscalerError := a.processors.TemplateNodeInfoProvider.Process(autoscalingCtx, readyNodes, daemonsets, a.taintConfig, currentTime) + nodeInfosForGroups, autoscalerError := a.processors.TemplateNodeInfoProvider.Process(autoscalingCtx, allNodes, daemonsets, a.taintConfig, currentTime) if autoscalerError != nil { klog.Errorf("Failed to get node infos for groups: %v", autoscalerError) return autoscalerError.AddPrefix("failed to build node infos for node groups: ") diff --git a/cluster-autoscaler/processors/nodeinfosprovider/mixed_nodeinfos_processor.go b/cluster-autoscaler/processors/nodeinfosprovider/mixed_nodeinfos_processor.go index 4b297372e7b1..304a05039e39 100644 --- a/cluster-autoscaler/processors/nodeinfosprovider/mixed_nodeinfos_processor.go +++ b/cluster-autoscaler/processors/nodeinfosprovider/mixed_nodeinfos_processor.go @@ -78,6 +78,18 @@ func (p *MixedTemplateNodeInfoProvider) Process(autoscalingCtx *ca_context.Autos result := make(map[string]*framework.NodeInfo) seenGroups := make(map[string]bool) + // sort nodes into those good and bad candidates for templates. the bad candidates will be processed + // at the end of this function as a last resort for a node info template. + goodCandidates := make([]*apiv1.Node, 0) + badCandidates := make([]*apiv1.Node, 0) + for _, node := range nodes { + if isNodeGoodTemplateCandidate(node, now) { + goodCandidates = append(goodCandidates, node) + } else { + badCandidates = append(badCandidates, node) + } + } + // processNode returns information whether the nodeTemplate was generated and if there was an error. processNode := func(node *apiv1.Node) (bool, string, caerror.AutoscalerError) { nodeGroup, err := autoscalingCtx.CloudProvider.NodeGroupForNode(node) @@ -103,11 +115,7 @@ func (p *MixedTemplateNodeInfoProvider) Process(autoscalingCtx *ca_context.Autos return false, "", nil } - for _, node := range nodes { - // Broken nodes might have some stuff missing. Skipping. - if !isNodeGoodTemplateCandidate(node, now) { - continue - } + for _, node := range goodCandidates { added, id, typedErr := processNode(node) if typedErr != nil { return map[string]*framework.NodeInfo{}, typedErr @@ -156,11 +164,7 @@ func (p *MixedTemplateNodeInfoProvider) Process(autoscalingCtx *ca_context.Autos } // Last resort - unready/unschedulable nodes. - for _, node := range nodes { - // Allowing broken nodes - if isNodeGoodTemplateCandidate(node, now) { - continue - } + for _, node := range badCandidates { added, _, typedErr := processNode(node) if typedErr != nil { return map[string]*framework.NodeInfo{}, typedErr