diff --git a/api/v1alpha1/openstackfloatingippool_types.go b/api/v1alpha1/openstackfloatingippool_types.go index b3b0ebfc2a..931f2d503e 100644 --- a/api/v1alpha1/openstackfloatingippool_types.go +++ b/api/v1alpha1/openstackfloatingippool_types.go @@ -63,7 +63,7 @@ type OpenStackFloatingIPPoolSpec struct { // FloatingIPNetwork is the external network to use for floating ips, if there's only one external network it will be used by default // +optional - FloatingIPNetwork infrav1.NetworkFilter `json:"floatingIPNetwork"` + FloatingIPNetwork infrav1.NetworkParam `json:"floatingIPNetwork"` // The stratergy to use for reclaiming floating ips when they are released from a machine // +kubebuilder:validation:Optional diff --git a/api/v1alpha5/conversion.go b/api/v1alpha5/conversion.go index 5c2516b891..b8190e088a 100644 --- a/api/v1alpha5/conversion.go +++ b/api/v1alpha5/conversion.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha5 import ( + "errors" "strings" conversion "k8s.io/apimachinery/pkg/conversion" @@ -205,8 +206,8 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec(in *i return err } - if in.ExternalNetwork != nil && in.ExternalNetwork.ID != "" { - out.ExternalNetworkID = in.ExternalNetwork.ID + if in.ExternalNetwork != nil && in.ExternalNetwork.ID != nil { + out.ExternalNetworkID = *in.ExternalNetwork.ID } if len(in.ManagedSubnets) > 0 { @@ -246,8 +247,8 @@ func Convert_v1alpha5_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O } if in.ExternalNetworkID != "" { - out.ExternalNetwork = &infrav1.NetworkFilter{ - ID: in.ExternalNetworkID, + out.ExternalNetwork = &infrav1.NetworkParam{ + ID: &in.ExternalNetworkID, } } @@ -746,18 +747,49 @@ func Convert_v1beta1_SecurityGroupFilter_To_v1alpha5_SecurityGroupFilter(in *inf return nil } -func Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilter, out *infrav1.NetworkFilter, s conversion.Scope) error { - if err := autoConvert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(in, out, s); err != nil { +func Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkParam(in *NetworkFilter, out *infrav1.NetworkParam, s conversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + outFilter := &infrav1.NetworkFilter{} + if err := autoConvert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(in, outFilter, s); err != nil { return err } - infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &out.FilterByNeutronTags) + infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &outFilter.FilterByNeutronTags) + if !outFilter.IsZero() { + out.Filter = outFilter + } return nil } -func Convert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(in *infrav1.NetworkFilter, out *NetworkFilter, s conversion.Scope) error { - if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(in, out, s); err != nil { - return err +func Convert_v1beta1_NetworkParam_To_v1alpha5_NetworkFilter(in *infrav1.NetworkParam, out *NetworkFilter, s conversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + if in.Filter != nil { + if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(in.Filter, out, s); err != nil { + return err + } + infrav1.ConvertAllTagsFrom(&in.Filter.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) } - infrav1.ConvertAllTagsFrom(&in.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) return nil } + +// conversion-gen registers the following functions so we have to define them, but nothing should ever call them. +func Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(_ *NetworkFilter, _ *infrav1.NetworkFilter, _ conversion.Scope) error { + return errors.New("Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter should not be called") +} + +func Convert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(_ *infrav1.NetworkFilter, _ *NetworkFilter, _ conversion.Scope) error { + return errors.New("Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter should not be called") +} + +func Convert_v1alpha5_NetworkParam_To_v1beta1_NetworkParam(_ *NetworkParam, _ *infrav1.NetworkParam, _ conversion.Scope) error { + return errors.New("Convert_v1alpha6_NetworkParam_To_v1beta1_NetworkParam should not be called") +} + +func Convert_v1beta1_NetworkParam_To_v1alpha5_NetworkParam(_ *infrav1.NetworkParam, _ *NetworkParam, _ conversion.Scope) error { + return errors.New("Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkParam should not be called") +} diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index f5d208397e..e8376912a3 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -252,6 +252,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*NetworkFilter)(nil), (*v1beta1.NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkParam(a.(*NetworkFilter), b.(*v1beta1.NetworkParam), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NetworkParam)(nil), (*v1beta1.NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_NetworkParam_To_v1beta1_NetworkParam(a.(*NetworkParam), b.(*v1beta1.NetworkParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*Network)(nil), (*v1beta1.NetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha5_Network_To_v1beta1_NetworkStatus(a.(*Network), b.(*v1beta1.NetworkStatus), scope) }); err != nil { @@ -337,6 +347,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.NetworkParam)(nil), (*NetworkFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkParam_To_v1alpha5_NetworkFilter(a.(*v1beta1.NetworkParam), b.(*NetworkFilter), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.NetworkParam)(nil), (*NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkParam_To_v1alpha5_NetworkParam(a.(*v1beta1.NetworkParam), b.(*NetworkParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.NetworkStatusWithSubnets)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_NetworkStatusWithSubnets_To_v1alpha5_Network(a.(*v1beta1.NetworkStatusWithSubnets), b.(*Network), scope) }); err != nil { @@ -570,7 +590,7 @@ func autoConvert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilt out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID + // WARNING: in.ID requires manual conversion: does not exist in peer-type // WARNING: in.Tags requires manual conversion: does not exist in peer-type // WARNING: in.TagsAny requires manual conversion: does not exist in peer-type // WARNING: in.NotTags requires manual conversion: does not exist in peer-type @@ -582,11 +602,24 @@ func autoConvert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(in *v1beta1.Net out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } +func autoConvert_v1alpha5_NetworkParam_To_v1beta1_NetworkParam(in *NetworkParam, out *v1beta1.NetworkParam, s conversion.Scope) error { + // WARNING: in.UUID requires manual conversion: does not exist in peer-type + // WARNING: in.FixedIP requires manual conversion: does not exist in peer-type + // WARNING: in.Filter requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter) + // WARNING: in.Subnets requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_NetworkParam_To_v1alpha5_NetworkParam(in *v1beta1.NetworkParam, out *NetworkParam, s conversion.Scope) error { + // WARNING: in.ID requires manual conversion: does not exist in peer-type + // WARNING: in.Filter requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter) + return nil +} + func autoConvert_v1alpha5_OpenStackCluster_To_v1beta1_OpenStackCluster(in *OpenStackCluster, out *v1beta1.OpenStackCluster, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha5_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(&in.Spec, &out.Spec, s); err != nil { @@ -664,7 +697,7 @@ func Convert_v1beta1_OpenStackClusterList_To_v1alpha5_OpenStackClusterList(in *v func autoConvert_v1alpha5_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *OpenStackClusterSpec, out *v1beta1.OpenStackClusterSpec, s conversion.Scope) error { // WARNING: in.CloudName requires manual conversion: does not exist in peer-type // WARNING: in.NodeCIDR requires manual conversion: does not exist in peer-type - // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam) // WARNING: in.Subnet requires manual conversion: does not exist in peer-type // WARNING: in.DNSNameservers requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { @@ -716,7 +749,7 @@ func autoConvert_v1alpha5_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(i func autoConvert_v1beta1_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec(in *v1beta1.OpenStackClusterSpec, out *OpenStackClusterSpec, s conversion.Scope) error { // WARNING: in.ManagedSubnets requires manual conversion: does not exist in peer-type // WARNING: in.Router requires manual conversion: does not exist in peer-type - // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.NetworkFilter) // WARNING: in.Subnets requires manual conversion: does not exist in peer-type // WARNING: in.NetworkMTU requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { @@ -1339,8 +1372,8 @@ func Convert_v1beta1_OpenStackMachineTemplateSpec_To_v1alpha5_OpenStackMachineTe func autoConvert_v1alpha5_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta1.PortOpts, s conversion.Scope) error { if in.Network != nil { in, out := &in.Network, &out.Network - *out = new(v1beta1.NetworkFilter) - if err := Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkFilter(*in, *out, s); err != nil { + *out = new(v1beta1.NetworkParam) + if err := Convert_v1alpha5_NetworkFilter_To_v1beta1_NetworkParam(*in, *out, s); err != nil { return err } } else { @@ -1383,7 +1416,7 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha5_PortOpts(in *v1beta1.PortOpts, out if in.Network != nil { in, out := &in.Network, &out.Network *out = new(NetworkFilter) - if err := Convert_v1beta1_NetworkFilter_To_v1alpha5_NetworkFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_NetworkParam_To_v1alpha5_NetworkFilter(*in, *out, s); err != nil { return err } } else { diff --git a/api/v1alpha6/conversion_test.go b/api/v1alpha6/conversion_test.go index 3e5484cafd..d403145c93 100644 --- a/api/v1alpha6/conversion_test.go +++ b/api/v1alpha6/conversion_test.go @@ -68,6 +68,14 @@ func TestFuzzyConversion(t *testing.T) { return ret } + nonEmptyString := func(c fuzz.Continue) string { + for { + if s := c.RandString(); s != "" { + return s + } + } + } + fuzzerFuncs := func(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ func(instance *Instance, c fuzz.Continue) { @@ -206,6 +214,22 @@ func TestFuzzyConversion(t *testing.T) { filter.NotTags = filterInvalidTags(filter.NotTags) filter.NotTagsAny = filterInvalidTags(filter.NotTagsAny) }, + + // v1beta1 network param contains exactly one of ID or filter + func(param *infrav1.NetworkParam, c fuzz.Continue) { + if c.RandBool() { + id := nonEmptyString(c) + param.ID = &id + param.Filter = nil + } else { + filter := infrav1.NetworkFilter{} + for filter.IsZero() { + c.FuzzNoCustom(&filter) + } + param.Filter = &filter + param.ID = nil + } + }, } } @@ -303,8 +327,8 @@ func TestNetworksToPorts(t *testing.T) { afterMachineSpec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkuuid, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkuuid), }, }, }, @@ -330,15 +354,17 @@ func TestNetworksToPorts(t *testing.T) { afterMachineSpec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - Name: "network-name", - Description: "network-description", - ProjectID: "project-id", - FilterByNeutronTags: infrav1.FilterByNeutronTags{ - Tags: []infrav1.NeutronTag{"tags"}, - TagsAny: []infrav1.NeutronTag{"tags-any"}, - NotTags: []infrav1.NeutronTag{"not-tags"}, - NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{ + Name: "network-name", + Description: "network-description", + ProjectID: "project-id", + FilterByNeutronTags: infrav1.FilterByNeutronTags{ + Tags: []infrav1.NeutronTag{"tags"}, + TagsAny: []infrav1.NeutronTag{"tags-any"}, + NotTags: []infrav1.NeutronTag{"not-tags"}, + NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + }, }, }, }, @@ -362,8 +388,8 @@ func TestNetworksToPorts(t *testing.T) { afterMachineSpec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkuuid, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkuuid), }, FixedIPs: []infrav1.FixedIP{ { @@ -406,8 +432,8 @@ func TestNetworksToPorts(t *testing.T) { afterMachineSpec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkuuid, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkuuid), }, FixedIPs: []infrav1.FixedIP{ { @@ -466,8 +492,8 @@ func TestNetworksToPorts(t *testing.T) { afterMachineSpec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkuuid, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkuuid), }, FixedIPs: []infrav1.FixedIP{ { @@ -478,8 +504,8 @@ func TestNetworksToPorts(t *testing.T) { }, }, { - Network: &infrav1.NetworkFilter{ - ID: networkuuid, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkuuid), }, FixedIPs: []infrav1.FixedIP{ { diff --git a/api/v1alpha6/openstackcluster_conversion.go b/api/v1alpha6/openstackcluster_conversion.go index 95131f6c3f..423adc52e3 100644 --- a/api/v1alpha6/openstackcluster_conversion.go +++ b/api/v1alpha6/openstackcluster_conversion.go @@ -164,23 +164,14 @@ func restorev1alpha6ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackCl func restorev1beta1ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *infrav1.OpenStackClusterSpec) { // Bastion is restored separately - if dst.Network.IsEmpty() { - dst.Network = previous.Network - } + restorev1beta1NetworkParam(previous.Network, dst.Network) - // Restore all fields except ID, which should have been copied over in conversion + // ExternalNetwork by filter will be been lost in down-conversion if previous.ExternalNetwork != nil { if dst.ExternalNetwork == nil { - dst.ExternalNetwork = &infrav1.NetworkFilter{} + dst.ExternalNetwork = &infrav1.NetworkParam{} } - - dst.ExternalNetwork.Name = previous.ExternalNetwork.Name - dst.ExternalNetwork.Description = previous.ExternalNetwork.Description - dst.ExternalNetwork.ProjectID = previous.ExternalNetwork.ProjectID - dst.ExternalNetwork.Tags = previous.ExternalNetwork.Tags - dst.ExternalNetwork.TagsAny = previous.ExternalNetwork.TagsAny - dst.ExternalNetwork.NotTags = previous.ExternalNetwork.NotTags - dst.ExternalNetwork.NotTagsAny = previous.ExternalNetwork.NotTagsAny + dst.ExternalNetwork.Filter = previous.ExternalNetwork.Filter } // Restore fields not present in v1alpha6 @@ -227,15 +218,15 @@ func Convert_v1alpha6_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O } if in.Network != (NetworkFilter{}) { - out.Network = &infrav1.NetworkFilter{} - if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(&in.Network, out.Network, s); err != nil { + out.Network = &infrav1.NetworkParam{} + if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkParam(&in.Network, out.Network, s); err != nil { return err } } if in.ExternalNetworkID != "" { - out.ExternalNetwork = &infrav1.NetworkFilter{ - ID: in.ExternalNetworkID, + out.ExternalNetwork = &infrav1.NetworkParam{ + ID: &in.ExternalNetworkID, } } @@ -294,13 +285,13 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in *i } if in.Network != nil { - if err := Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(in.Network, &out.Network, s); err != nil { + if err := Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkFilter(in.Network, &out.Network, s); err != nil { return err } } - if in.ExternalNetwork != nil && in.ExternalNetwork.ID != "" { - out.ExternalNetworkID = in.ExternalNetwork.ID + if in.ExternalNetwork != nil && in.ExternalNetwork.ID != nil { + out.ExternalNetworkID = *in.ExternalNetwork.ID } if len(in.Subnets) >= 1 { diff --git a/api/v1alpha6/openstackmachine_conversion.go b/api/v1alpha6/openstackmachine_conversion.go index d0f01cac1b..1193ae0d27 100644 --- a/api/v1alpha6/openstackmachine_conversion.go +++ b/api/v1alpha6/openstackmachine_conversion.go @@ -179,7 +179,7 @@ func convertNetworksToPorts(networks []NetworkParam, s apiconversion.Scope) ([]i network := networks[i] // This will remain null if the network is not specified in NetworkParam - var networkFilter *infrav1.NetworkFilter + var networkFilter *infrav1.NetworkParam // In v1alpha6, if network.Filter resolved to multiple networks // then we would add multiple ports. It is not possible to @@ -189,12 +189,12 @@ func convertNetworksToPorts(networks []NetworkParam, s apiconversion.Scope) ([]i // create the port. switch { case network.UUID != "": - networkFilter = &infrav1.NetworkFilter{ - ID: network.UUID, + networkFilter = &infrav1.NetworkParam{ + ID: &network.UUID, } case network.Filter != (NetworkFilter{}): - networkFilter = &infrav1.NetworkFilter{} - if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(&network.Filter, networkFilter, s); err != nil { + networkFilter = &infrav1.NetworkParam{} + if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkParam(&network.Filter, networkFilter, s); err != nil { return nil, err } } diff --git a/api/v1alpha6/types_conversion.go b/api/v1alpha6/types_conversion.go index 6ec354ecee..bd9ad97d58 100644 --- a/api/v1alpha6/types_conversion.go +++ b/api/v1alpha6/types_conversion.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha6 import ( + "errors" "strings" apiconversion "k8s.io/apimachinery/pkg/conversion" @@ -102,27 +103,61 @@ func Convert_v1beta1_SecurityGroupFilter_To_v1alpha6_SecurityGroupFilter(in *inf /* NetworkParam, NetworkFilter */ func restorev1alpha6NetworkFilter(previous *NetworkFilter, dst *NetworkFilter) { + if previous == nil || dst == nil { + return + } + // The edge cases with multiple commas are too tricky in this direction, // so we just restore the whole thing. dst.Tags = previous.Tags dst.TagsAny = previous.TagsAny dst.NotTags = previous.NotTags dst.NotTagsAny = previous.NotTagsAny + + if dst.ID != "" { + dst.Name = previous.Name + dst.Description = previous.Description + dst.ProjectID = previous.ProjectID + } +} + +func restorev1beta1NetworkParam(previous *infrav1.NetworkParam, dst *infrav1.NetworkParam) { + if previous == nil || dst == nil { + return + } + + if dst.Filter != nil && previous.Filter != nil { + dst.Filter.FilterByNeutronTags = previous.Filter.FilterByNeutronTags + } } -func Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilter, out *infrav1.NetworkFilter, s apiconversion.Scope) error { - if err := autoConvert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(in, out, s); err != nil { +func Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkParam(in *NetworkFilter, out *infrav1.NetworkParam, s apiconversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + outFilter := &infrav1.NetworkFilter{} + if err := autoConvert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(in, outFilter, s); err != nil { return err } - infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &out.FilterByNeutronTags) + infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &outFilter.FilterByNeutronTags) + if !outFilter.IsZero() { + out.Filter = outFilter + } return nil } -func Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(in *infrav1.NetworkFilter, out *NetworkFilter, s apiconversion.Scope) error { - if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(in, out, s); err != nil { - return err +func Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkFilter(in *infrav1.NetworkParam, out *NetworkFilter, s apiconversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + if in.Filter != nil { + if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(in.Filter, out, s); err != nil { + return err + } + infrav1.ConvertAllTagsFrom(&in.Filter.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) } - infrav1.ConvertAllTagsFrom(&in.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) return nil } @@ -181,9 +216,7 @@ func restorev1alpha6Port(previous *PortOpts, dst *PortOpts) { } } - if dst.Network != nil && previous.Network != nil { - restorev1alpha6NetworkFilter(previous.Network, dst.Network) - } + restorev1alpha6NetworkFilter(previous.Network, dst.Network) if len(dst.FixedIPs) == len(previous.FixedIPs) { for i := range dst.FixedIPs { @@ -392,3 +425,22 @@ func Convert_v1beta1_OpenStackIdentityReference_To_v1alpha6_OpenStackIdentityRef out.Name = in.Name return nil } + +/* Other */ + +// conversion-gen registers the following functions so we have to define them, but nothing should ever call them. +func Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(_ *NetworkFilter, _ *infrav1.NetworkFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter should not be called") +} + +func Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(_ *infrav1.NetworkFilter, _ *NetworkFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter should not be called") +} + +func Convert_v1alpha6_NetworkParam_To_v1beta1_NetworkParam(_ *NetworkParam, _ *infrav1.NetworkParam, _ apiconversion.Scope) error { + return errors.New("Convert_v1alpha6_NetworkParam_To_v1beta1_NetworkParam should not be called") +} + +func Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkParam(_ *infrav1.NetworkParam, _ *NetworkParam, _ apiconversion.Scope) error { + return errors.New("Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkParam should not be called") +} diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index b35e716ad4..34023deb82 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -271,6 +271,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*NetworkFilter)(nil), (*v1beta1.NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkParam(a.(*NetworkFilter), b.(*v1beta1.NetworkParam), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NetworkParam)(nil), (*v1beta1.NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha6_NetworkParam_To_v1beta1_NetworkParam(a.(*NetworkParam), b.(*v1beta1.NetworkParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*Network)(nil), (*v1beta1.NetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha6_Network_To_v1beta1_NetworkStatus(a.(*Network), b.(*v1beta1.NetworkStatus), scope) }); err != nil { @@ -346,6 +356,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.NetworkParam)(nil), (*NetworkFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkFilter(a.(*v1beta1.NetworkParam), b.(*NetworkFilter), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.NetworkParam)(nil), (*NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkParam(a.(*v1beta1.NetworkParam), b.(*NetworkParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.NetworkStatusWithSubnets)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_NetworkStatusWithSubnets_To_v1alpha6_Network(a.(*v1beta1.NetworkStatusWithSubnets), b.(*Network), scope) }); err != nil { @@ -600,7 +620,7 @@ func autoConvert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilt out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID + // WARNING: in.ID requires manual conversion: does not exist in peer-type // WARNING: in.Tags requires manual conversion: does not exist in peer-type // WARNING: in.TagsAny requires manual conversion: does not exist in peer-type // WARNING: in.NotTags requires manual conversion: does not exist in peer-type @@ -612,11 +632,24 @@ func autoConvert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(in *v1beta1.Net out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } +func autoConvert_v1alpha6_NetworkParam_To_v1beta1_NetworkParam(in *NetworkParam, out *v1beta1.NetworkParam, s conversion.Scope) error { + // WARNING: in.UUID requires manual conversion: does not exist in peer-type + // WARNING: in.FixedIP requires manual conversion: does not exist in peer-type + // WARNING: in.Filter requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter) + // WARNING: in.Subnets requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_NetworkParam_To_v1alpha6_NetworkParam(in *v1beta1.NetworkParam, out *NetworkParam, s conversion.Scope) error { + // WARNING: in.ID requires manual conversion: does not exist in peer-type + // WARNING: in.Filter requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter) + return nil +} + func autoConvert_v1alpha6_OpenStackCluster_To_v1beta1_OpenStackCluster(in *OpenStackCluster, out *v1beta1.OpenStackCluster, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha6_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(&in.Spec, &out.Spec, s); err != nil { @@ -694,7 +727,7 @@ func Convert_v1beta1_OpenStackClusterList_To_v1alpha6_OpenStackClusterList(in *v func autoConvert_v1alpha6_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *OpenStackClusterSpec, out *v1beta1.OpenStackClusterSpec, s conversion.Scope) error { // WARNING: in.CloudName requires manual conversion: does not exist in peer-type // WARNING: in.NodeCIDR requires manual conversion: does not exist in peer-type - // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam) // WARNING: in.Subnet requires manual conversion: does not exist in peer-type // WARNING: in.DNSNameservers requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { @@ -749,7 +782,7 @@ func autoConvert_v1alpha6_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(i func autoConvert_v1beta1_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in *v1beta1.OpenStackClusterSpec, out *OpenStackClusterSpec, s conversion.Scope) error { // WARNING: in.ManagedSubnets requires manual conversion: does not exist in peer-type // WARNING: in.Router requires manual conversion: does not exist in peer-type - // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.NetworkFilter) // WARNING: in.Subnets requires manual conversion: does not exist in peer-type // WARNING: in.NetworkMTU requires manual conversion: does not exist in peer-type if in.ExternalRouterIPs != nil { @@ -1374,8 +1407,8 @@ func Convert_v1beta1_OpenStackMachineTemplateSpec_To_v1alpha6_OpenStackMachineTe func autoConvert_v1alpha6_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta1.PortOpts, s conversion.Scope) error { if in.Network != nil { in, out := &in.Network, &out.Network - *out = new(v1beta1.NetworkFilter) - if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter(*in, *out, s); err != nil { + *out = new(v1beta1.NetworkParam) + if err := Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkParam(*in, *out, s); err != nil { return err } } else { @@ -1419,7 +1452,7 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha6_PortOpts(in *v1beta1.PortOpts, out if in.Network != nil { in, out := &in.Network, &out.Network *out = new(NetworkFilter) - if err := Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkFilter(*in, *out, s); err != nil { return err } } else { diff --git a/api/v1alpha7/conversion_test.go b/api/v1alpha7/conversion_test.go index a2c770984f..ee12616e8e 100644 --- a/api/v1alpha7/conversion_test.go +++ b/api/v1alpha7/conversion_test.go @@ -68,6 +68,14 @@ func TestFuzzyConversion(t *testing.T) { return ret } + nonEmptyString := func(c fuzz.Continue) string { + for { + if s := c.RandString(); s != "" { + return s + } + } + } + fuzzerFuncs := func(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ func(spec *infrav1.OpenStackClusterSpec, c fuzz.Continue) { @@ -194,6 +202,22 @@ func TestFuzzyConversion(t *testing.T) { filter.NotTags = filterInvalidTags(filter.NotTags) filter.NotTagsAny = filterInvalidTags(filter.NotTagsAny) }, + + // v1beta1 network param contains exactly one of ID or filter + func(param *infrav1.NetworkParam, c fuzz.Continue) { + if c.RandBool() { + id := nonEmptyString(c) + param.ID = &id + param.Filter = nil + } else { + filter := infrav1.NetworkFilter{} + for filter.IsZero() { + c.FuzzNoCustom(&filter) + } + param.Filter = &filter + param.ID = nil + } + }, } } diff --git a/api/v1alpha7/openstackcluster_conversion.go b/api/v1alpha7/openstackcluster_conversion.go index 6cbd9a5118..47b13f903b 100644 --- a/api/v1alpha7/openstackcluster_conversion.go +++ b/api/v1alpha7/openstackcluster_conversion.go @@ -169,29 +169,28 @@ func restorev1alpha7ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackCl } func restorev1beta1ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *infrav1.OpenStackClusterSpec) { + if previous == nil || dst == nil { + return + } + // Bastion is restored separately - if dst.Network.IsEmpty() { + if dst.Network == nil { dst.Network = previous.Network } - // Restore all fields except ID, which should have been copied over in conversion + // ExternalNetwork by filter will be been lost in down-conversion if previous.ExternalNetwork != nil { if dst.ExternalNetwork == nil { - dst.ExternalNetwork = &infrav1.NetworkFilter{} + dst.ExternalNetwork = &infrav1.NetworkParam{} } - - dst.ExternalNetwork.Name = previous.ExternalNetwork.Name - dst.ExternalNetwork.Description = previous.ExternalNetwork.Description - dst.ExternalNetwork.ProjectID = previous.ExternalNetwork.ProjectID - dst.ExternalNetwork.Tags = previous.ExternalNetwork.Tags - dst.ExternalNetwork.TagsAny = previous.ExternalNetwork.TagsAny - dst.ExternalNetwork.NotTags = previous.ExternalNetwork.NotTags - dst.ExternalNetwork.NotTagsAny = previous.ExternalNetwork.NotTagsAny + dst.ExternalNetwork.Filter = previous.ExternalNetwork.Filter } dst.DisableExternalNetwork = previous.DisableExternalNetwork + restorev1beta1NetworkParam(previous.Network, dst.Network) + if len(previous.Subnets) > 1 { dst.Subnets = append(dst.Subnets, previous.Subnets[1:]...) } @@ -231,15 +230,15 @@ func Convert_v1alpha7_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O } if in.Network != (NetworkFilter{}) { - out.Network = &infrav1.NetworkFilter{} - if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(&in.Network, out.Network, s); err != nil { + out.Network = &infrav1.NetworkParam{} + if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(&in.Network, out.Network, s); err != nil { return err } } if in.ExternalNetworkID != "" { - out.ExternalNetwork = &infrav1.NetworkFilter{ - ID: in.ExternalNetworkID, + out.ExternalNetwork = &infrav1.NetworkParam{ + ID: &in.ExternalNetworkID, } } @@ -298,13 +297,13 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in *i } if in.Network != nil { - if err := Convert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(in.Network, &out.Network, s); err != nil { + if err := Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(in.Network, &out.Network, s); err != nil { return err } } - if in.ExternalNetwork != nil && in.ExternalNetwork.ID != "" { - out.ExternalNetworkID = in.ExternalNetwork.ID + if in.ExternalNetwork != nil && in.ExternalNetwork.ID != nil { + out.ExternalNetworkID = *in.ExternalNetwork.ID } if len(in.Subnets) >= 1 { diff --git a/api/v1alpha7/types_conversion.go b/api/v1alpha7/types_conversion.go index 98bdd6df26..dfaf2bd804 100644 --- a/api/v1alpha7/types_conversion.go +++ b/api/v1alpha7/types_conversion.go @@ -63,27 +63,63 @@ func Convert_v1beta1_SecurityGroupFilter_To_v1alpha7_SecurityGroupFilter(in *inf /* NetworkFilter */ func restorev1alpha7NetworkFilter(previous *NetworkFilter, dst *NetworkFilter) { + if previous == nil || dst == nil { + return + } + // The edge cases with multiple commas are too tricky in this direction, // so we just restore the whole thing. dst.Tags = previous.Tags dst.TagsAny = previous.TagsAny dst.NotTags = previous.NotTags dst.NotTagsAny = previous.NotTagsAny + + // If ID was set we lost all over filter params + if dst.ID != "" { + dst.Name = previous.Name + dst.Description = previous.Description + dst.ProjectID = previous.ProjectID + } +} + +func restorev1beta1NetworkParam(previous *infrav1.NetworkParam, dst *infrav1.NetworkParam) { + if previous == nil || dst == nil { + return + } + + if dst.Filter != nil && previous.Filter != nil { + dst.Filter.FilterByNeutronTags = previous.Filter.FilterByNeutronTags + } } -func Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilter, out *infrav1.NetworkFilter, s apiconversion.Scope) error { - if err := autoConvert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(in, out, s); err != nil { +func Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(in *NetworkFilter, out *infrav1.NetworkParam, s apiconversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + outFilter := &infrav1.NetworkFilter{} + if err := autoConvert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(in, outFilter, s); err != nil { return err } - infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &out.FilterByNeutronTags) + infrav1.ConvertAllTagsTo(in.Tags, in.TagsAny, in.NotTags, in.NotTagsAny, &outFilter.FilterByNeutronTags) + if !outFilter.IsZero() { + out.Filter = outFilter + } return nil } -func Convert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(in *infrav1.NetworkFilter, out *NetworkFilter, s apiconversion.Scope) error { - if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(in, out, s); err != nil { - return err +func Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(in *infrav1.NetworkParam, out *NetworkFilter, s apiconversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + + if in.Filter != nil { + if err := autoConvert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(in.Filter, out, s); err != nil { + return err + } + infrav1.ConvertAllTagsFrom(&in.Filter.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) } - infrav1.ConvertAllTagsFrom(&in.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) return nil } @@ -167,6 +203,8 @@ func restorev1alpha7Port(previous *PortOpts, dst *PortOpts) { } func restorev1beta1Port(previous *infrav1.PortOpts, dst *infrav1.PortOpts) { + restorev1beta1NetworkParam(previous.Network, dst.Network) + optional.RestoreString(&previous.NameSuffix, &dst.NameSuffix) optional.RestoreString(&previous.Description, &dst.Description) optional.RestoreString(&previous.MACAddress, &dst.MACAddress) @@ -351,3 +389,14 @@ func Convert_v1beta1_OpenStackIdentityReference_To_v1alpha7_OpenStackIdentityRef out.Name = in.Name return nil } + +/* Other */ + +// conversion-gen registers the following functions so we have to define them, but nothing should ever call them. +func Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(_ *NetworkFilter, _ *infrav1.NetworkFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1alpha6_NetworkFilter_To_v1beta1_NetworkFilter should not be called") +} + +func Convert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(_ *infrav1.NetworkFilter, _ *NetworkFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1beta1_NetworkFilter_To_v1alpha6_NetworkFilter should not be called") +} diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 5646dbe6f8..8b070e14bf 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -336,6 +336,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*NetworkFilter)(nil), (*v1beta1.NetworkParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(a.(*NetworkFilter), b.(*v1beta1.NetworkParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*OpenStackClusterSpec)(nil), (*v1beta1.OpenStackClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha7_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(a.(*OpenStackClusterSpec), b.(*v1beta1.OpenStackClusterSpec), scope) }); err != nil { @@ -391,6 +396,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.NetworkParam)(nil), (*NetworkFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(a.(*v1beta1.NetworkParam), b.(*NetworkFilter), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.OpenStackClusterSpec)(nil), (*OpenStackClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(a.(*v1beta1.OpenStackClusterSpec), b.(*OpenStackClusterSpec), scope) }); err != nil { @@ -754,7 +764,7 @@ func autoConvert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(in *NetworkFilt out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID + // WARNING: in.ID requires manual conversion: does not exist in peer-type // WARNING: in.Tags requires manual conversion: does not exist in peer-type // WARNING: in.TagsAny requires manual conversion: does not exist in peer-type // WARNING: in.NotTags requires manual conversion: does not exist in peer-type @@ -766,7 +776,6 @@ func autoConvert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(in *v1beta1.Net out.Name = in.Name out.Description = in.Description out.ProjectID = in.ProjectID - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } @@ -907,7 +916,7 @@ func autoConvert_v1alpha7_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(i } else { out.Router = nil } - // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.NetworkFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam) // WARNING: in.Subnet requires manual conversion: does not exist in peer-type if err := optional.Convert_int_To_optional_Int(&in.NetworkMTU, &out.NetworkMTU, s); err != nil { return err @@ -973,7 +982,7 @@ func autoConvert_v1beta1_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(i } else { out.Router = nil } - // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.NetworkFilter) + // WARNING: in.Network requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.NetworkFilter) // WARNING: in.Subnets requires manual conversion: does not exist in peer-type if err := optional.Convert_optional_Int_To_int(&in.NetworkMTU, &out.NetworkMTU, s); err != nil { return err @@ -1574,8 +1583,8 @@ func Convert_v1beta1_OpenStackMachineTemplateSpec_To_v1alpha7_OpenStackMachineTe func autoConvert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta1.PortOpts, s conversion.Scope) error { if in.Network != nil { in, out := &in.Network, &out.Network - *out = new(v1beta1.NetworkFilter) - if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkFilter(*in, *out, s); err != nil { + *out = new(v1beta1.NetworkParam) + if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(*in, *out, s); err != nil { return err } } else { @@ -1617,7 +1626,7 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *v1beta1.PortOpts, out if in.Network != nil { in, out := &in.Network, &out.Network *out = new(NetworkFilter) - if err := Convert_v1beta1_NetworkFilter_To_v1alpha7_NetworkFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(*in, *out, s); err != nil { return err } } else { diff --git a/api/v1beta1/openstackcluster_types.go b/api/v1beta1/openstackcluster_types.go index 06d23de650..35a2bf5505 100644 --- a/api/v1beta1/openstackcluster_types.go +++ b/api/v1beta1/openstackcluster_types.go @@ -48,7 +48,7 @@ type OpenStackClusterSpec struct { // Network specifies an existing network to use if no ManagedSubnets // are specified. // +optional - Network *NetworkFilter `json:"network,omitempty"` + Network *NetworkParam `json:"network,omitempty"` // Subnets specifies existing subnets to use if not ManagedSubnets are // specified. All subnets must be in the network specified by Network. @@ -86,7 +86,7 @@ type OpenStackClusterSpec struct { // If ExternalNetwork is not defined and there are no external networks // the controller will proceed as though DisableExternalNetwork was set. // +optional - ExternalNetwork *NetworkFilter `json:"externalNetwork,omitempty"` + ExternalNetwork *NetworkParam `json:"externalNetwork,omitempty"` // DisableExternalNetwork specifies whether or not to attempt to connect the cluster // to an external network. This allows for the creation of clusters when connecting diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index a8a9288393..935f632561 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -84,6 +84,10 @@ type FilterByNeutronTags struct { NotTagsAny []NeutronTag `json:"notTagsAny,omitempty"` } +func (f *FilterByNeutronTags) IsZero() bool { + return f == nil || len(f.Tags) == 0 && len(f.TagsAny) == 0 && len(f.NotTags) == 0 && len(f.NotTagsAny) == 0 +} + type SecurityGroupFilter struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` @@ -93,27 +97,38 @@ type SecurityGroupFilter struct { FilterByNeutronTags `json:",inline"` } +// NetworkParam specifies an OpenStack network. It may be specified by either ID or Filter, but not both. +// +kubebuilder:validation:MaxProperties:=1 +// +kubebuilder:validation:MinProperties:=1 +type NetworkParam struct { + // ID is the ID of the network to use. If ID is provided, the other filters cannot be provided. Must be in UUID format. + // +kubebuilder:validation:Format:=uuid + // +optional + ID optional.String `json:"id,omitempty"` + + // Filter specifies a filter to select an OpenStack network. If provided, cannot be empty. + // +optional + Filter *NetworkFilter `json:"filter,omitempty"` +} + +// NetworkFilter specifies a query to select an OpenStack network. At least one property must be set. +// +kubebuilder:validation:MinProperties:=1 type NetworkFilter struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` ProjectID string `json:"projectID,omitempty"` - ID string `json:"id,omitempty"` FilterByNeutronTags `json:",inline"` } -func (networkFilter *NetworkFilter) IsEmpty() bool { +func (networkFilter *NetworkFilter) IsZero() bool { if networkFilter == nil { return true } return networkFilter.Name == "" && networkFilter.Description == "" && networkFilter.ProjectID == "" && - networkFilter.ID == "" && - len(networkFilter.Tags) == 0 && - len(networkFilter.TagsAny) == 0 && - len(networkFilter.NotTags) == 0 && - len(networkFilter.NotTagsAny) == 0 + networkFilter.FilterByNeutronTags.IsZero() } type SubnetFilter struct { @@ -169,7 +184,7 @@ 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. // +optional - Network *NetworkFilter `json:"network,omitempty"` + Network *NetworkParam `json:"network,omitempty"` // Description is a human-readable description for the port. // +optional diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 892e950daf..a64cad9843 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -409,6 +409,31 @@ func (in *NetworkFilter) DeepCopy() *NetworkFilter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkParam) DeepCopyInto(out *NetworkParam) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Filter != nil { + in, out := &in.Filter, &out.Filter + *out = new(NetworkFilter) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkParam. +func (in *NetworkParam) DeepCopy() *NetworkParam { + if in == nil { + return nil + } + out := new(NetworkParam) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkStatus) DeepCopyInto(out *NetworkStatus) { *out = *in @@ -528,7 +553,7 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { } if in.Network != nil { in, out := &in.Network, &out.Network - *out = new(NetworkFilter) + *out = new(NetworkParam) (*in).DeepCopyInto(*out) } if in.Subnets != nil { @@ -552,7 +577,7 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { } if in.ExternalNetwork != nil { in, out := &in.ExternalNetwork, &out.ExternalNetwork - *out = new(NetworkFilter) + *out = new(NetworkParam) (*in).DeepCopyInto(*out) } if in.DisableExternalNetwork != nil { @@ -1090,7 +1115,7 @@ func (in *PortOpts) DeepCopyInto(out *PortOpts) { *out = *in if in.Network != nil { in, out := &in.Network, &out.Network - *out = new(NetworkFilter) + *out = new(NetworkParam) (*in).DeepCopyInto(*out) } if in.Description != nil { 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 abdbf1c5da..ce8db36caf 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -5234,68 +5234,80 @@ spec: description: |- 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. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select + an OpenStack network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. + If ID is provided, the other filters cannot be + provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object profile: description: |- @@ -5663,68 +5675,79 @@ spec: If ExternalNetwork is not defined and there are no external networks the controller will proceed as though DisableExternalNetwork was set. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID is provided, + the other filters cannot be provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object externalRouterIPs: description: |- @@ -5978,68 +6001,79 @@ spec: description: |- Network specifies an existing network to use if no ManagedSubnets are specified. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID is provided, + the other filters cannot be provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object networkMTU: description: |- 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 66b4e12174..72e9aabe49 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2665,68 +2665,81 @@ spec: description: |- 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. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to + select an OpenStack network. If provided, + cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network + to use. If ID is provided, the other filters + cannot be provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object profile: description: |- @@ -3097,68 +3110,80 @@ spec: If ExternalNetwork is not defined and there are no external networks the controller will proceed as though DisableExternalNetwork was set. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID + is provided, the other filters cannot be provided. Must + be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object externalRouterIPs: description: |- @@ -3414,68 +3439,80 @@ spec: description: |- Network specifies an existing network to use if no ManagedSubnets are specified. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID + is provided, the other filters cannot be provided. Must + be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object networkMTU: description: |- diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml index 75281a6f06..de118da2df 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml @@ -45,68 +45,79 @@ spec: description: FloatingIPNetwork is the external network to use for floating ips, if there's only one external network it will be used by default + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID is provided, + the other filters cannot be provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object identityRef: description: IdentityRef is a reference to a identity to be used when diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml index 3d866edc37..bc3d9658b0 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -2011,68 +2011,80 @@ spec: description: |- 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. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select an OpenStack + network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. If ID is + provided, the other filters cannot be provided. Must be + in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object profile: description: |- diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml index 12d3b1ea40..7c481ff2b8 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -1688,68 +1688,80 @@ spec: description: |- 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. + maxProperties: 1 + minProperties: 1 properties: - description: - type: string + filter: + description: Filter specifies a filter to select + an OpenStack network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object id: + description: ID is the ID of the network to use. + If ID is provided, the other filters cannot be + provided. Must be in UUID format. + format: uuid type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set type: object profile: description: |- diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index f1ab14b09c..59d6321a59 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -51,7 +51,6 @@ import ( "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" utils "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/controllers" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/filterconvert" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names" ) @@ -649,20 +648,13 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe openStackCluster.Status.Network = &infrav1.NetworkStatusWithSubnets{} } - if !openStackCluster.Spec.Network.IsEmpty() { - netOpts := filterconvert.NetworkFilterToListOpts(openStackCluster.Spec.Network) - networkList, err := networkingService.GetNetworksByFilter(&netOpts) + if openStackCluster.Spec.Network != nil { + network, err := networkingService.GetNetworkByParam(openStackCluster.Spec.Network) if err != nil { handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find network: %w", err)) - return fmt.Errorf("error fetching networks: %w", err) - } - if len(networkList) == 0 { - handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find any network")) - return fmt.Errorf("failed to find any network") - } - if len(networkList) == 1 { - setClusterNetwork(openStackCluster, &networkList[0]) + return fmt.Errorf("error fetching cluster network: %w", err) } + setClusterNetwork(openStackCluster, network) } subnets, err := getClusterSubnets(networkingService, openStackCluster) diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go index b738cafe47..1de32fa013 100644 --- a/controllers/openstackcluster_controller_test.go +++ b/controllers/openstackcluster_controller_test.go @@ -26,7 +26,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" @@ -506,6 +505,7 @@ var _ = Describe("OpenStackCluster controller", func() { err = deleteBastion(scope, capiCluster, testCluster) Expect(err).To(BeNil()) }) + It("should implicitly filter cluster subnets by cluster network", func() { const externalNetworkID = "a42211a2-4d2c-426f-9413-830e4b4abbbc" const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" @@ -519,11 +519,11 @@ var _ = Describe("OpenStackCluster controller", func() { }, DisableAPIServerFloatingIP: pointer.Bool(true), APIServerFixedIP: pointer.String("10.0.0.1"), - ExternalNetwork: &infrav1.NetworkFilter{ - ID: externalNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(externalNetworkID), }, - Network: &infrav1.NetworkFilter{ - ID: clusterNetworkID, + Network: &infrav1.NetworkParam{ + ID: pointer.String(clusterNetworkID), }, } testCluster.Status = infrav1.OpenStackClusterStatus{ @@ -550,26 +550,15 @@ var _ = Describe("OpenStackCluster controller", func() { networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() // Fetch external network - networkClientRecorder.ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{ - ID: externalNetworkID, - }, - External: pointer.Bool(true), - }).Return([]networks.Network{ - { - ID: externalNetworkID, - Name: "external-network", - }, + networkClientRecorder.GetNetwork(externalNetworkID).Return(&networks.Network{ + ID: externalNetworkID, + Name: "external-network", }, nil) // Fetch cluster network - networkClientRecorder.ListNetwork(&networks.ListOpts{ - ID: clusterNetworkID, - }).Return([]networks.Network{ - { - ID: clusterNetworkID, - Name: "cluster-network", - }, + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", }, nil) // Fetching cluster subnets should be filtered by cluster network id @@ -600,11 +589,11 @@ var _ = Describe("OpenStackCluster controller", func() { }, DisableAPIServerFloatingIP: pointer.Bool(true), APIServerFixedIP: pointer.String("10.0.0.1"), - ExternalNetwork: &infrav1.NetworkFilter{ - ID: externalNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(externalNetworkID), }, - Network: &infrav1.NetworkFilter{ - ID: clusterNetworkID, + Network: &infrav1.NetworkParam{ + ID: pointer.String(clusterNetworkID), }, Subnets: []infrav1.SubnetFilter{ {ID: clusterSubnets[0]}, @@ -635,26 +624,15 @@ var _ = Describe("OpenStackCluster controller", func() { networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() // Fetch external network - networkClientRecorder.ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{ - ID: externalNetworkID, - }, - External: pointer.Bool(true), - }).Return([]networks.Network{ - { - ID: externalNetworkID, - Name: "external-network", - }, + networkClientRecorder.GetNetwork(externalNetworkID).Return(&networks.Network{ + ID: externalNetworkID, + Name: "external-network", }, nil) // Fetch cluster network - networkClientRecorder.ListNetwork(&networks.ListOpts{ - ID: clusterNetworkID, - }).Return([]networks.Network{ - { - ID: clusterNetworkID, - Name: "cluster-network", - }, + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", }, nil) networkClientRecorder.GetSubnet(clusterSubnets[0]).Return(&subnets.Subnet{ diff --git a/controllers/openstackfloatingippool_controller.go b/controllers/openstackfloatingippool_controller.go index f002752111..01e496d636 100644 --- a/controllers/openstackfloatingippool_controller.go +++ b/controllers/openstackfloatingippool_controller.go @@ -22,7 +22,6 @@ import ( "fmt" "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -44,7 +43,6 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/filterconvert" ) const ( @@ -400,23 +398,15 @@ func (r *OpenStackFloatingIPPoolReconciler) reconcileFloatingIPNetwork(scope *sc return err } - netListOpts := external.ListOptsExt{ - ListOptsBuilder: filterconvert.NetworkFilterToListOpts(&pool.Spec.FloatingIPNetwork), - External: pointer.Bool(true), - } - - networkList, err := networkingService.GetNetworksByFilter(&netListOpts) + network, err := networkingService.GetNetworkByParam(&pool.Spec.FloatingIPNetwork) if err != nil { return fmt.Errorf("failed to find network: %w", err) } - if len(networkList) > 1 { - return fmt.Errorf("found multiple networks, expects filter to match one (result: %v)", networkList) - } pool.Status.FloatingIPNetwork = &infrav1.NetworkStatus{ - ID: networkList[0].ID, - Name: networkList[0].Name, - Tags: networkList[0].Tags, + ID: network.ID, + Name: network.Name, + Tags: network.Tags, } return nil } diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index fc84797f7a..4972fb0eb2 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -102,8 +102,8 @@ specified. If specified, no new router will be created.

network
- -NetworkFilter + +NetworkParam @@ -165,8 +165,8 @@ This is necessary if the router needs a fixed ip in a specific subnet.

externalNetwork
- -NetworkFilter + +NetworkParam @@ -1806,10 +1806,10 @@ bool

(Appears on: -OpenStackClusterSpec, -PortOpts) +NetworkParam)

+

NetworkFilter specifies a query to select an OpenStack network. At least one property must be set.

@@ -1851,27 +1851,63 @@ string + + + +
+FilterByNeutronTags
+ + +FilterByNeutronTags + + +
+

+(Members of FilterByNeutronTags are embedded into this type.) +

+
+

NetworkParam +

+

+(Appears on: +OpenStackClusterSpec, +PortOpts) +

+

+

NetworkParam specifies an OpenStack network. It may be specified by either ID or Filter, but not both.

+

+ + + + + + + + + + @@ -2037,8 +2073,8 @@ specified. If specified, no new router will be created.

@@ -2100,8 +2136,8 @@ This is necessary if the router needs a fixed ip in a specific subnet.

@@ -2619,8 +2655,8 @@ specified. If specified, no new router will be created.

@@ -2682,8 +2718,8 @@ This is necessary if the router needs a fixed ip in a specific subnet.

@@ -3654,8 +3690,8 @@ OpenStackMachineTemplateResource diff --git a/pkg/cloud/services/compute/referenced_resources_test.go b/pkg/cloud/services/compute/referenced_resources_test.go index 1f13ce5427..5d3c3455d7 100644 --- a/pkg/cloud/services/compute/referenced_resources_test.go +++ b/pkg/cloud/services/compute/referenced_resources_test.go @@ -128,8 +128,8 @@ func Test_ResolveReferencedMachineResources(t *testing.T) { Image: infrav1.ImageFilter{ID: pointer.String(imageID1)}, Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkID2, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkID2), }, }, }, diff --git a/pkg/cloud/services/networking/network.go b/pkg/cloud/services/networking/network.go index fbee4d4b10..4192d05e35 100644 --- a/pkg/cloud/services/networking/network.go +++ b/pkg/cloud/services/networking/network.go @@ -17,6 +17,7 @@ limitations under the License. package networking import ( + "errors" "fmt" "github.com/gophercloud/gophercloud" @@ -78,42 +79,45 @@ func (c createOpts) ToNetworkCreateMap() (map[string]interface{}, error) { // - no external network was given in the cluster spec and no external network was found // - the user has set OpenStackCluster.Spec.DisableExternalNetwork to true. func (s *Service) ReconcileExternalNetwork(openStackCluster *infrav1.OpenStackCluster) error { - var listOpts external.ListOptsExt - if pointer.BoolDeref(openStackCluster.Spec.DisableExternalNetwork, false) { s.scope.Logger().Info("External network is disabled - proceeding with internal network only") openStackCluster.Status.ExternalNetwork = nil return nil } - listOpts = external.ListOptsExt{ - ListOptsBuilder: filterconvert.NetworkFilterToListOpts(openStackCluster.Spec.ExternalNetwork), - External: pointer.Bool(true), - } - networkList, err := s.client.ListNetwork(listOpts) - if err != nil { - return err - } + var network *networks.Network + if openStackCluster.Spec.ExternalNetwork == nil { + // No external network specified in the cluster spec. Default behaviour: query all external networks. + // * If there's only one, use that. + // * If there's none don't use an external network. + // * If there's more than one it's an error. - switch len(networkList) { - case 0: - if openStackCluster.Spec.ExternalNetwork == nil { - // Not finding an external network is fine if ExternalNetwork is not set + // Empty NetworkFilter will query all networks + var err error + network, err = s.getNetworkByFilter(&infrav1.NetworkFilter{}, ExternalNetworksOnly) + if errors.Is(err, ErrNoMatches) { openStackCluster.Status.ExternalNetwork = nil s.scope.Logger().Info("No external network found - proceeding with internal network only") return nil } - return fmt.Errorf("no external network found") - case 1: - openStackCluster.Status.ExternalNetwork = &infrav1.NetworkStatus{ - ID: networkList[0].ID, - Name: networkList[0].Name, - Tags: networkList[0].Tags, + if err != nil { + return fmt.Errorf("failed to get external network: %w", err) } - s.scope.Logger().Info("External network found", "id", networkList[0].ID) - return nil + } else { + var err error + network, err = s.GetNetworkByParam(openStackCluster.Spec.ExternalNetwork, ExternalNetworksOnly) + if err != nil { + return fmt.Errorf("failed to get external network: %w", err) + } + } + + openStackCluster.Status.ExternalNetwork = &infrav1.NetworkStatus{ + ID: network.ID, + Name: network.Name, + Tags: network.Tags, } - return fmt.Errorf("found %d external networks, which should not happen", len(networkList)) + s.scope.Logger().Info("External network found", "id", network.ID) + return nil } func (s *Service) ReconcileNetwork(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error { @@ -290,32 +294,66 @@ func (s *Service) getNetworkByName(networkName string) (networks.Network, error) return networks.Network{}, fmt.Errorf("found %d networks with the name %s, which should not happen", len(networkList), networkName) } -// GetNetworksByFilter retrieves networks by querying openstack with filters. -func (s *Service) GetNetworksByFilter(opts networks.ListOptsBuilder) ([]networks.Network, error) { - if opts == nil { - return nil, fmt.Errorf("no Filters were passed") +type GetNetworkOpts func(networks.ListOptsBuilder) networks.ListOptsBuilder + +func ExternalNetworksOnly(opts networks.ListOptsBuilder) networks.ListOptsBuilder { + return &external.ListOptsExt{ + ListOptsBuilder: opts, + External: pointer.Bool(true), } - networkList, err := s.client.ListNetwork(opts) +} + +// getNetworksByFilter retrieves networks by querying openstack with filters. +func (s *Service) getNetworkByFilter(filter *infrav1.NetworkFilter, opts ...GetNetworkOpts) (*networks.Network, error) { + var listOpts networks.ListOptsBuilder + listOpts = filterconvert.NetworkFilterToListOpts(filter) + for _, opt := range opts { + listOpts = opt(listOpts) + } + + networks, err := s.client.ListNetwork(listOpts) if err != nil { return nil, err } - if len(networkList) == 0 { - return nil, fmt.Errorf("no networks could be found with the filters provided") + if len(networks) == 0 { + return nil, ErrNoMatches + } + if len(networks) > 1 { + return nil, ErrMultipleMatches } - return networkList, nil + return &networks[0], nil } -// GetNetworkIDsByFilter retrieves network ids by querying openstack with filters. -func (s *Service) GetNetworkIDsByFilter(opts networks.ListOptsBuilder) ([]string, error) { - nets, err := s.GetNetworksByFilter(opts) - if err != nil { - return nil, err +// GetNetworkByParam gets the network specified by the given NetworkParam. +func (s *Service) GetNetworkByParam(param *infrav1.NetworkParam, opts ...GetNetworkOpts) (*networks.Network, error) { + if param.ID != nil { + return s.GetNetworkByID(*param.ID) } - ids := []string{} - for _, network := range nets { - ids = append(ids, network.ID) + + if param.Filter == nil { + return nil, errors.New("no filter or ID provided") + } + + return s.getNetworkByFilter(param.Filter, opts...) +} + +// GetNetworkIDByParam returns the ID of the network specified by the given +// NetworkParam. It does not make an OpenStack call if the network is specified +// by ID. +func (s *Service) GetNetworkIDByParam(param *infrav1.NetworkParam, opts ...GetNetworkOpts) (string, error) { + if param.ID != nil { + return *param.ID, nil + } + + if param.Filter == nil { + return "", errors.New("no filter or ID provided") + } + + network, err := s.getNetworkByFilter(param.Filter, opts...) + if err != nil { + return "", err } - return ids, nil + return network.ID, nil } // GetSubnetsByFilter gets the id of a subnet by querying openstack with filters. diff --git a/pkg/cloud/services/networking/network_test.go b/pkg/cloud/services/networking/network_test.go index ff772ffe76..41fa452838 100644 --- a/pkg/cloud/services/networking/network_test.go +++ b/pkg/cloud/services/networking/network_test.go @@ -21,6 +21,7 @@ import ( "github.com/go-logr/logr/testr" "github.com/golang/mock/gomock" + "github.com/google/go-cmp/cmp" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" @@ -205,16 +206,26 @@ func Test_ReconcileNetwork(t *testing.T) { } func Test_ReconcileExternalNetwork(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeNetworkID := "d08803fc-2fa5-4179-b9f7-8c43d0af2fe6" fakeNetworkname := "external-network" + // Use gomega to match the ListOptsBuilder argument + getExternalNetwork := func(g Gomega, listOpts networks.ListOpts, ret []networks.Network) func(networks.ListOptsBuilder) ([]networks.Network, error) { + return func(opts networks.ListOptsBuilder) ([]networks.Network, error) { + expected := &external.ListOptsExt{ + ListOptsBuilder: listOpts, + External: pointer.Bool(true), + } + g.Expect(opts).To(Equal(expected), cmp.Diff(opts, expected)) + + return ret, nil + } + } + tests := []struct { name string openStackCluster *infrav1.OpenStackCluster - expect func(m *mock.MockNetworkClientMockRecorder) + expect func(g Gomega, m *mock.MockNetworkClientMockRecorder) want *infrav1.OpenStackCluster wantErr bool }{ @@ -222,28 +233,21 @@ func Test_ReconcileExternalNetwork(t *testing.T) { name: "reconcile external network by ID", openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - ID: fakeNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(fakeNetworkID), }, }, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { - m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{ID: fakeNetworkID}, - External: pointer.Bool(true), - }). - Return([]networks.Network{ - { - ID: fakeNetworkID, - Name: fakeNetworkname, - }, - }, nil) + expect: func(_ Gomega, m *mock.MockNetworkClientMockRecorder) { + m.GetNetwork(fakeNetworkID).Return(&networks.Network{ + ID: fakeNetworkID, + Name: fakeNetworkname, + }, nil) }, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - ID: fakeNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(fakeNetworkID), }, }, Status: infrav1.OpenStackClusterStatus{ @@ -259,28 +263,24 @@ func Test_ReconcileExternalNetwork(t *testing.T) { name: "reconcile external network by name", openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - Name: fakeNetworkname, + ExternalNetwork: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: fakeNetworkname}, }, }, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { - m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{Name: fakeNetworkname}, - External: pointer.Bool(true), - }). - Return([]networks.Network{ + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { + m.ListNetwork(gomock.Any()). + DoAndReturn(getExternalNetwork(g, networks.ListOpts{Name: fakeNetworkname}, []networks.Network{ { ID: fakeNetworkID, Name: fakeNetworkname, }, - }, nil) + })) }, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - Name: fakeNetworkname, + ExternalNetwork: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: fakeNetworkname}, }, }, Status: infrav1.OpenStackClusterStatus{ @@ -293,26 +293,43 @@ func Test_ReconcileExternalNetwork(t *testing.T) { wantErr: false, }, { - name: "reconcile external network by ID when no external network found", + name: "reconcile external network by ID when external network by id not found", openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - ID: fakeNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(fakeNetworkID), }, }, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { - m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{ID: fakeNetworkID}, - External: pointer.Bool(true), - }). - Return([]networks.Network{}, nil) + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { + m.GetNetwork(fakeNetworkID).Return(nil, gophercloud.ErrDefault404{}) }, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ - ExternalNetwork: &infrav1.NetworkFilter{ - ID: fakeNetworkID, + ExternalNetwork: &infrav1.NetworkParam{ + ID: pointer.String(fakeNetworkID), + }, + }, + }, + wantErr: true, + }, + { + name: "reconcile external network by ID when external network by name not found", + openStackCluster: &infrav1.OpenStackCluster{ + Spec: infrav1.OpenStackClusterSpec{ + ExternalNetwork: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: fakeNetworkname}, + }, + }, + }, + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { + m.ListNetwork(gomock.Any()). + DoAndReturn(getExternalNetwork(g, networks.ListOpts{Name: fakeNetworkname}, []networks.Network{})) + }, + want: &infrav1.OpenStackCluster{ + Spec: infrav1.OpenStackClusterSpec{ + ExternalNetwork: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: fakeNetworkname}, }, }, }, @@ -325,7 +342,7 @@ func Test_ReconcileExternalNetwork(t *testing.T) { DisableExternalNetwork: pointer.Bool(true), }, }, - expect: func(m *mock.MockNetworkClientMockRecorder) {}, + expect: func(_ Gomega, m *mock.MockNetworkClientMockRecorder) {}, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{ DisableExternalNetwork: pointer.Bool(true), @@ -337,17 +354,13 @@ func Test_ReconcileExternalNetwork(t *testing.T) { wantErr: false, }, { - name: "reconcile external network with no filter when zero external network found", + name: "reconcile external network with no filter when zero external networks found", openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{}, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { - m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{}, - External: pointer.Bool(true), - }). - Return([]networks.Network{}, nil) + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { + m.ListNetwork(gomock.Any()). + DoAndReturn(getExternalNetwork(g, networks.ListOpts{}, []networks.Network{})) }, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{}, @@ -362,18 +375,14 @@ func Test_ReconcileExternalNetwork(t *testing.T) { openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{}, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { - m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{}, - External: pointer.Bool(true), - }). - Return([]networks.Network{ + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { + m.ListNetwork(gomock.Any()). + DoAndReturn(getExternalNetwork(g, networks.ListOpts{}, []networks.Network{ { ID: fakeNetworkID, Name: fakeNetworkname, }, - }, nil) + })) }, want: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{}, @@ -391,22 +400,27 @@ func Test_ReconcileExternalNetwork(t *testing.T) { openStackCluster: &infrav1.OpenStackCluster{ Spec: infrav1.OpenStackClusterSpec{}, }, - expect: func(m *mock.MockNetworkClientMockRecorder) { + expect: func(g Gomega, m *mock.MockNetworkClientMockRecorder) { m. - ListNetwork(external.ListOptsExt{ - ListOptsBuilder: networks.ListOpts{}, - External: pointer.Bool(true), - }). - Return([]networks.Network{ - { - ID: fakeNetworkID, - Name: fakeNetworkname, - }, - { - ID: "d08803fc-2fa5-4179-b9f7-8c43d0af2fe7", - Name: "external-network-2", - }, - }, nil) + ListNetwork(gomock.Any()). + DoAndReturn(func(opts networks.ListOptsBuilder) ([]networks.Network, error) { + expected := &external.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + External: pointer.Bool(true), + } + g.Expect(opts).To(Equal(expected), cmp.Diff(opts, expected)) + + return []networks.Network{ + { + ID: fakeNetworkID, + Name: fakeNetworkname, + }, + { + ID: "d08803fc-2fa5-4179-b9f7-8c43d0af2fe7", + Name: "external-network-2", + }, + }, nil + }) }, want: &infrav1.OpenStackCluster{}, wantErr: true, @@ -415,9 +429,12 @@ func Test_ReconcileExternalNetwork(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + g := NewWithT(t) mockClient := mock.NewMockNetworkClient(mockCtrl) - tt.expect(mockClient.EXPECT()) + tt.expect(g, mockClient.EXPECT()) scopeFactory := scope.NewMockScopeFactory(mockCtrl, "") log := testr.New(t) @@ -429,7 +446,7 @@ func Test_ReconcileExternalNetwork(t *testing.T) { if (err != nil) != tt.wantErr { t.Errorf("ReconcileExternalNetwork() error = %v, wantErr %v", err, tt.wantErr) } - g.Expect(tt.openStackCluster).To(Equal(tt.want)) + g.Expect(tt.openStackCluster).To(Equal(tt.want), cmp.Diff(tt.openStackCluster, tt.want)) }) } } diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go index f1fbfef6ce..8677b87a04 100644 --- a/pkg/cloud/services/networking/port.go +++ b/pkg/cloud/services/networking/port.go @@ -35,7 +35,6 @@ import ( "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/filterconvert" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names" ) @@ -469,8 +468,12 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, defaultNetwork *in } switch { - case port.Network != nil && port.Network.ID != "": - networkID = port.Network.ID + case port.Network != nil: + var err error + networkID, err = s.GetNetworkIDByParam(port.Network) + if err != nil { + return "", nil, err + } // No network, but fixed IPs are defined(we handled the no fixed // IPs case above): try to infer network from a subnet @@ -513,21 +516,9 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, defaultNetwork *in return "", nil, err } - // Network is defined by filter default: - networkListOpts := filterconvert.NetworkFilterToListOpts(port.Network) - netIDs, err := s.GetNetworkIDsByFilter(networkListOpts) - if err != nil { - return "", nil, err - } - - // TODO: These are spec errors: they should set the machine to failed - if len(netIDs) > 1 { - return "", nil, fmt.Errorf("network filter for port %d returns more than one result", portIdx) - } else if len(netIDs) == 0 { - return "", nil, fmt.Errorf("network filter for port %d returns no networks", portIdx) - } - networkID = netIDs[0] + // TODO: This is a spec errors: it should set the machine to failed + return "", nil, fmt.Errorf("unable to determine network for port %d", portIdx) } // Network ID is now known. Resolve all FixedIPs diff --git a/pkg/cloud/services/networking/port_test.go b/pkg/cloud/services/networking/port_test.go index 466ee35a89..8fb6e035b2 100644 --- a/pkg/cloud/services/networking/port_test.go +++ b/pkg/cloud/services/networking/port_test.go @@ -475,8 +475,8 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - ID: networkID, + Network: &infrav1.NetworkParam{ + ID: pointer.String(networkID), }, }, }, @@ -497,8 +497,8 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkFilter{ - Name: "test-network", + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: "test-network"}, }, }, }, @@ -858,7 +858,7 @@ func Test_getPortName(t *testing.T) { instanceName: "test-1-instance", spec: &infrav1.PortOpts{ NameSuffix: pointer.String("foo2"), - Network: &infrav1.NetworkFilter{ID: "bar"}, + Network: &infrav1.NetworkParam{ID: pointer.String("bar")}, ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ DisablePortSecurity: pointer.Bool(true), }, diff --git a/pkg/utils/filterconvert/convert.go b/pkg/utils/filterconvert/convert.go index a1574cc49f..01ce2126ec 100644 --- a/pkg/utils/filterconvert/convert.go +++ b/pkg/utils/filterconvert/convert.go @@ -71,7 +71,6 @@ func NetworkFilterToListOpts(networkFilter *infrav1.NetworkFilter) networks.List Name: networkFilter.Name, Description: networkFilter.Description, ProjectID: networkFilter.ProjectID, - ID: networkFilter.ID, Tags: infrav1.JoinTags(networkFilter.Tags), TagsAny: infrav1.JoinTags(networkFilter.TagsAny), NotTags: infrav1.JoinTags(networkFilter.NotTags), diff --git a/test/e2e/suites/apivalidations/filters_test.go b/test/e2e/suites/apivalidations/filters_test.go index b4e256cff8..1420ebb258 100644 --- a/test/e2e/suites/apivalidations/filters_test.go +++ b/test/e2e/suites/apivalidations/filters_test.go @@ -60,7 +60,7 @@ var _ = Describe("Filter API validations", func() { ports := make([]infrav1.PortOpts, len(tags)) for i := range tags { port := &ports[i] - port.Network = &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]} + port.Network = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}} port.FixedIPs = []infrav1.FixedIP{{Subnet: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}}} port.SecurityGroups = []infrav1.SecurityGroupFilter{{FilterByNeutronTags: tags[i]}} } @@ -74,8 +74,8 @@ var _ = Describe("Filter API validations", func() { } cluster.Spec.Subnets = subnets if len(tags) > 0 { - cluster.Spec.Network = &infrav1.NetworkFilter{FilterByNeutronTags: tags[0]} - cluster.Spec.ExternalNetwork = &infrav1.NetworkFilter{FilterByNeutronTags: tags[0]} + cluster.Spec.Network = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[0]}} + cluster.Spec.ExternalNetwork = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[0]}} cluster.Spec.Router = &infrav1.RouterFilter{FilterByNeutronTags: tags[0]} } Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed") @@ -107,7 +107,7 @@ var _ = Describe("Filter API validations", func() { { machine := machine.DeepCopy() machine.Spec.Ports = []infrav1.PortOpts{ - {Network: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}}, + {Network: &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}}}, } Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port network neutron tags") } @@ -138,13 +138,13 @@ var _ = Describe("Filter API validations", func() { { cluster := cluster.DeepCopy() - cluster.Spec.Network = &infrav1.NetworkFilter{FilterByNeutronTags: tag} + cluster.Spec.Network = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tag}} Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid network neutron tags") } { cluster := cluster.DeepCopy() - cluster.Spec.ExternalNetwork = &infrav1.NetworkFilter{FilterByNeutronTags: tag} + cluster.Spec.ExternalNetwork = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tag}} Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid external network neutron tags") }
FieldDescription
id
string
+(Optional) +

ID is the ID of the network to use. If ID is provided, the other filters cannot be provided. Must be in UUID format.

-FilterByNeutronTags
+filter
- -FilterByNeutronTags + +NetworkFilter
-

-(Members of FilterByNeutronTags are embedded into this type.) -

+(Optional) +

Filter specifies a filter to select an OpenStack network. If provided, cannot be empty.

network
- -NetworkFilter + +NetworkParam
externalNetwork
- -NetworkFilter + +NetworkParam
network
- -NetworkFilter + +NetworkParam
externalNetwork
- -NetworkFilter + +NetworkParam
network
- -NetworkFilter + +NetworkParam