Skip to content

Commit

Permalink
feat(securityGroups): Improve robustness of security group reconcilers
Browse files Browse the repository at this point in the history
  • Loading branch information
Guido van der Hart committed Sep 11, 2024
1 parent 20beff9 commit 3b16deb
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 362 deletions.
2 changes: 1 addition & 1 deletion api/v1beta1/osccluster_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func ValidateOscClusterSpec(spec OscClusterSpec) field.ErrorList {
// ValidateCidr check that the cidr string is a valide CIDR
func ValidateCidr(cidr string) (string, error) {
if !strings.Contains(cidr, "/") {
return cidr, errors.New("Invalid Not A CIDR")
return cidr, errors.New("invalid Not A CIDR")
}
_, _, err := net.ParseCIDR(cidr)
if err != nil {
Expand Down
9 changes: 6 additions & 3 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ type OscNetwork struct {
// The subregion name
// + optional
SubregionName string `json:"subregionName,omitempty"`
// Add SecurityGroup Rule after the cluster is created
// + optional
ExtraSecurityGroupRule bool `json:"extraSecurityGroupRule,omitempty"`
}

type OscLoadBalancer struct {
Expand Down Expand Up @@ -219,6 +216,9 @@ type OscSecurityGroup struct {
// The description of the security group
// +optional
Description string `json:"description,omitempty"`
// Should the default allow all outbound rule be deleted
// +optional
DeleteDefaultOutboundRule bool `json:"deleteDefaultOutboundRule,omitempty"`
// The Security Group Rules configuration
// +optional
SecurityGroupRules []OscSecurityGroupRule `json:"securityGroupRules,omitempty"`
Expand Down Expand Up @@ -278,6 +278,9 @@ type OscSecurityGroupRule struct {
// The ip range of the security group rule
// +optional
IpRange string `json:"ipRange,omitempty"`
// The name of the security group to use as target
// +optional
TargetSecurityGroupName string `json:"targetSecurityGroupName,omitempty"`
// The beginning of the port range
// +optional
FromPortRange int32 `json:"fromPortRange,omitempty"`
Expand Down
10 changes: 0 additions & 10 deletions cloud/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,6 @@ func (s *ClusterScope) GetNet() *infrastructurev1beta1.OscNet {
return &s.OscCluster.Spec.Network.Net
}

// GetExtraSecurityGroupRule return the extraSecurityGroupRule
func (s *ClusterScope) GetExtraSecurityGroupRule() bool {
return s.OscCluster.Spec.Network.ExtraSecurityGroupRule
}

// SetExtraSecurityGroupRule set the extraSecurityGroupRule
func (s *ClusterScope) SetExtraSecurityGroupRule(extraSecurityGroupRule bool) {
s.OscCluster.Spec.Network.ExtraSecurityGroupRule = extraSecurityGroupRule
}

// GetPublicIpNameAfterBastion return publicIpNameAfterBastion
func (s *ClusterScope) GetPublicIpNameAfterBastion() bool {
return s.OscCluster.Spec.Network.Bastion.PublicIpNameAfterBastion
Expand Down
5 changes: 2 additions & 3 deletions cloud/services/security/mock_security/securitygroup_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 30 additions & 14 deletions cloud/services/security/securitygroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package security

import (
"fmt"
"net/http"

"errors"

Expand All @@ -36,7 +35,7 @@ type OscSecurityGroupInterface interface {
CreateSecurityGroup(netId string, clusterName string, securityGroupName string, securityGroupDescription string, securityGroupTag string) (*osc.SecurityGroup, error)
CreateSecurityGroupRule(securityGroupId string, flow string, ipProtocol string, ipRange string, securityGroupMemberId string, fromPortRange int32, toPortRange int32) (*osc.SecurityGroup, error)
DeleteSecurityGroupRule(securityGroupId string, flow string, ipProtocol string, ipRange string, securityGroupMemberId string, fromPortRange int32, toPortRange int32) error
DeleteSecurityGroup(securityGroupId string) (error, *http.Response)
DeleteSecurityGroup(securityGroupId string) error
GetSecurityGroup(securityGroupId string) (*osc.SecurityGroup, error)
GetSecurityGroupFromSecurityGroupRule(securityGroupId string, Flow string, IpProtocols string, IpRanges string, securityGroupMemberId string, FromPortRanges int32, ToPortRanges int32) (*osc.SecurityGroup, error)
GetSecurityGroupIdsFromNetIds(netId string) ([]string, error)
Expand Down Expand Up @@ -212,7 +211,7 @@ func (s *Service) DeleteSecurityGroupRule(securityGroupId string, flow string, i
oscApiClient := s.scope.GetApi()
oscAuthClient := s.scope.GetAuth()

deleteSecurityGroupCallBack := func() (bool, error) {
deleteSecurityGroupRuleCallBack := func() (bool, error) {
var httpRes *_nethttp.Response
var err error

Expand All @@ -233,26 +232,45 @@ func (s *Service) DeleteSecurityGroupRule(securityGroupId string, flow string, i
return true, err
}
backoff := reconciler.EnvBackoff()
waitErr := wait.ExponentialBackoff(backoff, deleteSecurityGroupCallBack)
waitErr := wait.ExponentialBackoff(backoff, deleteSecurityGroupRuleCallBack)
if waitErr != nil {
return waitErr
}
return nil
}

// DeleteSecurityGroup delete the securitygroup associated with the net
func (s *Service) DeleteSecurityGroup(securityGroupId string) (error, *http.Response) {
func (s *Service) DeleteSecurityGroup(securityGroupId string) error {
deleteSecurityGroupRequest := osc.DeleteSecurityGroupRequest{SecurityGroupId: &securityGroupId}
oscApiClient := s.scope.GetApi()
oscAuthClient := s.scope.GetAuth()
_, httpRes, err := oscApiClient.SecurityGroupApi.DeleteSecurityGroup(oscAuthClient).DeleteSecurityGroupRequest(deleteSecurityGroupRequest).Execute()
if err != nil {
if httpRes != nil {
fmt.Printf("Error with http result %s", httpRes.Status)
return err, httpRes

deleteSecurityGroupCallBack := func() (bool, error) {
var httpRes *_nethttp.Response
var err error

_, httpRes, err = oscApiClient.SecurityGroupApi.DeleteSecurityGroup(oscAuthClient).DeleteSecurityGroupRequest(deleteSecurityGroupRequest).Execute()
if err != nil {
if httpRes != nil {
return false, fmt.Errorf("error %w httpRes %s", err, httpRes.Status)
}
requestStr := fmt.Sprintf("%v", deleteSecurityGroupRequest)
if reconciler.KeepRetryWithError(
requestStr,
httpRes.StatusCode,
reconciler.ThrottlingErrors) {
return false, nil
}
return false, err
}
return true, err
}
backoff := reconciler.EnvBackoff()
waitErr := wait.ExponentialBackoff(backoff, deleteSecurityGroupCallBack)
if waitErr != nil {
return waitErr
}
return nil, httpRes
return nil
}

// GetSecurityGroup retrieve security group object from the security group id
Expand Down Expand Up @@ -308,9 +326,7 @@ func (s *Service) GetSecurityGroupFromSecurityGroupRule(securityGroupId string,
fromPortRanges = -1
toPortRanges = -1
}
if fromPortRanges == 53 && toPortRanges == 53 {
ipProtocols = "udp"
}

switch {
case flow == "Inbound":
readSecurityGroupRuleRequest = osc.ReadSecurityGroupsRequest{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.16.2
name: oscclusters.infrastructure.cluster.x-k8s.io
spec:
group: infrastructure.cluster.x-k8s.io
Expand Down Expand Up @@ -122,9 +122,6 @@ spec:
items:
type: string
type: array
extraSecurityGroupRule:
description: Add SecurityGroup Rule after the cluster is created
type: boolean
image:
description: The image configuration
properties:
Expand Down Expand Up @@ -329,6 +326,10 @@ spec:
securityGroups:
items:
properties:
deleteDefaultOutboundRule:
description: Should the default allow all outbound rule
be deleted
type: boolean
description:
description: The description of the security group
type: string
Expand Down Expand Up @@ -364,6 +365,10 @@ spec:
resourceId:
description: The security group rule id
type: string
targetSecurityGroupName:
description: The name of the security group to use
as target
type: string
toPortRange:
description: The end of the port range
format: int32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.16.2
name: oscclustertemplates.infrastructure.cluster.x-k8s.io
spec:
group: infrastructure.cluster.x-k8s.io
Expand Down Expand Up @@ -49,26 +49,22 @@ spec:
ObjectMeta is metadata that all persisted resources must have, which includes all objects
users must create. This is a copy of customizable fields from metav1.ObjectMeta.
ObjectMeta is embedded in `Machine.Spec`, `MachineDeployment.Template` and `MachineSet.Template`,
which are not top-level Kubernetes objects. Given that metav1.ObjectMeta has lots of special cases
and read-only fields which end up in the generated CRD validation, having it as a subset simplifies
the API and some issues that can impact user experience.
During the [upgrade to controller-tools@v2](https://github.com/kubernetes-sigs/cluster-api/pull/1054)
for v1alpha2, we noticed a failure would occur running Cluster API test suite against the new CRDs,
specifically `spec.metadata.creationTimestamp in body must be of type string: "null"`.
The investigation showed that `controller-tools@v2` behaves differently than its previous version
when handling types from [metav1](k8s.io/apimachinery/pkg/apis/meta/v1) package.
In more details, we found that embedded (non-top level) types that embedded `metav1.ObjectMeta`
had validation properties, including for `creationTimestamp` (metav1.Time).
The `metav1.Time` type specifies a custom json marshaller that, when IsZero() is true, returns `null`
which breaks validation because the field isn't marked as nullable.
In future versions, controller-tools@v2 might allow overriding the type and validation for embedded
types. When that happens, this hack should be revisited.
properties:
Expand Down Expand Up @@ -178,10 +174,6 @@ spec:
items:
type: string
type: array
extraSecurityGroupRule:
description: Add SecurityGroup Rule after the cluster
is created
type: boolean
image:
description: The image configuration
properties:
Expand Down Expand Up @@ -399,6 +391,10 @@ spec:
securityGroups:
items:
properties:
deleteDefaultOutboundRule:
description: Should the default allow all outbound
rule be deleted
type: boolean
description:
description: The description of the security group
type: string
Expand Down Expand Up @@ -436,6 +432,10 @@ spec:
resourceId:
description: The security group rule id
type: string
targetSecurityGroupName:
description: The name of the security group
to use as target
type: string
toPortRange:
description: The end of the port range
format: int32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.16.2
name: oscmachines.infrastructure.cluster.x-k8s.io
spec:
group: infrastructure.cluster.x-k8s.io
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.16.2
name: oscmachinetemplates.infrastructure.cluster.x-k8s.io
spec:
group: infrastructure.cluster.x-k8s.io
Expand Down Expand Up @@ -50,26 +50,22 @@ spec:
ObjectMeta is metadata that all persisted resources must have, which includes all objects
users must create. This is a copy of customizable fields from metav1.ObjectMeta.
ObjectMeta is embedded in `Machine.Spec`, `MachineDeployment.Template` and `MachineSet.Template`,
which are not top-level Kubernetes objects. Given that metav1.ObjectMeta has lots of special cases
and read-only fields which end up in the generated CRD validation, having it as a subset simplifies
the API and some issues that can impact user experience.
During the [upgrade to controller-tools@v2](https://github.com/kubernetes-sigs/cluster-api/pull/1054)
for v1alpha2, we noticed a failure would occur running Cluster API test suite against the new CRDs,
specifically `spec.metadata.creationTimestamp in body must be of type string: "null"`.
The investigation showed that `controller-tools@v2` behaves differently than its previous version
when handling types from [metav1](k8s.io/apimachinery/pkg/apis/meta/v1) package.
In more details, we found that embedded (non-top level) types that embedded `metav1.ObjectMeta`
had validation properties, including for `creationTimestamp` (metav1.Time).
The `metav1.Time` type specifies a custom json marshaller that, when IsZero() is true, returns `null`
which breaks validation because the field isn't marked as nullable.
In future versions, controller-tools@v2 might allow overriding the type and validation for embedded
types. When that happens, this hack should be revisited.
properties:
Expand Down
Loading

0 comments on commit 3b16deb

Please sign in to comment.