Skip to content

Commit 921ee62

Browse files
fix: prevent hash collisions while resolving subnets, security groups and AMIs from nodeclass selectors
1 parent e5a6cff commit 921ee62

File tree

21 files changed

+925
-120
lines changed

21 files changed

+925
-120
lines changed

pkg/controllers/nodeclass/ami.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030

3131
v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
3232
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily"
33+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
3334
)
3435

3536
type AMI struct {
@@ -47,7 +48,7 @@ func NewAMIReconciler(provider amifamily.Provider) *AMI {
4748
func (a *AMI) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (reconcile.Result, error) {
4849
amis, err := a.amiProvider.List(ctx, nodeClass)
4950
if err != nil {
50-
if amifamily.IsAl2DeprecationError(err) {
51+
if amifamilytypes.IsAl2DeprecationError(err) {
5152
nodeClass.StatusConditions().SetFalse(v1.ConditionTypeAMIsReady, "UnsupportedAlias", err.Error())
5253
return reconcile.Result{}, reconcile.TerminalError(fmt.Errorf("getting amis, %w", err))
5354
}
@@ -60,13 +61,13 @@ func (a *AMI) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (reconc
6061
// Returning 'ok' in this case means that the nodeclass will remain in an unready state until the component is restarted.
6162
return reconcile.Result{RequeueAfter: time.Minute}, nil
6263
}
63-
if uniqueAMIs := lo.Uniq(lo.Map(amis, func(a amifamily.AMI, _ int) string {
64+
if uniqueAMIs := lo.Uniq(lo.Map(amis, func(a amifamilytypes.AMI, _ int) string {
6465
return a.AmiID
6566
})); a.cm.HasChanged(fmt.Sprintf("amis/%s", nodeClass.Name), uniqueAMIs) {
6667
log.FromContext(ctx).WithValues("ids", uniqueAMIs).V(1).Info("discovered amis")
6768
}
6869

69-
nodeClass.Status.AMIs = lo.Map(amis, func(ami amifamily.AMI, _ int) v1.AMI {
70+
nodeClass.Status.AMIs = lo.Map(amis, func(ami amifamilytypes.AMI, _ int) v1.AMI {
7071
reqs := lo.Map(ami.Requirements.NodeSelectorRequirements(), func(item karpv1.NodeSelectorRequirementWithMinValues, _ int) corev1.NodeSelectorRequirement {
7172
return item.NodeSelectorRequirement
7273
})

pkg/controllers/nodeclass/ami_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ var _ = Describe("NodeClass AMI Status Controller", func() {
651651
awsEnv.Clock.Step(40 * time.Minute)
652652

653653
// Flush Cache
654-
awsEnv.EC2Cache.Flush()
654+
awsEnv.AMICache.Flush()
655655

656656
ExpectObjectReconciled(ctx, env.Client, controller, nodeClass)
657657
nodeClass = ExpectExists(ctx, env.Client, nodeClass)
@@ -750,7 +750,7 @@ var _ = Describe("NodeClass AMI Status Controller", func() {
750750
},
751751
})
752752

753-
awsEnv.EC2Cache.Flush()
753+
awsEnv.AMICache.Flush()
754754

755755
ExpectApplied(ctx, env.Client, nodeClass)
756756
ExpectObjectReconciled(ctx, env.Client, controller, nodeClass)

pkg/controllers/nodeclass/validation.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
awserrors "github.com/aws/karpenter-provider-aws/pkg/errors"
4343
"github.com/aws/karpenter-provider-aws/pkg/operator/options"
4444
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily"
45+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
4546
"github.com/aws/karpenter-provider-aws/pkg/providers/instance"
4647
"github.com/aws/karpenter-provider-aws/pkg/providers/instancetype"
4748
"github.com/aws/karpenter-provider-aws/pkg/providers/launchtemplate"
@@ -420,7 +421,7 @@ func (v *Validation) getPrioritizedInstanceTypes(ctx context.Context, nodeClass
420421
{
421422
Name: string(ec2types.InstanceTypeM5Large),
422423
Requirements: scheduling.NewRequirements(append(
423-
lo.Values(amifamily.VariantStandard.Requirements()),
424+
lo.Values(amifamilytypes.VariantStandard.Requirements()),
424425
scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, karpv1.ArchitectureAmd64),
425426
scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpExists),
426427
scheduling.NewRequirement(corev1.LabelWindowsBuild, corev1.NodeSelectorOpExists),
@@ -429,7 +430,7 @@ func (v *Validation) getPrioritizedInstanceTypes(ctx context.Context, nodeClass
429430
{
430431
Name: string(ec2types.InstanceTypeM6gLarge),
431432
Requirements: scheduling.NewRequirements(append(
432-
lo.Values(amifamily.VariantStandard.Requirements()),
433+
lo.Values(amifamilytypes.VariantStandard.Requirements()),
433434
scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, karpv1.ArchitectureArm64),
434435
scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpExists),
435436
scheduling.NewRequirement(corev1.LabelWindowsBuild, corev1.NodeSelectorOpExists),
@@ -483,7 +484,7 @@ func getAMICompatibleInstanceTypes(instanceTypes []*cloudprovider.InstanceType,
483484
continue
484485
}
485486
amiRequirements := scheduling.NewNodeSelectorRequirements(ami.Requirements...)
486-
if amiRequirements.IsCompatible(amifamily.VariantStandard.Requirements()) {
487+
if amiRequirements.IsCompatible(amifamilytypes.VariantStandard.Requirements()) {
487488
selectedInstanceTypes = append(selectedInstanceTypes, amiMap[ami.ID]...)
488489
}
489490
}

pkg/controllers/providers/ssm/invalidation/controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
3030
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily"
31+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
3132
"github.com/aws/karpenter-provider-aws/pkg/providers/ssm"
3233
)
3334

@@ -63,7 +64,7 @@ func (c *Controller) Reconcile(ctx context.Context) (reconciler.Result, error) {
6364
}
6465
amiIDsToParameters[entry.Value] = entry.Parameter
6566
}
66-
amis := []amifamily.AMI{}
67+
amis := []amifamilytypes.AMI{}
6768
for _, nodeClass := range lo.Map(lo.Keys(amiIDsToParameters), func(amiID string, _ int) *v1.EC2NodeClass {
6869
return &v1.EC2NodeClass{
6970
Spec: v1.EC2NodeClassSpec{

pkg/fake/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
type MockedFunction[I any, O any] struct {
2727
Output AtomicPtr[O] // Output to return on call to this function
28+
MultiOut AtomicPtrSlice[O]
2829
OutputPages AtomicPtrSlice[O]
2930
CalledWithInput AtomicPtrSlice[I] // Slice used to keep track of passed input to this function
3031
Error AtomicError // Error to return a certain number of times defined by custom error options
@@ -60,6 +61,11 @@ func (m *MockedFunction[I, O]) Invoke(input *I, defaultTransformer func(*I) (*O,
6061
m.successfulCalls.Add(1)
6162
return m.Output.Clone(), nil
6263
}
64+
65+
if m.MultiOut.Len() > 0 {
66+
m.successfulCalls.Add(1)
67+
return m.MultiOut.Pop(), nil
68+
}
6369
// This output pages multi-threaded handling isn't perfect
6470
// It will fail if pages are asynchronously requested from the same NextToken
6571
if m.OutputPages.Len() > 0 {

pkg/providers/amifamily/al2.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"sigs.k8s.io/karpenter/pkg/cloudprovider"
3333

3434
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap"
35+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
3536
"github.com/aws/karpenter-provider-aws/pkg/providers/ssm"
3637
)
3738

@@ -40,24 +41,24 @@ type AL2 struct {
4041
*Options
4142
}
4243

43-
func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (DescribeImageQuery, error) {
44-
ids := map[string][]Variant{}
45-
for path, variants := range map[string][]Variant{
44+
func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (amifamilytypes.DescribeImageQuery, error) {
45+
ids := map[string][]amifamilytypes.Variant{}
46+
for path, variants := range map[string][]amifamilytypes.Variant{
4647
fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/%s/image_id", k8sVersion, lo.Ternary(
4748
amiVersion == v1.AliasVersionLatest,
4849
"recommended",
4950
fmt.Sprintf("amazon-eks-node-%s-%s", k8sVersion, amiVersion),
50-
)): {VariantStandard},
51+
)): {amifamilytypes.VariantStandard},
5152
fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2-arm64/%s/image_id", k8sVersion, lo.Ternary(
5253
amiVersion == v1.AliasVersionLatest,
5354
"recommended",
5455
fmt.Sprintf("amazon-eks-arm64-node-%s-%s", k8sVersion, amiVersion),
55-
)): {VariantStandard},
56+
)): {amifamilytypes.VariantStandard},
5657
fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2-gpu/%s/image_id", k8sVersion, lo.Ternary(
5758
amiVersion == v1.AliasVersionLatest,
5859
"recommended",
5960
fmt.Sprintf("amazon-eks-gpu-node-%s-%s", k8sVersion, amiVersion),
60-
)): {VariantNeuron, VariantNvidia},
61+
)): {amifamilytypes.VariantNeuron, amifamilytypes.VariantNvidia},
6162
} {
6263
imageID, err := ssmProvider.Get(ctx, ssm.Parameter{
6364
Name: path,
@@ -70,16 +71,16 @@ func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k
7071
}
7172
// Failed to discover any AMIs, we should short circuit AMI discovery
7273
if len(ids) == 0 {
73-
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2@%s", amiVersion))
74+
return amifamilytypes.DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2@%s", amiVersion))
7475
}
7576

76-
return DescribeImageQuery{
77+
return amifamilytypes.DescribeImageQuery{
7778
Filters: []ec2types.Filter{{
7879
Name: lo.ToPtr("image-id"),
7980
Values: lo.Keys(ids),
8081
}},
81-
KnownRequirements: lo.MapValues(ids, func(variants []Variant, _ string) []scheduling.Requirements {
82-
return lo.Map(variants, func(v Variant, _ int) scheduling.Requirements { return v.Requirements() })
82+
KnownRequirements: lo.MapValues(ids, func(variants []amifamilytypes.Variant, _ string) []scheduling.Requirements {
83+
return lo.Map(variants, func(v amifamilytypes.Variant, _ int) scheduling.Requirements { return v.Requirements() })
8384
}),
8485
}, nil
8586
}

pkg/providers/amifamily/al2023.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
3030
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap"
31+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
3132
"github.com/aws/karpenter-provider-aws/pkg/providers/ssm"
3233
)
3334

@@ -36,11 +37,11 @@ type AL2023 struct {
3637
*Options
3738
}
3839

39-
func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (DescribeImageQuery, error) {
40-
ids := map[string]Variant{}
41-
for arch, variants := range map[string][]Variant{
42-
"x86_64": {VariantStandard, VariantNvidia, VariantNeuron},
43-
"arm64": {VariantStandard, VariantNvidia},
40+
func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (amifamilytypes.DescribeImageQuery, error) {
41+
ids := map[string]amifamilytypes.Variant{}
42+
for arch, variants := range map[string][]amifamilytypes.Variant{
43+
"x86_64": {amifamilytypes.VariantStandard, amifamilytypes.VariantNvidia, amifamilytypes.VariantNeuron},
44+
"arm64": {amifamilytypes.VariantStandard, amifamilytypes.VariantNvidia},
4445
} {
4546
for _, variant := range variants {
4647
path := a.resolvePath(arch, string(variant), k8sVersion, amiVersion)
@@ -56,15 +57,15 @@ func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider
5657
}
5758
// Failed to discover any AMIs, we should short circuit AMI discovery
5859
if len(ids) == 0 {
59-
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2023@%s", amiVersion))
60+
return amifamilytypes.DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2023@%s", amiVersion))
6061
}
6162

62-
return DescribeImageQuery{
63+
return amifamilytypes.DescribeImageQuery{
6364
Filters: []ec2types.Filter{{
6465
Name: lo.ToPtr("image-id"),
6566
Values: lo.Keys(ids),
6667
}},
67-
KnownRequirements: lo.MapValues(ids, func(v Variant, _ string) []scheduling.Requirements {
68+
KnownRequirements: lo.MapValues(ids, func(v amifamilytypes.Variant, _ string) []scheduling.Requirements {
6869
return []scheduling.Requirements{v.Requirements()}
6970
}),
7071
}, nil

pkg/providers/amifamily/ami.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ import (
2424
"github.com/aws/aws-sdk-go-v2/aws"
2525
"github.com/aws/aws-sdk-go-v2/service/ec2"
2626
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
27-
"github.com/mitchellh/hashstructure/v2"
27+
"github.com/awslabs/operatorpkg/object"
2828
"github.com/patrickmn/go-cache"
2929
"github.com/samber/lo"
3030
"k8s.io/utils/clock"
3131

3232
"github.com/aws/karpenter-provider-aws/pkg/errors"
33+
"github.com/aws/karpenter-provider-aws/pkg/utils"
3334

3435
v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
3536
sdk "github.com/aws/karpenter-provider-aws/pkg/aws"
37+
amifamilytypes "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/types"
3638
"github.com/aws/karpenter-provider-aws/pkg/providers/version"
3739

3840
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -43,7 +45,7 @@ import (
4345
)
4446

4547
type Provider interface {
46-
List(ctx context.Context, nodeClass *v1.EC2NodeClass) (AMIs, error)
48+
List(ctx context.Context, nodeClass *v1.EC2NodeClass) (amifamilytypes.AMIs, error)
4749
}
4850

4951
type DefaultProvider struct {
@@ -67,7 +69,7 @@ func NewDefaultProvider(clk clock.Clock, versionProvider version.Provider, ssmPr
6769
}
6870

6971
// List Returning a list of AMIs with its associated requirements
70-
func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) (AMIs, error) {
72+
func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) (amifamilytypes.AMIs, error) {
7173
p.Lock()
7274
defer p.Unlock()
7375
queries, err := p.DescribeImageQueries(ctx, nodeClass)
@@ -83,28 +85,26 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass)
8385
}
8486

8587
//nolint:gocyclo
86-
func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]DescribeImageQuery, error) {
88+
func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]amifamilytypes.DescribeImageQuery, error) {
8789
// Aliases are mutually exclusive, both on the term level and field level within a term.
8890
// This is enforced by a CEL validation, we will treat this as an invariant.
8991
if alias := nodeClass.Alias(); alias != nil {
9092
kubernetesVersion := p.versionProvider.Get(ctx)
9193
if alias.Family == v1.AMIFamilyAL2 {
9294
minorVersion, err := strconv.Atoi(strings.Split(kubernetesVersion, ".")[1])
9395
if err == nil && minorVersion >= 33 {
94-
return nil, &AL2DeprecationError{
95-
error: fmt.Errorf("AL2 aliases are no longer supported on EKS 1.33+ (see https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions-standard.html#kubernetes-1-33)"),
96-
}
96+
return nil, amifamilytypes.NewAL2DeprecationError("AL2 aliases are no longer supported on EKS 1.33+ (see https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions-standard.html#kubernetes-1-33)")
9797
}
9898
}
9999
query, err := GetAMIFamily(alias.Family, nil).DescribeImageQuery(ctx, p.ssmProvider, kubernetesVersion, alias.Version)
100100
if err != nil {
101-
return []DescribeImageQuery{}, err
101+
return []amifamilytypes.DescribeImageQuery{}, err
102102
}
103-
return []DescribeImageQuery{query}, nil
103+
return []amifamilytypes.DescribeImageQuery{query}, nil
104104
}
105105

106106
idFilter := ec2types.Filter{Name: aws.String("image-id")}
107-
queries := []DescribeImageQuery{}
107+
queries := []amifamilytypes.DescribeImageQuery{}
108108
for _, term := range nodeClass.Spec.AMISelectorTerms {
109109
switch {
110110
case term.ID != "":
@@ -116,7 +116,7 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v
116116
})
117117
if err != nil {
118118
if !errors.IsNotFound(err) {
119-
return []DescribeImageQuery{}, fmt.Errorf("resolving ssm parameter, %w", err)
119+
return []amifamilytypes.DescribeImageQuery{}, fmt.Errorf("resolving ssm parameter, %w", err)
120120
}
121121
log.FromContext(ctx).WithValues("ssmParameter", term.SSMParameter).V(1).Error(err, "parameter not found")
122122
continue
@@ -127,13 +127,13 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v
127127
}
128128
idFilter.Values = append(idFilter.Values, imageID)
129129
default:
130-
query := DescribeImageQuery{
130+
query := amifamilytypes.DescribeImageQuery{
131131
Owners: lo.Ternary(term.Owner != "", []string{term.Owner}, []string{}),
132132
}
133133
if term.Name != "" {
134134
// Default owners to self,amazon to ensure Karpenter only discovers cross-account AMIs if the user specifically allows it.
135135
// Removing this default would cause Karpenter to discover publicly shared AMIs passing the name filter.
136-
query = DescribeImageQuery{
136+
query = amifamilytypes.DescribeImageQuery{
137137
Owners: lo.Ternary(term.Owner != "", []string{term.Owner}, []string{"self", "amazon"}),
138138
}
139139
query.Filters = append(query.Filters, ec2types.Filter{
@@ -159,23 +159,22 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v
159159
}
160160
}
161161
if len(idFilter.Values) > 0 {
162-
queries = append(queries, DescribeImageQuery{Filters: []ec2types.Filter{idFilter}})
162+
queries = append(queries, amifamilytypes.DescribeImageQuery{Filters: []ec2types.Filter{idFilter}})
163163
}
164164
return queries, nil
165165
}
166166

167167
//nolint:gocyclo
168-
func (p *DefaultProvider) amis(ctx context.Context, queries []DescribeImageQuery) (AMIs, error) {
169-
hash, err := hashstructure.Hash(queries, hashstructure.FormatV2, &hashstructure.HashOptions{SlicesAsSets: true})
170-
if err != nil {
171-
return nil, err
172-
}
173-
if images, ok := p.cache.Get(fmt.Sprintf("%d", hash)); ok {
168+
func (p *DefaultProvider) amis(ctx context.Context, queries []amifamilytypes.DescribeImageQuery) (amifamilytypes.AMIs, error) {
169+
// Sort queries for a consistent hash.
170+
utils.SortQueries(queries)
171+
hash := object.Hash(queries)
172+
if images, ok := p.cache.Get(hash); ok {
174173
// Ensure what's returned from this function is a deep-copy of AMIs so alterations
175174
// to the data don't affect the original
176-
return append(AMIs{}, images.(AMIs)...), nil
175+
return append(amifamilytypes.AMIs{}, images.(amifamilytypes.AMIs)...), nil
177176
}
178-
images := map[uint64]AMI{}
177+
images := map[string]amifamilytypes.AMI{}
179178
for _, query := range queries {
180179
paginator := ec2.NewDescribeImagesPaginator(p.ec2api, query.DescribeImagesInput())
181180
for paginator.HasMorePages() {
@@ -195,9 +194,11 @@ func (p *DefaultProvider) amis(ctx context.Context, queries []DescribeImageQuery
195194
// Following checks are needed in order to always priortize non deprecated AMIs
196195
// If we already have an image with the same set of requirements, but this image (candidate) is newer, replace the previous (existing) image.
197196
// If we already have an image with the same set of requirements which is deprecated, but this image (candidate) is newer or non deprecated, replace the previous (existing) image
198-
reqsHash := lo.Must(hashstructure.Hash(reqs.NodeSelectorRequirements(), hashstructure.FormatV2, &hashstructure.HashOptions{SlicesAsSets: true}))
199-
candidateDeprecated := parseTimeWithDefault(lo.FromPtr(image.DeprecationTime), maxTime).Unix() <= p.clk.Now().Unix()
200-
ami := AMI{
197+
// Sort requirements to get a consistent hash.
198+
utils.SortRequirements(reqs.NodeSelectorRequirements())
199+
reqsHash := object.Hash(reqs.NodeSelectorRequirements())
200+
candidateDeprecated := amifamilytypes.ParseTimeWithDefault(lo.FromPtr(image.DeprecationTime), amifamilytypes.MaxTime).Unix() <= p.clk.Now().Unix()
201+
ami := amifamilytypes.AMI{
201202
Name: lo.FromPtr(image.Name),
202203
AmiID: lo.FromPtr(image.ImageId),
203204
CreationDate: lo.FromPtr(image.CreationDate),
@@ -214,7 +215,7 @@ func (p *DefaultProvider) amis(ctx context.Context, queries []DescribeImageQuery
214215
}
215216
}
216217
}
217-
p.cache.SetDefault(fmt.Sprintf("%d", hash), AMIs(lo.Values(images)))
218+
p.cache.SetDefault(hash, amifamilytypes.AMIs(lo.Values(images)))
218219
return lo.Values(images), nil
219220
}
220221

@@ -242,9 +243,9 @@ func MapToInstanceTypes(instanceTypes []*cloudprovider.InstanceType, amis []v1.A
242243
// 0 if AMI i == AMI j, where creation date, deprecation status and name are all equal
243244
// -1 if AMI i < AMI j, if AMI i is non-deprecated or newer than AMI j
244245
// +1 if AMI i > AMI j, if AMI j is non-deprecated or newer than AMI i
245-
func compareAMI(i, j AMI) int {
246-
iCreationDate := parseTimeWithDefault(i.CreationDate, minTime)
247-
jCreationDate := parseTimeWithDefault(j.CreationDate, minTime)
246+
func compareAMI(i, j amifamilytypes.AMI) int {
247+
iCreationDate := amifamilytypes.ParseTimeWithDefault(i.CreationDate, amifamilytypes.MinTime)
248+
jCreationDate := amifamilytypes.ParseTimeWithDefault(j.CreationDate, amifamilytypes.MinTime)
248249
// Prioritize non-deprecated AMIs over deprecated ones
249250
if i.Deprecated != j.Deprecated {
250251
return lo.Ternary(i.Deprecated, 1, -1)

0 commit comments

Comments
 (0)