sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/ec2/instances_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 ec2
    18  
    19  import (
    20  	"encoding/base64"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/aws/aws-sdk-go/aws"
    25  	"github.com/aws/aws-sdk-go/aws/awserr"
    26  	"github.com/aws/aws-sdk-go/service/ec2"
    27  	"github.com/golang/mock/gomock"
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/pkg/errors"
    30  	corev1 "k8s.io/api/core/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/utils/pointer"
    34  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    35  
    36  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    37  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors"
    38  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/filter"
    39  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    40  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/userdata"
    41  	"sigs.k8s.io/cluster-api-provider-aws/test/mocks"
    42  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    43  )
    44  
    45  func TestInstanceIfExists(t *testing.T) {
    46  	mockCtrl := gomock.NewController(t)
    47  	defer mockCtrl.Finish()
    48  
    49  	testCases := []struct {
    50  		name       string
    51  		instanceID string
    52  		expect     func(m *mocks.MockEC2APIMockRecorder)
    53  		check      func(instance *infrav1.Instance, err error)
    54  	}{
    55  		{
    56  			name:       "does not exist",
    57  			instanceID: "hello",
    58  			expect: func(m *mocks.MockEC2APIMockRecorder) {
    59  				m.DescribeInstances(gomock.Eq(&ec2.DescribeInstancesInput{
    60  					InstanceIds: []*string{aws.String("hello")},
    61  				})).
    62  					Return(nil, awserrors.NewNotFound("not found"))
    63  			},
    64  			check: func(instance *infrav1.Instance, err error) {
    65  				if err == nil {
    66  					t.Fatalf("expects error when instance could not be found: %v", err)
    67  				}
    68  
    69  				if instance != nil {
    70  					t.Fatalf("Did not expect anything but got something: %+v", instance)
    71  				}
    72  			},
    73  		},
    74  		{
    75  			name:       "does not exist with bad request error",
    76  			instanceID: "hello-does-not-exist",
    77  			expect: func(m *mocks.MockEC2APIMockRecorder) {
    78  				m.DescribeInstances(gomock.Eq(&ec2.DescribeInstancesInput{
    79  					InstanceIds: []*string{aws.String("hello-does-not-exist")},
    80  				})).
    81  					Return(nil, awserr.New(awserrors.InvalidInstanceID, "does not exist", nil))
    82  			},
    83  			check: func(instance *infrav1.Instance, err error) {
    84  				if err == nil {
    85  					t.Fatalf("expects error when DescribeInstances returns error: %v", err)
    86  				}
    87  
    88  				if instance != nil {
    89  					t.Fatalf("Did not expect anything but got something: %+v", instance)
    90  				}
    91  			},
    92  		},
    93  		{
    94  			name:       "instance exists",
    95  			instanceID: "id-1",
    96  			expect: func(m *mocks.MockEC2APIMockRecorder) {
    97  				az := "test-zone-1a"
    98  				m.DescribeInstances(gomock.Eq(&ec2.DescribeInstancesInput{
    99  					InstanceIds: []*string{aws.String("id-1")},
   100  				})).
   101  					Return(&ec2.DescribeInstancesOutput{
   102  						Reservations: []*ec2.Reservation{
   103  							{
   104  								Instances: []*ec2.Instance{
   105  									{
   106  										InstanceId:   aws.String("id-1"),
   107  										InstanceType: aws.String("m5.large"),
   108  										SubnetId:     aws.String("subnet-1"),
   109  										ImageId:      aws.String("ami-1"),
   110  										IamInstanceProfile: &ec2.IamInstanceProfile{
   111  											Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   112  										},
   113  										State: &ec2.InstanceState{
   114  											Code: aws.Int64(16),
   115  											Name: aws.String(ec2.StateAvailable),
   116  										},
   117  										RootDeviceName: aws.String("device-1"),
   118  										BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   119  											{
   120  												DeviceName: aws.String("device-1"),
   121  												Ebs: &ec2.EbsInstanceBlockDevice{
   122  													VolumeId: aws.String("volume-1"),
   123  												},
   124  											},
   125  										},
   126  										Placement: &ec2.Placement{
   127  											AvailabilityZone: &az,
   128  										},
   129  									},
   130  								},
   131  							},
   132  						},
   133  					}, nil)
   134  			},
   135  			check: func(instance *infrav1.Instance, err error) {
   136  				if err != nil {
   137  					t.Fatalf("did not expect error: %v", err)
   138  				}
   139  
   140  				if instance == nil {
   141  					t.Fatalf("expected instance but got nothing")
   142  				}
   143  
   144  				if instance.ID != "id-1" {
   145  					t.Fatalf("expected id-1 but got: %v", instance.ID)
   146  				}
   147  			},
   148  		},
   149  		{
   150  			name:       "error describing instances",
   151  			instanceID: "one",
   152  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   153  				m.DescribeInstances(&ec2.DescribeInstancesInput{
   154  					InstanceIds: []*string{aws.String("one")},
   155  				}).
   156  					Return(nil, errors.New("some unknown error"))
   157  			},
   158  			check: func(i *infrav1.Instance, err error) {
   159  				if err == nil {
   160  					t.Fatalf("expected an error but got none.")
   161  				}
   162  			},
   163  		},
   164  	}
   165  
   166  	for _, tc := range testCases {
   167  		t.Run(tc.name, func(t *testing.T) {
   168  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
   169  
   170  			scheme := runtime.NewScheme()
   171  			_ = infrav1.AddToScheme(scheme)
   172  			client := fake.NewClientBuilder().WithScheme(scheme).Build()
   173  
   174  			scope, err := scope.NewClusterScope(scope.ClusterScopeParams{
   175  				Client:  client,
   176  				Cluster: &clusterv1.Cluster{},
   177  				AWSCluster: &infrav1.AWSCluster{
   178  					ObjectMeta: metav1.ObjectMeta{Name: "test"},
   179  					Spec: infrav1.AWSClusterSpec{
   180  						NetworkSpec: infrav1.NetworkSpec{
   181  							VPC: infrav1.VPCSpec{
   182  								ID: "test-vpc",
   183  							},
   184  						},
   185  					},
   186  				},
   187  			})
   188  			if err != nil {
   189  				t.Fatalf("Failed to create test context: %v", err)
   190  			}
   191  
   192  			tc.expect(ec2Mock.EXPECT())
   193  
   194  			s := NewService(scope)
   195  			s.EC2Client = ec2Mock
   196  
   197  			instance, err := s.InstanceIfExists(&tc.instanceID)
   198  			tc.check(instance, err)
   199  		})
   200  	}
   201  }
   202  
   203  func TestTerminateInstance(t *testing.T) {
   204  	mockCtrl := gomock.NewController(t)
   205  	defer mockCtrl.Finish()
   206  
   207  	instanceNotFoundError := errors.New("instance not found")
   208  
   209  	testCases := []struct {
   210  		name       string
   211  		instanceID string
   212  		expect     func(m *mocks.MockEC2APIMockRecorder)
   213  		check      func(err error)
   214  	}{
   215  		{
   216  			name:       "instance exists",
   217  			instanceID: "i-exist",
   218  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   219  				m.TerminateInstances(gomock.Eq(&ec2.TerminateInstancesInput{
   220  					InstanceIds: []*string{aws.String("i-exist")},
   221  				})).
   222  					Return(&ec2.TerminateInstancesOutput{}, nil)
   223  			},
   224  			check: func(err error) {
   225  				if err != nil {
   226  					t.Fatalf("did not expect error: %v", err)
   227  				}
   228  			},
   229  		},
   230  		{
   231  			name:       "instance does not exist",
   232  			instanceID: "i-donotexist",
   233  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   234  				m.TerminateInstances(gomock.Eq(&ec2.TerminateInstancesInput{
   235  					InstanceIds: []*string{aws.String("i-donotexist")},
   236  				})).
   237  					Return(&ec2.TerminateInstancesOutput{}, instanceNotFoundError)
   238  			},
   239  			check: func(err error) {
   240  				if err == nil {
   241  					t.Fatalf("did not expect error: %v", err)
   242  				}
   243  			},
   244  		},
   245  	}
   246  
   247  	for _, tc := range testCases {
   248  		t.Run(tc.name, func(t *testing.T) {
   249  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
   250  
   251  			scheme := runtime.NewScheme()
   252  			_ = infrav1.AddToScheme(scheme)
   253  			client := fake.NewClientBuilder().WithScheme(scheme).Build()
   254  			scope, err := scope.NewClusterScope(scope.ClusterScopeParams{
   255  				Client:     client,
   256  				Cluster:    &clusterv1.Cluster{},
   257  				AWSCluster: &infrav1.AWSCluster{},
   258  			})
   259  			if err != nil {
   260  				t.Fatalf("Failed to create test context: %v", err)
   261  			}
   262  
   263  			tc.expect(ec2Mock.EXPECT())
   264  
   265  			s := NewService(scope)
   266  			s.EC2Client = ec2Mock
   267  
   268  			err = s.TerminateInstance(tc.instanceID)
   269  			tc.check(err)
   270  		})
   271  	}
   272  }
   273  
   274  func TestCreateInstance(t *testing.T) {
   275  	secret := &corev1.Secret{
   276  		ObjectMeta: metav1.ObjectMeta{
   277  			Name: "bootstrap-data",
   278  		},
   279  		Data: map[string][]byte{
   280  			"value": []byte("data"),
   281  		},
   282  	}
   283  
   284  	az := "test-zone-1a"
   285  	tenancy := "dedicated"
   286  
   287  	data := []byte("userData")
   288  
   289  	userDataCompressed, err := userdata.GzipBytes(data)
   290  	if err != nil {
   291  		t.Fatal("Failed to gzip test user data")
   292  	}
   293  
   294  	isUncompressedFalse := false
   295  	isUncompressedTrue := true
   296  
   297  	testcases := []struct {
   298  		name          string
   299  		machine       clusterv1.Machine
   300  		machineConfig *infrav1.AWSMachineSpec
   301  		awsCluster    *infrav1.AWSCluster
   302  		expect        func(m *mocks.MockEC2APIMockRecorder)
   303  		check         func(instance *infrav1.Instance, err error)
   304  	}{
   305  		{
   306  			name: "simple",
   307  			machine: clusterv1.Machine{
   308  				ObjectMeta: metav1.ObjectMeta{
   309  					Labels: map[string]string{"set": "node"},
   310  				},
   311  				Spec: clusterv1.MachineSpec{
   312  					Bootstrap: clusterv1.Bootstrap{
   313  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   314  					},
   315  				},
   316  			},
   317  			machineConfig: &infrav1.AWSMachineSpec{
   318  				AMI: infrav1.AMIReference{
   319  					ID: aws.String("abc"),
   320  				},
   321  				InstanceType: "m5.large",
   322  			},
   323  			awsCluster: &infrav1.AWSCluster{
   324  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   325  				Spec: infrav1.AWSClusterSpec{
   326  					NetworkSpec: infrav1.NetworkSpec{
   327  						Subnets: infrav1.Subnets{
   328  							infrav1.SubnetSpec{
   329  								ID:       "subnet-1",
   330  								IsPublic: false,
   331  							},
   332  							infrav1.SubnetSpec{
   333  								IsPublic: false,
   334  							},
   335  						},
   336  					},
   337  				},
   338  				Status: infrav1.AWSClusterStatus{
   339  					Network: infrav1.NetworkStatus{
   340  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   341  							infrav1.SecurityGroupControlPlane: {
   342  								ID: "1",
   343  							},
   344  							infrav1.SecurityGroupNode: {
   345  								ID: "2",
   346  							},
   347  							infrav1.SecurityGroupLB: {
   348  								ID: "3",
   349  							},
   350  						},
   351  						APIServerELB: infrav1.ClassicELB{
   352  							DNSName: "test-apiserver.us-east-1.aws",
   353  						},
   354  					},
   355  				},
   356  			},
   357  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   358  				m. // TODO: Restore these parameters, but with the tags as well
   359  					RunInstances(gomock.Any()).
   360  					Return(&ec2.Reservation{
   361  						Instances: []*ec2.Instance{
   362  							{
   363  								State: &ec2.InstanceState{
   364  									Name: aws.String(ec2.InstanceStateNamePending),
   365  								},
   366  								IamInstanceProfile: &ec2.IamInstanceProfile{
   367  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   368  								},
   369  								InstanceId:     aws.String("two"),
   370  								InstanceType:   aws.String("m5.large"),
   371  								SubnetId:       aws.String("subnet-1"),
   372  								ImageId:        aws.String("ami-1"),
   373  								RootDeviceName: aws.String("device-1"),
   374  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   375  									{
   376  										DeviceName: aws.String("device-1"),
   377  										Ebs: &ec2.EbsInstanceBlockDevice{
   378  											VolumeId: aws.String("volume-1"),
   379  										},
   380  									},
   381  								},
   382  								Placement: &ec2.Placement{
   383  									AvailabilityZone: &az,
   384  								},
   385  							},
   386  						},
   387  					}, nil)
   388  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   389  					Return(nil)
   390  			},
   391  			check: func(instance *infrav1.Instance, err error) {
   392  				if err != nil {
   393  					t.Fatalf("did not expect error: %v", err)
   394  				}
   395  			},
   396  		},
   397  		{
   398  			name: "with availability zone",
   399  			machine: clusterv1.Machine{
   400  				ObjectMeta: metav1.ObjectMeta{
   401  					Labels: map[string]string{"set": "node"},
   402  				},
   403  				Spec: clusterv1.MachineSpec{
   404  					Bootstrap: clusterv1.Bootstrap{
   405  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   406  					},
   407  				},
   408  			},
   409  			machineConfig: &infrav1.AWSMachineSpec{
   410  				AMI: infrav1.AMIReference{
   411  					ID: aws.String("abc"),
   412  				},
   413  				InstanceType:  "m5.2xlarge",
   414  				FailureDomain: aws.String("us-east-1c"),
   415  			},
   416  			awsCluster: &infrav1.AWSCluster{
   417  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   418  				Spec: infrav1.AWSClusterSpec{
   419  					NetworkSpec: infrav1.NetworkSpec{
   420  						Subnets: infrav1.Subnets{
   421  							infrav1.SubnetSpec{
   422  								ID:               "subnet-1",
   423  								AvailabilityZone: "us-east-1a",
   424  								IsPublic:         false,
   425  							},
   426  							infrav1.SubnetSpec{
   427  								ID:               "subnet-2",
   428  								AvailabilityZone: "us-east-1b",
   429  								IsPublic:         false,
   430  							},
   431  							infrav1.SubnetSpec{
   432  								ID:               "subnet-3",
   433  								AvailabilityZone: "us-east-1c",
   434  								IsPublic:         false,
   435  							},
   436  							infrav1.SubnetSpec{
   437  								ID:               "subnet-3-public",
   438  								AvailabilityZone: "us-east-1c",
   439  								IsPublic:         true,
   440  							},
   441  						},
   442  					},
   443  				},
   444  				Status: infrav1.AWSClusterStatus{
   445  					Network: infrav1.NetworkStatus{
   446  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   447  							infrav1.SecurityGroupControlPlane: {
   448  								ID: "1",
   449  							},
   450  							infrav1.SecurityGroupNode: {
   451  								ID: "2",
   452  							},
   453  							infrav1.SecurityGroupLB: {
   454  								ID: "3",
   455  							},
   456  						},
   457  						APIServerELB: infrav1.ClassicELB{
   458  							DNSName: "test-apiserver.us-east-1.aws",
   459  						},
   460  					},
   461  				},
   462  			},
   463  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   464  				m.
   465  					RunInstances(gomock.Any()).
   466  					Return(&ec2.Reservation{
   467  						Instances: []*ec2.Instance{
   468  							{
   469  								State: &ec2.InstanceState{
   470  									Name: aws.String(ec2.InstanceStateNamePending),
   471  								},
   472  								IamInstanceProfile: &ec2.IamInstanceProfile{
   473  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   474  								},
   475  								InstanceId:     aws.String("two"),
   476  								InstanceType:   aws.String("m5.large"),
   477  								SubnetId:       aws.String("subnet-3"),
   478  								ImageId:        aws.String("ami-1"),
   479  								RootDeviceName: aws.String("device-1"),
   480  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   481  									{
   482  										DeviceName: aws.String("device-1"),
   483  										Ebs: &ec2.EbsInstanceBlockDevice{
   484  											VolumeId: aws.String("volume-1"),
   485  										},
   486  									},
   487  								},
   488  								Placement: &ec2.Placement{
   489  									AvailabilityZone: &az,
   490  								},
   491  							},
   492  						},
   493  					}, nil)
   494  
   495  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   496  					Return(nil)
   497  			},
   498  			check: func(instance *infrav1.Instance, err error) {
   499  				if err != nil {
   500  					t.Fatalf("did not expect error: %v", err)
   501  				}
   502  
   503  				if instance.SubnetID != "subnet-3" {
   504  					t.Fatalf("expected subnet-3 from availability zone us-east-1c, got %q", instance.SubnetID)
   505  				}
   506  			},
   507  		},
   508  		{
   509  			name: "with ImageLookupOrg specified at the machine level",
   510  			machine: clusterv1.Machine{
   511  				ObjectMeta: metav1.ObjectMeta{
   512  					Labels: map[string]string{"set": "node"},
   513  				},
   514  				Spec: clusterv1.MachineSpec{
   515  					Bootstrap: clusterv1.Bootstrap{
   516  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   517  					},
   518  					Version: pointer.StringPtr("v1.16.1"),
   519  				},
   520  			},
   521  			machineConfig: &infrav1.AWSMachineSpec{
   522  				ImageLookupOrg: "test-org-123",
   523  				InstanceType:   "m5.large",
   524  			},
   525  			awsCluster: &infrav1.AWSCluster{
   526  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   527  				Spec: infrav1.AWSClusterSpec{
   528  					NetworkSpec: infrav1.NetworkSpec{
   529  						Subnets: infrav1.Subnets{
   530  							infrav1.SubnetSpec{
   531  								ID:       "subnet-1",
   532  								IsPublic: false,
   533  							},
   534  							infrav1.SubnetSpec{
   535  								IsPublic: false,
   536  							},
   537  						},
   538  					},
   539  				},
   540  				Status: infrav1.AWSClusterStatus{
   541  					Network: infrav1.NetworkStatus{
   542  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   543  							infrav1.SecurityGroupControlPlane: {
   544  								ID: "1",
   545  							},
   546  							infrav1.SecurityGroupNode: {
   547  								ID: "2",
   548  							},
   549  							infrav1.SecurityGroupLB: {
   550  								ID: "3",
   551  							},
   552  						},
   553  						APIServerELB: infrav1.ClassicELB{
   554  							DNSName: "test-apiserver.us-east-1.aws",
   555  						},
   556  					},
   557  				},
   558  			},
   559  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   560  				amiName, err := GenerateAmiName("capa-ami-{{.BaseOS}}-?{{.K8sVersion}}-*", "ubuntu-18.04", "v1.16.1")
   561  				if err != nil {
   562  					t.Fatalf("Failed to process ami format: %v", err)
   563  				}
   564  				// verify that the ImageLookupOrg is used when finding AMIs
   565  				m.
   566  					DescribeImages(gomock.Eq(&ec2.DescribeImagesInput{
   567  						Filters: []*ec2.Filter{
   568  							{
   569  								Name:   aws.String("owner-id"),
   570  								Values: []*string{aws.String("test-org-123")},
   571  							},
   572  							{
   573  								Name:   aws.String("name"),
   574  								Values: []*string{aws.String(amiName)},
   575  							},
   576  							{
   577  								Name:   aws.String("architecture"),
   578  								Values: []*string{aws.String("x86_64")},
   579  							},
   580  							{
   581  								Name:   aws.String("state"),
   582  								Values: []*string{aws.String("available")},
   583  							},
   584  							{
   585  								Name:   aws.String("virtualization-type"),
   586  								Values: []*string{aws.String("hvm")},
   587  							},
   588  						},
   589  					})).
   590  					Return(&ec2.DescribeImagesOutput{
   591  						Images: []*ec2.Image{
   592  							{
   593  								Name:         aws.String("ami-1"),
   594  								CreationDate: aws.String("2006-01-02T15:04:05.000Z"),
   595  							},
   596  						},
   597  					}, nil)
   598  				m. // TODO: Restore these parameters, but with the tags as well
   599  					RunInstances(gomock.Any()).
   600  					Return(&ec2.Reservation{
   601  						Instances: []*ec2.Instance{
   602  							{
   603  								State: &ec2.InstanceState{
   604  									Name: aws.String(ec2.InstanceStateNamePending),
   605  								},
   606  								IamInstanceProfile: &ec2.IamInstanceProfile{
   607  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   608  								},
   609  								InstanceId:     aws.String("two"),
   610  								InstanceType:   aws.String("m5.large"),
   611  								SubnetId:       aws.String("subnet-1"),
   612  								ImageId:        aws.String("ami-1"),
   613  								RootDeviceName: aws.String("device-1"),
   614  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   615  									{
   616  										DeviceName: aws.String("device-1"),
   617  										Ebs: &ec2.EbsInstanceBlockDevice{
   618  											VolumeId: aws.String("volume-1"),
   619  										},
   620  									},
   621  								},
   622  								Placement: &ec2.Placement{
   623  									AvailabilityZone: &az,
   624  								},
   625  							},
   626  						},
   627  					}, nil)
   628  
   629  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   630  					Return(nil)
   631  			},
   632  			check: func(instance *infrav1.Instance, err error) {
   633  				if err != nil {
   634  					t.Fatalf("did not expect error: %v", err)
   635  				}
   636  			},
   637  		},
   638  		{
   639  			name: "with ImageLookupOrg specified at the cluster-level",
   640  			machine: clusterv1.Machine{
   641  				ObjectMeta: metav1.ObjectMeta{
   642  					Labels: map[string]string{"set": "node"},
   643  				},
   644  				Spec: clusterv1.MachineSpec{
   645  					Bootstrap: clusterv1.Bootstrap{
   646  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   647  					},
   648  					Version: pointer.StringPtr("v1.16.1"),
   649  				},
   650  			},
   651  			machineConfig: &infrav1.AWSMachineSpec{
   652  				InstanceType: "m5.large",
   653  			},
   654  			awsCluster: &infrav1.AWSCluster{
   655  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   656  				Spec: infrav1.AWSClusterSpec{
   657  					NetworkSpec: infrav1.NetworkSpec{
   658  						Subnets: infrav1.Subnets{
   659  							infrav1.SubnetSpec{
   660  								ID:       "subnet-1",
   661  								IsPublic: false,
   662  							},
   663  							infrav1.SubnetSpec{
   664  								IsPublic: false,
   665  							},
   666  						},
   667  					},
   668  					ImageLookupOrg: "cluster-level-image-lookup-org",
   669  				},
   670  				Status: infrav1.AWSClusterStatus{
   671  					Network: infrav1.NetworkStatus{
   672  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   673  							infrav1.SecurityGroupControlPlane: {
   674  								ID: "1",
   675  							},
   676  							infrav1.SecurityGroupNode: {
   677  								ID: "2",
   678  							},
   679  							infrav1.SecurityGroupLB: {
   680  								ID: "3",
   681  							},
   682  						},
   683  						APIServerELB: infrav1.ClassicELB{
   684  							DNSName: "test-apiserver.us-east-1.aws",
   685  						},
   686  					},
   687  				},
   688  			},
   689  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   690  				amiName, err := GenerateAmiName("capa-ami-{{.BaseOS}}-?{{.K8sVersion}}-*", "ubuntu-18.04", "v1.16.1")
   691  				if err != nil {
   692  					t.Fatalf("Failed to process ami format: %v", err)
   693  				}
   694  				// verify that the ImageLookupOrg is used when finding AMIs
   695  				m.
   696  					DescribeImages(gomock.Eq(&ec2.DescribeImagesInput{
   697  						Filters: []*ec2.Filter{
   698  							{
   699  								Name:   aws.String("owner-id"),
   700  								Values: []*string{aws.String("cluster-level-image-lookup-org")},
   701  							},
   702  							{
   703  								Name:   aws.String("name"),
   704  								Values: []*string{aws.String(amiName)},
   705  							},
   706  							{
   707  								Name:   aws.String("architecture"),
   708  								Values: []*string{aws.String("x86_64")},
   709  							},
   710  							{
   711  								Name:   aws.String("state"),
   712  								Values: []*string{aws.String("available")},
   713  							},
   714  							{
   715  								Name:   aws.String("virtualization-type"),
   716  								Values: []*string{aws.String("hvm")},
   717  							},
   718  						},
   719  					})).
   720  					Return(&ec2.DescribeImagesOutput{
   721  						Images: []*ec2.Image{
   722  							{
   723  								Name:         aws.String("ami-1"),
   724  								CreationDate: aws.String("2006-01-02T15:04:05.000Z"),
   725  							},
   726  						},
   727  					}, nil)
   728  				m. // TODO: Restore these parameters, but with the tags as well
   729  					RunInstances(gomock.Any()).
   730  					Return(&ec2.Reservation{
   731  						Instances: []*ec2.Instance{
   732  							{
   733  								State: &ec2.InstanceState{
   734  									Name: aws.String(ec2.InstanceStateNamePending),
   735  								},
   736  								IamInstanceProfile: &ec2.IamInstanceProfile{
   737  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   738  								},
   739  								InstanceId:     aws.String("two"),
   740  								InstanceType:   aws.String("m5.large"),
   741  								SubnetId:       aws.String("subnet-1"),
   742  								ImageId:        aws.String("ami-1"),
   743  								RootDeviceName: aws.String("device-1"),
   744  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   745  									{
   746  										DeviceName: aws.String("device-1"),
   747  										Ebs: &ec2.EbsInstanceBlockDevice{
   748  											VolumeId: aws.String("volume-1"),
   749  										},
   750  									},
   751  								},
   752  								Placement: &ec2.Placement{
   753  									AvailabilityZone: &az,
   754  								},
   755  							},
   756  						},
   757  					}, nil)
   758  
   759  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   760  					Return(nil)
   761  			},
   762  			check: func(instance *infrav1.Instance, err error) {
   763  				if err != nil {
   764  					t.Fatalf("did not expect error: %v", err)
   765  				}
   766  			},
   767  		},
   768  		{
   769  			name: "AWSMachine ImageLookupOrg overrides AWSCluster ImageLookupOrg",
   770  			machine: clusterv1.Machine{
   771  				ObjectMeta: metav1.ObjectMeta{
   772  					Labels: map[string]string{"set": "node"},
   773  				},
   774  				Spec: clusterv1.MachineSpec{
   775  					Bootstrap: clusterv1.Bootstrap{
   776  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   777  					},
   778  					Version: pointer.StringPtr("v1.16.1"),
   779  				},
   780  			},
   781  			machineConfig: &infrav1.AWSMachineSpec{
   782  				InstanceType:   "m5.large",
   783  				ImageLookupOrg: "machine-level-image-lookup-org",
   784  			},
   785  			awsCluster: &infrav1.AWSCluster{
   786  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   787  				Spec: infrav1.AWSClusterSpec{
   788  					NetworkSpec: infrav1.NetworkSpec{
   789  						Subnets: infrav1.Subnets{
   790  							infrav1.SubnetSpec{
   791  								ID:       "subnet-1",
   792  								IsPublic: false,
   793  							},
   794  							infrav1.SubnetSpec{
   795  								IsPublic: false,
   796  							},
   797  						},
   798  					},
   799  					ImageLookupOrg: "cluster-level-image-lookup-org",
   800  				},
   801  				Status: infrav1.AWSClusterStatus{
   802  					Network: infrav1.NetworkStatus{
   803  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   804  							infrav1.SecurityGroupControlPlane: {
   805  								ID: "1",
   806  							},
   807  							infrav1.SecurityGroupNode: {
   808  								ID: "2",
   809  							},
   810  							infrav1.SecurityGroupLB: {
   811  								ID: "3",
   812  							},
   813  						},
   814  						APIServerELB: infrav1.ClassicELB{
   815  							DNSName: "test-apiserver.us-east-1.aws",
   816  						},
   817  					},
   818  				},
   819  			},
   820  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   821  				amiName, err := GenerateAmiName("capa-ami-{{.BaseOS}}-?{{.K8sVersion}}-*", "ubuntu-18.04", "v1.16.1")
   822  				if err != nil {
   823  					t.Fatalf("Failed to process ami format: %v", err)
   824  				}
   825  				// verify that the ImageLookupOrg is used when finding AMIs
   826  				m.
   827  					DescribeImages(gomock.Eq(&ec2.DescribeImagesInput{
   828  						Filters: []*ec2.Filter{
   829  							{
   830  								Name:   aws.String("owner-id"),
   831  								Values: []*string{aws.String("machine-level-image-lookup-org")},
   832  							},
   833  							{
   834  								Name:   aws.String("name"),
   835  								Values: []*string{aws.String(amiName)},
   836  							},
   837  							{
   838  								Name:   aws.String("architecture"),
   839  								Values: []*string{aws.String("x86_64")},
   840  							},
   841  							{
   842  								Name:   aws.String("state"),
   843  								Values: []*string{aws.String("available")},
   844  							},
   845  							{
   846  								Name:   aws.String("virtualization-type"),
   847  								Values: []*string{aws.String("hvm")},
   848  							},
   849  						},
   850  					})).
   851  					Return(&ec2.DescribeImagesOutput{
   852  						Images: []*ec2.Image{
   853  							{
   854  								Name:         aws.String("ami-1"),
   855  								CreationDate: aws.String("2006-01-02T15:04:05.000Z"),
   856  							},
   857  						},
   858  					}, nil)
   859  				m. // TODO: Restore these parameters, but with the tags as well
   860  					RunInstances(gomock.Any()).
   861  					Return(&ec2.Reservation{
   862  						Instances: []*ec2.Instance{
   863  							{
   864  								State: &ec2.InstanceState{
   865  									Name: aws.String(ec2.InstanceStateNamePending),
   866  								},
   867  								IamInstanceProfile: &ec2.IamInstanceProfile{
   868  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   869  								},
   870  								InstanceId:     aws.String("two"),
   871  								InstanceType:   aws.String("m5.large"),
   872  								SubnetId:       aws.String("subnet-1"),
   873  								ImageId:        aws.String("ami-1"),
   874  								RootDeviceName: aws.String("device-1"),
   875  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   876  									{
   877  										DeviceName: aws.String("device-1"),
   878  										Ebs: &ec2.EbsInstanceBlockDevice{
   879  											VolumeId: aws.String("volume-1"),
   880  										},
   881  									},
   882  								},
   883  								Placement: &ec2.Placement{
   884  									AvailabilityZone: &az,
   885  								},
   886  							},
   887  						},
   888  					}, nil)
   889  
   890  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   891  					Return(nil)
   892  			},
   893  			check: func(instance *infrav1.Instance, err error) {
   894  				if err != nil {
   895  					t.Fatalf("did not expect error: %v", err)
   896  				}
   897  			},
   898  		},
   899  		{
   900  			name: "subnet filter and failureDomain defined",
   901  			machine: clusterv1.Machine{
   902  				ObjectMeta: metav1.ObjectMeta{
   903  					Labels: map[string]string{"set": "node"},
   904  				},
   905  				Spec: clusterv1.MachineSpec{
   906  					Bootstrap: clusterv1.Bootstrap{
   907  						DataSecretName: pointer.StringPtr("bootstrap-data"),
   908  					},
   909  				},
   910  			},
   911  			machineConfig: &infrav1.AWSMachineSpec{
   912  				AMI: infrav1.AMIReference{
   913  					ID: aws.String("abc"),
   914  				},
   915  				InstanceType: "m5.large",
   916  				Subnet: &infrav1.AWSResourceReference{
   917  					Filters: []infrav1.Filter{{
   918  						Name:   "tag:some-tag",
   919  						Values: []string{"some-value"},
   920  					}},
   921  				},
   922  				FailureDomain: aws.String("us-east-1b"),
   923  			},
   924  			awsCluster: &infrav1.AWSCluster{
   925  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
   926  				Spec: infrav1.AWSClusterSpec{
   927  					NetworkSpec: infrav1.NetworkSpec{
   928  						VPC: infrav1.VPCSpec{
   929  							ID: "vpc-id",
   930  						},
   931  					},
   932  				},
   933  				Status: infrav1.AWSClusterStatus{
   934  					Network: infrav1.NetworkStatus{
   935  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
   936  							infrav1.SecurityGroupControlPlane: {
   937  								ID: "1",
   938  							},
   939  							infrav1.SecurityGroupNode: {
   940  								ID: "2",
   941  							},
   942  							infrav1.SecurityGroupLB: {
   943  								ID: "3",
   944  							},
   945  						},
   946  						APIServerELB: infrav1.ClassicELB{
   947  							DNSName: "test-apiserver.us-east-1.aws",
   948  						},
   949  					},
   950  				},
   951  			},
   952  			expect: func(m *mocks.MockEC2APIMockRecorder) {
   953  				m.
   954  					DescribeSubnets(&ec2.DescribeSubnetsInput{
   955  						Filters: []*ec2.Filter{
   956  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
   957  							filter.EC2.VPC("vpc-id"),
   958  							{Name: aws.String("tag:some-tag"), Values: aws.StringSlice([]string{"some-value"})},
   959  						},
   960  					}).
   961  					Return(&ec2.DescribeSubnetsOutput{
   962  						Subnets: []*ec2.Subnet{{
   963  							SubnetId:         aws.String("filtered-subnet-1"),
   964  							AvailabilityZone: aws.String("us-east-1b"),
   965  						}},
   966  					}, nil)
   967  				m.
   968  					RunInstances(gomock.Any()).
   969  					Return(&ec2.Reservation{
   970  						Instances: []*ec2.Instance{
   971  							{
   972  								State: &ec2.InstanceState{
   973  									Name: aws.String(ec2.InstanceStateNamePending),
   974  								},
   975  								IamInstanceProfile: &ec2.IamInstanceProfile{
   976  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   977  								},
   978  								InstanceId:     aws.String("two"),
   979  								InstanceType:   aws.String("m5.large"),
   980  								SubnetId:       aws.String("subnet-1"),
   981  								ImageId:        aws.String("ami-1"),
   982  								RootDeviceName: aws.String("device-1"),
   983  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   984  									{
   985  										DeviceName: aws.String("device-1"),
   986  										Ebs: &ec2.EbsInstanceBlockDevice{
   987  											VolumeId: aws.String("volume-1"),
   988  										},
   989  									},
   990  								},
   991  								Placement: &ec2.Placement{
   992  									AvailabilityZone: &az,
   993  								},
   994  							},
   995  						},
   996  					}, nil)
   997  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
   998  					Return(nil)
   999  			},
  1000  			check: func(instance *infrav1.Instance, err error) {
  1001  				if err != nil {
  1002  					t.Fatalf("did not expect error: %v", err)
  1003  				}
  1004  			},
  1005  		},
  1006  		{
  1007  			name: "with subnet ID that belongs to Cluster",
  1008  			machine: clusterv1.Machine{
  1009  				ObjectMeta: metav1.ObjectMeta{
  1010  					Labels: map[string]string{"set": "node"},
  1011  				},
  1012  				Spec: clusterv1.MachineSpec{
  1013  					Bootstrap: clusterv1.Bootstrap{
  1014  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1015  					},
  1016  				},
  1017  			},
  1018  			machineConfig: &infrav1.AWSMachineSpec{
  1019  				AMI: infrav1.AMIReference{
  1020  					ID: aws.String("abc"),
  1021  				},
  1022  				InstanceType: "m5.large",
  1023  				Subnet: &infrav1.AWSResourceReference{
  1024  					ID: aws.String("matching-subnet"),
  1025  				},
  1026  			},
  1027  			awsCluster: &infrav1.AWSCluster{
  1028  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1029  				Spec: infrav1.AWSClusterSpec{
  1030  					NetworkSpec: infrav1.NetworkSpec{
  1031  						VPC: infrav1.VPCSpec{
  1032  							ID: "vpc-id",
  1033  						},
  1034  						Subnets: infrav1.Subnets{{
  1035  							ID: "matching-subnet",
  1036  						}},
  1037  					},
  1038  				},
  1039  				Status: infrav1.AWSClusterStatus{
  1040  					Network: infrav1.NetworkStatus{
  1041  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1042  							infrav1.SecurityGroupControlPlane: {
  1043  								ID: "1",
  1044  							},
  1045  							infrav1.SecurityGroupNode: {
  1046  								ID: "2",
  1047  							},
  1048  							infrav1.SecurityGroupLB: {
  1049  								ID: "3",
  1050  							},
  1051  						},
  1052  						APIServerELB: infrav1.ClassicELB{
  1053  							DNSName: "test-apiserver.us-east-1.aws",
  1054  						},
  1055  					},
  1056  				},
  1057  			},
  1058  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1059  				m.
  1060  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1061  						Filters: []*ec2.Filter{
  1062  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1063  							filter.EC2.VPC("vpc-id"),
  1064  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"matching-subnet"})},
  1065  						},
  1066  					}).
  1067  					Return(&ec2.DescribeSubnetsOutput{
  1068  						Subnets: []*ec2.Subnet{{
  1069  							SubnetId:         aws.String("matching-subnet"),
  1070  							AvailabilityZone: aws.String("us-east-1b"),
  1071  						}},
  1072  					}, nil)
  1073  				m.
  1074  					RunInstances(gomock.Any()).
  1075  					Return(&ec2.Reservation{
  1076  						Instances: []*ec2.Instance{
  1077  							{
  1078  								State: &ec2.InstanceState{
  1079  									Name: aws.String(ec2.InstanceStateNamePending),
  1080  								},
  1081  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1082  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1083  								},
  1084  								InstanceId:     aws.String("two"),
  1085  								InstanceType:   aws.String("m5.large"),
  1086  								SubnetId:       aws.String("matching-subnet"),
  1087  								ImageId:        aws.String("ami-1"),
  1088  								RootDeviceName: aws.String("device-1"),
  1089  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1090  									{
  1091  										DeviceName: aws.String("device-1"),
  1092  										Ebs: &ec2.EbsInstanceBlockDevice{
  1093  											VolumeId: aws.String("volume-1"),
  1094  										},
  1095  									},
  1096  								},
  1097  								Placement: &ec2.Placement{
  1098  									AvailabilityZone: &az,
  1099  								},
  1100  							},
  1101  						},
  1102  					}, nil)
  1103  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  1104  					Return(nil)
  1105  			},
  1106  			check: func(instance *infrav1.Instance, err error) {
  1107  				if err != nil {
  1108  					t.Fatalf("did not expect error: %v", err)
  1109  				}
  1110  			},
  1111  		},
  1112  		{
  1113  			name: "with subnet ID that does not exist",
  1114  			machine: clusterv1.Machine{
  1115  				ObjectMeta: metav1.ObjectMeta{
  1116  					Labels: map[string]string{"set": "node"},
  1117  				},
  1118  				Spec: clusterv1.MachineSpec{
  1119  					Bootstrap: clusterv1.Bootstrap{
  1120  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1121  					},
  1122  				},
  1123  			},
  1124  			machineConfig: &infrav1.AWSMachineSpec{
  1125  				AMI: infrav1.AMIReference{
  1126  					ID: aws.String("abc"),
  1127  				},
  1128  				InstanceType: "m5.large",
  1129  				Subnet: &infrav1.AWSResourceReference{
  1130  					ID: aws.String("non-matching-subnet"),
  1131  				},
  1132  			},
  1133  			awsCluster: &infrav1.AWSCluster{
  1134  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1135  				Spec: infrav1.AWSClusterSpec{
  1136  					NetworkSpec: infrav1.NetworkSpec{
  1137  						VPC: infrav1.VPCSpec{
  1138  							ID: "vpc-id",
  1139  						},
  1140  						Subnets: infrav1.Subnets{{
  1141  							ID: "subnet-1",
  1142  						}},
  1143  					},
  1144  				},
  1145  				Status: infrav1.AWSClusterStatus{
  1146  					Network: infrav1.NetworkStatus{
  1147  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1148  							infrav1.SecurityGroupControlPlane: {
  1149  								ID: "1",
  1150  							},
  1151  							infrav1.SecurityGroupNode: {
  1152  								ID: "2",
  1153  							},
  1154  							infrav1.SecurityGroupLB: {
  1155  								ID: "3",
  1156  							},
  1157  						},
  1158  						APIServerELB: infrav1.ClassicELB{
  1159  							DNSName: "test-apiserver.us-east-1.aws",
  1160  						},
  1161  					},
  1162  				},
  1163  			},
  1164  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1165  				m.
  1166  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1167  						Filters: []*ec2.Filter{
  1168  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1169  							filter.EC2.VPC("vpc-id"),
  1170  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"non-matching-subnet"})},
  1171  						},
  1172  					}).
  1173  					Return(&ec2.DescribeSubnetsOutput{
  1174  						Subnets: []*ec2.Subnet{},
  1175  					}, nil)
  1176  			},
  1177  			check: func(instance *infrav1.Instance, err error) {
  1178  				expectedErrMsg := "failed to run machine \"aws-test1\", no subnets available matching criteria"
  1179  				if err == nil {
  1180  					t.Fatalf("Expected error, but got nil")
  1181  				}
  1182  
  1183  				if !strings.Contains(err.Error(), expectedErrMsg) {
  1184  					t.Fatalf("Expected error: %s\nInstead got: %s", expectedErrMsg, err.Error())
  1185  				}
  1186  			},
  1187  		},
  1188  		{
  1189  			name: "with subnet ID that does not belong to Cluster",
  1190  			machine: clusterv1.Machine{
  1191  				ObjectMeta: metav1.ObjectMeta{
  1192  					Labels: map[string]string{"set": "node"},
  1193  				},
  1194  				Spec: clusterv1.MachineSpec{
  1195  					Bootstrap: clusterv1.Bootstrap{
  1196  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1197  					},
  1198  				},
  1199  			},
  1200  			machineConfig: &infrav1.AWSMachineSpec{
  1201  				AMI: infrav1.AMIReference{
  1202  					ID: aws.String("abc"),
  1203  				},
  1204  				InstanceType: "m5.large",
  1205  				Subnet: &infrav1.AWSResourceReference{
  1206  					ID: aws.String("matching-subnet"),
  1207  				},
  1208  			},
  1209  			awsCluster: &infrav1.AWSCluster{
  1210  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1211  				Spec: infrav1.AWSClusterSpec{
  1212  					NetworkSpec: infrav1.NetworkSpec{
  1213  						VPC: infrav1.VPCSpec{
  1214  							ID: "vpc-id",
  1215  						},
  1216  						Subnets: infrav1.Subnets{{
  1217  							ID: "subnet-1",
  1218  						}},
  1219  					},
  1220  				},
  1221  				Status: infrav1.AWSClusterStatus{
  1222  					Network: infrav1.NetworkStatus{
  1223  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1224  							infrav1.SecurityGroupControlPlane: {
  1225  								ID: "1",
  1226  							},
  1227  							infrav1.SecurityGroupNode: {
  1228  								ID: "2",
  1229  							},
  1230  							infrav1.SecurityGroupLB: {
  1231  								ID: "3",
  1232  							},
  1233  						},
  1234  						APIServerELB: infrav1.ClassicELB{
  1235  							DNSName: "test-apiserver.us-east-1.aws",
  1236  						},
  1237  					},
  1238  				},
  1239  			},
  1240  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1241  				m.
  1242  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1243  						Filters: []*ec2.Filter{
  1244  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1245  							filter.EC2.VPC("vpc-id"),
  1246  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"matching-subnet"})},
  1247  						},
  1248  					}).
  1249  					Return(&ec2.DescribeSubnetsOutput{
  1250  						Subnets: []*ec2.Subnet{{
  1251  							SubnetId: aws.String("matching-subnet"),
  1252  						}},
  1253  					}, nil)
  1254  				m.
  1255  					RunInstances(gomock.Any()).
  1256  					Return(&ec2.Reservation{
  1257  						Instances: []*ec2.Instance{
  1258  							{
  1259  								State: &ec2.InstanceState{
  1260  									Name: aws.String(ec2.InstanceStateNamePending),
  1261  								},
  1262  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1263  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1264  								},
  1265  								InstanceId:     aws.String("two"),
  1266  								InstanceType:   aws.String("m5.large"),
  1267  								SubnetId:       aws.String("matching-subnet"),
  1268  								ImageId:        aws.String("ami-1"),
  1269  								RootDeviceName: aws.String("device-1"),
  1270  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1271  									{
  1272  										DeviceName: aws.String("device-1"),
  1273  										Ebs: &ec2.EbsInstanceBlockDevice{
  1274  											VolumeId: aws.String("volume-1"),
  1275  										},
  1276  									},
  1277  								},
  1278  								Placement: &ec2.Placement{
  1279  									AvailabilityZone: &az,
  1280  								},
  1281  							},
  1282  						},
  1283  					}, nil)
  1284  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  1285  					Return(nil)
  1286  			},
  1287  			check: func(instance *infrav1.Instance, err error) {
  1288  				if err != nil {
  1289  					t.Fatalf("did not expect error: %v", err)
  1290  				}
  1291  			},
  1292  		},
  1293  		{
  1294  			name: "subnet id and failureDomain don't match",
  1295  			machine: clusterv1.Machine{
  1296  				ObjectMeta: metav1.ObjectMeta{
  1297  					Labels: map[string]string{"set": "node"},
  1298  				},
  1299  				Spec: clusterv1.MachineSpec{
  1300  					Bootstrap: clusterv1.Bootstrap{
  1301  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1302  					},
  1303  				},
  1304  			},
  1305  			machineConfig: &infrav1.AWSMachineSpec{
  1306  				AMI: infrav1.AMIReference{
  1307  					ID: aws.String("abc"),
  1308  				},
  1309  				InstanceType: "m5.large",
  1310  				Subnet: &infrav1.AWSResourceReference{
  1311  					ID: aws.String("subnet-1"),
  1312  				},
  1313  				FailureDomain: aws.String("us-east-1b"),
  1314  			},
  1315  			awsCluster: &infrav1.AWSCluster{
  1316  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1317  				Spec: infrav1.AWSClusterSpec{
  1318  					NetworkSpec: infrav1.NetworkSpec{
  1319  						VPC: infrav1.VPCSpec{
  1320  							ID: "vpc-id",
  1321  						},
  1322  						Subnets: infrav1.Subnets{{
  1323  							ID:               "subnet-1",
  1324  							AvailabilityZone: "us-west-1b",
  1325  						}},
  1326  					},
  1327  				},
  1328  				Status: infrav1.AWSClusterStatus{
  1329  					Network: infrav1.NetworkStatus{
  1330  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1331  							infrav1.SecurityGroupControlPlane: {
  1332  								ID: "1",
  1333  							},
  1334  							infrav1.SecurityGroupNode: {
  1335  								ID: "2",
  1336  							},
  1337  							infrav1.SecurityGroupLB: {
  1338  								ID: "3",
  1339  							},
  1340  						},
  1341  						APIServerELB: infrav1.ClassicELB{
  1342  							DNSName: "test-apiserver.us-east-1.aws",
  1343  						},
  1344  					},
  1345  				},
  1346  			},
  1347  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1348  				m.
  1349  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1350  						Filters: []*ec2.Filter{
  1351  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1352  							filter.EC2.VPC("vpc-id"),
  1353  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"subnet-1"})},
  1354  						},
  1355  					}).
  1356  					Return(&ec2.DescribeSubnetsOutput{
  1357  						Subnets: []*ec2.Subnet{{
  1358  							SubnetId:         aws.String("subnet-1"),
  1359  							AvailabilityZone: aws.String("us-west-1b"),
  1360  						}},
  1361  					}, nil)
  1362  			},
  1363  			check: func(instance *infrav1.Instance, err error) {
  1364  				expectedErrMsg := "failed to run machine \"aws-test1\", found 1 subnets matching criteria but post-filtering failed. subnet \"subnet-1\" availability zone \"us-west-1b\" does not match failure domain \"us-east-1b\""
  1365  				if err == nil {
  1366  					t.Fatalf("Expected error, but got nil")
  1367  				}
  1368  
  1369  				if !strings.Contains(err.Error(), expectedErrMsg) {
  1370  					t.Fatalf("Expected error: %s\nInstead got: `%s", expectedErrMsg, err.Error())
  1371  				}
  1372  			},
  1373  		},
  1374  		{
  1375  			name: "public IP true and failureDomain doesn't have public subnet",
  1376  			machine: clusterv1.Machine{
  1377  				ObjectMeta: metav1.ObjectMeta{
  1378  					Labels: map[string]string{"set": "node"},
  1379  				},
  1380  				Spec: clusterv1.MachineSpec{
  1381  					Bootstrap: clusterv1.Bootstrap{
  1382  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1383  					},
  1384  				},
  1385  			},
  1386  			machineConfig: &infrav1.AWSMachineSpec{
  1387  				AMI: infrav1.AMIReference{
  1388  					ID: aws.String("abc"),
  1389  				},
  1390  				InstanceType:  "m5.large",
  1391  				FailureDomain: aws.String("us-east-1b"),
  1392  				PublicIP:      aws.Bool(true),
  1393  			},
  1394  			awsCluster: &infrav1.AWSCluster{
  1395  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1396  				Spec: infrav1.AWSClusterSpec{
  1397  					NetworkSpec: infrav1.NetworkSpec{
  1398  						VPC: infrav1.VPCSpec{
  1399  							ID: "vpc-id",
  1400  						},
  1401  						Subnets: infrav1.Subnets{{
  1402  							ID:               "private-subnet-1",
  1403  							AvailabilityZone: "us-east-1b",
  1404  							IsPublic:         false,
  1405  						}},
  1406  					},
  1407  				},
  1408  				Status: infrav1.AWSClusterStatus{
  1409  					Network: infrav1.NetworkStatus{
  1410  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1411  							infrav1.SecurityGroupControlPlane: {
  1412  								ID: "1",
  1413  							},
  1414  							infrav1.SecurityGroupNode: {
  1415  								ID: "2",
  1416  							},
  1417  							infrav1.SecurityGroupLB: {
  1418  								ID: "3",
  1419  							},
  1420  						},
  1421  						APIServerELB: infrav1.ClassicELB{
  1422  							DNSName: "test-apiserver.us-east-1.aws",
  1423  						},
  1424  					},
  1425  				},
  1426  			},
  1427  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1428  			},
  1429  			check: func(instance *infrav1.Instance, err error) {
  1430  				expectedErrMsg := "failed to run machine \"aws-test1\" with public IP, no public subnets available in availability zone \"us-east-1b\""
  1431  				if err == nil {
  1432  					t.Fatalf("Expected error, but got nil")
  1433  				}
  1434  
  1435  				if !strings.Contains(err.Error(), expectedErrMsg) {
  1436  					t.Fatalf("Expected error: %s\nInstead got: `%s", expectedErrMsg, err.Error())
  1437  				}
  1438  			},
  1439  		},
  1440  		{
  1441  			name: "public IP true and public subnet ID given",
  1442  			machine: clusterv1.Machine{
  1443  				ObjectMeta: metav1.ObjectMeta{
  1444  					Labels: map[string]string{"set": "node"},
  1445  				},
  1446  				Spec: clusterv1.MachineSpec{
  1447  					Bootstrap: clusterv1.Bootstrap{
  1448  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1449  					},
  1450  				},
  1451  			},
  1452  			machineConfig: &infrav1.AWSMachineSpec{
  1453  				AMI: infrav1.AMIReference{
  1454  					ID: aws.String("abc"),
  1455  				},
  1456  				InstanceType: "m5.large",
  1457  				Subnet: &infrav1.AWSResourceReference{
  1458  					ID: aws.String("public-subnet-1"),
  1459  				},
  1460  				PublicIP: aws.Bool(true),
  1461  			},
  1462  			awsCluster: &infrav1.AWSCluster{
  1463  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1464  				Spec: infrav1.AWSClusterSpec{
  1465  					NetworkSpec: infrav1.NetworkSpec{
  1466  						VPC: infrav1.VPCSpec{
  1467  							ID: "vpc-id",
  1468  						},
  1469  						Subnets: infrav1.Subnets{{
  1470  							ID:       "public-subnet-1",
  1471  							IsPublic: true,
  1472  						}},
  1473  					},
  1474  				},
  1475  				Status: infrav1.AWSClusterStatus{
  1476  					Network: infrav1.NetworkStatus{
  1477  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1478  							infrav1.SecurityGroupControlPlane: {
  1479  								ID: "1",
  1480  							},
  1481  							infrav1.SecurityGroupNode: {
  1482  								ID: "2",
  1483  							},
  1484  							infrav1.SecurityGroupLB: {
  1485  								ID: "3",
  1486  							},
  1487  						},
  1488  						APIServerELB: infrav1.ClassicELB{
  1489  							DNSName: "test-apiserver.us-east-1.aws",
  1490  						},
  1491  					},
  1492  				},
  1493  			},
  1494  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1495  				m.
  1496  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1497  						Filters: []*ec2.Filter{
  1498  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1499  							filter.EC2.VPC("vpc-id"),
  1500  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"public-subnet-1"})},
  1501  						},
  1502  					}).
  1503  					Return(&ec2.DescribeSubnetsOutput{
  1504  						Subnets: []*ec2.Subnet{{
  1505  							SubnetId:            aws.String("public-subnet-1"),
  1506  							AvailabilityZone:    aws.String("us-east-1b"),
  1507  							MapPublicIpOnLaunch: aws.Bool(true),
  1508  						}},
  1509  					}, nil)
  1510  				m.
  1511  					RunInstances(gomock.Any()).
  1512  					Return(&ec2.Reservation{
  1513  						Instances: []*ec2.Instance{
  1514  							{
  1515  								State: &ec2.InstanceState{
  1516  									Name: aws.String(ec2.InstanceStateNamePending),
  1517  								},
  1518  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1519  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1520  								},
  1521  								InstanceId:     aws.String("two"),
  1522  								InstanceType:   aws.String("m5.large"),
  1523  								SubnetId:       aws.String("public-subnet-1"),
  1524  								ImageId:        aws.String("ami-1"),
  1525  								RootDeviceName: aws.String("device-1"),
  1526  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1527  									{
  1528  										DeviceName: aws.String("device-1"),
  1529  										Ebs: &ec2.EbsInstanceBlockDevice{
  1530  											VolumeId: aws.String("volume-1"),
  1531  										},
  1532  									},
  1533  								},
  1534  								Placement: &ec2.Placement{
  1535  									AvailabilityZone: &az,
  1536  								},
  1537  							},
  1538  						},
  1539  					}, nil)
  1540  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  1541  					Return(nil)
  1542  			},
  1543  			check: func(instance *infrav1.Instance, err error) {
  1544  				if err != nil {
  1545  					t.Fatalf("did not expect error: %v", err)
  1546  				}
  1547  			},
  1548  		},
  1549  		{
  1550  			name: "public IP true and private subnet ID given",
  1551  			machine: clusterv1.Machine{
  1552  				ObjectMeta: metav1.ObjectMeta{
  1553  					Labels: map[string]string{"set": "node"},
  1554  				},
  1555  				Spec: clusterv1.MachineSpec{
  1556  					Bootstrap: clusterv1.Bootstrap{
  1557  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1558  					},
  1559  				},
  1560  			},
  1561  			machineConfig: &infrav1.AWSMachineSpec{
  1562  				AMI: infrav1.AMIReference{
  1563  					ID: aws.String("abc"),
  1564  				},
  1565  				InstanceType: "m5.large",
  1566  				Subnet: &infrav1.AWSResourceReference{
  1567  					ID: aws.String("private-subnet-1"),
  1568  				},
  1569  				PublicIP: aws.Bool(true),
  1570  			},
  1571  			awsCluster: &infrav1.AWSCluster{
  1572  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1573  				Spec: infrav1.AWSClusterSpec{
  1574  					NetworkSpec: infrav1.NetworkSpec{
  1575  						VPC: infrav1.VPCSpec{
  1576  							ID: "vpc-id",
  1577  						},
  1578  						Subnets: infrav1.Subnets{{
  1579  							ID:       "private-subnet-1",
  1580  							IsPublic: false,
  1581  						}},
  1582  					},
  1583  				},
  1584  				Status: infrav1.AWSClusterStatus{
  1585  					Network: infrav1.NetworkStatus{
  1586  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1587  							infrav1.SecurityGroupControlPlane: {
  1588  								ID: "1",
  1589  							},
  1590  							infrav1.SecurityGroupNode: {
  1591  								ID: "2",
  1592  							},
  1593  							infrav1.SecurityGroupLB: {
  1594  								ID: "3",
  1595  							},
  1596  						},
  1597  						APIServerELB: infrav1.ClassicELB{
  1598  							DNSName: "test-apiserver.us-east-1.aws",
  1599  						},
  1600  					},
  1601  				},
  1602  			},
  1603  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1604  				m.
  1605  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1606  						Filters: []*ec2.Filter{
  1607  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1608  							filter.EC2.VPC("vpc-id"),
  1609  							{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{"private-subnet-1"})},
  1610  						},
  1611  					}).
  1612  					Return(&ec2.DescribeSubnetsOutput{
  1613  						Subnets: []*ec2.Subnet{{
  1614  							SubnetId:            aws.String("private-subnet-1"),
  1615  							AvailabilityZone:    aws.String("us-east-1b"),
  1616  							MapPublicIpOnLaunch: aws.Bool(false),
  1617  						}},
  1618  					}, nil)
  1619  			},
  1620  			check: func(instance *infrav1.Instance, err error) {
  1621  				expectedErrMsg := "failed to run machine \"aws-test1\", found 1 subnets matching criteria but post-filtering failed. subnet \"private-subnet-1\" is a private subnet."
  1622  				if err == nil {
  1623  					t.Fatalf("Expected error, but got nil")
  1624  				}
  1625  
  1626  				if !strings.Contains(err.Error(), expectedErrMsg) {
  1627  					t.Fatalf("Expected error: %s\nInstead got: `%s", expectedErrMsg, err.Error())
  1628  				}
  1629  			},
  1630  		},
  1631  		{
  1632  			name: "both public IP and subnet filter defined",
  1633  			machine: clusterv1.Machine{
  1634  				ObjectMeta: metav1.ObjectMeta{
  1635  					Labels: map[string]string{"set": "node"},
  1636  				},
  1637  				Spec: clusterv1.MachineSpec{
  1638  					Bootstrap: clusterv1.Bootstrap{
  1639  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1640  					},
  1641  				},
  1642  			},
  1643  			machineConfig: &infrav1.AWSMachineSpec{
  1644  				AMI: infrav1.AMIReference{
  1645  					ID: aws.String("abc"),
  1646  				},
  1647  				InstanceType: "m5.large",
  1648  				Subnet: &infrav1.AWSResourceReference{
  1649  					Filters: []infrav1.Filter{{
  1650  						Name:   "tag:some-tag",
  1651  						Values: []string{"some-value"},
  1652  					}},
  1653  				},
  1654  				PublicIP: aws.Bool(true),
  1655  			},
  1656  			awsCluster: &infrav1.AWSCluster{
  1657  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1658  				Spec: infrav1.AWSClusterSpec{
  1659  					NetworkSpec: infrav1.NetworkSpec{
  1660  						VPC: infrav1.VPCSpec{
  1661  							ID: "vpc-id",
  1662  						},
  1663  						Subnets: infrav1.Subnets{
  1664  							infrav1.SubnetSpec{
  1665  								ID:       "private-subnet-1",
  1666  								IsPublic: false,
  1667  							},
  1668  							infrav1.SubnetSpec{
  1669  								ID:       "public-subnet-1",
  1670  								IsPublic: true,
  1671  							},
  1672  						},
  1673  					},
  1674  				},
  1675  				Status: infrav1.AWSClusterStatus{
  1676  					Network: infrav1.NetworkStatus{
  1677  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1678  							infrav1.SecurityGroupControlPlane: {
  1679  								ID: "1",
  1680  							},
  1681  							infrav1.SecurityGroupNode: {
  1682  								ID: "2",
  1683  							},
  1684  							infrav1.SecurityGroupLB: {
  1685  								ID: "3",
  1686  							},
  1687  						},
  1688  						APIServerELB: infrav1.ClassicELB{
  1689  							DNSName: "test-apiserver.us-east-1.aws",
  1690  						},
  1691  					},
  1692  				},
  1693  			},
  1694  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1695  				m.
  1696  					DescribeSubnets(&ec2.DescribeSubnetsInput{
  1697  						Filters: []*ec2.Filter{
  1698  							filter.EC2.SubnetStates(ec2.SubnetStatePending, ec2.SubnetStateAvailable),
  1699  							filter.EC2.VPC("vpc-id"),
  1700  							{Name: aws.String("tag:some-tag"), Values: aws.StringSlice([]string{"some-value"})},
  1701  						},
  1702  					}).
  1703  					Return(&ec2.DescribeSubnetsOutput{
  1704  						Subnets: []*ec2.Subnet{{
  1705  							SubnetId:            aws.String("filtered-subnet-1"),
  1706  							MapPublicIpOnLaunch: aws.Bool(true),
  1707  						}},
  1708  					}, nil)
  1709  				m.
  1710  					RunInstances(gomock.Any()).
  1711  					Return(&ec2.Reservation{
  1712  						Instances: []*ec2.Instance{
  1713  							{
  1714  								State: &ec2.InstanceState{
  1715  									Name: aws.String(ec2.InstanceStateNamePending),
  1716  								},
  1717  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1718  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1719  								},
  1720  								InstanceId:     aws.String("two"),
  1721  								InstanceType:   aws.String("m5.large"),
  1722  								SubnetId:       aws.String("public-subnet-1"),
  1723  								ImageId:        aws.String("ami-1"),
  1724  								RootDeviceName: aws.String("device-1"),
  1725  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1726  									{
  1727  										DeviceName: aws.String("device-1"),
  1728  										Ebs: &ec2.EbsInstanceBlockDevice{
  1729  											VolumeId: aws.String("volume-1"),
  1730  										},
  1731  									},
  1732  								},
  1733  								Placement: &ec2.Placement{
  1734  									AvailabilityZone: &az,
  1735  								},
  1736  							},
  1737  						},
  1738  					}, nil)
  1739  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  1740  					Return(nil)
  1741  			},
  1742  			check: func(instance *infrav1.Instance, err error) {
  1743  				if err != nil {
  1744  					t.Fatalf("did not expect error: %v", err)
  1745  				}
  1746  			},
  1747  		},
  1748  		{
  1749  			name: "public IP true and public subnet exists",
  1750  			machine: clusterv1.Machine{
  1751  				ObjectMeta: metav1.ObjectMeta{
  1752  					Labels: map[string]string{"set": "node"},
  1753  				},
  1754  				Spec: clusterv1.MachineSpec{
  1755  					Bootstrap: clusterv1.Bootstrap{
  1756  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1757  					},
  1758  				},
  1759  			},
  1760  			machineConfig: &infrav1.AWSMachineSpec{
  1761  				AMI: infrav1.AMIReference{
  1762  					ID: aws.String("abc"),
  1763  				},
  1764  				InstanceType: "m5.large",
  1765  				PublicIP:     aws.Bool(true),
  1766  			},
  1767  			awsCluster: &infrav1.AWSCluster{
  1768  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1769  				Spec: infrav1.AWSClusterSpec{
  1770  					NetworkSpec: infrav1.NetworkSpec{
  1771  						VPC: infrav1.VPCSpec{
  1772  							ID: "vpc-id",
  1773  						},
  1774  						Subnets: infrav1.Subnets{
  1775  							infrav1.SubnetSpec{
  1776  								ID:       "private-subnet-1",
  1777  								IsPublic: false,
  1778  							},
  1779  							infrav1.SubnetSpec{
  1780  								ID:       "public-subnet-1",
  1781  								IsPublic: true,
  1782  							},
  1783  						},
  1784  					},
  1785  				},
  1786  				Status: infrav1.AWSClusterStatus{
  1787  					Network: infrav1.NetworkStatus{
  1788  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1789  							infrav1.SecurityGroupControlPlane: {
  1790  								ID: "1",
  1791  							},
  1792  							infrav1.SecurityGroupNode: {
  1793  								ID: "2",
  1794  							},
  1795  							infrav1.SecurityGroupLB: {
  1796  								ID: "3",
  1797  							},
  1798  						},
  1799  						APIServerELB: infrav1.ClassicELB{
  1800  							DNSName: "test-apiserver.us-east-1.aws",
  1801  						},
  1802  					},
  1803  				},
  1804  			},
  1805  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1806  				m.
  1807  					RunInstances(gomock.Any()).
  1808  					Return(&ec2.Reservation{
  1809  						Instances: []*ec2.Instance{
  1810  							{
  1811  								State: &ec2.InstanceState{
  1812  									Name: aws.String(ec2.InstanceStateNamePending),
  1813  								},
  1814  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1815  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1816  								},
  1817  								InstanceId:     aws.String("two"),
  1818  								InstanceType:   aws.String("m5.large"),
  1819  								SubnetId:       aws.String("public-subnet-1"),
  1820  								ImageId:        aws.String("ami-1"),
  1821  								RootDeviceName: aws.String("device-1"),
  1822  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1823  									{
  1824  										DeviceName: aws.String("device-1"),
  1825  										Ebs: &ec2.EbsInstanceBlockDevice{
  1826  											VolumeId: aws.String("volume-1"),
  1827  										},
  1828  									},
  1829  								},
  1830  								Placement: &ec2.Placement{
  1831  									AvailabilityZone: &az,
  1832  								},
  1833  							},
  1834  						},
  1835  					}, nil)
  1836  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  1837  					Return(nil)
  1838  			},
  1839  			check: func(instance *infrav1.Instance, err error) {
  1840  				if err != nil {
  1841  					t.Fatalf("did not expect error: %v", err)
  1842  				}
  1843  			},
  1844  		},
  1845  		{
  1846  			name: "public IP true and no public subnet exists",
  1847  			machine: clusterv1.Machine{
  1848  				ObjectMeta: metav1.ObjectMeta{
  1849  					Labels: map[string]string{"set": "node"},
  1850  				},
  1851  				Spec: clusterv1.MachineSpec{
  1852  					Bootstrap: clusterv1.Bootstrap{
  1853  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1854  					},
  1855  				},
  1856  			},
  1857  			machineConfig: &infrav1.AWSMachineSpec{
  1858  				AMI: infrav1.AMIReference{
  1859  					ID: aws.String("abc"),
  1860  				},
  1861  				InstanceType: "m5.large",
  1862  				PublicIP:     aws.Bool(true),
  1863  			},
  1864  			awsCluster: &infrav1.AWSCluster{
  1865  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1866  				Spec: infrav1.AWSClusterSpec{
  1867  					NetworkSpec: infrav1.NetworkSpec{
  1868  						VPC: infrav1.VPCSpec{
  1869  							ID: "vpc-id",
  1870  						},
  1871  						Subnets: infrav1.Subnets{
  1872  							infrav1.SubnetSpec{
  1873  								ID:       "private-subnet-1",
  1874  								IsPublic: false,
  1875  							},
  1876  						},
  1877  					},
  1878  				},
  1879  				Status: infrav1.AWSClusterStatus{
  1880  					Network: infrav1.NetworkStatus{
  1881  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1882  							infrav1.SecurityGroupControlPlane: {
  1883  								ID: "1",
  1884  							},
  1885  							infrav1.SecurityGroupNode: {
  1886  								ID: "2",
  1887  							},
  1888  							infrav1.SecurityGroupLB: {
  1889  								ID: "3",
  1890  							},
  1891  						},
  1892  						APIServerELB: infrav1.ClassicELB{
  1893  							DNSName: "test-apiserver.us-east-1.aws",
  1894  						},
  1895  					},
  1896  				},
  1897  			},
  1898  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1899  			},
  1900  			check: func(instance *infrav1.Instance, err error) {
  1901  				expectedErrMsg := "failed to run machine \"aws-test1\" with public IP, no public subnets available"
  1902  				if err == nil {
  1903  					t.Fatalf("Expected error, but got nil")
  1904  				}
  1905  
  1906  				if !strings.Contains(err.Error(), expectedErrMsg) {
  1907  					t.Fatalf("Expected error: %s\nInstead got: %s", expectedErrMsg, err.Error())
  1908  				}
  1909  			},
  1910  		},
  1911  		{
  1912  			name: "with multiple block device mappings",
  1913  			machine: clusterv1.Machine{
  1914  				ObjectMeta: metav1.ObjectMeta{
  1915  					Labels: map[string]string{"set": "node"},
  1916  				},
  1917  				Spec: clusterv1.MachineSpec{
  1918  					Bootstrap: clusterv1.Bootstrap{
  1919  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  1920  					},
  1921  				},
  1922  			},
  1923  			machineConfig: &infrav1.AWSMachineSpec{
  1924  				AMI: infrav1.AMIReference{
  1925  					ID: aws.String("abc"),
  1926  				},
  1927  				InstanceType: "m5.large",
  1928  				NonRootVolumes: []infrav1.Volume{{
  1929  					DeviceName: "device-2",
  1930  					Size:       8,
  1931  				}},
  1932  			},
  1933  			awsCluster: &infrav1.AWSCluster{
  1934  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  1935  				Spec: infrav1.AWSClusterSpec{
  1936  					NetworkSpec: infrav1.NetworkSpec{
  1937  						Subnets: infrav1.Subnets{
  1938  							infrav1.SubnetSpec{
  1939  								ID:       "subnet-1",
  1940  								IsPublic: false,
  1941  							},
  1942  							infrav1.SubnetSpec{
  1943  								IsPublic: false,
  1944  							},
  1945  						},
  1946  					},
  1947  				},
  1948  				Status: infrav1.AWSClusterStatus{
  1949  					Network: infrav1.NetworkStatus{
  1950  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  1951  							infrav1.SecurityGroupControlPlane: {
  1952  								ID: "1",
  1953  							},
  1954  							infrav1.SecurityGroupNode: {
  1955  								ID: "2",
  1956  							},
  1957  							infrav1.SecurityGroupLB: {
  1958  								ID: "3",
  1959  							},
  1960  						},
  1961  						APIServerELB: infrav1.ClassicELB{
  1962  							DNSName: "test-apiserver.us-east-1.aws",
  1963  						},
  1964  					},
  1965  				},
  1966  			},
  1967  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  1968  				m. // TODO: Restore these parameters, but with the tags as well
  1969  					RunInstances(gomock.Any()).
  1970  					Return(&ec2.Reservation{
  1971  						Instances: []*ec2.Instance{
  1972  							{
  1973  								State: &ec2.InstanceState{
  1974  									Name: aws.String(ec2.InstanceStateNamePending),
  1975  								},
  1976  								IamInstanceProfile: &ec2.IamInstanceProfile{
  1977  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  1978  								},
  1979  								InstanceId:     aws.String("two"),
  1980  								InstanceType:   aws.String("m5.large"),
  1981  								SubnetId:       aws.String("subnet-1"),
  1982  								ImageId:        aws.String("ami-1"),
  1983  								RootDeviceName: aws.String("device-1"),
  1984  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  1985  									{
  1986  										DeviceName: aws.String("device-1"),
  1987  										Ebs: &ec2.EbsInstanceBlockDevice{
  1988  											VolumeId: aws.String("volume-1"),
  1989  										},
  1990  									},
  1991  									{
  1992  										DeviceName: aws.String("device-2"),
  1993  										Ebs: &ec2.EbsInstanceBlockDevice{
  1994  											VolumeId: aws.String("volume-2"),
  1995  										},
  1996  									},
  1997  								},
  1998  								Placement: &ec2.Placement{
  1999  									AvailabilityZone: &az,
  2000  								},
  2001  							},
  2002  						},
  2003  					}, nil)
  2004  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2005  					Return(nil)
  2006  			},
  2007  			check: func(instance *infrav1.Instance, err error) {
  2008  				if err != nil {
  2009  					t.Fatalf("did not expect error: %v", err)
  2010  				}
  2011  			},
  2012  		},
  2013  		{
  2014  			name: "with dedicated tenancy cloud-config",
  2015  			machine: clusterv1.Machine{
  2016  				ObjectMeta: metav1.ObjectMeta{
  2017  					Labels:    map[string]string{"set": "node"},
  2018  					Namespace: "default",
  2019  					Name:      "machine-aws-test1",
  2020  				},
  2021  				Spec: clusterv1.MachineSpec{
  2022  					Bootstrap: clusterv1.Bootstrap{
  2023  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2024  					},
  2025  				},
  2026  			},
  2027  			machineConfig: &infrav1.AWSMachineSpec{
  2028  				AMI: infrav1.AMIReference{
  2029  					ID: aws.String("abc"),
  2030  				},
  2031  				InstanceType:         "m5.large",
  2032  				Tenancy:              "dedicated",
  2033  				UncompressedUserData: &isUncompressedFalse,
  2034  			},
  2035  			awsCluster: &infrav1.AWSCluster{
  2036  				Spec: infrav1.AWSClusterSpec{
  2037  					NetworkSpec: infrav1.NetworkSpec{
  2038  						Subnets: infrav1.Subnets{
  2039  							infrav1.SubnetSpec{
  2040  								ID:       "subnet-1",
  2041  								IsPublic: false,
  2042  							},
  2043  							infrav1.SubnetSpec{
  2044  								IsPublic: false,
  2045  							},
  2046  						},
  2047  					},
  2048  				},
  2049  				Status: infrav1.AWSClusterStatus{
  2050  					Network: infrav1.NetworkStatus{
  2051  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2052  							infrav1.SecurityGroupControlPlane: {
  2053  								ID: "1",
  2054  							},
  2055  							infrav1.SecurityGroupNode: {
  2056  								ID: "2",
  2057  							},
  2058  							infrav1.SecurityGroupLB: {
  2059  								ID: "3",
  2060  							},
  2061  						},
  2062  						APIServerELB: infrav1.ClassicELB{
  2063  							DNSName: "test-apiserver.us-east-1.aws",
  2064  						},
  2065  					},
  2066  				},
  2067  			},
  2068  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2069  				m. // TODO: Restore these parameters, but with the tags as well
  2070  					RunInstances(gomock.Eq(&ec2.RunInstancesInput{
  2071  						ImageId:      aws.String("abc"),
  2072  						InstanceType: aws.String("m5.large"),
  2073  						KeyName:      aws.String("default"),
  2074  						MaxCount:     aws.Int64(1),
  2075  						MinCount:     aws.Int64(1),
  2076  						Placement: &ec2.Placement{
  2077  							Tenancy: &tenancy,
  2078  						},
  2079  						SecurityGroupIds: []*string{aws.String("2"), aws.String("3")},
  2080  						SubnetId:         aws.String("subnet-1"),
  2081  						TagSpecifications: []*ec2.TagSpecification{
  2082  							{
  2083  								ResourceType: aws.String("instance"),
  2084  								Tags: []*ec2.Tag{
  2085  									{
  2086  										Key:   aws.String("MachineName"),
  2087  										Value: aws.String("default/machine-aws-test1"),
  2088  									},
  2089  									{
  2090  										Key:   aws.String("Name"),
  2091  										Value: aws.String("aws-test1"),
  2092  									},
  2093  									{
  2094  										Key:   aws.String("kubernetes.io/cluster/test1"),
  2095  										Value: aws.String("owned"),
  2096  									},
  2097  									{
  2098  										Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test1"),
  2099  										Value: aws.String("owned"),
  2100  									},
  2101  									{
  2102  										Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
  2103  										Value: aws.String("node"),
  2104  									},
  2105  								},
  2106  							},
  2107  						},
  2108  						UserData: aws.String(base64.StdEncoding.EncodeToString(userDataCompressed)),
  2109  					})).
  2110  					Return(&ec2.Reservation{
  2111  						Instances: []*ec2.Instance{
  2112  							{
  2113  								State: &ec2.InstanceState{
  2114  									Name: aws.String(ec2.InstanceStateNamePending),
  2115  								},
  2116  								IamInstanceProfile: &ec2.IamInstanceProfile{
  2117  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2118  								},
  2119  								InstanceId:     aws.String("two"),
  2120  								InstanceType:   aws.String("m5.large"),
  2121  								SubnetId:       aws.String("subnet-1"),
  2122  								ImageId:        aws.String("ami-1"),
  2123  								RootDeviceName: aws.String("device-1"),
  2124  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2125  									{
  2126  										DeviceName: aws.String("device-1"),
  2127  										Ebs: &ec2.EbsInstanceBlockDevice{
  2128  											VolumeId: aws.String("volume-1"),
  2129  										},
  2130  									},
  2131  								},
  2132  								Placement: &ec2.Placement{
  2133  									AvailabilityZone: &az,
  2134  									Tenancy:          &tenancy,
  2135  								},
  2136  							},
  2137  						},
  2138  					}, nil)
  2139  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2140  					Return(nil)
  2141  			},
  2142  			check: func(instance *infrav1.Instance, err error) {
  2143  				if err != nil {
  2144  					t.Fatalf("did not expect error: %v", err)
  2145  				}
  2146  			},
  2147  		},
  2148  		{
  2149  			name: "with dedicated tenancy ignition",
  2150  			machine: clusterv1.Machine{
  2151  				ObjectMeta: metav1.ObjectMeta{
  2152  					Labels:    map[string]string{"set": "node"},
  2153  					Namespace: "default",
  2154  					Name:      "machine-aws-test1",
  2155  				},
  2156  				Spec: clusterv1.MachineSpec{
  2157  					Bootstrap: clusterv1.Bootstrap{
  2158  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2159  					},
  2160  				},
  2161  			},
  2162  			machineConfig: &infrav1.AWSMachineSpec{
  2163  				AMI: infrav1.AMIReference{
  2164  					ID: aws.String("abc"),
  2165  				},
  2166  				InstanceType:         "m5.large",
  2167  				Tenancy:              "dedicated",
  2168  				UncompressedUserData: &isUncompressedTrue,
  2169  				Ignition:             &infrav1.Ignition{},
  2170  			},
  2171  			awsCluster: &infrav1.AWSCluster{
  2172  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2173  				Spec: infrav1.AWSClusterSpec{
  2174  					NetworkSpec: infrav1.NetworkSpec{
  2175  						Subnets: infrav1.Subnets{
  2176  							infrav1.SubnetSpec{
  2177  								ID:       "subnet-1",
  2178  								IsPublic: false,
  2179  							},
  2180  							infrav1.SubnetSpec{
  2181  								IsPublic: false,
  2182  							},
  2183  						},
  2184  					},
  2185  				},
  2186  				Status: infrav1.AWSClusterStatus{
  2187  					Network: infrav1.NetworkStatus{
  2188  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2189  							infrav1.SecurityGroupControlPlane: {
  2190  								ID: "1",
  2191  							},
  2192  							infrav1.SecurityGroupNode: {
  2193  								ID: "2",
  2194  							},
  2195  							infrav1.SecurityGroupLB: {
  2196  								ID: "3",
  2197  							},
  2198  						},
  2199  						APIServerELB: infrav1.ClassicELB{
  2200  							DNSName: "test-apiserver.us-east-1.aws",
  2201  						},
  2202  					},
  2203  				},
  2204  			},
  2205  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2206  				m. // TODO: Restore these parameters, but with the tags as well
  2207  					RunInstances(gomock.Eq(&ec2.RunInstancesInput{
  2208  						ImageId:      aws.String("abc"),
  2209  						InstanceType: aws.String("m5.large"),
  2210  						KeyName:      aws.String("default"),
  2211  						MaxCount:     aws.Int64(1),
  2212  						MinCount:     aws.Int64(1),
  2213  						Placement: &ec2.Placement{
  2214  							Tenancy: &tenancy,
  2215  						},
  2216  						SecurityGroupIds: []*string{aws.String("2"), aws.String("3")},
  2217  						SubnetId:         aws.String("subnet-1"),
  2218  						TagSpecifications: []*ec2.TagSpecification{
  2219  							{
  2220  								ResourceType: aws.String("instance"),
  2221  								Tags: []*ec2.Tag{
  2222  									{
  2223  										Key:   aws.String("MachineName"),
  2224  										Value: aws.String("default/machine-aws-test1"),
  2225  									},
  2226  									{
  2227  										Key:   aws.String("Name"),
  2228  										Value: aws.String("aws-test1"),
  2229  									},
  2230  									{
  2231  										Key:   aws.String("kubernetes.io/cluster/test1"),
  2232  										Value: aws.String("owned"),
  2233  									},
  2234  									{
  2235  										Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test1"),
  2236  										Value: aws.String("owned"),
  2237  									},
  2238  									{
  2239  										Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
  2240  										Value: aws.String("node"),
  2241  									},
  2242  								},
  2243  							},
  2244  						},
  2245  						UserData: aws.String(base64.StdEncoding.EncodeToString(data)),
  2246  					})).
  2247  					Return(&ec2.Reservation{
  2248  						Instances: []*ec2.Instance{
  2249  							{
  2250  								State: &ec2.InstanceState{
  2251  									Name: aws.String(ec2.InstanceStateNamePending),
  2252  								},
  2253  								IamInstanceProfile: &ec2.IamInstanceProfile{
  2254  									Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2255  								},
  2256  								InstanceId:     aws.String("two"),
  2257  								InstanceType:   aws.String("m5.large"),
  2258  								SubnetId:       aws.String("subnet-1"),
  2259  								ImageId:        aws.String("ami-1"),
  2260  								RootDeviceName: aws.String("device-1"),
  2261  								BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2262  									{
  2263  										DeviceName: aws.String("device-1"),
  2264  										Ebs: &ec2.EbsInstanceBlockDevice{
  2265  											VolumeId: aws.String("volume-1"),
  2266  										},
  2267  									},
  2268  								},
  2269  								Placement: &ec2.Placement{
  2270  									AvailabilityZone: &az,
  2271  									Tenancy:          &tenancy,
  2272  								},
  2273  							},
  2274  						},
  2275  					}, nil)
  2276  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2277  					Return(nil)
  2278  			},
  2279  			check: func(instance *infrav1.Instance, err error) {
  2280  				if err != nil {
  2281  					t.Fatalf("did not expect error: %v", err)
  2282  				}
  2283  			},
  2284  		},
  2285  		{
  2286  			name: "expect the default SSH key when none is provided",
  2287  			machine: clusterv1.Machine{
  2288  				ObjectMeta: metav1.ObjectMeta{
  2289  					Labels: map[string]string{"set": "node"},
  2290  				},
  2291  				Spec: clusterv1.MachineSpec{
  2292  					Bootstrap: clusterv1.Bootstrap{
  2293  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2294  					},
  2295  					Version: pointer.StringPtr("v1.16.1"),
  2296  				},
  2297  			},
  2298  			machineConfig: &infrav1.AWSMachineSpec{
  2299  				InstanceType: "m5.large",
  2300  			},
  2301  			awsCluster: &infrav1.AWSCluster{
  2302  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2303  				Spec: infrav1.AWSClusterSpec{
  2304  					NetworkSpec: infrav1.NetworkSpec{
  2305  						Subnets: infrav1.Subnets{
  2306  							infrav1.SubnetSpec{
  2307  								ID:       "subnet-1",
  2308  								IsPublic: false,
  2309  							},
  2310  							infrav1.SubnetSpec{
  2311  								IsPublic: false,
  2312  							},
  2313  						},
  2314  					},
  2315  				},
  2316  				Status: infrav1.AWSClusterStatus{
  2317  					Network: infrav1.NetworkStatus{
  2318  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2319  							infrav1.SecurityGroupControlPlane: {
  2320  								ID: "1",
  2321  							},
  2322  							infrav1.SecurityGroupNode: {
  2323  								ID: "2",
  2324  							},
  2325  							infrav1.SecurityGroupLB: {
  2326  								ID: "3",
  2327  							},
  2328  						},
  2329  						APIServerELB: infrav1.ClassicELB{
  2330  							DNSName: "test-apiserver.us-east-1.aws",
  2331  						},
  2332  					},
  2333  				},
  2334  			},
  2335  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2336  				m.
  2337  					DescribeImages(gomock.Any()).
  2338  					Return(&ec2.DescribeImagesOutput{
  2339  						Images: []*ec2.Image{
  2340  							{
  2341  								Name:         aws.String("ami-1"),
  2342  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2343  							},
  2344  						},
  2345  					}, nil)
  2346  				m. // TODO: Restore these parameters, but with the tags as well
  2347  					RunInstances(gomock.Any()).
  2348  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2349  						if input.KeyName == nil {
  2350  							t.Fatal("Expected key name not to be nil")
  2351  						}
  2352  						if *input.KeyName != defaultSSHKeyName {
  2353  							t.Fatalf("Expected SSH key name to be '%s', not '%s'", defaultSSHKeyName, *input.KeyName)
  2354  						}
  2355  						return &ec2.Reservation{
  2356  							Instances: []*ec2.Instance{
  2357  								{
  2358  									State: &ec2.InstanceState{
  2359  										Name: aws.String(ec2.InstanceStateNamePending),
  2360  									},
  2361  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2362  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2363  									},
  2364  									InstanceId:     aws.String("two"),
  2365  									InstanceType:   aws.String("m5.large"),
  2366  									SubnetId:       aws.String("subnet-1"),
  2367  									ImageId:        aws.String("ami-1"),
  2368  									RootDeviceName: aws.String("device-1"),
  2369  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2370  										{
  2371  											DeviceName: aws.String("device-1"),
  2372  											Ebs: &ec2.EbsInstanceBlockDevice{
  2373  												VolumeId: aws.String("volume-1"),
  2374  											},
  2375  										},
  2376  									},
  2377  									Placement: &ec2.Placement{
  2378  										AvailabilityZone: &az,
  2379  									},
  2380  								},
  2381  							},
  2382  						}, nil
  2383  					})
  2384  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2385  					Return(nil)
  2386  			},
  2387  			check: func(instance *infrav1.Instance, err error) {
  2388  				if err != nil {
  2389  					t.Fatalf("did not expect error: %v", err)
  2390  				}
  2391  			},
  2392  		},
  2393  		{
  2394  			name: "expect to use the cluster level ssh key name when no machine key name is provided",
  2395  			machine: clusterv1.Machine{
  2396  				ObjectMeta: metav1.ObjectMeta{
  2397  					Labels: map[string]string{"set": "node"},
  2398  				},
  2399  				Spec: clusterv1.MachineSpec{
  2400  					Bootstrap: clusterv1.Bootstrap{
  2401  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2402  					},
  2403  					Version: pointer.StringPtr("v1.16.1"),
  2404  				},
  2405  			},
  2406  			machineConfig: &infrav1.AWSMachineSpec{
  2407  				InstanceType: "m5.large",
  2408  			},
  2409  			awsCluster: &infrav1.AWSCluster{
  2410  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2411  				Spec: infrav1.AWSClusterSpec{
  2412  					NetworkSpec: infrav1.NetworkSpec{
  2413  						Subnets: infrav1.Subnets{
  2414  							infrav1.SubnetSpec{
  2415  								ID:       "subnet-1",
  2416  								IsPublic: false,
  2417  							},
  2418  							infrav1.SubnetSpec{
  2419  								IsPublic: false,
  2420  							},
  2421  						},
  2422  					},
  2423  					SSHKeyName: aws.String("specific-cluster-key-name"),
  2424  				},
  2425  				Status: infrav1.AWSClusterStatus{
  2426  					Network: infrav1.NetworkStatus{
  2427  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2428  							infrav1.SecurityGroupControlPlane: {
  2429  								ID: "1",
  2430  							},
  2431  							infrav1.SecurityGroupNode: {
  2432  								ID: "2",
  2433  							},
  2434  							infrav1.SecurityGroupLB: {
  2435  								ID: "3",
  2436  							},
  2437  						},
  2438  						APIServerELB: infrav1.ClassicELB{
  2439  							DNSName: "test-apiserver.us-east-1.aws",
  2440  						},
  2441  					},
  2442  				},
  2443  			},
  2444  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2445  				m.
  2446  					DescribeImages(gomock.Any()).
  2447  					Return(&ec2.DescribeImagesOutput{
  2448  						Images: []*ec2.Image{
  2449  							{
  2450  								Name:         aws.String("ami-1"),
  2451  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2452  							},
  2453  						},
  2454  					}, nil)
  2455  				m. // TODO: Restore these parameters, but with the tags as well
  2456  					RunInstances(gomock.Any()).
  2457  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2458  						if input.KeyName == nil {
  2459  							t.Fatal("Expected key name not to be nil")
  2460  						}
  2461  						if *input.KeyName != "specific-cluster-key-name" {
  2462  							t.Fatalf("Expected SSH key name to be '%s', not '%s'", "specific-cluster-key-name", *input.KeyName)
  2463  						}
  2464  						return &ec2.Reservation{
  2465  							Instances: []*ec2.Instance{
  2466  								{
  2467  									State: &ec2.InstanceState{
  2468  										Name: aws.String(ec2.InstanceStateNamePending),
  2469  									},
  2470  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2471  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2472  									},
  2473  									InstanceId:     aws.String("two"),
  2474  									InstanceType:   aws.String("m5.large"),
  2475  									SubnetId:       aws.String("subnet-1"),
  2476  									ImageId:        aws.String("ami-1"),
  2477  									RootDeviceName: aws.String("device-1"),
  2478  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2479  										{
  2480  											DeviceName: aws.String("device-1"),
  2481  											Ebs: &ec2.EbsInstanceBlockDevice{
  2482  												VolumeId: aws.String("volume-1"),
  2483  											},
  2484  										},
  2485  									},
  2486  									Placement: &ec2.Placement{
  2487  										AvailabilityZone: &az,
  2488  									},
  2489  								},
  2490  							},
  2491  						}, nil
  2492  					})
  2493  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2494  					Return(nil)
  2495  			},
  2496  			check: func(instance *infrav1.Instance, err error) {
  2497  				if err != nil {
  2498  					t.Fatalf("did not expect error: %v", err)
  2499  				}
  2500  			},
  2501  		},
  2502  		{
  2503  			name: "expect to use the machine level ssh key name when both cluster and machine key names are provided",
  2504  			machine: clusterv1.Machine{
  2505  				ObjectMeta: metav1.ObjectMeta{
  2506  					Labels: map[string]string{"set": "node"},
  2507  				},
  2508  				Spec: clusterv1.MachineSpec{
  2509  					Bootstrap: clusterv1.Bootstrap{
  2510  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2511  					},
  2512  					Version: pointer.StringPtr("v1.16.1"),
  2513  				},
  2514  			},
  2515  			machineConfig: &infrav1.AWSMachineSpec{
  2516  				InstanceType: "m5.large",
  2517  				SSHKeyName:   aws.String("specific-machine-ssh-key-name"),
  2518  			},
  2519  			awsCluster: &infrav1.AWSCluster{
  2520  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2521  				Spec: infrav1.AWSClusterSpec{
  2522  					NetworkSpec: infrav1.NetworkSpec{
  2523  						Subnets: infrav1.Subnets{
  2524  							infrav1.SubnetSpec{
  2525  								ID:       "subnet-1",
  2526  								IsPublic: false,
  2527  							},
  2528  							infrav1.SubnetSpec{
  2529  								IsPublic: false,
  2530  							},
  2531  						},
  2532  					},
  2533  					SSHKeyName: aws.String("specific-cluster-key-name"),
  2534  				},
  2535  				Status: infrav1.AWSClusterStatus{
  2536  					Network: infrav1.NetworkStatus{
  2537  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2538  							infrav1.SecurityGroupControlPlane: {
  2539  								ID: "1",
  2540  							},
  2541  							infrav1.SecurityGroupNode: {
  2542  								ID: "2",
  2543  							},
  2544  							infrav1.SecurityGroupLB: {
  2545  								ID: "3",
  2546  							},
  2547  						},
  2548  						APIServerELB: infrav1.ClassicELB{
  2549  							DNSName: "test-apiserver.us-east-1.aws",
  2550  						},
  2551  					},
  2552  				},
  2553  			},
  2554  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2555  				m.
  2556  					DescribeImages(gomock.Any()).
  2557  					Return(&ec2.DescribeImagesOutput{
  2558  						Images: []*ec2.Image{
  2559  							{
  2560  								Name:         aws.String("ami-1"),
  2561  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2562  							},
  2563  						},
  2564  					}, nil)
  2565  				m. // TODO: Restore these parameters, but with the tags as well
  2566  					RunInstances(gomock.Any()).
  2567  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2568  						if input.KeyName == nil {
  2569  							t.Fatal("Expected key name not to be nil")
  2570  						}
  2571  						if *input.KeyName != "specific-machine-ssh-key-name" {
  2572  							t.Fatalf("Expected SSH key name to be '%s', not '%s'", "specific-machine-ssh-key-name", *input.KeyName)
  2573  						}
  2574  						return &ec2.Reservation{
  2575  							Instances: []*ec2.Instance{
  2576  								{
  2577  									State: &ec2.InstanceState{
  2578  										Name: aws.String(ec2.InstanceStateNamePending),
  2579  									},
  2580  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2581  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2582  									},
  2583  									InstanceId:     aws.String("two"),
  2584  									InstanceType:   aws.String("m5.large"),
  2585  									SubnetId:       aws.String("subnet-1"),
  2586  									ImageId:        aws.String("ami-1"),
  2587  									RootDeviceName: aws.String("device-1"),
  2588  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2589  										{
  2590  											DeviceName: aws.String("device-1"),
  2591  											Ebs: &ec2.EbsInstanceBlockDevice{
  2592  												VolumeId: aws.String("volume-1"),
  2593  											},
  2594  										},
  2595  									},
  2596  									Placement: &ec2.Placement{
  2597  										AvailabilityZone: &az,
  2598  									},
  2599  								},
  2600  							},
  2601  						}, nil
  2602  					})
  2603  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2604  					Return(nil)
  2605  			},
  2606  			check: func(instance *infrav1.Instance, err error) {
  2607  				if err != nil {
  2608  					t.Fatalf("did not expect error: %v", err)
  2609  				}
  2610  			},
  2611  		},
  2612  		{
  2613  			name: "expect ssh key to be unset when cluster key name is empty string and machine key name is nil",
  2614  			machine: clusterv1.Machine{
  2615  				ObjectMeta: metav1.ObjectMeta{
  2616  					Labels: map[string]string{"set": "node"},
  2617  				},
  2618  				Spec: clusterv1.MachineSpec{
  2619  					Bootstrap: clusterv1.Bootstrap{
  2620  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2621  					},
  2622  					Version: pointer.StringPtr("v1.16.1"),
  2623  				},
  2624  			},
  2625  			machineConfig: &infrav1.AWSMachineSpec{
  2626  				InstanceType: "m5.large",
  2627  				SSHKeyName:   nil,
  2628  			},
  2629  			awsCluster: &infrav1.AWSCluster{
  2630  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2631  				Spec: infrav1.AWSClusterSpec{
  2632  					NetworkSpec: infrav1.NetworkSpec{
  2633  						Subnets: infrav1.Subnets{
  2634  							infrav1.SubnetSpec{
  2635  								ID:       "subnet-1",
  2636  								IsPublic: false,
  2637  							},
  2638  							infrav1.SubnetSpec{
  2639  								IsPublic: false,
  2640  							},
  2641  						},
  2642  					},
  2643  					SSHKeyName: aws.String(""),
  2644  				},
  2645  				Status: infrav1.AWSClusterStatus{
  2646  					Network: infrav1.NetworkStatus{
  2647  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2648  							infrav1.SecurityGroupControlPlane: {
  2649  								ID: "1",
  2650  							},
  2651  							infrav1.SecurityGroupNode: {
  2652  								ID: "2",
  2653  							},
  2654  							infrav1.SecurityGroupLB: {
  2655  								ID: "3",
  2656  							},
  2657  						},
  2658  						APIServerELB: infrav1.ClassicELB{
  2659  							DNSName: "test-apiserver.us-east-1.aws",
  2660  						},
  2661  					},
  2662  				},
  2663  			},
  2664  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2665  				m.
  2666  					DescribeImages(gomock.Any()).
  2667  					Return(&ec2.DescribeImagesOutput{
  2668  						Images: []*ec2.Image{
  2669  							{
  2670  								Name:         aws.String("ami-1"),
  2671  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2672  							},
  2673  						},
  2674  					}, nil)
  2675  				m. // TODO: Restore these parameters, but with the tags as well
  2676  					RunInstances(gomock.Any()).
  2677  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2678  						if input.KeyName != nil {
  2679  							t.Fatalf("Expected key name to be nil/unspecified, not '%s'", *input.KeyName)
  2680  						}
  2681  						return &ec2.Reservation{
  2682  							Instances: []*ec2.Instance{
  2683  								{
  2684  									State: &ec2.InstanceState{
  2685  										Name: aws.String(ec2.InstanceStateNamePending),
  2686  									},
  2687  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2688  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2689  									},
  2690  									InstanceId:     aws.String("two"),
  2691  									InstanceType:   aws.String("m5.large"),
  2692  									SubnetId:       aws.String("subnet-1"),
  2693  									ImageId:        aws.String("ami-1"),
  2694  									RootDeviceName: aws.String("device-1"),
  2695  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2696  										{
  2697  											DeviceName: aws.String("device-1"),
  2698  											Ebs: &ec2.EbsInstanceBlockDevice{
  2699  												VolumeId: aws.String("volume-1"),
  2700  											},
  2701  										},
  2702  									},
  2703  									Placement: &ec2.Placement{
  2704  										AvailabilityZone: &az,
  2705  									},
  2706  								},
  2707  							},
  2708  						}, nil
  2709  					})
  2710  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2711  					Return(nil)
  2712  			},
  2713  			check: func(instance *infrav1.Instance, err error) {
  2714  				if err != nil {
  2715  					t.Fatalf("did not expect error: %v", err)
  2716  				}
  2717  			},
  2718  		},
  2719  		{
  2720  			name: "expect ssh key to be unset when cluster key name is empty string and machine key name is empty string",
  2721  			machine: clusterv1.Machine{
  2722  				ObjectMeta: metav1.ObjectMeta{
  2723  					Labels: map[string]string{"set": "node"},
  2724  				},
  2725  				Spec: clusterv1.MachineSpec{
  2726  					Bootstrap: clusterv1.Bootstrap{
  2727  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2728  					},
  2729  					Version: pointer.StringPtr("v1.16.1"),
  2730  				},
  2731  			},
  2732  			machineConfig: &infrav1.AWSMachineSpec{
  2733  				InstanceType: "m5.large",
  2734  				SSHKeyName:   aws.String(""),
  2735  			},
  2736  			awsCluster: &infrav1.AWSCluster{
  2737  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2738  				Spec: infrav1.AWSClusterSpec{
  2739  					NetworkSpec: infrav1.NetworkSpec{
  2740  						Subnets: infrav1.Subnets{
  2741  							infrav1.SubnetSpec{
  2742  								ID:       "subnet-1",
  2743  								IsPublic: false,
  2744  							},
  2745  							infrav1.SubnetSpec{
  2746  								IsPublic: false,
  2747  							},
  2748  						},
  2749  					},
  2750  					SSHKeyName: aws.String(""),
  2751  				},
  2752  				Status: infrav1.AWSClusterStatus{
  2753  					Network: infrav1.NetworkStatus{
  2754  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2755  							infrav1.SecurityGroupControlPlane: {
  2756  								ID: "1",
  2757  							},
  2758  							infrav1.SecurityGroupNode: {
  2759  								ID: "2",
  2760  							},
  2761  							infrav1.SecurityGroupLB: {
  2762  								ID: "3",
  2763  							},
  2764  						},
  2765  						APIServerELB: infrav1.ClassicELB{
  2766  							DNSName: "test-apiserver.us-east-1.aws",
  2767  						},
  2768  					},
  2769  				},
  2770  			},
  2771  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2772  				m.
  2773  					DescribeImages(gomock.Any()).
  2774  					Return(&ec2.DescribeImagesOutput{
  2775  						Images: []*ec2.Image{
  2776  							{
  2777  								Name:         aws.String("ami-1"),
  2778  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2779  							},
  2780  						},
  2781  					}, nil)
  2782  				m. // TODO: Restore these parameters, but with the tags as well
  2783  					RunInstances(gomock.Any()).
  2784  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2785  						if input.KeyName != nil {
  2786  							t.Fatalf("Expected key name to be nil/unspecified, not '%s'", *input.KeyName)
  2787  						}
  2788  						return &ec2.Reservation{
  2789  							Instances: []*ec2.Instance{
  2790  								{
  2791  									State: &ec2.InstanceState{
  2792  										Name: aws.String(ec2.InstanceStateNamePending),
  2793  									},
  2794  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2795  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2796  									},
  2797  									InstanceId:     aws.String("two"),
  2798  									InstanceType:   aws.String("m5.large"),
  2799  									SubnetId:       aws.String("subnet-1"),
  2800  									ImageId:        aws.String("ami-1"),
  2801  									RootDeviceName: aws.String("device-1"),
  2802  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2803  										{
  2804  											DeviceName: aws.String("device-1"),
  2805  											Ebs: &ec2.EbsInstanceBlockDevice{
  2806  												VolumeId: aws.String("volume-1"),
  2807  											},
  2808  										},
  2809  									},
  2810  									Placement: &ec2.Placement{
  2811  										AvailabilityZone: &az,
  2812  									},
  2813  								},
  2814  							},
  2815  						}, nil
  2816  					})
  2817  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2818  					Return(nil)
  2819  			},
  2820  			check: func(instance *infrav1.Instance, err error) {
  2821  				if err != nil {
  2822  					t.Fatalf("did not expect error: %v", err)
  2823  				}
  2824  			},
  2825  		},
  2826  		{
  2827  			name: "expect ssh key to be unset when cluster key name is nil and machine key name is empty string",
  2828  			machine: clusterv1.Machine{
  2829  				ObjectMeta: metav1.ObjectMeta{
  2830  					Labels: map[string]string{"set": "node"},
  2831  				},
  2832  				Spec: clusterv1.MachineSpec{
  2833  					Bootstrap: clusterv1.Bootstrap{
  2834  						DataSecretName: pointer.StringPtr("bootstrap-data"),
  2835  					},
  2836  					Version: pointer.StringPtr("v1.16.1"),
  2837  				},
  2838  			},
  2839  			machineConfig: &infrav1.AWSMachineSpec{
  2840  				InstanceType: "m5.large",
  2841  				SSHKeyName:   aws.String(""),
  2842  			},
  2843  			awsCluster: &infrav1.AWSCluster{
  2844  				ObjectMeta: metav1.ObjectMeta{Name: "test"},
  2845  				Spec: infrav1.AWSClusterSpec{
  2846  					NetworkSpec: infrav1.NetworkSpec{
  2847  						Subnets: infrav1.Subnets{
  2848  							infrav1.SubnetSpec{
  2849  								ID:       "subnet-1",
  2850  								IsPublic: false,
  2851  							},
  2852  							infrav1.SubnetSpec{
  2853  								IsPublic: false,
  2854  							},
  2855  						},
  2856  					},
  2857  					SSHKeyName: nil,
  2858  				},
  2859  				Status: infrav1.AWSClusterStatus{
  2860  					Network: infrav1.NetworkStatus{
  2861  						SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
  2862  							infrav1.SecurityGroupControlPlane: {
  2863  								ID: "1",
  2864  							},
  2865  							infrav1.SecurityGroupNode: {
  2866  								ID: "2",
  2867  							},
  2868  							infrav1.SecurityGroupLB: {
  2869  								ID: "3",
  2870  							},
  2871  						},
  2872  						APIServerELB: infrav1.ClassicELB{
  2873  							DNSName: "test-apiserver.us-east-1.aws",
  2874  						},
  2875  					},
  2876  				},
  2877  			},
  2878  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  2879  				m.
  2880  					DescribeImages(gomock.Any()).
  2881  					Return(&ec2.DescribeImagesOutput{
  2882  						Images: []*ec2.Image{
  2883  							{
  2884  								Name:         aws.String("ami-1"),
  2885  								CreationDate: aws.String("2011-02-08T17:02:31.000Z"),
  2886  							},
  2887  						},
  2888  					}, nil)
  2889  				m. // TODO: Restore these parameters, but with the tags as well
  2890  					RunInstances(gomock.Any()).
  2891  					DoAndReturn(func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
  2892  						if input.KeyName != nil {
  2893  							t.Fatalf("Expected key name to be nil/unspecified, not '%s'", *input.KeyName)
  2894  						}
  2895  						return &ec2.Reservation{
  2896  							Instances: []*ec2.Instance{
  2897  								{
  2898  									State: &ec2.InstanceState{
  2899  										Name: aws.String(ec2.InstanceStateNamePending),
  2900  									},
  2901  									IamInstanceProfile: &ec2.IamInstanceProfile{
  2902  										Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
  2903  									},
  2904  									InstanceId:     aws.String("two"),
  2905  									InstanceType:   aws.String("m5.large"),
  2906  									SubnetId:       aws.String("subnet-1"),
  2907  									ImageId:        aws.String("ami-1"),
  2908  									RootDeviceName: aws.String("device-1"),
  2909  									BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
  2910  										{
  2911  											DeviceName: aws.String("device-1"),
  2912  											Ebs: &ec2.EbsInstanceBlockDevice{
  2913  												VolumeId: aws.String("volume-1"),
  2914  											},
  2915  										},
  2916  									},
  2917  									Placement: &ec2.Placement{
  2918  										AvailabilityZone: &az,
  2919  									},
  2920  								},
  2921  							},
  2922  						}, nil
  2923  					})
  2924  				m.WaitUntilInstanceRunningWithContext(gomock.Any(), gomock.Any(), gomock.Any()).
  2925  					Return(nil)
  2926  			},
  2927  			check: func(instance *infrav1.Instance, err error) {
  2928  				if err != nil {
  2929  					t.Fatalf("did not expect error: %v", err)
  2930  				}
  2931  			},
  2932  		},
  2933  	}
  2934  
  2935  	for _, tc := range testcases {
  2936  		t.Run(tc.name, func(t *testing.T) {
  2937  			mockCtrl := gomock.NewController(t)
  2938  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
  2939  
  2940  			scheme, err := setupScheme()
  2941  			if err != nil {
  2942  				t.Fatalf("failed to create scheme: %v", err)
  2943  			}
  2944  
  2945  			cluster := &clusterv1.Cluster{
  2946  				ObjectMeta: metav1.ObjectMeta{
  2947  					Name: "test1",
  2948  				},
  2949  				Spec: clusterv1.ClusterSpec{
  2950  					ClusterNetwork: &clusterv1.ClusterNetwork{
  2951  						ServiceDomain: "cluster.local",
  2952  						Services: &clusterv1.NetworkRanges{
  2953  							CIDRBlocks: []string{"192.168.0.0/16"},
  2954  						},
  2955  						Pods: &clusterv1.NetworkRanges{
  2956  							CIDRBlocks: []string{"192.168.0.0/16"},
  2957  						},
  2958  					},
  2959  				},
  2960  			}
  2961  
  2962  			machine := &tc.machine
  2963  
  2964  			awsMachine := &infrav1.AWSMachine{
  2965  				ObjectMeta: metav1.ObjectMeta{
  2966  					Name: "aws-test1",
  2967  					OwnerReferences: []metav1.OwnerReference{
  2968  						{
  2969  							APIVersion: clusterv1.GroupVersion.String(),
  2970  							Kind:       "Machine",
  2971  							Name:       "test1",
  2972  						},
  2973  					},
  2974  				},
  2975  			}
  2976  
  2977  			client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, cluster, machine).Build()
  2978  			clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
  2979  				Client:     client,
  2980  				Cluster:    cluster,
  2981  				AWSCluster: tc.awsCluster,
  2982  			})
  2983  			if err != nil {
  2984  				t.Fatalf("Failed to create test context: %v", err)
  2985  			}
  2986  
  2987  			machineScope, err := scope.NewMachineScope(scope.MachineScopeParams{
  2988  				Client:       client,
  2989  				Cluster:      cluster,
  2990  				Machine:      machine,
  2991  				AWSMachine:   awsMachine,
  2992  				InfraCluster: clusterScope,
  2993  			})
  2994  			if err != nil {
  2995  				t.Fatalf("Failed to create test context: %v", err)
  2996  			}
  2997  			machineScope.AWSMachine.Spec = *tc.machineConfig
  2998  			tc.expect(ec2Mock.EXPECT())
  2999  
  3000  			s := NewService(clusterScope)
  3001  			s.EC2Client = ec2Mock
  3002  
  3003  			instance, err := s.CreateInstance(machineScope, data, "")
  3004  			tc.check(instance, err)
  3005  		})
  3006  	}
  3007  }
  3008  
  3009  func TestGetInstanceMarketOptionsRequest(t *testing.T) {
  3010  	testCases := []struct {
  3011  		name              string
  3012  		spotMarketOptions *infrav1.SpotMarketOptions
  3013  		expectedRequest   *ec2.InstanceMarketOptionsRequest
  3014  	}{
  3015  		{
  3016  			name:              "with no Spot options specified",
  3017  			spotMarketOptions: nil,
  3018  			expectedRequest:   nil,
  3019  		},
  3020  		{
  3021  			name:              "with an empty Spot options specified",
  3022  			spotMarketOptions: &infrav1.SpotMarketOptions{},
  3023  			expectedRequest: &ec2.InstanceMarketOptionsRequest{
  3024  				MarketType: aws.String(ec2.MarketTypeSpot),
  3025  				SpotOptions: &ec2.SpotMarketOptions{
  3026  					InstanceInterruptionBehavior: aws.String(ec2.InstanceInterruptionBehaviorTerminate),
  3027  					SpotInstanceType:             aws.String(ec2.SpotInstanceTypeOneTime),
  3028  				},
  3029  			},
  3030  		},
  3031  		{
  3032  			name: "with an empty MaxPrice specified",
  3033  			spotMarketOptions: &infrav1.SpotMarketOptions{
  3034  				MaxPrice: aws.String(""),
  3035  			},
  3036  			expectedRequest: &ec2.InstanceMarketOptionsRequest{
  3037  				MarketType: aws.String(ec2.MarketTypeSpot),
  3038  				SpotOptions: &ec2.SpotMarketOptions{
  3039  					InstanceInterruptionBehavior: aws.String(ec2.InstanceInterruptionBehaviorTerminate),
  3040  					SpotInstanceType:             aws.String(ec2.SpotInstanceTypeOneTime),
  3041  				},
  3042  			},
  3043  		},
  3044  		{
  3045  			name: "with a valid MaxPrice specified",
  3046  			spotMarketOptions: &infrav1.SpotMarketOptions{
  3047  				MaxPrice: aws.String("0.01"),
  3048  			},
  3049  			expectedRequest: &ec2.InstanceMarketOptionsRequest{
  3050  				MarketType: aws.String(ec2.MarketTypeSpot),
  3051  				SpotOptions: &ec2.SpotMarketOptions{
  3052  					InstanceInterruptionBehavior: aws.String(ec2.InstanceInterruptionBehaviorTerminate),
  3053  					SpotInstanceType:             aws.String(ec2.SpotInstanceTypeOneTime),
  3054  					MaxPrice:                     aws.String("0.01"),
  3055  				},
  3056  			},
  3057  		},
  3058  	}
  3059  
  3060  	for _, tc := range testCases {
  3061  		t.Run(tc.name, func(t *testing.T) {
  3062  			request := getInstanceMarketOptionsRequest(tc.spotMarketOptions)
  3063  			if !cmp.Equal(request, tc.expectedRequest) {
  3064  				t.Errorf("Case: %s. Got: %v, expected: %v", tc.name, request, tc.expectedRequest)
  3065  			}
  3066  		})
  3067  	}
  3068  }
  3069  
  3070  func TestGetFilteredSecurityGroupID(t *testing.T) {
  3071  	mockCtrl := gomock.NewController(t)
  3072  	defer mockCtrl.Finish()
  3073  
  3074  	securityGroupFilterName := "sg1"
  3075  	securityGroupFilterValues := []string{"test"}
  3076  	securityGroupID := "1"
  3077  
  3078  	testCases := []struct {
  3079  		name          string
  3080  		securityGroup infrav1.AWSResourceReference
  3081  		expect        func(m *mocks.MockEC2APIMockRecorder)
  3082  		check         func(id string, err error)
  3083  	}{
  3084  		{
  3085  			name: "successfully return security group id",
  3086  			securityGroup: infrav1.AWSResourceReference{
  3087  				Filters: []infrav1.Filter{
  3088  					{
  3089  						Name: securityGroupFilterName, Values: securityGroupFilterValues,
  3090  					},
  3091  				},
  3092  			},
  3093  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  3094  				m.DescribeSecurityGroups(gomock.Eq(&ec2.DescribeSecurityGroupsInput{
  3095  					Filters: []*ec2.Filter{
  3096  						{
  3097  							Name:   aws.String(securityGroupFilterName),
  3098  							Values: aws.StringSlice(securityGroupFilterValues),
  3099  						},
  3100  					},
  3101  				})).Return(
  3102  					&ec2.DescribeSecurityGroupsOutput{
  3103  						SecurityGroups: []*ec2.SecurityGroup{
  3104  							{
  3105  								GroupId: aws.String(securityGroupID),
  3106  							},
  3107  						},
  3108  					}, nil)
  3109  			},
  3110  			check: func(id string, err error) {
  3111  				if err != nil {
  3112  					t.Fatalf("did not expect error: %v", err)
  3113  				}
  3114  
  3115  				if id != securityGroupID {
  3116  					t.Fatalf("expected security group id %v but got: %v", securityGroupID, id)
  3117  				}
  3118  			},
  3119  		},
  3120  		{
  3121  			name:          "return early when filters are missing",
  3122  			securityGroup: infrav1.AWSResourceReference{},
  3123  			expect:        func(m *mocks.MockEC2APIMockRecorder) {},
  3124  			check: func(id string, err error) {
  3125  				if err != nil {
  3126  					t.Fatalf("did not expect error: %v", err)
  3127  				}
  3128  
  3129  				if id != "" {
  3130  					t.Fatalf("didn't expect secutity group id %v", id)
  3131  				}
  3132  			},
  3133  		},
  3134  		{
  3135  			name: "error describing security group",
  3136  			securityGroup: infrav1.AWSResourceReference{
  3137  				Filters: []infrav1.Filter{
  3138  					{
  3139  						Name: securityGroupFilterName, Values: securityGroupFilterValues,
  3140  					},
  3141  				},
  3142  			},
  3143  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  3144  				m.DescribeSecurityGroups(gomock.Eq(&ec2.DescribeSecurityGroupsInput{
  3145  					Filters: []*ec2.Filter{
  3146  						{
  3147  							Name:   aws.String(securityGroupFilterName),
  3148  							Values: aws.StringSlice(securityGroupFilterValues),
  3149  						},
  3150  					},
  3151  				})).Return(nil, errors.New("some error"))
  3152  			},
  3153  			check: func(id string, err error) {
  3154  				if err == nil {
  3155  					t.Fatalf("expected error but got none.")
  3156  				}
  3157  			},
  3158  		},
  3159  		{
  3160  			name: "error when no security groups found",
  3161  			securityGroup: infrav1.AWSResourceReference{
  3162  				Filters: []infrav1.Filter{
  3163  					{
  3164  						Name: securityGroupFilterName, Values: securityGroupFilterValues,
  3165  					},
  3166  				},
  3167  			},
  3168  			expect: func(m *mocks.MockEC2APIMockRecorder) {
  3169  				m.DescribeSecurityGroups(gomock.Eq(&ec2.DescribeSecurityGroupsInput{
  3170  					Filters: []*ec2.Filter{
  3171  						{
  3172  							Name:   aws.String(securityGroupFilterName),
  3173  							Values: aws.StringSlice(securityGroupFilterValues),
  3174  						},
  3175  					},
  3176  				})).Return(
  3177  					&ec2.DescribeSecurityGroupsOutput{
  3178  						SecurityGroups: []*ec2.SecurityGroup{},
  3179  					}, nil)
  3180  			},
  3181  			check: func(id string, err error) {
  3182  				if err == nil {
  3183  					t.Fatalf("expected error but got none.")
  3184  				}
  3185  			},
  3186  		},
  3187  	}
  3188  
  3189  	for _, tc := range testCases {
  3190  		t.Run(tc.name, func(t *testing.T) {
  3191  			ec2Mock := mocks.NewMockEC2API(mockCtrl)
  3192  			tc.expect(ec2Mock.EXPECT())
  3193  
  3194  			s := Service{
  3195  				EC2Client: ec2Mock,
  3196  			}
  3197  
  3198  			id, err := s.getFilteredSecurityGroupID(tc.securityGroup)
  3199  			tc.check(id, err)
  3200  		})
  3201  	}
  3202  }