sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controllers/awsmachine_controller_unit_test.go (about)

     1  /*
     2  Copyright 2019 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 controllers
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"flag"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/aws/aws-sdk-go/aws"
    28  	"github.com/golang/mock/gomock"
    29  	. "github.com/onsi/ginkgo"
    30  	. "github.com/onsi/gomega"
    31  	. "github.com/onsi/gomega/gstruct"
    32  	"github.com/pkg/errors"
    33  	corev1 "k8s.io/api/core/v1"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	"k8s.io/client-go/tools/record"
    37  	"k8s.io/klog/v2"
    38  	"k8s.io/klog/v2/klogr"
    39  	"k8s.io/utils/pointer"
    40  	ctrl "sigs.k8s.io/controller-runtime"
    41  	"sigs.k8s.io/controller-runtime/pkg/client"
    42  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    43  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    44  
    45  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    46  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud"
    47  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    48  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services"
    49  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/mock_services"
    50  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    51  	"sigs.k8s.io/cluster-api/controllers/noderefutil"
    52  	capierrors "sigs.k8s.io/cluster-api/errors"
    53  	"sigs.k8s.io/cluster-api/util"
    54  )
    55  
    56  const providerID = "aws:////myMachine"
    57  
    58  func TestAWSMachineReconciler(t *testing.T) {
    59  	var (
    60  		reconciler     AWSMachineReconciler
    61  		cs             *scope.ClusterScope
    62  		ms             *scope.MachineScope
    63  		mockCtrl       *gomock.Controller
    64  		ec2Svc         *mock_services.MockEC2Interface
    65  		elbSvc         *mock_services.MockELBInterface
    66  		secretSvc      *mock_services.MockSecretInterface
    67  		objectStoreSvc *mock_services.MockObjectStoreInterface
    68  		recorder       *record.FakeRecorder
    69  	)
    70  
    71  	setup := func(t *testing.T, g *WithT, awsMachine *infrav1.AWSMachine) {
    72  		// https://github.com/kubernetes/klog/issues/87#issuecomment-540153080
    73  		// TODO: Replace with LogToOutput when https://github.com/kubernetes/klog/pull/99 merges
    74  		t.Helper()
    75  
    76  		var err error
    77  
    78  		if err := flag.Set("logtostderr", "false"); err != nil {
    79  			_ = fmt.Errorf("Error setting logtostderr flag")
    80  		}
    81  		if err := flag.Set("v", "2"); err != nil {
    82  			_ = fmt.Errorf("Error setting v flag")
    83  		}
    84  		klog.SetOutput(GinkgoWriter)
    85  
    86  		secret := &corev1.Secret{
    87  			ObjectMeta: metav1.ObjectMeta{
    88  				Name: "bootstrap-data",
    89  			},
    90  			Data: map[string][]byte{
    91  				"value": []byte("shell-script"),
    92  			},
    93  		}
    94  
    95  		secretIgnition := &corev1.Secret{
    96  			ObjectMeta: metav1.ObjectMeta{
    97  				Name: "bootstrap-data-ignition",
    98  			},
    99  			Data: map[string][]byte{
   100  				"value":  []byte("ignitionJSON"),
   101  				"format": []byte("ignition"),
   102  			},
   103  		}
   104  
   105  		client := fake.NewClientBuilder().WithObjects(awsMachine, secret, secretIgnition).Build()
   106  		ms, err = scope.NewMachineScope(
   107  			scope.MachineScopeParams{
   108  				Client: client,
   109  				Cluster: &clusterv1.Cluster{
   110  					ObjectMeta: metav1.ObjectMeta{
   111  						Name: "test",
   112  					},
   113  					Status: clusterv1.ClusterStatus{
   114  						InfrastructureReady: true,
   115  					},
   116  				},
   117  				Machine: &clusterv1.Machine{
   118  					ObjectMeta: metav1.ObjectMeta{
   119  						Name: "test",
   120  					},
   121  					Spec: clusterv1.MachineSpec{
   122  						ClusterName: "capi-test",
   123  						Bootstrap: clusterv1.Bootstrap{
   124  							DataSecretName: pointer.StringPtr("bootstrap-data"),
   125  						},
   126  					},
   127  				},
   128  				InfraCluster: cs,
   129  				AWSMachine:   awsMachine,
   130  			},
   131  		)
   132  		g.Expect(err).To(BeNil())
   133  
   134  		cs, err = scope.NewClusterScope(
   135  			scope.ClusterScopeParams{
   136  				Client:     fake.NewClientBuilder().WithObjects(awsMachine, secret).Build(),
   137  				Cluster:    &clusterv1.Cluster{},
   138  				AWSCluster: &infrav1.AWSCluster{ObjectMeta: metav1.ObjectMeta{Name: "test"}},
   139  			},
   140  		)
   141  		g.Expect(err).To(BeNil())
   142  
   143  		ms, err = scope.NewMachineScope(
   144  			scope.MachineScopeParams{
   145  				Client: client,
   146  				Cluster: &clusterv1.Cluster{
   147  					Status: clusterv1.ClusterStatus{
   148  						InfrastructureReady: true,
   149  					},
   150  				},
   151  				Machine: &clusterv1.Machine{
   152  					Spec: clusterv1.MachineSpec{
   153  						ClusterName: "capi-test",
   154  						Bootstrap: clusterv1.Bootstrap{
   155  							DataSecretName: pointer.StringPtr("bootstrap-data"),
   156  						},
   157  					},
   158  				},
   159  				InfraCluster: cs,
   160  				AWSMachine:   awsMachine,
   161  			},
   162  		)
   163  		g.Expect(err).To(BeNil())
   164  
   165  		mockCtrl = gomock.NewController(t)
   166  		ec2Svc = mock_services.NewMockEC2Interface(mockCtrl)
   167  		secretSvc = mock_services.NewMockSecretInterface(mockCtrl)
   168  		elbSvc = mock_services.NewMockELBInterface(mockCtrl)
   169  		objectStoreSvc = mock_services.NewMockObjectStoreInterface(mockCtrl)
   170  
   171  		// If your test hangs for 9 minutes, increase the value here to the number of events during a reconciliation loop
   172  		recorder = record.NewFakeRecorder(2)
   173  
   174  		reconciler = AWSMachineReconciler{
   175  			ec2ServiceFactory: func(scope.EC2Scope) services.EC2Interface {
   176  				return ec2Svc
   177  			},
   178  			secretsManagerServiceFactory: func(cloud.ClusterScoper) services.SecretInterface {
   179  				return secretSvc
   180  			},
   181  			objectStoreServiceFactory: func(cloud.ClusterScoper) services.ObjectStoreInterface {
   182  				return objectStoreSvc
   183  			},
   184  			Recorder: recorder,
   185  			Log:      klogr.New(),
   186  		}
   187  	}
   188  	teardown := func(t *testing.T, g *WithT) {
   189  		t.Helper()
   190  		mockCtrl.Finish()
   191  	}
   192  
   193  	t.Run("Reconciling an AWSMachine", func(t *testing.T) {
   194  		t.Run("when can't reach amazon", func(t *testing.T) {
   195  			expectedErr := errors.New("no connection available ")
   196  			runningInstance := func(t *testing.T, g *WithT) {
   197  				t.Helper()
   198  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, expectedErr).AnyTimes()
   199  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
   200  			}
   201  
   202  			t.Run("should exit immediately on an error state", func(t *testing.T) {
   203  				g := NewWithT(t)
   204  				awsMachine := getAWSMachine()
   205  				setup(t, g, awsMachine)
   206  				defer teardown(t, g)
   207  				runningInstance(t, g)
   208  				er := capierrors.CreateMachineError
   209  				ms.AWSMachine.Status.FailureReason = &er
   210  				ms.AWSMachine.Status.FailureMessage = pointer.StringPtr("Couldn't create machine")
   211  
   212  				buf := new(bytes.Buffer)
   213  				klog.SetOutput(buf)
   214  
   215  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   216  				g.Expect(buf).To(ContainSubstring("Error state detected, skipping reconciliation"))
   217  			})
   218  
   219  			t.Run("should exit immediately if cluster infra isn't ready", func(t *testing.T) {
   220  				g := NewWithT(t)
   221  				awsMachine := getAWSMachine()
   222  				setup(t, g, awsMachine)
   223  				defer teardown(t, g)
   224  				runningInstance(t, g)
   225  				ms.Cluster.Status.InfrastructureReady = false
   226  
   227  				buf := new(bytes.Buffer)
   228  				klog.SetOutput(buf)
   229  
   230  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   231  				g.Expect(err).To(BeNil())
   232  				g.Expect(buf.String()).To(ContainSubstring("Cluster infrastructure is not ready yet"))
   233  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav1.WaitingForClusterInfrastructureReason}})
   234  			})
   235  
   236  			t.Run("should exit immediately if bootstrap data secret reference isn't available", func(t *testing.T) {
   237  				g := NewWithT(t)
   238  				awsMachine := getAWSMachine()
   239  				setup(t, g, awsMachine)
   240  				defer teardown(t, g)
   241  				runningInstance(t, g)
   242  				ms.Machine.Spec.Bootstrap.DataSecretName = nil
   243  
   244  				buf := new(bytes.Buffer)
   245  				klog.SetOutput(buf)
   246  
   247  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   248  
   249  				g.Expect(err).To(BeNil())
   250  				g.Expect(buf.String()).To(ContainSubstring("Bootstrap data secret reference is not yet available"))
   251  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav1.WaitingForBootstrapDataReason}})
   252  			})
   253  
   254  			t.Run("should return an error when we can't list instances by tags", func(t *testing.T) {
   255  				g := NewWithT(t)
   256  				awsMachine := getAWSMachine()
   257  				setup(t, g, awsMachine)
   258  				defer teardown(t, g)
   259  				runningInstance(t, g)
   260  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   261  				g.Expect(errors.Cause(err)).To(MatchError(expectedErr))
   262  			})
   263  
   264  			t.Run("shouldn't add our finalizer to the machine", func(t *testing.T) {
   265  				g := NewWithT(t)
   266  				awsMachine := getAWSMachine()
   267  				setup(t, g, awsMachine)
   268  				defer teardown(t, g)
   269  				runningInstance(t, g)
   270  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   271  
   272  				g.Expect(len(ms.AWSMachine.Finalizers)).To(Equal(0))
   273  			})
   274  		})
   275  
   276  		t.Run("when provider ID is populated correctly", func(t *testing.T) {
   277  			id := providerID
   278  			providerID := func(t *testing.T, g *WithT) {
   279  				t.Helper()
   280  				_, err := noderefutil.NewProviderID(id)
   281  				g.Expect(err).To(BeNil())
   282  
   283  				ms.AWSMachine.Spec.ProviderID = &id
   284  			}
   285  
   286  			t.Run("should look up by provider ID when one exists", func(t *testing.T) {
   287  				g := NewWithT(t)
   288  				awsMachine := getAWSMachine()
   289  				setup(t, g, awsMachine)
   290  				defer teardown(t, g)
   291  
   292  				providerID(t, g)
   293  				expectedErr := errors.New("no connection available ")
   294  				ec2Svc.EXPECT().InstanceIfExists(PointsTo("myMachine")).Return(nil, expectedErr)
   295  
   296  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   297  				g.Expect(errors.Cause(err)).To(MatchError(expectedErr))
   298  			})
   299  
   300  			t.Run("should fail to create instance and keep the finalizers as is", func(t *testing.T) {
   301  				g := NewWithT(t)
   302  				awsMachine := getAWSMachine()
   303  				setup(t, g, awsMachine)
   304  				defer teardown(t, g)
   305  
   306  				providerID(t, g)
   307  				expectedErr := errors.New("Invalid instance")
   308  				ec2Svc.EXPECT().InstanceIfExists(gomock.Any()).Return(nil, nil)
   309  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   310  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, expectedErr)
   311  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   312  
   313  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   314  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   315  				g.Expect(errors.Cause(err)).To(MatchError(expectedErr))
   316  			})
   317  		})
   318  
   319  		t.Run("should fail to find instance if no provider ID provided", func(t *testing.T) {
   320  			g := NewWithT(t)
   321  			awsMachine := getAWSMachine()
   322  			setup(t, g, awsMachine)
   323  			defer teardown(t, g)
   324  			id := "aws////myMachine"
   325  
   326  			ms.AWSMachine.Spec.ProviderID = &id
   327  			expectedErr := "providerID must be of the form <cloudProvider>://<optional>/<segments>/<provider id>"
   328  
   329  			_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   330  			g.Expect(err.Error()).To(ContainSubstring(expectedErr))
   331  		})
   332  
   333  		t.Run("when instance creation succeeds", func(t *testing.T) {
   334  			var instance *infrav1.Instance
   335  
   336  			instanceCreate := func(t *testing.T, g *WithT) {
   337  				t.Helper()
   338  
   339  				instance = &infrav1.Instance{
   340  					ID:        "myMachine",
   341  					VolumeIDs: []string{"volume-1", "volume-2"},
   342  				}
   343  				instance.State = infrav1.InstanceStatePending
   344  
   345  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil)
   346  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil)
   347  			}
   348  
   349  			t.Run("instance security group errors", func(t *testing.T) {
   350  				var buf *bytes.Buffer
   351  				getInstanceSecurityGroups := func(t *testing.T, g *WithT) {
   352  					t.Helper()
   353  
   354  					buf = new(bytes.Buffer)
   355  					klog.SetOutput(buf)
   356  					ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(nil, errors.New("stop here"))
   357  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   358  				}
   359  
   360  				t.Run("should set attributes after creating an instance", func(t *testing.T) {
   361  					g := NewWithT(t)
   362  					awsMachine := getAWSMachine()
   363  					setup(t, g, awsMachine)
   364  					defer teardown(t, g)
   365  					instanceCreate(t, g)
   366  					getInstanceSecurityGroups(t, g)
   367  
   368  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   369  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   370  					g.Expect(ms.AWSMachine.Spec.ProviderID).To(PointTo(Equal(providerID)))
   371  				})
   372  
   373  				t.Run("should set instance to pending", func(t *testing.T) {
   374  					g := NewWithT(t)
   375  					awsMachine := getAWSMachine()
   376  					setup(t, g, awsMachine)
   377  					defer teardown(t, g)
   378  					instanceCreate(t, g)
   379  					getInstanceSecurityGroups(t, g)
   380  
   381  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   382  					instance.State = infrav1.InstanceStatePending
   383  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   384  					g.Expect(ms.AWSMachine.Status.InstanceState).To(PointTo(Equal(infrav1.InstanceStatePending)))
   385  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   386  					g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state changed")))
   387  
   388  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   389  				})
   390  
   391  				t.Run("should set instance to running", func(t *testing.T) {
   392  					g := NewWithT(t)
   393  					awsMachine := getAWSMachine()
   394  					setup(t, g, awsMachine)
   395  					defer teardown(t, g)
   396  
   397  					instanceCreate(t, g)
   398  					getInstanceSecurityGroups(t, g)
   399  
   400  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   401  					instance.State = infrav1.InstanceStateRunning
   402  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   403  					g.Expect(ms.AWSMachine.Status.InstanceState).To(PointTo(Equal(infrav1.InstanceStateRunning)))
   404  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(true))
   405  					g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state changed")))
   406  					expectConditions(g, ms.AWSMachine, []conditionAssertion{
   407  						{conditionType: infrav1.InstanceReadyCondition, status: corev1.ConditionTrue},
   408  					})
   409  				})
   410  			})
   411  			t.Run("new EC2 instance state: should error when the instance state is a new unseen one", func(t *testing.T) {
   412  				g := NewWithT(t)
   413  				awsMachine := getAWSMachine()
   414  				setup(t, g, awsMachine)
   415  				defer teardown(t, g)
   416  				instanceCreate(t, g)
   417  
   418  				buf := new(bytes.Buffer)
   419  				klog.SetOutput(buf)
   420  				instance.State = "NewAWSMachineState"
   421  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
   422  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   423  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   424  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   425  				g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   426  				g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state is undefined")))
   427  				g.Eventually(recorder.Events).Should(Receive(ContainSubstring("InstanceUnhandledState")))
   428  				g.Expect(ms.AWSMachine.Status.FailureMessage).To(PointTo(Equal("EC2 instance state \"NewAWSMachineState\" is undefined")))
   429  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{conditionType: infrav1.InstanceReadyCondition, status: corev1.ConditionUnknown}})
   430  			})
   431  			t.Run("security Groups succeed", func(t *testing.T) {
   432  				getCoreSecurityGroups := func(t *testing.T, g *WithT) {
   433  					t.Helper()
   434  
   435  					ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).
   436  						Return(map[string][]string{"eid": {}}, nil)
   437  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   438  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil)
   439  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   440  				}
   441  				t.Run("should reconcile security groups", func(t *testing.T) {
   442  					g := NewWithT(t)
   443  					awsMachine := getAWSMachine()
   444  					setup(t, g, awsMachine)
   445  					defer teardown(t, g)
   446  					instanceCreate(t, g)
   447  					getCoreSecurityGroups(t, g)
   448  
   449  					ms.AWSMachine.Spec.AdditionalSecurityGroups = []infrav1.AWSResourceReference{
   450  						{
   451  							ID: pointer.StringPtr("sg-2345"),
   452  						},
   453  					}
   454  					ec2Svc.EXPECT().UpdateInstanceSecurityGroups(instance.ID, []string{"sg-2345"})
   455  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return([]string{"sg-2345"}, nil)
   456  
   457  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   458  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{conditionType: infrav1.SecurityGroupsReadyCondition, status: corev1.ConditionTrue}})
   459  				})
   460  
   461  				t.Run("should not tag instances if there's no tags", func(t *testing.T) {
   462  					g := NewWithT(t)
   463  					awsMachine := getAWSMachine()
   464  					setup(t, g, awsMachine)
   465  					defer teardown(t, g)
   466  					instanceCreate(t, g)
   467  					getCoreSecurityGroups(t, g)
   468  
   469  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   470  					ec2Svc.EXPECT().UpdateInstanceSecurityGroups(gomock.Any(), gomock.Any()).Times(0)
   471  					if _, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs); err != nil {
   472  						_ = fmt.Errorf("reconcileNormal reutrned an error during test")
   473  					}
   474  				})
   475  
   476  				t.Run("should tag instances from machine and cluster tags", func(t *testing.T) {
   477  					g := NewWithT(t)
   478  					awsMachine := getAWSMachine()
   479  					setup(t, g, awsMachine)
   480  					defer teardown(t, g)
   481  					instanceCreate(t, g)
   482  					getCoreSecurityGroups(t, g)
   483  
   484  					ms.AWSMachine.Spec.AdditionalTags = infrav1.Tags{"kind": "alicorn"}
   485  					cs.AWSCluster.Spec.AdditionalTags = infrav1.Tags{"colour": "lavender"}
   486  
   487  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   488  					ec2Svc.EXPECT().UpdateResourceTags(
   489  						gomock.Any(),
   490  						map[string]string{
   491  							"kind": "alicorn",
   492  						},
   493  						map[string]string{},
   494  					).Return(nil).Times(2)
   495  
   496  					ec2Svc.EXPECT().UpdateResourceTags(
   497  						PointsTo("myMachine"),
   498  						map[string]string{
   499  							"colour": "lavender",
   500  							"kind":   "alicorn",
   501  						},
   502  						map[string]string{},
   503  					).Return(nil)
   504  
   505  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   506  					g.Expect(err).To(BeNil())
   507  				})
   508  				t.Run("should tag instances volume tags", func(t *testing.T) {
   509  					g := NewWithT(t)
   510  					awsMachine := getAWSMachineWithAdditionalTags()
   511  					setup(t, g, awsMachine)
   512  					defer teardown(t, g)
   513  					instanceCreate(t, g)
   514  					getCoreSecurityGroups(t, g)
   515  
   516  					ms.AWSMachine.Spec.AdditionalTags = infrav1.Tags{"rootDeviceID": "id1", "rootDeviceSize": "30"}
   517  
   518  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   519  					ec2Svc.EXPECT().UpdateResourceTags(
   520  						gomock.Any(),
   521  						map[string]string{
   522  							"rootDeviceID":   "id1",
   523  							"rootDeviceSize": "30",
   524  						},
   525  						map[string]string{},
   526  					).Return(nil).Times(3)
   527  
   528  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   529  					g.Expect(err).To(BeNil())
   530  				})
   531  			})
   532  
   533  			t.Run("temporarily stopping then starting the AWSMachine(stateless)", func(t *testing.T) {
   534  				var buf *bytes.Buffer
   535  				getCoreSecurityGroups := func(t *testing.T, g *WithT) {
   536  					t.Helper()
   537  
   538  					buf = new(bytes.Buffer)
   539  					klog.SetOutput(buf)
   540  					ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).
   541  						Return(map[string][]string{"eid": {}}, nil).Times(1)
   542  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   543  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
   544  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   545  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   546  				}
   547  
   548  				t.Run("should set instance to stopping and unready", func(t *testing.T) {
   549  					g := NewWithT(t)
   550  					awsMachine := getAWSMachine()
   551  					setup(t, g, awsMachine)
   552  					defer teardown(t, g)
   553  					instanceCreate(t, g)
   554  					getCoreSecurityGroups(t, g)
   555  
   556  					instance.State = infrav1.InstanceStateStopping
   557  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   558  					g.Expect(ms.AWSMachine.Status.InstanceState).To(PointTo(Equal(infrav1.InstanceStateStopping)))
   559  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   560  					g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state changed")))
   561  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.InstanceStoppedReason}})
   562  				})
   563  
   564  				t.Run("should then set instance to stopped and unready", func(t *testing.T) {
   565  					g := NewWithT(t)
   566  					awsMachine := getAWSMachine()
   567  					setup(t, g, awsMachine)
   568  					defer teardown(t, g)
   569  					instanceCreate(t, g)
   570  					getCoreSecurityGroups(t, g)
   571  
   572  					instance.State = infrav1.InstanceStateStopped
   573  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   574  					g.Expect(ms.AWSMachine.Status.InstanceState).To(PointTo(Equal(infrav1.InstanceStateStopped)))
   575  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   576  					g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state changed")))
   577  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.InstanceStoppedReason}})
   578  				})
   579  
   580  				t.Run("should then set instance to running and ready once it is restarted", func(t *testing.T) {
   581  					g := NewWithT(t)
   582  					awsMachine := getAWSMachine()
   583  					setup(t, g, awsMachine)
   584  					defer teardown(t, g)
   585  					instanceCreate(t, g)
   586  					getCoreSecurityGroups(t, g)
   587  
   588  					instance.State = infrav1.InstanceStateRunning
   589  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   590  					g.Expect(ms.AWSMachine.Status.InstanceState).To(PointTo(Equal(infrav1.InstanceStateRunning)))
   591  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(true))
   592  					g.Expect(buf.String()).To(ContainSubstring(("EC2 instance state changed")))
   593  				})
   594  			})
   595  			t.Run("deleting the AWSMachine manually", func(t *testing.T) {
   596  				var buf *bytes.Buffer
   597  				deleteMachine := func(t *testing.T, g *WithT) {
   598  					t.Helper()
   599  
   600  					buf = new(bytes.Buffer)
   601  					klog.SetOutput(buf)
   602  					secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
   603  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   604  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   605  				}
   606  
   607  				t.Run("should warn if an instance is shutting-down", func(t *testing.T) {
   608  					g := NewWithT(t)
   609  					awsMachine := getAWSMachine()
   610  					setup(t, g, awsMachine)
   611  					defer teardown(t, g)
   612  					instanceCreate(t, g)
   613  					deleteMachine(t, g)
   614  					instance.State = infrav1.InstanceStateShuttingDown
   615  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   616  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   617  					g.Expect(buf.String()).To(ContainSubstring(("Unexpected EC2 instance termination")))
   618  					g.Eventually(recorder.Events).Should(Receive(ContainSubstring("UnexpectedTermination")))
   619  				})
   620  
   621  				t.Run("should error when the instance is seen as terminated", func(t *testing.T) {
   622  					g := NewWithT(t)
   623  					awsMachine := getAWSMachine()
   624  					setup(t, g, awsMachine)
   625  					defer teardown(t, g)
   626  					instanceCreate(t, g)
   627  					deleteMachine(t, g)
   628  
   629  					instance.State = infrav1.InstanceStateTerminated
   630  					_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   631  					g.Expect(ms.AWSMachine.Status.Ready).To(Equal(false))
   632  					g.Expect(buf.String()).To(ContainSubstring(("Unexpected EC2 instance termination")))
   633  					g.Eventually(recorder.Events).Should(Receive(ContainSubstring("UnexpectedTermination")))
   634  					g.Expect(ms.AWSMachine.Status.FailureMessage).To(PointTo(Equal("EC2 instance state \"terminated\" is unexpected")))
   635  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.InstanceTerminatedReason}})
   636  				})
   637  			})
   638  			t.Run("should not register if control plane ELB is already registered", func(t *testing.T) {
   639  				g := NewWithT(t)
   640  				awsMachine := getAWSMachine()
   641  				setup(t, g, awsMachine)
   642  				defer teardown(t, g)
   643  				instanceCreate(t, g)
   644  
   645  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
   646  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
   647  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   648  					return elbSvc
   649  				}
   650  
   651  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(true, nil)
   652  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   653  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   654  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
   655  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
   656  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   657  
   658  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   659  				g.Expect(err).To(BeNil())
   660  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   661  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   662  			})
   663  			t.Run("should attach control plane ELB to instance", func(t *testing.T) {
   664  				g := NewWithT(t)
   665  				awsMachine := getAWSMachine()
   666  				setup(t, g, awsMachine)
   667  				defer teardown(t, g)
   668  				instanceCreate(t, g)
   669  
   670  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
   671  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
   672  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   673  					return elbSvc
   674  				}
   675  
   676  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(false, nil)
   677  				elbSvc.EXPECT().RegisterInstanceWithAPIServerELB(gomock.Any()).Return(nil)
   678  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   679  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   680  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
   681  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
   682  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   683  
   684  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   685  				g.Expect(err).To(BeNil())
   686  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   687  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.ELBAttachedCondition, corev1.ConditionTrue, "", ""}})
   688  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   689  			})
   690  			t.Run("Should store userdata using AWS Secrets Manager", func(t *testing.T) {
   691  				g := NewWithT(t)
   692  				awsMachine := getAWSMachine()
   693  				setup(t, g, awsMachine)
   694  				defer teardown(t, g)
   695  				instanceCreate(t, g)
   696  
   697  				ms.AWSMachine.Spec.CloudInit.InsecureSkipSecretsManager = true
   698  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
   699  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
   700  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
   701  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   702  					return elbSvc
   703  				}
   704  
   705  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   706  				g.Expect(err).To(BeNil())
   707  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   708  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   709  			})
   710  			t.Run("should fail to delete bootstrap data secret if AWSMachine state is updated", func(t *testing.T) {
   711  				g := NewWithT(t)
   712  				awsMachine := getAWSMachine()
   713  				setup(t, g, awsMachine)
   714  				defer teardown(t, g)
   715  				instanceCreate(t, g)
   716  				ms.Machine.Status.NodeRef = &corev1.ObjectReference{
   717  					Namespace: "default",
   718  					Name:      "test",
   719  				}
   720  
   721  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   722  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   723  				secretSvc.EXPECT().Delete(gomock.Any()).Return(errors.New("failed to delete entries from AWS Secret")).Times(1)
   724  
   725  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   726  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   727  				g.Expect(err).To(MatchError(ContainSubstring("failed to delete entries from AWS Secret")))
   728  			})
   729  		})
   730  		t.Run("when instance creation fails", func(t *testing.T) {
   731  			var instance *infrav1.Instance
   732  			instanceCreate := func(t *testing.T, g *WithT) {
   733  				t.Helper()
   734  				instance = &infrav1.Instance{
   735  					ID:               "myMachine",
   736  					VolumeIDs:        []string{"volume-1", "volume-2"},
   737  					AvailabilityZone: "us-east-1",
   738  				}
   739  				instance.State = infrav1.InstanceStatePending
   740  			}
   741  			t.Run("Should fail while getting userdata", func(t *testing.T) {
   742  				expectedError := "failed to generate init script"
   743  				g := NewWithT(t)
   744  				awsMachine := getAWSMachine()
   745  				setup(t, g, awsMachine)
   746  				defer teardown(t, g)
   747  				instanceCreate(t, g)
   748  
   749  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil)
   750  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   751  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New(expectedError)).Times(1)
   752  
   753  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   754  				g.Expect(err.Error()).To(ContainSubstring(expectedError))
   755  
   756  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   757  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.InstanceProvisionFailedReason}})
   758  			})
   759  			t.Run("should fail to determine the registration status of control plane ELB", func(t *testing.T) {
   760  				g := NewWithT(t)
   761  				awsMachine := getAWSMachine()
   762  				setup(t, g, awsMachine)
   763  				defer teardown(t, g)
   764  				instanceCreate(t, g)
   765  
   766  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
   767  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
   768  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   769  					return elbSvc
   770  				}
   771  
   772  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil)
   773  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil)
   774  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(false, errors.New("error describing ELB"))
   775  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   776  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   777  
   778  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   779  				g.Expect(err).ToNot(BeNil())
   780  				g.Expect(err.Error()).To(ContainSubstring("error describing ELB"))
   781  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   782  				g.Eventually(recorder.Events).Should(Receive(ContainSubstring("FailedAttachControlPlaneELB")))
   783  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   784  			})
   785  			t.Run("should fail to attach control plane ELB to instance", func(t *testing.T) {
   786  				g := NewWithT(t)
   787  				awsMachine := getAWSMachine()
   788  				setup(t, g, awsMachine)
   789  				defer teardown(t, g)
   790  				instanceCreate(t, g)
   791  
   792  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
   793  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
   794  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   795  					return elbSvc
   796  				}
   797  
   798  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil)
   799  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil)
   800  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(false, nil)
   801  				elbSvc.EXPECT().RegisterInstanceWithAPIServerELB(gomock.Any()).Return(errors.New("failed to attach ELB"))
   802  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil).Times(1)
   803  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   804  
   805  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   806  				g.Expect(err).ToNot(BeNil())
   807  				g.Expect(err.Error()).To(ContainSubstring("failed to attach ELB"))
   808  				g.Eventually(recorder.Events).Should(Receive(ContainSubstring("FailedAttachControlPlaneELB")))
   809  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   810  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   811  			})
   812  			t.Run("should fail to delete bootstrap data secret if AWSMachine is in failed state", func(t *testing.T) {
   813  				g := NewWithT(t)
   814  				awsMachine := getAWSMachine()
   815  				setup(t, g, awsMachine)
   816  				defer teardown(t, g)
   817  				ms.SetSecretPrefix("test")
   818  				ms.AWSMachine.Status.FailureReason = (*capierrors.MachineStatusError)(aws.String("error in AWSMachine"))
   819  				ms.SetSecretCount(0)
   820  
   821  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   822  				g.Expect(err).To(MatchError(ContainSubstring("secretPrefix present, but secretCount is not set")))
   823  			})
   824  			t.Run("Should fail in ensureTag", func(t *testing.T) {
   825  				id := providerID
   826  				ensureTag := func(t *testing.T, g *WithT) {
   827  					t.Helper()
   828  					ec2Svc.EXPECT().InstanceIfExists(gomock.Any()).Return(nil, nil)
   829  					ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil)
   830  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil)
   831  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
   832  				}
   833  
   834  				t.Run("Should fail to return machine annotations after instance is created", func(t *testing.T) {
   835  					g := NewWithT(t)
   836  					awsMachine := getAWSMachine()
   837  					setup(t, g, awsMachine)
   838  					defer teardown(t, g)
   839  					ms.AWSMachine.Spec.ProviderID = &id
   840  
   841  					instanceCreate(t, g)
   842  					ensureTag(t, g)
   843  					ms.AWSMachine.Annotations = map[string]string{TagsLastAppliedAnnotation: "12345"}
   844  
   845  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   846  					g.Expect(err.Error()).To(ContainSubstring("json: cannot unmarshal number into Go value of type map[string]interface {}"))
   847  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   848  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   849  				})
   850  				t.Run("Should fail to update resource tags after instance is created", func(t *testing.T) {
   851  					g := NewWithT(t)
   852  					awsMachine := getAWSMachine()
   853  					setup(t, g, awsMachine)
   854  					defer teardown(t, g)
   855  					id := providerID
   856  					ms.AWSMachine.Spec.ProviderID = &id
   857  
   858  					instanceCreate(t, g)
   859  					ensureTag(t, g)
   860  					ms.AWSMachine.Annotations = map[string]string{TagsLastAppliedAnnotation: "{\"tag\":\"tag1\"}"}
   861  
   862  					ec2Svc.EXPECT().UpdateResourceTags(gomock.Any(), gomock.Any(), map[string]string{"tag": "tag1"}).Return(errors.New("failed to update resource tag"))
   863  
   864  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   865  					g.Expect(err).ToNot(BeNil())
   866  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   867  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.InstanceReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.InstanceNotReadyReason}})
   868  				})
   869  			})
   870  			t.Run("While ensuring SecurityGroups", func(t *testing.T) {
   871  				id := providerID
   872  				ensureSecurityGroups := func(t *testing.T, g *WithT) {
   873  					t.Helper()
   874  					ec2Svc.EXPECT().InstanceIfExists(gomock.Any()).Return(nil, nil)
   875  					ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil)
   876  					secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("test", int32(1), nil)
   877  					secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
   878  					ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil)
   879  				}
   880  
   881  				t.Run("Should fail to return machine annotations", func(t *testing.T) {
   882  					g := NewWithT(t)
   883  					awsMachine := getAWSMachine()
   884  					setup(t, g, awsMachine)
   885  					defer teardown(t, g)
   886  					id := providerID
   887  					ms.AWSMachine.Spec.ProviderID = &id
   888  
   889  					instanceCreate(t, g)
   890  					ensureSecurityGroups(t, g)
   891  					ms.AWSMachine.Annotations = map[string]string{SecurityGroupsLastAppliedAnnotation: "12345"}
   892  
   893  					ec2Svc.EXPECT().UpdateResourceTags(gomock.Any(), map[string]string{"tag": "\"old_tag\"\"\""}, gomock.Any()).Return(nil).AnyTimes()
   894  
   895  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   896  					g.Expect(err).ToNot(BeNil())
   897  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   898  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.SecurityGroupsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.SecurityGroupsFailedReason}})
   899  				})
   900  				t.Run("Should fail to fetch core security groups", func(t *testing.T) {
   901  					g := NewWithT(t)
   902  					awsMachine := getAWSMachine()
   903  					setup(t, g, awsMachine)
   904  					defer teardown(t, g)
   905  					ms.AWSMachine.Spec.ProviderID = &id
   906  
   907  					instanceCreate(t, g)
   908  					ensureSecurityGroups(t, g)
   909  					ms.AWSMachine.Annotations = map[string]string{SecurityGroupsLastAppliedAnnotation: "{\"tag\":\"tag1\"}"}
   910  
   911  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, errors.New("failed to get core security groups")).Times(1)
   912  
   913  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   914  					g.Expect(err).ToNot(BeNil())
   915  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   916  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.SecurityGroupsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.SecurityGroupsFailedReason}})
   917  				})
   918  				t.Run("Should return silently if ensureSecurityGroups fails to fetch additional security groups", func(t *testing.T) {
   919  					g := NewWithT(t)
   920  					awsMachine := getAWSMachine()
   921  					setup(t, g, awsMachine)
   922  					defer teardown(t, g)
   923  					id := providerID
   924  					ms.AWSMachine.Spec.ProviderID = &id
   925  
   926  					instanceCreate(t, g)
   927  					ensureSecurityGroups(t, g)
   928  					ms.AWSMachine.Annotations = map[string]string{SecurityGroupsLastAppliedAnnotation: "{\"tag\":\"tag1\"}"}
   929  					ms.AWSMachine.Spec.AdditionalSecurityGroups = []infrav1.AWSResourceReference{
   930  						{
   931  							Filters: []infrav1.Filter{
   932  								{
   933  									Name:   "example-name",
   934  									Values: []string{"example-value"},
   935  								},
   936  							},
   937  						},
   938  					}
   939  
   940  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil)
   941  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return([]string{"sg-1"}, errors.New("failed to get filtered SGs"))
   942  
   943  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   944  					g.Expect(err).To(BeNil())
   945  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   946  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.SecurityGroupsReadyCondition, corev1.ConditionTrue, "", ""}})
   947  				})
   948  				t.Run("Should fail to update security group", func(t *testing.T) {
   949  					g := NewWithT(t)
   950  					awsMachine := getAWSMachine()
   951  					setup(t, g, awsMachine)
   952  					defer teardown(t, g)
   953  					id := providerID
   954  					ms.AWSMachine.Spec.ProviderID = &id
   955  
   956  					instanceCreate(t, g)
   957  					ensureSecurityGroups(t, g)
   958  					ms.AWSMachine.Annotations = map[string]string{SecurityGroupsLastAppliedAnnotation: "{\"tag\":\"tag1\"}"}
   959  					ms.AWSMachine.Spec.AdditionalSecurityGroups = []infrav1.AWSResourceReference{
   960  						{
   961  							Filters: []infrav1.Filter{
   962  								{
   963  									Name:   "id",
   964  									Values: []string{"sg-1"},
   965  								},
   966  							},
   967  						},
   968  					}
   969  
   970  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil)
   971  					ec2Svc.EXPECT().UpdateInstanceSecurityGroups(gomock.Any(), gomock.Any()).Return(errors.New("failed to update security groups"))
   972  					ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return([]string{"sg-1"}, nil)
   973  
   974  					_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
   975  					g.Expect(err).ToNot(BeNil())
   976  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(infrav1.MachineFinalizer))
   977  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.SecurityGroupsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityError, infrav1.SecurityGroupsFailedReason}})
   978  				})
   979  			})
   980  		})
   981  	})
   982  
   983  	t.Run("Secrets management lifecycle", func(t *testing.T) {
   984  		t.Run("Secrets management lifecycle when creating EC2 instances", func(t *testing.T) {
   985  			var instance *infrav1.Instance
   986  			secretPrefix := "test/secret"
   987  
   988  			t.Run("should leverage AWS Secrets Manager", func(t *testing.T) {
   989  				g := NewWithT(t)
   990  				awsMachine := getAWSMachine()
   991  				setup(t, g, awsMachine)
   992  				defer teardown(t, g)
   993  
   994  				instance = &infrav1.Instance{
   995  					ID:    "myMachine",
   996  					State: infrav1.InstanceStatePending,
   997  				}
   998  
   999  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil).AnyTimes()
  1000  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(secretPrefix, int32(1), nil).Times(1)
  1001  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil).AnyTimes()
  1002  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
  1003  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
  1004  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1005  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1006  
  1007  				ms.AWSMachine.ObjectMeta.Labels = map[string]string{
  1008  					clusterv1.MachineControlPlaneLabelName: "",
  1009  				}
  1010  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1011  			})
  1012  		})
  1013  
  1014  		t.Run("Secrets management lifecycle when there's a node ref and a secret ARN", func(t *testing.T) {
  1015  			var instance *infrav1.Instance
  1016  			setNodeRef := func(t *testing.T, g *WithT) {
  1017  				t.Helper()
  1018  
  1019  				instance = &infrav1.Instance{
  1020  					ID: "myMachine",
  1021  				}
  1022  
  1023  				ms.Machine.Status.NodeRef = &corev1.ObjectReference{
  1024  					Kind:       "Node",
  1025  					Name:       "myMachine",
  1026  					APIVersion: "v1",
  1027  				}
  1028  
  1029  				ms.AWSMachine.Spec.CloudInit = infrav1.CloudInit{
  1030  					SecretPrefix:         "secret",
  1031  					SecretCount:          5,
  1032  					SecureSecretsBackend: infrav1.SecretBackendSecretsManager,
  1033  				}
  1034  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(instance, nil).AnyTimes()
  1035  			}
  1036  
  1037  			t.Run("should delete the secret if the instance is running", func(t *testing.T) {
  1038  				g := NewWithT(t)
  1039  				awsMachine := getAWSMachine()
  1040  				setup(t, g, awsMachine)
  1041  				defer teardown(t, g)
  1042  				setNodeRef(t, g)
  1043  
  1044  				instance.State = infrav1.InstanceStateRunning
  1045  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).
  1046  					Return(map[string][]string{"eid": {}}, nil).Times(1)
  1047  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1048  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1049  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1050  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1051  			})
  1052  
  1053  			t.Run("should delete the secret if the instance is terminated", func(t *testing.T) {
  1054  				g := NewWithT(t)
  1055  				awsMachine := getAWSMachine()
  1056  				setup(t, g, awsMachine)
  1057  				defer teardown(t, g)
  1058  				setNodeRef(t, g)
  1059  
  1060  				instance.State = infrav1.InstanceStateTerminated
  1061  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1062  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1063  			})
  1064  
  1065  			t.Run("should delete the secret if the AWSMachine is deleted", func(t *testing.T) {
  1066  				g := NewWithT(t)
  1067  				awsMachine := getAWSMachine()
  1068  				setup(t, g, awsMachine)
  1069  				defer teardown(t, g)
  1070  				setNodeRef(t, g)
  1071  
  1072  				instance.State = infrav1.InstanceStateRunning
  1073  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1074  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1075  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1076  			})
  1077  
  1078  			t.Run("should delete the secret if the AWSMachine is in a failure condition", func(t *testing.T) {
  1079  				g := NewWithT(t)
  1080  				awsMachine := getAWSMachine()
  1081  				setup(t, g, awsMachine)
  1082  				defer teardown(t, g)
  1083  				setNodeRef(t, g)
  1084  
  1085  				ms.AWSMachine.Status.FailureReason = capierrors.MachineStatusErrorPtr(capierrors.UpdateMachineError)
  1086  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1087  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1088  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1089  			})
  1090  			t.Run("should not attempt to delete the secret if InsecureSkipSecretsManager is set on CloudInit", func(t *testing.T) {
  1091  				g := NewWithT(t)
  1092  				awsMachine := getAWSMachine()
  1093  				setup(t, g, awsMachine)
  1094  				defer teardown(t, g)
  1095  				setNodeRef(t, g)
  1096  
  1097  				ms.AWSMachine.Spec.CloudInit.InsecureSkipSecretsManager = true
  1098  
  1099  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(0)
  1100  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1101  
  1102  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1103  			})
  1104  		})
  1105  
  1106  		t.Run("Secrets management lifecycle when there's only a secret ARN and no node ref", func(t *testing.T) {
  1107  			var instance *infrav1.Instance
  1108  			setSSM := func(t *testing.T, g *WithT) {
  1109  				t.Helper()
  1110  
  1111  				instance = &infrav1.Instance{
  1112  					ID: "myMachine",
  1113  				}
  1114  
  1115  				ms.AWSMachine.Spec.CloudInit = infrav1.CloudInit{
  1116  					SecretPrefix:         "secret",
  1117  					SecretCount:          5,
  1118  					SecureSecretsBackend: infrav1.SecretBackendSecretsManager,
  1119  				}
  1120  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(instance, nil).AnyTimes()
  1121  			}
  1122  
  1123  			t.Run("should not delete the secret if the instance is running", func(t *testing.T) {
  1124  				g := NewWithT(t)
  1125  				awsMachine := getAWSMachine()
  1126  				setup(t, g, awsMachine)
  1127  				defer teardown(t, g)
  1128  				setSSM(t, g)
  1129  
  1130  				instance.State = infrav1.InstanceStateRunning
  1131  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).
  1132  					Return(map[string][]string{"eid": {}}, nil).Times(1)
  1133  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1134  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1135  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).MaxTimes(0)
  1136  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1137  			})
  1138  
  1139  			t.Run("should delete the secret if the instance is terminated", func(t *testing.T) {
  1140  				g := NewWithT(t)
  1141  				awsMachine := getAWSMachine()
  1142  				setup(t, g, awsMachine)
  1143  				defer teardown(t, g)
  1144  				setSSM(t, g)
  1145  
  1146  				instance.State = infrav1.InstanceStateTerminated
  1147  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1148  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1149  			})
  1150  
  1151  			t.Run("should delete the secret if the AWSMachine is deleted", func(t *testing.T) {
  1152  				g := NewWithT(t)
  1153  				awsMachine := getAWSMachine()
  1154  				setup(t, g, awsMachine)
  1155  				defer teardown(t, g)
  1156  				setSSM(t, g)
  1157  
  1158  				instance.State = infrav1.InstanceStateRunning
  1159  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1160  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1161  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1162  			})
  1163  
  1164  			t.Run("should delete the secret if the AWSMachine is in a failure condition", func(t *testing.T) {
  1165  				g := NewWithT(t)
  1166  				awsMachine := getAWSMachine()
  1167  				setup(t, g, awsMachine)
  1168  				defer teardown(t, g)
  1169  				setSSM(t, g)
  1170  
  1171  				ms.AWSMachine.Status.FailureReason = capierrors.MachineStatusErrorPtr(capierrors.UpdateMachineError)
  1172  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1173  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1174  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1175  			})
  1176  		})
  1177  
  1178  		t.Run("Secrets management lifecycle when there is an intermittent connection issue and no secret could be stored", func(t *testing.T) {
  1179  			var instance *infrav1.Instance
  1180  			secretPrefix := "test/secret"
  1181  
  1182  			getInstances := func(t *testing.T, g *WithT) {
  1183  				t.Helper()
  1184  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil).AnyTimes()
  1185  			}
  1186  
  1187  			t.Run("should error if secret could not be created", func(t *testing.T) {
  1188  				g := NewWithT(t)
  1189  				awsMachine := getAWSMachine()
  1190  				setup(t, g, awsMachine)
  1191  				defer teardown(t, g)
  1192  				getInstances(t, g)
  1193  
  1194  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(secretPrefix, int32(0), errors.New("connection error")).Times(1)
  1195  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1196  				g.Expect(err).ToNot(BeNil())
  1197  				g.Expect(err.Error()).To(ContainSubstring("connection error"))
  1198  				g.Expect(ms.GetSecretPrefix()).To(Equal("prefix"))
  1199  				g.Expect(ms.GetSecretCount()).To(Equal(int32(1000)))
  1200  			})
  1201  			t.Run("should update prefix and count on successful creation", func(t *testing.T) {
  1202  				g := NewWithT(t)
  1203  				awsMachine := getAWSMachine()
  1204  				setup(t, g, awsMachine)
  1205  				defer teardown(t, g)
  1206  				getInstances(t, g)
  1207  
  1208  				instance = &infrav1.Instance{
  1209  					ID: "myMachine",
  1210  				}
  1211  				instance.State = infrav1.InstanceStatePending
  1212  				secretSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(secretPrefix, int32(1), nil).Times(1)
  1213  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil).AnyTimes()
  1214  				secretSvc.EXPECT().UserData(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
  1215  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
  1216  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1217  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1218  
  1219  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1220  
  1221  				g.Expect(err).To(BeNil())
  1222  				g.Expect(ms.GetSecretPrefix()).To(Equal(secretPrefix))
  1223  				g.Expect(ms.GetSecretCount()).To(Equal(int32(1)))
  1224  			})
  1225  		})
  1226  	})
  1227  
  1228  	t.Run("Object storage lifecycle", func(t *testing.T) {
  1229  		t.Run("creating EC2 instances", func(t *testing.T) {
  1230  			var instance *infrav1.Instance
  1231  
  1232  			getInstances := func(t *testing.T, g *WithT) {
  1233  				t.Helper()
  1234  
  1235  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil).AnyTimes()
  1236  			}
  1237  
  1238  			useIgnition := func(t *testing.T, g *WithT) {
  1239  				t.Helper()
  1240  
  1241  				ms.Machine.Spec.Bootstrap.DataSecretName = pointer.StringPtr("bootstrap-data-ignition")
  1242  				ms.AWSMachine.Spec.CloudInit.SecretCount = 0
  1243  				ms.AWSMachine.Spec.CloudInit.SecretPrefix = ""
  1244  			}
  1245  
  1246  			t.Run("should leverage AWS S3", func(t *testing.T) {
  1247  				g := NewWithT(t)
  1248  				awsMachine := getAWSMachine()
  1249  				setup(t, g, awsMachine)
  1250  				defer teardown(t, g)
  1251  				getInstances(t, g)
  1252  				useIgnition(t, g)
  1253  
  1254  				instance = &infrav1.Instance{
  1255  					ID:    "myMachine",
  1256  					State: infrav1.InstanceStatePending,
  1257  				}
  1258  				fakeS3URL := "s3://foo"
  1259  
  1260  				objectStoreSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(fakeS3URL, nil).Times(1)
  1261  				ec2Svc.EXPECT().CreateInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(instance, nil).AnyTimes()
  1262  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
  1263  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1264  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1265  
  1266  				ms.AWSMachine.ObjectMeta.Labels = map[string]string{
  1267  					clusterv1.MachineControlPlaneLabelName: "",
  1268  				}
  1269  
  1270  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1271  				g.Expect(err).To(BeNil())
  1272  			})
  1273  		})
  1274  
  1275  		t.Run("there's a node ref and a secret ARN", func(t *testing.T) {
  1276  			var instance *infrav1.Instance
  1277  			setNodeRef := func(t *testing.T, g *WithT) {
  1278  				t.Helper()
  1279  
  1280  				instance = &infrav1.Instance{
  1281  					ID: "myMachine",
  1282  				}
  1283  
  1284  				ms.Machine.Status.NodeRef = &corev1.ObjectReference{
  1285  					Kind:       "Node",
  1286  					Name:       "myMachine",
  1287  					APIVersion: "v1",
  1288  				}
  1289  
  1290  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(instance, nil).AnyTimes()
  1291  			}
  1292  			useIgnition := func(t *testing.T, g *WithT) {
  1293  				t.Helper()
  1294  
  1295  				ms.Machine.Spec.Bootstrap.DataSecretName = pointer.StringPtr("bootstrap-data-ignition")
  1296  				ms.AWSMachine.Spec.CloudInit.SecretCount = 0
  1297  				ms.AWSMachine.Spec.CloudInit.SecretPrefix = ""
  1298  			}
  1299  
  1300  			t.Run("should delete the object if the instance is running", func(t *testing.T) {
  1301  				g := NewWithT(t)
  1302  				awsMachine := getAWSMachine()
  1303  				setup(t, g, awsMachine)
  1304  				defer teardown(t, g)
  1305  				setNodeRef(t, g)
  1306  				useIgnition(t, g)
  1307  
  1308  				instance.State = infrav1.InstanceStateRunning
  1309  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
  1310  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1311  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1312  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1313  
  1314  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1315  			})
  1316  
  1317  			t.Run("should delete the object if the instance is terminated", func(t *testing.T) {
  1318  				g := NewWithT(t)
  1319  				awsMachine := getAWSMachine()
  1320  				setup(t, g, awsMachine)
  1321  				defer teardown(t, g)
  1322  				setNodeRef(t, g)
  1323  				useIgnition(t, g)
  1324  
  1325  				instance.State = infrav1.InstanceStateTerminated
  1326  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1327  
  1328  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1329  			})
  1330  
  1331  			t.Run("should delete the object if the instance is deleted", func(t *testing.T) {
  1332  				g := NewWithT(t)
  1333  				awsMachine := getAWSMachine()
  1334  				setup(t, g, awsMachine)
  1335  				defer teardown(t, g)
  1336  				setNodeRef(t, g)
  1337  				useIgnition(t, g)
  1338  
  1339  				instance.State = infrav1.InstanceStateRunning
  1340  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1341  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1342  
  1343  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1344  			})
  1345  
  1346  			t.Run("should delete the object if the AWSMachine is in a failure condition", func(t *testing.T) {
  1347  				g := NewWithT(t)
  1348  				awsMachine := getAWSMachine()
  1349  				setup(t, g, awsMachine)
  1350  				defer teardown(t, g)
  1351  				setNodeRef(t, g)
  1352  				useIgnition(t, g)
  1353  
  1354  				// TODO: This seems to have no effect on the test result.
  1355  				ms.AWSMachine.Status.FailureReason = capierrors.MachineStatusErrorPtr(capierrors.UpdateMachineError)
  1356  
  1357  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1358  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1359  
  1360  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1361  			})
  1362  		})
  1363  
  1364  		t.Run("there's only a secret ARN and no node ref", func(t *testing.T) {
  1365  			var instance *infrav1.Instance
  1366  
  1367  			getInstances := func(t *testing.T, g *WithT) {
  1368  				t.Helper()
  1369  
  1370  				instance = &infrav1.Instance{
  1371  					ID: "myMachine",
  1372  				}
  1373  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(instance, nil).AnyTimes()
  1374  			}
  1375  
  1376  			useIgnition := func(t *testing.T, g *WithT) {
  1377  				t.Helper()
  1378  
  1379  				ms.Machine.Spec.Bootstrap.DataSecretName = pointer.StringPtr("bootstrap-data-ignition")
  1380  				ms.AWSMachine.Spec.CloudInit.SecretCount = 0
  1381  				ms.AWSMachine.Spec.CloudInit.SecretPrefix = ""
  1382  			}
  1383  
  1384  			t.Run("should not delete the object if the instance is running", func(t *testing.T) {
  1385  				g := NewWithT(t)
  1386  				awsMachine := getAWSMachine()
  1387  				setup(t, g, awsMachine)
  1388  				defer teardown(t, g)
  1389  				getInstances(t, g)
  1390  
  1391  				instance.State = infrav1.InstanceStateRunning
  1392  				ec2Svc.EXPECT().GetInstanceSecurityGroups(gomock.Any()).Return(map[string][]string{"eid": {}}, nil).Times(1)
  1393  				ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{}, nil).Times(1)
  1394  				ec2Svc.EXPECT().GetAdditionalSecurityGroupsIDs(gomock.Any()).Return(nil, nil)
  1395  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).MaxTimes(0)
  1396  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1397  			})
  1398  
  1399  			t.Run("should delete the object if the instance is terminated", func(t *testing.T) {
  1400  				g := NewWithT(t)
  1401  				awsMachine := getAWSMachine()
  1402  				setup(t, g, awsMachine)
  1403  				defer teardown(t, g)
  1404  				getInstances(t, g)
  1405  				useIgnition(t, g)
  1406  
  1407  				instance.State = infrav1.InstanceStateTerminated
  1408  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1409  				_, _ = reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1410  			})
  1411  
  1412  			t.Run("should delete the object if the AWSMachine is deleted", func(t *testing.T) {
  1413  				g := NewWithT(t)
  1414  				awsMachine := getAWSMachine()
  1415  				setup(t, g, awsMachine)
  1416  				defer teardown(t, g)
  1417  				getInstances(t, g)
  1418  				useIgnition(t, g)
  1419  
  1420  				instance.State = infrav1.InstanceStateRunning
  1421  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1422  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1423  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1424  			})
  1425  
  1426  			t.Run("should delete the object if the AWSMachine is in a failure condition", func(t *testing.T) {
  1427  				g := NewWithT(t)
  1428  				awsMachine := getAWSMachine()
  1429  				setup(t, g, awsMachine)
  1430  				defer teardown(t, g)
  1431  				getInstances(t, g)
  1432  				useIgnition(t, g)
  1433  
  1434  				// TODO: This seems to have no effect on the test result.
  1435  				ms.AWSMachine.Status.FailureReason = capierrors.MachineStatusErrorPtr(capierrors.UpdateMachineError)
  1436  				objectStoreSvc.EXPECT().Delete(gomock.Any()).Return(nil).Times(1)
  1437  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil).AnyTimes()
  1438  				_, _ = reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1439  			})
  1440  		})
  1441  
  1442  		t.Run("there is an intermittent connection issue and no object could be created", func(t *testing.T) {
  1443  			useIgnition := func(t *testing.T, g *WithT) {
  1444  				t.Helper()
  1445  
  1446  				ms.Machine.Spec.Bootstrap.DataSecretName = pointer.StringPtr("bootstrap-data-ignition")
  1447  				ms.AWSMachine.Spec.CloudInit.SecretCount = 0
  1448  				ms.AWSMachine.Spec.CloudInit.SecretPrefix = ""
  1449  			}
  1450  
  1451  			t.Run("should error if object could not be created", func(t *testing.T) {
  1452  				g := NewWithT(t)
  1453  				awsMachine := getAWSMachine()
  1454  				setup(t, g, awsMachine)
  1455  				defer teardown(t, g)
  1456  				useIgnition(t, g)
  1457  
  1458  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil).AnyTimes()
  1459  				objectStoreSvc.EXPECT().Create(gomock.Any(), gomock.Any()).Return("", errors.New("connection error")).Times(1)
  1460  				_, err := reconciler.reconcileNormal(context.Background(), ms, cs, cs, cs, cs)
  1461  				g.Expect(err).ToNot(BeNil())
  1462  				g.Expect(err.Error()).To(ContainSubstring("connection error"))
  1463  			})
  1464  		})
  1465  	})
  1466  
  1467  	t.Run("Deleting an AWSMachine", func(t *testing.T) {
  1468  		finalizer := func(t *testing.T, g *WithT) {
  1469  			t.Helper()
  1470  
  1471  			ms.AWSMachine.Finalizers = []string{
  1472  				infrav1.MachineFinalizer,
  1473  				metav1.FinalizerDeleteDependents,
  1474  			}
  1475  		}
  1476  		t.Run("should exit immediately on an error state", func(t *testing.T) {
  1477  			g := NewWithT(t)
  1478  			awsMachine := getAWSMachine()
  1479  			setup(t, g, awsMachine)
  1480  			defer teardown(t, g)
  1481  			finalizer(t, g)
  1482  
  1483  			expectedErr := errors.New("no connection available ")
  1484  			ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, expectedErr).AnyTimes()
  1485  			secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1486  
  1487  			_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1488  			g.Expect(errors.Cause(err)).To(MatchError(expectedErr))
  1489  		})
  1490  		t.Run("should log and remove finalizer when no machine exists", func(t *testing.T) {
  1491  			g := NewWithT(t)
  1492  			awsMachine := getAWSMachine()
  1493  			setup(t, g, awsMachine)
  1494  			defer teardown(t, g)
  1495  			finalizer(t, g)
  1496  
  1497  			ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(nil, nil)
  1498  			secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1499  
  1500  			buf := new(bytes.Buffer)
  1501  			klog.SetOutput(buf)
  1502  
  1503  			_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1504  			g.Expect(err).To(BeNil())
  1505  			g.Expect(buf.String()).To(ContainSubstring("Unable to locate EC2 instance by ID or tags"))
  1506  			g.Expect(ms.AWSMachine.Finalizers).To(ConsistOf(metav1.FinalizerDeleteDependents))
  1507  			g.Eventually(recorder.Events).Should(Receive(ContainSubstring("NoInstanceFound")))
  1508  		})
  1509  		t.Run("should ignore instances in shutting down state", func(t *testing.T) {
  1510  			g := NewWithT(t)
  1511  			awsMachine := getAWSMachine()
  1512  			setup(t, g, awsMachine)
  1513  			defer teardown(t, g)
  1514  			finalizer(t, g)
  1515  
  1516  			ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1517  				State: infrav1.InstanceStateShuttingDown,
  1518  			}, nil)
  1519  			secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1520  
  1521  			buf := new(bytes.Buffer)
  1522  			klog.SetOutput(buf)
  1523  
  1524  			_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1525  			g.Expect(err).To(BeNil())
  1526  			g.Expect(buf.String()).To(ContainSubstring("EC2 instance is shutting down or already terminated"))
  1527  			g.Expect(ms.AWSMachine.Finalizers).To(ConsistOf(metav1.FinalizerDeleteDependents))
  1528  		})
  1529  		t.Run("should ignore instances in terminated down state", func(t *testing.T) {
  1530  			g := NewWithT(t)
  1531  			awsMachine := getAWSMachine()
  1532  			setup(t, g, awsMachine)
  1533  			defer teardown(t, g)
  1534  			finalizer(t, g)
  1535  
  1536  			ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1537  				State: infrav1.InstanceStateTerminated,
  1538  			}, nil)
  1539  			secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1540  
  1541  			buf := new(bytes.Buffer)
  1542  			klog.SetOutput(buf)
  1543  
  1544  			_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1545  			g.Expect(err).To(BeNil())
  1546  			g.Expect(buf.String()).To(ContainSubstring("EC2 instance is shutting down or already terminated"))
  1547  			g.Expect(ms.AWSMachine.Finalizers).To(ConsistOf(metav1.FinalizerDeleteDependents))
  1548  		})
  1549  		t.Run("instance not shutting down yet", func(t *testing.T) {
  1550  			id := "aws:////myid"
  1551  			getRunningInstance := func(t *testing.T, g *WithT) {
  1552  				t.Helper()
  1553  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{ID: id}, nil)
  1554  				secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1555  			}
  1556  			t.Run("should return an error when the instance can't be terminated", func(t *testing.T) {
  1557  				g := NewWithT(t)
  1558  				awsMachine := getAWSMachine()
  1559  				setup(t, g, awsMachine)
  1560  				defer teardown(t, g)
  1561  				finalizer(t, g)
  1562  				getRunningInstance(t, g)
  1563  
  1564  				expected := errors.New("can't reach AWS to terminate machine")
  1565  				ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(expected)
  1566  
  1567  				buf := new(bytes.Buffer)
  1568  				klog.SetOutput(buf)
  1569  
  1570  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1571  				g.Expect(errors.Cause(err)).To(MatchError(expected))
  1572  				g.Expect(buf.String()).To(ContainSubstring("Terminating EC2 instance"))
  1573  				g.Eventually(recorder.Events).Should(Receive(ContainSubstring("FailedTerminate")))
  1574  			})
  1575  			t.Run("when instance can be shut down", func(t *testing.T) {
  1576  				terminateInstance := func(t *testing.T, g *WithT) {
  1577  					t.Helper()
  1578  					ec2Svc.EXPECT().TerminateInstanceAndWait(gomock.Any()).Return(nil)
  1579  					secretSvc.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes()
  1580  				}
  1581  
  1582  				t.Run("should error when it can't retrieve security groups if there are network interfaces", func(t *testing.T) {
  1583  					g := NewWithT(t)
  1584  					awsMachine := getAWSMachine()
  1585  					setup(t, g, awsMachine)
  1586  					defer teardown(t, g)
  1587  					finalizer(t, g)
  1588  					getRunningInstance(t, g)
  1589  					terminateInstance(t, g)
  1590  
  1591  					ms.AWSMachine.Spec.NetworkInterfaces = []string{
  1592  						"eth0",
  1593  						"eth1",
  1594  					}
  1595  					expected := errors.New("can't reach AWS to list security groups")
  1596  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return(nil, expected)
  1597  
  1598  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1599  					g.Expect(errors.Cause(err)).To(MatchError(expected))
  1600  				})
  1601  
  1602  				t.Run("should error when it can't detach a security group from an interface", func(t *testing.T) {
  1603  					g := NewWithT(t)
  1604  					awsMachine := getAWSMachine()
  1605  					setup(t, g, awsMachine)
  1606  					defer teardown(t, g)
  1607  					finalizer(t, g)
  1608  					getRunningInstance(t, g)
  1609  					terminateInstance(t, g)
  1610  
  1611  					ms.AWSMachine.Spec.NetworkInterfaces = []string{
  1612  						"eth0",
  1613  						"eth1",
  1614  					}
  1615  					expected := errors.New("can't reach AWS to detach security group")
  1616  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{"sg0", "sg1"}, nil)
  1617  					ec2Svc.EXPECT().DetachSecurityGroupsFromNetworkInterface(gomock.Any(), gomock.Any()).Return(expected)
  1618  
  1619  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1620  					g.Expect(errors.Cause(err)).To(MatchError(expected))
  1621  				})
  1622  
  1623  				t.Run("should detach all combinations of network interfaces", func(t *testing.T) {
  1624  					g := NewWithT(t)
  1625  					awsMachine := getAWSMachine()
  1626  					setup(t, g, awsMachine)
  1627  					defer teardown(t, g)
  1628  					finalizer(t, g)
  1629  					getRunningInstance(t, g)
  1630  					terminateInstance(t, g)
  1631  
  1632  					ms.AWSMachine.Spec.NetworkInterfaces = []string{
  1633  						"eth0",
  1634  						"eth1",
  1635  					}
  1636  					groups := []string{"sg0", "sg1"}
  1637  					ec2Svc.EXPECT().GetCoreSecurityGroups(gomock.Any()).Return([]string{"sg0", "sg1"}, nil)
  1638  					ec2Svc.EXPECT().DetachSecurityGroupsFromNetworkInterface(groups, "eth0").Return(nil)
  1639  					ec2Svc.EXPECT().DetachSecurityGroupsFromNetworkInterface(groups, "eth1").Return(nil)
  1640  
  1641  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1642  					g.Expect(err).To(BeNil())
  1643  				})
  1644  
  1645  				t.Run("should remove security groups", func(t *testing.T) {
  1646  					g := NewWithT(t)
  1647  					awsMachine := getAWSMachine()
  1648  					setup(t, g, awsMachine)
  1649  					defer teardown(t, g)
  1650  					finalizer(t, g)
  1651  					getRunningInstance(t, g)
  1652  					terminateInstance(t, g)
  1653  
  1654  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1655  					g.Expect(err).To(BeNil())
  1656  					g.Expect(ms.AWSMachine.Finalizers).To(ConsistOf(metav1.FinalizerDeleteDependents))
  1657  				})
  1658  
  1659  				t.Run("should fail to detach control plane ELB from instance", func(t *testing.T) {
  1660  					g := NewWithT(t)
  1661  					awsMachine := getAWSMachine()
  1662  					setup(t, g, awsMachine)
  1663  					defer teardown(t, g)
  1664  					finalizer(t, g)
  1665  					ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
  1666  					ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
  1667  					reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
  1668  						return elbSvc
  1669  					}
  1670  
  1671  					ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1672  						State: infrav1.InstanceStateTerminated,
  1673  					}, nil)
  1674  					elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(false, errors.New("error describing ELB"))
  1675  
  1676  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1677  					g.Expect(err).ToNot(BeNil())
  1678  					g.Expect(err.Error()).To(ContainSubstring("error describing ELB"))
  1679  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1680  					g.Eventually(recorder.Events).Should(Receive(ContainSubstring("FailedDetachControlPlaneELB")))
  1681  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.ELBAttachedCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, "DeletingFailed"}})
  1682  				})
  1683  
  1684  				t.Run("should not do anything if control plane ELB is already detached from instance", func(t *testing.T) {
  1685  					g := NewWithT(t)
  1686  					awsMachine := getAWSMachine()
  1687  					setup(t, g, awsMachine)
  1688  					defer teardown(t, g)
  1689  					finalizer(t, g)
  1690  					ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
  1691  					ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
  1692  					reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
  1693  						return elbSvc
  1694  					}
  1695  
  1696  					ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1697  						State: infrav1.InstanceStateTerminated,
  1698  					}, nil)
  1699  					elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(false, nil)
  1700  
  1701  					_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1702  					g.Expect(err).To(BeNil())
  1703  					g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1704  					expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.ELBAttachedCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason}})
  1705  				})
  1706  			})
  1707  		})
  1708  		t.Run("Reconcile LB detachment", func(t *testing.T) {
  1709  			t.Run("should fail to determine registration status of ELB", func(t *testing.T) {
  1710  				g := NewWithT(t)
  1711  				awsMachine := getAWSMachine()
  1712  				setup(t, g, awsMachine)
  1713  				defer teardown(t, g)
  1714  				finalizer(t, g)
  1715  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
  1716  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
  1717  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
  1718  					return elbSvc
  1719  				}
  1720  
  1721  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1722  					State: infrav1.InstanceStateTerminated,
  1723  				}, nil)
  1724  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(true, nil)
  1725  				elbSvc.EXPECT().DeregisterInstanceFromAPIServerELB(gomock.Any()).Return(nil)
  1726  
  1727  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1728  				g.Expect(err).To(BeNil())
  1729  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1730  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.ELBAttachedCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason}})
  1731  			})
  1732  			t.Run("should fail to detach control plane ELB from instance", func(t *testing.T) {
  1733  				g := NewWithT(t)
  1734  				awsMachine := getAWSMachine()
  1735  				setup(t, g, awsMachine)
  1736  				defer teardown(t, g)
  1737  				finalizer(t, g)
  1738  				ms.Machine.Labels = map[string]string{clusterv1.MachineControlPlaneLabelName: ""}
  1739  				ms.AWSMachine.Status.InstanceState = &infrav1.InstanceStateStopping
  1740  				reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
  1741  					return elbSvc
  1742  				}
  1743  
  1744  				ec2Svc.EXPECT().GetRunningInstanceByTags(gomock.Any()).Return(&infrav1.Instance{
  1745  					State: infrav1.InstanceStateTerminated,
  1746  				}, nil)
  1747  				elbSvc.EXPECT().IsInstanceRegisteredWithAPIServerELB(gomock.Any()).Return(true, nil)
  1748  				elbSvc.EXPECT().DeregisterInstanceFromAPIServerELB(gomock.Any()).Return(errors.New("Duplicate access point name for load balancer"))
  1749  
  1750  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1751  				g.Expect(err).ToNot(BeNil())
  1752  				g.Expect(err.Error()).To(ContainSubstring("Duplicate access point name for load balancer"))
  1753  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1754  				expectConditions(g, ms.AWSMachine, []conditionAssertion{{infrav1.ELBAttachedCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, "DeletingFailed"}})
  1755  			})
  1756  			t.Run("should fail if secretPrefix present, but secretCount is not set", func(t *testing.T) {
  1757  				g := NewWithT(t)
  1758  				awsMachine := getAWSMachine()
  1759  				setup(t, g, awsMachine)
  1760  				defer teardown(t, g)
  1761  				finalizer(t, g)
  1762  				ms.SetSecretPrefix("test")
  1763  				ms.SetSecretCount(0)
  1764  
  1765  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1766  				g.Expect(err).To(MatchError(ContainSubstring("secretPrefix present, but secretCount is not set")))
  1767  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1768  			})
  1769  			t.Run("should fail if secrets backend is invalid", func(t *testing.T) {
  1770  				g := NewWithT(t)
  1771  				awsMachine := getAWSMachine()
  1772  				setup(t, g, awsMachine)
  1773  				defer teardown(t, g)
  1774  				finalizer(t, g)
  1775  				ms.AWSMachine.Spec.CloudInit.SecureSecretsBackend = "InvalidSecretBackend"
  1776  
  1777  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1778  				g.Expect(err).To(MatchError(ContainSubstring("invalid secret backend")))
  1779  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1780  			})
  1781  			t.Run("should fail if deleting entries from AWS Secret fails", func(t *testing.T) {
  1782  				g := NewWithT(t)
  1783  				awsMachine := getAWSMachine()
  1784  				setup(t, g, awsMachine)
  1785  				defer teardown(t, g)
  1786  				finalizer(t, g)
  1787  				ms.SetSecretPrefix("test")
  1788  				ms.SetSecretCount(1)
  1789  				secretSvc.EXPECT().Delete(gomock.Any()).Return(errors.New("Hierarchy Type Mismatch Exception"))
  1790  
  1791  				_, err := reconciler.reconcileDelete(ms, cs, cs, cs, cs)
  1792  				g.Expect(err).To(MatchError(ContainSubstring("Hierarchy Type Mismatch Exception")))
  1793  				g.Expect(ms.AWSMachine.Finalizers).To(ContainElement(metav1.FinalizerDeleteDependents))
  1794  			})
  1795  		})
  1796  	})
  1797  }
  1798  
  1799  func TestAWSMachineReconciler_AWSClusterToAWSMachines(t *testing.T) {
  1800  	testCases := []struct {
  1801  		name         string
  1802  		ownerCluster *clusterv1.Cluster
  1803  		awsCluster   *infrav1.AWSCluster
  1804  		awsMachine   *clusterv1.Machine
  1805  		requests     []reconcile.Request
  1806  	}{
  1807  		{
  1808  			name:         "Should create reconcile request successfully",
  1809  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-6"}},
  1810  			awsMachine: &clusterv1.Machine{
  1811  				ObjectMeta: metav1.ObjectMeta{
  1812  					Name: "aws-test-6",
  1813  					Labels: map[string]string{
  1814  						clusterv1.ClusterLabelName: "capi-test-6",
  1815  					},
  1816  				},
  1817  				Spec: clusterv1.MachineSpec{
  1818  					ClusterName: "capi-test",
  1819  					InfrastructureRef: corev1.ObjectReference{
  1820  						Kind:       "AWSMachine",
  1821  						Name:       "aws-machine-6",
  1822  						APIVersion: infrav1.GroupVersion.String(),
  1823  					},
  1824  				},
  1825  			},
  1826  			awsCluster: &infrav1.AWSCluster{
  1827  				ObjectMeta: metav1.ObjectMeta{
  1828  					Name: "aws-test-6",
  1829  					OwnerReferences: []metav1.OwnerReference{
  1830  						{
  1831  							Name:       "capi-test-6",
  1832  							Kind:       "Cluster",
  1833  							APIVersion: clusterv1.GroupVersion.String(),
  1834  						},
  1835  					},
  1836  				},
  1837  			},
  1838  			requests: []reconcile.Request{
  1839  				{
  1840  					NamespacedName: types.NamespacedName{
  1841  						Namespace: "default",
  1842  						Name:      "aws-machine-6",
  1843  					},
  1844  				},
  1845  			},
  1846  		},
  1847  		{
  1848  			name:         "Should not create reconcile request for deleted clusters",
  1849  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", DeletionTimestamp: &metav1.Time{Time: time.Now()}}},
  1850  			awsMachine: &clusterv1.Machine{
  1851  				ObjectMeta: metav1.ObjectMeta{
  1852  					Labels: map[string]string{
  1853  						clusterv1.ClusterLabelName: "aws-test-1",
  1854  					},
  1855  					Name: "aws-test-1",
  1856  				},
  1857  				Spec: clusterv1.MachineSpec{
  1858  					ClusterName: "capi-test",
  1859  					InfrastructureRef: corev1.ObjectReference{
  1860  						Kind:       "AWSMachine",
  1861  						Name:       "aws-machine-1",
  1862  						APIVersion: infrav1.GroupVersion.String(),
  1863  					},
  1864  				},
  1865  			},
  1866  			awsCluster: &infrav1.AWSCluster{
  1867  				ObjectMeta: metav1.ObjectMeta{
  1868  					Name: "aws-test-1",
  1869  					OwnerReferences: []metav1.OwnerReference{
  1870  						{
  1871  							Name:       "capi-test-1",
  1872  							Kind:       "Cluster",
  1873  							APIVersion: clusterv1.GroupVersion.String(),
  1874  						},
  1875  					},
  1876  					DeletionTimestamp: &metav1.Time{Time: time.Now()},
  1877  				},
  1878  			},
  1879  		},
  1880  		{
  1881  			name: "Should not create reconcile request if ownerCluster not found",
  1882  			awsMachine: &clusterv1.Machine{
  1883  				ObjectMeta: metav1.ObjectMeta{
  1884  					Labels: map[string]string{
  1885  						clusterv1.ClusterLabelName: "aws-test-2",
  1886  					},
  1887  					Name: "aws-test-2",
  1888  				},
  1889  				Spec: clusterv1.MachineSpec{
  1890  					ClusterName: "capi-test",
  1891  					InfrastructureRef: corev1.ObjectReference{
  1892  						Kind:       "AWSMachine",
  1893  						Name:       "aws-machine-2",
  1894  						APIVersion: infrav1.GroupVersion.String(),
  1895  					},
  1896  				},
  1897  			},
  1898  			awsCluster: &infrav1.AWSCluster{
  1899  				ObjectMeta: metav1.ObjectMeta{
  1900  					Name: "aws-test-2",
  1901  					OwnerReferences: []metav1.OwnerReference{
  1902  						{
  1903  							Name:       "capi-test-2",
  1904  							Kind:       "Cluster",
  1905  							APIVersion: clusterv1.GroupVersion.String(),
  1906  						},
  1907  					},
  1908  				},
  1909  			},
  1910  		},
  1911  		{
  1912  			name:         "Should not create reconcile request if owned Machines not found",
  1913  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-3"}},
  1914  			awsMachine: &clusterv1.Machine{
  1915  				ObjectMeta: metav1.ObjectMeta{
  1916  					Name: "aws-test-3",
  1917  				},
  1918  				Spec: clusterv1.MachineSpec{
  1919  					ClusterName: "capi-test",
  1920  					InfrastructureRef: corev1.ObjectReference{
  1921  						Kind:       "AWSMachine",
  1922  						Name:       "aws-machine-3",
  1923  						APIVersion: infrav1.GroupVersion.String(),
  1924  					},
  1925  				},
  1926  			},
  1927  			awsCluster: &infrav1.AWSCluster{
  1928  				ObjectMeta: metav1.ObjectMeta{
  1929  					Name: "aws-test-3",
  1930  					OwnerReferences: []metav1.OwnerReference{
  1931  						{
  1932  							Name:       "capi-test-3",
  1933  							Kind:       "Cluster",
  1934  							APIVersion: clusterv1.GroupVersion.String(),
  1935  						},
  1936  					},
  1937  				},
  1938  			},
  1939  			requests: []reconcile.Request{},
  1940  		},
  1941  		{
  1942  			name:         "Should not create reconcile request if owned Machine type is not AWSMachine",
  1943  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-4"}},
  1944  			awsMachine: &clusterv1.Machine{
  1945  				ObjectMeta: metav1.ObjectMeta{
  1946  					Labels: map[string]string{
  1947  						clusterv1.ClusterLabelName: "capi-test-4",
  1948  					},
  1949  					Name:      "aws-test-4",
  1950  					Namespace: "default",
  1951  				},
  1952  				TypeMeta: metav1.TypeMeta{
  1953  					Kind: "Machine",
  1954  				},
  1955  				Spec: clusterv1.MachineSpec{
  1956  					ClusterName: "capi-test",
  1957  					InfrastructureRef: corev1.ObjectReference{
  1958  						Kind:       "Machine",
  1959  						Name:       "aws-machine-4",
  1960  						APIVersion: infrav1.GroupVersion.String(),
  1961  					},
  1962  				},
  1963  			},
  1964  			awsCluster: &infrav1.AWSCluster{
  1965  				ObjectMeta: metav1.ObjectMeta{
  1966  					Name: "aws-test-4",
  1967  					OwnerReferences: []metav1.OwnerReference{
  1968  						{
  1969  							Name:       "capi-test-4",
  1970  							Kind:       "Cluster",
  1971  							APIVersion: clusterv1.GroupVersion.String(),
  1972  						},
  1973  					},
  1974  				},
  1975  			},
  1976  			requests: []reconcile.Request{},
  1977  		},
  1978  		{
  1979  			name:         "Should not create reconcile request if name for machine in infrastructure ref not found",
  1980  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-5"}},
  1981  			awsMachine: &clusterv1.Machine{
  1982  				ObjectMeta: metav1.ObjectMeta{
  1983  					Name: "aws-test-5",
  1984  					Labels: map[string]string{
  1985  						clusterv1.ClusterLabelName: "capi-test-5",
  1986  					},
  1987  				},
  1988  				Spec: clusterv1.MachineSpec{
  1989  					ClusterName: "capi-test",
  1990  					InfrastructureRef: corev1.ObjectReference{
  1991  						Kind:       "AWSMachine",
  1992  						APIVersion: infrav1.GroupVersion.String(),
  1993  					},
  1994  				},
  1995  			},
  1996  			awsCluster: &infrav1.AWSCluster{
  1997  				ObjectMeta: metav1.ObjectMeta{
  1998  					Name: "aws-test-5",
  1999  					OwnerReferences: []metav1.OwnerReference{
  2000  						{
  2001  							Name:       "capi-test-5",
  2002  							Kind:       "Cluster",
  2003  							APIVersion: clusterv1.GroupVersion.String(),
  2004  						},
  2005  					},
  2006  				},
  2007  			},
  2008  			requests: []reconcile.Request{},
  2009  		},
  2010  	}
  2011  	for _, tc := range testCases {
  2012  		t.Run(tc.name, func(t *testing.T) {
  2013  			g := NewWithT(t)
  2014  			reconciler := &AWSMachineReconciler{
  2015  				Client: testEnv.Client,
  2016  				Log:    klogr.New(),
  2017  			}
  2018  			ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
  2019  			g.Expect(err).To(BeNil())
  2020  
  2021  			createObject(g, tc.ownerCluster, ns.Name)
  2022  			defer cleanupObject(g, tc.ownerCluster)
  2023  
  2024  			createObject(g, tc.awsMachine, ns.Name)
  2025  			defer cleanupObject(g, tc.awsMachine)
  2026  
  2027  			tc.awsCluster.Namespace = ns.Name
  2028  			defer t.Cleanup(func() {
  2029  				g.Expect(testEnv.Cleanup(ctx, tc.awsCluster, ns)).To(Succeed())
  2030  			})
  2031  
  2032  			requests := reconciler.AWSClusterToAWSMachines(klogr.New())(tc.awsCluster)
  2033  			if tc.requests != nil {
  2034  				if len(tc.requests) > 0 {
  2035  					tc.requests[0].Namespace = ns.Name
  2036  				}
  2037  				g.Expect(requests).To(ConsistOf(tc.requests))
  2038  			} else {
  2039  				g.Expect(requests).To(BeNil())
  2040  			}
  2041  		})
  2042  	}
  2043  }
  2044  
  2045  func TestAWSMachineReconciler_requeueAWSMachinesForUnpausedCluster(t *testing.T) {
  2046  	testCases := []struct {
  2047  		name         string
  2048  		ownerCluster *clusterv1.Cluster
  2049  		requests     []reconcile.Request
  2050  	}{
  2051  		{
  2052  			name:         "Should not create reconcile request for deleted clusters",
  2053  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", Namespace: "default", DeletionTimestamp: &metav1.Time{Time: time.Now()}}},
  2054  		},
  2055  		{
  2056  			name:         "Should create reconcile request successfully",
  2057  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", Namespace: "default"}},
  2058  			requests:     []reconcile.Request{},
  2059  		},
  2060  	}
  2061  	for _, tc := range testCases {
  2062  		t.Run(tc.name, func(t *testing.T) {
  2063  			g := NewWithT(t)
  2064  			reconciler := &AWSMachineReconciler{
  2065  				Client: testEnv.Client,
  2066  				Log:    klogr.New(),
  2067  			}
  2068  			requests := reconciler.requeueAWSMachinesForUnpausedCluster(klogr.New())(tc.ownerCluster)
  2069  			if tc.requests != nil {
  2070  				g.Expect(requests).To(ConsistOf(tc.requests))
  2071  			} else {
  2072  				g.Expect(requests).To(BeNil())
  2073  			}
  2074  		})
  2075  	}
  2076  }
  2077  
  2078  func TestAWSMachineReconciler_indexAWSMachineByInstanceID(t *testing.T) {
  2079  	t.Run("Should not return instance id if cluster type is not AWSCluster", func(t *testing.T) {
  2080  		g := NewWithT(t)
  2081  		reconciler := &AWSMachineReconciler{
  2082  			Client: testEnv.Client,
  2083  			Log:    klogr.New(),
  2084  		}
  2085  		machine := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", Namespace: "default"}}
  2086  		requests := reconciler.indexAWSMachineByInstanceID(machine)
  2087  		g.Expect(requests).To(BeNil())
  2088  	})
  2089  	t.Run("Should return instance id successfully", func(t *testing.T) {
  2090  		g := NewWithT(t)
  2091  		reconciler := &AWSMachineReconciler{
  2092  			Client: testEnv.Client,
  2093  			Log:    klogr.New(),
  2094  		}
  2095  		awsMachine := &infrav1.AWSMachine{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", Namespace: "default"}, Spec: infrav1.AWSMachineSpec{InstanceID: aws.String("12345")}}
  2096  		requests := reconciler.indexAWSMachineByInstanceID(awsMachine)
  2097  		g.Expect(requests).To(ConsistOf([]string{"12345"}))
  2098  	})
  2099  	t.Run("Should not return instance id if instance id is not present", func(t *testing.T) {
  2100  		g := NewWithT(t)
  2101  		reconciler := &AWSMachineReconciler{
  2102  			Client: testEnv.Client,
  2103  			Log:    klogr.New(),
  2104  		}
  2105  		awsMachine := &infrav1.AWSMachine{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1", Namespace: "default"}}
  2106  		requests := reconciler.indexAWSMachineByInstanceID(awsMachine)
  2107  		g.Expect(requests).To(BeNil())
  2108  	})
  2109  }
  2110  
  2111  func TestAWSMachineReconciler_Reconcile(t *testing.T) {
  2112  	testCases := []struct {
  2113  		name         string
  2114  		awsMachine   *infrav1.AWSMachine
  2115  		ownerMachine *clusterv1.Machine
  2116  		ownerCluster *clusterv1.Cluster
  2117  		awsCluster   *infrav1.AWSCluster
  2118  		expectError  bool
  2119  		requeue      bool
  2120  	}{
  2121  		{
  2122  			name:        "Should Reconcile successfully if no AWSMachine found",
  2123  			expectError: false,
  2124  		},
  2125  		{
  2126  			name:        "Should Reconcile AWSMachine with requeue",
  2127  			awsMachine:  &infrav1.AWSMachine{ObjectMeta: metav1.ObjectMeta{Name: "aws-test-1"}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"}},
  2128  			requeue:     true,
  2129  			expectError: false,
  2130  		},
  2131  		{
  2132  			name: "Should fail Reconcile with GetOwnerMachine failure",
  2133  			awsMachine: &infrav1.AWSMachine{
  2134  				ObjectMeta: metav1.ObjectMeta{
  2135  					Name: "aws-test-2",
  2136  					OwnerReferences: []metav1.OwnerReference{
  2137  						{
  2138  							APIVersion: clusterv1.GroupVersion.String(),
  2139  							Kind:       "Machine",
  2140  							Name:       "capi-test-machine",
  2141  							UID:        "1",
  2142  						},
  2143  					},
  2144  				},
  2145  				Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2146  			},
  2147  			expectError: true,
  2148  		},
  2149  		{
  2150  			name: "Should not Reconcile if machine does not contain cluster label",
  2151  			awsMachine: &infrav1.AWSMachine{
  2152  				ObjectMeta: metav1.ObjectMeta{
  2153  					Name: "aws-test-3", Annotations: map[string]string{clusterv1.PausedAnnotation: ""}, OwnerReferences: []metav1.OwnerReference{
  2154  						{
  2155  							APIVersion: clusterv1.GroupVersion.String(),
  2156  							Kind:       "Machine",
  2157  							Name:       "capi-test-machine",
  2158  							UID:        "1",
  2159  						},
  2160  					},
  2161  				}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2162  			},
  2163  			ownerMachine: &clusterv1.Machine{
  2164  				ObjectMeta: metav1.ObjectMeta{
  2165  					Name: "capi-test-machine",
  2166  				},
  2167  				Spec: clusterv1.MachineSpec{
  2168  					ClusterName: "capi-test",
  2169  				},
  2170  			},
  2171  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1"}},
  2172  			expectError:  false,
  2173  		},
  2174  		{
  2175  			name: "Should not Reconcile if cluster is paused",
  2176  			awsMachine: &infrav1.AWSMachine{
  2177  				ObjectMeta: metav1.ObjectMeta{
  2178  					Name: "aws-test-4", Annotations: map[string]string{clusterv1.PausedAnnotation: ""}, OwnerReferences: []metav1.OwnerReference{
  2179  						{
  2180  							APIVersion: clusterv1.GroupVersion.String(),
  2181  							Kind:       "Machine",
  2182  							Name:       "capi-test-machine",
  2183  							UID:        "1",
  2184  						},
  2185  					},
  2186  				}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2187  			},
  2188  			ownerMachine: &clusterv1.Machine{
  2189  				ObjectMeta: metav1.ObjectMeta{
  2190  					Labels: map[string]string{
  2191  						clusterv1.ClusterLabelName: "capi-test-1",
  2192  					},
  2193  					Name: "capi-test-machine", Namespace: "default",
  2194  				},
  2195  				Spec: clusterv1.MachineSpec{
  2196  					ClusterName: "capi-test",
  2197  				},
  2198  			},
  2199  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1"}},
  2200  			expectError:  false,
  2201  		},
  2202  		{
  2203  			name: "Should not Reconcile if AWSManagedControlPlane is not ready",
  2204  			awsMachine: &infrav1.AWSMachine{
  2205  				ObjectMeta: metav1.ObjectMeta{
  2206  					Name: "aws-test-5", OwnerReferences: []metav1.OwnerReference{
  2207  						{
  2208  							APIVersion: clusterv1.GroupVersion.String(),
  2209  							Kind:       "Machine",
  2210  							Name:       "capi-test-machine",
  2211  							UID:        "1",
  2212  						},
  2213  					},
  2214  				}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2215  			},
  2216  			ownerMachine: &clusterv1.Machine{
  2217  				ObjectMeta: metav1.ObjectMeta{
  2218  					Labels: map[string]string{
  2219  						clusterv1.ClusterLabelName: "capi-test-1",
  2220  					},
  2221  					Name: "capi-test-machine", Namespace: "default",
  2222  				}, Spec: clusterv1.MachineSpec{
  2223  					ClusterName: "capi-test",
  2224  				},
  2225  			},
  2226  			ownerCluster: &clusterv1.Cluster{
  2227  				ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1"},
  2228  				Spec: clusterv1.ClusterSpec{
  2229  					ControlPlaneRef: &corev1.ObjectReference{Kind: AWSManagedControlPlaneRefKind},
  2230  				},
  2231  			},
  2232  			expectError: false,
  2233  		},
  2234  		{
  2235  			name: "Should not Reconcile if AWSCluster is not ready",
  2236  			awsMachine: &infrav1.AWSMachine{
  2237  				ObjectMeta: metav1.ObjectMeta{
  2238  					Name: "aws-test-5", OwnerReferences: []metav1.OwnerReference{
  2239  						{
  2240  							APIVersion: clusterv1.GroupVersion.String(),
  2241  							Kind:       "Machine",
  2242  							Name:       "capi-test-machine",
  2243  							UID:        "1",
  2244  						},
  2245  					},
  2246  				}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2247  			},
  2248  			ownerMachine: &clusterv1.Machine{
  2249  				ObjectMeta: metav1.ObjectMeta{
  2250  					Labels: map[string]string{
  2251  						clusterv1.ClusterLabelName: "capi-test-1",
  2252  					},
  2253  					Name: "capi-test-machine", Namespace: "default",
  2254  				},
  2255  				Spec: clusterv1.MachineSpec{
  2256  					ClusterName: "capi-test",
  2257  				},
  2258  			},
  2259  			ownerCluster: &clusterv1.Cluster{
  2260  				ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1"},
  2261  				Spec: clusterv1.ClusterSpec{
  2262  					InfrastructureRef: &corev1.ObjectReference{Name: "aws-test-5"},
  2263  				},
  2264  			},
  2265  			expectError: false,
  2266  		},
  2267  		{
  2268  			name: "Should fail to reconcile while fetching infra cluster",
  2269  			awsMachine: &infrav1.AWSMachine{
  2270  				ObjectMeta: metav1.ObjectMeta{
  2271  					Name: "aws-test-5", OwnerReferences: []metav1.OwnerReference{
  2272  						{
  2273  							APIVersion: clusterv1.GroupVersion.String(),
  2274  							Kind:       "Machine",
  2275  							Name:       "capi-test-machine",
  2276  							UID:        "1",
  2277  						},
  2278  					},
  2279  				}, Spec: infrav1.AWSMachineSpec{InstanceType: "test"},
  2280  			},
  2281  			ownerMachine: &clusterv1.Machine{
  2282  				ObjectMeta: metav1.ObjectMeta{
  2283  					Labels: map[string]string{
  2284  						clusterv1.ClusterLabelName: "capi-test-1",
  2285  					},
  2286  					Name: "capi-test-machine", Namespace: "default",
  2287  				},
  2288  				Spec: clusterv1.MachineSpec{
  2289  					ClusterName: "capi-test",
  2290  				},
  2291  			},
  2292  			ownerCluster: &clusterv1.Cluster{
  2293  				ObjectMeta: metav1.ObjectMeta{Name: "capi-test-1"},
  2294  				Spec: clusterv1.ClusterSpec{
  2295  					InfrastructureRef: &corev1.ObjectReference{Name: "aws-test-5"},
  2296  				},
  2297  			},
  2298  			awsCluster:  &infrav1.AWSCluster{ObjectMeta: metav1.ObjectMeta{Name: "aws-test-5"}},
  2299  			expectError: true,
  2300  		},
  2301  	}
  2302  
  2303  	for _, tc := range testCases {
  2304  		t.Run(tc.name, func(t *testing.T) {
  2305  			g := NewWithT(t)
  2306  			reconciler := &AWSMachineReconciler{
  2307  				Client: testEnv.Client,
  2308  			}
  2309  			ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
  2310  			g.Expect(err).To(BeNil())
  2311  			defer func() {
  2312  				g.Expect(testEnv.Cleanup(ctx, ns)).To(Succeed())
  2313  			}()
  2314  
  2315  			createObject(g, tc.ownerCluster, ns.Name)
  2316  			defer cleanupObject(g, tc.ownerCluster)
  2317  
  2318  			createObject(g, tc.awsCluster, ns.Name)
  2319  			defer cleanupObject(g, tc.awsCluster)
  2320  
  2321  			createObject(g, tc.ownerMachine, ns.Name)
  2322  			defer cleanupObject(g, tc.ownerMachine)
  2323  
  2324  			createObject(g, tc.awsMachine, ns.Name)
  2325  			defer cleanupObject(g, tc.awsMachine)
  2326  			if tc.awsMachine != nil {
  2327  				g.Eventually(func() bool {
  2328  					machine := &infrav1.AWSMachine{}
  2329  					key := client.ObjectKey{
  2330  						Name:      tc.awsMachine.Name,
  2331  						Namespace: ns.Name,
  2332  					}
  2333  					err = testEnv.Get(ctx, key, machine)
  2334  					return err == nil
  2335  				}, 10*time.Second).Should(Equal(true))
  2336  
  2337  				result, err := reconciler.Reconcile(ctx, ctrl.Request{
  2338  					NamespacedName: client.ObjectKey{
  2339  						Namespace: tc.awsMachine.Namespace,
  2340  						Name:      tc.awsMachine.Name,
  2341  					},
  2342  				})
  2343  				if tc.expectError {
  2344  					g.Expect(err).ToNot(BeNil())
  2345  				} else {
  2346  					g.Expect(err).To(BeNil())
  2347  				}
  2348  				if tc.requeue {
  2349  					g.Expect(result.RequeueAfter).To(BeZero())
  2350  				}
  2351  			} else {
  2352  				_, err = reconciler.Reconcile(ctx, ctrl.Request{
  2353  					NamespacedName: client.ObjectKey{
  2354  						Namespace: "default",
  2355  						Name:      "test",
  2356  					},
  2357  				})
  2358  				g.Expect(err).To(BeNil())
  2359  			}
  2360  		})
  2361  	}
  2362  }
  2363  
  2364  func createObject(g *WithT, obj client.Object, namespace string) {
  2365  	if obj.DeepCopyObject() != nil {
  2366  		obj.SetNamespace(namespace)
  2367  		g.Expect(testEnv.Create(ctx, obj)).To(Succeed())
  2368  	}
  2369  }
  2370  
  2371  func cleanupObject(g *WithT, obj client.Object) {
  2372  	if obj.DeepCopyObject() != nil {
  2373  		g.Expect(testEnv.Cleanup(ctx, obj)).To(Succeed())
  2374  	}
  2375  }