sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/network/natgateways_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package network 18 19 import ( 20 "context" 21 "testing" 22 23 "github.com/aws/aws-sdk-go/aws" 24 "github.com/aws/aws-sdk-go/service/ec2" 25 "github.com/golang/mock/gomock" 26 . "github.com/onsi/gomega" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "sigs.k8s.io/controller-runtime/pkg/client/fake" 30 31 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 32 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors" 33 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope" 34 "sigs.k8s.io/cluster-api-provider-aws/test/mocks" 35 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 36 ) 37 38 const ( 39 ElasticIPAllocationID = "elastic-ip-allocation-id" 40 ) 41 42 func TestReconcileNatGateways(t *testing.T) { 43 mockCtrl := gomock.NewController(t) 44 defer mockCtrl.Finish() 45 46 testCases := []struct { 47 name string 48 input []infrav1.SubnetSpec 49 expect func(m *mocks.MockEC2APIMockRecorder) 50 }{ 51 { 52 name: "single private subnet exists, should create no NAT gateway", 53 input: []infrav1.SubnetSpec{ 54 { 55 ID: "subnet-1", 56 AvailabilityZone: "us-east-1a", 57 CidrBlock: "10.0.10.0/24", 58 IsPublic: false, 59 }, 60 }, 61 expect: func(m *mocks.MockEC2APIMockRecorder) { 62 m.CreateNatGateway(gomock.Any()).Times(0) 63 }, 64 }, 65 { 66 name: "no private subnet exists, should create no NAT gateway", 67 input: []infrav1.SubnetSpec{ 68 { 69 ID: "subnet-1", 70 AvailabilityZone: "us-east-1a", 71 CidrBlock: "10.0.10.0/24", 72 IsPublic: true, 73 }, 74 }, 75 expect: func(m *mocks.MockEC2APIMockRecorder) { 76 m.DescribeNatGatewaysPages(gomock.Any(), gomock.Any()).Times(0) 77 m.CreateNatGateway(gomock.Any()).Times(0) 78 }, 79 }, 80 { 81 name: "public & private subnet exists, should create 1 NAT gateway", 82 input: []infrav1.SubnetSpec{ 83 { 84 ID: "subnet-1", 85 AvailabilityZone: "us-east-1a", 86 CidrBlock: "10.0.10.0/24", 87 IsPublic: true, 88 }, 89 { 90 ID: "subnet-2", 91 AvailabilityZone: "us-east-1a", 92 CidrBlock: "10.0.12.0/24", 93 IsPublic: false, 94 }, 95 }, 96 expect: func(m *mocks.MockEC2APIMockRecorder) { 97 m.DescribeNatGatewaysPages( 98 gomock.Eq(&ec2.DescribeNatGatewaysInput{ 99 Filter: []*ec2.Filter{ 100 { 101 Name: aws.String("vpc-id"), 102 Values: []*string{aws.String(subnetsVPCID)}, 103 }, 104 { 105 Name: aws.String("state"), 106 Values: []*string{aws.String("pending"), aws.String("available")}, 107 }, 108 }, 109 }), 110 gomock.Any()).Return(nil) 111 112 m.DescribeAddresses(gomock.Any()). 113 Return(&ec2.DescribeAddressesOutput{}, nil) 114 115 m.AllocateAddress(&ec2.AllocateAddressInput{ 116 Domain: aws.String("vpc"), 117 TagSpecifications: []*ec2.TagSpecification{ 118 { 119 ResourceType: aws.String("elastic-ip"), 120 Tags: []*ec2.Tag{ 121 { 122 Key: aws.String("Name"), 123 Value: aws.String("test-cluster-eip-apiserver"), 124 }, 125 { 126 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 127 Value: aws.String("owned"), 128 }, 129 { 130 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 131 Value: aws.String("apiserver"), 132 }, 133 }, 134 }, 135 }, 136 }).Return(&ec2.AllocateAddressOutput{ 137 AllocationId: aws.String(ElasticIPAllocationID), 138 }, nil) 139 140 m.CreateNatGateway(&ec2.CreateNatGatewayInput{ 141 AllocationId: aws.String(ElasticIPAllocationID), 142 SubnetId: aws.String("subnet-1"), 143 TagSpecifications: []*ec2.TagSpecification{ 144 { 145 ResourceType: aws.String("natgateway"), 146 Tags: []*ec2.Tag{ 147 { 148 Key: aws.String("Name"), 149 Value: aws.String("test-cluster-nat"), 150 }, 151 { 152 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 153 Value: aws.String("owned"), 154 }, 155 { 156 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 157 Value: aws.String("common"), 158 }, 159 }, 160 }, 161 }, 162 }, 163 ).Return(&ec2.CreateNatGatewayOutput{ 164 NatGateway: &ec2.NatGateway{ 165 NatGatewayId: aws.String("natgateway"), 166 SubnetId: aws.String("subnet-1"), 167 }, 168 }, nil) 169 170 m.WaitUntilNatGatewayAvailable(&ec2.DescribeNatGatewaysInput{ 171 NatGatewayIds: []*string{aws.String("natgateway")}, 172 }).Return(nil) 173 }, 174 }, 175 { 176 name: "two public & 1 private subnet, and one NAT gateway exists", 177 input: []infrav1.SubnetSpec{ 178 { 179 ID: "subnet-1", 180 AvailabilityZone: "us-east-1a", 181 CidrBlock: "10.0.10.0/24", 182 IsPublic: true, 183 }, 184 { 185 ID: "subnet-2", 186 AvailabilityZone: "us-east-1a", 187 CidrBlock: "10.0.12.0/24", 188 IsPublic: false, 189 }, 190 { 191 ID: "subnet-3", 192 AvailabilityZone: "us-east-1b", 193 CidrBlock: "10.0.13.0/24", 194 IsPublic: true, 195 }, 196 }, 197 expect: func(m *mocks.MockEC2APIMockRecorder) { 198 m.DescribeNatGatewaysPages( 199 gomock.Eq(&ec2.DescribeNatGatewaysInput{ 200 Filter: []*ec2.Filter{ 201 { 202 Name: aws.String("vpc-id"), 203 Values: []*string{aws.String(subnetsVPCID)}, 204 }, 205 { 206 Name: aws.String("state"), 207 Values: []*string{aws.String("pending"), aws.String("available")}, 208 }, 209 }, 210 }), 211 gomock.Any()).Do(func(_, y interface{}) { 212 funct := y.(func(page *ec2.DescribeNatGatewaysOutput, lastPage bool) bool) 213 funct(&ec2.DescribeNatGatewaysOutput{NatGateways: []*ec2.NatGateway{{ 214 NatGatewayId: aws.String("gateway"), 215 SubnetId: aws.String("subnet-1"), 216 }}}, true) 217 }).Return(nil) 218 219 m.DescribeAddresses(gomock.Any()). 220 Return(&ec2.DescribeAddressesOutput{}, nil) 221 222 m.AllocateAddress(&ec2.AllocateAddressInput{ 223 Domain: aws.String("vpc"), 224 TagSpecifications: []*ec2.TagSpecification{ 225 { 226 ResourceType: aws.String("elastic-ip"), 227 Tags: []*ec2.Tag{ 228 { 229 Key: aws.String("Name"), 230 Value: aws.String("test-cluster-eip-apiserver"), 231 }, 232 { 233 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 234 Value: aws.String("owned"), 235 }, 236 { 237 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 238 Value: aws.String("apiserver"), 239 }, 240 }, 241 }, 242 }, 243 }).Return(&ec2.AllocateAddressOutput{ 244 AllocationId: aws.String(ElasticIPAllocationID), 245 }, nil) 246 247 m.CreateNatGateway(&ec2.CreateNatGatewayInput{ 248 AllocationId: aws.String(ElasticIPAllocationID), 249 SubnetId: aws.String("subnet-3"), 250 TagSpecifications: []*ec2.TagSpecification{ 251 { 252 ResourceType: aws.String("natgateway"), 253 Tags: []*ec2.Tag{ 254 { 255 Key: aws.String("Name"), 256 Value: aws.String("test-cluster-nat"), 257 }, 258 { 259 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 260 Value: aws.String("owned"), 261 }, 262 { 263 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 264 Value: aws.String("common"), 265 }, 266 }, 267 }, 268 }, 269 }).Return(&ec2.CreateNatGatewayOutput{ 270 NatGateway: &ec2.NatGateway{ 271 NatGatewayId: aws.String("natgateway"), 272 SubnetId: aws.String("subnet-3"), 273 }, 274 }, nil) 275 276 m.WaitUntilNatGatewayAvailable(&ec2.DescribeNatGatewaysInput{ 277 NatGatewayIds: []*string{aws.String("natgateway")}, 278 }).Return(nil) 279 280 m.CreateTags(gomock.AssignableToTypeOf(&ec2.CreateTagsInput{})). 281 Return(nil, nil).Times(1) 282 }, 283 }, 284 { 285 name: "public & private subnet, and one NAT gateway exists", 286 input: []infrav1.SubnetSpec{ 287 { 288 ID: "subnet-1", 289 AvailabilityZone: "us-east-1a", 290 CidrBlock: "10.0.10.0/24", 291 IsPublic: true, 292 }, 293 { 294 ID: "subnet-2", 295 AvailabilityZone: "us-east-1a", 296 CidrBlock: "10.0.12.0/24", 297 IsPublic: false, 298 }, 299 }, 300 expect: func(m *mocks.MockEC2APIMockRecorder) { 301 m.DescribeNatGatewaysPages( 302 gomock.Eq(&ec2.DescribeNatGatewaysInput{ 303 Filter: []*ec2.Filter{ 304 { 305 Name: aws.String("vpc-id"), 306 Values: []*string{aws.String(subnetsVPCID)}, 307 }, 308 { 309 Name: aws.String("state"), 310 Values: []*string{aws.String("pending"), aws.String("available")}, 311 }, 312 }, 313 }), 314 gomock.Any()).Do(func(_, y interface{}) { 315 funct := y.(func(page *ec2.DescribeNatGatewaysOutput, lastPage bool) bool) 316 funct(&ec2.DescribeNatGatewaysOutput{NatGateways: []*ec2.NatGateway{{ 317 NatGatewayId: aws.String("gateway"), 318 SubnetId: aws.String("subnet-1"), 319 Tags: []*ec2.Tag{ 320 { 321 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 322 Value: aws.String("common"), 323 }, 324 { 325 Key: aws.String("Name"), 326 Value: aws.String("test-cluster-nat"), 327 }, 328 { 329 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 330 Value: aws.String("owned"), 331 }, 332 }, 333 }}}, true) 334 }).Return(nil) 335 336 m.DescribeAddresses(gomock.Any()).Times(0) 337 m.AllocateAddress(gomock.Any()).Times(0) 338 m.CreateNatGateway(gomock.Any()).Times(0) 339 }, 340 }, 341 { 342 name: "public & private subnet declared, but don't exist yet", 343 input: []infrav1.SubnetSpec{ 344 { 345 ID: "", 346 AvailabilityZone: "us-east-1a", 347 CidrBlock: "10.0.10.0/24", 348 IsPublic: true, 349 }, 350 { 351 ID: "", 352 AvailabilityZone: "us-east-1a", 353 CidrBlock: "10.0.12.0/24", 354 IsPublic: false, 355 }, 356 }, 357 expect: func(m *mocks.MockEC2APIMockRecorder) { 358 m.DescribeNatGatewaysPages(gomock.Any(), gomock.Any()). 359 Return(nil). 360 Times(1) 361 }, 362 }, 363 } 364 365 for _, tc := range testCases { 366 t.Run(tc.name, func(t *testing.T) { 367 ec2Mock := mocks.NewMockEC2API(mockCtrl) 368 scheme := runtime.NewScheme() 369 _ = infrav1.AddToScheme(scheme) 370 awsCluster := &infrav1.AWSCluster{ 371 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 372 Spec: infrav1.AWSClusterSpec{ 373 NetworkSpec: infrav1.NetworkSpec{ 374 VPC: infrav1.VPCSpec{ 375 ID: subnetsVPCID, 376 Tags: infrav1.Tags{ 377 infrav1.ClusterTagKey("test-cluster"): "owned", 378 }, 379 }, 380 Subnets: tc.input, 381 }, 382 }, 383 } 384 client := fake.NewClientBuilder().WithScheme(scheme).Build() 385 ctx := context.TODO() 386 client.Create(ctx, awsCluster) 387 clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{ 388 Cluster: &clusterv1.Cluster{ 389 ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, 390 }, 391 AWSCluster: awsCluster, 392 Client: client, 393 }) 394 if err != nil { 395 t.Fatalf("Failed to create test context: %v", err) 396 } 397 398 tc.expect(ec2Mock.EXPECT()) 399 400 s := NewService(clusterScope) 401 s.EC2Client = ec2Mock 402 403 if err := s.reconcileNatGateways(); err != nil { 404 t.Fatalf("got an unexpected error: %v", err) 405 } 406 }) 407 } 408 } 409 410 func TestDeleteNatGateways(t *testing.T) { 411 mockCtrl := gomock.NewController(t) 412 defer mockCtrl.Finish() 413 414 testCases := []struct { 415 name string 416 input []infrav1.SubnetSpec 417 isUnmanagedVPC bool 418 expect func(m *mocks.MockEC2APIMockRecorder) 419 wantErr bool 420 }{ 421 { 422 name: "Should skip deletion if vpc is unmanaged", 423 isUnmanagedVPC: true, 424 }, 425 { 426 name: "Should skip deletion if no private subnet is present", 427 input: []infrav1.SubnetSpec{ 428 { 429 ID: "subnet-1", 430 AvailabilityZone: "us-east-1a", 431 CidrBlock: "10.0.10.0/24", 432 IsPublic: true, 433 }, 434 }, 435 }, 436 { 437 name: "Should skip deletion if no public subnet is present", 438 input: []infrav1.SubnetSpec{ 439 { 440 ID: "subnet-1", 441 AvailabilityZone: "us-east-1a", 442 CidrBlock: "10.0.10.0/24", 443 IsPublic: false, 444 }, 445 }, 446 }, 447 { 448 name: "Should skip deletion if no existing natgateway present", 449 input: []infrav1.SubnetSpec{ 450 { 451 ID: "subnet-1", 452 AvailabilityZone: "us-east-1a", 453 CidrBlock: "10.0.10.0/24", 454 IsPublic: true, 455 }, 456 { 457 ID: "subnet-2", 458 AvailabilityZone: "us-east-1a", 459 CidrBlock: "10.0.12.0/24", 460 IsPublic: false, 461 }, 462 }, 463 expect: func(m *mocks.MockEC2APIMockRecorder) { 464 m.DescribeNatGatewaysPages( 465 gomock.Eq(&ec2.DescribeNatGatewaysInput{ 466 Filter: []*ec2.Filter{ 467 { 468 Name: aws.String("vpc-id"), 469 Values: []*string{aws.String("managed-vpc")}, 470 }, 471 { 472 Name: aws.String("state"), 473 Values: []*string{aws.String("pending"), aws.String("available")}, 474 }, 475 }, 476 }), 477 gomock.Any()).Return(nil) 478 }, 479 }, 480 { 481 name: "Should successfully delete natgateways", 482 input: []infrav1.SubnetSpec{ 483 { 484 ID: "subnet-1", 485 AvailabilityZone: "us-east-1a", 486 CidrBlock: "10.0.10.0/24", 487 IsPublic: true, 488 }, 489 { 490 ID: "subnet-2", 491 AvailabilityZone: "us-east-1a", 492 CidrBlock: "10.0.12.0/24", 493 IsPublic: false, 494 }, 495 }, 496 expect: func(m *mocks.MockEC2APIMockRecorder) { 497 m.DescribeNatGatewaysPages( 498 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), 499 gomock.Any()).Do(mockDescribeNatGatewaysOutput).Return(nil) 500 501 m.DeleteNatGateway(gomock.Eq(&ec2.DeleteNatGatewayInput{ 502 NatGatewayId: aws.String("natgateway"), 503 })).Return(&ec2.DeleteNatGatewayOutput{}, nil) 504 505 m.DescribeNatGateways(gomock.Eq(&ec2.DescribeNatGatewaysInput{ 506 NatGatewayIds: []*string{aws.String("natgateway")}, 507 })).Return(&ec2.DescribeNatGatewaysOutput{ 508 NatGateways: []*ec2.NatGateway{ 509 { 510 State: aws.String("available"), 511 }, 512 }, 513 }, nil) 514 m.DescribeNatGateways(gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{})).Return(&ec2.DescribeNatGatewaysOutput{ 515 NatGateways: []*ec2.NatGateway{ 516 { 517 State: aws.String("deleted"), 518 }, 519 }, 520 }, nil) 521 }, 522 }, 523 { 524 name: "Should return error if natgateway has unknown state", 525 input: []infrav1.SubnetSpec{ 526 { 527 ID: "subnet-1", 528 AvailabilityZone: "us-east-1a", 529 CidrBlock: "10.0.10.0/24", 530 IsPublic: true, 531 }, 532 { 533 ID: "", 534 AvailabilityZone: "us-east-1a", 535 CidrBlock: "10.0.12.0/24", 536 IsPublic: true, 537 }, 538 { 539 ID: "subnet-3", 540 AvailabilityZone: "us-east-1a", 541 CidrBlock: "10.0.14.0/24", 542 IsPublic: false, 543 }, 544 }, 545 expect: func(m *mocks.MockEC2APIMockRecorder) { 546 m.DescribeNatGatewaysPages( 547 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), gomock.Any()).Do(mockDescribeNatGatewaysOutput).Return(nil) 548 549 m.DeleteNatGateway(gomock.Eq(&ec2.DeleteNatGatewayInput{ 550 NatGatewayId: aws.String("natgateway"), 551 })).Return(&ec2.DeleteNatGatewayOutput{}, nil) 552 553 m.DescribeNatGateways(gomock.Eq(&ec2.DescribeNatGatewaysInput{ 554 NatGatewayIds: []*string{aws.String("natgateway")}, 555 })).Return(&ec2.DescribeNatGatewaysOutput{ 556 NatGateways: []*ec2.NatGateway{ 557 { 558 State: aws.String("unknown"), 559 }, 560 }, 561 }, nil) 562 }, 563 wantErr: true, 564 }, 565 { 566 name: "Should return error if describe natgateway output and error, both are nil", 567 input: []infrav1.SubnetSpec{ 568 { 569 ID: "subnet-1", 570 AvailabilityZone: "us-east-1a", 571 CidrBlock: "10.0.10.0/24", 572 IsPublic: true, 573 }, 574 { 575 ID: "subnet-2", 576 AvailabilityZone: "us-east-1a", 577 CidrBlock: "10.0.14.0/24", 578 IsPublic: false, 579 }, 580 }, 581 expect: func(m *mocks.MockEC2APIMockRecorder) { 582 m.DescribeNatGatewaysPages( 583 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), gomock.Any()).Do(mockDescribeNatGatewaysOutput).Return(nil) 584 585 m.DeleteNatGateway(gomock.Eq(&ec2.DeleteNatGatewayInput{ 586 NatGatewayId: aws.String("natgateway"), 587 })).Return(&ec2.DeleteNatGatewayOutput{}, nil) 588 589 m.DescribeNatGateways(gomock.Eq(&ec2.DescribeNatGatewaysInput{ 590 NatGatewayIds: []*string{aws.String("natgateway")}, 591 })).Return(nil, nil) 592 }, 593 wantErr: true, 594 }, 595 { 596 name: "Should return error if describe natgateway pages fails", 597 input: []infrav1.SubnetSpec{ 598 { 599 ID: "subnet-1", 600 AvailabilityZone: "us-east-1a", 601 CidrBlock: "10.0.10.0/24", 602 IsPublic: true, 603 }, 604 { 605 ID: "subnet-2", 606 AvailabilityZone: "us-east-1a", 607 CidrBlock: "10.0.14.0/24", 608 IsPublic: false, 609 }, 610 }, 611 expect: func(m *mocks.MockEC2APIMockRecorder) { 612 m.DescribeNatGatewaysPages( 613 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), gomock.Any()).Return(awserrors.NewFailedDependency("failed dependency")) 614 }, 615 wantErr: true, 616 }, 617 { 618 name: "Should return error if delete natgateway fails", 619 input: []infrav1.SubnetSpec{ 620 { 621 ID: "subnet-1", 622 AvailabilityZone: "us-east-1a", 623 CidrBlock: "10.0.10.0/24", 624 IsPublic: true, 625 }, 626 { 627 ID: "subnet-2", 628 AvailabilityZone: "us-east-1a", 629 CidrBlock: "10.0.14.0/24", 630 IsPublic: false, 631 }, 632 }, 633 expect: func(m *mocks.MockEC2APIMockRecorder) { 634 m.DescribeNatGatewaysPages( 635 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), gomock.Any()).Do(mockDescribeNatGatewaysOutput).Return(nil) 636 637 m.DeleteNatGateway(gomock.Eq(&ec2.DeleteNatGatewayInput{ 638 NatGatewayId: aws.String("natgateway"), 639 })).Return(nil, awserrors.NewFailedDependency("failed dependency")) 640 }, 641 wantErr: true, 642 }, 643 { 644 name: "Should return error if describe natgateway fails", 645 input: []infrav1.SubnetSpec{ 646 { 647 ID: "subnet-1", 648 AvailabilityZone: "us-east-1a", 649 CidrBlock: "10.0.10.0/24", 650 IsPublic: true, 651 }, 652 { 653 ID: "subnet-2", 654 AvailabilityZone: "us-east-1a", 655 CidrBlock: "10.0.14.0/24", 656 IsPublic: false, 657 }, 658 }, 659 expect: func(m *mocks.MockEC2APIMockRecorder) { 660 m.DescribeNatGatewaysPages( 661 gomock.AssignableToTypeOf(&ec2.DescribeNatGatewaysInput{}), gomock.Any()).Do(mockDescribeNatGatewaysOutput).Return(nil) 662 663 m.DeleteNatGateway(gomock.Eq(&ec2.DeleteNatGatewayInput{ 664 NatGatewayId: aws.String("natgateway"), 665 })).Return(&ec2.DeleteNatGatewayOutput{}, nil) 666 667 m.DescribeNatGateways(gomock.Eq(&ec2.DescribeNatGatewaysInput{ 668 NatGatewayIds: []*string{aws.String("natgateway")}, 669 })).Return(nil, awserrors.NewNotFound("not found")) 670 }, 671 wantErr: true, 672 }, 673 } 674 675 for _, tc := range testCases { 676 t.Run(tc.name, func(t *testing.T) { 677 g := NewWithT(t) 678 ec2Mock := mocks.NewMockEC2API(mockCtrl) 679 scheme := runtime.NewScheme() 680 _ = infrav1.AddToScheme(scheme) 681 awsCluster := &infrav1.AWSCluster{ 682 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 683 Spec: infrav1.AWSClusterSpec{ 684 NetworkSpec: infrav1.NetworkSpec{ 685 VPC: infrav1.VPCSpec{ 686 ID: "managed-vpc", 687 Tags: infrav1.Tags{ 688 infrav1.ClusterTagKey("test-cluster"): "owned", 689 }, 690 }, 691 Subnets: tc.input, 692 }, 693 }, 694 } 695 if tc.isUnmanagedVPC { 696 awsCluster.Spec.NetworkSpec.VPC.Tags = infrav1.Tags{} 697 } 698 client := fake.NewClientBuilder().WithScheme(scheme).Build() 699 clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{ 700 Cluster: &clusterv1.Cluster{ 701 ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, 702 }, 703 AWSCluster: awsCluster, 704 Client: client, 705 }) 706 g.Expect(err).NotTo(HaveOccurred()) 707 if tc.expect != nil { 708 tc.expect(ec2Mock.EXPECT()) 709 } 710 711 s := NewService(clusterScope) 712 s.EC2Client = ec2Mock 713 714 err = s.deleteNatGateways() 715 if tc.wantErr { 716 g.Expect(err).To(HaveOccurred()) 717 return 718 } 719 g.Expect(err).NotTo(HaveOccurred()) 720 }) 721 } 722 } 723 724 var mockDescribeNatGatewaysOutput = func(_, y interface{}) { 725 funct := y.(func(page *ec2.DescribeNatGatewaysOutput, lastPage bool) bool) 726 funct(&ec2.DescribeNatGatewaysOutput{NatGateways: []*ec2.NatGateway{{ 727 NatGatewayId: aws.String("natgateway"), 728 SubnetId: aws.String("subnet-1"), 729 }}}, true) 730 }