@@ -208,15 +208,17 @@ func (v *Validation) validateCreateLaunchTemplateAuthorization(
208208 nodeClaim * karpv1.NodeClaim ,
209209 tags map [string ]string ,
210210) (launchTemplate * launchtemplate.LaunchTemplate , result reconcile.Result , err error ) {
211- instanceTypes , err := v . getPrioritizedInstanceTypes (ctx , nodeClass )
211+ nodePools , err := nodepoolutils . ListManaged (ctx , v . kubeClient , v . cloudProvider , nodepoolutils . ForNodeClass ( nodeClass ) )
212212 if err != nil {
213- return nil , reconcile.Result {}, fmt .Errorf ("generating options , %w" , err )
213+ return nil , reconcile.Result {}, fmt .Errorf ("listing nodepools for nodeclass , %w" , err )
214214 }
215- // pass 1 instance type in EnsureAll to only create 1 launch template
216- tenancyType , err := v .getTenancyType (ctx , nodeClass )
215+ instanceTypes , err := v .getPrioritizedInstanceTypes (ctx , nodeClass , nodePools )
217216 if err != nil {
218- return nil , reconcile.Result {}, fmt .Errorf ("determining instance tenancy , %w" , err )
217+ return nil , reconcile.Result {}, fmt .Errorf ("generating options , %w" , err )
219218 }
219+ // pass 1 instance type in EnsureAll to only create 1 launch template
220+ tenancyType := getTenancyType (nodePools )
221+
220222 launchTemplates , err := v .launchTemplateProvider .EnsureAll (ctx , nodeClass , nodeClaim , instanceTypes [:1 ], karpv1 .CapacityTypeOnDemand , tags , string (tenancyType ))
221223 if err != nil {
222224 if awserrors .IsRateLimitedError (err ) || awserrors .IsServerError (err ) {
@@ -396,16 +398,20 @@ func getFleetLaunchTemplateConfig(
396398 }
397399}
398400
399- func (v * Validation ) getPrioritizedInstanceTypes (ctx context.Context , nodeClass * v1.EC2NodeClass ) ([]* cloudprovider.InstanceType , error ) {
400- // Select an instance type to use for validation. If NodePools exist for this NodeClass, we'll use an instance type
401- // selected by one of those NodePools. We should also prioritize an InstanceType which will launch with a non-GPU
402- // (VariantStandard) AMI, since GPU AMIs may have a larger snapshot size than that supported by the NodeClass'
403- // blockDeviceMappings.
401+ // getPrioritizedInstanceTypes returns the set of instances which could be launched using this NodeClass based on the
402+ // requirements of linked NodePools. If no NodePools exist for the given NodeClass, this function returns two default
403+ // instance types (one x86_64 and one arm64). If the 2 default instance types are not compatible with the NodeClass,
404+ // this function we'll use an instance type that could be selected with an open NodePool.
405+ func (v * Validation ) getPrioritizedInstanceTypes (ctx context.Context , nodeClass * v1.EC2NodeClass , nodePools []* karpv1.NodePool ) ([]* cloudprovider.InstanceType , error ) {
406+ // We should prioritize an InstanceType which will launch with a non-GPU (VariantStandard) AMI, since GPU
407+ // AMIs may have a larger snapshot size than that supported by the NodeClass' blockDeviceMappings.
404408 // Historical Issue: https://github.com/aws/karpenter-provider-aws/issues/7928
405- instanceTypes , err := v .getInstanceTypesForNodeClass (ctx , nodeClass )
409+ instanceTypes , err := v .instanceTypeProvider . List (ctx , nodeClass )
406410 if err != nil {
407- return nil , err
411+ return nil , fmt . Errorf ( "listing instance types for nodeclass, %w" , err )
408412 }
413+ instanceTypes = v .instanceTypeProvider .FilterForNodeClass (instanceTypes , nodeClass )
414+ compatibleInstanceTypes := getNodePoolCompatibleInstanceTypes (instanceTypes , nodePools )
409415
410416 // If there weren't any matching instance types, we should fallback to some defaults. There's an instance type included
411417 // for both x86_64 and arm64 architectures, ensuring that there will be a matching AMI. We also fallback to the default
@@ -414,51 +420,54 @@ func (v *Validation) getPrioritizedInstanceTypes(ctx context.Context, nodeClass
414420 // wouldn't be chosen due to cost in practice. This ensures the behavior matches that on Karpenter v1.3, preventing a
415421 // potential regression for Windows users.
416422 // Tracking issue: https://github.com/aws/karpenter-provider-aws/issues/7985
417- if len (instanceTypes ) == 0 || lo .ContainsBy ([]string {
423+ if len (compatibleInstanceTypes ) == 0 || lo .ContainsBy ([]string {
418424 v1 .AMIFamilyWindows2019 ,
419425 v1 .AMIFamilyWindows2022 ,
420426 v1 .AMIFamilyWindows2025 ,
421427 }, func (family string ) bool {
422428 return family == nodeClass .AMIFamily ()
423429 }) {
424- instanceTypes = []* cloudprovider.InstanceType {
425- {
426- Name : string (ec2types .InstanceTypeM5Large ),
427- Requirements : scheduling .NewRequirements (append (
428- lo .Values (amifamily .VariantStandard .Requirements ()),
429- scheduling .NewRequirement (corev1 .LabelArchStable , corev1 .NodeSelectorOpIn , karpv1 .ArchitectureAmd64 ),
430- scheduling .NewRequirement (corev1 .LabelOSStable , corev1 .NodeSelectorOpExists ),
431- scheduling .NewRequirement (corev1 .LabelWindowsBuild , corev1 .NodeSelectorOpExists ),
432- )... ),
433- },
434- {
435- Name : string (ec2types .InstanceTypeM6gLarge ),
436- Requirements : scheduling .NewRequirements (append (
437- lo .Values (amifamily .VariantStandard .Requirements ()),
438- scheduling .NewRequirement (corev1 .LabelArchStable , corev1 .NodeSelectorOpIn , karpv1 .ArchitectureArm64 ),
439- scheduling .NewRequirement (corev1 .LabelOSStable , corev1 .NodeSelectorOpExists ),
440- scheduling .NewRequirement (corev1 .LabelWindowsBuild , corev1 .NodeSelectorOpExists ),
441- )... ),
442- },
443- }
444- instanceTypes = getAMICompatibleInstanceTypes (instanceTypes , nodeClass )
430+ compatibleInstanceTypes = v .getFallbackInstanceTypes (instanceTypes , nodeClass )
445431 }
446-
447- return instanceTypes , nil
432+ return getAMICompatibleInstanceTypes (compatibleInstanceTypes , nodeClass ), nil
448433}
449434
450- // getInstanceTypesForNodeClass returns the set of instances which could be launched using this NodeClass based on the
451- // requirements of linked NodePools. If no NodePools exist for the given NodeClass, this function returns two default
452- // instance types (one x86_64 and one arm64).
453- func (v * Validation ) getInstanceTypesForNodeClass (ctx context.Context , nodeClass * v1.EC2NodeClass ) ([]* cloudprovider.InstanceType , error ) {
454- instanceTypes , err := v .instanceTypeProvider .List (ctx , nodeClass )
455- if err != nil {
456- return nil , fmt .Errorf ("listing instance types for nodeclass, %w" , err )
435+ func (v * Validation ) getFallbackInstanceTypes (instanceTypes []* cloudprovider.InstanceType , nodeClass * v1.EC2NodeClass ) []* cloudprovider.InstanceType {
436+ fallbackInstanceTypes := []* cloudprovider.InstanceType {
437+ {
438+ Name : string (ec2types .InstanceTypeM5Large ),
439+ Requirements : scheduling .NewRequirements (append (
440+ lo .Values (amifamily .VariantStandard .Requirements ()),
441+ scheduling .NewRequirement (corev1 .LabelArchStable , corev1 .NodeSelectorOpIn , karpv1 .ArchitectureAmd64 ),
442+ scheduling .NewRequirement (corev1 .LabelOSStable , corev1 .NodeSelectorOpExists ),
443+ scheduling .NewRequirement (corev1 .LabelWindowsBuild , corev1 .NodeSelectorOpExists ),
444+ )... ),
445+ },
446+ {
447+ Name : string (ec2types .InstanceTypeM6gLarge ),
448+ Requirements : scheduling .NewRequirements (append (
449+ lo .Values (amifamily .VariantStandard .Requirements ()),
450+ scheduling .NewRequirement (corev1 .LabelArchStable , corev1 .NodeSelectorOpIn , karpv1 .ArchitectureArm64 ),
451+ scheduling .NewRequirement (corev1 .LabelOSStable , corev1 .NodeSelectorOpExists ),
452+ scheduling .NewRequirement (corev1 .LabelWindowsBuild , corev1 .NodeSelectorOpExists ),
453+ )... ),
454+ },
457455 }
458- nodePools , err := nodepoolutils .ListManaged (ctx , v .kubeClient , v .cloudProvider , nodepoolutils .ForNodeClass (nodeClass ))
459- if err != nil {
460- return nil , fmt .Errorf ("listing nodepools for nodeclass, %w" , err )
456+ fallbackInstanceTypes = v .instanceTypeProvider .FilterForNodeClass (fallbackInstanceTypes , nodeClass )
457+ return lo .Ternary (len (fallbackInstanceTypes ) == 0 , instanceTypes , fallbackInstanceTypes )
458+ }
459+
460+ func getTenancyType (nodePools []* karpv1.NodePool ) ec2types.Tenancy {
461+ for _ , np := range nodePools {
462+ reqs := scheduling .NewNodeSelectorRequirementsWithMinValues (np .Spec .Template .Spec .Requirements ... )
463+ if reqs .Has (v1 .LabelInstanceTenancy ) && reqs .Get (v1 .LabelInstanceTenancy ).Has (string (ec2types .TenancyDedicated )) {
464+ return ec2types .TenancyDedicated
465+ }
461466 }
467+ return ec2types .TenancyDefault
468+ }
469+
470+ func getNodePoolCompatibleInstanceTypes (instanceTypes []* cloudprovider.InstanceType , nodePools []* karpv1.NodePool ) []* cloudprovider.InstanceType {
462471 var compatibleInstanceTypes []* cloudprovider.InstanceType
463472 names := sets .New [string ]()
464473 for _ , np := range nodePools {
@@ -477,22 +486,7 @@ func (v *Validation) getInstanceTypesForNodeClass(ctx context.Context, nodeClass
477486 compatibleInstanceTypes = append (compatibleInstanceTypes , it )
478487 }
479488 }
480- return getAMICompatibleInstanceTypes (compatibleInstanceTypes , nodeClass ), nil
481- }
482-
483- func (v * Validation ) getTenancyType (ctx context.Context , nodeClass * v1.EC2NodeClass ) (ec2types.Tenancy , error ) {
484- nodePools , err := nodepoolutils .ListManaged (ctx , v .kubeClient , v .cloudProvider , nodepoolutils .ForNodeClass (nodeClass ))
485- if err != nil {
486- return "" , fmt .Errorf ("listing nodepools for nodeclass, %w" , err )
487- }
488-
489- for _ , np := range nodePools {
490- reqs := scheduling .NewNodeSelectorRequirementsWithMinValues (np .Spec .Template .Spec .Requirements ... )
491- if reqs .Has (v1 .LabelInstanceTenancy ) && reqs .Get (v1 .LabelInstanceTenancy ).Has (string (ec2types .TenancyDedicated )) {
492- return ec2types .TenancyDedicated , nil
493- }
494- }
495- return ec2types .TenancyDefault , nil
489+ return compatibleInstanceTypes
496490}
497491
498492func getAMICompatibleInstanceTypes (instanceTypes []* cloudprovider.InstanceType , nodeClass * v1.EC2NodeClass ) []* cloudprovider.InstanceType {
0 commit comments