sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controllers/awscluster_controller_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  package controllers
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/aws/aws-sdk-go/aws"
    25  	"github.com/aws/aws-sdk-go/service/ec2"
    26  	"github.com/golang/mock/gomock"
    27  	. "github.com/onsi/gomega"
    28  	"github.com/pkg/errors"
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/client-go/tools/record"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  
    34  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    35  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope"
    36  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services"
    37  	ec2Service "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/ec2"
    38  	elbService "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/elb"
    39  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/network"
    40  	"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/securitygroup"
    41  	"sigs.k8s.io/cluster-api-provider-aws/test/mocks"
    42  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    43  	"sigs.k8s.io/cluster-api/util"
    44  )
    45  
    46  func TestAWSClusterReconciler_IntegrationTests(t *testing.T) {
    47  	var (
    48  		reconciler AWSClusterReconciler
    49  		mockCtrl   *gomock.Controller
    50  		recorder   *record.FakeRecorder
    51  		ctx        context.Context
    52  	)
    53  
    54  	setup := func(t *testing.T) {
    55  		t.Helper()
    56  		mockCtrl = gomock.NewController(t)
    57  		recorder = record.NewFakeRecorder(10)
    58  		reconciler = AWSClusterReconciler{
    59  			Client:   testEnv.Client,
    60  			Recorder: recorder,
    61  		}
    62  		ctx = context.TODO()
    63  	}
    64  
    65  	teardown := func() {
    66  		mockCtrl.Finish()
    67  	}
    68  
    69  	t.Run("Should successfully reconcile AWSCluster creation with unmanaged VPC", func(t *testing.T) {
    70  		g := NewWithT(t)
    71  		mockCtrl = gomock.NewController(t)
    72  		ec2Mock := mocks.NewMockEC2API(mockCtrl)
    73  		elbMock := mocks.NewMockELBAPI(mockCtrl)
    74  		expect := func(m *mocks.MockEC2APIMockRecorder, e *mocks.MockELBAPIMockRecorder) {
    75  			mockedCreateVPCCalls(m)
    76  			mockedCreateSGCalls(m)
    77  			mockedCreateLBCalls(t, e)
    78  			mockedDescribeInstanceCall(m)
    79  		}
    80  		expect(ec2Mock.EXPECT(), elbMock.EXPECT())
    81  
    82  		setup(t)
    83  		controllerIdentity := createControllerIdentity(g)
    84  		ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("integ-test-%s", util.RandomString(5)))
    85  		g.Expect(err).To(BeNil())
    86  
    87  		awsCluster := getAWSCluster("test", ns.Name)
    88  
    89  		g.Expect(testEnv.Create(ctx, &awsCluster)).To(Succeed())
    90  		g.Eventually(func() bool {
    91  			cluster := &infrav1.AWSCluster{}
    92  			key := client.ObjectKey{
    93  				Name:      awsCluster.Name,
    94  				Namespace: ns.Name,
    95  			}
    96  			err := testEnv.Get(ctx, key, cluster)
    97  			return err == nil
    98  		}, 10*time.Second).Should(Equal(true))
    99  
   100  		defer teardown()
   101  		defer t.Cleanup(func() {
   102  			g.Expect(testEnv.Cleanup(ctx, &awsCluster, controllerIdentity, ns)).To(Succeed())
   103  		})
   104  
   105  		cs, err := getClusterScope(awsCluster)
   106  		g.Expect(err).To(BeNil())
   107  		networkSvc := network.NewService(cs)
   108  		networkSvc.EC2Client = ec2Mock
   109  		reconciler.networkServiceFactory = func(clusterScope scope.ClusterScope) services.NetworkInterface {
   110  			return networkSvc
   111  		}
   112  
   113  		ec2Svc := ec2Service.NewService(cs)
   114  		ec2Svc.EC2Client = ec2Mock
   115  		reconciler.ec2ServiceFactory = func(scope scope.EC2Scope) services.EC2Interface {
   116  			return ec2Svc
   117  		}
   118  		testSecurityGroupRoles := []infrav1.SecurityGroupRole{
   119  			infrav1.SecurityGroupBastion,
   120  			infrav1.SecurityGroupAPIServerLB,
   121  			infrav1.SecurityGroupLB,
   122  			infrav1.SecurityGroupControlPlane,
   123  			infrav1.SecurityGroupNode,
   124  		}
   125  		sgSvc := securitygroup.NewService(cs, testSecurityGroupRoles)
   126  		sgSvc.EC2Client = ec2Mock
   127  
   128  		reconciler.securityGroupFactory = func(clusterScope scope.ClusterScope) services.SecurityGroupInterface {
   129  			return sgSvc
   130  		}
   131  		elbSvc := elbService.NewService(cs)
   132  		elbSvc.EC2Client = ec2Mock
   133  		elbSvc.ELBClient = elbMock
   134  
   135  		reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   136  			return elbSvc
   137  		}
   138  		cs.SetSubnets([]infrav1.SubnetSpec{
   139  			{
   140  				ID:               "subnet-2",
   141  				AvailabilityZone: "us-east-1c",
   142  				IsPublic:         true,
   143  				CidrBlock:        "10.0.11.0/24",
   144  			},
   145  			{
   146  				ID:               "subnet-1",
   147  				AvailabilityZone: "us-east-1a",
   148  				CidrBlock:        "10.0.10.0/24",
   149  				IsPublic:         false,
   150  			},
   151  		})
   152  		_, err = reconciler.reconcileNormal(cs)
   153  		g.Expect(err).To(BeNil())
   154  		g.Expect(cs.VPC().ID).To(Equal("vpc-exists"))
   155  		expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{
   156  			{conditionType: infrav1.ClusterSecurityGroupsReadyCondition, status: corev1.ConditionTrue, severity: "", reason: ""},
   157  			{conditionType: infrav1.BastionHostReadyCondition, status: corev1.ConditionTrue, severity: "", reason: ""},
   158  			{conditionType: infrav1.VpcReadyCondition, status: corev1.ConditionTrue, severity: "", reason: ""},
   159  			{conditionType: infrav1.SubnetsReadyCondition, status: corev1.ConditionTrue, severity: "", reason: ""},
   160  		})
   161  	})
   162  	t.Run("Should fail on AWSCluster reconciliation if VPC limit exceeded", func(t *testing.T) {
   163  		// Assuming the max VPC limit is 2 and when two VPCs are created, the creation of 3rd VPC throws mocked error from EC2 API
   164  		g := NewWithT(t)
   165  		mockCtrl = gomock.NewController(t)
   166  		ec2Mock := mocks.NewMockEC2API(mockCtrl)
   167  		expect := func(m *mocks.MockEC2APIMockRecorder) {
   168  			mockedCreateMaximumVPCCalls(m)
   169  		}
   170  		expect(ec2Mock.EXPECT())
   171  
   172  		setup(t)
   173  		controllerIdentity := createControllerIdentity(g)
   174  		ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("integ-test-%s", util.RandomString(5)))
   175  		g.Expect(err).To(BeNil())
   176  		awsCluster := infrav1.AWSCluster{
   177  			ObjectMeta: metav1.ObjectMeta{
   178  				Name:      "test",
   179  				Namespace: ns.Name,
   180  			},
   181  			Spec: infrav1.AWSClusterSpec{
   182  				Region: "us-east-1",
   183  			},
   184  		}
   185  		g.Expect(testEnv.Create(ctx, &awsCluster)).To(Succeed())
   186  
   187  		defer teardown()
   188  		g.Eventually(func() bool {
   189  			cluster := &infrav1.AWSCluster{}
   190  			key := client.ObjectKey{
   191  				Name:      awsCluster.Name,
   192  				Namespace: ns.Name,
   193  			}
   194  			err := testEnv.Get(ctx, key, cluster)
   195  			return err == nil
   196  		}, 10*time.Second).Should(Equal(true))
   197  		defer t.Cleanup(func() {
   198  			g.Expect(testEnv.Cleanup(ctx, &awsCluster, controllerIdentity, ns)).To(Succeed())
   199  		})
   200  		cs, err := getClusterScope(awsCluster)
   201  		g.Expect(err).To(BeNil())
   202  		s := network.NewService(cs)
   203  		s.EC2Client = ec2Mock
   204  
   205  		reconciler.networkServiceFactory = func(clusterScope scope.ClusterScope) services.NetworkInterface {
   206  			return s
   207  		}
   208  		_, err = reconciler.reconcileNormal(cs)
   209  		g.Expect(err.Error()).To(ContainSubstring("The maximum number of VPCs has been reached"))
   210  	})
   211  	t.Run("Should successfully delete AWSCluster with managed VPC", func(t *testing.T) {
   212  		g := NewWithT(t)
   213  
   214  		mockCtrl = gomock.NewController(t)
   215  		ec2Mock := mocks.NewMockEC2API(mockCtrl)
   216  		elbMock := mocks.NewMockELBAPI(mockCtrl)
   217  		expect := func(m *mocks.MockEC2APIMockRecorder, e *mocks.MockELBAPIMockRecorder) {
   218  			mockedDeleteVPCCalls(m)
   219  			mockedDescribeInstanceCall(m)
   220  			mockedDeleteLBCalls(e)
   221  			mockedDeleteInstanceCalls(m)
   222  			mockedDeleteSGCalls(m)
   223  		}
   224  		expect(ec2Mock.EXPECT(), elbMock.EXPECT())
   225  
   226  		setup(t)
   227  		controllerIdentity := createControllerIdentity(g)
   228  		ns, err := testEnv.CreateNamespace(ctx, fmt.Sprintf("integ-test-%s", util.RandomString(5)))
   229  		g.Expect(err).To(BeNil())
   230  		awsCluster := getAWSCluster("test", ns.Name)
   231  
   232  		g.Expect(testEnv.Create(ctx, &awsCluster)).To(Succeed())
   233  		defer teardown()
   234  		g.Eventually(func() bool {
   235  			cluster := &infrav1.AWSCluster{}
   236  			key := client.ObjectKey{
   237  				Name:      awsCluster.Name,
   238  				Namespace: ns.Name,
   239  			}
   240  			err := testEnv.Get(ctx, key, cluster)
   241  			return err == nil
   242  		}, 10*time.Second).Should(Equal(true))
   243  
   244  		defer t.Cleanup(func() {
   245  			g.Expect(testEnv.Cleanup(ctx, &awsCluster, controllerIdentity, ns)).To(Succeed())
   246  		})
   247  
   248  		cs, err := getClusterScope(awsCluster)
   249  		g.Expect(err).To(BeNil())
   250  
   251  		networkSvc := network.NewService(cs)
   252  		networkSvc.EC2Client = ec2Mock
   253  		reconciler.networkServiceFactory = func(clusterScope scope.ClusterScope) services.NetworkInterface {
   254  			return networkSvc
   255  		}
   256  
   257  		ec2Svc := ec2Service.NewService(cs)
   258  		ec2Svc.EC2Client = ec2Mock
   259  		reconciler.ec2ServiceFactory = func(ec2Scope scope.EC2Scope) services.EC2Interface {
   260  			return ec2Svc
   261  		}
   262  
   263  		elbSvc := elbService.NewService(cs)
   264  		elbSvc.EC2Client = ec2Mock
   265  		elbSvc.ELBClient = elbMock
   266  		reconciler.elbServiceFactory = func(elbScope scope.ELBScope) services.ELBInterface {
   267  			return elbSvc
   268  		}
   269  
   270  		testSecurityGroupRoles := []infrav1.SecurityGroupRole{
   271  			infrav1.SecurityGroupBastion,
   272  			infrav1.SecurityGroupAPIServerLB,
   273  			infrav1.SecurityGroupLB,
   274  			infrav1.SecurityGroupControlPlane,
   275  			infrav1.SecurityGroupNode,
   276  		}
   277  		sgSvc := securitygroup.NewService(cs, testSecurityGroupRoles)
   278  		sgSvc.EC2Client = ec2Mock
   279  		reconciler.securityGroupFactory = func(clusterScope scope.ClusterScope) services.SecurityGroupInterface {
   280  			return sgSvc
   281  		}
   282  
   283  		_, err = reconciler.reconcileDelete(ctx, cs)
   284  		g.Expect(err).To(BeNil())
   285  		expectAWSClusterConditions(g, cs.AWSCluster, []conditionAssertion{{infrav1.LoadBalancerReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   286  			{infrav1.BastionHostReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   287  			{infrav1.SecondaryCidrsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletingReason},
   288  			{infrav1.RouteTablesReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   289  			{infrav1.NatGatewaysReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   290  			{infrav1.InternetGatewayReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   291  			{infrav1.SubnetsReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   292  			{infrav1.VpcReadyCondition, corev1.ConditionFalse, clusterv1.ConditionSeverityInfo, clusterv1.DeletedReason},
   293  		})
   294  	})
   295  }
   296  
   297  func mockedDeleteSGCalls(m *mocks.MockEC2APIMockRecorder) {
   298  	m.DescribeSecurityGroupsPages(gomock.Any(), gomock.Any()).Return(nil)
   299  }
   300  
   301  func createControllerIdentity(g *WithT) *infrav1.AWSClusterControllerIdentity {
   302  	controllerIdentity := &infrav1.AWSClusterControllerIdentity{
   303  		TypeMeta: metav1.TypeMeta{
   304  			Kind: string(infrav1.ControllerIdentityKind),
   305  		},
   306  		ObjectMeta: metav1.ObjectMeta{
   307  			Name: "default",
   308  		},
   309  		Spec: infrav1.AWSClusterControllerIdentitySpec{
   310  			AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{
   311  				AllowedNamespaces: &infrav1.AllowedNamespaces{},
   312  			},
   313  		},
   314  	}
   315  	g.Expect(testEnv.Create(ctx, controllerIdentity)).To(Succeed())
   316  	return controllerIdentity
   317  }
   318  
   319  func mockedDescribeInstanceCall(m *mocks.MockEC2APIMockRecorder) {
   320  	m.DescribeInstances(gomock.Eq(&ec2.DescribeInstancesInput{
   321  		Filters: []*ec2.Filter{
   322  			{
   323  				Name:   aws.String("tag:sigs.k8s.io/cluster-api-provider-aws/role"),
   324  				Values: aws.StringSlice([]string{"bastion"}),
   325  			},
   326  			{
   327  				Name:   aws.String("tag-key"),
   328  				Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
   329  			},
   330  			{
   331  				Name:   aws.String("instance-state-name"),
   332  				Values: aws.StringSlice([]string{"pending", "running", "stopping", "stopped"}),
   333  			},
   334  		},
   335  	})).Return(&ec2.DescribeInstancesOutput{
   336  		Reservations: []*ec2.Reservation{
   337  			{
   338  				Instances: []*ec2.Instance{
   339  					{
   340  						InstanceId:   aws.String("id-1"),
   341  						InstanceType: aws.String("m5.large"),
   342  						SubnetId:     aws.String("subnet-1"),
   343  						ImageId:      aws.String("ami-1"),
   344  						IamInstanceProfile: &ec2.IamInstanceProfile{
   345  							Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
   346  						},
   347  						State: &ec2.InstanceState{
   348  							Code: aws.Int64(16),
   349  							Name: aws.String(ec2.StateAvailable),
   350  						},
   351  						RootDeviceName: aws.String("device-1"),
   352  						BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
   353  							{
   354  								DeviceName: aws.String("device-1"),
   355  								Ebs: &ec2.EbsInstanceBlockDevice{
   356  									VolumeId: aws.String("volume-1"),
   357  								},
   358  							},
   359  						},
   360  						Placement: &ec2.Placement{
   361  							AvailabilityZone: aws.String("us-east-1a"),
   362  						},
   363  					},
   364  				},
   365  			},
   366  		},
   367  	}, nil)
   368  }
   369  
   370  func mockedDeleteInstanceCalls(m *mocks.MockEC2APIMockRecorder) {
   371  	m.TerminateInstances(
   372  		gomock.Eq(&ec2.TerminateInstancesInput{
   373  			InstanceIds: aws.StringSlice([]string{"id-1"}),
   374  		}),
   375  	).
   376  		Return(nil, nil)
   377  	m.WaitUntilInstanceTerminated(
   378  		gomock.Eq(&ec2.DescribeInstancesInput{
   379  			InstanceIds: aws.StringSlice([]string{"id-1"}),
   380  		}),
   381  	).
   382  		Return(nil)
   383  }
   384  
   385  func mockedCreateVPCCalls(m *mocks.MockEC2APIMockRecorder) {
   386  	m.CreateTags(gomock.Eq(&ec2.CreateTagsInput{
   387  		Resources: aws.StringSlice([]string{"subnet-1"}),
   388  		Tags: []*ec2.Tag{
   389  			{
   390  				Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   391  				Value: aws.String("shared"),
   392  			},
   393  			{
   394  				Key:   aws.String("kubernetes.io/role/internal-elb"),
   395  				Value: aws.String("1"),
   396  			},
   397  		},
   398  	})).Return(&ec2.CreateTagsOutput{}, nil)
   399  	m.CreateTags(gomock.Eq(&ec2.CreateTagsInput{
   400  		Resources: aws.StringSlice([]string{"subnet-2"}),
   401  		Tags: []*ec2.Tag{
   402  			{
   403  				Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   404  				Value: aws.String("shared"),
   405  			},
   406  			{
   407  				Key:   aws.String("kubernetes.io/role/elb"),
   408  				Value: aws.String("1"),
   409  			},
   410  		},
   411  	})).Return(&ec2.CreateTagsOutput{}, nil).AnyTimes()
   412  	m.CreateTags(gomock.Eq(&ec2.CreateTagsInput{
   413  		Resources: aws.StringSlice([]string{"subnet-2"}),
   414  		Tags: []*ec2.Tag{
   415  			{
   416  				Key:   aws.String("Name"),
   417  				Value: aws.String("test-cluster-subnet-public-us-east-1c"),
   418  			},
   419  			{
   420  				Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   421  				Value: aws.String("shared"),
   422  			},
   423  			{
   424  				Key:   aws.String("kubernetes.io/role/internal-elb"),
   425  				Value: aws.String("1"),
   426  			},
   427  			{
   428  				Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   429  				Value: aws.String("owned"),
   430  			},
   431  			{
   432  				Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   433  				Value: aws.String("public"),
   434  			},
   435  		},
   436  	})).Return(&ec2.CreateTagsOutput{}, nil).AnyTimes()
   437  	m.CreateSubnet(gomock.Eq(&ec2.CreateSubnetInput{
   438  		VpcId:            aws.String("vpc-exists"),
   439  		CidrBlock:        aws.String("10.0.11.0/24"),
   440  		AvailabilityZone: aws.String("us-east-1c"),
   441  		TagSpecifications: []*ec2.TagSpecification{
   442  			{
   443  				ResourceType: aws.String("subnet"),
   444  				Tags: []*ec2.Tag{
   445  					{
   446  						Key:   aws.String("Name"),
   447  						Value: aws.String("test-cluster-subnet-public-us-east-1c"),
   448  					},
   449  					{
   450  						Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   451  						Value: aws.String("shared"),
   452  					},
   453  					{
   454  						Key:   aws.String("kubernetes.io/role/internal-elb"),
   455  						Value: aws.String("1"),
   456  					},
   457  					{
   458  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   459  						Value: aws.String("owned"),
   460  					},
   461  					{
   462  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   463  						Value: aws.String("public"),
   464  					},
   465  				},
   466  			},
   467  		},
   468  	})).Return(&ec2.CreateSubnetOutput{
   469  		Subnet: &ec2.Subnet{
   470  			VpcId:               aws.String("vpc-exists"),
   471  			SubnetId:            aws.String("subnet-2"),
   472  			CidrBlock:           aws.String("10.0.11.0/24"),
   473  			AvailabilityZone:    aws.String("us-east-1c"),
   474  			MapPublicIpOnLaunch: aws.Bool(false),
   475  			Tags: []*ec2.Tag{
   476  				{
   477  					Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   478  					Value: aws.String("owned"),
   479  				},
   480  				{
   481  					Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   482  					Value: aws.String("public"),
   483  				},
   484  				{
   485  					Key:   aws.String("Name"),
   486  					Value: aws.String("test-cluster-subnet-public"),
   487  				},
   488  				{
   489  					Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   490  					Value: aws.String("shared"),
   491  				},
   492  			},
   493  		},
   494  	}, nil).AnyTimes()
   495  	m.DescribeSubnets(gomock.Eq(&ec2.DescribeSubnetsInput{
   496  		Filters: []*ec2.Filter{
   497  			{
   498  				Name:   aws.String("state"),
   499  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   500  			},
   501  			{
   502  				Name:   aws.String("vpc-id"),
   503  				Values: aws.StringSlice([]string{"vpc-exists"}),
   504  			},
   505  		}})).Return(&ec2.DescribeSubnetsOutput{
   506  		Subnets: []*ec2.Subnet{
   507  			{
   508  				VpcId:               aws.String("vpc-exists"),
   509  				SubnetId:            aws.String("subnet-1"),
   510  				AvailabilityZone:    aws.String("us-east-1a"),
   511  				CidrBlock:           aws.String("10.0.10.0/24"),
   512  				MapPublicIpOnLaunch: aws.Bool(false),
   513  			},
   514  			{
   515  				VpcId:               aws.String("vpc-exists"),
   516  				SubnetId:            aws.String("subnet-2"),
   517  				AvailabilityZone:    aws.String("us-east-1c"),
   518  				CidrBlock:           aws.String("10.0.11.0/24"),
   519  				MapPublicIpOnLaunch: aws.Bool(false),
   520  				Tags: []*ec2.Tag{
   521  					{
   522  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   523  						Value: aws.String("owned"),
   524  					},
   525  					{
   526  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   527  						Value: aws.String("public"),
   528  					},
   529  					{
   530  						Key:   aws.String("Name"),
   531  						Value: aws.String("test-cluster-subnet-public"),
   532  					},
   533  					{
   534  						Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   535  						Value: aws.String("shared"),
   536  					},
   537  				},
   538  			},
   539  		},
   540  	}, nil)
   541  	m.DescribeRouteTables(gomock.Eq(&ec2.DescribeRouteTablesInput{
   542  		Filters: []*ec2.Filter{
   543  			{
   544  				Name:   aws.String("vpc-id"),
   545  				Values: aws.StringSlice([]string{"vpc-exists"}),
   546  			},
   547  		}})).Return(&ec2.DescribeRouteTablesOutput{
   548  		RouteTables: []*ec2.RouteTable{
   549  			{
   550  				Routes: []*ec2.Route{
   551  					{
   552  						GatewayId: aws.String("igw-12345"),
   553  					},
   554  				},
   555  			},
   556  		},
   557  	}, nil)
   558  	m.DescribeNatGatewaysPages(gomock.Eq(&ec2.DescribeNatGatewaysInput{
   559  		Filter: []*ec2.Filter{
   560  			{
   561  				Name:   aws.String("vpc-id"),
   562  				Values: []*string{aws.String("vpc-exists")},
   563  			},
   564  			{
   565  				Name:   aws.String("state"),
   566  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   567  			},
   568  		}}), gomock.Any()).Return(nil)
   569  	m.DescribeVpcs(gomock.Eq(&ec2.DescribeVpcsInput{
   570  		VpcIds: []*string{
   571  			aws.String("vpc-exists"),
   572  		},
   573  		Filters: []*ec2.Filter{
   574  			{
   575  				Name:   aws.String("state"),
   576  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   577  			},
   578  		},
   579  	})).
   580  		Return(&ec2.DescribeVpcsOutput{
   581  			Vpcs: []*ec2.Vpc{
   582  				{
   583  					State:     aws.String("available"),
   584  					VpcId:     aws.String("vpc-exists"),
   585  					CidrBlock: aws.String("10.0.0.0/8"),
   586  					Tags: []*ec2.Tag{
   587  						{
   588  							Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   589  							Value: aws.String("common"),
   590  						},
   591  						{
   592  							Key:   aws.String("Name"),
   593  							Value: aws.String("test-cluster"),
   594  						},
   595  					},
   596  				},
   597  			},
   598  		}, nil)
   599  }
   600  
   601  func mockedCreateMaximumVPCCalls(m *mocks.MockEC2APIMockRecorder) {
   602  	m.CreateVpc(gomock.AssignableToTypeOf(&ec2.CreateVpcInput{})).Return(nil, errors.New("The maximum number of VPCs has been reached"))
   603  }
   604  
   605  func mockedDeleteVPCCalls(m *mocks.MockEC2APIMockRecorder) {
   606  	m.DescribeSubnets(gomock.Eq(&ec2.DescribeSubnetsInput{
   607  		Filters: []*ec2.Filter{
   608  			{
   609  				Name:   aws.String("state"),
   610  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   611  			},
   612  			{
   613  				Name:   aws.String("vpc-id"),
   614  				Values: aws.StringSlice([]string{"vpc-exists"}),
   615  			},
   616  		}})).Return(&ec2.DescribeSubnetsOutput{
   617  		Subnets: []*ec2.Subnet{
   618  			{
   619  				VpcId:               aws.String("vpc-exists"),
   620  				SubnetId:            aws.String("subnet-1"),
   621  				AvailabilityZone:    aws.String("us-east-1a"),
   622  				CidrBlock:           aws.String("10.0.10.0/24"),
   623  				MapPublicIpOnLaunch: aws.Bool(false),
   624  			},
   625  		},
   626  	}, nil).AnyTimes()
   627  	m.DescribeRouteTables(gomock.Eq(&ec2.DescribeRouteTablesInput{
   628  		Filters: []*ec2.Filter{
   629  			{
   630  				Name:   aws.String("vpc-id"),
   631  				Values: aws.StringSlice([]string{"vpc-exists"}),
   632  			},
   633  			{
   634  				Name:   aws.String("tag-key"),
   635  				Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
   636  			},
   637  		}})).Return(&ec2.DescribeRouteTablesOutput{
   638  		RouteTables: []*ec2.RouteTable{
   639  			{
   640  				Routes: []*ec2.Route{
   641  					{
   642  						GatewayId: aws.String("igw-12345"),
   643  					},
   644  				},
   645  				RouteTableId: aws.String("rt-12345"),
   646  			},
   647  		},
   648  	}, nil).AnyTimes()
   649  	m.DeleteRouteTable(gomock.Eq(&ec2.DeleteRouteTableInput{
   650  		RouteTableId: aws.String("rt-12345"),
   651  	}))
   652  	m.DescribeInternetGateways(gomock.Eq(&ec2.DescribeInternetGatewaysInput{
   653  		Filters: []*ec2.Filter{
   654  			{
   655  				Name:   aws.String("attachment.vpc-id"),
   656  				Values: aws.StringSlice([]string{"vpc-exists"}),
   657  			},
   658  		},
   659  	})).Return(&ec2.DescribeInternetGatewaysOutput{
   660  		InternetGateways: []*ec2.InternetGateway{
   661  			{
   662  				Attachments:       nil,
   663  				InternetGatewayId: aws.String("ig-12345"),
   664  			},
   665  		},
   666  	}, nil)
   667  	m.DetachInternetGateway(gomock.Eq(&ec2.DetachInternetGatewayInput{
   668  		VpcId:             aws.String("vpc-exists"),
   669  		InternetGatewayId: aws.String("ig-12345"),
   670  	}))
   671  	m.DeleteInternetGateway(gomock.Eq(&ec2.DeleteInternetGatewayInput{
   672  		InternetGatewayId: aws.String("ig-12345"),
   673  	}))
   674  	m.DescribeNatGatewaysPages(gomock.Eq(&ec2.DescribeNatGatewaysInput{
   675  		Filter: []*ec2.Filter{
   676  			{
   677  				Name:   aws.String("vpc-id"),
   678  				Values: []*string{aws.String("vpc-exists")},
   679  			},
   680  			{
   681  				Name:   aws.String("state"),
   682  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   683  			},
   684  		}}), gomock.Any()).Return(nil).AnyTimes()
   685  	m.DescribeAddresses(gomock.Eq(&ec2.DescribeAddressesInput{
   686  		Filters: []*ec2.Filter{
   687  			{
   688  				Name:   aws.String("tag-key"),
   689  				Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
   690  			}},
   691  	})).Return(&ec2.DescribeAddressesOutput{
   692  		Addresses: []*ec2.Address{
   693  			{
   694  				AssociationId: aws.String("1234"),
   695  				AllocationId:  aws.String("1234"),
   696  				PublicIp:      aws.String("1.2.3.4"),
   697  			},
   698  		},
   699  	}, nil)
   700  	m.DisassociateAddress(&ec2.DisassociateAddressInput{
   701  		AssociationId: aws.String("1234"),
   702  	})
   703  	m.ReleaseAddress(&ec2.ReleaseAddressInput{
   704  		AllocationId: aws.String("1234"),
   705  	})
   706  	m.DescribeVpcs(gomock.Eq(&ec2.DescribeVpcsInput{
   707  		VpcIds: []*string{
   708  			aws.String("vpc-exists"),
   709  		},
   710  		Filters: []*ec2.Filter{
   711  			{
   712  				Name:   aws.String("state"),
   713  				Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
   714  			},
   715  		},
   716  	})).
   717  		Return(&ec2.DescribeVpcsOutput{
   718  			Vpcs: []*ec2.Vpc{
   719  				{
   720  					State:     aws.String("available"),
   721  					VpcId:     aws.String("vpc-exists"),
   722  					CidrBlock: aws.String("10.0.0.0/8"),
   723  					Tags: []*ec2.Tag{
   724  						{
   725  							Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   726  							Value: aws.String("common"),
   727  						},
   728  						{
   729  							Key:   aws.String("Name"),
   730  							Value: aws.String("test-cluster"),
   731  						},
   732  						{
   733  							Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   734  							Value: aws.String("owned"),
   735  						},
   736  					},
   737  				},
   738  			},
   739  		}, nil)
   740  	m.DeleteSubnet(gomock.Eq(&ec2.DeleteSubnetInput{
   741  		SubnetId: aws.String("subnet-1"),
   742  	}))
   743  	m.DeleteVpc(gomock.Eq(&ec2.DeleteVpcInput{
   744  		VpcId: aws.String("vpc-exists"),
   745  	}))
   746  }
   747  
   748  func mockedCreateSGCalls(m *mocks.MockEC2APIMockRecorder) {
   749  	m.DescribeSecurityGroups(gomock.Eq(&ec2.DescribeSecurityGroupsInput{
   750  		Filters: []*ec2.Filter{
   751  			{
   752  				Name:   aws.String("vpc-id"),
   753  				Values: aws.StringSlice([]string{"vpc-exists"}),
   754  			},
   755  			{
   756  				Name:   aws.String("tag-key"),
   757  				Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
   758  			},
   759  		},
   760  	})).Return(
   761  		&ec2.DescribeSecurityGroupsOutput{
   762  			SecurityGroups: []*ec2.SecurityGroup{
   763  				{
   764  					GroupId:   aws.String("1"),
   765  					GroupName: aws.String("test-sg"),
   766  				},
   767  			},
   768  		}, nil)
   769  	m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{
   770  		VpcId:       aws.String("vpc-exists"),
   771  		GroupName:   aws.String("test-cluster-bastion"),
   772  		Description: aws.String("Kubernetes cluster test-cluster: bastion"),
   773  		TagSpecifications: []*ec2.TagSpecification{
   774  			{
   775  				ResourceType: aws.String("security-group"),
   776  				Tags: []*ec2.Tag{
   777  					{
   778  						Key:   aws.String("Name"),
   779  						Value: aws.String("test-cluster-bastion"),
   780  					},
   781  					{
   782  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   783  						Value: aws.String("owned"),
   784  					},
   785  					{
   786  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   787  						Value: aws.String("bastion"),
   788  					},
   789  				},
   790  			},
   791  		},
   792  	})).
   793  		Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-bastion")}, nil)
   794  	m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{
   795  		VpcId:       aws.String("vpc-exists"),
   796  		GroupName:   aws.String("test-cluster-apiserver-lb"),
   797  		Description: aws.String("Kubernetes cluster test-cluster: apiserver-lb"),
   798  		TagSpecifications: []*ec2.TagSpecification{
   799  			{
   800  				ResourceType: aws.String("security-group"),
   801  				Tags: []*ec2.Tag{
   802  					{
   803  						Key:   aws.String("Name"),
   804  						Value: aws.String("test-cluster-apiserver-lb"),
   805  					},
   806  					{
   807  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   808  						Value: aws.String("owned"),
   809  					},
   810  					{
   811  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   812  						Value: aws.String("apiserver-lb"),
   813  					},
   814  				},
   815  			},
   816  		},
   817  	})).
   818  		Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-apiserver-lb")}, nil)
   819  	m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{
   820  		VpcId:       aws.String("vpc-exists"),
   821  		GroupName:   aws.String("test-cluster-lb"),
   822  		Description: aws.String("Kubernetes cluster test-cluster: lb"),
   823  		TagSpecifications: []*ec2.TagSpecification{
   824  			{
   825  				ResourceType: aws.String("security-group"),
   826  				Tags: []*ec2.Tag{
   827  					{
   828  						Key:   aws.String("Name"),
   829  						Value: aws.String("test-cluster-lb"),
   830  					},
   831  					{
   832  						Key:   aws.String("kubernetes.io/cluster/test-cluster"),
   833  						Value: aws.String("owned"),
   834  					},
   835  					{
   836  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   837  						Value: aws.String("owned"),
   838  					},
   839  					{
   840  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   841  						Value: aws.String("lb"),
   842  					},
   843  				},
   844  			},
   845  		},
   846  	})).
   847  		Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-lb")}, nil)
   848  	securityGroupControl := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{
   849  		VpcId:       aws.String("vpc-exists"),
   850  		GroupName:   aws.String("test-cluster-controlplane"),
   851  		Description: aws.String("Kubernetes cluster test-cluster: controlplane"),
   852  		TagSpecifications: []*ec2.TagSpecification{
   853  			{
   854  				ResourceType: aws.String("security-group"),
   855  				Tags: []*ec2.Tag{
   856  					{
   857  						Key:   aws.String("Name"),
   858  						Value: aws.String("test-cluster-controlplane"),
   859  					},
   860  					{
   861  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   862  						Value: aws.String("owned"),
   863  					},
   864  					{
   865  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   866  						Value: aws.String("controlplane"),
   867  					},
   868  				},
   869  			},
   870  		},
   871  	})).
   872  		Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-controlplane")}, nil)
   873  	securityGroupNode := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{
   874  		VpcId:       aws.String("vpc-exists"),
   875  		GroupName:   aws.String("test-cluster-node"),
   876  		Description: aws.String("Kubernetes cluster test-cluster: node"),
   877  		TagSpecifications: []*ec2.TagSpecification{
   878  			{
   879  				ResourceType: aws.String("security-group"),
   880  				Tags: []*ec2.Tag{
   881  					{
   882  						Key:   aws.String("Name"),
   883  						Value: aws.String("test-cluster-node"),
   884  					},
   885  					{
   886  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"),
   887  						Value: aws.String("owned"),
   888  					},
   889  					{
   890  						Key:   aws.String("sigs.k8s.io/cluster-api-provider-aws/role"),
   891  						Value: aws.String("node"),
   892  					},
   893  				},
   894  			},
   895  		},
   896  	})).
   897  		Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-node")}, nil)
   898  	m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{
   899  		GroupId: aws.String("sg-controlplane"),
   900  	})).
   901  		Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil).
   902  		After(securityGroupControl).Times(2)
   903  	m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{
   904  		GroupId: aws.String("sg-node"),
   905  	})).
   906  		Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil).
   907  		After(securityGroupNode).Times(2)
   908  }