@@ -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
4547type 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
4951type 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