Skip to content

Commit afe3eba

Browse files
authored
fix: ensure filtered offering is available for capacity blocks (#8518)
1 parent 781485b commit afe3eba

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

pkg/providers/instance/filter/filter.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ type capacityBlockFilter struct {
170170
requirements scheduling.Requirements
171171
}
172172

173+
//nolint:gocyclo
173174
func (f capacityBlockFilter) FilterReject(instanceTypes []*cloudprovider.InstanceType) ([]*cloudprovider.InstanceType, []*cloudprovider.InstanceType) {
174175
if !f.shouldFilter(instanceTypes) {
175176
return instanceTypes, nil
@@ -181,6 +182,9 @@ func (f capacityBlockFilter) FilterReject(instanceTypes []*cloudprovider.Instanc
181182
if o.CapacityType() != karpv1.CapacityTypeReserved {
182183
continue
183184
}
185+
if !o.Available || !f.requirements.IsCompatible(o.Requirements, scheduling.AllowUndefinedWellKnownLabels) {
186+
continue
187+
}
184188
if o.Requirements.Get(v1.LabelCapacityReservationType).Any() != string(v1.CapacityReservationTypeCapacityBlock) {
185189
continue
186190
}

pkg/providers/instance/filter/filter_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package filter_test
1717
import (
1818
"context"
1919
"fmt"
20+
"math/rand/v2"
2021
"testing"
2122

2223
"github.com/awslabs/operatorpkg/option"
@@ -274,7 +275,40 @@ var _ = Describe("InstanceFiltersTest", func() {
274275
})
275276
expectInstanceTypes(kept, "cheap-instance")
276277
expectInstanceTypes(rejected, "expensive-instance")
278+
Expect(kept[0].Offerings).To(HaveLen(1))
277279
})
280+
DescribeTable(
281+
"OfferingSelection",
282+
func(expectedReservationID string, offerings ...*cloudprovider.Offering) {
283+
f := filter.CapacityBlockFilter(
284+
scheduling.NewRequirements(scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpExists),
285+
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpNotIn, "forbidden-zone"),
286+
))
287+
kept, _ := f.FilterReject([]*cloudprovider.InstanceType{makeInstanceType("instance", withOfferings(offerings...))})
288+
Expect(kept[0].Offerings).To(HaveLen(1))
289+
Expect(kept[0].Offerings[0].ReservationID()).To(Equal(expectedReservationID))
290+
},
291+
Entry(
292+
"should select the cheapest offering",
293+
"cheapest",
294+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(1.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("cheapest")),
295+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(2.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("expensive")),
296+
),
297+
Entry(
298+
"should not select unavailable offerings",
299+
"cheapest-available",
300+
makeOffering(karpv1.CapacityTypeReserved, false, withPrice(1.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("cheapest")),
301+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(1.5), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("cheapest-available")),
302+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(2.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("expensive")),
303+
),
304+
Entry(
305+
"should not select incompatible offerings",
306+
"cheapest-compatible",
307+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(1.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withZone("forbidden-zone"), withReservationID("cheapest")),
308+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(1.5), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("cheapest-compatible")),
309+
makeOffering(karpv1.CapacityTypeReserved, true, withPrice(2.0), withCapacityReservationType(v1.CapacityReservationTypeCapacityBlock), withReservationID("expensive")),
310+
),
311+
)
278312
DescribeTable(
279313
"shouldn't filter instance types when the capacity reservation type is not capacity-block",
280314
func(crt v1.CapacityReservationType) {
@@ -291,6 +325,9 @@ var _ = Describe("InstanceFiltersTest", func() {
291325
})
292326
expectInstanceTypes(kept, "cheap-instance", "expensive-instance")
293327
Expect(rejected).To(BeEmpty())
328+
for _, it := range kept {
329+
Expect(it.Offerings).To(HaveLen(2))
330+
}
294331
},
295332
lo.FilterMap(v1.CapacityReservationType("").Values(), func(crt v1.CapacityReservationType, _ int) (TableEntry, bool) {
296333
return Entry(fmt.Sprintf("when the capacity reservation type is %q", string(crt)), crt), crt != v1.CapacityReservationTypeCapacityBlock
@@ -310,6 +347,9 @@ var _ = Describe("InstanceFiltersTest", func() {
310347
})
311348
expectInstanceTypes(kept, "cheap-instance", "expensive-instance")
312349
Expect(rejected).To(BeEmpty())
350+
for _, it := range kept {
351+
Expect(it.Offerings).To(HaveLen(2))
352+
}
313353
})
314354
})
315355

@@ -666,6 +706,9 @@ func withOfferings(offerings ...*cloudprovider.Offering) mockInstanceTypeOptions
666706

667707
func makeInstanceType(name string, opts ...mockInstanceTypeOptions) *cloudprovider.InstanceType {
668708
instanceType := option.Resolve(opts...)
709+
rand.Shuffle(len(instanceType.Offerings), func(i, j int) {
710+
instanceType.Offerings[i], instanceType.Offerings[j] = instanceType.Offerings[j], instanceType.Offerings[i]
711+
})
669712
instanceType.Name = name
670713
instanceType.Overhead = &cloudprovider.InstanceTypeOverhead{
671714
KubeReserved: corev1.ResourceList{},

0 commit comments

Comments
 (0)