in pkg/controllers/nodeclass/validation.go [345:425]
func (v *Validation) mockLaunchTemplateOptions(
ctx context.Context,
nodeClaim *karpv1.NodeClaim,
nodeClass *v1.EC2NodeClass,
tags map[string]string,
) (*amifamily.LaunchTemplate, error) {
amiOptions, err := v.launchTemplateProvider.CreateAMIOptions(
ctx,
nodeClass,
lo.Assign(nodeClaim.Labels, map[string]string{karpv1.CapacityTypeLabelKey: karpv1.CapacityTypeOnDemand}),
tags,
)
if err != nil {
return nil, err
}
// Select an instance type to use for validation. If NodePools exist for this NodeClass, we'll use an instance type
// selected by one of those NodePools. We should also prioritize an InstanceType which will launch with a non-GPU
// (VariantStandard) AMI, since GPU AMIs may have a larger snapshot size than that supported by the NodeClass'
// blockDeviceMappings.
// Historical Issue: https://github.com/aws/karpenter-provider-aws/issues/7928
instanceTypes, err := v.getInstanceTypesForNodeClass(ctx, nodeClass)
if err != nil {
return nil, err
}
amiMap := amifamily.MapToInstanceTypes(instanceTypes, nodeClass.Status.AMIs)
var selectedInstanceTypes []*cloudprovider.InstanceType
for _, ami := range nodeClass.Status.AMIs {
if len(amiMap[ami.ID]) == 0 {
continue
}
amiRequirements := scheduling.NewNodeSelectorRequirements(ami.Requirements...)
if amiRequirements.IsCompatible(amifamily.VariantStandard.Requirements()) {
selectedInstanceTypes = append(selectedInstanceTypes, amiMap[ami.ID]...)
}
}
// If we fail to find an instance type compatible with a standard AMI, fallback
if len(selectedInstanceTypes) == 0 && len(amiMap) != 0 {
selectedInstanceTypes = lo.Flatten(lo.Values(amiMap))
}
// If there weren't any matching instance types, we should fallback to some defaults. There's an instance type included
// for both x86_64 and arm64 architectures, ensuring that there will be a matching AMI. We also fallback to the default
// instance types if the AMI family is Windows. Karpenter currently incorrectly marks certain instance types as Windows
// compatible, and dynamic instance type resolution may choose those instance types for the dry-run, even if they
// wouldn't be chosen due to cost in practice. This ensures the behavior matches that on Karpenter v1.3, preventing a
// potential regression for Windows users.
// Tracking issue: https://github.com/aws/karpenter-provider-aws/issues/7985
if len(selectedInstanceTypes) == 0 || lo.ContainsBy([]string{
v1.AMIFamilyWindows2019,
v1.AMIFamilyWindows2022,
}, func(family string) bool {
return family == nodeClass.AMIFamily()
}) {
selectedInstanceTypes = []*cloudprovider.InstanceType{
{
Name: string(ec2types.InstanceTypeM5Large),
Requirements: scheduling.NewRequirements(append(
lo.Values(amifamily.VariantStandard.Requirements()),
scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, karpv1.ArchitectureAmd64),
scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpExists),
scheduling.NewRequirement(corev1.LabelWindowsBuild, corev1.NodeSelectorOpExists),
)...),
},
{
Name: string(ec2types.InstanceTypeM6gLarge),
Requirements: scheduling.NewRequirements(append(
lo.Values(amifamily.VariantStandard.Requirements()),
scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, karpv1.ArchitectureArm64),
scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpExists),
scheduling.NewRequirement(corev1.LabelWindowsBuild, corev1.NodeSelectorOpExists),
)...),
},
}
}
opts, err := v.amiResolver.Resolve(nodeClass, nodeClaim, selectedInstanceTypes, karpv1.CapacityTypeOnDemand, amiOptions)
if err != nil {
return nil, err
}
return opts[0], nil
}