Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cluster-autoscaler/core/scaledown/eligibility/eligibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (c *Checker) unremovableReasonAndNodeUtilization(autoscalingCtx *ca_context
}
}

underutilized, err := c.isNodeBelowUtilizationThreshold(autoscalingCtx, node, nodeGroup, utilInfo)
underutilized, err := c.isNodeAtOrBelowUtilizationThreshold(autoscalingCtx, node, nodeGroup, utilInfo)
if err != nil {
klog.Warningf("Failed to check utilization thresholds for %s: %v", node.Name, err)
return simulator.UnexpectedError, nil
Expand All @@ -169,8 +169,8 @@ func (c *Checker) unremovableReasonAndNodeUtilization(autoscalingCtx *ca_context
return simulator.NoReason, &utilInfo
}

// isNodeBelowUtilizationThreshold determines if a given node utilization is below threshold.
func (c *Checker) isNodeBelowUtilizationThreshold(autoscalingCtx *ca_context.AutoscalingContext, node *apiv1.Node, nodeGroup cloudprovider.NodeGroup, utilInfo utilization.Info) (bool, error) {
// isNodeAtOrBelowUtilizationThreshold determines if a given node utilization is at or below threshold.
func (c *Checker) isNodeAtOrBelowUtilizationThreshold(autoscalingCtx *ca_context.AutoscalingContext, node *apiv1.Node, nodeGroup cloudprovider.NodeGroup, utilInfo utilization.Info) (bool, error) {
var threshold float64
var err error
gpuConfig := autoscalingCtx.CloudProvider.GetNodeGpuConfig(node)
Expand All @@ -185,7 +185,7 @@ func (c *Checker) isNodeBelowUtilizationThreshold(autoscalingCtx *ca_context.Aut
return false, err
}
}
if utilInfo.Utilization >= threshold {
if utilInfo.Utilization > threshold {
return false, nil
}
return true, nil
Expand Down
57 changes: 38 additions & 19 deletions cluster-autoscaler/core/scaledown/eligibility/eligibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ import (
)

type testCase struct {
desc string
nodes []*apiv1.Node
pods []*apiv1.Pod
draSnapshot *drasnapshot.Snapshot
draEnabled bool
wantUnneeded []string
wantUnremovable []*simulator.UnremovableNode
scaleDownUnready bool
ignoreDaemonSetsUtilization bool
desc string
nodes []*apiv1.Node
pods []*apiv1.Pod
draSnapshot *drasnapshot.Snapshot
draEnabled bool
wantUnneeded []string
wantUnremovable []*simulator.UnremovableNode
scaleDownUnready bool
ignoreDaemonSetsUtilization bool
scaleDownUtilizationThreshold *float64
}

func getTestCases(ignoreDaemonSetsUtilization bool, suffix string, now time.Time) []testCase {
Expand Down Expand Up @@ -180,15 +181,16 @@ func getTestCases(ignoreDaemonSetsUtilization bool, suffix string, now time.Time
}

if ignoreDaemonSetsUtilization {
finalTestCases = append(testCases, testCase{
desc: "high utilization daemonsets node is filtered out",
nodes: []*apiv1.Node{regularNode},
pods: []*apiv1.Pod{smallPod, dsPod},
wantUnneeded: []string{},
wantUnremovable: []*simulator.UnremovableNode{{Node: regularNode, Reason: simulator.NotUnderutilized}},
scaleDownUnready: true,
ignoreDaemonSetsUtilization: false,
},
finalTestCases = append(testCases,
testCase{
desc: "high utilization daemonsets node is filtered out",
nodes: []*apiv1.Node{regularNode},
pods: []*apiv1.Pod{smallPod, dsPod},
wantUnneeded: []string{},
wantUnremovable: []*simulator.UnremovableNode{{Node: regularNode, Reason: simulator.NotUnderutilized}},
scaleDownUnready: true,
ignoreDaemonSetsUtilization: false,
},
testCase{
desc: "high utilization daemonsets node stays",
nodes: []*apiv1.Node{regularNode},
Expand All @@ -197,6 +199,19 @@ func getTestCases(ignoreDaemonSetsUtilization bool, suffix string, now time.Time
wantUnremovable: []*simulator.UnremovableNode{},
scaleDownUnready: true,
ignoreDaemonSetsUtilization: true,
},
testCase{
desc: "only daemonsets pods on this nodes",
nodes: []*apiv1.Node{regularNode},
pods: []*apiv1.Pod{dsPod},
wantUnneeded: []string{"regular"},
wantUnremovable: []*simulator.UnremovableNode{},
scaleDownUnready: true,
ignoreDaemonSetsUtilization: true,
scaleDownUtilizationThreshold: func() *float64 {
threshold := float64(0)
return &threshold
}(),
})
}

Expand All @@ -210,12 +225,16 @@ func TestFilterOutUnremovable(t *testing.T) {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
utilizationThreshold := config.DefaultScaleDownUtilizationThreshold
if tc.scaleDownUtilizationThreshold != nil {
utilizationThreshold = *tc.scaleDownUtilizationThreshold
}
options := config.AutoscalingOptions{
DynamicResourceAllocationEnabled: tc.draEnabled,
UnremovableNodeRecheckTimeout: 5 * time.Minute,
ScaleDownUnreadyEnabled: tc.scaleDownUnready,
NodeGroupDefaults: config.NodeGroupAutoscalingOptions{
ScaleDownUtilizationThreshold: config.DefaultScaleDownUtilizationThreshold,
ScaleDownUtilizationThreshold: utilizationThreshold,
ScaleDownGpuUtilizationThreshold: config.DefaultScaleDownGpuUtilizationThreshold,
ScaleDownUnneededTime: config.DefaultScaleDownUnneededTime,
ScaleDownUnreadyTime: config.DefaultScaleDownUnreadyTime,
Expand Down