sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/securitygroup/securitygroups_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 securitygroup 18 19 import ( 20 "strings" 21 "testing" 22 23 "github.com/aws/aws-sdk-go/aws" 24 "github.com/aws/aws-sdk-go/aws/awserr" 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 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/util/sets" 32 "sigs.k8s.io/controller-runtime/pkg/client/fake" 33 34 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 35 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors" 36 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope" 37 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services" 38 "sigs.k8s.io/cluster-api-provider-aws/test/mocks" 39 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 40 ) 41 42 var ( 43 testSecurityGroupRoles = []infrav1.SecurityGroupRole{ 44 infrav1.SecurityGroupBastion, 45 infrav1.SecurityGroupAPIServerLB, 46 infrav1.SecurityGroupLB, 47 infrav1.SecurityGroupControlPlane, 48 infrav1.SecurityGroupNode, 49 } 50 ) 51 52 func TestReconcileSecurityGroups(t *testing.T) { 53 mockCtrl := gomock.NewController(t) 54 defer mockCtrl.Finish() 55 56 testCases := []struct { 57 name string 58 input *infrav1.NetworkSpec 59 expect func(m *mocks.MockEC2APIMockRecorder) 60 err error 61 }{ 62 { 63 name: "no existing", 64 input: &infrav1.NetworkSpec{ 65 VPC: infrav1.VPCSpec{ 66 ID: "vpc-securitygroups", 67 InternetGatewayID: aws.String("igw-01"), 68 Tags: infrav1.Tags{ 69 infrav1.ClusterTagKey("test-cluster"): "owned", 70 }, 71 }, 72 Subnets: infrav1.Subnets{ 73 infrav1.SubnetSpec{ 74 ID: "subnet-securitygroups-private", 75 IsPublic: false, 76 AvailabilityZone: "us-east-1a", 77 }, 78 infrav1.SubnetSpec{ 79 ID: "subnet-securitygroups-public", 80 IsPublic: true, 81 NatGatewayID: aws.String("nat-01"), 82 AvailabilityZone: "us-east-1a", 83 }, 84 }, 85 }, 86 expect: func(m *mocks.MockEC2APIMockRecorder) { 87 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})). 88 Return(&ec2.DescribeSecurityGroupsOutput{}, nil) 89 90 securityGroupBastion := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{ 91 VpcId: aws.String("vpc-securitygroups"), 92 GroupName: aws.String("test-cluster-bastion"), 93 Description: aws.String("Kubernetes cluster test-cluster: bastion"), 94 TagSpecifications: []*ec2.TagSpecification{ 95 { 96 ResourceType: aws.String("security-group"), 97 Tags: []*ec2.Tag{ 98 { 99 Key: aws.String("Name"), 100 Value: aws.String("test-cluster-bastion"), 101 }, 102 { 103 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 104 Value: aws.String("owned"), 105 }, 106 { 107 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 108 Value: aws.String("bastion"), 109 }, 110 }, 111 }, 112 }, 113 })). 114 Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-bastion")}, nil) 115 116 m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{ 117 GroupId: aws.String("sg-bastion"), 118 })). 119 Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil). 120 After(securityGroupBastion) 121 122 securityGroupAPIServerLb := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{ 123 VpcId: aws.String("vpc-securitygroups"), 124 GroupName: aws.String("test-cluster-apiserver-lb"), 125 Description: aws.String("Kubernetes cluster test-cluster: apiserver-lb"), 126 TagSpecifications: []*ec2.TagSpecification{ 127 { 128 ResourceType: aws.String("security-group"), 129 Tags: []*ec2.Tag{ 130 { 131 Key: aws.String("Name"), 132 Value: aws.String("test-cluster-apiserver-lb"), 133 }, 134 { 135 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 136 Value: aws.String("owned"), 137 }, 138 { 139 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 140 Value: aws.String("apiserver-lb"), 141 }, 142 }, 143 }, 144 }, 145 })). 146 Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-apiserver-lb")}, nil) 147 148 m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{ 149 GroupId: aws.String("sg-apiserver-lb"), 150 })). 151 Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil). 152 After(securityGroupAPIServerLb) 153 154 m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{ 155 VpcId: aws.String("vpc-securitygroups"), 156 GroupName: aws.String("test-cluster-lb"), 157 Description: aws.String("Kubernetes cluster test-cluster: lb"), 158 TagSpecifications: []*ec2.TagSpecification{ 159 { 160 ResourceType: aws.String("security-group"), 161 Tags: []*ec2.Tag{ 162 { 163 Key: aws.String("Name"), 164 Value: aws.String("test-cluster-lb"), 165 }, 166 { 167 Key: aws.String("kubernetes.io/cluster/test-cluster"), 168 Value: aws.String("owned"), 169 }, 170 { 171 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 172 Value: aws.String("owned"), 173 }, 174 { 175 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 176 Value: aws.String("lb"), 177 }, 178 }, 179 }, 180 }, 181 })). 182 Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-lb")}, nil) 183 184 securityGroupControl := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{ 185 VpcId: aws.String("vpc-securitygroups"), 186 GroupName: aws.String("test-cluster-controlplane"), 187 Description: aws.String("Kubernetes cluster test-cluster: controlplane"), 188 TagSpecifications: []*ec2.TagSpecification{ 189 { 190 ResourceType: aws.String("security-group"), 191 Tags: []*ec2.Tag{ 192 { 193 Key: aws.String("Name"), 194 Value: aws.String("test-cluster-controlplane"), 195 }, 196 { 197 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 198 Value: aws.String("owned"), 199 }, 200 { 201 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 202 Value: aws.String("controlplane"), 203 }, 204 }, 205 }, 206 }, 207 })). 208 Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-control")}, nil) 209 210 m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{ 211 GroupId: aws.String("sg-control"), 212 })). 213 Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil). 214 After(securityGroupControl) 215 216 securityGroupNode := m.CreateSecurityGroup(gomock.Eq(&ec2.CreateSecurityGroupInput{ 217 VpcId: aws.String("vpc-securitygroups"), 218 GroupName: aws.String("test-cluster-node"), 219 Description: aws.String("Kubernetes cluster test-cluster: node"), 220 TagSpecifications: []*ec2.TagSpecification{ 221 { 222 ResourceType: aws.String("security-group"), 223 Tags: []*ec2.Tag{ 224 { 225 Key: aws.String("Name"), 226 Value: aws.String("test-cluster-node"), 227 }, 228 { 229 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), 230 Value: aws.String("owned"), 231 }, 232 { 233 Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), 234 Value: aws.String("node"), 235 }, 236 }, 237 }, 238 }, 239 })). 240 Return(&ec2.CreateSecurityGroupOutput{GroupId: aws.String("sg-node")}, nil) 241 242 m.AuthorizeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.AuthorizeSecurityGroupIngressInput{ 243 GroupId: aws.String("sg-node"), 244 })). 245 Return(&ec2.AuthorizeSecurityGroupIngressOutput{}, nil). 246 After(securityGroupNode) 247 }, 248 }, 249 { 250 name: "all overridden, do not tag", 251 input: &infrav1.NetworkSpec{ 252 VPC: infrav1.VPCSpec{ 253 ID: "vpc-securitygroups", 254 InternetGatewayID: aws.String("igw-01"), 255 }, 256 Subnets: infrav1.Subnets{ 257 infrav1.SubnetSpec{ 258 ID: "subnet-securitygroups-private", 259 IsPublic: false, 260 AvailabilityZone: "us-east-1a", 261 }, 262 infrav1.SubnetSpec{ 263 ID: "subnet-securitygroups-public", 264 IsPublic: true, 265 NatGatewayID: aws.String("nat-01"), 266 AvailabilityZone: "us-east-1a", 267 }, 268 }, 269 SecurityGroupOverrides: map[infrav1.SecurityGroupRole]string{ 270 infrav1.SecurityGroupBastion: "sg-bastion", 271 infrav1.SecurityGroupAPIServerLB: "sg-apiserver-lb", 272 infrav1.SecurityGroupLB: "sg-lb", 273 infrav1.SecurityGroupControlPlane: "sg-control", 274 infrav1.SecurityGroupNode: "sg-node", 275 }, 276 }, 277 expect: func(m *mocks.MockEC2APIMockRecorder) { 278 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})). 279 Return(&ec2.DescribeSecurityGroupsOutput{ 280 SecurityGroups: []*ec2.SecurityGroup{ 281 {GroupId: aws.String("sg-bastion"), GroupName: aws.String("Bastion Security Group")}, 282 {GroupId: aws.String("sg-apiserver-lb"), GroupName: aws.String("API load balancer Security Group")}, 283 {GroupId: aws.String("sg-lb"), GroupName: aws.String("Load balancer Security Group")}, 284 {GroupId: aws.String("sg-control"), GroupName: aws.String("Control plane Security Group")}, 285 {GroupId: aws.String("sg-node"), GroupName: aws.String("Node Security Group")}, 286 }, 287 }, nil).AnyTimes() 288 }, 289 }, 290 { 291 name: "managed vpc with overrides, returns error", 292 input: &infrav1.NetworkSpec{ 293 VPC: infrav1.VPCSpec{ 294 ID: "vpc-securitygroups", 295 InternetGatewayID: aws.String("igw-01"), 296 Tags: infrav1.Tags{ 297 infrav1.ClusterTagKey("test-cluster"): "owned", 298 }, 299 }, 300 Subnets: infrav1.Subnets{ 301 infrav1.SubnetSpec{ 302 ID: "subnet-securitygroups-private", 303 IsPublic: false, 304 AvailabilityZone: "us-east-1a", 305 }, 306 infrav1.SubnetSpec{ 307 ID: "subnet-securitygroups-public", 308 IsPublic: true, 309 NatGatewayID: aws.String("nat-01"), 310 AvailabilityZone: "us-east-1a", 311 }, 312 }, 313 SecurityGroupOverrides: map[infrav1.SecurityGroupRole]string{ 314 infrav1.SecurityGroupBastion: "sg-bastion", 315 infrav1.SecurityGroupAPIServerLB: "sg-apiserver-lb", 316 infrav1.SecurityGroupLB: "sg-lb", 317 infrav1.SecurityGroupControlPlane: "sg-control", 318 infrav1.SecurityGroupNode: "sg-node", 319 }, 320 }, 321 expect: func(m *mocks.MockEC2APIMockRecorder) { 322 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})). 323 Return(&ec2.DescribeSecurityGroupsOutput{ 324 SecurityGroups: []*ec2.SecurityGroup{ 325 {GroupId: aws.String("sg-bastion"), GroupName: aws.String("Bastion Security Group")}, 326 {GroupId: aws.String("sg-apiserver-lb"), GroupName: aws.String("API load balancer Security Group")}, 327 {GroupId: aws.String("sg-lb"), GroupName: aws.String("Load balancer Security Group")}, 328 {GroupId: aws.String("sg-control"), GroupName: aws.String("Control plane Security Group")}, 329 {GroupId: aws.String("sg-node"), GroupName: aws.String("Node Security Group")}, 330 }, 331 }, nil).AnyTimes() 332 }, 333 err: errors.New(`security group overrides provided for managed vpc "test-cluster"`), 334 }, 335 } 336 337 for _, tc := range testCases { 338 t.Run(tc.name, func(t *testing.T) { 339 ec2Mock := mocks.NewMockEC2API(mockCtrl) 340 341 scheme := runtime.NewScheme() 342 _ = infrav1.AddToScheme(scheme) 343 client := fake.NewClientBuilder().WithScheme(scheme).Build() 344 cs, err := scope.NewClusterScope(scope.ClusterScopeParams{ 345 Client: client, 346 Cluster: &clusterv1.Cluster{ 347 ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, 348 }, 349 AWSCluster: &infrav1.AWSCluster{ 350 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 351 Spec: infrav1.AWSClusterSpec{ 352 NetworkSpec: *tc.input, 353 }, 354 }, 355 }) 356 if err != nil { 357 t.Fatalf("Failed to create test context: %v", err) 358 } 359 360 tc.expect(ec2Mock.EXPECT()) 361 362 s := NewService(cs, testSecurityGroupRoles) 363 s.EC2Client = ec2Mock 364 365 if err := s.ReconcileSecurityGroups(); err != nil && tc.err != nil { 366 if !strings.Contains(err.Error(), tc.err.Error()) { 367 t.Fatalf("was expecting error to look like '%v', but got '%v'", tc.err, err) 368 } 369 } else if err != nil { 370 t.Fatalf("got an unexpected error: %v", err) 371 } 372 }) 373 } 374 } 375 376 func TestControlPlaneSecurityGroupNotOpenToAnyCIDR(t *testing.T) { 377 scheme := runtime.NewScheme() 378 _ = infrav1.AddToScheme(scheme) 379 client := fake.NewClientBuilder().WithScheme(scheme).Build() 380 cs, err := scope.NewClusterScope(scope.ClusterScopeParams{ 381 Client: client, 382 Cluster: &clusterv1.Cluster{ 383 ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, 384 }, 385 AWSCluster: &infrav1.AWSCluster{}, 386 }) 387 if err != nil { 388 t.Fatalf("Failed to create test context: %v", err) 389 } 390 391 s := NewService(cs, testSecurityGroupRoles) 392 rules, err := s.getSecurityGroupIngressRules(infrav1.SecurityGroupControlPlane) 393 if err != nil { 394 t.Fatalf("Failed to lookup controlplane security group ingress rules: %v", err) 395 } 396 397 for _, r := range rules { 398 if sets.NewString(r.CidrBlocks...).Has(services.AnyIPv4CidrBlock) { 399 t.Fatal("Ingress rule allows any CIDR block") 400 } 401 } 402 } 403 404 func TestDeleteSecurityGroups(t *testing.T) { 405 mockCtrl := gomock.NewController(t) 406 defer mockCtrl.Finish() 407 408 testCases := []struct { 409 name string 410 input *infrav1.NetworkSpec 411 expect func(m *mocks.MockEC2APIMockRecorder) 412 wantErr bool 413 }{ 414 { 415 name: "do not delete overridden security groups", 416 input: &infrav1.NetworkSpec{ 417 VPC: infrav1.VPCSpec{ 418 ID: "vpc-securitygroups", 419 InternetGatewayID: aws.String("igw-01"), 420 }, 421 Subnets: infrav1.Subnets{ 422 infrav1.SubnetSpec{ 423 ID: "subnet-securitygroups-private", 424 IsPublic: false, 425 AvailabilityZone: "us-east-1a", 426 }, 427 infrav1.SubnetSpec{ 428 ID: "subnet-securitygroups-public", 429 IsPublic: true, 430 NatGatewayID: aws.String("nat-01"), 431 AvailabilityZone: "us-east-1a", 432 }, 433 }, 434 SecurityGroupOverrides: map[infrav1.SecurityGroupRole]string{ 435 infrav1.SecurityGroupBastion: "sg-bastion", 436 infrav1.SecurityGroupAPIServerLB: "sg-apiserver-lb", 437 infrav1.SecurityGroupLB: "sg-lb", 438 infrav1.SecurityGroupControlPlane: "sg-control", 439 infrav1.SecurityGroupNode: "sg-node", 440 }, 441 }, 442 expect: func(m *mocks.MockEC2APIMockRecorder) { 443 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()).Return(nil) 444 }, 445 }, 446 { 447 name: "Should skip SG deletion if VPC ID not present", 448 input: &infrav1.NetworkSpec{ 449 VPC: infrav1.VPCSpec{}, 450 }, 451 }, 452 { 453 name: "Should return error if unable to find cluster-owned security groups in vpc", 454 input: &infrav1.NetworkSpec{ 455 VPC: infrav1.VPCSpec{ID: "vpc-id"}, 456 }, 457 expect: func(m *mocks.MockEC2APIMockRecorder) { 458 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()).Return(awserrors.NewFailedDependency("dependency-failure")) 459 }, 460 wantErr: true, 461 }, 462 { 463 name: "Should return error if unable to describe any SG present in VPC and owned by cluster", 464 input: &infrav1.NetworkSpec{ 465 VPC: infrav1.VPCSpec{ID: "vpc-id"}, 466 }, 467 expect: func(m *mocks.MockEC2APIMockRecorder) { 468 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()). 469 Do(processSecurityGroupsPage).Return(nil) 470 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})).Return(nil, awserr.New("dependency-failure", "dependency-failure", errors.Errorf("dependency-failure"))) 471 }, 472 wantErr: true, 473 }, 474 { 475 name: "Should not revoke Ingress rules for a SG if IP permissions are not set and able to delete the SG", 476 input: &infrav1.NetworkSpec{ 477 VPC: infrav1.VPCSpec{ID: "vpc-id"}, 478 }, 479 expect: func(m *mocks.MockEC2APIMockRecorder) { 480 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()). 481 Do(processSecurityGroupsPage).Return(nil) 482 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})).Return(&ec2.DescribeSecurityGroupsOutput{ 483 SecurityGroups: []*ec2.SecurityGroup{ 484 { 485 GroupId: aws.String("group-id"), 486 GroupName: aws.String("group-name"), 487 }, 488 }, 489 }, nil) 490 m.DeleteSecurityGroup(gomock.AssignableToTypeOf(&ec2.DeleteSecurityGroupInput{})).Return(nil, nil) 491 }, 492 }, 493 { 494 name: "Should return error if failed to revoke Ingress rules for a SG", 495 input: &infrav1.NetworkSpec{ 496 VPC: infrav1.VPCSpec{ID: "vpc-id"}, 497 }, 498 expect: func(m *mocks.MockEC2APIMockRecorder) { 499 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()). 500 Do(processSecurityGroupsPage).Return(nil) 501 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})).Return(&ec2.DescribeSecurityGroupsOutput{ 502 SecurityGroups: []*ec2.SecurityGroup{ 503 { 504 GroupId: aws.String("group-id"), 505 GroupName: aws.String("group-name"), 506 IpPermissions: []*ec2.IpPermission{ 507 { 508 ToPort: aws.Int64(4), 509 }, 510 }, 511 }, 512 }, 513 }, nil) 514 m.RevokeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.RevokeSecurityGroupIngressInput{})).Return(nil, awserr.New("failure", "failure", errors.Errorf("failure"))) 515 }, 516 wantErr: true, 517 }, 518 { 519 name: "Should delete SG successfully", 520 input: &infrav1.NetworkSpec{ 521 VPC: infrav1.VPCSpec{ID: "vpc-id"}, 522 }, 523 expect: func(m *mocks.MockEC2APIMockRecorder) { 524 m.DescribeSecurityGroupsPages(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{}), gomock.Any()). 525 Do(processSecurityGroupsPage).Return(nil) 526 m.DescribeSecurityGroups(gomock.AssignableToTypeOf(&ec2.DescribeSecurityGroupsInput{})).Return(&ec2.DescribeSecurityGroupsOutput{ 527 SecurityGroups: []*ec2.SecurityGroup{ 528 { 529 GroupId: aws.String("group-id"), 530 GroupName: aws.String("group-name"), 531 IpPermissions: []*ec2.IpPermission{ 532 { 533 ToPort: aws.Int64(4), 534 }, 535 }, 536 }, 537 }, 538 }, nil) 539 m.RevokeSecurityGroupIngress(gomock.AssignableToTypeOf(&ec2.RevokeSecurityGroupIngressInput{})).Return(nil, nil) 540 m.DeleteSecurityGroup(gomock.AssignableToTypeOf(&ec2.DeleteSecurityGroupInput{})).Return(nil, nil) 541 }, 542 }, 543 } 544 for _, tc := range testCases { 545 t.Run(tc.name, func(t *testing.T) { 546 g := NewWithT(t) 547 ec2Mock := mocks.NewMockEC2API(mockCtrl) 548 549 scheme := runtime.NewScheme() 550 g.Expect(infrav1.AddToScheme(scheme)).NotTo(HaveOccurred()) 551 552 awsCluster := &infrav1.AWSCluster{ 553 TypeMeta: metav1.TypeMeta{ 554 APIVersion: infrav1.GroupVersion.String(), 555 Kind: "AWSCluster", 556 }, 557 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 558 Spec: infrav1.AWSClusterSpec{ 559 NetworkSpec: *tc.input, 560 }, 561 } 562 563 client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(awsCluster).Build() 564 565 cs, err := scope.NewClusterScope(scope.ClusterScopeParams{ 566 Client: client, 567 Cluster: &clusterv1.Cluster{ 568 ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, 569 }, 570 AWSCluster: awsCluster, 571 }) 572 g.Expect(err).NotTo(HaveOccurred()) 573 574 if tc.expect != nil { 575 tc.expect(ec2Mock.EXPECT()) 576 } 577 578 s := NewService(cs, testSecurityGroupRoles) 579 s.EC2Client = ec2Mock 580 581 err = s.DeleteSecurityGroups() 582 if tc.wantErr { 583 g.Expect(err).To(HaveOccurred()) 584 return 585 } 586 g.Expect(err).NotTo(HaveOccurred()) 587 }) 588 } 589 } 590 591 func TestIngressRulesFromSDKType(t *testing.T) { 592 tests := []struct { 593 name string 594 input *ec2.IpPermission 595 expected infrav1.IngressRules 596 }{ 597 { 598 name: "Two group pairs", 599 input: &ec2.IpPermission{ 600 IpProtocol: aws.String("tcp"), 601 FromPort: aws.Int64(10250), 602 ToPort: aws.Int64(10250), 603 UserIdGroupPairs: []*ec2.UserIdGroupPair{ 604 { 605 Description: aws.String("Kubelet API"), 606 UserId: aws.String("aws-user-id-1"), 607 GroupId: aws.String("sg-source-1"), 608 }, 609 { 610 Description: aws.String("Kubelet API"), 611 UserId: aws.String("aws-user-id-1"), 612 GroupId: aws.String("sg-source-2"), 613 }, 614 }, 615 }, 616 expected: infrav1.IngressRules{ 617 { 618 Description: "Kubelet API", 619 Protocol: "tcp", 620 FromPort: 10250, 621 ToPort: 10250, 622 SourceSecurityGroupIDs: []string{"sg-source-1", "sg-source-2"}, 623 }, 624 }, 625 }, 626 { 627 name: "Mix of group pairs and cidr blocks", 628 input: &ec2.IpPermission{ 629 IpProtocol: aws.String("tcp"), 630 FromPort: aws.Int64(22), 631 ToPort: aws.Int64(22), 632 IpRanges: []*ec2.IpRange{ 633 { 634 CidrIp: aws.String("0.0.0.0/0"), 635 Description: aws.String("MY-SSH"), 636 }, 637 }, 638 UserIdGroupPairs: []*ec2.UserIdGroupPair{ 639 { 640 UserId: aws.String("aws-user-id-1"), 641 GroupId: aws.String("sg-source-1"), 642 Description: aws.String("SSH"), 643 }, 644 }, 645 }, 646 expected: infrav1.IngressRules{ 647 { 648 Description: "MY-SSH", 649 Protocol: "tcp", 650 FromPort: 22, 651 ToPort: 22, 652 CidrBlocks: []string{"0.0.0.0/0"}, 653 }, 654 { 655 Description: "SSH", 656 Protocol: "tcp", 657 FromPort: 22, 658 ToPort: 22, 659 SourceSecurityGroupIDs: []string{"sg-source-1"}, 660 }, 661 }, 662 }, 663 } 664 665 for _, tc := range tests { 666 t.Run(tc.name, func(t *testing.T) { 667 g := NewGomegaWithT(t) 668 output := ingressRulesFromSDKType(tc.input) 669 670 g.Expect(output).To(Equal(tc.expected)) 671 }) 672 } 673 } 674 675 var processSecurityGroupsPage = func(_, y interface{}) { 676 funcType := y.(func(out *ec2.DescribeSecurityGroupsOutput, last bool) bool) 677 funcType(&ec2.DescribeSecurityGroupsOutput{ 678 SecurityGroups: []*ec2.SecurityGroup{ 679 { 680 GroupId: aws.String("group-id"), 681 GroupName: aws.String("group-name"), 682 }, 683 }, 684 }, true) 685 }