diff --git a/cloud/scope/powervs_cluster.go b/cloud/scope/powervs_cluster.go index 6074780ab..fd9fb7034 100644 --- a/cloud/scope/powervs_cluster.go +++ b/cloud/scope/powervs_cluster.go @@ -1259,7 +1259,7 @@ func (s *PowerVSClusterScope) ReconcileVPCSecurityGroups() error { if securityGroup.Name != nil { securityGroupID, securityGroupRuleIDs, _ = s.GetVPCSecurityGroupByName(*securityGroup.Name) } else { - _, securityGroupRuleIDs, _ = s.GetVPCSecurityGroupByID(*securityGroup.ID) + securityGroupID, securityGroupRuleIDs, _ = s.GetVPCSecurityGroupByID(*securityGroup.ID) } if securityGroupID != nil && securityGroupRuleIDs != nil { @@ -1333,7 +1333,7 @@ func (s *PowerVSClusterScope) createVPCSecurityGroupRule(securityGroupID, direct if err != nil { return fmt.Errorf("failed to find VPC security group by name '%s', err: %w", *remote.SecurityGroupName, err) } - if sg.Name != nil { + if sg == nil { return fmt.Errorf("VPC security group by name '%s' does not exist", *remote.SecurityGroupName) } s.V(3).Info("Creating VPC security group rule", "securityGroupID", *securityGroupID, "direction", *direction, "protocol", *protocol, "securityGroup", *remote.SecurityGroupName, "securityGroupCRN", *sg.CRN) @@ -1511,10 +1511,6 @@ func (s *PowerVSClusterScope) validateSecurityGroupRule(originalSecurityGroupRul } protocol := string(rule.Protocol) - portMin := rule.PortRange.MinimumPort - portMax := rule.PortRange.MaximumPort - icmpCode := rule.ICMPCode - icmpType := rule.ICMPType for _, ogRuleIntf := range originalSecurityGroupRules { switch reflect.TypeOf(ogRuleIntf).String() { @@ -1531,6 +1527,8 @@ func (s *PowerVSClusterScope) validateSecurityGroupRule(originalSecurityGroupRul } } case "*vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp": + portMin := rule.PortRange.MinimumPort + portMax := rule.PortRange.MaximumPort ogRule := ogRuleIntf.(*vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp) ruleID = ogRule.ID @@ -1543,6 +1541,8 @@ func (s *PowerVSClusterScope) validateSecurityGroupRule(originalSecurityGroupRul } } case "*vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp": + icmpCode := rule.ICMPCode + icmpType := rule.ICMPType ogRule := ogRuleIntf.(*vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp) ruleID = ogRule.ID @@ -1624,7 +1624,7 @@ func (s *PowerVSClusterScope) validateVPCSecurityGroup(securityGroup infrav1beta return nil, nil, nil } } - if securityGroupDet != nil && *securityGroupDet.VPC.ID != *s.GetVPCID() { + if securityGroupDet.VPC == nil || securityGroupDet.VPC.ID == nil || *securityGroupDet.VPC.ID != *s.GetVPCID() { return nil, nil, fmt.Errorf("VPC security group by name exists but is not attached to VPC") } @@ -1633,7 +1633,7 @@ func (s *PowerVSClusterScope) validateVPCSecurityGroup(securityGroup infrav1beta return nil, nil, fmt.Errorf("failed to validate VPC security group rules: %v", err) } if !ok { - if _, _, controllerCreated := s.GetVPCSecurityGroupByName(*securityGroup.Name); !*controllerCreated { + if _, _, controllerCreated := s.GetVPCSecurityGroupByName(*securityGroup.Name); controllerCreated != nil && !*controllerCreated { return nil, nil, fmt.Errorf("VPC security group by name exists but rules are not matching") } return nil, nil, s.createVPCSecurityGroupRulesAndSetStatus(securityGroup.Rules, securityGroupDet.ID, securityGroupDet.Name) diff --git a/cloud/scope/powervs_cluster_test.go b/cloud/scope/powervs_cluster_test.go index 13ef28abb..49e7d33d9 100644 --- a/cloud/scope/powervs_cluster_test.go +++ b/cloud/scope/powervs_cluster_test.go @@ -6118,3 +6118,2348 @@ func makePowerVSClusterScope(mockTransitGateway *tgmock.MockTransitGateway, mock return clusterScope } + +func TestReconcileVPCSecurityGroups(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + securityGroupID := "securityGroupID" + securityGroupName := "securityGroupName" + + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("When SecurityGroup ID is set and returns error while getting SecurityGroup", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(nil, nil, errors.New("failed to get security group")) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When SecurityGroup Name is set and returns error while creating SecurityGroup", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, nil) + mockVPC.EXPECT().CreateSecurityGroup(gomock.Any()).Return(nil, nil, errors.New("failed to create security group")) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup Name is set and creates SecurityGroup successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, nil) + mockVPC.EXPECT().CreateSecurityGroup(gomock.Any()).Return(&vpcv1.SecurityGroup{ID: ptr.To("securityGroupID")}, nil, nil) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroup Name is set and SecurityGroup already exists", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: &securityGroupName, ID: &securityGroupID, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}}, nil) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).To(BeNil()) + }) + + t.Run("When SecurityGroup ID is set and SecurityGroup already exists", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: &securityGroupName, ID: &securityGroupID, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}}, nil, nil) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroup Name is set and GetSecurityGroup returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroupStatus := make(map[string]infrav1beta2.VPCSecurityGroupStatus) + vpcSecurityGroupStatus[securityGroupName] = infrav1beta2.VPCSecurityGroupStatus{ID: ptr.To("securityGroupID"), RuleIDs: []*string{ptr.To("ruleID")}, ControllerCreated: ptr.To(true)} + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPCSecurityGroups: vpcSecurityGroupStatus, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(nil, nil, errors.New("failed to get security group")) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When SecurityGroup Name is set and returns error while getting SecurityGroupRules", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroupStatus := make(map[string]infrav1beta2.VPCSecurityGroupStatus) + vpcSecurityGroupStatus[securityGroupName] = infrav1beta2.VPCSecurityGroupStatus{ID: &securityGroupID, RuleIDs: []*string{ptr.To("ruleID")}, ControllerCreated: ptr.To(true)} + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPCSecurityGroups: vpcSecurityGroupStatus, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(&vpcv1.SecurityGroup{ID: ptr.To("securityGroupID"), Name: ptr.To("securityGroupName")}, nil, nil) + mockVPC.EXPECT().GetSecurityGroupRule(gomock.Any()).Return(nil, nil, errors.New("failed to get security group rule")) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup is created successfully but returns error while creating SecurityGroupRules", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, nil) + mockVPC.EXPECT().CreateSecurityGroup(gomock.Any()).Return(&vpcv1.SecurityGroup{ID: ptr.To("securityGroupID")}, nil, nil) + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(nil, nil, errors.New("failed to create security group rule")) + err := clusterScope.ReconcileVPCSecurityGroups() + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestValidateVPCSecurityGroup(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("When SecurityGroup by name exists and SecurityGroupRule matches", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + } + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("securityGroupID"), Rules: vpcSecurityGroupRules, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}} + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(securityGroupDetails, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(sg).To(BeEquivalentTo(securityGroupDetails)) + g.Expect(err).To(BeNil()) + }) + + t.Run("When SecurityGroup by id exists and SecurityGroupRule matches", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + } + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("securityGroupID"), Rules: vpcSecurityGroupRules, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}} + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(securityGroupDetails, nil, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(sg).To(BeEquivalentTo(securityGroupDetails)) + g.Expect(err).To(BeNil()) + }) + + t.Run("When SecurityGroup by name doesn't exist and return error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupNamw"), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, errors.New("failed to get SecurityGroup")) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup by id doesn't exist and return error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(nil, nil, errors.New("failed to get SecurityGroup")) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup by name exists but is not attached to VPC", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("sgID")} + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(securityGroupDetails, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeEmpty()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup by name exists but VPC id not matching", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("sgID"), VPC: &vpcv1.VPCReference{ID: ptr.To("vpcID")}} + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(securityGroupDetails, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroup by id exists but VPC id not matching", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("sgID"), VPC: &vpcv1.VPCReference{ID: ptr.To("vpcID")}} + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(securityGroupDetails, nil, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When it returns error while validating SecurityGroupRules", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + PortMax: (ptr.To(int64(65535))), + PortMin: (ptr.To(int64(1))), + } + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("securityGroupID"), Rules: vpcSecurityGroupRules, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}} + mockVPC.EXPECT().GetSecurityGroup(gomock.Any()).Return(securityGroupDetails, nil, nil) + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + + t.Run("When SecurityGroupRule doesn't exist and creates it", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.1.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("securityGroupID"), VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}} + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{Protocol: ptr.To("tcp"), ID: ptr.To("ruleID")}, nil, nil) + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(securityGroupDetails, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).To(BeNil()) + }) + + t.Run("When SecurityGroupRule doesn't match and return error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroupStatus := make(map[string]infrav1beta2.VPCSecurityGroupStatus) + vpcSecurityGroupStatus["securityGroupName"] = infrav1beta2.VPCSecurityGroupStatus{ID: ptr.To("securityGroupID"), RuleIDs: []*string{}, ControllerCreated: ptr.To(false)} + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPCSecurityGroups: vpcSecurityGroupStatus, + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + securityGroupDetails := &vpcv1.SecurityGroup{Name: ptr.To("securityGroupName"), ID: ptr.To("securityGroupID"), Rules: vpcSecurityGroupRules, VPC: &vpcv1.VPCReference{ID: ptr.To("VPCID")}} + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(securityGroupDetails, nil) + sg, ruleIDs, err := clusterScope.validateVPCSecurityGroup(vpcSecurityGroup) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestValidateVPCSecurityGroupRule(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("When it matches SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolTcpudp", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + PortMax: (ptr.To(int64(65535))), + PortMin: (ptr.To(int64(1))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(*ruleID).To(BeEquivalentTo("ruleID")) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolTcpudp", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + PortMax: (ptr.To(int64(65535))), + PortMin: (ptr.To(int64(1))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolTcpudp returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + PortMax: (ptr.To(int64(65535))), + PortMin: (ptr.To(int64(1))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When it matches SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolAll", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := []vpcv1.SecurityGroupRuleIntf{} + vpcSecurityGroupRules = append(vpcSecurityGroupRules, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(*ruleID).To(BeEquivalentTo("ruleID")) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolAll", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + Address: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := []vpcv1.SecurityGroupRuleIntf{} + vpcSecurityGroupRules = append(vpcSecurityGroupRules, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolAll returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When it matches SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolIcmp", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + ICMPCode: (ptr.To(int64(12))), + ICMPType: (ptr.To(int64(3))), + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CRN: ptr.To("crn"), + }, + ID: ptr.To("ruleID"), + Code: (ptr.To(int64(12))), + Type: (ptr.To(int64(3))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: ptr.To("crn"), CRN: ptr.To("crn")}, nil) + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(*ruleID).To(BeEquivalentTo("ruleID")) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolIcmp", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + ICMPCode: (ptr.To(int64(12))), + ICMPType: (ptr.To(int64(3))), + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CRN: ptr.To("crn"), + }, + ID: ptr.To("ruleID"), + Code: (ptr.To(int64(12))), + Type: (ptr.To(int64(3))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: ptr.To("crn"), CRN: ptr.To("CRN")}, nil) + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of protocolType SecurityGroupRuleSecurityGroupRuleProtocolIcmp returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + ICMPCode: (ptr.To(int64(12))), + ICMPType: (ptr.To(int64(3))), + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CRN: ptr.To("crn"), + }, + ID: ptr.To("ruleID"), + Code: (ptr.To(int64(12))), + Type: (ptr.To(int64(3))), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, errors.New("failed to get securityGroup")) + ruleID, match, err := clusterScope.validateSecurityGroupRule(vpcSecurityGroupRules, rules.Direction, rules.Destination, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestValidateVPCSecurityGroupRules(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("When SecurityGroupRule of Direction Inbound matches", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("inbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleIDs, match, err := clusterScope.validateVPCSecurityGroupRules(vpcSecurityGroupRules, vpcSecurityGroup.Rules) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Inbound doesn't match", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("inbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("securityGroupID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleIDs, match, err := clusterScope.validateVPCSecurityGroupRules(vpcSecurityGroupRules, vpcSecurityGroup.Rules) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Inbound returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("inbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + ruleIDs, match, err := clusterScope.validateVPCSecurityGroupRules(vpcSecurityGroupRules, vpcSecurityGroup.Rules) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Outbound matches", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("0.0.0.0/0"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleIDs, match, err := clusterScope.validateVPCSecurityGroupRules(vpcSecurityGroupRules, vpcSecurityGroup.Rules) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Outbound doesn't match", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroupRule := vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{ + Direction: ptr.To("outbound"), + Protocol: ptr.To("tcp"), + Remote: &vpcv1.SecurityGroupRuleRemote{ + CIDRBlock: ptr.To("192.168.1.1/24"), + }, + ID: ptr.To("ruleID"), + } + + vpcSecurityGroupRules := append([]vpcv1.SecurityGroupRuleIntf{}, &vpcSecurityGroupRule) + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + ID: ptr.To("securityGroupID"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + ruleIDs, match, err := clusterScope.validateVPCSecurityGroupRules(vpcSecurityGroupRules, vpcSecurityGroup.Rules) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) +} + +func TestValidateVPCSecurityGroupRuleRemote(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + t.Run("When it matches the remoteType Address", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{Address: ptr.To("192.168.0.1/24")}, remote) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match the remoteType Address", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{Address: ptr.To("192.168.1.1/24")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it matches the remoteType Any", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CIDRBlock: ptr.To("0.0.0.0/0")}, remote) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match the remoteType Any", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CIDRBlock: ptr.To("192.168.1.1/24")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it matches the remoteType CIDR", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(&vpcv1.Subnet{Ipv4CIDRBlock: ptr.To("192.168.1.1/24")}, nil) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CIDRBlock: ptr.To("192.168.1.1/24")}, remote) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match the remoteType CIDR", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(&vpcv1.Subnet{Ipv4CIDRBlock: ptr.To("192.168.0.1/24")}, nil) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CIDRBlock: ptr.To("192.168.1.1/24")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When the remoteType CIDR and it returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CIDRBlock: ptr.To("192.168.1.1/24")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When it matches the remoteType SG", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: ptr.To("192.168.1.1/24"), CRN: ptr.To("crn")}, nil) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CRN: ptr.To("crn")}, remote) + g.Expect(match).To(BeTrue()) + g.Expect(err).To(BeNil()) + }) + t.Run("When it doesn't match the remoteType SG", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{Name: ptr.To("192.168.1.1/24"), CRN: ptr.To("CRN")}, nil) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CRN: ptr.To("crn")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).To(BeNil()) + }) + t.Run("When the remoteType SG and it returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, errors.New("failed to get security group")) + match, err := clusterScope.validateVPCSecurityGroupRuleRemote(&vpcv1.SecurityGroupRuleRemote{CRN: ptr.To("crn")}, remote) + g.Expect(match).To(BeFalse()) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestCreateVPCSecurityGroupRule(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + securityGroupID := "securityGroupID" + var portMax int64 = 65535 + var portMin int64 = 1 + var protocol = "tcp" + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("Creates SecurityGroupRule of remoteType Address successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{Direction: ptr.To("outbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("outbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeEquivalentTo(ptr.To("ruleID"))) + g.Expect(err).To(BeNil()) + }) + t.Run("Creates SecurityGroupRule of remoteType CIDR successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(&vpcv1.Subnet{Ipv4CIDRBlock: ptr.To("192.168.1.1/24")}, nil) + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{Direction: ptr.To("outbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("outbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeEquivalentTo(ptr.To("ruleID"))) + g.Expect(err).To(BeNil()) + }) + t.Run("SecurityGroupRule of remoteType CIDR returns error when getting VPC subnet", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, errors.New("failed to get VPC subnet")) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("outbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("Creates SecurityGroupRule of remoteType Any successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAny, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{Direction: ptr.To("outbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("outbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeEquivalentTo(ptr.To("ruleID"))) + g.Expect(err).To(BeNil()) + }) + t.Run("Creates SecurityGroupRule of remoteType SG successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(&vpcv1.SecurityGroup{CRN: ptr.To("crn"), Name: ptr.To("securityGroupName")}, nil) + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolIcmp{Direction: ptr.To("inbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("inbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeEquivalentTo(ptr.To("ruleID"))) + g.Expect(err).To(BeNil()) + }) + t.Run("SecurityGroupRule of remoteType SG returns error while getting securityGroup", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, errors.New("failed to get security group")) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("inbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("SecurityGroupRule of remoteType SG returns error when SecurityGroup doesn't exist", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + remote := infrav1beta2.VPCSecurityGroupRuleRemote{ + SecurityGroupName: ptr.To("securityGroupName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeSG, + } + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, remote), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + }), + }, + }, + } + + mockVPC.EXPECT().GetSecurityGroupByName(gomock.Any()).Return(nil, nil) + ruleID, err := clusterScope.createVPCSecurityGroupRule(&securityGroupID, ptr.To("inbound"), &protocol, &portMin, &portMax, remote) + g.Expect(ruleID).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestCreateVPCSecurityGroupRules(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + + t.Run("When SecurityGroupRule of Direction Outbound created successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{Direction: ptr.To("outbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleIDs, err := clusterScope.createVPCSecurityGroupRules(vpcSecurityGroup.Rules, ptr.To("securityGroupID")) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Outbound returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, nil) + ruleIDs, err := clusterScope.createVPCSecurityGroupRules(vpcSecurityGroup.Rules, ptr.To("securityGroupID")) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Inbound created successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + PortRange: &infrav1beta2.VPCSecurityGroupPortRange{MaximumPort: 65535, + MinimumPort: 1, + }, + }, + } + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolTcpudp{Direction: ptr.To("inbound"), ID: ptr.To("ruleID")}, nil, nil) + ruleIDs, err := clusterScope.createVPCSecurityGroupRules(vpcSecurityGroup.Rules, ptr.To("securityGroupID")) + g.Expect(ruleIDs).To(BeEquivalentTo([]*string{ptr.To("ruleID")})) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroupRule of Direction Inbound returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionInbound, + Source: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + CIDRSubnetName: ptr.To("CIDRSubnetName"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeCIDR, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + VPC: &infrav1beta2.ResourceReference{ + ID: ptr.To("VPCID"), + }, + }, + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, vpcSecurityGroup), + }, + }, + } + + mockVPC.EXPECT().GetVPCSubnetByName(gomock.Any()).Return(nil, nil) + ruleIDs, err := clusterScope.createVPCSecurityGroupRules(vpcSecurityGroup.Rules, ptr.To("securityGroupID")) + g.Expect(ruleIDs).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestCreateVPCSecurityGroupRulesAndSetStatus(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + t.Run("When SecurityGroupRule is created successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{ + ID: ptr.To("resourceGroupID"), + }, + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(&vpcv1.SecurityGroupRuleSecurityGroupRuleProtocolAll{Direction: ptr.To("outbound"), ID: ptr.To("ruleID")}, nil, nil) + err := clusterScope.createVPCSecurityGroupRulesAndSetStatus(vpcSecurityGroup.Rules, ptr.To("securityGroupID"), ptr.To("securityGroupName")) + g.Expect(err).To(BeNil()) + }) + t.Run("When CreateSecurityGroupRule returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + rules := infrav1beta2.VPCSecurityGroupRule{ + Direction: infrav1beta2.VPCSecurityGroupRuleDirectionOutbound, + Destination: &infrav1beta2.VPCSecurityGroupRulePrototype{ + Remotes: append([]infrav1beta2.VPCSecurityGroupRuleRemote{}, infrav1beta2.VPCSecurityGroupRuleRemote{ + Address: ptr.To("192.168.0.1/24"), + RemoteType: infrav1beta2.VPCSecurityGroupRuleRemoteTypeAddress, + }), + Protocol: infrav1beta2.VPCSecurityGroupRuleProtocolTCP, + }, + } + vpcSecurityGroup := infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + Rules: append([]*infrav1beta2.VPCSecurityGroupRule{}, &rules), + } + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{ + ID: ptr.To("resourceGroupID"), + }, + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroupRule(gomock.Any()).Return(nil, nil, errors.New("failed to create securityGroupRules")) + err := clusterScope.createVPCSecurityGroupRulesAndSetStatus(vpcSecurityGroup.Rules, ptr.To("securityGroupID"), ptr.To("securityGroupName")) + g.Expect(err).ToNot(BeNil()) + }) +} + +func TestCreateVPCSecurityGroup(t *testing.T) { + var ( + mockVPC *mock.MockVpc + mockCtrl *gomock.Controller + ) + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + mockVPC = mock.NewMockVpc(mockCtrl) + } + teardown := func() { + mockCtrl.Finish() + } + t.Run("When SecurityGroup is created successfully", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{ + ID: ptr.To("resourceGroupID"), + }, + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroup(gomock.Any()).Return(&vpcv1.SecurityGroup{ID: ptr.To("securityGroupID")}, nil, nil) + sg, err := clusterScope.createVPCSecurityGroup(clusterScope.IBMPowerVSCluster.Spec.VPCSecurityGroups[0]) + g.Expect(*sg).To(BeEquivalentTo("securityGroupID")) + g.Expect(err).To(BeNil()) + }) + t.Run("When SecurityGroup returns error", func(t *testing.T) { + g := NewWithT(t) + setup(t) + t.Cleanup(teardown) + clusterScope := PowerVSClusterScope{ + IBMVPCClient: mockVPC, + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + VPCSecurityGroups: append([]infrav1beta2.VPCSecurityGroup{}, infrav1beta2.VPCSecurityGroup{ + Name: ptr.To("securityGroupName"), + }), + ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{ + ID: ptr.To("resourceGroupID"), + }, + }, + }, + } + + mockVPC.EXPECT().CreateSecurityGroup(gomock.Any()).Return(nil, nil, errors.New("failed to create SecurityGroup")) + sg, err := clusterScope.createVPCSecurityGroup(clusterScope.IBMPowerVSCluster.Spec.VPCSecurityGroups[0]) + g.Expect(sg).To(BeNil()) + g.Expect(err).ToNot(BeNil()) + }) +}