diff --git a/api/v1alpha5/conversion.go b/api/v1alpha5/conversion.go index ddaf5d71ab..bfb8329cf4 100644 --- a/api/v1alpha5/conversion.go +++ b/api/v1alpha5/conversion.go @@ -196,6 +196,11 @@ func Convert_v1alpha8_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec(in * out.ExternalNetworkID = in.ExternalNetwork.ID } + if len(in.ManagedSubnets) > 0 { + out.NodeCIDR = in.ManagedSubnets[0].CIDR + out.DNSNameservers = in.ManagedSubnets[0].DNSNameservers + } + if in.Subnets != nil { if len(in.Subnets) >= 1 { if err := Convert_v1alpha8_SubnetFilter_To_v1alpha5_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { @@ -228,6 +233,16 @@ func Convert_v1alpha5_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in * out.Subnets = []infrav1.SubnetFilter{subnet} } + if len(in.NodeCIDR) > 0 { + out.ManagedSubnets = []infrav1.SubnetSpec{ + { + CIDR: in.NodeCIDR, + DNSNameservers: in.DNSNameservers, + }, + } + } + // We're dropping DNSNameservers even if these were set as without NodeCIDR it doesn't make sense. + return nil } diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index 20646768bf..c9e8b952a2 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -655,12 +655,12 @@ func Convert_v1alpha8_OpenStackClusterList_To_v1alpha5_OpenStackClusterList(in * func autoConvert_v1alpha5_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in *OpenStackClusterSpec, out *v1alpha8.OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.NodeCIDR requires manual conversion: does not exist in peer-type if err := Convert_v1alpha5_NetworkFilter_To_v1alpha8_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnet requires manual conversion: does not exist in peer-type - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) + // WARNING: in.DNSNameservers requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { in, out := &in.ExternalRouterIPs, &out.ExternalRouterIPs *out = make([]v1alpha8.ExternalRouterIPParam, len(*in)) @@ -701,14 +701,13 @@ func autoConvert_v1alpha5_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec(in *v1alpha8.OpenStackClusterSpec, out *OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.ManagedSubnets requires manual conversion: does not exist in peer-type // WARNING: in.Router requires manual conversion: does not exist in peer-type if err := Convert_v1alpha8_NetworkFilter_To_v1alpha5_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnets requires manual conversion: does not exist in peer-type // WARNING: in.NetworkMTU requires manual conversion: does not exist in peer-type - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) if in.ExternalRouterIPs != nil { in, out := &in.ExternalRouterIPs, &out.ExternalRouterIPs *out = make([]ExternalRouterIPParam, len(*in)) diff --git a/api/v1alpha6/conversion.go b/api/v1alpha6/conversion.go index a01430e935..177a2f0f8c 100644 --- a/api/v1alpha6/conversion.go +++ b/api/v1alpha6/conversion.go @@ -127,6 +127,11 @@ func restorev1alpha6ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackCl } } + // We only restore DNSNameservers when these were lossly converted when NodeCIDR is empty. + if len(previous.DNSNameservers) > 0 && dst.NodeCIDR == "" { + dst.DNSNameservers = previous.DNSNameservers + } + prevBastion := previous.Bastion dstBastion := dst.Bastion if prevBastion != nil && dstBastion != nil { @@ -545,6 +550,11 @@ func Convert_v1alpha8_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in * } } + if len(in.ManagedSubnets) > 0 { + out.NodeCIDR = in.ManagedSubnets[0].CIDR + out.DNSNameservers = in.ManagedSubnets[0].DNSNameservers + } + return nil } @@ -569,6 +579,16 @@ func Convert_v1alpha6_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in * out.Subnets = []infrav1.SubnetFilter{subnet} } + // DNSNameservers without NodeCIDR doesn't make sense, so we drop that. + if len(in.NodeCIDR) > 0 { + out.ManagedSubnets = []infrav1.SubnetSpec{ + { + CIDR: in.NodeCIDR, + DNSNameservers: in.DNSNameservers, + }, + } + } + return nil } diff --git a/api/v1alpha6/conversion_test.go b/api/v1alpha6/conversion_test.go index 5a2a9a6f6d..e6ed8f4c9e 100644 --- a/api/v1alpha6/conversion_test.go +++ b/api/v1alpha6/conversion_test.go @@ -132,6 +132,16 @@ func TestFuzzyConversion(t *testing.T) { } } }, + + func(spec *infrav1.SubnetSpec, c fuzz.Continue) { + c.FuzzNoCustom(spec) + + // CIDR is required and API validates that it's present, so + // we force it to always be set. + for spec.CIDR == "" { + spec.CIDR = c.RandString() + } + }, } } @@ -156,6 +166,7 @@ func TestFuzzyConversion(t *testing.T) { Hub: &infrav1.OpenStackClusterTemplate{}, Spoke: &OpenStackClusterTemplate{}, HubAfterMutation: ignoreDataAnnotation, + FuzzerFuncs: []fuzzer.FuzzerFuncs{fuzzerFuncs}, }))) t.Run("for OpenStackClusterTemplate with mutate", runParallel(testhelpers.FuzzMutateTestFunc(testhelpers.FuzzMutateTestFuncInput{ diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index 0589be11fb..08c25e97cf 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -677,12 +677,12 @@ func Convert_v1alpha8_OpenStackClusterList_To_v1alpha6_OpenStackClusterList(in * func autoConvert_v1alpha6_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in *OpenStackClusterSpec, out *v1alpha8.OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.NodeCIDR requires manual conversion: does not exist in peer-type if err := Convert_v1alpha6_NetworkFilter_To_v1alpha8_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnet requires manual conversion: does not exist in peer-type - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) + // WARNING: in.DNSNameservers requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { in, out := &in.ExternalRouterIPs, &out.ExternalRouterIPs *out = make([]v1alpha8.ExternalRouterIPParam, len(*in)) @@ -724,14 +724,13 @@ func autoConvert_v1alpha6_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in *v1alpha8.OpenStackClusterSpec, out *OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.ManagedSubnets requires manual conversion: does not exist in peer-type // WARNING: in.Router requires manual conversion: does not exist in peer-type if err := Convert_v1alpha8_NetworkFilter_To_v1alpha6_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnets requires manual conversion: does not exist in peer-type // WARNING: in.NetworkMTU requires manual conversion: does not exist in peer-type - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) if in.ExternalRouterIPs != nil { in, out := &in.ExternalRouterIPs, &out.ExternalRouterIPs *out = make([]ExternalRouterIPParam, len(*in)) diff --git a/api/v1alpha7/conversion.go b/api/v1alpha7/conversion.go index df31c7d81c..9bfa6ad90d 100644 --- a/api/v1alpha7/conversion.go +++ b/api/v1alpha7/conversion.go @@ -39,6 +39,24 @@ var v1alpha7OpenStackClusterRestorer = conversion.RestorerFor[*OpenStackCluster] }, restorev1alpha7Bastion, ), + "spec": conversion.HashedFieldRestorer( + func(c *OpenStackCluster) *OpenStackClusterSpec { + return &c.Spec + }, + restorev1alpha7ClusterSpec, + + // Filter out Bastion, which is restored separately + conversion.HashedFilterField[*OpenStackCluster, OpenStackClusterSpec]( + func(s *OpenStackClusterSpec) *OpenStackClusterSpec { + if s.Bastion != nil { + f := *s + f.Bastion = nil + return &f + } + return s + }, + ), + ), } var v1alpha8OpenStackClusterRestorer = conversion.RestorerFor[*infrav1.OpenStackCluster]{ @@ -111,6 +129,19 @@ func restorev1alpha8Bastion(previous **infrav1.Bastion, dst **infrav1.Bastion) { } } +func restorev1alpha7ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackClusterSpec) { + prevBastion := previous.Bastion + dstBastion := dst.Bastion + if prevBastion != nil && dstBastion != nil { + restorev1alpha7MachineSpec(&prevBastion.Instance, &dstBastion.Instance) + } + + // We only restore DNSNameservers when these were lossly converted when NodeCIDR is empty. + if len(previous.DNSNameservers) > 0 && dst.NodeCIDR == "" { + dst.DNSNameservers = previous.DNSNameservers + } +} + func restorev1alpha8ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *infrav1.OpenStackClusterSpec) { prevBastion := previous.Bastion dstBastion := dst.Bastion @@ -132,6 +163,8 @@ func restorev1alpha8ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *inf if len(previous.Subnets) > 1 { dst.Subnets = append(dst.Subnets, previous.Subnets[1:]...) } + + dst.ManagedSubnets = previous.ManagedSubnets } func (r *OpenStackCluster) ConvertTo(dstRaw ctrlconversion.Hub) error { @@ -171,6 +204,7 @@ func (r *OpenStackClusterList) ConvertFrom(srcRaw ctrlconversion.Hub) error { var _ ctrlconversion.Convertible = &OpenStackClusterTemplate{} func restorev1alpha7ClusterTemplateSpec(previous *OpenStackClusterTemplateSpec, dst *OpenStackClusterTemplateSpec) { + restorev1alpha7ClusterSpec(&previous.Template.Spec, &dst.Template.Spec) restorev1alpha7Bastion(&previous.Template.Spec.Bastion, &dst.Template.Spec.Bastion) } @@ -465,6 +499,16 @@ func Convert_v1alpha7_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in * out.Subnets = []infrav1.SubnetFilter{subnet} } + // DNSNameservers without NodeCIDR doesn't make sense, so we drop that. + if len(in.NodeCIDR) > 0 { + out.ManagedSubnets = []infrav1.SubnetSpec{ + { + CIDR: in.NodeCIDR, + DNSNameservers: in.DNSNameservers, + }, + } + } + return nil } @@ -484,5 +528,10 @@ func Convert_v1alpha8_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in * } } + if len(in.ManagedSubnets) > 0 { + out.NodeCIDR = in.ManagedSubnets[0].CIDR + out.DNSNameservers = in.ManagedSubnets[0].DNSNameservers + } + return nil } diff --git a/api/v1alpha7/conversion_test.go b/api/v1alpha7/conversion_test.go index c57cc5d163..97763d7d4b 100644 --- a/api/v1alpha7/conversion_test.go +++ b/api/v1alpha7/conversion_test.go @@ -100,6 +100,16 @@ func TestFuzzyConversion(t *testing.T) { } } }, + + func(spec *infrav1.SubnetSpec, c fuzz.Continue) { + c.FuzzNoCustom(spec) + + // CIDR is required and API validates that it's present, so + // we force it to always be set. + for spec.CIDR == "" { + spec.CIDR = c.RandString() + } + }, } } diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 42a17e16eb..de0e42be72 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -877,14 +877,14 @@ func Convert_v1alpha8_OpenStackClusterList_To_v1alpha7_OpenStackClusterList(in * func autoConvert_v1alpha7_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec(in *OpenStackClusterSpec, out *v1alpha8.OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.NodeCIDR requires manual conversion: does not exist in peer-type out.Router = (*v1alpha8.RouterFilter)(unsafe.Pointer(in.Router)) if err := Convert_v1alpha7_NetworkFilter_To_v1alpha8_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnet requires manual conversion: does not exist in peer-type out.NetworkMTU = in.NetworkMTU - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) + // WARNING: in.DNSNameservers requires manual conversion: does not exist in peer-type out.ExternalRouterIPs = *(*[]v1alpha8.ExternalRouterIPParam)(unsafe.Pointer(&in.ExternalRouterIPs)) // WARNING: in.ExternalNetworkID requires manual conversion: does not exist in peer-type if err := Convert_v1alpha7_APIServerLoadBalancer_To_v1alpha8_APIServerLoadBalancer(&in.APIServerLoadBalancer, &out.APIServerLoadBalancer, s); err != nil { @@ -916,14 +916,13 @@ func autoConvert_v1alpha7_OpenStackClusterSpec_To_v1alpha8_OpenStackClusterSpec( func autoConvert_v1alpha8_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in *v1alpha8.OpenStackClusterSpec, out *OpenStackClusterSpec, s conversion.Scope) error { out.CloudName = in.CloudName - out.NodeCIDR = in.NodeCIDR + // WARNING: in.ManagedSubnets requires manual conversion: does not exist in peer-type out.Router = (*RouterFilter)(unsafe.Pointer(in.Router)) if err := Convert_v1alpha8_NetworkFilter_To_v1alpha7_NetworkFilter(&in.Network, &out.Network, s); err != nil { return err } // WARNING: in.Subnets requires manual conversion: does not exist in peer-type out.NetworkMTU = in.NetworkMTU - out.DNSNameservers = *(*[]string)(unsafe.Pointer(&in.DNSNameservers)) out.ExternalRouterIPs = *(*[]ExternalRouterIPParam)(unsafe.Pointer(&in.ExternalRouterIPs)) // WARNING: in.ExternalNetwork requires manual conversion: does not exist in peer-type // WARNING: in.DisableExternalNetwork requires manual conversion: does not exist in peer-type diff --git a/api/v1alpha8/openstackcluster_types.go b/api/v1alpha8/openstackcluster_types.go index a669c8317d..7002daf3dc 100644 --- a/api/v1alpha8/openstackcluster_types.go +++ b/api/v1alpha8/openstackcluster_types.go @@ -34,10 +34,11 @@ type OpenStackClusterSpec struct { // +optional CloudName string `json:"cloudName"` - // NodeCIDR is the OpenStack Subnet to be created. Cluster actuator will create a - // network, a subnet with NodeCIDR, and a router connected to this subnet. - // If you leave this empty, no network will be created. - NodeCIDR string `json:"nodeCidr,omitempty"` + // ManagedSubnets describe OpenStack Subnets to be created. Cluster actuator will create a network, + // subnets with the defined CIDR, and a router connected to these subnets. Currently only one IPv4 + // subnet is supported. If you leave this empty, no network will be created. + // +kubebuilder:validation:MaxItems=1 + ManagedSubnets []SubnetSpec `json:"managedSubnets,omitempty"` // If NodeCIDR is set this option can be used to detect an existing router. // If specified, no new router will be created. @@ -58,11 +59,6 @@ type OpenStackClusterSpec struct { // +optional NetworkMTU int `json:"networkMtu,omitempty"` - // DNSNameservers is the list of nameservers for OpenStack Subnet being created. - // Set this value when you need create a new network/subnet while the access - // through DNS is required. - // +listType=set - DNSNameservers []string `json:"dnsNameservers,omitempty"` // ExternalRouterIPs is an array of externalIPs on the respective subnets. // This is necessary if the router needs a fixed ip in a specific subnet. ExternalRouterIPs []ExternalRouterIPParam `json:"externalRouterIPs,omitempty"` diff --git a/api/v1alpha8/types.go b/api/v1alpha8/types.go index bdbf2155bf..1278f83639 100644 --- a/api/v1alpha8/types.go +++ b/api/v1alpha8/types.go @@ -87,6 +87,16 @@ type RouterFilter struct { NotTagsAny string `json:"notTagsAny,omitempty"` } +type SubnetSpec struct { + // CIDR is representing the IP address range used to create the subnet, e.g. 10.0.0.0/24. + // This field is required when defining a subnet. + // +required + CIDR string `json:"cidr"` + // DNSNameservers holds a list of DNS server addresses that will be provided when creating + // the subnet. These addresses need to have the same IP version as CIDR. + DNSNameservers []string `json:"dnsNameservers,omitempty"` +} + type PortOpts struct { // Network is a query for an openstack network that the port will be created or discovered on. // This will fail if the query returns more than one network. diff --git a/api/v1alpha8/zz_generated.deepcopy.go b/api/v1alpha8/zz_generated.deepcopy.go index df370f8983..5be338cb7f 100644 --- a/api/v1alpha8/zz_generated.deepcopy.go +++ b/api/v1alpha8/zz_generated.deepcopy.go @@ -366,6 +366,13 @@ func (in *OpenStackClusterList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { *out = *in + if in.ManagedSubnets != nil { + in, out := &in.ManagedSubnets, &out.ManagedSubnets + *out = make([]SubnetSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Router != nil { in, out := &in.Router, &out.Router *out = new(RouterFilter) @@ -377,11 +384,6 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { *out = make([]SubnetFilter, len(*in)) copy(*out, *in) } - if in.DNSNameservers != nil { - in, out := &in.DNSNameservers, &out.DNSNameservers - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.ExternalRouterIPs != nil { in, out := &in.ExternalRouterIPs, &out.ExternalRouterIPs *out = make([]ExternalRouterIPParam, len(*in)) @@ -1119,6 +1121,26 @@ func (in *SubnetFilter) DeepCopy() *SubnetFilter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { + *out = *in + if in.DNSNameservers != nil { + in, out := &in.DNSNameservers, &out.DNSNameservers + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSpec. +func (in *SubnetSpec) DeepCopy() *SubnetSpec { + if in == nil { + return nil + } + out := new(SubnetSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValueSpec) DeepCopyInto(out *ValueSpec) { *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 0e929c11c8..31c214580b 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -5366,15 +5366,6 @@ spec: DisablePortSecurity disables the port security of the network created for the Kubernetes cluster, which also disables SecurityGroups type: boolean - dnsNameservers: - description: |- - DNSNameservers is the list of nameservers for OpenStack Subnet being created. - Set this value when you need create a new network/subnet while the access - through DNS is required. - items: - type: string - type: array - x-kubernetes-list-type: set externalNetwork: description: ExternalNetwork is the OpenStack Network to be used to get public internet to the VMs. @@ -5468,6 +5459,30 @@ spec: 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 + managedSubnets: + description: |- + ManagedSubnets describe OpenStack Subnets to be created. Cluster actuator will create a network, + subnets with the defined CIDR, and a router connected to these subnets. Currently only one IPv4 + subnet is supported. If you leave this empty, no network will be created. + items: + properties: + cidr: + description: |- + CIDR is representing the IP address range used to create the subnet, e.g. 10.0.0.0/24. + This field is required when defining a subnet. + type: string + dnsNameservers: + description: |- + DNSNameservers holds a list of DNS server addresses that will be provided when creating + the subnet. These addresses need to have the same IP version as CIDR. + items: + type: string + type: array + required: + - cidr + type: object + maxItems: 1 + type: array network: description: If NodeCIDR cannot be set this can be used to detect an existing network. @@ -5496,12 +5511,6 @@ spec: If leaved empty, the network will have the default MTU defined in Openstack network service. To use this field, the Openstack installation requires the net-mtu neutron API extension. type: integer - nodeCidr: - description: |- - NodeCIDR is the OpenStack Subnet to be created. Cluster actuator will create a - network, a subnet with NodeCIDR, and a router connected to this subnet. - If you leave this empty, no network will be created. - type: string router: description: |- If NodeCIDR is set this option can be used to detect an existing router. 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 ae5555b4a2..b9c154dc7f 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2801,15 +2801,6 @@ spec: DisablePortSecurity disables the port security of the network created for the Kubernetes cluster, which also disables SecurityGroups type: boolean - dnsNameservers: - description: |- - DNSNameservers is the list of nameservers for OpenStack Subnet being created. - Set this value when you need create a new network/subnet while the access - through DNS is required. - items: - type: string - type: array - x-kubernetes-list-type: set externalNetwork: description: ExternalNetwork is the OpenStack Network to be used to get public internet to the VMs. @@ -2903,6 +2894,30 @@ spec: 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 + managedSubnets: + description: |- + ManagedSubnets describe OpenStack Subnets to be created. Cluster actuator will create a network, + subnets with the defined CIDR, and a router connected to these subnets. Currently only one IPv4 + subnet is supported. If you leave this empty, no network will be created. + items: + properties: + cidr: + description: |- + CIDR is representing the IP address range used to create the subnet, e.g. 10.0.0.0/24. + This field is required when defining a subnet. + type: string + dnsNameservers: + description: |- + DNSNameservers holds a list of DNS server addresses that will be provided when creating + the subnet. These addresses need to have the same IP version as CIDR. + items: + type: string + type: array + required: + - cidr + type: object + maxItems: 1 + type: array network: description: If NodeCIDR cannot be set this can be used to detect an existing network. @@ -2931,12 +2946,6 @@ spec: If leaved empty, the network will have the default MTU defined in Openstack network service. To use this field, the Openstack installation requires the net-mtu neutron API extension. type: integer - nodeCidr: - description: |- - NodeCIDR is the OpenStack Subnet to be created. Cluster actuator will create a - network, a subnet with NodeCIDR, and a router connected to this subnet. - If you leave this empty, no network will be created. - type: string router: description: |- If NodeCIDR is set this option can be used to detect an existing router. diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index d63a6692da..661d399434 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -183,8 +183,8 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope return reconcile.Result{}, fmt.Errorf("failed to delete security groups: %w", err) } - // if NodeCIDR was not set, no network was created. - if openStackCluster.Spec.NodeCIDR != "" { + // if ManagedSubnets was not set, no network was created. + if len(openStackCluster.Spec.ManagedSubnets) == 0 { if err = networkingService.DeleteRouter(openStackCluster, clusterName); err != nil { handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to delete router: %w", err)) return ctrl.Result{}, fmt.Errorf("failed to delete router: %w", err) @@ -504,7 +504,7 @@ func reconcileNetworkComponents(scope scope.Scope, cluster *clusterv1.Cluster, o return fmt.Errorf("failed to reconcile external network: %w", err) } - if openStackCluster.Spec.NodeCIDR == "" { + if len(openStackCluster.Spec.ManagedSubnets) == 0 { scope.Logger().V(4).Info("No need to reconcile network, searching network and subnet instead") netOpts := openStackCluster.Spec.Network.ToListOpt() @@ -537,7 +537,7 @@ func reconcileNetworkComponents(scope scope.Scope, cluster *clusterv1.Cluster, o return err } openStackCluster.Status.Network.Subnets = subnets - } else { + } else if len(openStackCluster.Spec.ManagedSubnets) == 1 { err := networkingService.ReconcileNetwork(openStackCluster, clusterName) if err != nil { handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile network: %w", err)) @@ -553,6 +553,8 @@ func reconcileNetworkComponents(scope scope.Scope, cluster *clusterv1.Cluster, o handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile router: %w", err)) return fmt.Errorf("failed to reconcile router: %w", err) } + } else { + return fmt.Errorf("failed to reconcile network: ManagedSubnets only supports one element, %d provided", len(openStackCluster.Spec.ManagedSubnets)) } err = networkingService.ReconcileSecurityGroups(openStackCluster, clusterName) diff --git a/docs/book/src/clusteropenstack/configuration.md b/docs/book/src/clusteropenstack/configuration.md index 3ae013333f..46c7b00363 100644 --- a/docs/book/src/clusteropenstack/configuration.md +++ b/docs/book/src/clusteropenstack/configuration.md @@ -247,7 +247,7 @@ prevent a floating IP from being allocated. > This can be a project-specific network, if the management cluster lives in the same project > as the workload cluster, or a network that is shared across multiple projects. > -> In particular, this means that the cluster **cannot** use `OpenStackCluster.spec.nodeCidr` +> In particular, this means that the cluster **cannot** use `OpenStackCluster.spec.managedSubnets` > to provision a new network for the cluster. Instead, use `OpenStackCluster.spec.network` > to explicitly specify the same network as the management cluster is on. diff --git a/docs/book/src/topics/crd-changes/v1alpha7-to-v1alpha8.md b/docs/book/src/topics/crd-changes/v1alpha7-to-v1alpha8.md index efcf44d169..37df012a8c 100644 --- a/docs/book/src/topics/crd-changes/v1alpha7-to-v1alpha8.md +++ b/docs/book/src/topics/crd-changes/v1alpha7-to-v1alpha8.md @@ -176,4 +176,23 @@ In v1alpha8, this will be automatically converted to: `Subnets` allows specifications of maximum two `SubnetFilter` one being IPv4 and the other IPv6. Both subnets must be on the same network. Any filtered subnets will be added to `OpenStackCluster.Status.Network.Subnets`. -When subnets are not specified on `OpenStackCluster` and only the network is, the network is used to identify the subnets to use. If more than two subnets exist in the network, the user must specify which ones to use by defining the `OpenStackCluster.Spec.Subnets` field. \ No newline at end of file +When subnets are not specified on `OpenStackCluster` and only the network is, the network is used to identify the subnets to use. If more than two subnets exist in the network, the user must specify which ones to use by defining the `OpenStackCluster.Spec.Subnets` field. + +#### ⚠️ Change to nodeCidr and dnsNameservers + +In v1alpha8, `OpenStackCluster.Spec.ManagedSubnets` array field is introduced. The `NodeCIDR` and `DNSNameservers` fields of `OpenStackCluster.Spec` are moved into that structure (renaming `NodeCIDR` to `CIDR`). For example: + +```yaml + nodeCidr: "10.0.0.0/24" + dnsNameservers: "10.0.0.123" +``` + +In v1alpha8, this will be automatically converted to: + +```yaml + managedSubnets: + - cidr: "10.0.0.0/24" + dnsNameservers: "10.0.0.123" +``` + +Please note that currently `managedSubnets` can only hold one element. \ No newline at end of file diff --git a/kustomize/v1alpha8/default/cluster-template.yaml b/kustomize/v1alpha8/default/cluster-template.yaml index 0dc7472f18..5482f50448 100644 --- a/kustomize/v1alpha8/default/cluster-template.yaml +++ b/kustomize/v1alpha8/default/cluster-template.yaml @@ -29,9 +29,10 @@ spec: apiServerLoadBalancer: enabled: true managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 - dnsNameservers: - - ${OPENSTACK_DNS_NAMESERVERS} + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - ${OPENSTACK_DNS_NAMESERVERS} externalNetwork: id: ${OPENSTACK_EXTERNAL_NETWORK_ID} --- diff --git a/pkg/cloud/services/networking/network.go b/pkg/cloud/services/networking/network.go index 5eaf691471..540dce22f4 100644 --- a/pkg/cloud/services/networking/network.go +++ b/pkg/cloud/services/networking/network.go @@ -211,14 +211,16 @@ func (s *Service) ReconcileSubnet(openStackCluster *infrav1.OpenStackCluster, cl subnetList, err := s.client.ListSubnet(subnets.ListOpts{ NetworkID: openStackCluster.Status.Network.ID, - CIDR: openStackCluster.Spec.NodeCIDR, + // Currently we only support 1 SubnetSpec. + CIDR: openStackCluster.Spec.ManagedSubnets[0].CIDR, }) if err != nil { return err } if len(subnetList) > 1 { - return fmt.Errorf("found %d subnets with the name %s, which should not happen", len(subnetList), subnetName) + return fmt.Errorf("found %d subnets with the CIDR %s and network %s, which should not happen", + len(subnetList), openStackCluster.Spec.ManagedSubnets[0], openStackCluster.Status.Network.ID) } var subnet *subnets.Subnet @@ -249,8 +251,8 @@ func (s *Service) createSubnet(openStackCluster *infrav1.OpenStackCluster, clust NetworkID: openStackCluster.Status.Network.ID, Name: name, IPVersion: 4, - CIDR: openStackCluster.Spec.NodeCIDR, - DNSNameservers: openStackCluster.Spec.DNSNameservers, + CIDR: openStackCluster.Spec.ManagedSubnets[0].CIDR, + DNSNameservers: openStackCluster.Spec.ManagedSubnets[0].DNSNameservers, Description: names.GetDescription(clusterName), } diff --git a/templates/cluster-template-flatcar-sysext.yaml b/templates/cluster-template-flatcar-sysext.yaml index 3994a67311..8e2f89ea2a 100644 --- a/templates/cluster-template-flatcar-sysext.yaml +++ b/templates/cluster-template-flatcar-sysext.yaml @@ -225,15 +225,16 @@ metadata: name: ${CLUSTER_NAME} spec: cloudName: ${OPENSTACK_CLOUD} - dnsNameservers: - - ${OPENSTACK_DNS_NAMESERVERS} externalNetwork: id: ${OPENSTACK_EXTERNAL_NETWORK_ID} identityRef: kind: Secret name: ${CLUSTER_NAME}-cloud-config managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - ${OPENSTACK_DNS_NAMESERVERS} --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha8 kind: OpenStackMachineTemplate diff --git a/templates/cluster-template-flatcar.yaml b/templates/cluster-template-flatcar.yaml index ff093ece03..edccdf99ab 100644 --- a/templates/cluster-template-flatcar.yaml +++ b/templates/cluster-template-flatcar.yaml @@ -149,15 +149,16 @@ metadata: name: ${CLUSTER_NAME} spec: cloudName: ${OPENSTACK_CLOUD} - dnsNameservers: - - ${OPENSTACK_DNS_NAMESERVERS} externalNetwork: id: ${OPENSTACK_EXTERNAL_NETWORK_ID} identityRef: kind: Secret name: ${CLUSTER_NAME}-cloud-config managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - ${OPENSTACK_DNS_NAMESERVERS} --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha8 kind: OpenStackMachineTemplate diff --git a/templates/cluster-template-without-lb.yaml b/templates/cluster-template-without-lb.yaml index 665437a0a1..97d39b1d17 100644 --- a/templates/cluster-template-without-lb.yaml +++ b/templates/cluster-template-without-lb.yaml @@ -106,15 +106,16 @@ metadata: name: ${CLUSTER_NAME} spec: cloudName: ${OPENSTACK_CLOUD} - dnsNameservers: - - ${OPENSTACK_DNS_NAMESERVERS} externalNetwork: id: ${OPENSTACK_EXTERNAL_NETWORK_ID} identityRef: kind: Secret name: ${CLUSTER_NAME}-cloud-config managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - ${OPENSTACK_DNS_NAMESERVERS} --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha8 kind: OpenStackMachineTemplate diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index af65b8865f..1384b473b0 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -108,15 +108,16 @@ spec: apiServerLoadBalancer: enabled: true cloudName: ${OPENSTACK_CLOUD} - dnsNameservers: - - ${OPENSTACK_DNS_NAMESERVERS} externalNetwork: id: ${OPENSTACK_EXTERNAL_NETWORK_ID} identityRef: kind: Secret name: ${CLUSTER_NAME}-cloud-config managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - ${OPENSTACK_DNS_NAMESERVERS} --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha8 kind: OpenStackMachineTemplate diff --git a/templates/clusterclass-dev-test.yaml b/templates/clusterclass-dev-test.yaml index 40b8758502..44930abc7d 100644 --- a/templates/clusterclass-dev-test.yaml +++ b/templates/clusterclass-dev-test.yaml @@ -118,13 +118,14 @@ spec: apiServerLoadBalancer: enabled: true cloudName: ${OPENSTACK_CLOUD:=capo-e2e} - dnsNameservers: - - 8.8.8.8 identityRef: kind: Secret name: dev-test-cloud-config managedSecurityGroups: true - nodeCidr: 10.6.0.0/24 + managedSubnets: + - cidr: 10.6.0.0/24 + dnsNameservers: + - 8.8.8.8 --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha8 kind: OpenStackMachineTemplate