sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/autoscaling/autoscalinggroup_test.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  	"sort"
    21  	"testing"
    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/golang/mock/gomock"
    27  	"github.com/google/go-cmp/cmp"
    28  	. "github.com/onsi/gomega"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    33  
    34  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    35  	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1"
    36  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors"
    37  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    38  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/autoscaling/mock_autoscalingiface"
    39  	"sigs.k8s.io/cluster-api-provider-aws/test/mocks"
    40  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    41  	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    42  )
    43  
    44  func TestService_GetASGByName(t *testing.T) {
    45  	mockCtrl := gomock.NewController(t)
    46  	defer mockCtrl.Finish()
    47  	tests := []struct {
    48  		name            string
    49  		machinePoolName string
    50  		wantErr         bool
    51  		wantASG         bool
    52  		expect          func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
    53  	}{
    54  		{
    55  			name:            "should return nil if ASG is not found",
    56  			machinePoolName: "test-asg-is-not-present",
    57  			wantErr:         false,
    58  			wantASG:         false,
    59  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
    60  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
    61  					AutoScalingGroupNames: []*string{
    62  						aws.String("test-asg-is-not-present"),
    63  					},
    64  				})).
    65  					Return(nil, awserrors.NewNotFound("not found"))
    66  			},
    67  		},
    68  		{
    69  			name:            "should return error if describe asg failed",
    70  			machinePoolName: "dependency-failure-occurred",
    71  			wantErr:         true,
    72  			wantASG:         false,
    73  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
    74  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
    75  					AutoScalingGroupNames: []*string{
    76  						aws.String("dependency-failure-occurred"),
    77  					},
    78  				})).
    79  					Return(nil, awserrors.NewFailedDependency("unknown error occurred"))
    80  			},
    81  		},
    82  		{
    83  			name:            "should return ASG, if found",
    84  			machinePoolName: "test-group-is-present",
    85  			wantErr:         false,
    86  			wantASG:         true,
    87  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
    88  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
    89  					AutoScalingGroupNames: []*string{
    90  						aws.String("test-group-is-present"),
    91  					},
    92  				})).
    93  					Return(&autoscaling.DescribeAutoScalingGroupsOutput{
    94  						AutoScalingGroups: []*autoscaling.Group{
    95  							{
    96  								AutoScalingGroupName: aws.String("test-group-is-present"),
    97  								MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
    98  									InstancesDistribution: &autoscaling.InstancesDistribution{
    99  										OnDemandAllocationStrategy: aws.String("prioritized"),
   100  									},
   101  									LaunchTemplate: &autoscaling.LaunchTemplate{},
   102  								},
   103  							},
   104  						}}, nil)
   105  			},
   106  		},
   107  	}
   108  	for _, tt := range tests {
   109  		t.Run(tt.name, func(t *testing.T) {
   110  			g := NewWithT(t)
   111  			fakeClient := getFakeClient()
   112  
   113  			clusterScope, err := getClusterScope(fakeClient)
   114  			g.Expect(err).ToNot(HaveOccurred())
   115  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   116  			tt.expect(asgMock.EXPECT())
   117  			s := NewService(clusterScope)
   118  			s.ASGClient = asgMock
   119  
   120  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
   121  			g.Expect(err).ToNot(HaveOccurred())
   122  			mps.AWSMachinePool.Name = tt.machinePoolName
   123  
   124  			asg, err := s.GetASGByName(mps)
   125  			checkErr(tt.wantErr, err, g)
   126  			checkASG(tt.wantASG, asg, g)
   127  		})
   128  	}
   129  }
   130  
   131  func TestService_SDKToAutoScalingGroup(t *testing.T) {
   132  	tests := []struct {
   133  		name    string
   134  		input   *autoscaling.Group
   135  		want    *expinfrav1.AutoScalingGroup
   136  		wantErr bool
   137  	}{
   138  		{
   139  			name: "valid input - all required fields filled",
   140  			input: &autoscaling.Group{
   141  				AutoScalingGroupARN:  aws.String("test-id"),
   142  				AutoScalingGroupName: aws.String("test-name"),
   143  				DesiredCapacity:      aws.Int64(1234),
   144  				MaxSize:              aws.Int64(1234),
   145  				MinSize:              aws.Int64(1234),
   146  				CapacityRebalance:    aws.Bool(true),
   147  				MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
   148  					InstancesDistribution: &autoscaling.InstancesDistribution{
   149  						OnDemandAllocationStrategy:          aws.String("prioritized"),
   150  						OnDemandBaseCapacity:                aws.Int64(1234),
   151  						OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
   152  						SpotAllocationStrategy:              aws.String("lowest-price"),
   153  					},
   154  					LaunchTemplate: &autoscaling.LaunchTemplate{
   155  						Overrides: []*autoscaling.LaunchTemplateOverrides{
   156  							{
   157  								InstanceType:     aws.String("t2.medium"),
   158  								WeightedCapacity: aws.String("test-weighted-cap"),
   159  							},
   160  						},
   161  					},
   162  				},
   163  			},
   164  			want: &expinfrav1.AutoScalingGroup{
   165  				ID:                "test-id",
   166  				Name:              "test-name",
   167  				DesiredCapacity:   aws.Int32(1234),
   168  				MaxSize:           int32(1234),
   169  				MinSize:           int32(1234),
   170  				CapacityRebalance: true,
   171  				MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
   172  					InstancesDistribution: &expinfrav1.InstancesDistribution{
   173  						OnDemandAllocationStrategy:          expinfrav1.OnDemandAllocationStrategyPrioritized,
   174  						OnDemandBaseCapacity:                aws.Int64(1234),
   175  						OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
   176  						SpotAllocationStrategy:              expinfrav1.SpotAllocationStrategyLowestPrice,
   177  					},
   178  					Overrides: []expinfrav1.Overrides{
   179  						{
   180  							InstanceType: "t2.medium",
   181  						},
   182  					},
   183  				},
   184  			},
   185  			wantErr: false,
   186  		},
   187  		{
   188  			name: "valid input - all fields filled",
   189  			input: &autoscaling.Group{
   190  				AutoScalingGroupARN:  aws.String("test-id"),
   191  				AutoScalingGroupName: aws.String("test-name"),
   192  				DesiredCapacity:      aws.Int64(1234),
   193  				MaxSize:              aws.Int64(1234),
   194  				MinSize:              aws.Int64(1234),
   195  				CapacityRebalance:    aws.Bool(true),
   196  				MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
   197  					InstancesDistribution: &autoscaling.InstancesDistribution{
   198  						OnDemandAllocationStrategy:          aws.String("prioritized"),
   199  						OnDemandBaseCapacity:                aws.Int64(1234),
   200  						OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
   201  						SpotAllocationStrategy:              aws.String("lowest-price"),
   202  					},
   203  					LaunchTemplate: &autoscaling.LaunchTemplate{
   204  						Overrides: []*autoscaling.LaunchTemplateOverrides{
   205  							{
   206  								InstanceType:     aws.String("t2.medium"),
   207  								WeightedCapacity: aws.String("test-weighted-cap"),
   208  							},
   209  						},
   210  					},
   211  				},
   212  				Status: aws.String("status"),
   213  				Tags: []*autoscaling.TagDescription{
   214  					{
   215  						Key:   aws.String("key"),
   216  						Value: aws.String("value"),
   217  					},
   218  				},
   219  				Instances: []*autoscaling.Instance{
   220  					{
   221  						InstanceId:       aws.String("instanceId"),
   222  						LifecycleState:   aws.String("lifecycleState"),
   223  						AvailabilityZone: aws.String("us-east-1a"),
   224  					},
   225  				},
   226  			},
   227  			want: &expinfrav1.AutoScalingGroup{
   228  				ID:                "test-id",
   229  				Name:              "test-name",
   230  				DesiredCapacity:   aws.Int32(1234),
   231  				MaxSize:           int32(1234),
   232  				MinSize:           int32(1234),
   233  				CapacityRebalance: true,
   234  				MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
   235  					InstancesDistribution: &expinfrav1.InstancesDistribution{
   236  						OnDemandAllocationStrategy:          expinfrav1.OnDemandAllocationStrategyPrioritized,
   237  						OnDemandBaseCapacity:                aws.Int64(1234),
   238  						OnDemandPercentageAboveBaseCapacity: aws.Int64(1234),
   239  						SpotAllocationStrategy:              expinfrav1.SpotAllocationStrategyLowestPrice,
   240  					},
   241  					Overrides: []expinfrav1.Overrides{
   242  						{
   243  							InstanceType: "t2.medium",
   244  						},
   245  					},
   246  				},
   247  				Status: "status",
   248  				Tags: map[string]string{
   249  					"key": "value",
   250  				},
   251  				Instances: []infrav1.Instance{
   252  					{
   253  						ID:               "instanceId",
   254  						State:            "lifecycleState",
   255  						AvailabilityZone: "us-east-1a",
   256  					},
   257  				},
   258  			},
   259  			wantErr: false,
   260  		},
   261  		{
   262  			name: "valid input - without mixedInstancesPolicy",
   263  			input: &autoscaling.Group{
   264  				AutoScalingGroupARN:  aws.String("test-id"),
   265  				AutoScalingGroupName: aws.String("test-name"),
   266  				DesiredCapacity:      aws.Int64(1234),
   267  				MaxSize:              aws.Int64(1234),
   268  				MinSize:              aws.Int64(1234),
   269  				CapacityRebalance:    aws.Bool(true),
   270  				MixedInstancesPolicy: nil,
   271  			},
   272  			want: &expinfrav1.AutoScalingGroup{
   273  				ID:                   "test-id",
   274  				Name:                 "test-name",
   275  				DesiredCapacity:      aws.Int32(1234),
   276  				MaxSize:              int32(1234),
   277  				MinSize:              int32(1234),
   278  				CapacityRebalance:    true,
   279  				MixedInstancesPolicy: nil,
   280  			},
   281  			wantErr: false,
   282  		},
   283  	}
   284  	for _, tt := range tests {
   285  		t.Run(tt.name, func(t *testing.T) {
   286  			s := &Service{}
   287  			got, err := s.SDKToAutoScalingGroup(tt.input)
   288  			if (err != nil) != tt.wantErr {
   289  				t.Errorf("Service.SDKToAutoScalingGroup() error = %v, wantErr %v", err, tt.wantErr)
   290  				return
   291  			}
   292  			if !cmp.Equal(got, tt.want) {
   293  				t.Errorf("Service.SDKToAutoScalingGroup() = %v, want %v", got, tt.want)
   294  			}
   295  		})
   296  	}
   297  }
   298  
   299  func TestService_ASGIfExists(t *testing.T) {
   300  	mockCtrl := gomock.NewController(t)
   301  	defer mockCtrl.Finish()
   302  
   303  	tests := []struct {
   304  		name    string
   305  		asgName *string
   306  		wantErr bool
   307  		wantASG bool
   308  		expect  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   309  	}{
   310  		{
   311  			name:    "should return nil if ASG name is not given",
   312  			asgName: nil,
   313  			wantErr: false,
   314  			wantASG: false,
   315  			expect:  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {},
   316  		},
   317  		{
   318  			name:    "should return without error if ASG is not found",
   319  			asgName: aws.String("asgName"),
   320  			wantErr: false,
   321  			wantASG: false,
   322  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   323  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
   324  					AutoScalingGroupNames: []*string{
   325  						aws.String("asgName"),
   326  					},
   327  				})).
   328  					Return(nil, awserrors.NewNotFound("resource not found"))
   329  			},
   330  		},
   331  		{
   332  			name:    "should return error if describe ASG fails",
   333  			asgName: aws.String("asgName"),
   334  			wantErr: true,
   335  			wantASG: false,
   336  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   337  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
   338  					AutoScalingGroupNames: []*string{
   339  						aws.String("asgName"),
   340  					},
   341  				})).
   342  					Return(nil, awserrors.NewFailedDependency("unknown error occurred"))
   343  			},
   344  		},
   345  		{
   346  			name:    "should return ASG, if found",
   347  			asgName: aws.String("asgName"),
   348  			wantErr: false,
   349  			wantASG: true,
   350  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   351  				m.DescribeAutoScalingGroups(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
   352  					AutoScalingGroupNames: []*string{
   353  						aws.String("asgName"),
   354  					},
   355  				})).
   356  					Return(&autoscaling.DescribeAutoScalingGroupsOutput{
   357  						AutoScalingGroups: []*autoscaling.Group{
   358  							{
   359  								AutoScalingGroupName: aws.String("asgName"),
   360  								MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
   361  									InstancesDistribution: &autoscaling.InstancesDistribution{
   362  										OnDemandAllocationStrategy: aws.String("prioritized"),
   363  									},
   364  									LaunchTemplate: &autoscaling.LaunchTemplate{},
   365  								},
   366  							},
   367  						}}, nil)
   368  			},
   369  		},
   370  	}
   371  	for _, tt := range tests {
   372  		t.Run(tt.name, func(t *testing.T) {
   373  			g := NewWithT(t)
   374  			fakeClient := getFakeClient()
   375  
   376  			clusterScope, err := getClusterScope(fakeClient)
   377  			g.Expect(err).ToNot(HaveOccurred())
   378  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   379  			tt.expect(asgMock.EXPECT())
   380  			s := NewService(clusterScope)
   381  			s.ASGClient = asgMock
   382  
   383  			asg, err := s.ASGIfExists(tt.asgName)
   384  			checkErr(tt.wantErr, err, g)
   385  			checkASG(tt.wantASG, asg, g)
   386  		})
   387  	}
   388  }
   389  
   390  func TestService_CreateASG(t *testing.T) {
   391  	mockCtrl := gomock.NewController(t)
   392  	defer mockCtrl.Finish()
   393  	tests := []struct {
   394  		name                  string
   395  		machinePoolName       string
   396  		setupMachinePoolScope func(*scope.MachinePoolScope)
   397  		wantErr               bool
   398  		wantASG               bool
   399  		expect                func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   400  	}{
   401  		{
   402  			name:                  "should return without error if create ASG is successful",
   403  			machinePoolName:       "create-asg-success",
   404  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {},
   405  			wantErr:               false,
   406  			wantASG:               false,
   407  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   408  				expected := &autoscaling.CreateAutoScalingGroupInput{
   409  					AutoScalingGroupName: aws.String("create-asg-success"),
   410  					CapacityRebalance:    aws.Bool(false),
   411  					DefaultCooldown:      aws.Int64(0),
   412  					MixedInstancesPolicy: &autoscaling.MixedInstancesPolicy{
   413  						InstancesDistribution: &autoscaling.InstancesDistribution{
   414  							OnDemandAllocationStrategy:          aws.String("prioritized"),
   415  							OnDemandBaseCapacity:                aws.Int64(0),
   416  							OnDemandPercentageAboveBaseCapacity: aws.Int64(100),
   417  							SpotAllocationStrategy:              aws.String(""),
   418  						},
   419  						LaunchTemplate: &autoscaling.LaunchTemplate{
   420  							LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{
   421  								LaunchTemplateName: aws.String("create-asg-success"),
   422  								Version:            aws.String("$Latest"),
   423  							},
   424  							Overrides: []*autoscaling.LaunchTemplateOverrides{
   425  								{
   426  									InstanceType: aws.String("t1.large"),
   427  								},
   428  							},
   429  						},
   430  					},
   431  					MaxSize: aws.Int64(0),
   432  					MinSize: aws.Int64(0),
   433  					Tags: []*autoscaling.Tag{
   434  						{
   435  							Key:               aws.String("kubernetes.io/cluster/test"),
   436  							PropagateAtLaunch: aws.Bool(false),
   437  							ResourceId:        aws.String("create-asg-success"),
   438  							ResourceType:      aws.String("auto-scaling-group"),
   439  							Value:             aws.String("owned"),
   440  						},
   441  						{
   442  							Key:               aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test"),
   443  							PropagateAtLaunch: aws.Bool(false),
   444  							ResourceId:        aws.String("create-asg-success"),
   445  							ResourceType:      aws.String("auto-scaling-group"),
   446  							Value:             aws.String("owned"),
   447  						},
   448  						{
   449  							Key:               aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   450  							PropagateAtLaunch: aws.Bool(false),
   451  							ResourceId:        aws.String("create-asg-success"),
   452  							ResourceType:      aws.String("auto-scaling-group"),
   453  							Value:             aws.String("node"),
   454  						},
   455  						{
   456  							Key:               aws.String("Name"),
   457  							PropagateAtLaunch: aws.Bool(false),
   458  							ResourceId:        aws.String("create-asg-success"),
   459  							ResourceType:      aws.String("auto-scaling-group"),
   460  							Value:             aws.String("create-asg-success"),
   461  						},
   462  					},
   463  					VPCZoneIdentifier: aws.String("subnet1"),
   464  				}
   465  
   466  				m.CreateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.CreateAutoScalingGroupInput{})).Do(
   467  					func(actual *autoscaling.CreateAutoScalingGroupInput) (*autoscaling.CreateAutoScalingGroupOutput, error) {
   468  						sortTagsByKey := func(tags []*autoscaling.Tag) {
   469  							sort.Slice(tags, func(i, j int) bool {
   470  								return *(tags[i].Key) < *(tags[j].Key)
   471  							})
   472  						}
   473  						// sorting tags to avoid failure due to different ordering of tags
   474  						sortTagsByKey(actual.Tags)
   475  						sortTagsByKey(expected.Tags)
   476  						if !cmp.Equal(expected, actual) {
   477  							t.Fatalf("Actual CreateAutoScalingGroupInput did not match expected, Actual : %v, Expected: %v", actual, expected)
   478  						}
   479  						return &autoscaling.CreateAutoScalingGroupOutput{}, nil
   480  					})
   481  			},
   482  		},
   483  		{
   484  			name:            "should return error if subnet not found for asg",
   485  			machinePoolName: "create-asg-fail",
   486  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {
   487  				mps.AWSMachinePool.Spec.Subnets = nil
   488  				mps.InfraCluster.(*scope.ClusterScope).AWSCluster.Spec.NetworkSpec.Subnets = nil
   489  			},
   490  			wantErr: true,
   491  			wantASG: false,
   492  			expect:  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {},
   493  		},
   494  		{
   495  			name:            "should return error if create ASG fails",
   496  			machinePoolName: "create-asg-fail",
   497  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {
   498  				mps.AWSMachinePool.Spec.MixedInstancesPolicy = nil
   499  			},
   500  			wantErr: true,
   501  			wantASG: false,
   502  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   503  				m.CreateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.CreateAutoScalingGroupInput{})).Return(nil, awserrors.NewFailedDependency("dependency failure"))
   504  			},
   505  		},
   506  		{
   507  			name:            "should return error if launch template is missing",
   508  			machinePoolName: "create-asg-fail",
   509  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {
   510  				mps.AWSMachinePool.Spec.MixedInstancesPolicy = nil
   511  				mps.AWSMachinePool.Status.LaunchTemplateID = ""
   512  			},
   513  			wantErr: true,
   514  			wantASG: false,
   515  			expect:  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {},
   516  		},
   517  	}
   518  	for _, tt := range tests {
   519  		t.Run(tt.name, func(t *testing.T) {
   520  			g := NewWithT(t)
   521  			fakeClient := getFakeClient()
   522  
   523  			clusterScope, err := getClusterScope(fakeClient)
   524  			g.Expect(err).ToNot(HaveOccurred())
   525  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   526  			tt.expect(asgMock.EXPECT())
   527  			s := NewService(clusterScope)
   528  			s.ASGClient = asgMock
   529  
   530  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
   531  			g.Expect(err).ToNot(HaveOccurred())
   532  			mps.AWSMachinePool.Name = tt.machinePoolName
   533  			tt.setupMachinePoolScope(mps)
   534  			asg, err := s.CreateASG(mps)
   535  			checkErr(tt.wantErr, err, g)
   536  			checkASG(tt.wantASG, asg, g)
   537  		})
   538  	}
   539  }
   540  
   541  func TestService_UpdateASG(t *testing.T) {
   542  	mockCtrl := gomock.NewController(t)
   543  	defer mockCtrl.Finish()
   544  
   545  	tests := []struct {
   546  		name                  string
   547  		machinePoolName       string
   548  		setupMachinePoolScope func(*scope.MachinePoolScope)
   549  		wantErr               bool
   550  		expect                func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   551  	}{
   552  		{
   553  			name:            "should return without error if update ASG is successful",
   554  			machinePoolName: "update-asg-success",
   555  			wantErr:         false,
   556  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {
   557  				mps.AWSMachinePool.Spec.Subnets = nil
   558  			},
   559  			expect: func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   560  				m.UpdateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.UpdateAutoScalingGroupInput{})).Return(&autoscaling.UpdateAutoScalingGroupOutput{}, nil)
   561  			},
   562  		},
   563  		{
   564  			name:            "should return error if update ASG fails",
   565  			machinePoolName: "update-asg-fail",
   566  			wantErr:         true,
   567  			setupMachinePoolScope: func(mps *scope.MachinePoolScope) {
   568  				mps.AWSMachinePool.Spec.MixedInstancesPolicy = nil
   569  			},
   570  			expect: func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   571  				m.UpdateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.UpdateAutoScalingGroupInput{})).Return(nil, awserrors.NewFailedDependency("dependency failure"))
   572  			},
   573  		},
   574  	}
   575  	for _, tt := range tests {
   576  		t.Run(tt.name, func(t *testing.T) {
   577  			g := NewWithT(t)
   578  			fakeClient := getFakeClient()
   579  
   580  			clusterScope, err := getClusterScope(fakeClient)
   581  			g.Expect(err).ToNot(HaveOccurred())
   582  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
   583  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   584  			tt.expect(ec2Mock.EXPECT(), asgMock.EXPECT())
   585  			s := NewService(clusterScope)
   586  			s.ASGClient = asgMock
   587  
   588  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
   589  			g.Expect(err).ToNot(HaveOccurred())
   590  			mps.AWSMachinePool.Name = tt.machinePoolName
   591  
   592  			err = s.UpdateASG(mps)
   593  			checkErr(tt.wantErr, err, g)
   594  		})
   595  	}
   596  }
   597  
   598  func TestService_UpdateASGWithSubnetFilters(t *testing.T) {
   599  	mockCtrl := gomock.NewController(t)
   600  	defer mockCtrl.Finish()
   601  
   602  	tests := []struct {
   603  		name                 string
   604  		machinePoolName      string
   605  		awsResourceReference []infrav1.AWSResourceReference
   606  		wantErr              bool
   607  		expect               func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   608  	}{
   609  		{
   610  			name:            "should return without error if update ASG is successful",
   611  			machinePoolName: "update-asg-success",
   612  			wantErr:         false,
   613  			awsResourceReference: []infrav1.AWSResourceReference{
   614  				{
   615  					Filters: []infrav1.Filter{{Name: "availability-zone", Values: []string{"us-east-1a"}}},
   616  				},
   617  			},
   618  			expect: func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   619  				e.DescribeSubnets(gomock.AssignableToTypeOf(&ec2.DescribeSubnetsInput{})).Return(&ec2.DescribeSubnetsOutput{
   620  					Subnets: []*ec2.Subnet{{SubnetId: aws.String("subnet-02")}},
   621  				}, nil)
   622  				m.UpdateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.UpdateAutoScalingGroupInput{})).Return(&autoscaling.UpdateAutoScalingGroupOutput{}, nil)
   623  			},
   624  		},
   625  		{
   626  			name:            "should return an error if no matching subnets found",
   627  			machinePoolName: "update-asg-fail",
   628  			wantErr:         true,
   629  			awsResourceReference: []infrav1.AWSResourceReference{
   630  				{
   631  					Filters: []infrav1.Filter{
   632  						{
   633  							Name:   "tag:subnet-role",
   634  							Values: []string{"non-existent"},
   635  						},
   636  					},
   637  				},
   638  			},
   639  			expect: func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   640  				e.DescribeSubnets(gomock.AssignableToTypeOf(&ec2.DescribeSubnetsInput{})).Return(&ec2.DescribeSubnetsOutput{
   641  					Subnets: []*ec2.Subnet{},
   642  				}, nil)
   643  			},
   644  		},
   645  		{
   646  			name:            "should return error if update ASG fails",
   647  			machinePoolName: "update-asg-fail",
   648  			wantErr:         true,
   649  			awsResourceReference: []infrav1.AWSResourceReference{
   650  				{
   651  					ID: aws.String("subnet-01"),
   652  				},
   653  			},
   654  			expect: func(e *mocks.MockEC2APIMockRecorder, m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   655  				m.UpdateAutoScalingGroup(gomock.AssignableToTypeOf(&autoscaling.UpdateAutoScalingGroupInput{})).Return(nil, awserrors.NewFailedDependency("dependency failure"))
   656  			},
   657  		},
   658  	}
   659  	for _, tt := range tests {
   660  		t.Run(tt.name, func(t *testing.T) {
   661  			g := NewWithT(t)
   662  			fakeClient := getFakeClient()
   663  
   664  			clusterScope, err := getClusterScope(fakeClient)
   665  			g.Expect(err).ToNot(HaveOccurred())
   666  
   667  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
   668  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   669  			if tt.expect != nil {
   670  				tt.expect(ec2Mock.EXPECT(), asgMock.EXPECT())
   671  			}
   672  			s := NewService(clusterScope)
   673  			s.ASGClient = asgMock
   674  			s.EC2Client = ec2Mock
   675  
   676  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
   677  			g.Expect(err).ToNot(HaveOccurred())
   678  			mps.AWSMachinePool.Name = tt.machinePoolName
   679  			mps.AWSMachinePool.Spec.Subnets = tt.awsResourceReference
   680  
   681  			err = s.UpdateASG(mps)
   682  			checkErr(tt.wantErr, err, g)
   683  		})
   684  	}
   685  }
   686  
   687  func TestService_UpdateResourceTags(t *testing.T) {
   688  	mockCtrl := gomock.NewController(t)
   689  	defer mockCtrl.Finish()
   690  
   691  	type args struct {
   692  		resourceID *string
   693  		create     map[string]string
   694  		remove     map[string]string
   695  	}
   696  
   697  	tests := []struct {
   698  		name    string
   699  		args    args
   700  		wantErr bool
   701  		expect  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   702  	}{
   703  		{
   704  			name: "should return nil if nothing to update",
   705  			args: args{
   706  				resourceID: aws.String("mock-resource-id"),
   707  			},
   708  			wantErr: false,
   709  			expect:  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {},
   710  		},
   711  		{
   712  			name: "should create tags if new tags are passed",
   713  			args: args{
   714  				resourceID: aws.String("mock-resource-id"),
   715  				create: map[string]string{
   716  					"key1": "value1",
   717  				},
   718  			},
   719  			wantErr: false,
   720  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   721  				m.CreateOrUpdateTags(gomock.Eq(&autoscaling.CreateOrUpdateTagsInput{
   722  					Tags: mapToTags(map[string]string{
   723  						"key1": "value1",
   724  					}, aws.String("mock-resource-id")),
   725  				})).
   726  					Return(nil, nil)
   727  			},
   728  		},
   729  		{
   730  			name: "should return error if new tags creation failed",
   731  			args: args{
   732  				resourceID: aws.String("mock-resource-id"),
   733  				create: map[string]string{
   734  					"key1": "value1",
   735  				},
   736  			},
   737  			wantErr: true,
   738  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   739  				m.CreateOrUpdateTags(gomock.Eq(&autoscaling.CreateOrUpdateTagsInput{
   740  					Tags: mapToTags(map[string]string{
   741  						"key1": "value1",
   742  					}, aws.String("mock-resource-id")),
   743  				})).
   744  					Return(nil, awserrors.NewNotFound("not found"))
   745  			},
   746  		},
   747  		{
   748  			name: "should remove tags successfully if tags to be deleted",
   749  			args: args{
   750  				resourceID: aws.String("mock-resource-id"),
   751  				remove: map[string]string{
   752  					"key1": "value1",
   753  				},
   754  			},
   755  			wantErr: false,
   756  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   757  				m.DeleteTags(gomock.Eq(&autoscaling.DeleteTagsInput{
   758  					Tags: mapToTags(map[string]string{
   759  						"key1": "value1",
   760  					}, aws.String("mock-resource-id")),
   761  				})).
   762  					Return(nil, nil)
   763  			},
   764  		},
   765  		{
   766  			name: "should return error if removing existing tags failed",
   767  			args: args{
   768  				resourceID: aws.String("mock-resource-id"),
   769  				remove: map[string]string{
   770  					"key1": "value1",
   771  				},
   772  			},
   773  			wantErr: true,
   774  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   775  				m.DeleteTags(gomock.Eq(&autoscaling.DeleteTagsInput{
   776  					Tags: mapToTags(map[string]string{
   777  						"key1": "value1",
   778  					}, aws.String("mock-resource-id")),
   779  				})).
   780  					Return(nil, awserrors.NewNotFound("not found"))
   781  			},
   782  		},
   783  	}
   784  
   785  	for _, tt := range tests {
   786  		t.Run(tt.name, func(t *testing.T) {
   787  			g := NewWithT(t)
   788  			fakeClient := getFakeClient()
   789  
   790  			clusterScope, err := getClusterScope(fakeClient)
   791  			g.Expect(err).ToNot(HaveOccurred())
   792  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   793  			tt.expect(asgMock.EXPECT())
   794  			s := NewService(clusterScope)
   795  			s.ASGClient = asgMock
   796  
   797  			err = s.UpdateResourceTags(tt.args.resourceID, tt.args.create, tt.args.remove)
   798  			checkErr(tt.wantErr, err, g)
   799  		})
   800  	}
   801  }
   802  
   803  func TestService_DeleteASG(t *testing.T) {
   804  	mockCtrl := gomock.NewController(t)
   805  	defer mockCtrl.Finish()
   806  
   807  	tests := []struct {
   808  		name    string
   809  		wantErr bool
   810  		expect  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   811  	}{
   812  		{
   813  			name:    "Delete ASG successful",
   814  			wantErr: false,
   815  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   816  				m.DeleteAutoScalingGroup(gomock.Eq(&autoscaling.DeleteAutoScalingGroupInput{
   817  					AutoScalingGroupName: aws.String("asgName"),
   818  					ForceDelete:          aws.Bool(true),
   819  				})).
   820  					Return(nil, nil)
   821  			},
   822  		},
   823  		{
   824  			name:    "Delete ASG should fail when ASG is not found",
   825  			wantErr: true,
   826  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   827  				m.DeleteAutoScalingGroup(gomock.Eq(&autoscaling.DeleteAutoScalingGroupInput{
   828  					AutoScalingGroupName: aws.String("asgName"),
   829  					ForceDelete:          aws.Bool(true),
   830  				})).
   831  					Return(nil, awserrors.NewNotFound("not found"))
   832  			},
   833  		},
   834  	}
   835  
   836  	for _, tt := range tests {
   837  		t.Run(tt.name, func(t *testing.T) {
   838  			g := NewWithT(t)
   839  			fakeClient := getFakeClient()
   840  
   841  			clusterScope, err := getClusterScope(fakeClient)
   842  			g.Expect(err).ToNot(HaveOccurred())
   843  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   844  			tt.expect(asgMock.EXPECT())
   845  			s := NewService(clusterScope)
   846  			s.ASGClient = asgMock
   847  
   848  			err = s.DeleteASG("asgName")
   849  			checkErr(tt.wantErr, err, g)
   850  		})
   851  	}
   852  }
   853  
   854  func TestService_DeleteASGAndWait(t *testing.T) {
   855  	mockCtrl := gomock.NewController(t)
   856  	defer mockCtrl.Finish()
   857  
   858  	tests := []struct {
   859  		name    string
   860  		wantErr bool
   861  		expect  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   862  	}{
   863  		{
   864  			name:    "Delete ASG with wait passed",
   865  			wantErr: false,
   866  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   867  				m.DeleteAutoScalingGroup(gomock.Eq(&autoscaling.DeleteAutoScalingGroupInput{
   868  					AutoScalingGroupName: aws.String("asgName"),
   869  					ForceDelete:          aws.Bool(true),
   870  				})).
   871  					Return(nil, nil)
   872  				m.WaitUntilGroupNotExists(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
   873  					AutoScalingGroupNames: aws.StringSlice([]string{"asgName"}),
   874  				})).
   875  					Return(nil)
   876  			},
   877  		},
   878  		{
   879  			name:    "should return error if delete ASG failed while waiting",
   880  			wantErr: true,
   881  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   882  				m.DeleteAutoScalingGroup(gomock.Eq(&autoscaling.DeleteAutoScalingGroupInput{
   883  					AutoScalingGroupName: aws.String("asgName"),
   884  					ForceDelete:          aws.Bool(true),
   885  				})).
   886  					Return(nil, nil)
   887  				m.WaitUntilGroupNotExists(gomock.Eq(&autoscaling.DescribeAutoScalingGroupsInput{
   888  					AutoScalingGroupNames: aws.StringSlice([]string{"asgName"}),
   889  				})).
   890  					Return(awserrors.NewFailedDependency("dependency error"))
   891  			},
   892  		},
   893  		{
   894  			name:    "should return error if delete ASG failed during ASG deletion",
   895  			wantErr: true,
   896  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   897  				m.DeleteAutoScalingGroup(gomock.Eq(&autoscaling.DeleteAutoScalingGroupInput{
   898  					AutoScalingGroupName: aws.String("asgName"),
   899  					ForceDelete:          aws.Bool(true),
   900  				})).
   901  					Return(nil, awserrors.NewNotFound("not found"))
   902  			},
   903  		},
   904  	}
   905  
   906  	for _, tt := range tests {
   907  		t.Run(tt.name, func(t *testing.T) {
   908  			g := NewWithT(t)
   909  			fakeClient := getFakeClient()
   910  
   911  			clusterScope, err := getClusterScope(fakeClient)
   912  			g.Expect(err).ToNot(HaveOccurred())
   913  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   914  			tt.expect(asgMock.EXPECT())
   915  			s := NewService(clusterScope)
   916  			s.ASGClient = asgMock
   917  
   918  			err = s.DeleteASGAndWait("asgName")
   919  			checkErr(tt.wantErr, err, g)
   920  		})
   921  	}
   922  }
   923  
   924  func TestService_CanStartASGInstanceRefresh(t *testing.T) {
   925  	mockCtrl := gomock.NewController(t)
   926  	defer mockCtrl.Finish()
   927  
   928  	tests := []struct {
   929  		name     string
   930  		wantErr  bool
   931  		canStart bool
   932  		expect   func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
   933  	}{
   934  		{
   935  			name:     "should return error if describe instance refresh failed",
   936  			wantErr:  true,
   937  			canStart: false,
   938  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   939  				m.DescribeInstanceRefreshes(gomock.Eq(&autoscaling.DescribeInstanceRefreshesInput{
   940  					AutoScalingGroupName: aws.String("machinePoolName"),
   941  				})).
   942  					Return(nil, awserrors.NewNotFound("not found"))
   943  			},
   944  		},
   945  		{
   946  			name:     "should return true if no instance available for refresh",
   947  			wantErr:  false,
   948  			canStart: true,
   949  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   950  				m.DescribeInstanceRefreshes(gomock.Eq(&autoscaling.DescribeInstanceRefreshesInput{
   951  					AutoScalingGroupName: aws.String("machinePoolName"),
   952  				})).
   953  					Return(&autoscaling.DescribeInstanceRefreshesOutput{}, nil)
   954  			},
   955  		},
   956  		{
   957  			name:     "should return false if some instances have unfinished refresh",
   958  			wantErr:  false,
   959  			canStart: false,
   960  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
   961  				m.DescribeInstanceRefreshes(gomock.Eq(&autoscaling.DescribeInstanceRefreshesInput{
   962  					AutoScalingGroupName: aws.String("machinePoolName"),
   963  				})).
   964  					Return(&autoscaling.DescribeInstanceRefreshesOutput{
   965  						InstanceRefreshes: []*autoscaling.InstanceRefresh{
   966  							{
   967  								Status: aws.String(autoscaling.InstanceRefreshStatusInProgress),
   968  							},
   969  						},
   970  					}, nil)
   971  			},
   972  		},
   973  	}
   974  
   975  	for _, tt := range tests {
   976  		t.Run(tt.name, func(t *testing.T) {
   977  			g := NewWithT(t)
   978  			fakeClient := getFakeClient()
   979  
   980  			clusterScope, err := getClusterScope(fakeClient)
   981  			g.Expect(err).ToNot(HaveOccurred())
   982  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
   983  			tt.expect(asgMock.EXPECT())
   984  			s := NewService(clusterScope)
   985  			s.ASGClient = asgMock
   986  
   987  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
   988  			g.Expect(err).ToNot(HaveOccurred())
   989  			mps.AWSMachinePool.Name = "machinePoolName"
   990  
   991  			out, err := s.CanStartASGInstanceRefresh(mps)
   992  			checkErr(tt.wantErr, err, g)
   993  			if tt.canStart {
   994  				g.Expect(out).To(BeTrue())
   995  				return
   996  			}
   997  			g.Expect(out).To(BeFalse())
   998  		})
   999  	}
  1000  }
  1001  
  1002  func TestService_StartASGInstanceRefresh(t *testing.T) {
  1003  	mockCtrl := gomock.NewController(t)
  1004  	defer mockCtrl.Finish()
  1005  
  1006  	tests := []struct {
  1007  		name    string
  1008  		wantErr bool
  1009  		expect  func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder)
  1010  	}{
  1011  		{
  1012  			name:    "should return error if start instance refresh failed",
  1013  			wantErr: true,
  1014  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
  1015  				m.StartInstanceRefresh(gomock.Eq(&autoscaling.StartInstanceRefreshInput{
  1016  					AutoScalingGroupName: aws.String("mpn"),
  1017  					Strategy:             aws.String("Rolling"),
  1018  					Preferences: &autoscaling.RefreshPreferences{
  1019  						InstanceWarmup:       aws.Int64(100),
  1020  						MinHealthyPercentage: aws.Int64(80),
  1021  					},
  1022  				})).
  1023  					Return(nil, awserrors.NewNotFound("not found"))
  1024  			},
  1025  		},
  1026  		{
  1027  			name:    "should return nil if start instance refresh is success",
  1028  			wantErr: false,
  1029  			expect: func(m *mock_autoscalingiface.MockAutoScalingAPIMockRecorder) {
  1030  				m.StartInstanceRefresh(gomock.Eq(&autoscaling.StartInstanceRefreshInput{
  1031  					AutoScalingGroupName: aws.String("mpn"),
  1032  					Strategy:             aws.String("Rolling"),
  1033  					Preferences: &autoscaling.RefreshPreferences{
  1034  						InstanceWarmup:       aws.Int64(100),
  1035  						MinHealthyPercentage: aws.Int64(80),
  1036  					},
  1037  				})).
  1038  					Return(&autoscaling.StartInstanceRefreshOutput{}, nil)
  1039  			},
  1040  		},
  1041  	}
  1042  
  1043  	for _, tt := range tests {
  1044  		t.Run(tt.name, func(t *testing.T) {
  1045  			g := NewWithT(t)
  1046  			fakeClient := getFakeClient()
  1047  
  1048  			clusterScope, err := getClusterScope(fakeClient)
  1049  			g.Expect(err).ToNot(HaveOccurred())
  1050  			asgMock := mock_autoscalingiface.NewMockAutoScalingAPI(mockCtrl)
  1051  			tt.expect(asgMock.EXPECT())
  1052  			s := NewService(clusterScope)
  1053  			s.ASGClient = asgMock
  1054  
  1055  			mps, err := getMachinePoolScope(fakeClient, clusterScope)
  1056  			g.Expect(err).ToNot(HaveOccurred())
  1057  			mps.AWSMachinePool.Name = "mpn"
  1058  
  1059  			err = s.StartASGInstanceRefresh(mps)
  1060  			checkErr(tt.wantErr, err, g)
  1061  		})
  1062  	}
  1063  }
  1064  
  1065  func getFakeClient() client.Client {
  1066  	scheme := runtime.NewScheme()
  1067  	_ = infrav1.AddToScheme(scheme)
  1068  	_ = expinfrav1.AddToScheme(scheme)
  1069  	_ = expclusterv1.AddToScheme(scheme)
  1070  	return fake.NewClientBuilder().WithScheme(scheme).Build()
  1071  }
  1072  
  1073  func checkErr(wantErr bool, err error, g *WithT) {
  1074  	if wantErr {
  1075  		g.Expect(err).To(HaveOccurred())
  1076  		return
  1077  	}
  1078  	g.Expect(err).To(BeNil())
  1079  }
  1080  
  1081  func checkASG(wantASG bool, asg *expinfrav1.AutoScalingGroup, g *WithT) {
  1082  	if wantASG {
  1083  		g.Expect(asg).To(Not(BeNil()))
  1084  		return
  1085  	}
  1086  	g.Expect(asg).To(BeNil())
  1087  }
  1088  
  1089  func getClusterScope(client client.Client) (*scope.ClusterScope, error) {
  1090  	cluster := &clusterv1.Cluster{
  1091  		ObjectMeta: metav1.ObjectMeta{
  1092  			Name: "test",
  1093  		},
  1094  	}
  1095  	cs, err := scope.NewClusterScope(scope.ClusterScopeParams{
  1096  		Client:  client,
  1097  		Cluster: cluster,
  1098  		AWSCluster: &infrav1.AWSCluster{
  1099  			Spec: infrav1.AWSClusterSpec{
  1100  				NetworkSpec: infrav1.NetworkSpec{
  1101  					Subnets: []infrav1.SubnetSpec{
  1102  						{
  1103  							ID: "subnetId",
  1104  						},
  1105  					},
  1106  				},
  1107  			},
  1108  		},
  1109  	})
  1110  	if err != nil {
  1111  		return nil, err
  1112  	}
  1113  	return cs, nil
  1114  }
  1115  
  1116  func getMachinePoolScope(client client.Client, clusterScope *scope.ClusterScope) (*scope.MachinePoolScope, error) {
  1117  	awsMachinePool := &expinfrav1.AWSMachinePool{
  1118  		Spec: expinfrav1.AWSMachinePoolSpec{
  1119  			Subnets: []infrav1.AWSResourceReference{
  1120  				{
  1121  					ID: aws.String("subnet1"),
  1122  				},
  1123  			},
  1124  			RefreshPreferences: &expinfrav1.RefreshPreferences{
  1125  				Strategy:             aws.String("Rolling"),
  1126  				InstanceWarmup:       aws.Int64(100),
  1127  				MinHealthyPercentage: aws.Int64(80),
  1128  			},
  1129  			MixedInstancesPolicy: &expinfrav1.MixedInstancesPolicy{
  1130  				InstancesDistribution: &expinfrav1.InstancesDistribution{
  1131  					OnDemandAllocationStrategy:          "prioritized",
  1132  					OnDemandBaseCapacity:                aws.Int64(0),
  1133  					OnDemandPercentageAboveBaseCapacity: aws.Int64(100),
  1134  				},
  1135  				Overrides: []expinfrav1.Overrides{
  1136  					{
  1137  						InstanceType: "t1.large",
  1138  					},
  1139  				},
  1140  			},
  1141  		},
  1142  		Status: expinfrav1.AWSMachinePoolStatus{
  1143  			LaunchTemplateID: "launchTemplateID",
  1144  		},
  1145  	}
  1146  	mps, err := scope.NewMachinePoolScope(scope.MachinePoolScopeParams{
  1147  		Client:         client,
  1148  		Cluster:        clusterScope.Cluster,
  1149  		MachinePool:    &expclusterv1.MachinePool{},
  1150  		InfraCluster:   clusterScope,
  1151  		AWSMachinePool: awsMachinePool,
  1152  	})
  1153  	if err != nil {
  1154  		return nil, err
  1155  	}
  1156  	return mps, nil
  1157  }