func()

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
}