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
25 changes: 23 additions & 2 deletions pkg/cloudprovider/cloudprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"log"
"slices"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -611,7 +612,8 @@ func filterCompatibleInstanceTypes(instanceTypes []*ClusterAPIInstanceType, node
filteredInstances := lo.Filter(instanceTypes, func(i *ClusterAPIInstanceType, _ int) bool {
// TODO (elmiko) if/when we have offering availability, this is a good place to filter out unavailable instance types
return reqs.Compatible(i.Requirements, scheduling.AllowUndefinedWellKnownLabels) == nil &&
resources.Fits(nodeClaim.Spec.Resources.Requests, i.Allocatable())
resources.Fits(nodeClaim.Spec.Resources.Requests, i.Allocatable()) &&
i.Offerings.Available().HasCompatible(reqs)
})

return filteredInstances
Expand All @@ -631,6 +633,25 @@ func labelsFromScaleFromZeroAnnotation(annotation string) map[string]string {
return labels
}

// isBelowMaxSize checks if the MachineDeployment's current replicas are below the max size
// defined by the Cluster Autoscaler annotation. If the annotation is not present or cannot
// be parsed, it returns true (no limit enforced).
func isBelowMaxSize(machineDeployment *capiv1beta1.MachineDeployment) bool {
if machineDeployment == nil {
return false
}
maxSizeStr, ok := machineDeployment.Annotations[capiv1beta1.AutoscalerMaxSizeAnnotation]
if !ok {
return true
}
maxSize, err := strconv.Atoi(maxSizeStr)
if err != nil {
return true
}
replicas := int(ptr.Deref(machineDeployment.Spec.Replicas, 0))
return replicas < maxSize
}

func machineDeploymentToInstanceType(machineDeployment *capiv1beta1.MachineDeployment) *ClusterAPIInstanceType {
instanceType := &ClusterAPIInstanceType{}

Expand All @@ -657,7 +678,7 @@ func machineDeploymentToInstanceType(machineDeployment *capiv1beta1.MachineDeplo
&cloudprovider.Offering{
Requirements: scheduling.NewRequirements(requirements...),
Price: 0.0,
Available: true,
Available: isBelowMaxSize(machineDeployment),
},
}

Expand Down
90 changes: 90 additions & 0 deletions pkg/cloudprovider/cloudprovider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,96 @@ var _ = Describe("machineDeploymentToInstanceType function", func() {
})
})

var _ = Describe("isBelowMaxSize function", func() {
It("returns false when MachineDeployment is nil", func() {
Expect(isBelowMaxSize(nil)).To(BeFalse())
})

It("returns true when no max size annotation is present", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(5))
Expect(isBelowMaxSize(md)).To(BeTrue())
})

It("returns true when replicas are below max size", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(3))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
Expect(isBelowMaxSize(md)).To(BeTrue())
})

It("returns false when replicas equal max size", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(10))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
Expect(isBelowMaxSize(md)).To(BeFalse())
})

It("returns false when replicas exceed max size", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(12))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
Expect(isBelowMaxSize(md)).To(BeFalse())
})

It("returns true when max size annotation is not a valid integer", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(5))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "not-a-number",
}
Expect(isBelowMaxSize(md)).To(BeTrue())
})

It("returns true when replicas is nil (defaults to 0)", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = nil
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
Expect(isBelowMaxSize(md)).To(BeTrue())
})

It("returns false when max size is 0", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(0))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "0",
}
Expect(isBelowMaxSize(md)).To(BeFalse())
})
})

var _ = Describe("machineDeploymentToInstanceType max size behavior", func() {
It("sets offering Available to false when replicas are at max size", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(10))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
instanceType := machineDeploymentToInstanceType(md)
Expect(instanceType.Offerings).To(HaveLen(1))
Expect(instanceType.Offerings[0]).To(HaveField("Available", false))
})

It("sets offering Available to true when replicas are below max size", func() {
md := newMachineDeployment("md-1", "test-cluster", true)
md.Spec.Replicas = ptr.To(int32(3))
md.Annotations = map[string]string{
capiv1beta1.AutoscalerMaxSizeAnnotation: "10",
}
instanceType := machineDeploymentToInstanceType(md)
Expect(instanceType.Offerings).To(HaveLen(1))
Expect(instanceType.Offerings[0]).To(HaveField("Available", true))
})
})

func newMachine(machineName string, clusterName string, karpenterMember bool) *capiv1beta1.Machine {
machine := &capiv1beta1.Machine{}
machine.SetName(machineName)
Expand Down
Loading