in pkg/selector/selector.go [155:280]
func (itf Selector) rawFilter(filters Filters) ([]instancetypes.Details, error) {
filters, err := itf.AggregateFilterTransform(filters)
if err != nil {
return nil, err
}
var locations, availabilityZones []string
if filters.CPUArchitecture != nil && *filters.CPUArchitecture == cpuArchitectureAMD64 {
*filters.CPUArchitecture = cpuArchitectureX8664
}
if filters.VirtualizationType != nil && *filters.VirtualizationType == virtualizationTypePV {
*filters.VirtualizationType = virtualizationTypeParaVirtual
}
if filters.AvailabilityZones != nil {
availabilityZones = *filters.AvailabilityZones
locations = *filters.AvailabilityZones
} else if filters.Region != nil {
locations = []string{*filters.Region}
}
locationInstanceOfferings, err := itf.RetrieveInstanceTypesSupportedInLocations(locations)
if err != nil {
return nil, err
}
instanceTypesInput := &ec2.DescribeInstanceTypesInput{}
instanceTypeCandidates := map[string]*instancetypes.Details{}
// innerErr will hold any error while processing DescribeInstanceTypes pages
var innerErr error
err = itf.EC2.DescribeInstanceTypesPages(instanceTypesInput, func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool {
for _, instanceTypeInfo := range page.InstanceTypes {
instanceTypeName := *instanceTypeInfo.InstanceType
instanceTypeCandidates[instanceTypeName] = &instancetypes.Details{InstanceTypeInfo: *instanceTypeInfo}
isFpga := instanceTypeInfo.FpgaInfo != nil
var instanceTypeHourlyPriceForFilter float64 // Price used to filter based on usage class
var instanceTypeHourlyPriceOnDemand, instanceTypeHourlyPriceSpot *float64
// If prices are fetched, populate the fields irrespective of the price filters
if itf.EC2Pricing.LastOnDemandCacheUTC() != nil {
price, err := itf.EC2Pricing.GetOndemandInstanceTypeCost(instanceTypeName)
if err != nil {
fmt.Printf("Could not retrieve instantaneous hourly on-demand price for instance type %s\n", instanceTypeName)
} else {
instanceTypeHourlyPriceOnDemand = &price
instanceTypeCandidates[instanceTypeName].OndemandPricePerHour = instanceTypeHourlyPriceOnDemand
}
}
if itf.EC2Pricing.LastSpotCacheUTC() != nil {
price, err := itf.EC2Pricing.GetSpotInstanceTypeNDayAvgCost(instanceTypeName, availabilityZones, 30)
if err != nil {
fmt.Printf("Could not retrieve 30 day avg hourly spot price for instance type %s\n", instanceTypeName)
} else {
instanceTypeHourlyPriceSpot = &price
instanceTypeCandidates[instanceTypeName].SpotPrice = instanceTypeHourlyPriceSpot
}
}
if filters.PricePerHour != nil {
// If price filter is present, prices should be already fetched
// If prices are not fetched, filter should fail and the corresponding error is already printed
if filters.UsageClass != nil && *filters.UsageClass == "spot" && instanceTypeHourlyPriceSpot != nil {
instanceTypeHourlyPriceForFilter = *instanceTypeHourlyPriceSpot
} else if instanceTypeHourlyPriceOnDemand != nil {
instanceTypeHourlyPriceForFilter = *instanceTypeHourlyPriceOnDemand
}
}
// filterToInstanceSpecMappingPairs is a map of filter name [key] to filter pair [value].
// A filter pair includes user input filter value and instance spec value retrieved from DescribeInstanceTypes
filterToInstanceSpecMappingPairs := map[string]filterPair{
cpuArchitecture: {filters.CPUArchitecture, instanceTypeInfo.ProcessorInfo.SupportedArchitectures},
usageClass: {filters.UsageClass, instanceTypeInfo.SupportedUsageClasses},
rootDeviceType: {filters.RootDeviceType, instanceTypeInfo.SupportedRootDeviceTypes},
hibernationSupported: {filters.HibernationSupported, instanceTypeInfo.HibernationSupported},
vcpusRange: {filters.VCpusRange, instanceTypeInfo.VCpuInfo.DefaultVCpus},
memoryRange: {filters.MemoryRange, instanceTypeInfo.MemoryInfo.SizeInMiB},
gpuMemoryRange: {filters.GpuMemoryRange, getTotalGpuMemory(instanceTypeInfo.GpuInfo)},
gpusRange: {filters.GpusRange, getTotalGpusCount(instanceTypeInfo.GpuInfo)},
placementGroupStrategy: {filters.PlacementGroupStrategy, instanceTypeInfo.PlacementGroupInfo.SupportedStrategies},
hypervisor: {filters.Hypervisor, instanceTypeInfo.Hypervisor},
baremetal: {filters.BareMetal, instanceTypeInfo.BareMetal},
burstable: {filters.Burstable, instanceTypeInfo.BurstablePerformanceSupported},
fpga: {filters.Fpga, &isFpga},
enaSupport: {filters.EnaSupport, supportSyntaxToBool(instanceTypeInfo.NetworkInfo.EnaSupport)},
efaSupport: {filters.EfaSupport, instanceTypeInfo.NetworkInfo.EfaSupported},
vcpusToMemoryRatio: {filters.VCpusToMemoryRatio, calculateVCpusToMemoryRatio(instanceTypeInfo.VCpuInfo.DefaultVCpus, instanceTypeInfo.MemoryInfo.SizeInMiB)},
currentGeneration: {filters.CurrentGeneration, instanceTypeInfo.CurrentGeneration},
networkInterfaces: {filters.NetworkInterfaces, instanceTypeInfo.NetworkInfo.MaximumNetworkInterfaces},
networkPerformance: {filters.NetworkPerformance, getNetworkPerformance(instanceTypeInfo.NetworkInfo.NetworkPerformance)},
instanceTypes: {filters.InstanceTypes, instanceTypeInfo.InstanceType},
virtualizationType: {filters.VirtualizationType, instanceTypeInfo.SupportedVirtualizationTypes},
pricePerHour: {filters.PricePerHour, &instanceTypeHourlyPriceForFilter},
}
if isInDenyList(filters.DenyList, instanceTypeName) || !isInAllowList(filters.AllowList, instanceTypeName) {
delete(instanceTypeCandidates, instanceTypeName)
}
if !isSupportedInLocation(locationInstanceOfferings, instanceTypeName) {
delete(instanceTypeCandidates, instanceTypeName)
}
var isInstanceSupported bool
isInstanceSupported, innerErr = itf.executeFilters(filterToInstanceSpecMappingPairs, instanceTypeName)
if innerErr != nil {
// stops paging through instance types
return false
}
if !isInstanceSupported {
delete(instanceTypeCandidates, instanceTypeName)
}
}
// continue paging through instance types
return true
})
if err != nil {
return nil, err
}
if innerErr != nil {
return nil, innerErr
}
instanceTypeInfoSlice := []instancetypes.Details{}
for _, instanceTypeInfo := range instanceTypeCandidates {
instanceTypeInfoSlice = append(instanceTypeInfoSlice, *instanceTypeInfo)
}
return sortInstanceTypeInfo(instanceTypeInfoSlice), nil
}