in pkg/providers/amifamily/resolver.go [125:177]
func (r DefaultResolver) Resolve(nodeClass *v1.EC2NodeClass, nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, capacityType string, options *Options) ([]*LaunchTemplate, error) {
amiFamily := GetAMIFamily(nodeClass.AMIFamily(), options)
if len(nodeClass.Status.AMIs) == 0 {
return nil, fmt.Errorf("no amis exist given constraints")
}
mappedAMIs := MapToInstanceTypes(instanceTypes, nodeClass.Status.AMIs)
if len(mappedAMIs) == 0 {
return nil, fmt.Errorf("no instance types satisfy requirements of amis %v", lo.Uniq(lo.Map(nodeClass.Status.AMIs, func(a v1.AMI, _ int) string { return a.ID })))
}
var resolvedTemplates []*LaunchTemplate
for amiID, instanceTypes := range mappedAMIs {
// In order to support reserved ENIs for CNI custom networking setups,
// we need to pass down the max-pods calculation to the kubelet.
// This requires that we resolve a unique launch template per max-pods value.
// Similarly, instance types configured with EFAs require unique launch templates depending on the number of
// EFAs they support.
// Reservations IDs are also included since we need to create a separate LaunchTemplate per reservation ID when
// launching reserved capacity. If it's a reserved capacity launch, we've already filtered the instance types
// further up the call stack.
type launchTemplateParams struct {
efaCount int
maxPods int
// reservationIDs is encoded as a string rather than a slice to ensure this type is comparable for use by `lo.GroupBy`.
reservationIDs string
}
paramsToInstanceTypes := lo.GroupBy(instanceTypes, func(it *cloudprovider.InstanceType) launchTemplateParams {
return launchTemplateParams{
efaCount: lo.Ternary(
lo.Contains(lo.Keys(nodeClaim.Spec.Resources.Requests), v1.ResourceEFA),
int(lo.ToPtr(it.Capacity[v1.ResourceEFA]).Value()),
0,
),
maxPods: int(it.Capacity.Pods().Value()),
// If we're dealing with reserved instances, there's only going to be a single instance per group. This invariant
// is due to reservation IDs not being shared across instance types. Because of this, we don't need to worry about
// ordering in this string.
reservationIDs: lo.Ternary(
capacityType == karpv1.CapacityTypeReserved,
strings.Join(lo.FilterMap(it.Offerings, func(o *cloudprovider.Offering, _ int) (string, bool) {
return o.ReservationID(), o.CapacityType() == karpv1.CapacityTypeReserved
}), ","),
"",
),
}
})
for params, instanceTypes := range paramsToInstanceTypes {
reservationIDs := strings.Split(params.reservationIDs, ",")
resolvedTemplates = append(resolvedTemplates, r.resolveLaunchTemplates(nodeClass, nodeClaim, instanceTypes, capacityType, amiFamily, amiID, params.maxPods, params.efaCount, reservationIDs, options)...)
}
}
return resolvedTemplates, nil
}