diff --git a/api/v1alpha5/conversion.go b/api/v1alpha5/conversion.go index b4855587e0..b80cf8fed6 100644 --- a/api/v1alpha5/conversion.go +++ b/api/v1alpha5/conversion.go @@ -522,3 +522,7 @@ func Convert_v1alpha5_Bastion_To_v1alpha8_Bastion(in *Bastion, out *infrav1.Bast in.Instance.FloatingIP = out.FloatingIP return nil } + +func Convert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(in *infrav1.SecurityGroupRule, out *SecurityGroupRule, s conversion.Scope) error { + return autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(in, out, s) +} diff --git a/api/v1alpha5/conversion_test.go b/api/v1alpha5/conversion_test.go index f8ad758fca..283e3efded 100644 --- a/api/v1alpha5/conversion_test.go +++ b/api/v1alpha5/conversion_test.go @@ -49,7 +49,7 @@ func TestConvertFrom(t *testing.T) { Spec: OpenStackClusterSpec{}, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - "cluster.x-k8s.io/conversion-data": "{\"spec\":{\"allowAllInClusterTraffic\":false,\"apiServerLoadBalancer\":{},\"cloudName\":\"\",\"controlPlaneEndpoint\":{\"host\":\"\",\"port\":0},\"disableAPIServerFloatingIP\":false,\"disableExternalNetwork\":false,\"externalNetwork\":{},\"managedSecurityGroups\":false,\"network\":{},\"subnet\":{}},\"status\":{\"ready\":false}}", + "cluster.x-k8s.io/conversion-data": "{\"spec\":{\"allowAllInClusterTraffic\":false,\"apiServerLoadBalancer\":{},\"cloudName\":\"\",\"controlPlaneEndpoint\":{\"host\":\"\",\"port\":0},\"disableAPIServerFloatingIP\":false,\"disableExternalNetwork\":false,\"externalNetwork\":{},\"network\":{},\"subnet\":{}},\"status\":{\"ready\":false}}", }, }, }, @@ -64,7 +64,7 @@ func TestConvertFrom(t *testing.T) { Spec: OpenStackClusterTemplateSpec{}, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - "cluster.x-k8s.io/conversion-data": "{\"spec\":{\"template\":{\"spec\":{\"allowAllInClusterTraffic\":false,\"apiServerLoadBalancer\":{},\"cloudName\":\"\",\"controlPlaneEndpoint\":{\"host\":\"\",\"port\":0},\"disableAPIServerFloatingIP\":false,\"disableExternalNetwork\":false,\"externalNetwork\":{},\"managedSecurityGroups\":false,\"network\":{},\"subnet\":{}}}}}", + "cluster.x-k8s.io/conversion-data": "{\"spec\":{\"template\":{\"spec\":{\"allowAllInClusterTraffic\":false,\"apiServerLoadBalancer\":{},\"cloudName\":\"\",\"controlPlaneEndpoint\":{\"host\":\"\",\"port\":0},\"disableAPIServerFloatingIP\":false,\"disableExternalNetwork\":false,\"externalNetwork\":{},\"network\":{},\"subnet\":{}}}}}", }, }, }, diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index af5bc6fcbf..6ef062873e 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -682,7 +682,7 @@ func autoConvert_v1alpha5_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (bool vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -733,7 +733,7 @@ func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec vs bool) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -774,9 +774,33 @@ func autoConvert_v1alpha5_OpenStackClusterStatus_To_v1alpha8_OpenStackClusterSta out.ExternalNetwork = nil } out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha5_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha5_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha5_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(v1alpha8.BastionStatus) @@ -814,9 +838,34 @@ func autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha5_OpenStackClusterSta // WARNING: in.Router requires manual conversion: does not exist in peer-type // WARNING: in.APIServerLoadBalancer requires manual conversion: does not exist in peer-type out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha5_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha5_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + // WARNING: in.AllNodesSecurityGroup requires manual conversion: does not exist in peer-type + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha5_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(Instance) @@ -1382,7 +1431,17 @@ func Convert_v1alpha8_Router_To_v1alpha5_Router(in *v1alpha8.Router, out *Router func autoConvert_v1alpha5_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, out *v1alpha8.SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]v1alpha8.SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1alpha8.SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha5_SecurityGroupRule_To_v1alpha8_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1394,7 +1453,17 @@ func Convert_v1alpha5_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, func autoConvert_v1alpha8_SecurityGroup_To_v1alpha5_SecurityGroup(in *v1alpha8.SecurityGroup, out *SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1467,14 +1536,10 @@ func autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(in *v1 out.Protocol = in.Protocol out.RemoteGroupID = in.RemoteGroupID out.RemoteIPPrefix = in.RemoteIPPrefix + // WARNING: in.RemoteManagedGroups requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule is an autogenerated conversion function. -func Convert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(in *v1alpha8.SecurityGroupRule, out *SecurityGroupRule, s conversion.Scope) error { - return autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha5_SecurityGroupRule(in, out, s) -} - func autoConvert_v1alpha5_Subnet_To_v1alpha8_Subnet(in *Subnet, out *v1alpha8.Subnet, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID diff --git a/api/v1alpha6/conversion.go b/api/v1alpha6/conversion.go index 2e2000cfd4..2bcd4e9b19 100644 --- a/api/v1alpha6/conversion.go +++ b/api/v1alpha6/conversion.go @@ -86,6 +86,10 @@ func restorev1alpha8ClusterStatus(previous *infrav1.OpenStackClusterStatus, dst if previous.Bastion != nil { dst.Bastion.ReferencedResources = previous.Bastion.ReferencedResources } + + if previous.AllNodesSecurityGroup != nil { + dst.AllNodesSecurityGroup = previous.AllNodesSecurityGroup + } } func restorev1alpha6ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackClusterSpec) { @@ -128,6 +132,11 @@ var v1alpha6OpenStackClusterRestorer = conversion.RestorerFor[*OpenStackCluster] } var v1alpha8OpenStackClusterRestorer = conversion.RestorerFor[*infrav1.OpenStackCluster]{ + "managedSecurityGroups": conversion.UnconditionalFieldRestorer( + func(c *infrav1.OpenStackCluster) **infrav1.SecurityGroupsSpec { + return &c.Spec.ManagedSecurityGroups + }, + ), "externalNetwork": conversion.UnconditionalFieldRestorer( func(c *infrav1.OpenStackCluster) *infrav1.NetworkFilter { return &c.Spec.ExternalNetwork @@ -208,6 +217,11 @@ var v1alpha6OpenStackClusterTemplateRestorer = conversion.RestorerFor[*OpenStack } var v1alpha8OpenStackClusterTemplateRestorer = conversion.RestorerFor[*infrav1.OpenStackClusterTemplate]{ + "managedSecurityGroups": conversion.UnconditionalFieldRestorer( + func(c *infrav1.OpenStackClusterTemplate) **infrav1.SecurityGroupsSpec { + return &c.Spec.Template.Spec.ManagedSecurityGroups + }, + ), "externalNetwork": conversion.UnconditionalFieldRestorer( func(c *infrav1.OpenStackClusterTemplate) *infrav1.NetworkFilter { return &c.Spec.Template.Spec.ExternalNetwork @@ -486,6 +500,10 @@ func Convert_v1alpha8_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in * out.ExternalNetworkID = in.ExternalNetwork.ID } + if in.ManagedSecurityGroups != nil && in.ManagedSecurityGroups.Enabled { + out.ManagedSecurityGroups = true + } + return nil } @@ -501,6 +519,12 @@ func Convert_v1alpha6_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in * } } + if in.ManagedSecurityGroups { + out.ManagedSecurityGroups = &infrav1.SecurityGroupsSpec{ + Enabled: true, + } + } + return nil } @@ -780,3 +804,12 @@ func Convert_v1alpha8_Bastion_To_v1alpha6_Bastion(in *infrav1.Bastion, out *Bast out.Instance.FloatingIP = in.FloatingIP return nil } + +func Convert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(in *infrav1.SecurityGroupRule, out *SecurityGroupRule, s apiconversion.Scope) error { + err := autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(in, out, s) + if err != nil { + return err + } + + return nil +} diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index 8a00f25e45..e563ff07a9 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -704,7 +704,7 @@ func autoConvert_v1alpha6_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (bool vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -756,7 +756,7 @@ func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec vs bool) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -797,9 +797,33 @@ func autoConvert_v1alpha6_OpenStackClusterStatus_To_v1alpha8_OpenStackClusterSta out.ExternalNetwork = nil } out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha6_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha6_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha6_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(v1alpha8.BastionStatus) @@ -837,9 +861,34 @@ func autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha6_OpenStackClusterSta // WARNING: in.Router requires manual conversion: does not exist in peer-type // WARNING: in.APIServerLoadBalancer requires manual conversion: does not exist in peer-type out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha6_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha6_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + // WARNING: in.AllNodesSecurityGroup requires manual conversion: does not exist in peer-type + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha6_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(Instance) @@ -1406,7 +1455,17 @@ func Convert_v1alpha8_Router_To_v1alpha6_Router(in *v1alpha8.Router, out *Router func autoConvert_v1alpha6_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, out *v1alpha8.SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]v1alpha8.SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1alpha8.SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha6_SecurityGroupRule_To_v1alpha8_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1418,7 +1477,17 @@ func Convert_v1alpha6_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, func autoConvert_v1alpha8_SecurityGroup_To_v1alpha6_SecurityGroup(in *v1alpha8.SecurityGroup, out *SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1491,14 +1560,10 @@ func autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(in *v1 out.Protocol = in.Protocol out.RemoteGroupID = in.RemoteGroupID out.RemoteIPPrefix = in.RemoteIPPrefix + // WARNING: in.RemoteManagedGroups requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule is an autogenerated conversion function. -func Convert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(in *v1alpha8.SecurityGroupRule, out *SecurityGroupRule, s conversion.Scope) error { - return autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha6_SecurityGroupRule(in, out, s) -} - func autoConvert_v1alpha6_Subnet_To_v1alpha8_Subnet(in *Subnet, out *v1alpha8.Subnet, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID diff --git a/api/v1alpha7/conversion.go b/api/v1alpha7/conversion.go index 31862c3f86..7c955e547a 100644 --- a/api/v1alpha7/conversion.go +++ b/api/v1alpha7/conversion.go @@ -63,6 +63,11 @@ var v1alpha8OpenStackClusterRestorer = conversion.RestorerFor[*infrav1.OpenStack return &c.Status.Bastion.ReferencedResources }, ), + "allNodesSecurityGroup": conversion.UnconditionalFieldRestorer( + func(c *infrav1.OpenStackCluster) **infrav1.SecurityGroup { + return &c.Status.AllNodesSecurityGroup + }, + ), } func restorev1alpha7MachineSpec(previous *OpenStackMachineSpec, dst *OpenStackMachineSpec) { @@ -97,6 +102,8 @@ func restorev1alpha8ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *inf dst.ExternalNetwork.NotTagsAny = previous.ExternalNetwork.NotTagsAny dst.DisableExternalNetwork = previous.DisableExternalNetwork + + dst.ManagedSecurityGroups = previous.ManagedSecurityGroups } func (r *OpenStackCluster) ConvertTo(dstRaw ctrlconversion.Hub) error { @@ -383,6 +390,12 @@ func Convert_v1alpha7_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in * } } + if in.ManagedSecurityGroups { + out.ManagedSecurityGroups = &infrav1.SecurityGroupsSpec{ + Enabled: true, + } + } + return nil } @@ -396,5 +409,27 @@ func Convert_v1alpha8_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in * out.ExternalNetworkID = in.ExternalNetwork.ID } + if in.ManagedSecurityGroups != nil && in.ManagedSecurityGroups.Enabled { + out.ManagedSecurityGroups = true + } + + return nil +} + +func Convert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(in *infrav1.OpenStackClusterStatus, out *OpenStackClusterStatus, s apiconversion.Scope) error { + err := autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(in, out, s) + if err != nil { + return err + } + + return nil +} + +func Convert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(in *infrav1.SecurityGroupRule, out *SecurityGroupRule, s apiconversion.Scope) error { + err := autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(in, out, s) + if err != nil { + return err + } + return nil } diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 2df0972e6e..dc51b99d43 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -189,11 +189,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1alpha8.OpenStackClusterStatus)(nil), (*OpenStackClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(a.(*v1alpha8.OpenStackClusterStatus), b.(*OpenStackClusterStatus), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*OpenStackClusterTemplate)(nil), (*v1alpha8.OpenStackClusterTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha7_OpenStackClusterTemplate_To_v1alpha8_OpenStackClusterTemplate(a.(*OpenStackClusterTemplate), b.(*v1alpha8.OpenStackClusterTemplate), scope) }); err != nil { @@ -439,6 +434,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha8.OpenStackClusterStatus)(nil), (*OpenStackClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(a.(*v1alpha8.OpenStackClusterStatus), b.(*OpenStackClusterStatus), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha8.OpenStackMachineSpec)(nil), (*OpenStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha8_OpenStackMachineSpec_To_v1alpha7_OpenStackMachineSpec(a.(*v1alpha8.OpenStackMachineSpec), b.(*OpenStackMachineSpec), scope) }); err != nil { @@ -896,7 +896,7 @@ func autoConvert_v1alpha7_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (bool vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -938,7 +938,7 @@ func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec( out.APIServerFloatingIP = in.APIServerFloatingIP out.APIServerFixedIP = in.APIServerFixedIP out.APIServerPort = in.APIServerPort - out.ManagedSecurityGroups = in.ManagedSecurityGroups + // WARNING: in.ManagedSecurityGroups requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8.SecurityGroupsSpec vs bool) out.AllowAllInClusterTraffic = in.AllowAllInClusterTraffic out.DisablePortSecurity = in.DisablePortSecurity out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) @@ -965,9 +965,33 @@ func autoConvert_v1alpha7_OpenStackClusterStatus_To_v1alpha8_OpenStackClusterSta out.Router = (*v1alpha8.Router)(unsafe.Pointer(in.Router)) out.APIServerLoadBalancer = (*v1alpha8.LoadBalancer)(unsafe.Pointer(in.APIServerLoadBalancer)) out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*v1alpha8.SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha7_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha7_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(v1alpha8.SecurityGroup) + if err := Convert_v1alpha7_SecurityGroup_To_v1alpha8_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(v1alpha8.BastionStatus) @@ -994,9 +1018,34 @@ func autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterSta out.Router = (*Router)(unsafe.Pointer(in.Router)) out.APIServerLoadBalancer = (*LoadBalancer)(unsafe.Pointer(in.APIServerLoadBalancer)) out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) - out.ControlPlaneSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.ControlPlaneSecurityGroup)) - out.WorkerSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.WorkerSecurityGroup)) - out.BastionSecurityGroup = (*SecurityGroup)(unsafe.Pointer(in.BastionSecurityGroup)) + if in.ControlPlaneSecurityGroup != nil { + in, out := &in.ControlPlaneSecurityGroup, &out.ControlPlaneSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha7_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlaneSecurityGroup = nil + } + if in.WorkerSecurityGroup != nil { + in, out := &in.WorkerSecurityGroup, &out.WorkerSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha7_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.WorkerSecurityGroup = nil + } + // WARNING: in.AllNodesSecurityGroup requires manual conversion: does not exist in peer-type + if in.BastionSecurityGroup != nil { + in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup + *out = new(SecurityGroup) + if err := Convert_v1alpha8_SecurityGroup_To_v1alpha7_SecurityGroup(*in, *out, s); err != nil { + return err + } + } else { + out.BastionSecurityGroup = nil + } if in.Bastion != nil { in, out := &in.Bastion, &out.Bastion *out = new(BastionStatus) @@ -1011,11 +1060,6 @@ func autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterSta return nil } -// Convert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus is an autogenerated conversion function. -func Convert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(in *v1alpha8.OpenStackClusterStatus, out *OpenStackClusterStatus, s conversion.Scope) error { - return autoConvert_v1alpha8_OpenStackClusterStatus_To_v1alpha7_OpenStackClusterStatus(in, out, s) -} - func autoConvert_v1alpha7_OpenStackClusterTemplate_To_v1alpha8_OpenStackClusterTemplate(in *OpenStackClusterTemplate, out *v1alpha8.OpenStackClusterTemplate, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha7_OpenStackClusterTemplateSpec_To_v1alpha8_OpenStackClusterTemplateSpec(&in.Spec, &out.Spec, s); err != nil { @@ -1553,7 +1597,17 @@ func Convert_v1alpha8_RouterFilter_To_v1alpha7_RouterFilter(in *v1alpha8.RouterF func autoConvert_v1alpha7_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, out *v1alpha8.SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]v1alpha8.SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1alpha8.SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha7_SecurityGroupRule_To_v1alpha8_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1565,7 +1619,17 @@ func Convert_v1alpha7_SecurityGroup_To_v1alpha8_SecurityGroup(in *SecurityGroup, func autoConvert_v1alpha8_SecurityGroup_To_v1alpha7_SecurityGroup(in *v1alpha8.SecurityGroup, out *SecurityGroup, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.Rules = *(*[]SecurityGroupRule)(unsafe.Pointer(&in.Rules)) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]SecurityGroupRule, len(*in)) + for i := range *in { + if err := Convert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Rules = nil + } return nil } @@ -1638,14 +1702,10 @@ func autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(in *v1 out.Protocol = in.Protocol out.RemoteGroupID = in.RemoteGroupID out.RemoteIPPrefix = in.RemoteIPPrefix + // WARNING: in.RemoteManagedGroups requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule is an autogenerated conversion function. -func Convert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(in *v1alpha8.SecurityGroupRule, out *SecurityGroupRule, s conversion.Scope) error { - return autoConvert_v1alpha8_SecurityGroupRule_To_v1alpha7_SecurityGroupRule(in, out, s) -} - func autoConvert_v1alpha7_Subnet_To_v1alpha8_Subnet(in *Subnet, out *v1alpha8.Subnet, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID diff --git a/api/v1alpha8/openstackcluster_types.go b/api/v1alpha8/openstackcluster_types.go index b0b6aaabcf..633bf62cfd 100644 --- a/api/v1alpha8/openstackcluster_types.go +++ b/api/v1alpha8/openstackcluster_types.go @@ -119,10 +119,11 @@ type OpenStackClusterSpec struct { // ManagedSecurityGroups determines whether OpenStack security groups for the cluster // will be managed by the OpenStack provider or whether pre-existing security groups will // be specified as part of the configuration. - // By default, the managed security groups have rules that allow the Kubelet, etcd, the - // Kubernetes API server and the Calico CNI plugin to function correctly. + // By default, the managed security groups have rules that allow the Kubelet, etcd and the + // Kubernetes API server to function correctly. + // It's possible to add additional rules to the managed security groups. // +optional - ManagedSecurityGroups bool `json:"managedSecurityGroups"` + ManagedSecurityGroups *SecurityGroupsSpec `json:"managedSecurityGroups,omitempty"` // AllowAllInClusterTraffic is only used when managed security groups are in use. // If set to true, the rules for the managed security groups are configured so that all @@ -192,6 +193,10 @@ type OpenStackClusterStatus struct { // Group that needs to be applied to worker nodes. WorkerSecurityGroup *SecurityGroup `json:"workerSecurityGroup,omitempty"` + // AllNodesSecurityGroup contains all the information about the OpenStack Security + // Group that needs to be applied to all nodes. + AllNodesSecurityGroup *SecurityGroup `json:"allNodesSecurityGroup,omitempty"` + BastionSecurityGroup *SecurityGroup `json:"bastionSecurityGroup,omitempty"` Bastion *BastionStatus `json:"bastion,omitempty"` diff --git a/api/v1alpha8/types.go b/api/v1alpha8/types.go index bdbf2155bf..f3f483ce4f 100644 --- a/api/v1alpha8/types.go +++ b/api/v1alpha8/types.go @@ -302,16 +302,27 @@ type SecurityGroup struct { // SecurityGroupRule represent the basic information of the associated OpenStack // Security Group Role. type SecurityGroupRule struct { - Description string `json:"description"` - ID string `json:"name"` - Direction string `json:"direction"` - EtherType string `json:"etherType"` - SecurityGroupID string `json:"securityGroupID"` - PortRangeMin int `json:"portRangeMin"` - PortRangeMax int `json:"portRangeMax"` - Protocol string `json:"protocol"` - RemoteGroupID string `json:"remoteGroupID"` - RemoteIPPrefix string `json:"remoteIPPrefix"` + Description string `json:"description"` + ID string `json:"name"` + Direction string `json:"direction"` + EtherType string `json:"etherType"` + SecurityGroupID string `json:"securityGroupID"` + PortRangeMin int `json:"portRangeMin"` + PortRangeMax int `json:"portRangeMax"` + Protocol string `json:"protocol"` + RemoteGroupID string `json:"remoteGroupID"` + RemoteIPPrefix string `json:"remoteIPPrefix"` + RemoteManagedGroups []string `json:"remoteManagedGroups"` +} + +// SecurityGroupsSpec defines the desired state of security groups and rules for the cluster. +type SecurityGroupsSpec struct { + // Enabled defines whether security groups should be created. + Enabled bool `json:"enabled,omitempty"` + + // AllNodesSecurityGroupRules defines the rules that should be applied to the security group that is applied to all nodes. + // +optional + AllNodesSecurityGroupRules []SecurityGroupRule `json:"AllNodesSecurityGroupRules,omitempty"` } // Equal checks if two SecurityGroupRules are the same. diff --git a/api/v1alpha8/zz_generated.deepcopy.go b/api/v1alpha8/zz_generated.deepcopy.go index 6598146562..2f92f4295b 100644 --- a/api/v1alpha8/zz_generated.deepcopy.go +++ b/api/v1alpha8/zz_generated.deepcopy.go @@ -385,6 +385,11 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { } out.ExternalNetwork = in.ExternalNetwork in.APIServerLoadBalancer.DeepCopyInto(&out.APIServerLoadBalancer) + if in.ManagedSecurityGroups != nil { + in, out := &in.ManagedSecurityGroups, &out.ManagedSecurityGroups + *out = new(SecurityGroupsSpec) + (*in).DeepCopyInto(*out) + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]string, len(*in)) @@ -458,6 +463,11 @@ func (in *OpenStackClusterStatus) DeepCopyInto(out *OpenStackClusterStatus) { *out = new(SecurityGroup) (*in).DeepCopyInto(*out) } + if in.AllNodesSecurityGroup != nil { + in, out := &in.AllNodesSecurityGroup, &out.AllNodesSecurityGroup + *out = new(SecurityGroup) + (*in).DeepCopyInto(*out) + } if in.BastionSecurityGroup != nil { in, out := &in.BastionSecurityGroup, &out.BastionSecurityGroup *out = new(SecurityGroup) @@ -1008,7 +1018,9 @@ func (in *SecurityGroup) DeepCopyInto(out *SecurityGroup) { if in.Rules != nil { in, out := &in.Rules, &out.Rules *out = make([]SecurityGroupRule, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } } @@ -1040,6 +1052,11 @@ func (in *SecurityGroupFilter) DeepCopy() *SecurityGroupFilter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecurityGroupRule) DeepCopyInto(out *SecurityGroupRule) { *out = *in + if in.RemoteManagedGroups != nil { + in, out := &in.RemoteManagedGroups, &out.RemoteManagedGroups + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityGroupRule. @@ -1052,6 +1069,28 @@ func (in *SecurityGroupRule) DeepCopy() *SecurityGroupRule { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityGroupsSpec) DeepCopyInto(out *SecurityGroupsSpec) { + *out = *in + if in.AllNodesSecurityGroupRules != nil { + in, out := &in.AllNodesSecurityGroupRules, &out.AllNodesSecurityGroupRules + *out = make([]SecurityGroupRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityGroupsSpec. +func (in *SecurityGroupsSpec) DeepCopy() *SecurityGroupsSpec { + if in == nil { + return nil + } + out := new(SecurityGroupsSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerGroupFilter) DeepCopyInto(out *ServerGroupFilter) { *out = *in diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml index c22d9fcea5..043ebc4833 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -5448,9 +5448,62 @@ spec: ManagedSecurityGroups determines whether OpenStack security groups for the cluster will be managed by the OpenStack provider or whether pre-existing security groups will be specified as part of the configuration. - By default, the managed security groups have rules that allow the Kubelet, etcd, the - Kubernetes API server and the Calico CNI plugin to function correctly. - type: boolean + By default, the managed security groups have rules that allow the Kubelet, etcd and the + Kubernetes API server to function correctly. + It's possible to add additional rules to the managed security groups. + properties: + AllNodesSecurityGroupRules: + description: AllNodesSecurityGroupRules defines the rules that + should be applied to the security group that is applied to all + nodes. + items: + description: |- + SecurityGroupRule represent the basic information of the associated OpenStack + Security Group Role. + properties: + description: + type: string + direction: + type: string + etherType: + type: string + name: + type: string + portRangeMax: + type: integer + portRangeMin: + type: integer + protocol: + type: string + remoteGroupID: + type: string + remoteIPPrefix: + type: string + remoteManagedGroups: + items: + type: string + type: array + securityGroupID: + type: string + required: + - description + - direction + - etherType + - name + - portRangeMax + - portRangeMin + - protocol + - remoteGroupID + - remoteIPPrefix + - remoteManagedGroups + - securityGroupID + type: object + type: array + enabled: + description: Enabled defines whether security groups should be + created. + type: boolean + type: object network: description: If NodeCIDR cannot be set this can be used to detect an existing network. @@ -5548,6 +5601,64 @@ spec: status: description: OpenStackClusterStatus defines the observed state of OpenStackCluster. properties: + allNodesSecurityGroup: + description: |- + AllNodesSecurityGroup contains all the information about the OpenStack Security + Group that needs to be applied to all nodes. + properties: + id: + type: string + name: + type: string + rules: + items: + description: |- + SecurityGroupRule represent the basic information of the associated OpenStack + Security Group Role. + properties: + description: + type: string + direction: + type: string + etherType: + type: string + name: + type: string + portRangeMax: + type: integer + portRangeMin: + type: integer + protocol: + type: string + remoteGroupID: + type: string + remoteIPPrefix: + type: string + remoteManagedGroups: + items: + type: string + type: array + securityGroupID: + type: string + required: + - description + - direction + - etherType + - name + - portRangeMax + - portRangeMin + - protocol + - remoteGroupID + - remoteIPPrefix + - remoteManagedGroups + - securityGroupID + type: object + type: array + required: + - id + - name + - rules + type: object apiServerLoadBalancer: description: APIServerLoadBalancer describes the api server load balancer if one exists @@ -5637,6 +5748,10 @@ spec: type: string remoteIPPrefix: type: string + remoteManagedGroups: + items: + type: string + type: array securityGroupID: type: string required: @@ -5649,6 +5764,7 @@ spec: - protocol - remoteGroupID - remoteIPPrefix + - remoteManagedGroups - securityGroupID type: object type: array @@ -5691,6 +5807,10 @@ spec: type: string remoteIPPrefix: type: string + remoteManagedGroups: + items: + type: string + type: array securityGroupID: type: string required: @@ -5703,6 +5823,7 @@ spec: - protocol - remoteGroupID - remoteIPPrefix + - remoteManagedGroups - securityGroupID type: object type: array @@ -5882,6 +6003,10 @@ spec: type: string remoteIPPrefix: type: string + remoteManagedGroups: + items: + type: string + type: array securityGroupID: type: string required: @@ -5894,6 +6019,7 @@ spec: - protocol - remoteGroupID - remoteIPPrefix + - remoteManagedGroups - securityGroupID type: object type: array diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml index 4f96c272aa..0c64483463 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2883,9 +2883,62 @@ spec: ManagedSecurityGroups determines whether OpenStack security groups for the cluster will be managed by the OpenStack provider or whether pre-existing security groups will be specified as part of the configuration. - By default, the managed security groups have rules that allow the Kubelet, etcd, the - Kubernetes API server and the Calico CNI plugin to function correctly. - type: boolean + By default, the managed security groups have rules that allow the Kubelet, etcd and the + Kubernetes API server to function correctly. + It's possible to add additional rules to the managed security groups. + properties: + AllNodesSecurityGroupRules: + description: AllNodesSecurityGroupRules defines the rules + that should be applied to the security group that is + applied to all nodes. + items: + description: |- + SecurityGroupRule represent the basic information of the associated OpenStack + Security Group Role. + properties: + description: + type: string + direction: + type: string + etherType: + type: string + name: + type: string + portRangeMax: + type: integer + portRangeMin: + type: integer + protocol: + type: string + remoteGroupID: + type: string + remoteIPPrefix: + type: string + remoteManagedGroups: + items: + type: string + type: array + securityGroupID: + type: string + required: + - description + - direction + - etherType + - name + - portRangeMax + - portRangeMin + - protocol + - remoteGroupID + - remoteIPPrefix + - remoteManagedGroups + - securityGroupID + type: object + type: array + enabled: + description: Enabled defines whether security groups should + be created. + type: boolean + type: object network: description: If NodeCIDR cannot be set this can be used to detect an existing network. diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index e954631e1d..b210826edd 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -450,7 +450,7 @@ func bastionToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, clusterNa } instanceSpec.SecurityGroups = openStackCluster.Spec.Bastion.Instance.SecurityGroups - if openStackCluster.Spec.ManagedSecurityGroups { + if openStackCluster.Spec.ManagedSecurityGroups.Enabled { if openStackCluster.Status.BastionSecurityGroup != nil { instanceSpec.SecurityGroups = append(instanceSpec.SecurityGroups, infrav1.SecurityGroupFilter{ ID: openStackCluster.Status.BastionSecurityGroup.ID, diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 77104d804a..eab5ebb11c 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -525,7 +525,7 @@ func machineToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, machine * instanceSpec.Tags = machineTags instanceSpec.SecurityGroups = openStackMachine.Spec.SecurityGroups - if openStackCluster.Spec.ManagedSecurityGroups { + if openStackCluster.Spec.ManagedSecurityGroups.Enabled { var managedSecurityGroup string if util.IsControlPlaneMachine(machine) { if openStackCluster.Status.ControlPlaneSecurityGroup != nil { diff --git a/controllers/openstackmachine_controller_test.go b/controllers/openstackmachine_controller_test.go index bc0c3b96c5..a8cf2a46f4 100644 --- a/controllers/openstackmachine_controller_test.go +++ b/controllers/openstackmachine_controller_test.go @@ -141,7 +141,7 @@ func Test_machineToInstanceSpec(t *testing.T) { name: "Control plane security group", openStackCluster: func() *infrav1.OpenStackCluster { c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = true + c.Spec.ManagedSecurityGroups.Enabled = true return c }, machine: func() *clusterv1.Machine { @@ -162,7 +162,7 @@ func Test_machineToInstanceSpec(t *testing.T) { name: "Worker security group", openStackCluster: func() *infrav1.OpenStackCluster { c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = true + c.Spec.ManagedSecurityGroups.Enabled = true return c }, machine: getDefaultMachine, @@ -177,7 +177,7 @@ func Test_machineToInstanceSpec(t *testing.T) { name: "Control plane security group not applied to worker", openStackCluster: func() *infrav1.OpenStackCluster { c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = true + c.Spec.ManagedSecurityGroups.Enabled = true c.Status.WorkerSecurityGroup = nil return c }, @@ -193,7 +193,7 @@ func Test_machineToInstanceSpec(t *testing.T) { name: "Worker security group not applied to control plane", openStackCluster: func() *infrav1.OpenStackCluster { c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = true + c.Spec.ManagedSecurityGroups.Enabled = true c.Status.ControlPlaneSecurityGroup = nil return c }, @@ -215,7 +215,7 @@ func Test_machineToInstanceSpec(t *testing.T) { name: "Extra security group", openStackCluster: func() *infrav1.OpenStackCluster { c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = true + c.Spec.ManagedSecurityGroups.Enabled = true return c }, machine: getDefaultMachine, diff --git a/pkg/cloud/services/networking/securitygroups.go b/pkg/cloud/services/networking/securitygroups.go index e2a5617de6..e01d8d9af4 100644 --- a/pkg/cloud/services/networking/securitygroups.go +++ b/pkg/cloud/services/networking/securitygroups.go @@ -32,22 +32,25 @@ const ( controlPlaneSuffix string = "controlplane" workerSuffix string = "worker" bastionSuffix string = "bastion" + allNodesSuffix string = "allNodes" remoteGroupIDSelf string = "self" ) // ReconcileSecurityGroups reconcile the security groups. func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackCluster, clusterName string) error { s.scope.Logger().Info("Reconciling security groups") - if !openStackCluster.Spec.ManagedSecurityGroups { + if !openStackCluster.Spec.ManagedSecurityGroups.Enabled { s.scope.Logger().V(4).Info("No need to reconcile security groups") return nil } secControlPlaneGroupName := getSecControlPlaneGroupName(clusterName) secWorkerGroupName := getSecWorkerGroupName(clusterName) + secAllNodesGroupName := getSecAllNodesGroupName(clusterName) secGroupNames := map[string]string{ controlPlaneSuffix: secControlPlaneGroupName, workerSuffix: secWorkerGroupName, + allNodesSuffix: secAllNodesGroupName, } if openStackCluster.Spec.Bastion != nil && openStackCluster.Spec.Bastion.Enabled { @@ -89,6 +92,7 @@ func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackClu openStackCluster.Status.ControlPlaneSecurityGroup = observedSecGroups[controlPlaneSuffix] openStackCluster.Status.WorkerSecurityGroup = observedSecGroups[workerSuffix] openStackCluster.Status.BastionSecurityGroup = observedSecGroups[bastionSuffix] + openStackCluster.Status.AllNodesSecurityGroup = observedSecGroups[allNodesSuffix] return nil } @@ -99,6 +103,13 @@ func (s *Service) generateDesiredSecGroups(openStackCluster *infrav1.OpenStackCl var secControlPlaneGroupID string var secWorkerGroupID string var secBastionGroupID string + + // remoteManagedGroups is a map of suffix to security group ID. + // It will be used to fill in the RemoteGroupID field of the security group rules + // that reference a managed security group. + remoteManagedGroups := make(map[string]string) + remoteManagedGroups[remoteGroupIDSelf] = remoteGroupIDSelf + for i, v := range secGroupNames { secGroup, err := s.getSecurityGroupByName(v) if err != nil { @@ -107,10 +118,13 @@ func (s *Service) generateDesiredSecGroups(openStackCluster *infrav1.OpenStackCl switch i { case controlPlaneSuffix: secControlPlaneGroupID = secGroup.ID + remoteManagedGroups[controlPlaneSuffix] = secControlPlaneGroupID case workerSuffix: secWorkerGroupID = secGroup.ID + remoteManagedGroups[workerSuffix] = secWorkerGroupID case bastionSuffix: secBastionGroupID = secGroup.ID + remoteManagedGroups[bastionSuffix] = secBastionGroupID } } @@ -167,9 +181,64 @@ func (s *Service) generateDesiredSecGroups(openStackCluster *infrav1.OpenStackCl Rules: workerRules, } + allNodesRules, err := getAllNodesRules(remoteManagedGroups, openStackCluster.Spec.ManagedSecurityGroups.AllNodesSecurityGroupRules) + if err != nil { + return desiredSecGroups, err + } + desiredSecGroups[allNodesSuffix] = infrav1.SecurityGroup{ + Name: secGroupNames[allNodesSuffix], + Rules: allNodesRules, + } + return desiredSecGroups, nil } +// getAllNodesRules returns the rules for the allNodes security group that should be created. +func getAllNodesRules(remoteManagedGroups map[string]string, allNodesSecurityGroupRules []infrav1.SecurityGroupRule) ([]infrav1.SecurityGroupRule, error) { + rules := make([]infrav1.SecurityGroupRule, 0, len(allNodesSecurityGroupRules)) + for _, rule := range allNodesSecurityGroupRules { + if err := validateAllNodesRule(rule, remoteManagedGroups); err != nil { + return nil, err + } + if rule.RemoteManagedGroups == nil { + for suffix := range remoteManagedGroups { + r := rule + r.RemoteGroupID = remoteManagedGroups[suffix] + rules = append(rules, r) + } + } else { + for _, suffix := range rule.RemoteManagedGroups { + r := rule + r.RemoteGroupID = remoteManagedGroups[suffix] + rules = append(rules, r) + } + } + } + return rules, nil +} + +// validateAllNodesRule validates that the rule is valid for the allNodes security group. +func validateAllNodesRule(rule infrav1.SecurityGroupRule, remoteManagedGroups map[string]string) error { + if rule.RemoteGroupID != "" { + return fmt.Errorf("allNodesRule: remoteGroupID must not be set") + } + if rule.RemoteIPPrefix != "" { + return fmt.Errorf("allNodesRule: remoteIPPrefix must not be set") + } + + return validateRemoteManagedGroups(remoteManagedGroups, rule.RemoteManagedGroups) +} + +// validateRemoteManagedGroups validates that the remoteManagedGroups target existing managed security groups. +func validateRemoteManagedGroups(remoteManagedGroups map[string]string, ruleRemoteManagedGroups []string) error { + for _, group := range ruleRemoteManagedGroups { + if _, ok := remoteManagedGroups[group]; !ok { + return fmt.Errorf("remoteManagedGroups: %s is not a valid remote managed security group", group) + } + } + return nil +} + func (s *Service) GetSecurityGroups(securityGroupParams []infrav1.SecurityGroupFilter) ([]string, error) { var sgIDs []string for _, sg := range securityGroupParams { @@ -411,6 +480,10 @@ func getSecBastionGroupName(clusterName string) string { return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, bastionSuffix) } +func getSecAllNodesGroupName(clusterName string) string { + return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, allNodesSuffix) +} + func convertOSSecGroupToConfigSecGroup(osSecGroup groups.SecGroup) *infrav1.SecurityGroup { securityGroupRules := make([]infrav1.SecurityGroupRule, len(osSecGroup.Rules)) for i, rule := range osSecGroup.Rules { diff --git a/pkg/cloud/services/networking/securitygroups_test.go b/pkg/cloud/services/networking/securitygroups_test.go new file mode 100644 index 0000000000..c50a5de187 --- /dev/null +++ b/pkg/cloud/services/networking/securitygroups_test.go @@ -0,0 +1,271 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package networking + +import ( + "reflect" + "testing" + + infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8" +) + +func TestValidateAllNodesRule(t *testing.T) { + tests := []struct { + name string + rule infrav1.SecurityGroupRule + remoteManagedGroups map[string]string + wantErr bool + }{ + { + name: "Empty rule", + rule: infrav1.SecurityGroupRule{}, + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + "bastion": "3", + }, + wantErr: false, + }, + { + name: "Invalid rule with remoteGroupID", + rule: infrav1.SecurityGroupRule{ + RemoteGroupID: "groupID", + }, + wantErr: true, + }, + { + name: "Invalid rule with remoteIPPrefix", + rule: infrav1.SecurityGroupRule{ + RemoteIPPrefix: "0.0.0.0/0", + }, + wantErr: true, + }, + { + name: "Invalid rule with unknown remoteManagedGroup", + rule: infrav1.SecurityGroupRule{ + RemoteManagedGroups: []string{"unknownGroup"}, + }, + wantErr: true, + }, + { + name: "Valid rule with remoteManagedGroups", + rule: infrav1.SecurityGroupRule{ + RemoteManagedGroups: []string{"controlplane", "worker", "bastion"}, + }, + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + "bastion": "3", + }, + wantErr: false, + }, + { + name: "Invalid rule with bastion in remoteManagedGroups", + rule: infrav1.SecurityGroupRule{ + RemoteManagedGroups: []string{"controlplane", "worker", "bastion"}, + }, + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateAllNodesRule(tt.rule, tt.remoteManagedGroups) + if (err != nil) != tt.wantErr { + t.Errorf("validateAllNodesRule() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGetAllNodesRules(t *testing.T) { + tests := []struct { + name string + remoteManagedGroups map[string]string + allNodesSecurityGroupRules []infrav1.SecurityGroupRule + wantRules []infrav1.SecurityGroupRule + wantErr bool + }{ + { + name: "Empty remoteManagedGroups and allNodesSecurityGroupRules", + remoteManagedGroups: map[string]string{}, + allNodesSecurityGroupRules: []infrav1.SecurityGroupRule{}, + wantRules: []infrav1.SecurityGroupRule{}, + wantErr: false, + }, + { + name: "Valid remoteManagedGroups and allNodesSecurityGroupRules", + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + }, + allNodesSecurityGroupRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + }, + }, + wantRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteGroupID: "self", + }, + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteGroupID: "1", + }, + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteGroupID: "2", + }, + }, + wantErr: false, + }, + { + name: "Valid remoteManagedGroups in a rule", + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + }, + allNodesSecurityGroupRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteManagedGroups: []string{"controlplane"}, + }, + }, + wantRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteGroupID: "1", + RemoteManagedGroups: []string{"controlplane"}, + }, + }, + }, + { + name: "Invalid allNodesSecurityGroupRules with wrong remoteManagedGroups", + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + "bastion": "3", + }, + allNodesSecurityGroupRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteManagedGroups: []string{ + "controlplanezzz", + "worker", + "bastion", + }, + }, + }, + wantRules: nil, + wantErr: true, + }, + { + name: "Invalid allNodesSecurityGroupRules with bastion while remoteManagedGroups does not have bastion", + remoteManagedGroups: map[string]string{ + "self": "self", + "controlplane": "1", + "worker": "2", + }, + allNodesSecurityGroupRules: []infrav1.SecurityGroupRule{ + { + Protocol: "tcp", + PortRangeMin: 22, + PortRangeMax: 22, + RemoteManagedGroups: []string{ + "bastion", + }, + }, + }, + wantRules: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotRules, err := getAllNodesRules(tt.remoteManagedGroups, tt.allNodesSecurityGroupRules) + if (err != nil) != tt.wantErr { + t.Errorf("getAllNodesRules() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotRules, tt.wantRules) { + t.Errorf("getAllNodesRules() gotRules = %v, want %v", gotRules, tt.wantRules) + } + }) + } +} + +func TestValidateRemoteManagedGroups(t *testing.T) { + remoteManagedGroups := map[string]string{ + "controlplane": "1", + "worker": "2", + "bastion": "3", + } + + tests := []struct { + name string + remoteManagedGroups map[string]string + ruleRemoteManagedGroups []string + wantErr bool + }{ + { + name: "Valid remoteManagedGroups", + remoteManagedGroups: remoteManagedGroups, + ruleRemoteManagedGroups: []string{"controlplane", "worker"}, + wantErr: false, + }, + { + name: "Invalid remoteManagedGroups", + remoteManagedGroups: remoteManagedGroups, + ruleRemoteManagedGroups: []string{"controlplane", "worker", "unknownGroup"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateRemoteManagedGroups(tt.remoteManagedGroups, tt.ruleRemoteManagedGroups) + if (err != nil) != tt.wantErr { + t.Errorf("validateRemoteManagedGroups() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}