sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/autoscaling/autoscalinggroup.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package asg
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/aws/aws-sdk-go/aws"
    24  	"github.com/aws/aws-sdk-go/service/autoscaling"
    25  	"github.com/aws/aws-sdk-go/service/ec2"
    26  	"github.com/pkg/errors"
    27  	"k8s.io/utils/pointer"
    28  
    29  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    30  	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1"
    31  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors"
    32  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/converters"
    33  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    34  	"sigs.k8s.io/cluster-api-provider-aws/pkg/record"
    35  )
    36  
    37  // SDKToAutoScalingGroup converts an AWS EC2 SDK AutoScalingGroup to the CAPA AutoScalingGroup type.
    38  func (s *Service) SDKToAutoScalingGroup(v *autoscaling.Group) (*expinfrav1.AutoScalingGroup, error) {
    39  	i := &expinfrav1.AutoScalingGroup{
    40  		ID:   aws.StringValue(v.AutoScalingGroupARN),
    41  		Name: aws.StringValue(v.AutoScalingGroupName),
    42  		// TODO(rudoi): this is just terrible
    43  		DesiredCapacity:   aws.Int32(int32(aws.Int64Value(v.DesiredCapacity))),
    44  		MaxSize:           int32(aws.Int64Value(v.MaxSize)),
    45  		MinSize:           int32(aws.Int64Value(v.MinSize)),
    46  		CapacityRebalance: aws.BoolValue(v.CapacityRebalance),
    47  		//TODO: determine what additional values go here and what else should be in the struct
    48  	}
    49  
    50  	if v.MixedInstancesPolicy != nil {
    51  		i.MixedInstancesPolicy = &expinfrav1.MixedInstancesPolicy{
    52  			InstancesDistribution: &expinfrav1.InstancesDistribution{
    53  				OnDemandBaseCapacity:                v.MixedInstancesPolicy.InstancesDistribution.OnDemandBaseCapacity,
    54  				OnDemandPercentageAboveBaseCapacity: v.MixedInstancesPolicy.InstancesDistribution.OnDemandPercentageAboveBaseCapacity,
    55  			},
    56  		}
    57  
    58  		for _, override := range v.MixedInstancesPolicy.LaunchTemplate.Overrides {
    59  			i.MixedInstancesPolicy.Overrides = append(i.MixedInstancesPolicy.Overrides, expinfrav1.Overrides{InstanceType: aws.StringValue(override.InstanceType)})
    60  		}
    61  
    62  		onDemandAllocationStrategy := aws.StringValue(v.MixedInstancesPolicy.InstancesDistribution.OnDemandAllocationStrategy)
    63  		if onDemandAllocationStrategy == string(expinfrav1.OnDemandAllocationStrategyPrioritized) {
    64  			i.MixedInstancesPolicy.InstancesDistribution.OnDemandAllocationStrategy = expinfrav1.OnDemandAllocationStrategyPrioritized
    65  		}
    66  
    67  		spotAllocationStrategy := aws.StringValue(v.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy)
    68  		if spotAllocationStrategy == string(expinfrav1.SpotAllocationStrategyLowestPrice) {
    69  			i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyLowestPrice
    70  		} else {
    71  			i.MixedInstancesPolicy.InstancesDistribution.SpotAllocationStrategy = expinfrav1.SpotAllocationStrategyCapacityOptimized
    72  		}
    73  	}
    74  
    75  	if v.Status != nil {
    76  		i.Status = expinfrav1.ASGStatus(*v.Status)
    77  	}
    78  
    79  	if len(v.Tags) > 0 {
    80  		i.Tags = converters.ASGTagsToMap(v.Tags)
    81  	}
    82  
    83  	if len(v.Instances) > 0 {
    84  		for _, autoscalingInstance := range v.Instances {
    85  			tmp := &infrav1.Instance{
    86  				ID:               aws.StringValue(autoscalingInstance.InstanceId),
    87  				State:            infrav1.InstanceState(*autoscalingInstance.LifecycleState),
    88  				AvailabilityZone: *autoscalingInstance.AvailabilityZone,
    89  			}
    90  			i.Instances = append(i.Instances, *tmp)
    91  		}
    92  	}
    93  
    94  	return i, nil
    95  }
    96  
    97  // ASGIfExists returns the existing autoscaling group or nothing if it doesn't exist.
    98  func (s *Service) ASGIfExists(name *string) (*expinfrav1.AutoScalingGroup, error) {
    99  	if name == nil {
   100  		s.scope.Info("Autoscaling Group does not have a name")
   101  		return nil, nil
   102  	}
   103  
   104  	s.scope.Info("Looking for asg by name", "name", *name)
   105  
   106  	input := &autoscaling.DescribeAutoScalingGroupsInput{
   107  		AutoScalingGroupNames: []*string{name},
   108  	}
   109  
   110  	out, err := s.ASGClient.DescribeAutoScalingGroups(input)
   111  	switch {
   112  	case awserrors.IsNotFound(err):
   113  		return nil, nil
   114  	case err != nil:
   115  		record.Eventf(s.scope.InfraCluster(), "FailedDescribeAutoScalingGroups", "failed to describe ASG %q: %v", *name, err)
   116  		return nil, errors.Wrapf(err, "failed to describe AutoScaling Group: %q", *name)
   117  	}
   118  	//TODO: double check if you're handling nil vals
   119  	return s.SDKToAutoScalingGroup(out.AutoScalingGroups[0])
   120  }
   121  
   122  // GetASGByName returns the existing ASG or nothing if it doesn't exist.
   123  func (s *Service) GetASGByName(scope *scope.MachinePoolScope) (*expinfrav1.AutoScalingGroup, error) {
   124  	s.scope.V(2).Info("Looking for existing AutoScalingGroup by name")
   125  
   126  	input := &autoscaling.DescribeAutoScalingGroupsInput{
   127  		AutoScalingGroupNames: []*string{
   128  			aws.String(scope.Name()),
   129  		},
   130  	}
   131  
   132  	out, err := s.ASGClient.DescribeAutoScalingGroups(input)
   133  	switch {
   134  	case awserrors.IsNotFound(err):
   135  		return nil, nil
   136  	case err != nil:
   137  		record.Eventf(s.scope.InfraCluster(), "FailedDescribeInstances", "Failed to describe instances by tags: %v", err)
   138  		return nil, errors.Wrap(err, "failed to describe instances by tags")
   139  	case len(out.AutoScalingGroups) == 0:
   140  		record.Eventf(scope.AWSMachinePool, "FailedDescribeInstances", "No Auto Scaling Groups with %s found", scope.Name())
   141  		return nil, nil
   142  	}
   143  
   144  	return s.SDKToAutoScalingGroup(out.AutoScalingGroups[0])
   145  }
   146  
   147  // CreateASG runs an autoscaling group.
   148  func (s *Service) CreateASG(scope *scope.MachinePoolScope) (*expinfrav1.AutoScalingGroup, error) {
   149  	subnets, err := s.SubnetIDs(scope)
   150  	if err != nil {
   151  		return nil, fmt.Errorf("getting subnets for ASG: %w", err)
   152  	}
   153  
   154  	input := &expinfrav1.AutoScalingGroup{
   155  		Name:                 scope.Name(),
   156  		MaxSize:              scope.AWSMachinePool.Spec.MaxSize,
   157  		MinSize:              scope.AWSMachinePool.Spec.MinSize,
   158  		Subnets:              subnets,
   159  		DefaultCoolDown:      scope.AWSMachinePool.Spec.DefaultCoolDown,
   160  		CapacityRebalance:    scope.AWSMachinePool.Spec.CapacityRebalance,
   161  		MixedInstancesPolicy: scope.AWSMachinePool.Spec.MixedInstancesPolicy,
   162  	}
   163  
   164  	if scope.MachinePool.Spec.Replicas != nil {
   165  		input.DesiredCapacity = scope.MachinePool.Spec.Replicas
   166  	}
   167  
   168  	if scope.AWSMachinePool.Status.LaunchTemplateID == "" {
   169  		return nil, errors.New("AWSMachinePool has no LaunchTemplateID for some reason")
   170  	}
   171  
   172  	// Make sure to use the MachinePoolScope here to get the merger of AWSCluster and AWSMachinePool tags
   173  	additionalTags := scope.AdditionalTags()
   174  	// Set the cloud provider tag
   175  	additionalTags[infrav1.ClusterAWSCloudProviderTagKey(s.scope.KubernetesClusterName())] = string(infrav1.ResourceLifecycleOwned)
   176  
   177  	input.Tags = infrav1.Build(infrav1.BuildParams{
   178  		ClusterName: s.scope.KubernetesClusterName(),
   179  		Lifecycle:   infrav1.ResourceLifecycleOwned,
   180  		Name:        aws.String(scope.Name()),
   181  		Role:        aws.String("node"),
   182  		Additional:  additionalTags,
   183  	})
   184  
   185  	s.scope.Info("Running instance")
   186  	if err := s.runPool(input, scope.AWSMachinePool.Status.LaunchTemplateID); err != nil {
   187  		// Only record the failure event if the error is not related to failed dependencies.
   188  		// This is to avoid spamming failure events since the machine will be requeued by the actuator.
   189  		// if !awserrors.IsFailedDependency(errors.Cause(err)) {
   190  		// 	record.Warnf(scope.AWSMachinePool, "FailedCreate", "Failed to create instance: %v", err)
   191  		// }
   192  		s.scope.Error(err, "unable to create AutoScalingGroup")
   193  		return nil, err
   194  	}
   195  	record.Eventf(scope.AWSMachinePool, "SuccessfulCreate", "Created new ASG: %s", scope.Name())
   196  
   197  	return nil, nil
   198  }
   199  
   200  func (s *Service) runPool(i *expinfrav1.AutoScalingGroup, launchTemplateID string) error {
   201  	input := &autoscaling.CreateAutoScalingGroupInput{
   202  		AutoScalingGroupName: aws.String(i.Name),
   203  		MaxSize:              aws.Int64(int64(i.MaxSize)),
   204  		MinSize:              aws.Int64(int64(i.MinSize)),
   205  		VPCZoneIdentifier:    aws.String(strings.Join(i.Subnets, ", ")),
   206  		DefaultCooldown:      aws.Int64(int64(i.DefaultCoolDown.Duration.Seconds())),
   207  		CapacityRebalance:    aws.Bool(i.CapacityRebalance),
   208  	}
   209  
   210  	if i.DesiredCapacity != nil {
   211  		input.DesiredCapacity = aws.Int64(int64(aws.Int32Value(i.DesiredCapacity)))
   212  	}
   213  
   214  	if i.MixedInstancesPolicy != nil {
   215  		input.MixedInstancesPolicy = createSDKMixedInstancesPolicy(i.Name, i.MixedInstancesPolicy)
   216  	} else {
   217  		input.LaunchTemplate = &autoscaling.LaunchTemplateSpecification{
   218  			LaunchTemplateId: aws.String(launchTemplateID),
   219  			Version:          aws.String(expinfrav1.LaunchTemplateLatestVersion),
   220  		}
   221  	}
   222  
   223  	if i.Tags != nil {
   224  		input.Tags = BuildTagsFromMap(i.Name, i.Tags)
   225  	}
   226  
   227  	if _, err := s.ASGClient.CreateAutoScalingGroup(input); err != nil {
   228  		return errors.Wrap(err, "failed to create autoscaling group")
   229  	}
   230  
   231  	return nil
   232  }
   233  
   234  // DeleteASGAndWait will delete an ASG and wait until it is deleted.
   235  func (s *Service) DeleteASGAndWait(name string) error {
   236  	if err := s.DeleteASG(name); err != nil {
   237  		return err
   238  	}
   239  
   240  	s.scope.V(2).Info("Waiting for ASG to be deleted", "name", name)
   241  
   242  	input := &autoscaling.DescribeAutoScalingGroupsInput{
   243  		AutoScalingGroupNames: aws.StringSlice([]string{name}),
   244  	}
   245  
   246  	if err := s.ASGClient.WaitUntilGroupNotExists(input); err != nil {
   247  		return errors.Wrapf(err, "failed to wait for ASG %q deletion", name)
   248  	}
   249  
   250  	return nil
   251  }
   252  
   253  // DeleteASG will delete the ASG of a service.
   254  func (s *Service) DeleteASG(name string) error {
   255  	s.scope.V(2).Info("Attempting to delete ASG", "name", name)
   256  
   257  	input := &autoscaling.DeleteAutoScalingGroupInput{
   258  		AutoScalingGroupName: aws.String(name),
   259  		ForceDelete:          aws.Bool(true),
   260  	}
   261  
   262  	if _, err := s.ASGClient.DeleteAutoScalingGroup(input); err != nil {
   263  		return errors.Wrapf(err, "failed to delete ASG %q", name)
   264  	}
   265  
   266  	s.scope.V(2).Info("Deleted ASG", "name", name)
   267  	return nil
   268  }
   269  
   270  // UpdateASG will update the ASG of a service.
   271  func (s *Service) UpdateASG(scope *scope.MachinePoolScope) error {
   272  	subnetIDs, err := s.SubnetIDs(scope)
   273  	if err != nil {
   274  		return fmt.Errorf("getting subnets for ASG: %w", err)
   275  	}
   276  
   277  	input := &autoscaling.UpdateAutoScalingGroupInput{
   278  		AutoScalingGroupName: aws.String(scope.Name()), //TODO: define dynamically - borrow logic from ec2
   279  		MaxSize:              aws.Int64(int64(scope.AWSMachinePool.Spec.MaxSize)),
   280  		MinSize:              aws.Int64(int64(scope.AWSMachinePool.Spec.MinSize)),
   281  		VPCZoneIdentifier:    aws.String(strings.Join(subnetIDs, ", ")),
   282  		CapacityRebalance:    aws.Bool(scope.AWSMachinePool.Spec.CapacityRebalance),
   283  	}
   284  
   285  	if scope.MachinePool.Spec.Replicas != nil {
   286  		input.DesiredCapacity = aws.Int64(int64(*scope.MachinePool.Spec.Replicas))
   287  	}
   288  
   289  	if scope.AWSMachinePool.Spec.MixedInstancesPolicy != nil {
   290  		input.MixedInstancesPolicy = createSDKMixedInstancesPolicy(scope.Name(), scope.AWSMachinePool.Spec.MixedInstancesPolicy)
   291  	} else {
   292  		input.LaunchTemplate = &autoscaling.LaunchTemplateSpecification{
   293  			LaunchTemplateId: aws.String(scope.AWSMachinePool.Status.LaunchTemplateID),
   294  			Version:          aws.String(expinfrav1.LaunchTemplateLatestVersion),
   295  		}
   296  	}
   297  
   298  	if _, err := s.ASGClient.UpdateAutoScalingGroup(input); err != nil {
   299  		return errors.Wrapf(err, "failed to update ASG %q", scope.Name())
   300  	}
   301  
   302  	return nil
   303  }
   304  
   305  // CanStartASGInstanceRefresh will start an ASG instance with refresh.
   306  func (s *Service) CanStartASGInstanceRefresh(scope *scope.MachinePoolScope) (bool, error) {
   307  	describeInput := &autoscaling.DescribeInstanceRefreshesInput{AutoScalingGroupName: aws.String(scope.Name())}
   308  	refreshes, err := s.ASGClient.DescribeInstanceRefreshes(describeInput)
   309  	if err != nil {
   310  		return false, err
   311  	}
   312  	hasUnfinishedRefresh := false
   313  	if err == nil && len(refreshes.InstanceRefreshes) != 0 {
   314  		for i := range refreshes.InstanceRefreshes {
   315  			if *refreshes.InstanceRefreshes[i].Status == autoscaling.InstanceRefreshStatusInProgress ||
   316  				*refreshes.InstanceRefreshes[i].Status == autoscaling.InstanceRefreshStatusPending ||
   317  				*refreshes.InstanceRefreshes[i].Status == autoscaling.InstanceRefreshStatusCancelling {
   318  				hasUnfinishedRefresh = true
   319  			}
   320  		}
   321  	}
   322  	if hasUnfinishedRefresh {
   323  		return false, nil
   324  	}
   325  	return true, nil
   326  }
   327  
   328  // StartASGInstanceRefresh will start an ASG instance with refresh.
   329  func (s *Service) StartASGInstanceRefresh(scope *scope.MachinePoolScope) error {
   330  	strategy := pointer.StringPtr(autoscaling.RefreshStrategyRolling)
   331  	var minHealthyPercentage, instanceWarmup *int64
   332  	if scope.AWSMachinePool.Spec.RefreshPreferences != nil {
   333  		if scope.AWSMachinePool.Spec.RefreshPreferences.Strategy != nil {
   334  			strategy = scope.AWSMachinePool.Spec.RefreshPreferences.Strategy
   335  		}
   336  		if scope.AWSMachinePool.Spec.RefreshPreferences.InstanceWarmup != nil {
   337  			instanceWarmup = scope.AWSMachinePool.Spec.RefreshPreferences.InstanceWarmup
   338  		}
   339  		if scope.AWSMachinePool.Spec.RefreshPreferences.MinHealthyPercentage != nil {
   340  			minHealthyPercentage = scope.AWSMachinePool.Spec.RefreshPreferences.MinHealthyPercentage
   341  		}
   342  	}
   343  
   344  	input := &autoscaling.StartInstanceRefreshInput{
   345  		AutoScalingGroupName: aws.String(scope.Name()),
   346  		Strategy:             strategy,
   347  		Preferences: &autoscaling.RefreshPreferences{
   348  			InstanceWarmup:       instanceWarmup,
   349  			MinHealthyPercentage: minHealthyPercentage,
   350  		},
   351  	}
   352  
   353  	if _, err := s.ASGClient.StartInstanceRefresh(input); err != nil {
   354  		return errors.Wrapf(err, "failed to start ASG instance refresh %q", scope.Name())
   355  	}
   356  
   357  	return nil
   358  }
   359  
   360  func createSDKMixedInstancesPolicy(name string, i *expinfrav1.MixedInstancesPolicy) *autoscaling.MixedInstancesPolicy {
   361  	mixedInstancesPolicy := &autoscaling.MixedInstancesPolicy{
   362  		LaunchTemplate: &autoscaling.LaunchTemplate{
   363  			LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{
   364  				LaunchTemplateName: aws.String(name),
   365  				Version:            aws.String(expinfrav1.LaunchTemplateLatestVersion),
   366  			},
   367  		},
   368  	}
   369  
   370  	if i.InstancesDistribution != nil {
   371  		mixedInstancesPolicy.InstancesDistribution = &autoscaling.InstancesDistribution{
   372  			OnDemandAllocationStrategy:          aws.String(string(i.InstancesDistribution.OnDemandAllocationStrategy)),
   373  			OnDemandBaseCapacity:                i.InstancesDistribution.OnDemandBaseCapacity,
   374  			OnDemandPercentageAboveBaseCapacity: i.InstancesDistribution.OnDemandPercentageAboveBaseCapacity,
   375  			SpotAllocationStrategy:              aws.String(string(i.InstancesDistribution.SpotAllocationStrategy)),
   376  		}
   377  	}
   378  
   379  	for _, override := range i.Overrides {
   380  		mixedInstancesPolicy.LaunchTemplate.Overrides = append(mixedInstancesPolicy.LaunchTemplate.Overrides, &autoscaling.LaunchTemplateOverrides{
   381  			InstanceType: aws.String(override.InstanceType),
   382  		})
   383  	}
   384  
   385  	return mixedInstancesPolicy
   386  }
   387  
   388  // BuildTagsFromMap takes a map of keys and values and returns them as autoscaling group tags.
   389  func BuildTagsFromMap(asgName string, inTags map[string]string) []*autoscaling.Tag {
   390  	if inTags == nil {
   391  		return nil
   392  	}
   393  	tags := make([]*autoscaling.Tag, 0)
   394  	for k, v := range inTags {
   395  		tags = append(tags, &autoscaling.Tag{
   396  			Key:   aws.String(k),
   397  			Value: aws.String(v),
   398  			// We set the instance tags in the LaunchTemplate, disabling propagation to prevent the two
   399  			// resources from clobbering the tags set in the LaunchTemplate
   400  			PropagateAtLaunch: aws.Bool(false),
   401  			ResourceId:        aws.String(asgName),
   402  			ResourceType:      aws.String("auto-scaling-group"),
   403  		})
   404  	}
   405  
   406  	return tags
   407  }
   408  
   409  // UpdateResourceTags updates the tags for an autoscaling group.
   410  // This will be called if there is anything to create (update) or delete.
   411  // We may not always have to perform each action, so we check what we're
   412  // receiving to avoid calling AWS if we don't need to.
   413  func (s *Service) UpdateResourceTags(resourceID *string, create, remove map[string]string) error {
   414  	s.scope.V(2).Info("Attempting to update tags on resource", "resource-id", *resourceID)
   415  	s.scope.Info("updating tags on resource", "resource-id", *resourceID, "create", create, "remove", remove)
   416  
   417  	// If we have anything to create or update
   418  	if len(create) > 0 {
   419  		s.scope.V(2).Info("Attempting to create tags on resource", "resource-id", *resourceID)
   420  
   421  		createOrUpdateTagsInput := &autoscaling.CreateOrUpdateTagsInput{}
   422  
   423  		createOrUpdateTagsInput.Tags = mapToTags(create, resourceID)
   424  
   425  		if _, err := s.ASGClient.CreateOrUpdateTags(createOrUpdateTagsInput); err != nil {
   426  			return errors.Wrapf(err, "failed to update tags on AutoScalingGroup %q", *resourceID)
   427  		}
   428  	}
   429  
   430  	// If we have anything to remove
   431  	if len(remove) > 0 {
   432  		s.scope.V(2).Info("Attempting to delete tags on resource", "resource-id", *resourceID)
   433  
   434  		// Convert our remove map into an array of *ec2.Tag
   435  		removeTagsInput := mapToTags(remove, resourceID)
   436  
   437  		// Create the DeleteTags input
   438  		input := &autoscaling.DeleteTagsInput{
   439  			Tags: removeTagsInput,
   440  		}
   441  
   442  		// Delete tags in AWS.
   443  		if _, err := s.ASGClient.DeleteTags(input); err != nil {
   444  			return errors.Wrapf(err, "failed to delete tags on AutoScalingGroup %q: %v", *resourceID, remove)
   445  		}
   446  	}
   447  
   448  	return nil
   449  }
   450  
   451  func mapToTags(input map[string]string, resourceID *string) []*autoscaling.Tag {
   452  	tags := make([]*autoscaling.Tag, 0)
   453  	for k, v := range input {
   454  		tags = append(tags, &autoscaling.Tag{
   455  			Key:               aws.String(k),
   456  			PropagateAtLaunch: aws.Bool(false),
   457  			ResourceId:        resourceID,
   458  			ResourceType:      aws.String("auto-scaling-group"),
   459  			Value:             aws.String(v),
   460  		})
   461  	}
   462  	return tags
   463  }
   464  
   465  // SubnetIDs return subnet IDs of a AWSMachinePool based on given subnetIDs and filters.
   466  func (s *Service) SubnetIDs(scope *scope.MachinePoolScope) ([]string, error) {
   467  	subnetIDs := make([]string, 0)
   468  	var inputFilters = make([]*ec2.Filter, 0)
   469  
   470  	for _, subnet := range scope.AWSMachinePool.Spec.Subnets {
   471  		switch {
   472  		case subnet.ID != nil:
   473  			subnetIDs = append(subnetIDs, aws.StringValue(subnet.ID))
   474  		case subnet.Filters != nil:
   475  			for _, eachFilter := range subnet.Filters {
   476  				inputFilters = append(inputFilters, &ec2.Filter{
   477  					Name:   aws.String(eachFilter.Name),
   478  					Values: aws.StringSlice(eachFilter.Values),
   479  				})
   480  			}
   481  		}
   482  	}
   483  
   484  	if len(inputFilters) > 0 {
   485  		out, err := s.EC2Client.DescribeSubnets(&ec2.DescribeSubnetsInput{
   486  			Filters: inputFilters,
   487  		})
   488  		if err != nil {
   489  			return nil, err
   490  		}
   491  
   492  		for _, subnet := range out.Subnets {
   493  			subnetIDs = append(subnetIDs, *subnet.SubnetId)
   494  		}
   495  
   496  		if len(subnetIDs) == 0 {
   497  			errMessage := fmt.Sprintf("failed to create ASG %q, no subnets available matching criteria %q", scope.Name(), inputFilters)
   498  			record.Warnf(scope.AWSMachinePool, "FailedCreate", errMessage)
   499  			return subnetIDs, awserrors.NewFailedDependency(errMessage)
   500  		}
   501  	}
   502  
   503  	return scope.SubnetIDs(subnetIDs)
   504  }