@@ -24,6 +24,7 @@ import (
2424 "sync/atomic"
2525 "time"
2626
27+ "github.com/awslabs/operatorpkg/option"
2728 "github.com/awslabs/operatorpkg/serrors"
2829 "go.uber.org/multierr"
2930 "sigs.k8s.io/controller-runtime/pkg/log"
@@ -40,8 +41,6 @@ import (
4041 "k8s.io/apimachinery/pkg/api/resource"
4142 karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1"
4243
43- karpoptions "sigs.k8s.io/karpenter/pkg/operator/options"
44-
4544 v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
4645 awserrors "github.com/aws/karpenter-provider-aws/pkg/errors"
4746 "github.com/aws/karpenter-provider-aws/pkg/operator/options"
@@ -56,23 +55,21 @@ import (
5655 sdk "github.com/aws/karpenter-provider-aws/pkg/aws"
5756)
5857
59- type Provider interface {
60- EnsureAll (context.Context , * v1.EC2NodeClass , * karpv1.NodeClaim ,
61- []* cloudprovider.InstanceType , string , map [string ]string ) ([]* LaunchTemplate , error )
62- DeleteAll (context.Context , * v1.EC2NodeClass ) error
63- InvalidateCache (context.Context , string , string )
64- ResolveClusterCIDR (context.Context ) error
65- CreateAMIOptions (context.Context , * v1.EC2NodeClass , map [string ]string , map [string ]string ) (* amifamily.Options , error )
58+ type DefaultProviderOpts = option.Function [defaultProviderOpts ]
59+
60+ type defaultProviderOpts struct {
61+ launchModeProvider LaunchModeProvider
6662}
67- type LaunchTemplate struct {
68- Name string
69- InstanceTypes [] * cloudprovider. InstanceType
70- ImageID string
71- CapacityReservationID string
63+
64+ func WithLaunchModeProvider ( provider LaunchModeProvider ) DefaultProviderOpts {
65+ return func ( opts * defaultProviderOpts ) {
66+ opts . launchModeProvider = provider
67+ }
7268}
7369
7470type DefaultProvider struct {
7571 sync.Mutex
72+ LaunchModeProvider
7673 ec2api sdk.EC2API
7774 eksapi sdk.EKSAPI
7875 amiFamily amifamily.Resolver
@@ -87,10 +84,26 @@ type DefaultProvider struct {
8784 ClusterIPFamily corev1.IPFamily
8885}
8986
90- func NewDefaultProvider (ctx context.Context , cache * cache.Cache , ec2api sdk.EC2API , eksapi sdk.EKSAPI , amiFamily amifamily.Resolver ,
91- securityGroupProvider securitygroup.Provider , subnetProvider subnet.Provider ,
92- caBundle * string , startAsync <- chan struct {}, kubeDNSIP net.IP , clusterEndpoint string ) * DefaultProvider {
87+ func NewDefaultProvider (
88+ ctx context.Context ,
89+ cache * cache.Cache ,
90+ ec2api sdk.EC2API ,
91+ eksapi sdk.EKSAPI ,
92+ amiFamily amifamily.Resolver ,
93+ securityGroupProvider securitygroup.Provider ,
94+ subnetProvider subnet.Provider ,
95+ caBundle * string ,
96+ startAsync <- chan struct {},
97+ kubeDNSIP net.IP ,
98+ clusterEndpoint string ,
99+ opts ... DefaultProviderOpts ,
100+ ) * DefaultProvider {
101+ resolvedOpts := option .Resolve (opts ... )
102+ if resolvedOpts .launchModeProvider == nil {
103+ resolvedOpts .launchModeProvider = defaultLaunchModeProvider {}
104+ }
93105 l := & DefaultProvider {
106+ LaunchModeProvider : resolvedOpts .launchModeProvider ,
94107 ec2api : ec2api ,
95108 eksapi : eksapi ,
96109 amiFamily : amiFamily ,
@@ -165,9 +178,11 @@ func (p *DefaultProvider) InvalidateCache(ctx context.Context, ltName string, lt
165178 log .FromContext (ctx ).V (1 ).Info ("invalidating launch template in the cache because it no longer exists" )
166179 p .cache .Delete (ltName )
167180}
181+
168182func LaunchTemplateName (options * amifamily.LaunchTemplate ) string {
169183 return fmt .Sprintf ("%s/%d" , v1 .LaunchTemplateNamePrefix , lo .Must (hashstructure .Hash (options , hashstructure .FormatV2 , & hashstructure.HashOptions {SlicesAsSets : true })))
170184}
185+
171186func (p * DefaultProvider ) CreateAMIOptions (ctx context.Context , nodeClass * v1.EC2NodeClass , labels , tags map [string ]string ) (* amifamily.Options , error ) {
172187 // Remove any labels passed into userData that are prefixed with "node-restriction.kubernetes.io" or "kops.k8s.io" since the kubelet can't
173188 // register the node with any labels from this domain: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction
@@ -242,7 +257,7 @@ func (p *DefaultProvider) createLaunchTemplate(ctx context.Context, options *ami
242257 if err != nil {
243258 return ec2types.LaunchTemplate {}, err
244259 }
245- createLaunchTemplateInput := GetCreateLaunchTemplateInput ( ctx , options , p .ClusterIPFamily , userData )
260+ createLaunchTemplateInput := NewCreateLaunchTemplateInputBuilder ( options , p .ClusterIPFamily , userData ). WithLaunchModeProvider ( p ). Build ( ctx )
246261 output , err := p .ec2api .CreateLaunchTemplate (ctx , createLaunchTemplateInput )
247262 if err != nil {
248263 return ec2types.LaunchTemplate {}, err
@@ -251,79 +266,6 @@ func (p *DefaultProvider) createLaunchTemplate(ctx context.Context, options *ami
251266 return lo .FromPtr (output .LaunchTemplate ), nil
252267}
253268
254- // you need UserData, AmiID, tags, blockdevicemappings, instance profile,
255- func GetCreateLaunchTemplateInput (
256- ctx context.Context ,
257- options * amifamily.LaunchTemplate ,
258- ClusterIPFamily corev1.IPFamily ,
259- userData string ,
260- ) * ec2.CreateLaunchTemplateInput {
261- launchTemplateDataTags := []ec2types.LaunchTemplateTagSpecificationRequest {
262- {ResourceType : ec2types .ResourceTypeNetworkInterface , Tags : utils .EC2MergeTags (options .Tags )},
263- }
264- if options .CapacityType == karpv1 .CapacityTypeSpot {
265- launchTemplateDataTags = append (launchTemplateDataTags , ec2types.LaunchTemplateTagSpecificationRequest {ResourceType : ec2types .ResourceTypeSpotInstancesRequest , Tags : utils .EC2MergeTags (options .Tags )})
266- }
267- networkInterfaces := generateNetworkInterfaces (options , ClusterIPFamily )
268- lt := & ec2.CreateLaunchTemplateInput {
269- LaunchTemplateName : aws .String (LaunchTemplateName (options )),
270- LaunchTemplateData : & ec2types.RequestLaunchTemplateData {
271- BlockDeviceMappings : blockDeviceMappings (options .BlockDeviceMappings ),
272- IamInstanceProfile : & ec2types.LaunchTemplateIamInstanceProfileSpecificationRequest {
273- Name : aws .String (options .InstanceProfile ),
274- },
275- Monitoring : & ec2types.LaunchTemplatesMonitoringRequest {
276- Enabled : aws .Bool (options .DetailedMonitoring ),
277- },
278- // If the network interface is defined, the security groups are defined within it
279- SecurityGroupIds : lo .Ternary (networkInterfaces != nil , nil , lo .Map (options .SecurityGroups , func (s v1.SecurityGroup , _ int ) string { return s .ID })),
280- UserData : aws .String (userData ),
281- ImageId : aws .String (options .AMIID ),
282- MetadataOptions : & ec2types.LaunchTemplateInstanceMetadataOptionsRequest {
283- HttpEndpoint : ec2types .LaunchTemplateInstanceMetadataEndpointState (lo .FromPtr (options .MetadataOptions .HTTPEndpoint )),
284- HttpProtocolIpv6 : ec2types .LaunchTemplateInstanceMetadataProtocolIpv6 (lo .FromPtr (options .MetadataOptions .HTTPProtocolIPv6 )),
285- //Will be removed when we update options.MetadataOptions.HTTPPutResponseHopLimit type to be int32
286- //nolint: gosec
287- HttpPutResponseHopLimit : lo .ToPtr (int32 (lo .FromPtr (options .MetadataOptions .HTTPPutResponseHopLimit ))),
288- HttpTokens : ec2types .LaunchTemplateHttpTokensState (lo .FromPtr (options .MetadataOptions .HTTPTokens )),
289- // We statically set the InstanceMetadataTags to "disabled" for all new instances since
290- // account-wide defaults can override instance defaults on metadata settings
291- // This can cause instance failure on accounts that default to instance tags since Karpenter
292- // can't support instance tags with its current tags (e.g. kubernetes.io/cluster/*, karpenter.k8s.aws/ec2nodeclass)
293- // See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-options.html#instance-metadata-options-order-of-precedence
294- InstanceMetadataTags : ec2types .LaunchTemplateInstanceMetadataTagsStateDisabled ,
295- },
296- NetworkInterfaces : networkInterfaces ,
297- TagSpecifications : launchTemplateDataTags ,
298- },
299- TagSpecifications : []ec2types.TagSpecification {
300- {
301- ResourceType : ec2types .ResourceTypeLaunchTemplate ,
302- Tags : utils .EC2MergeTags (options .Tags ),
303- },
304- },
305- }
306- // Gate this specifically since the update to CapacityReservationPreference will opt od / spot launches out of open
307- // ODCRs, which is a breaking change from the pre-native ODCR support behavior.
308- if karpoptions .FromContext (ctx ).FeatureGates .ReservedCapacity {
309- lt .LaunchTemplateData .CapacityReservationSpecification = & ec2types.LaunchTemplateCapacityReservationSpecificationRequest {
310- CapacityReservationPreference : lo .Ternary (
311- options .CapacityType == karpv1 .CapacityTypeReserved ,
312- ec2types .CapacityReservationPreferenceCapacityReservationsOnly ,
313- ec2types .CapacityReservationPreferenceNone ,
314- ),
315- CapacityReservationTarget : lo .Ternary (
316- options .CapacityType == karpv1 .CapacityTypeReserved ,
317- & ec2types.CapacityReservationTarget {
318- CapacityReservationId : & options .CapacityReservationID ,
319- },
320- nil ,
321- ),
322- }
323- }
324- return lt
325- }
326-
327269// generateNetworkInterfaces generates network interfaces for the launch template.
328270func generateNetworkInterfaces (options * amifamily.LaunchTemplate , clusterIPFamily corev1.IPFamily ) []ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest {
329271 if options .EFACount != 0 {
0 commit comments