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

     1  /*
     2  Copyright 2022 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  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/golang/mock/gomock"
    27  	. "github.com/onsi/gomega"
    28  	corev1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/client-go/tools/record"
    32  	ctrl "sigs.k8s.io/controller-runtime"
    33  	"sigs.k8s.io/controller-runtime/pkg/client"
    34  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    35  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    36  
    37  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    38  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    39  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services"
    40  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/mock_services"
    41  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    42  	"sigs.k8s.io/cluster-api/util"
    43  )
    44  
    45  func TestAWSClusterReconciler_Reconcile(t *testing.T) {
    46  	testCases := []struct {
    47  		name         string
    48  		awsCluster   *infrav1.AWSCluster
    49  		ownerCluster *clusterv1.Cluster
    50  		expectError  bool
    51  	}{
    52  		{
    53  			name: "Should fail Reconcile if owner cluster not found",
    54  			awsCluster: &infrav1.AWSCluster{ObjectMeta: metav1.ObjectMeta{GenerateName: "aws-test-", OwnerReferences: []metav1.OwnerReference{
    55  				{
    56  					APIVersion: clusterv1.GroupVersion.String(),
    57  					Kind:       "Cluster",
    58  					Name:       "capi-fail-test",
    59  					UID:        "1",
    60  				}}}},
    61  			expectError: true,
    62  		},
    63  		{
    64  			name:        "Should not reconcile if owner reference is not set",
    65  			awsCluster:  &infrav1.AWSCluster{ObjectMeta: metav1.ObjectMeta{GenerateName: "aws-test-"}},
    66  			expectError: false,
    67  		},
    68  		{
    69  			name:         "Should not Reconcile if cluster is paused",
    70  			awsCluster:   &infrav1.AWSCluster{ObjectMeta: metav1.ObjectMeta{GenerateName: "aws-test-", Annotations: map[string]string{clusterv1.PausedAnnotation: ""}}},
    71  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{GenerateName: "capi-test-"}},
    72  			expectError:  false,
    73  		},
    74  		{
    75  			name:        "Should Reconcile successfully if no AWSCluster found",
    76  			expectError: false,
    77  		},
    78  	}
    79  
    80  	for _, tc := range testCases {
    81  		t.Run(tc.name, func(t *testing.T) {
    82  			g := NewWithT(t)
    83  			reconciler := &AWSClusterReconciler{
    84  				Client: testEnv.Client,
    85  			}
    86  
    87  			ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
    88  			g.Expect(err).To(BeNil())
    89  
    90  			if tc.ownerCluster != nil {
    91  				tc.ownerCluster.Namespace = ns.Name
    92  				g.Expect(testEnv.Create(ctx, tc.ownerCluster)).To(Succeed())
    93  				defer func(do ...client.Object) {
    94  					g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
    95  				}(tc.ownerCluster)
    96  				tc.awsCluster.OwnerReferences = []metav1.OwnerReference{
    97  					{
    98  						APIVersion: clusterv1.GroupVersion.String(),
    99  						Kind:       "Cluster",
   100  						Name:       tc.ownerCluster.Name,
   101  						UID:        "1",
   102  					},
   103  				}
   104  			}
   105  			createCluster(g, tc.awsCluster, ns.Name)
   106  			defer cleanupCluster(g, tc.awsCluster, ns)
   107  
   108  			if tc.awsCluster != nil {
   109  				_, err := reconciler.Reconcile(ctx, ctrl.Request{
   110  					NamespacedName: client.ObjectKey{
   111  						Namespace: tc.awsCluster.Namespace,
   112  						Name:      tc.awsCluster.Name,
   113  					},
   114  				})
   115  				if tc.expectError {
   116  					g.Expect(err).ToNot(BeNil())
   117  				} else {
   118  					g.Expect(err).To(BeNil())
   119  				}
   120  			} else {
   121  				_, err := reconciler.Reconcile(ctx, ctrl.Request{
   122  					NamespacedName: client.ObjectKey{
   123  						Namespace: ns.Name,
   124  						Name:      "test",
   125  					},
   126  				})
   127  				g.Expect(err).To(BeNil())
   128  			}
   129  		})
   130  	}
   131  }
   132  
   133  func TestAWSClusterReconcileOperations(t *testing.T) {
   134  	var (
   135  		reconciler AWSClusterReconciler
   136  		mockCtrl   *gomock.Controller
   137  		ec2Svc     *mock_services.MockEC2Interface
   138  		elbSvc     *mock_services.MockELBInterface
   139  		networkSvc *mock_services.MockNetworkInterface
   140  		sgSvc      *mock_services.MockSecurityGroupInterface
   141  		recorder   *record.FakeRecorder
   142  		ctx        context.Context
   143  	)
   144  
   145  	setup := func(t *testing.T, awsCluster *infrav1.AWSCluster) client.WithWatch {
   146  		t.Helper()
   147  		ctx = context.TODO()
   148  		secret := &corev1.Secret{
   149  			ObjectMeta: metav1.ObjectMeta{
   150  				Name:      "test-secret",
   151  				Namespace: "capa-system",
   152  			},
   153  			Data: map[string][]byte{
   154  				"AccessKeyID":     []byte("access-key-id"),
   155  				"SecretAccessKey": []byte("secret-access-key"),
   156  				"SessionToken":    []byte("session-token"),
   157  			},
   158  		}
   159  		csClient := fake.NewClientBuilder().WithObjects(awsCluster, secret).Build()
   160  
   161  		mockCtrl = gomock.NewController(t)
   162  		ec2Svc = mock_services.NewMockEC2Interface(mockCtrl)
   163  		elbSvc = mock_services.NewMockELBInterface(mockCtrl)
   164  		networkSvc = mock_services.NewMockNetworkInterface(mockCtrl)
   165  		sgSvc = mock_services.NewMockSecurityGroupInterface(mockCtrl)
   166  
   167  		recorder = record.NewFakeRecorder(2)
   168  
   169  		reconciler = AWSClusterReconciler{
   170  			Client: csClient,
   171  			ec2ServiceFactory: func(scope.EC2Scope) services.EC2Interface {
   172  				return ec2Svc
   173  			},
   174  			elbServiceFactory: func(elbScope scope.ELBScope) services.ELBInterface {
   175  				return elbSvc
   176  			},
   177  			networkServiceFactory: func(clusterScope scope.ClusterScope) services.NetworkInterface {
   178  				return networkSvc
   179  			},
   180  			securityGroupFactory: func(clusterScope scope.ClusterScope) services.SecurityGroupInterface {
   181  				return sgSvc
   182  			},
   183  			Recorder: recorder,
   184  		}
   185  		return csClient
   186  	}
   187  
   188  	teardown := func() {
   189  		mockCtrl.Finish()
   190  	}
   191  
   192  	t.Run("Reconciling an AWSCluster", func(t *testing.T) {
   193  		t.Run("Reconcile success", func(t *testing.T) {
   194  			t.Run("Should successfully create AWSCluster with Cluster Finalizer and LoadBalancerReady status true on AWSCluster", func(t *testing.T) {
   195  				g := NewWithT(t)
   196  				runningCluster := func() {
   197  					ec2Svc.EXPECT().ReconcileBastion().Return(nil)
   198  					elbSvc.EXPECT().ReconcileLoadbalancers().Return(nil)
   199  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   200  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(nil)
   201  				}
   202  
   203  				awsCluster := getAWSCluster("test", "test")
   204  				csClient := setup(t, &awsCluster)
   205  				defer teardown()
   206  				runningCluster()
   207  				cs, err := scope.NewClusterScope(
   208  					scope.ClusterScopeParams{
   209  						Client:     csClient,
   210  						Cluster:    &clusterv1.Cluster{},
   211  						AWSCluster: &awsCluster,
   212  					},
   213  				)
   214  				g.Expect(err).To(BeNil())
   215  				awsCluster.Status.Network.APIServerELB.DNSName = DNSName
   216  				awsCluster.Status.Network.APIServerELB.AvailabilityZones = []string{"us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e"}
   217  				cs.SetSubnets(infrav1.Subnets{
   218  					{
   219  						ID:               "private-subnet-1",
   220  						AvailabilityZone: "us-east-1b",
   221  						IsPublic:         false,
   222  					},
   223  					{
   224  						ID:               "private-subnet-2",
   225  						AvailabilityZone: "us-east-1a",
   226  						IsPublic:         false,
   227  					},
   228  					{
   229  						ID:               "private-subnet-3",
   230  						AvailabilityZone: "us-east-1c",
   231  						IsPublic:         false,
   232  					},
   233  					{
   234  						ID:               "private-subnet-4",
   235  						AvailabilityZone: "us-east-1d",
   236  						IsPublic:         false,
   237  					},
   238  					{
   239  						ID:               "private-subnet-5",
   240  						AvailabilityZone: "us-east-1e",
   241  						IsPublic:         false,
   242  					},
   243  				})
   244  				_, err = reconciler.reconcileNormal(cs)
   245  				g.Expect(err).To(BeNil())
   246  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.LoadBalancerReadyCondition, corev1.ConditionTrue, "", ""}})
   247  				g.Expect(awsCluster.GetFinalizers()).To(ContainElement(infrav1.ClusterFinalizer))
   248  			})
   249  		})
   250  		t.Run("Reconcile failure", func(t *testing.T) {
   251  			expectedErr := errors.New("failed to get resource")
   252  			t.Run("Should fail AWSCluster create with reconcile network failure", func(t *testing.T) {
   253  				g := NewWithT(t)
   254  				awsCluster := getAWSCluster("test", "test")
   255  				runningCluster := func() {
   256  					networkSvc.EXPECT().ReconcileNetwork().Return(expectedErr)
   257  				}
   258  				csClient := setup(t, &awsCluster)
   259  				defer teardown()
   260  				runningCluster()
   261  				cs, err := scope.NewClusterScope(
   262  					scope.ClusterScopeParams{
   263  						Client:     csClient,
   264  						Cluster:    &clusterv1.Cluster{},
   265  						AWSCluster: &awsCluster,
   266  					},
   267  				)
   268  				g.Expect(err).To(BeNil())
   269  				_, err = reconciler.reconcileNormal(cs)
   270  				g.Expect(err).Should(Equal(expectedErr))
   271  			})
   272  			t.Run("Should fail AWSCluster create with ClusterSecurityGroupsReadyCondition status false", func(t *testing.T) {
   273  				g := NewWithT(t)
   274  				awsCluster := getAWSCluster("test", "test")
   275  				runningCluster := func() {
   276  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   277  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(expectedErr)
   278  				}
   279  				csClient := setup(t, &awsCluster)
   280  				defer teardown()
   281  				runningCluster()
   282  				cs, err := scope.NewClusterScope(
   283  					scope.ClusterScopeParams{
   284  						Client:     csClient,
   285  						Cluster:    &clusterv1.Cluster{},
   286  						AWSCluster: &awsCluster,
   287  					},
   288  				)
   289  				g.Expect(err).To(BeNil())
   290  				_, err = reconciler.reconcileNormal(cs)
   291  				g.Expect(err).ToNot(BeNil())
   292  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.ClusterSecurityGroupsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.ClusterSecurityGroupReconciliationFailedReason}})
   293  			})
   294  			t.Run("Should fail AWSCluster create with BastionHostReadyCondition status false", func(t *testing.T) {
   295  				g := NewWithT(t)
   296  				awsCluster := getAWSCluster("test", "test")
   297  				runningCluster := func() {
   298  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   299  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(nil)
   300  					ec2Svc.EXPECT().ReconcileBastion().Return(expectedErr)
   301  				}
   302  				csClient := setup(t, &awsCluster)
   303  				defer teardown()
   304  				runningCluster()
   305  				cs, err := scope.NewClusterScope(
   306  					scope.ClusterScopeParams{
   307  						Client:     csClient,
   308  						Cluster:    &clusterv1.Cluster{},
   309  						AWSCluster: &awsCluster,
   310  					},
   311  				)
   312  				g.Expect(err).To(BeNil())
   313  				_, err = reconciler.reconcileNormal(cs)
   314  				g.Expect(err).ToNot(BeNil())
   315  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.BastionHostReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.BastionHostFailedReason}})
   316  			})
   317  			t.Run("Should fail AWSCluster create with failure in LoadBalancer reconciliation", func(t *testing.T) {
   318  				g := NewWithT(t)
   319  				awsCluster := getAWSCluster("test", "test")
   320  				runningCluster := func() {
   321  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   322  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(nil)
   323  					ec2Svc.EXPECT().ReconcileBastion().Return(nil)
   324  					elbSvc.EXPECT().ReconcileLoadbalancers().Return(expectedErr)
   325  				}
   326  				csClient := setup(t, &awsCluster)
   327  				defer teardown()
   328  				runningCluster()
   329  				cs, err := scope.NewClusterScope(
   330  					scope.ClusterScopeParams{
   331  						Client:     csClient,
   332  						Cluster:    &clusterv1.Cluster{},
   333  						AWSCluster: &awsCluster,
   334  					},
   335  				)
   336  				g.Expect(err).To(BeNil())
   337  				_, err = reconciler.reconcileNormal(cs)
   338  				g.Expect(err).ToNot(BeNil())
   339  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.LoadBalancerReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityWarning, infrav1.LoadBalancerFailedReason}})
   340  			})
   341  			t.Run("Should fail AWSCluster create with LoadBalancer reconcile failure with WaitForDNSName condition as false", func(t *testing.T) {
   342  				g := NewWithT(t)
   343  				awsCluster := getAWSCluster("test", "test")
   344  				runningCluster := func() {
   345  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   346  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(nil)
   347  					ec2Svc.EXPECT().ReconcileBastion().Return(nil)
   348  					elbSvc.EXPECT().ReconcileLoadbalancers().Return(nil)
   349  				}
   350  				csClient := setup(t, &awsCluster)
   351  				defer teardown()
   352  				runningCluster()
   353  				cs, err := scope.NewClusterScope(
   354  					scope.ClusterScopeParams{
   355  						Client:     csClient,
   356  						Cluster:    &clusterv1.Cluster{},
   357  						AWSCluster: &awsCluster,
   358  					},
   359  				)
   360  				g.Expect(err).To(BeNil())
   361  				_, err = reconciler.reconcileNormal(cs)
   362  				g.Expect(err).To(BeNil())
   363  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.LoadBalancerReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav1.WaitForDNSNameReason}})
   364  			})
   365  			t.Run("Should fail AWSCluster create with LoadBalancer reconcile failure with WaitForDNSNameResolve condition as false", func(t *testing.T) {
   366  				g := NewWithT(t)
   367  				awsCluster := getAWSCluster("test", "test")
   368  				runningCluster := func() {
   369  					networkSvc.EXPECT().ReconcileNetwork().Return(nil)
   370  					sgSvc.EXPECT().ReconcileSecurityGroups().Return(nil)
   371  					ec2Svc.EXPECT().ReconcileBastion().Return(nil)
   372  					elbSvc.EXPECT().ReconcileLoadbalancers().Return(nil)
   373  				}
   374  				csClient := setup(t, &awsCluster)
   375  				defer teardown()
   376  				runningCluster()
   377  				cs, err := scope.NewClusterScope(
   378  					scope.ClusterScopeParams{
   379  						Client:     csClient,
   380  						Cluster:    &clusterv1.Cluster{},
   381  						AWSCluster: &awsCluster,
   382  					},
   383  				)
   384  				awsCluster.Status.Network.APIServerELB.DNSName = "test-apiserver.us-east-1.aws"
   385  				g.Expect(err).To(BeNil())
   386  				_, err = reconciler.reconcileNormal(cs)
   387  				g.Expect(err).To(BeNil())
   388  				expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.LoadBalancerReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, infrav1.WaitForDNSNameResolveReason}})
   389  			})
   390  		})
   391  	})
   392  	t.Run("Reconcile delete AWSCluster", func(t *testing.T) {
   393  		t.Run("Reconcile success", func(t *testing.T) {
   394  			deleteCluster := func() {
   395  				ec2Svc.EXPECT().DeleteBastion().Return(nil)
   396  				elbSvc.EXPECT().DeleteLoadbalancers().Return(nil)
   397  				networkSvc.EXPECT().DeleteNetwork().Return(nil)
   398  				sgSvc.EXPECT().DeleteSecurityGroups().Return(nil)
   399  			}
   400  			t.Run("Should successfully delete AWSCluster with Cluster Finalizer removed", func(t *testing.T) {
   401  				g := NewWithT(t)
   402  				awsCluster := getAWSCluster("test", "test")
   403  				csClient := setup(t, &awsCluster)
   404  				defer teardown()
   405  				deleteCluster()
   406  				cs, err := scope.NewClusterScope(
   407  					scope.ClusterScopeParams{
   408  						Client:     csClient,
   409  						Cluster:    &clusterv1.Cluster{},
   410  						AWSCluster: &awsCluster,
   411  					},
   412  				)
   413  				g.Expect(err).To(BeNil())
   414  				_, err = reconciler.reconcileDelete(ctx, cs)
   415  				g.Expect(err).To(BeNil())
   416  				g.Expect(awsCluster.GetFinalizers()).ToNot(ContainElement(infrav1.ClusterFinalizer))
   417  			})
   418  		})
   419  		t.Run("Reconcile failure", func(t *testing.T) {
   420  			expectedErr := errors.New("failed to get resource")
   421  			t.Run("Should fail AWSCluster delete with LoadBalancer deletion failed and Cluster Finalizer not removed", func(t *testing.T) {
   422  				g := NewWithT(t)
   423  				deleteCluster := func() {
   424  					t.Helper()
   425  					elbSvc.EXPECT().DeleteLoadbalancers().Return(expectedErr)
   426  				}
   427  				awsCluster := getAWSCluster("test", "test")
   428  				awsCluster.Finalizers = []string{infrav1.ClusterFinalizer}
   429  				csClient := setup(t, &awsCluster)
   430  				defer teardown()
   431  				deleteCluster()
   432  				cs, err := scope.NewClusterScope(
   433  					scope.ClusterScopeParams{
   434  						Client:     csClient,
   435  						Cluster:    &clusterv1.Cluster{},
   436  						AWSCluster: &awsCluster,
   437  					},
   438  				)
   439  				g.Expect(err).To(BeNil())
   440  				_, err = reconciler.reconcileDelete(ctx, cs)
   441  				g.Expect(err).ToNot(BeNil())
   442  				g.Expect(awsCluster.GetFinalizers()).To(ContainElement(infrav1.ClusterFinalizer))
   443  			})
   444  			t.Run("Should fail AWSCluster delete with Bastion deletion failed and Cluster Finalizer not removed", func(t *testing.T) {
   445  				g := NewWithT(t)
   446  				deleteCluster := func() {
   447  					ec2Svc.EXPECT().DeleteBastion().Return(expectedErr)
   448  					elbSvc.EXPECT().DeleteLoadbalancers().Return(nil)
   449  				}
   450  				awsCluster := getAWSCluster("test", "test")
   451  				awsCluster.Finalizers = []string{infrav1.ClusterFinalizer}
   452  				csClient := setup(t, &awsCluster)
   453  				defer teardown()
   454  				deleteCluster()
   455  				cs, err := scope.NewClusterScope(
   456  					scope.ClusterScopeParams{
   457  						Client:     csClient,
   458  						Cluster:    &clusterv1.Cluster{},
   459  						AWSCluster: &awsCluster,
   460  					},
   461  				)
   462  				g.Expect(err).To(BeNil())
   463  				_, err = reconciler.reconcileDelete(ctx, cs)
   464  				g.Expect(err).ToNot(BeNil())
   465  				g.Expect(awsCluster.GetFinalizers()).To(ContainElement(infrav1.ClusterFinalizer))
   466  			})
   467  			t.Run("Should fail AWSCluster delete with security group deletion failed and Cluster Finalizer not removed", func(t *testing.T) {
   468  				g := NewWithT(t)
   469  				deleteCluster := func() {
   470  					ec2Svc.EXPECT().DeleteBastion().Return(nil)
   471  					elbSvc.EXPECT().DeleteLoadbalancers().Return(nil)
   472  					sgSvc.EXPECT().DeleteSecurityGroups().Return(expectedErr)
   473  				}
   474  				awsCluster := getAWSCluster("test", "test")
   475  				awsCluster.Finalizers = []string{infrav1.ClusterFinalizer}
   476  				csClient := setup(t, &awsCluster)
   477  				defer teardown()
   478  				deleteCluster()
   479  				cs, err := scope.NewClusterScope(
   480  					scope.ClusterScopeParams{
   481  						Client:     csClient,
   482  						Cluster:    &clusterv1.Cluster{},
   483  						AWSCluster: &awsCluster,
   484  					},
   485  				)
   486  				g.Expect(err).To(BeNil())
   487  				_, err = reconciler.reconcileDelete(ctx, cs)
   488  				g.Expect(err).ToNot(BeNil())
   489  				g.Expect(awsCluster.GetFinalizers()).To(ContainElement(infrav1.ClusterFinalizer))
   490  			})
   491  			t.Run("Should fail AWSCluster delete with network deletion failed and Cluster Finalizer not removed", func(t *testing.T) {
   492  				g := NewWithT(t)
   493  				deleteCluster := func() {
   494  					ec2Svc.EXPECT().DeleteBastion().Return(nil)
   495  					elbSvc.EXPECT().DeleteLoadbalancers().Return(nil)
   496  					sgSvc.EXPECT().DeleteSecurityGroups().Return(nil)
   497  					networkSvc.EXPECT().DeleteNetwork().Return(expectedErr)
   498  				}
   499  				awsCluster := getAWSCluster("test", "test")
   500  				awsCluster.Finalizers = []string{infrav1.ClusterFinalizer}
   501  				csClient := setup(t, &awsCluster)
   502  				defer teardown()
   503  				deleteCluster()
   504  				cs, err := scope.NewClusterScope(
   505  					scope.ClusterScopeParams{
   506  						Client:     csClient,
   507  						Cluster:    &clusterv1.Cluster{},
   508  						AWSCluster: &awsCluster,
   509  					},
   510  				)
   511  				g.Expect(err).To(BeNil())
   512  				_, err = reconciler.reconcileDelete(ctx, cs)
   513  				g.Expect(err).ToNot(BeNil())
   514  				g.Expect(awsCluster.GetFinalizers()).To(ContainElement(infrav1.ClusterFinalizer))
   515  			})
   516  		})
   517  	})
   518  }
   519  
   520  func TestAWSClusterReconciler_RequeueAWSClusterForUnpausedCluster(t *testing.T) {
   521  	testCases := []struct {
   522  		name         string
   523  		awsCluster   *infrav1.AWSCluster
   524  		ownerCluster *clusterv1.Cluster
   525  		requeue      bool
   526  	}{
   527  		{
   528  			name: "Should create reconcile request successfully",
   529  			awsCluster: &infrav1.AWSCluster{
   530  				ObjectMeta: metav1.ObjectMeta{GenerateName: "aws-test-"}, TypeMeta: metav1.TypeMeta{Kind: "AWSCluster", APIVersion: infrav1.GroupVersion.String()},
   531  			},
   532  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test"}},
   533  			requeue:      true,
   534  		},
   535  		{
   536  			name: "Should not create reconcile request if AWSCluster is externally managed",
   537  			awsCluster: &infrav1.AWSCluster{
   538  				ObjectMeta: metav1.ObjectMeta{GenerateName: "aws-test-", Annotations: map[string]string{clusterv1.ManagedByAnnotation: "capi-test"}},
   539  				TypeMeta:   metav1.TypeMeta{Kind: "AWSCluster", APIVersion: infrav1.GroupVersion.String()},
   540  			},
   541  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test"}},
   542  			requeue:      false,
   543  		},
   544  		{
   545  			name:         "Should not create reconcile request for deleted clusters",
   546  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test", DeletionTimestamp: &metav1.Time{Time: time.Now()}}},
   547  			requeue:      false,
   548  		},
   549  		{
   550  			name:         "Should not create reconcile request if infrastructure ref for AWSCluster on owner cluster is not set",
   551  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test"}},
   552  			requeue:      false,
   553  		},
   554  		{
   555  			name: "Should not create reconcile request if infrastructure ref type on owner cluster is not AWSCluster",
   556  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test"}, Spec: clusterv1.ClusterSpec{InfrastructureRef: &corev1.ObjectReference{
   557  				APIVersion: clusterv1.GroupVersion.String(),
   558  				Kind:       "Cluster",
   559  				Name:       "aws-test"}}},
   560  			requeue: false,
   561  		},
   562  		{
   563  			name: "Should not create reconcile request if AWSCluster not found",
   564  			ownerCluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "capi-test"}, Spec: clusterv1.ClusterSpec{InfrastructureRef: &corev1.ObjectReference{
   565  				APIVersion: clusterv1.GroupVersion.String(),
   566  				Kind:       "AWSCluster",
   567  				Name:       "aws-test"}}},
   568  			requeue: false,
   569  		},
   570  	}
   571  	for _, tc := range testCases {
   572  		t.Run(tc.name, func(t *testing.T) {
   573  			g := NewWithT(t)
   574  			log := ctrl.LoggerFrom(ctx)
   575  			reconciler := &AWSClusterReconciler{
   576  				Client: testEnv.Client,
   577  			}
   578  
   579  			ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("namespace-%s", util.RandomString(5)))
   580  			g.Expect(err).To(BeNil())
   581  			createCluster(g, tc.awsCluster, ns.Name)
   582  			defer cleanupCluster(g, tc.awsCluster, ns)
   583  
   584  			if tc.ownerCluster != nil {
   585  				if tc.awsCluster != nil {
   586  					tc.ownerCluster.Spec = clusterv1.ClusterSpec{InfrastructureRef: &corev1.ObjectReference{
   587  						APIVersion: infrav1.GroupVersion.String(),
   588  						Kind:       "AWSCluster",
   589  						Name:       tc.awsCluster.Name,
   590  						Namespace:  ns.Name,
   591  					}}
   592  				}
   593  				tc.ownerCluster.Namespace = ns.Name
   594  			}
   595  			handlerFunc := reconciler.requeueAWSClusterForUnpausedCluster(ctx, log)
   596  			result := handlerFunc(tc.ownerCluster)
   597  			if tc.requeue {
   598  				g.Expect(result).To(ContainElement(reconcile.Request{
   599  					NamespacedName: types.NamespacedName{
   600  						Namespace: ns.Name,
   601  						Name:      tc.awsCluster.Name,
   602  					},
   603  				}))
   604  			} else {
   605  				g.Expect(result).To(BeNil())
   606  			}
   607  		})
   608  	}
   609  }
   610  
   611  func createCluster(g *WithT, awsCluster *infrav1.AWSCluster, namespace string) {
   612  	if awsCluster != nil {
   613  		awsCluster.Namespace = namespace
   614  		g.Expect(testEnv.Create(ctx, awsCluster)).To(Succeed())
   615  		g.Eventually(func() bool {
   616  			cluster := &infrav1.AWSCluster{}
   617  			key := client.ObjectKey{
   618  				Name:      awsCluster.Name,
   619  				Namespace: namespace,
   620  			}
   621  			err := testEnv.Get(ctx, key, cluster)
   622  			return err == nil
   623  		}, 10*time.Second).Should(Equal(true))
   624  	}
   625  }
   626  
   627  func cleanupCluster(g *WithT, awsCluster *infrav1.AWSCluster, namespace *corev1.Namespace) {
   628  	if awsCluster != nil {
   629  		func(do ...client.Object) {
   630  			g.Expect(testEnv.Cleanup(ctx, do...)).To(Succeed())
   631  		}(awsCluster, namespace)
   632  	}
   633  }
   634  
   635  func TestSecurityGroupRolesForCluster(t *testing.T) {
   636  	tests := []struct {
   637  		name           string
   638  		bastionEnabled bool
   639  		want           []infrav1.SecurityGroupRole
   640  	}{
   641  		{
   642  			name:           "Should use bastion security group when bastion is enabled",
   643  			bastionEnabled: true,
   644  			want:           append(defaultAWSSecurityGroupRoles, infrav1.SecurityGroupBastion),
   645  		},
   646  		{
   647  			name:           "Should not use bastion security group when bastion is not enabled",
   648  			bastionEnabled: false,
   649  			want:           defaultAWSSecurityGroupRoles,
   650  		},
   651  	}
   652  
   653  	for _, tt := range tests {
   654  		t.Run(tt.name, func(t *testing.T) {
   655  			g := NewWithT(t)
   656  
   657  			c := getAWSCluster("test", "test")
   658  			c.Spec.Bastion.Enabled = tt.bastionEnabled
   659  			s, err := getClusterScope(c)
   660  			g.Expect(err).To(BeNil(), "failed to create cluster scope for test")
   661  
   662  			got := securityGroupRolesForCluster(*s)
   663  			g.Expect(got).To(Equal(tt.want))
   664  		})
   665  	}
   666  }