diff --git a/api/v1beta1/awscluster_conversion.go b/api/v1beta1/awscluster_conversion.go index 616a585329..92f0e96a9f 100644 --- a/api/v1beta1/awscluster_conversion.go +++ b/api/v1beta1/awscluster_conversion.go @@ -74,7 +74,7 @@ func restoreControlPlaneLoadBalancer(restored, dst *infrav2.AWSLoadBalancerSpec) dst.LoadBalancerType = restored.LoadBalancerType dst.DisableHostsRewrite = restored.DisableHostsRewrite dst.PreserveClientIP = restored.PreserveClientIP - dst.AdditionalIngressRules = restored.AdditionalIngressRules + dst.IngressRules = restored.IngressRules } // ConvertFrom converts the v1beta1 AWSCluster receiver to a v1beta1 AWSCluster. diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index c04b82ab03..6a5e3effa0 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -1219,7 +1219,7 @@ func autoConvert_v1beta2_AWSLoadBalancerSpec_To_v1beta1_AWSLoadBalancerSpec(in * out.Subnets = *(*[]string)(unsafe.Pointer(&in.Subnets)) out.HealthCheckProtocol = (*ClassicELBProtocol)(unsafe.Pointer(in.HealthCheckProtocol)) out.AdditionalSecurityGroups = *(*[]string)(unsafe.Pointer(&in.AdditionalSecurityGroups)) - // WARNING: in.AdditionalIngressRules requires manual conversion: does not exist in peer-type + // WARNING: in.IngressRules requires manual conversion: does not exist in peer-type // WARNING: in.LoadBalancerType requires manual conversion: does not exist in peer-type // WARNING: in.DisableHostsRewrite requires manual conversion: does not exist in peer-type // WARNING: in.PreserveClientIP requires manual conversion: does not exist in peer-type diff --git a/api/v1beta2/awscluster_types.go b/api/v1beta2/awscluster_types.go index 203dafcc05..92c3e0fa9b 100644 --- a/api/v1beta2/awscluster_types.go +++ b/api/v1beta2/awscluster_types.go @@ -208,10 +208,9 @@ type AWSLoadBalancerSpec struct { // +optional AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"` - // AdditionalIngressRules sets the additional ingress rules for the control plane load balancer. If no source security group ids are specified, the - // default control plane security group will be used. + // IngressRules sets the ingress rules for the control plane load balancer. // +optional - AdditionalIngressRules []IngressRule `json:"additionalIngressRules,omitempty"` + IngressRules []IngressRule `json:"ingressRules,omitempty"` // LoadBalancerType sets the type for a load balancer. The default type is classic. // +kubebuilder:default=classic diff --git a/api/v1beta2/awscluster_webhook.go b/api/v1beta2/awscluster_webhook.go index 0c7831ef53..51d35f18ba 100644 --- a/api/v1beta2/awscluster_webhook.go +++ b/api/v1beta2/awscluster_webhook.go @@ -199,9 +199,9 @@ func (r *AWSCluster) validateAdditionalIngressRules() field.ErrorList { return allErrs } - for _, rule := range r.Spec.ControlPlaneLoadBalancer.AdditionalIngressRules { + for _, rule := range r.Spec.ControlPlaneLoadBalancer.IngressRules { if (rule.CidrBlocks != nil || rule.IPv6CidrBlocks != nil) && (rule.SourceSecurityGroupIDs != nil || rule.SourceSecurityGroupRoles != nil) { - allErrs = append(allErrs, field.Invalid(field.NewPath("additionalIngressRules"), r.Spec.ControlPlaneLoadBalancer.AdditionalIngressRules, "CIDR blocks and security group IDs or security group roles cannot be used together")) + allErrs = append(allErrs, field.Invalid(field.NewPath("additionalIngressRules"), r.Spec.ControlPlaneLoadBalancer.IngressRules, "CIDR blocks and security group IDs or security group roles cannot be used together")) } } diff --git a/api/v1beta2/awscluster_webhook_test.go b/api/v1beta2/awscluster_webhook_test.go index 30b20c16f0..dfce2b2c7c 100644 --- a/api/v1beta2/awscluster_webhook_test.go +++ b/api/v1beta2/awscluster_webhook_test.go @@ -252,11 +252,11 @@ func TestAWSClusterValidateCreate(t *testing.T) { wantErr: true, }, { - name: "rejects additional ingress rules with cidr block and source security group id", + name: "rejects ingress rules with cidr block and source security group id", cluster: &AWSCluster{ Spec: AWSClusterSpec{ ControlPlaneLoadBalancer: &AWSLoadBalancerSpec{ - AdditionalIngressRules: []IngressRule{ + IngressRules: []IngressRule{ { Protocol: SecurityGroupProtocolTCP, CidrBlocks: []string{"test"}, @@ -269,11 +269,11 @@ func TestAWSClusterValidateCreate(t *testing.T) { wantErr: true, }, { - name: "rejects additional ingress rules with cidr block and source security group id and role", + name: "rejects ingress rules with cidr block and source security group id and role", cluster: &AWSCluster{ Spec: AWSClusterSpec{ ControlPlaneLoadBalancer: &AWSLoadBalancerSpec{ - AdditionalIngressRules: []IngressRule{ + IngressRules: []IngressRule{ { Protocol: SecurityGroupProtocolTCP, IPv6CidrBlocks: []string{"test"}, @@ -287,11 +287,11 @@ func TestAWSClusterValidateCreate(t *testing.T) { wantErr: true, }, { - name: "accepts additional ingress rules with cidr block", + name: "accepts ingress rules with cidr block", cluster: &AWSCluster{ Spec: AWSClusterSpec{ ControlPlaneLoadBalancer: &AWSLoadBalancerSpec{ - AdditionalIngressRules: []IngressRule{ + IngressRules: []IngressRule{ { Protocol: SecurityGroupProtocolTCP, CidrBlocks: []string{"test"}, @@ -303,11 +303,11 @@ func TestAWSClusterValidateCreate(t *testing.T) { wantErr: false, }, { - name: "accepts additional ingress rules with source security group role", + name: "accepts ingress rules with source security group role", cluster: &AWSCluster{ Spec: AWSClusterSpec{ ControlPlaneLoadBalancer: &AWSLoadBalancerSpec{ - AdditionalIngressRules: []IngressRule{ + IngressRules: []IngressRule{ { Protocol: SecurityGroupProtocolTCP, SourceSecurityGroupRoles: []SecurityGroupRole{SecurityGroupBastion}, @@ -319,11 +319,11 @@ func TestAWSClusterValidateCreate(t *testing.T) { wantErr: false, }, { - name: "accepts additional ingress rules with source security group id and role", + name: "accepts ingress rules with source security group id and role", cluster: &AWSCluster{ Spec: AWSClusterSpec{ ControlPlaneLoadBalancer: &AWSLoadBalancerSpec{ - AdditionalIngressRules: []IngressRule{ + IngressRules: []IngressRule{ { Protocol: SecurityGroupProtocolTCP, SourceSecurityGroupIDs: []string{"test"}, diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 9d14c7a8cf..80cb1064fa 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -574,8 +574,8 @@ func (in *AWSLoadBalancerSpec) DeepCopyInto(out *AWSLoadBalancerSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.AdditionalIngressRules != nil { - in, out := &in.AdditionalIngressRules, &out.AdditionalIngressRules + if in.IngressRules != nil { + in, out := &in.IngressRules, &out.IngressRules *out = make([]IngressRule, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index bb02f3d908..609fad7339 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -966,11 +966,43 @@ spec: description: ControlPlaneLoadBalancer is optional configuration for customizing control plane behavior. properties: - additionalIngressRules: - description: AdditionalIngressRules sets the additional ingress - rules for the control plane load balancer. If no source security - group ids are specified, the default control plane security - group will be used. + additionalSecurityGroups: + description: AdditionalSecurityGroups sets the security groups + used by the load balancer. Expected to be security group IDs + This is optional - if not provided new security groups will + be created for the load balancer + items: + type: string + type: array + crossZoneLoadBalancing: + description: "CrossZoneLoadBalancing enables the classic ELB cross + availability zone balancing. \n With cross-zone load balancing, + each load balancer node for your Classic Load Balancer distributes + requests evenly across the registered instances in all enabled + Availability Zones. If cross-zone load balancing is disabled, + each load balancer node distributes requests evenly across the + registered instances in its Availability Zone only. \n Defaults + to false." + type: boolean + disableHostsRewrite: + description: DisableHostsRewrite disabled the hair pinning issue + solution that adds the NLB's address as 127.0.0.1 to the hosts + file of each instance. This is by default, false. + type: boolean + healthCheckProtocol: + description: HealthCheckProtocol sets the protocol type for ELB + health check target default value is ELBProtocolSSL + enum: + - TCP + - SSL + - HTTP + - HTTPS + - TLS + - UDP + type: string + ingressRules: + description: IngressRules sets the ingress rules for the control + plane load balancer. items: description: IngressRule defines an AWS ingress rule for security groups. @@ -1040,40 +1072,6 @@ spec: - toPort type: object type: array - additionalSecurityGroups: - description: AdditionalSecurityGroups sets the security groups - used by the load balancer. Expected to be security group IDs - This is optional - if not provided new security groups will - be created for the load balancer - items: - type: string - type: array - crossZoneLoadBalancing: - description: "CrossZoneLoadBalancing enables the classic ELB cross - availability zone balancing. \n With cross-zone load balancing, - each load balancer node for your Classic Load Balancer distributes - requests evenly across the registered instances in all enabled - Availability Zones. If cross-zone load balancing is disabled, - each load balancer node distributes requests evenly across the - registered instances in its Availability Zone only. \n Defaults - to false." - type: boolean - disableHostsRewrite: - description: DisableHostsRewrite disabled the hair pinning issue - solution that adds the NLB's address as 127.0.0.1 to the hosts - file of each instance. This is by default, false. - type: boolean - healthCheckProtocol: - description: HealthCheckProtocol sets the protocol type for ELB - health check target default value is ELBProtocolSSL - enum: - - TCP - - SSL - - HTTP - - HTTPS - - TLS - - UDP - type: string loadBalancerType: default: classic description: LoadBalancerType sets the type for a load balancer. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml index cc410f8148..66f3927be0 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml @@ -559,11 +559,45 @@ spec: description: ControlPlaneLoadBalancer is optional configuration for customizing control plane behavior. properties: - additionalIngressRules: - description: AdditionalIngressRules sets the additional - ingress rules for the control plane load balancer. If - no source security group ids are specified, the default - control plane security group will be used. + additionalSecurityGroups: + description: AdditionalSecurityGroups sets the security + groups used by the load balancer. Expected to be security + group IDs This is optional - if not provided new security + groups will be created for the load balancer + items: + type: string + type: array + crossZoneLoadBalancing: + description: "CrossZoneLoadBalancing enables the classic + ELB cross availability zone balancing. \n With cross-zone + load balancing, each load balancer node for your Classic + Load Balancer distributes requests evenly across the + registered instances in all enabled Availability Zones. + If cross-zone load balancing is disabled, each load + balancer node distributes requests evenly across the + registered instances in its Availability Zone only. + \n Defaults to false." + type: boolean + disableHostsRewrite: + description: DisableHostsRewrite disabled the hair pinning + issue solution that adds the NLB's address as 127.0.0.1 + to the hosts file of each instance. This is by default, + false. + type: boolean + healthCheckProtocol: + description: HealthCheckProtocol sets the protocol type + for ELB health check target default value is ELBProtocolSSL + enum: + - TCP + - SSL + - HTTP + - HTTPS + - TLS + - UDP + type: string + ingressRules: + description: IngressRules sets the ingress rules for the + control plane load balancer. items: description: IngressRule defines an AWS ingress rule for security groups. @@ -634,42 +668,6 @@ spec: - toPort type: object type: array - additionalSecurityGroups: - description: AdditionalSecurityGroups sets the security - groups used by the load balancer. Expected to be security - group IDs This is optional - if not provided new security - groups will be created for the load balancer - items: - type: string - type: array - crossZoneLoadBalancing: - description: "CrossZoneLoadBalancing enables the classic - ELB cross availability zone balancing. \n With cross-zone - load balancing, each load balancer node for your Classic - Load Balancer distributes requests evenly across the - registered instances in all enabled Availability Zones. - If cross-zone load balancing is disabled, each load - balancer node distributes requests evenly across the - registered instances in its Availability Zone only. - \n Defaults to false." - type: boolean - disableHostsRewrite: - description: DisableHostsRewrite disabled the hair pinning - issue solution that adds the NLB's address as 127.0.0.1 - to the hosts file of each instance. This is by default, - false. - type: boolean - healthCheckProtocol: - description: HealthCheckProtocol sets the protocol type - for ELB health check target default value is ELBProtocolSSL - enum: - - TCP - - SSL - - HTTP - - HTTPS - - TLS - - UDP - type: string loadBalancerType: default: classic description: LoadBalancerType sets the type for a load diff --git a/docs/book/src/topics/bring-your-own-aws-infrastructure.md b/docs/book/src/topics/bring-your-own-aws-infrastructure.md index cf3237a3db..51b4e52372 100644 --- a/docs/book/src/topics/bring-your-own-aws-infrastructure.md +++ b/docs/book/src/topics/bring-your-own-aws-infrastructure.md @@ -163,11 +163,12 @@ It's also possible to specify custom ingress rules for the control plane load ba ```yaml spec: - additionalIngressRules: - - description: "example ingress rule" - protocol: "-1" # all - fromPort: 7777 - toPort: 7777 + controlPlaneLoadBalancer: + ingressRules: + - description: "example ingress rule" + protocol: "-1" # all + fromPort: 7777 + toPort: 7777 ``` > **WARNING:** Using an existing Classic ELB is an advanced feature. **If you use an existing Classic ELB, you must correctly configure it, and attach subnets to it.** diff --git a/pkg/cloud/services/securitygroup/securitygroups.go b/pkg/cloud/services/securitygroup/securitygroups.go index f4a760c3a7..451fb0ac44 100644 --- a/pkg/cloud/services/securitygroup/securitygroups.go +++ b/pkg/cloud/services/securitygroup/securitygroups.go @@ -520,18 +520,18 @@ func (s *Service) getSecurityGroupIngressRules(role infrav1.SecurityGroupRole) ( rules = append(rules, s.defaultSSHIngressRule(s.scope.SecurityGroups()[infrav1.SecurityGroupBastion].ID)) } if s.scope.ControlPlaneLoadBalancer() != nil { - additionalIngressRules := s.scope.ControlPlaneLoadBalancer().AdditionalIngressRules - for i := range additionalIngressRules { - if additionalIngressRules[i].SourceSecurityGroupIDs == nil && additionalIngressRules[i].SourceSecurityGroupRoles == nil { // if the rule doesn't have a source security group, use the control plane security group - additionalIngressRules[i].SourceSecurityGroupIDs = []string{s.scope.SecurityGroups()[infrav1.SecurityGroupControlPlane].ID} + ingressRules := s.scope.ControlPlaneLoadBalancer().IngressRules + for i := range ingressRules { + if ingressRules[i].SourceSecurityGroupIDs == nil && ingressRules[i].SourceSecurityGroupRoles == nil { // if the rule doesn't have a source security group, use the control plane security group + ingressRules[i].SourceSecurityGroupIDs = []string{s.scope.SecurityGroups()[infrav1.SecurityGroupControlPlane].ID} continue } - for _, sourceSGRole := range additionalIngressRules[i].SourceSecurityGroupRoles { - additionalIngressRules[i].SourceSecurityGroupIDs = append(additionalIngressRules[i].SourceSecurityGroupIDs, s.scope.SecurityGroups()[sourceSGRole].ID) + for _, sourceSGRole := range ingressRules[i].SourceSecurityGroupRoles { + ingressRules[i].SourceSecurityGroupIDs = append(ingressRules[i].SourceSecurityGroupIDs, s.scope.SecurityGroups()[sourceSGRole].ID) } } - rules = append(rules, s.scope.ControlPlaneLoadBalancer().AdditionalIngressRules...) + rules = append(rules, s.scope.ControlPlaneLoadBalancer().IngressRules...) } return append(cniRules, rules...), nil @@ -577,23 +577,9 @@ func (s *Service) getSecurityGroupIngressRules(role infrav1.SecurityGroupRole) ( } return infrav1.IngressRules{}, nil case infrav1.SecurityGroupAPIServerLB: - rules := infrav1.IngressRules{ - { - Description: "Kubernetes API", - Protocol: infrav1.SecurityGroupProtocolTCP, - FromPort: int64(s.scope.APIServerPort()), - ToPort: int64(s.scope.APIServerPort()), - CidrBlocks: cidrBlocks, - }, - } - if s.scope.VPC().IsIPv6Enabled() { - rules = append(rules, infrav1.IngressRule{ - Description: "Kubernetes API IPv6", - Protocol: infrav1.SecurityGroupProtocolTCP, - FromPort: int64(s.scope.APIServerPort()), - ToPort: int64(s.scope.APIServerPort()), - IPv6CidrBlocks: []string{services.AnyIPv6CidrBlock}, - }) + rules := s.getDefaultIngressRulesForControlPlaneLB() + if s.scope.ControlPlaneLoadBalancer() != nil && len(s.scope.ControlPlaneLoadBalancer().IngressRules) > 0 { + rules = s.scope.ControlPlaneLoadBalancer().IngressRules } return rules, nil case infrav1.SecurityGroupLB: @@ -799,3 +785,27 @@ func ingressRulesFromSDKType(v *ec2.IpPermission) (res infrav1.IngressRules) { return res } + +func (s *Service) getDefaultIngressRulesForControlPlaneLB() infrav1.IngressRules { + if s.scope.VPC().IsIPv6Enabled() { + return infrav1.IngressRules{ + { + Description: "Kubernetes API IPv6", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: int64(s.scope.APIServerPort()), + ToPort: int64(s.scope.APIServerPort()), + IPv6CidrBlocks: []string{services.AnyIPv6CidrBlock}, + }, + } + } + + return infrav1.IngressRules{ + { + Description: "Kubernetes API", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: int64(s.scope.APIServerPort()), + ToPort: int64(s.scope.APIServerPort()), + CidrBlocks: []string{services.AnyIPv4CidrBlock}, + }, + } +} diff --git a/pkg/cloud/services/securitygroup/securitygroups_test.go b/pkg/cloud/services/securitygroup/securitygroups_test.go index af2c9e8f09..aec3407191 100644 --- a/pkg/cloud/services/securitygroup/securitygroups_test.go +++ b/pkg/cloud/services/securitygroup/securitygroups_test.go @@ -625,7 +625,7 @@ func TestAdditionalControlPlaneSecurityGroup(t *testing.T) { { name: "default control plane security group is used", controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{ - AdditionalIngressRules: []infrav1.IngressRule{ + IngressRules: []infrav1.IngressRule{ { Description: "test", Protocol: infrav1.SecurityGroupProtocolTCP, @@ -645,7 +645,7 @@ func TestAdditionalControlPlaneSecurityGroup(t *testing.T) { { name: "custom security group id is used", controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{ - AdditionalIngressRules: []infrav1.IngressRule{ + IngressRules: []infrav1.IngressRule{ { Description: "test", Protocol: infrav1.SecurityGroupProtocolTCP, @@ -666,7 +666,7 @@ func TestAdditionalControlPlaneSecurityGroup(t *testing.T) { { name: "another security group role is used", controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{ - AdditionalIngressRules: []infrav1.IngressRule{ + IngressRules: []infrav1.IngressRule{ { Description: "test", Protocol: infrav1.SecurityGroupProtocolTCP, @@ -687,7 +687,7 @@ func TestAdditionalControlPlaneSecurityGroup(t *testing.T) { { name: "another security group role and a custom security group id is used", controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{ - AdditionalIngressRules: []infrav1.IngressRule{ + IngressRules: []infrav1.IngressRule{ { Description: "test", Protocol: infrav1.SecurityGroupProtocolTCP, @@ -773,6 +773,109 @@ func TestAdditionalControlPlaneSecurityGroup(t *testing.T) { } } +func TestControlPlaneLoadBalancerIngressRules(t *testing.T) { + scheme := runtime.NewScheme() + _ = infrav1.AddToScheme(scheme) + + testCases := []struct { + name string + controlPlaneLBSpec *infrav1.AWSLoadBalancerSpec + useIPV6 bool + expectedIngresRules infrav1.IngressRules + }{ + { + name: "when no ingress rules are passed the default is set", + controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{}, + useIPV6: false, + expectedIngresRules: infrav1.IngressRules{ + infrav1.IngressRule{ + Description: "Kubernetes API", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: 6443, + ToPort: 6443, + CidrBlocks: []string{services.AnyIPv4CidrBlock}, + }, + }, + }, + { + name: "when no ingress rules are passed and when using ipv6, the default for ipv6 is set", + controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{}, + useIPV6: true, + expectedIngresRules: infrav1.IngressRules{ + infrav1.IngressRule{ + Description: "Kubernetes API IPv6", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: 6443, + ToPort: 6443, + IPv6CidrBlocks: []string{services.AnyIPv6CidrBlock}, + }, + }, + }, + { + name: "defined rules are used", + controlPlaneLBSpec: &infrav1.AWSLoadBalancerSpec{ + IngressRules: infrav1.IngressRules{ + { + Description: "My custom ingress rule", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: 1234, + ToPort: 1234, + CidrBlocks: []string{"172.126.1.1/0"}, + }, + }, + }, + useIPV6: false, + expectedIngresRules: infrav1.IngressRules{ + infrav1.IngressRule{ + Description: "My custom ingress rule", + Protocol: infrav1.SecurityGroupProtocolTCP, + FromPort: 1234, + ToPort: 1234, + CidrBlocks: []string{"172.126.1.1/0"}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + vpcSpec := infrav1.VPCSpec{} + if tc.useIPV6 { + vpcSpec = infrav1.VPCSpec{ + IPv6: &infrav1.IPv6{}, + } + } + + cs, err := scope.NewClusterScope(scope.ClusterScopeParams{ + Client: fake.NewClientBuilder().WithScheme(scheme).Build(), + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}, + }, + AWSCluster: &infrav1.AWSCluster{ + Spec: infrav1.AWSClusterSpec{ + ControlPlaneLoadBalancer: tc.controlPlaneLBSpec, + NetworkSpec: infrav1.NetworkSpec{ + VPC: vpcSpec, + }, + }, + }, + }) + if err != nil { + t.Fatalf("Failed to create test context: %v", err) + } + + s := NewService(cs, testSecurityGroupRoles) + rules, err := s.getSecurityGroupIngressRules(infrav1.SecurityGroupAPIServerLB) + if err != nil { + t.Fatalf("Failed to lookup controlplane load balancer security group ingress rules: %v", err) + } + + g := NewGomegaWithT(t) + g.Expect(rules).To(Equal(tc.expectedIngresRules)) + }) + } +} + func TestDeleteSecurityGroups(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish()