From 4d85f6f7a4e04e9995091cfd543725f6ca979327 Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Tue, 26 Mar 2024 10:16:22 +0000 Subject: [PATCH] SubnetFilter to SubnetParam --- api/v1alpha5/conversion.go | 55 ++- api/v1alpha5/zz_generated.conversion.go | 43 +- api/v1alpha6/conversion_test.go | 68 +-- api/v1alpha6/openstackcluster_conversion.go | 19 +- api/v1alpha6/openstackmachine_conversion.go | 8 +- api/v1alpha6/types_conversion.go | 99 +++- api/v1alpha6/zz_generated.conversion.go | 43 +- api/v1alpha7/openstackcluster_conversion.go | 17 +- api/v1alpha7/types_conversion.go | 76 ++- api/v1alpha7/zz_generated.conversion.go | 23 +- api/v1beta1/openstackcluster_types.go | 2 +- api/v1beta1/types.go | 38 +- api/v1beta1/zz_generated.deepcopy.go | 29 +- ...re.cluster.x-k8s.io_openstackclusters.yaml | 449 +++++++++-------- ...er.x-k8s.io_openstackclustertemplates.yaml | 451 ++++++++++-------- ...re.cluster.x-k8s.io_openstackmachines.yaml | 149 +++--- ...er.x-k8s.io_openstackmachinetemplates.yaml | 150 +++--- controllers/openstackcluster_controller.go | 4 +- .../openstackcluster_controller_test.go | 24 +- docs/book/src/api/v1beta1/api.md | 74 ++- pkg/cloud/services/networking/network.go | 49 +- pkg/cloud/services/networking/port.go | 4 +- pkg/cloud/services/networking/port_test.go | 24 +- pkg/cloud/services/networking/router.go | 10 +- pkg/utils/filterconvert/convert.go | 1 - .../e2e/suites/apivalidations/filters_test.go | 56 ++- test/helpers/fuzzerfuncs.go | 8 +- 27 files changed, 1231 insertions(+), 742 deletions(-) diff --git a/api/v1alpha5/conversion.go b/api/v1alpha5/conversion.go index b8190e088a..19397b6956 100644 --- a/api/v1alpha5/conversion.go +++ b/api/v1alpha5/conversion.go @@ -217,7 +217,7 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha5_OpenStackClusterSpec(in *i if in.Subnets != nil { if len(in.Subnets) >= 1 { - if err := Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { return err } } @@ -254,11 +254,11 @@ func Convert_v1alpha5_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O emptySubnet := SubnetFilter{} if in.Subnet != emptySubnet { - subnet := infrav1.SubnetFilter{} - if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(&in.Subnet, &subnet, s); err != nil { + subnet := infrav1.SubnetParam{} + if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetParam(&in.Subnet, &subnet, s); err != nil { return err } - out.Subnets = []infrav1.SubnetFilter{subnet} + out.Subnets = []infrav1.SubnetParam{subnet} } if len(in.NodeCIDR) > 0 { @@ -547,25 +547,56 @@ func Convert_v1beta1_SecurityGroupFilter_To_v1alpha5_SecurityGroupParam(in *infr return nil } -func Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetFilter(in *SubnetParam, out *infrav1.SubnetFilter, s conversion.Scope) error { - if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(&in.Filter, out, s); err != nil { +func Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetParam(in *SubnetParam, out *infrav1.SubnetParam, s conversion.Scope) error { + if in.UUID != "" { + out.ID = &in.UUID + return nil + } + outFilter := &infrav1.SubnetFilter{} + if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(&in.Filter, outFilter, s); err != nil { return err } - if in.UUID != "" { - out.ID = in.UUID + if !outFilter.IsZero() { + out.Filter = outFilter } return nil } -func Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetParam(in *infrav1.SubnetFilter, out *SubnetParam, s conversion.Scope) error { - if err := Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(in, &out.Filter, s); err != nil { - return err +func Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetParam(in *infrav1.SubnetParam, out *SubnetParam, s conversion.Scope) error { + if in.ID != nil { + out.UUID = *in.ID + return nil + } + + if in.Filter != nil { + if err := Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(in.Filter, &out.Filter, s); err != nil { + return err + } } - out.UUID = in.ID return nil } +func Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetParam(in *SubnetFilter, out *infrav1.SubnetParam, s conversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + out.Filter = &infrav1.SubnetFilter{} + return Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(in, out.Filter, s) +} + +func Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetFilter(in *infrav1.SubnetParam, out *SubnetFilter, s conversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + if in.Filter != nil { + return Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(in.Filter, out, s) + } + return nil +} + func Convert_Map_string_To_Interface_To_v1beta1_BindingProfile(in map[string]string, out *infrav1.BindingProfile, _ conversion.Scope) error { for k, v := range in { if k == "capabilities" { diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index e8376912a3..8d24ce309a 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -317,8 +317,13 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*SubnetParam)(nil), (*v1beta1.SubnetFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetFilter(a.(*SubnetParam), b.(*v1beta1.SubnetFilter), scope) + if err := s.AddConversionFunc((*SubnetFilter)(nil), (*v1beta1.SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetParam(a.(*SubnetFilter), b.(*v1beta1.SubnetParam), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*SubnetParam)(nil), (*v1beta1.SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetParam(a.(*SubnetParam), b.(*v1beta1.SubnetParam), scope) }); err != nil { return err } @@ -417,8 +422,13 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*v1beta1.SubnetFilter)(nil), (*SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetParam(a.(*v1beta1.SubnetFilter), b.(*SubnetParam), scope) + if err := s.AddConversionFunc((*v1beta1.SubnetParam)(nil), (*SubnetFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetFilter(a.(*v1beta1.SubnetParam), b.(*SubnetFilter), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.SubnetParam)(nil), (*SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetParam(a.(*v1beta1.SubnetParam), b.(*SubnetParam), scope) }); err != nil { return err } @@ -496,7 +506,7 @@ func autoConvert_v1beta1_Bastion_To_v1alpha5_Bastion(in *v1beta1.Bastion, out *B func autoConvert_v1alpha5_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in *ExternalRouterIPParam, out *v1beta1.ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetFilter(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1alpha5_SubnetParam_To_v1beta1_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -509,7 +519,7 @@ func Convert_v1alpha5_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in func autoConvert_v1beta1_ExternalRouterIPParam_To_v1alpha5_ExternalRouterIPParam(in *v1beta1.ExternalRouterIPParam, out *ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -523,8 +533,8 @@ func Convert_v1beta1_ExternalRouterIPParam_To_v1alpha5_ExternalRouterIPParam(in func autoConvert_v1alpha5_FixedIP_To_v1beta1_FixedIP(in *FixedIP, out *v1beta1.FixedIP, s conversion.Scope) error { if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet - *out = new(v1beta1.SubnetFilter) - if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(*in, *out, s); err != nil { + *out = new(v1beta1.SubnetParam) + if err := Convert_v1alpha5_SubnetFilter_To_v1beta1_SubnetParam(*in, *out, s); err != nil { return err } } else { @@ -545,7 +555,7 @@ func autoConvert_v1beta1_FixedIP_To_v1alpha5_FixedIP(in *v1beta1.FixedIP, out *F if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet *out = new(SubnetFilter) - if err := Convert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha5_SubnetFilter(*in, *out, s); err != nil { return err } } else { @@ -1567,7 +1577,7 @@ func autoConvert_v1alpha5_SubnetFilter_To_v1beta1_SubnetFilter(in *SubnetFilter, out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - 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 @@ -1584,7 +1594,18 @@ func autoConvert_v1beta1_SubnetFilter_To_v1alpha5_SubnetFilter(in *v1beta1.Subne out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } + +func autoConvert_v1alpha5_SubnetParam_To_v1beta1_SubnetParam(in *SubnetParam, out *v1beta1.SubnetParam, s conversion.Scope) error { + // WARNING: in.UUID 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.SubnetFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubnetFilter) + return nil +} + +func autoConvert_v1beta1_SubnetParam_To_v1alpha5_SubnetParam(in *v1beta1.SubnetParam, out *SubnetParam, 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.SubnetFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5.SubnetFilter) + return nil +} diff --git a/api/v1alpha6/conversion_test.go b/api/v1alpha6/conversion_test.go index 0c1352767d..1456680359 100644 --- a/api/v1alpha6/conversion_test.go +++ b/api/v1alpha6/conversion_test.go @@ -286,8 +286,8 @@ func TestNetworksToPorts(t *testing.T) { }, FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - ID: subnetuuid, + Subnet: &infrav1.SubnetParam{ + ID: pointer.String(subnetuuid), }, }, }, @@ -330,20 +330,22 @@ func TestNetworksToPorts(t *testing.T) { }, FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "subnet-name", - Description: "subnet-description", - ProjectID: "project-id", - IPVersion: 6, - GatewayIP: "x.x.x.x", - CIDR: "y.y.y.y", - IPv6AddressMode: "address-mode", - IPv6RAMode: "ra-mode", - FilterByNeutronTags: infrav1.FilterByNeutronTags{ - Tags: []infrav1.NeutronTag{"tags"}, - TagsAny: []infrav1.NeutronTag{"tags-any"}, - NotTags: []infrav1.NeutronTag{"not-tags"}, - NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{ + Name: "subnet-name", + Description: "subnet-description", + ProjectID: "project-id", + IPVersion: 6, + GatewayIP: "x.x.x.x", + CIDR: "y.y.y.y", + IPv6AddressMode: "address-mode", + IPv6RAMode: "ra-mode", + FilterByNeutronTags: infrav1.FilterByNeutronTags{ + Tags: []infrav1.NeutronTag{"tags"}, + TagsAny: []infrav1.NeutronTag{"tags-any"}, + NotTags: []infrav1.NeutronTag{"not-tags"}, + NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + }, }, }, }, @@ -390,8 +392,8 @@ func TestNetworksToPorts(t *testing.T) { }, FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - ID: subnetuuid, + Subnet: &infrav1.SubnetParam{ + ID: pointer.String(subnetuuid), }, }, }, @@ -402,20 +404,22 @@ func TestNetworksToPorts(t *testing.T) { }, FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "subnet-name", - Description: "subnet-description", - ProjectID: "project-id", - IPVersion: 6, - GatewayIP: "x.x.x.x", - CIDR: "y.y.y.y", - IPv6AddressMode: "address-mode", - IPv6RAMode: "ra-mode", - FilterByNeutronTags: infrav1.FilterByNeutronTags{ - Tags: []infrav1.NeutronTag{"tags"}, - TagsAny: []infrav1.NeutronTag{"tags-any"}, - NotTags: []infrav1.NeutronTag{"not-tags"}, - NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{ + Name: "subnet-name", + Description: "subnet-description", + ProjectID: "project-id", + IPVersion: 6, + GatewayIP: "x.x.x.x", + CIDR: "y.y.y.y", + IPv6AddressMode: "address-mode", + IPv6RAMode: "ra-mode", + FilterByNeutronTags: infrav1.FilterByNeutronTags{ + Tags: []infrav1.NeutronTag{"tags"}, + TagsAny: []infrav1.NeutronTag{"tags-any"}, + NotTags: []infrav1.NeutronTag{"not-tags"}, + NotTagsAny: []infrav1.NeutronTag{"not-tags-any"}, + }, }, }, }, diff --git a/api/v1alpha6/openstackcluster_conversion.go b/api/v1alpha6/openstackcluster_conversion.go index 68242edd8c..66fd5896bb 100644 --- a/api/v1alpha6/openstackcluster_conversion.go +++ b/api/v1alpha6/openstackcluster_conversion.go @@ -152,7 +152,7 @@ func restorev1alpha6ClusterSpec(previous *OpenStackClusterSpec, dst *OpenStackCl if len(dst.ExternalRouterIPs) == len(previous.ExternalRouterIPs) { for i := range dst.ExternalRouterIPs { - restorev1alpha6SubnetFilter(&previous.ExternalRouterIPs[i].Subnet.Filter, &dst.ExternalRouterIPs[i].Subnet.Filter) + restorev1alpha6SubnetParam(&previous.ExternalRouterIPs[i].Subnet, &dst.ExternalRouterIPs[i].Subnet) } } @@ -179,10 +179,19 @@ func restorev1beta1ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *infr dst.NetworkMTU = previous.NetworkMTU dst.DisableExternalNetwork = previous.DisableExternalNetwork + if len(previous.Subnets) > 0 && len(dst.Subnets) > 0 { + restorev1beta1SubnetParam(&previous.Subnets[0], &dst.Subnets[0]) + } if len(previous.Subnets) > 1 { dst.Subnets = append(dst.Subnets, previous.Subnets[1:]...) } + if len(previous.ExternalRouterIPs) == len(dst.ExternalRouterIPs) { + for i := range dst.ExternalRouterIPs { + restorev1beta1SubnetParam(&previous.ExternalRouterIPs[i].Subnet, &dst.ExternalRouterIPs[i].Subnet) + } + } + dst.ManagedSubnets = previous.ManagedSubnets if previous.ManagedSecurityGroups != nil { @@ -232,11 +241,11 @@ func Convert_v1alpha6_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O emptySubnet := SubnetFilter{} if in.Subnet != emptySubnet { - subnet := infrav1.SubnetFilter{} - if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(&in.Subnet, &subnet, s); err != nil { + subnet := infrav1.SubnetParam{} + if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetParam(&in.Subnet, &subnet, s); err != nil { return err } - out.Subnets = []infrav1.SubnetFilter{subnet} + out.Subnets = []infrav1.SubnetParam{subnet} } // DNSNameservers without NodeCIDR doesn't make sense, so we drop that. @@ -295,7 +304,7 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in *i } if len(in.Subnets) >= 1 { - if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { return err } } diff --git a/api/v1alpha6/openstackmachine_conversion.go b/api/v1alpha6/openstackmachine_conversion.go index 9ab01741ea..d13eb5c124 100644 --- a/api/v1alpha6/openstackmachine_conversion.go +++ b/api/v1alpha6/openstackmachine_conversion.go @@ -223,18 +223,18 @@ func convertNetworksToPorts(networks []NetworkParam, s apiconversion.Scope) ([]i ports = append(ports, infrav1.PortOpts{ Network: networkFilter, FixedIPs: []infrav1.FixedIP{ - {Subnet: &infrav1.SubnetFilter{ID: subnet.UUID}}, + {Subnet: &infrav1.SubnetParam{ID: &subnet.UUID}}, }, }) } else { - subnetFilter := &infrav1.SubnetFilter{} - if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(&subnet.Filter, subnetFilter, s); err != nil { + subnetParam := &infrav1.SubnetParam{} + if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetParam(&subnet.Filter, subnetParam, s); err != nil { return nil, err } ports = append(ports, infrav1.PortOpts{ Network: networkFilter, FixedIPs: []infrav1.FixedIP{ - {Subnet: subnetFilter}, + {Subnet: subnetParam}, }, }) } diff --git a/api/v1alpha6/types_conversion.go b/api/v1alpha6/types_conversion.go index 80cefb86fc..1b2a642f34 100644 --- a/api/v1alpha6/types_conversion.go +++ b/api/v1alpha6/types_conversion.go @@ -24,6 +24,7 @@ import ( "k8s.io/utils/pointer" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" + optional "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/optional" ) const trueString = "true" @@ -164,29 +165,81 @@ func Convert_v1beta1_NetworkParam_To_v1alpha6_NetworkFilter(in *infrav1.NetworkP /* SubnetParam, SubnetFilter */ func restorev1alpha6SubnetFilter(previous *SubnetFilter, dst *SubnetFilter) { + 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 + + // We didn't convert other fields if ID was set + if previous.ID != "" { + dst.Name = previous.Name + dst.Description = previous.Description + dst.ProjectID = previous.ProjectID + dst.IPVersion = previous.IPVersion + dst.GatewayIP = previous.GatewayIP + dst.CIDR = previous.CIDR + dst.IPv6AddressMode = previous.IPv6AddressMode + dst.IPv6RAMode = previous.IPv6RAMode + } } -func Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetFilter(in *SubnetParam, out *infrav1.SubnetFilter, s apiconversion.Scope) error { - if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(&in.Filter, out, s); err != nil { - return err +func restorev1alpha6SubnetParam(previous *SubnetParam, dst *SubnetParam) { + if previous == nil || dst == nil { + return + } + + if previous.UUID != "" { + dst.Filter = previous.Filter + } else { + restorev1alpha6SubnetFilter(&previous.Filter, &dst.Filter) } +} + +func restorev1beta1SubnetParam(previous *infrav1.SubnetParam, dst *infrav1.SubnetParam) { + if previous == nil || dst == nil { + return + } + + optional.RestoreString(&previous.ID, &dst.ID) + + if dst.Filter != nil { + dst.Filter.FilterByNeutronTags = previous.Filter.FilterByNeutronTags + } +} + +func Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetParam(in *SubnetParam, out *infrav1.SubnetParam, s apiconversion.Scope) error { if in.UUID != "" { - out.ID = in.UUID + out.ID = &in.UUID + return nil + } + + outFilter := &infrav1.SubnetFilter{} + if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(&in.Filter, outFilter, s); err != nil { + return err + } + if !outFilter.IsZero() { + out.Filter = outFilter } return nil } -func Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetParam(in *infrav1.SubnetFilter, out *SubnetParam, s apiconversion.Scope) error { - if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(in, &out.Filter, s); err != nil { - return err +func Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetParam(in *infrav1.SubnetParam, out *SubnetParam, s apiconversion.Scope) error { + if in.ID != nil { + out.UUID = *in.ID + return nil + } + + if in.Filter != nil { + if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(in.Filter, &out.Filter, s); err != nil { + return err + } } - out.UUID = in.ID return nil } @@ -207,6 +260,36 @@ func Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(in *infrav1.SubnetFil return nil } +func Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetParam(in *SubnetFilter, out *infrav1.SubnetParam, s apiconversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + + outFilter := &infrav1.SubnetFilter{} + if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(in, outFilter, s); err != nil { + return err + } + if !outFilter.IsZero() { + out.Filter = outFilter + } + return nil +} + +func Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetFilter(in *infrav1.SubnetParam, out *SubnetFilter, s apiconversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + + if in.Filter != nil { + if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(in.Filter, out, s); err != nil { + return err + } + } + return nil +} + /* PortOpts, BindingProfile */ func restorev1alpha6Port(previous *PortOpts, dst *PortOpts) { diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index 34023deb82..9862d40792 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -336,8 +336,13 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*SubnetParam)(nil), (*v1beta1.SubnetFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetFilter(a.(*SubnetParam), b.(*v1beta1.SubnetFilter), scope) + if err := s.AddConversionFunc((*SubnetFilter)(nil), (*v1beta1.SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetParam(a.(*SubnetFilter), b.(*v1beta1.SubnetParam), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*SubnetParam)(nil), (*v1beta1.SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetParam(a.(*SubnetParam), b.(*v1beta1.SubnetParam), scope) }); err != nil { return err } @@ -431,8 +436,13 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*v1beta1.SubnetFilter)(nil), (*SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetParam(a.(*v1beta1.SubnetFilter), b.(*SubnetParam), scope) + if err := s.AddConversionFunc((*v1beta1.SubnetParam)(nil), (*SubnetFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetFilter(a.(*v1beta1.SubnetParam), b.(*SubnetFilter), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.SubnetParam)(nil), (*SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetParam(a.(*v1beta1.SubnetParam), b.(*SubnetParam), scope) }); err != nil { return err } @@ -520,7 +530,7 @@ func autoConvert_v1beta1_Bastion_To_v1alpha6_Bastion(in *v1beta1.Bastion, out *B func autoConvert_v1alpha6_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in *ExternalRouterIPParam, out *v1beta1.ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetFilter(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1alpha6_SubnetParam_To_v1beta1_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -533,7 +543,7 @@ func Convert_v1alpha6_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in func autoConvert_v1beta1_ExternalRouterIPParam_To_v1alpha6_ExternalRouterIPParam(in *v1beta1.ExternalRouterIPParam, out *ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -547,8 +557,8 @@ func Convert_v1beta1_ExternalRouterIPParam_To_v1alpha6_ExternalRouterIPParam(in func autoConvert_v1alpha6_FixedIP_To_v1beta1_FixedIP(in *FixedIP, out *v1beta1.FixedIP, s conversion.Scope) error { if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet - *out = new(v1beta1.SubnetFilter) - if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(*in, *out, s); err != nil { + *out = new(v1beta1.SubnetParam) + if err := Convert_v1alpha6_SubnetFilter_To_v1beta1_SubnetParam(*in, *out, s); err != nil { return err } } else { @@ -569,7 +579,7 @@ func autoConvert_v1beta1_FixedIP_To_v1alpha6_FixedIP(in *v1beta1.FixedIP, out *F if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet *out = new(SubnetFilter) - if err := Convert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha6_SubnetFilter(*in, *out, s); err != nil { return err } } else { @@ -1603,7 +1613,7 @@ func autoConvert_v1alpha6_SubnetFilter_To_v1beta1_SubnetFilter(in *SubnetFilter, out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - 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 @@ -1620,11 +1630,22 @@ func autoConvert_v1beta1_SubnetFilter_To_v1alpha6_SubnetFilter(in *v1beta1.Subne out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } +func autoConvert_v1alpha6_SubnetParam_To_v1beta1_SubnetParam(in *SubnetParam, out *v1beta1.SubnetParam, s conversion.Scope) error { + // WARNING: in.UUID 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.SubnetFilter vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubnetFilter) + return nil +} + +func autoConvert_v1beta1_SubnetParam_To_v1alpha6_SubnetParam(in *v1beta1.SubnetParam, out *SubnetParam, 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.SubnetFilter vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6.SubnetFilter) + return nil +} + func autoConvert_v1alpha6_ValueSpec_To_v1beta1_ValueSpec(in *ValueSpec, out *v1beta1.ValueSpec, s conversion.Scope) error { out.Name = in.Name out.Key = in.Key diff --git a/api/v1alpha7/openstackcluster_conversion.go b/api/v1alpha7/openstackcluster_conversion.go index 7ff49ecdcf..75e92032d1 100644 --- a/api/v1alpha7/openstackcluster_conversion.go +++ b/api/v1alpha7/openstackcluster_conversion.go @@ -191,10 +191,19 @@ func restorev1beta1ClusterSpec(previous *infrav1.OpenStackClusterSpec, dst *infr restorev1beta1NetworkParam(previous.Network, dst.Network) + if len(previous.Subnets) > 0 && len(dst.Subnets) > 0 { + restorev1beta1SubnetParam(&previous.Subnets[0], &dst.Subnets[0]) + } if len(previous.Subnets) > 1 { dst.Subnets = append(dst.Subnets, previous.Subnets[1:]...) } + if len(previous.ExternalRouterIPs) == len(dst.ExternalRouterIPs) { + for i := range dst.ExternalRouterIPs { + restorev1beta1SubnetParam(&previous.ExternalRouterIPs[i].Subnet, &dst.ExternalRouterIPs[i].Subnet) + } + } + dst.ManagedSubnets = previous.ManagedSubnets if previous.ManagedSecurityGroups != nil { @@ -244,11 +253,11 @@ func Convert_v1alpha7_OpenStackClusterSpec_To_v1beta1_OpenStackClusterSpec(in *O emptySubnet := SubnetFilter{} if in.Subnet != emptySubnet { - subnet := infrav1.SubnetFilter{} - if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(&in.Subnet, &subnet, s); err != nil { + subnet := infrav1.SubnetParam{} + if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetParam(&in.Subnet, &subnet, s); err != nil { return err } - out.Subnets = []infrav1.SubnetFilter{subnet} + out.Subnets = []infrav1.SubnetParam{subnet} } // DNSNameservers without NodeCIDR doesn't make sense, so we drop that. @@ -307,7 +316,7 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in *i } if len(in.Subnets) >= 1 { - if err := Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha7_SubnetFilter(&in.Subnets[0], &out.Subnet, s); err != nil { return err } } diff --git a/api/v1alpha7/types_conversion.go b/api/v1alpha7/types_conversion.go index 1139ebe05a..a63cd212ed 100644 --- a/api/v1alpha7/types_conversion.go +++ b/api/v1alpha7/types_conversion.go @@ -126,27 +126,74 @@ func Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(in *infrav1.NetworkP /* SubnetFilter */ func restorev1alpha7SubnetFilter(previous *SubnetFilter, dst *SubnetFilter) { + 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 will have lost all other fields in up-conversion + if previous.ID != "" { + dst.Name = previous.Name + dst.Description = previous.Description + dst.ProjectID = previous.ProjectID + dst.IPVersion = previous.IPVersion + dst.GatewayIP = previous.GatewayIP + dst.CIDR = previous.CIDR + dst.IPv6AddressMode = previous.IPv6AddressMode + dst.IPv6RAMode = previous.IPv6RAMode + } } -func Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(in *SubnetFilter, out *infrav1.SubnetFilter, s apiconversion.Scope) error { - if err := autoConvert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(in, out, s); err != nil { +func restorev1beta1SubnetParam(previous *infrav1.SubnetParam, dst *infrav1.SubnetParam) { + if previous == nil || dst == nil { + return + } + + optional.RestoreString(&previous.ID, &dst.ID) + + if dst.Filter != nil { + dst.Filter.FilterByNeutronTags = previous.Filter.FilterByNeutronTags + } +} + +func Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetParam(in *SubnetFilter, out *infrav1.SubnetParam, s apiconversion.Scope) error { + if in.ID != "" { + out.ID = &in.ID + return nil + } + + filter := &infrav1.SubnetFilter{} + if err := autoConvert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(in, filter, 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, &filter.FilterByNeutronTags) + + if !filter.IsZero() { + out.Filter = filter + } return nil } -func Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(in *infrav1.SubnetFilter, out *SubnetFilter, s apiconversion.Scope) error { - if err := autoConvert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(in, out, s); err != nil { +func Convert_v1beta1_SubnetParam_To_v1alpha7_SubnetFilter(in *infrav1.SubnetParam, out *SubnetFilter, s apiconversion.Scope) error { + if in.ID != nil { + out.ID = *in.ID + return nil + } + + if in.Filter == nil { + return nil + } + + if err := autoConvert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(in.Filter, out, s); err != nil { return err } - infrav1.ConvertAllTagsFrom(&in.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) + infrav1.ConvertAllTagsFrom(&in.Filter.FilterByNeutronTags, &out.Tags, &out.TagsAny, &out.NotTags, &out.NotTagsAny) return nil } @@ -214,9 +261,8 @@ func restorev1beta1Port(previous *infrav1.PortOpts, dst *infrav1.PortOpts) { prevFixedIP := &previous.FixedIPs[j] dstFixedIP := &dst.FixedIPs[j] - if dstFixedIP.IPAddress == nil || *dstFixedIP.IPAddress == "" { - dstFixedIP.IPAddress = prevFixedIP.IPAddress - } + optional.RestoreString(&prevFixedIP.IPAddress, &dstFixedIP.IPAddress) + restorev1beta1SubnetParam(prevFixedIP.Subnet, dstFixedIP.Subnet) } } @@ -225,9 +271,7 @@ func restorev1beta1Port(previous *infrav1.PortOpts, dst *infrav1.PortOpts) { prevAAP := &previous.AllowedAddressPairs[j] dstAAP := &dst.AllowedAddressPairs[j] - if dstAAP.MACAddress == nil || *dstAAP.MACAddress == "" { - dstAAP.MACAddress = prevAAP.MACAddress - } + optional.RestoreString(&prevAAP.MACAddress, &dstAAP.MACAddress) } } @@ -395,6 +439,14 @@ func Convert_v1beta1_OpenStackIdentityReference_To_v1alpha7_OpenStackIdentityRef // conversion-gen registers these functions so we must provider stubs, but // nothing should ever call them +func Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(_ *SubnetFilter, _ *infrav1.SubnetFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter should not be called") +} + +func Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(_ *infrav1.SubnetFilter, _ *SubnetFilter, _ apiconversion.Scope) error { + return errors.New("Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter should not be called") +} + 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") } diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 8b070e14bf..e2ae6041c7 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -381,6 +381,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*SubnetFilter)(nil), (*v1beta1.SubnetParam)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetParam(a.(*SubnetFilter), b.(*v1beta1.SubnetParam), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.BastionStatus)(nil), (*BastionStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_BastionStatus_To_v1alpha7_BastionStatus(a.(*v1beta1.BastionStatus), b.(*BastionStatus), scope) }); err != nil { @@ -451,6 +456,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.SubnetParam)(nil), (*SubnetFilter)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_SubnetParam_To_v1alpha7_SubnetFilter(a.(*v1beta1.SubnetParam), b.(*SubnetFilter), scope) + }); err != nil { + return err + } return nil } @@ -664,7 +674,7 @@ func Convert_v1beta1_BlockDeviceVolume_To_v1alpha7_BlockDeviceVolume(in *v1beta1 func autoConvert_v1alpha7_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in *ExternalRouterIPParam, out *v1beta1.ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetParam(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -677,7 +687,7 @@ func Convert_v1alpha7_ExternalRouterIPParam_To_v1beta1_ExternalRouterIPParam(in func autoConvert_v1beta1_ExternalRouterIPParam_To_v1alpha7_ExternalRouterIPParam(in *v1beta1.ExternalRouterIPParam, out *ExternalRouterIPParam, s conversion.Scope) error { out.FixedIP = in.FixedIP - if err := Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(&in.Subnet, &out.Subnet, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha7_SubnetFilter(&in.Subnet, &out.Subnet, s); err != nil { return err } return nil @@ -691,8 +701,8 @@ func Convert_v1beta1_ExternalRouterIPParam_To_v1alpha7_ExternalRouterIPParam(in func autoConvert_v1alpha7_FixedIP_To_v1beta1_FixedIP(in *FixedIP, out *v1beta1.FixedIP, s conversion.Scope) error { if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet - *out = new(v1beta1.SubnetFilter) - if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(*in, *out, s); err != nil { + *out = new(v1beta1.SubnetParam) + if err := Convert_v1alpha7_SubnetFilter_To_v1beta1_SubnetParam(*in, *out, s); err != nil { return err } } else { @@ -713,7 +723,7 @@ func autoConvert_v1beta1_FixedIP_To_v1alpha7_FixedIP(in *v1beta1.FixedIP, out *F if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet *out = new(SubnetFilter) - if err := Convert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(*in, *out, s); err != nil { + if err := Convert_v1beta1_SubnetParam_To_v1alpha7_SubnetFilter(*in, *out, s); err != nil { return err } } else { @@ -1783,7 +1793,7 @@ func autoConvert_v1alpha7_SubnetFilter_To_v1beta1_SubnetFilter(in *SubnetFilter, out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - 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 @@ -1800,7 +1810,6 @@ func autoConvert_v1beta1_SubnetFilter_To_v1alpha7_SubnetFilter(in *v1beta1.Subne out.CIDR = in.CIDR out.IPv6AddressMode = in.IPv6AddressMode out.IPv6RAMode = in.IPv6RAMode - out.ID = in.ID // WARNING: in.FilterByNeutronTags requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1beta1/openstackcluster_types.go b/api/v1beta1/openstackcluster_types.go index 35a2bf5505..b1566c01ac 100644 --- a/api/v1beta1/openstackcluster_types.go +++ b/api/v1beta1/openstackcluster_types.go @@ -58,7 +58,7 @@ type OpenStackClusterSpec struct { // +kubebuilder:validation:MaxItems=2 // +listType=atomic // +optional - Subnets []SubnetFilter `json:"subnets,omitempty"` + Subnets []SubnetParam `json:"subnets,omitempty"` // NetworkMTU sets the maximum transmission unit (MTU) value to address fragmentation for the private network ID. // This value will be used only if the Cluster actuator creates the network. diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index 34633a9261..0fe2f4a1f7 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -48,7 +48,7 @@ type ExternalRouterIPParam struct { // The FixedIP in the corresponding subnet FixedIP string `json:"fixedIP,omitempty"` // The subnet in which the FixedIP is used for the Gateway of this router - Subnet SubnetFilter `json:"subnet"` + Subnet SubnetParam `json:"subnet"` } // NeutronTag represents a tag on a Neutron resource. @@ -85,7 +85,7 @@ type FilterByNeutronTags struct { } func (f *FilterByNeutronTags) IsZero() bool { - return f == nil || len(f.Tags) == 0 && len(f.TagsAny) == 0 && len(f.NotTags) == 0 && len(f.NotTagsAny) == 0 + return f == nil || (len(f.Tags) == 0 && len(f.TagsAny) == 0 && len(f.NotTags) == 0 && len(f.NotTagsAny) == 0) } type SecurityGroupFilter struct { @@ -131,6 +131,22 @@ func (networkFilter *NetworkFilter) IsZero() bool { networkFilter.FilterByNeutronTags.IsZero() } +// SubnetParam specifies an OpenStack subnet to use. It may be specified by either ID or filter, but not both. +// +kubebuilder:validation:MaxProperties:=1 +// +kubebuilder:validation:MinProperties:=1 +type SubnetParam struct { + // ID is the uuid of the subnet. It will not be validated. + // +kubebuilder:validation:Format:=uuid + // +optional + ID optional.String `json:"id,omitempty"` + + // Filter specifies a filter to select the subnet. It must match exactly one subnet. + // +optional + Filter *SubnetFilter `json:"filter,omitempty"` +} + +// SubnetFilter specifies a filter to select a subnet. At least one parameter must be specified. +// +kubebuilder:validation:MinProperties:=1 type SubnetFilter struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` @@ -140,11 +156,25 @@ type SubnetFilter struct { CIDR string `json:"cidr,omitempty"` IPv6AddressMode string `json:"ipv6AddressMode,omitempty"` IPv6RAMode string `json:"ipv6RAMode,omitempty"` - ID string `json:"id,omitempty"` FilterByNeutronTags `json:",inline"` } +func (subnetFilter *SubnetFilter) IsZero() bool { + if subnetFilter == nil { + return true + } + return subnetFilter.Name == "" && + subnetFilter.Description == "" && + subnetFilter.ProjectID == "" && + subnetFilter.IPVersion == 0 && + subnetFilter.GatewayIP == "" && + subnetFilter.CIDR == "" && + subnetFilter.IPv6AddressMode == "" && + subnetFilter.IPv6RAMode == "" && + subnetFilter.FilterByNeutronTags.IsZero() +} + type RouterFilter struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` @@ -335,7 +365,7 @@ type FixedIP struct { // Subnet is an openstack subnet query that will return the id of a subnet to create // the fixed IP of a port in. This query must not return more than one subnet. // +optional - Subnet *SubnetFilter `json:"subnet,omitempty"` + Subnet *SubnetParam `json:"subnet,omitempty"` // IPAddress is a specific IP address to assign to the port. If Subnet // is also specified, IPAddress must be a valid IP address in the diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index bc21785a3b..6255ef3438 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -284,7 +284,7 @@ func (in *FixedIP) DeepCopyInto(out *FixedIP) { *out = *in if in.Subnet != nil { in, out := &in.Subnet, &out.Subnet - *out = new(SubnetFilter) + *out = new(SubnetParam) (*in).DeepCopyInto(*out) } if in.IPAddress != nil { @@ -566,7 +566,7 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { } if in.Subnets != nil { in, out := &in.Subnets, &out.Subnets - *out = make([]SubnetFilter, len(*in)) + *out = make([]SubnetParam, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1551,6 +1551,31 @@ func (in *SubnetFilter) DeepCopy() *SubnetFilter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetParam) DeepCopyInto(out *SubnetParam) { + *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(SubnetFilter) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetParam. +func (in *SubnetParam) DeepCopy() *SubnetParam { + if in == nil { + return nil + } + out := new(SubnetParam) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { *out = *in 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 f8146a4dc0..2a0546bb8c 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -5140,78 +5140,90 @@ spec: description: |- Subnet is an openstack subnet query that will return the id of a subnet to create the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to + select the subnet. It must match exactly + one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. + It will not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 type: object type: array @@ -5761,78 +5773,89 @@ spec: subnet: description: The subnet in which the FixedIP is used for the Gateway of this router + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to select the subnet. + It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. It will not be + validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 required: - subnet @@ -6157,78 +6180,90 @@ spec: all subnets in Network will be used. If 2 subnets are specified, one must be IPv4 and the other IPv6. items: + description: SubnetParam specifies an OpenStack subnet to use. It + may be specified by either ID or filter, but not both. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to select the subnet. + It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. It will not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 maxItems: 2 type: array diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml index 72e9aabe49..1eaba9daf1 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2570,78 +2570,90 @@ spec: description: |- Subnet is an openstack subnet query that will return the id of a subnet to create the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter + to select the subnet. It must match + exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the + subnet. It will not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 type: object type: array @@ -3197,78 +3209,89 @@ spec: subnet: description: The subnet in which the FixedIP is used for the Gateway of this router + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to select + the subnet. It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. It will + not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 required: - subnet @@ -3596,78 +3619,92 @@ spec: all subnets in Network will be used. If 2 subnets are specified, one must be IPv4 and the other IPv6. items: + description: SubnetParam specifies an OpenStack subnet to + use. It may be specified by either ID or filter, but not + both. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to select the + subnet. It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. It will not + be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 maxItems: 2 type: array 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 bc3d9658b0..c0463608c0 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -1918,78 +1918,89 @@ spec: description: |- Subnet is an openstack subnet query that will return the id of a subnet to create the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to select the + subnet. It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. It will + not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 type: object type: array 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 7c481ff2b8..2407fa4a06 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -1594,78 +1594,90 @@ spec: description: |- Subnet is an openstack subnet query that will return the id of a subnet to create the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string + filter: + description: Filter specifies a filter to + select the subnet. It must match exactly + one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + 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 uuid of the subnet. + It will not be validated. + format: uuid type: string - ipVersion: - type: integer - ipv6AddressMode: - type: string - ipv6RAMode: - 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 type: object type: array diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index 9c6e091254..c9283f11b7 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -885,9 +885,9 @@ func getClusterSubnets(networkingService *networking.Service, openStackCluster * } } else { for subnet := range openStackClusterSubnets { - filteredSubnet, err := networkingService.GetNetworkSubnetByFilter(networkID, &openStackClusterSubnets[subnet]) + filteredSubnet, err := networkingService.GetNetworkSubnetByParam(networkID, &openStackClusterSubnets[subnet]) if err != nil { - err = fmt.Errorf("failed to find subnet: %w", err) + err = fmt.Errorf("failed to find subnet %d in network %s: %w", subnet, networkID, err) if errors.Is(err, networking.ErrFilterMatch) { handleUpdateOSCError(openStackCluster, err) } diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go index 7f2aa20f1b..0b6fe63784 100644 --- a/controllers/openstackcluster_controller_test.go +++ b/controllers/openstackcluster_controller_test.go @@ -595,9 +595,9 @@ var _ = Describe("OpenStackCluster controller", func() { Network: &infrav1.NetworkParam{ ID: pointer.String(clusterNetworkID), }, - Subnets: []infrav1.SubnetFilter{ - {ID: clusterSubnets[0]}, - {ID: clusterSubnets[1]}, + Subnets: []infrav1.SubnetParam{ + {ID: &clusterSubnets[0]}, + {ID: &clusterSubnets[1]}, }, } testCluster.Status = infrav1.OpenStackClusterStatus{ @@ -636,15 +636,17 @@ var _ = Describe("OpenStackCluster controller", func() { }, nil) networkClientRecorder.GetSubnet(clusterSubnets[0]).Return(&subnets.Subnet{ - ID: clusterSubnets[0], - Name: "cluster-subnet", - CIDR: "192.168.0.0/24", + ID: clusterSubnets[0], + NetworkID: clusterNetworkID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", }, nil) networkClientRecorder.GetSubnet(clusterSubnets[1]).Return(&subnets.Subnet{ - ID: clusterSubnets[1], - Name: "cluster-subnet-v6", - CIDR: "2001:db8:2222:5555::/64", + ID: clusterSubnets[1], + NetworkID: clusterNetworkID, + Name: "cluster-subnet-v6", + CIDR: "2001:db8:2222:5555::/64", }, nil) err = reconcileNetworkComponents(scope, capiCluster, testCluster) @@ -661,8 +663,8 @@ var _ = Describe("OpenStackCluster controller", func() { DisableAPIServerFloatingIP: pointer.Bool(true), APIServerFixedIP: pointer.String("10.0.0.1"), DisableExternalNetwork: pointer.Bool(true), - Subnets: []infrav1.SubnetFilter{ - {ID: clusterSubnetID}, + Subnets: []infrav1.SubnetParam{ + {ID: pointer.String(clusterSubnetID)}, }, } err := k8sClient.Create(ctx, testCluster) diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index 527bf24ed0..24c303f15e 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -117,8 +117,8 @@ are specified.

subnets
- -[]SubnetFilter + +[]SubnetParam @@ -1435,8 +1435,8 @@ string subnet
- -SubnetFilter + +SubnetParam @@ -1548,8 +1548,8 @@ which contain any of the given tags will be excluded from the result.

subnet
- -SubnetFilter + +SubnetParam @@ -2093,8 +2093,8 @@ are specified.

subnets
- -[]SubnetFilter + +[]SubnetParam @@ -2675,8 +2675,8 @@ are specified.

subnets
- -[]SubnetFilter + +[]SubnetParam @@ -4804,11 +4804,10 @@ string

(Appears on: -ExternalRouterIPParam, -FixedIP, -OpenStackClusterSpec) +SubnetParam)

+

SubnetFilter specifies a filter to select a subnet. At least one parameter must be specified.

@@ -4900,27 +4899,64 @@ string + + + +
+FilterByNeutronTags
+ + +FilterByNeutronTags + + +
+

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

+
+

SubnetParam +

+

+(Appears on: +ExternalRouterIPParam, +FixedIP, +OpenStackClusterSpec) +

+

+

SubnetParam specifies an OpenStack subnet to use. It may be specified by either ID or filter, but not both.

+

+ + + + + + + + + + diff --git a/pkg/cloud/services/networking/network.go b/pkg/cloud/services/networking/network.go index 4192d05e35..716750e026 100644 --- a/pkg/cloud/services/networking/network.go +++ b/pkg/cloud/services/networking/network.go @@ -371,34 +371,51 @@ func (s *Service) GetSubnetsByFilter(opts subnets.ListOptsBuilder) ([]subnets.Su return subnetList, nil } -// GetSubnetByFilter gets a single subnet specified by the given SubnetFilter. -// It returns an ErrFilterMatch if no or multiple subnets are found. -func (s *Service) GetSubnetByFilter(filter *infrav1.SubnetFilter) (*subnets.Subnet, error) { - listOpts := filterconvert.SubnetFilterToListOpts(filter) - return s.getSubnetByFilter(listOpts) +// GetSubnetIDByParam gets the id of a subnet from the given SubnetParam. It +// does not make any OpenStack API calls if the subnet is specified by ID. +func (s *Service) GetSubnetIDByParam(param *infrav1.SubnetParam) (string, error) { + if param.ID != nil { + return *param.ID, nil + } + subnet, err := s.GetSubnetByParam(param) + if err != nil { + return "", err + } + return subnet.ID, nil } -// GetNetworkSubnetByFilter gets a single subnet of the given network, specified by the given SubnetFilter. +// GetSubnetByParam gets a single subnet specified by the given SubnetParam // It returns an ErrFilterMatch if no or multiple subnets are found. -func (s *Service) GetNetworkSubnetByFilter(networkID string, filter *infrav1.SubnetFilter) (*subnets.Subnet, error) { - listOpts := filterconvert.SubnetFilterToListOpts(filter) - listOpts.NetworkID = networkID - - return s.getSubnetByFilter(listOpts) +func (s *Service) GetSubnetByParam(param *infrav1.SubnetParam) (*subnets.Subnet, error) { + return s.GetNetworkSubnetByParam("", param) } -// getSubnetByFilter gets a single subnet specified by the given gophercloud ListOpts. +// GetNetworkSubnetByParam gets a single subnet of the given network, specified by the given SubnetParam. // It returns an ErrFilterMatch if no or multiple subnets are found. -func (s *Service) getSubnetByFilter(listOpts subnets.ListOpts) (*subnets.Subnet, error) { - // If the ID is set, we can just get the subnet by ID. - if listOpts.ID != "" { - subnet, err := s.client.GetSubnet(listOpts.ID) +func (s *Service) GetNetworkSubnetByParam(networkID string, param *infrav1.SubnetParam) (*subnets.Subnet, error) { + if param.ID != nil { + subnet, err := s.client.GetSubnet(*param.ID) if capoerrors.IsNotFound(err) { return nil, ErrNoMatches } + + if networkID != "" && subnet.NetworkID != networkID { + s.scope.Logger().V(4).Info("Subnet specified by ID does not belong to the given network", "subnetID", subnet.ID, "networkID", networkID) + return nil, ErrNoMatches + } return subnet, err } + if param.Filter == nil { + // Should have been caught by validation + return nil, errors.New("subnet filter: both id and filter are nil") + } + + listOpts := filterconvert.SubnetFilterToListOpts(param.Filter) + if networkID != "" { + listOpts.NetworkID = networkID + } + subnets, err := s.GetSubnetsByFilter(listOpts) if err != nil { return nil, err diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go index 8677b87a04..e75a650265 100644 --- a/pkg/cloud/services/networking/port.go +++ b/pkg/cloud/services/networking/port.go @@ -493,7 +493,7 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, defaultNetwork *in continue } - subnet, err := s.GetSubnetByFilter(fixedIP.Subnet) + subnet, err := s.GetSubnetByParam(fixedIP.Subnet) if err != nil { // Multiple matches might be ok later when we restrict matches to a single network if errors.Is(err, ErrMultipleMatches) { @@ -526,7 +526,7 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, defaultNetwork *in resolvedFixedIP := &resolvedFixedIPs[i] resolvedFixedIP.IPAddress = fixedIP.IPAddress if fixedIP.Subnet != nil && resolvedFixedIP.SubnetID == nil { - subnet, err := s.GetNetworkSubnetByFilter(networkID, fixedIP.Subnet) + subnet, err := s.GetNetworkSubnetByParam(networkID, fixedIP.Subnet) if err != nil { return "", nil, err } diff --git a/pkg/cloud/services/networking/port_test.go b/pkg/cloud/services/networking/port_test.go index 8fb6e035b2..ea0b6f3816 100644 --- a/pkg/cloud/services/networking/port_test.go +++ b/pkg/cloud/services/networking/port_test.go @@ -526,8 +526,8 @@ func TestService_ConstructPorts(t *testing.T) { { FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - ID: subnetID1, + Subnet: &infrav1.SubnetParam{ + ID: pointer.String(subnetID1), }, }, }, @@ -558,8 +558,8 @@ func TestService_ConstructPorts(t *testing.T) { { FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "test-subnet", + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, }, }, }, @@ -594,8 +594,8 @@ func TestService_ConstructPorts(t *testing.T) { { FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "test-subnet", + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, }, }, }, @@ -614,8 +614,8 @@ func TestService_ConstructPorts(t *testing.T) { { FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "test-subnet", + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, }, }, }, @@ -637,13 +637,13 @@ func TestService_ConstructPorts(t *testing.T) { { FixedIPs: []infrav1.FixedIP{ { - Subnet: &infrav1.SubnetFilter{ - Name: "test-subnet1", + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet1"}, }, }, { - Subnet: &infrav1.SubnetFilter{ - Name: "test-subnet2", + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet2"}, }, }, }, diff --git a/pkg/cloud/services/networking/router.go b/pkg/cloud/services/networking/router.go index e6267fdb30..7940222f74 100644 --- a/pkg/cloud/services/networking/router.go +++ b/pkg/cloud/services/networking/router.go @@ -168,13 +168,9 @@ func (s *Service) setRouterExternalIPs(openStackCluster *infrav1.OpenStackCluste for i := range openStackCluster.Spec.ExternalRouterIPs { externalRouterIP := openStackCluster.Spec.ExternalRouterIPs[i] - subnetID := externalRouterIP.Subnet.ID - if subnetID == "" { - subnet, err := s.GetSubnetByFilter(&externalRouterIP.Subnet) - if err != nil { - return fmt.Errorf("failed to get subnet for external router: %w", err) - } - subnetID = subnet.ID + subnetID, err := s.GetSubnetIDByParam(&externalRouterIP.Subnet) + if err != nil { + return fmt.Errorf("failed to get subnet for external router: %w", err) } updateOpts.GatewayInfo.ExternalFixedIPs = append(updateOpts.GatewayInfo.ExternalFixedIPs, routers.ExternalFixedIP{ IPAddress: externalRouterIP.FixedIP, diff --git a/pkg/utils/filterconvert/convert.go b/pkg/utils/filterconvert/convert.go index 01ce2126ec..c1194b7880 100644 --- a/pkg/utils/filterconvert/convert.go +++ b/pkg/utils/filterconvert/convert.go @@ -55,7 +55,6 @@ func SubnetFilterToListOpts(subnetFilter *infrav1.SubnetFilter) subnets.ListOpts CIDR: subnetFilter.CIDR, IPv6AddressMode: subnetFilter.IPv6AddressMode, IPv6RAMode: subnetFilter.IPv6RAMode, - ID: subnetFilter.ID, Tags: infrav1.JoinTags(subnetFilter.Tags), TagsAny: infrav1.JoinTags(subnetFilter.TagsAny), NotTags: infrav1.JoinTags(subnetFilter.NotTags), diff --git a/test/e2e/suites/apivalidations/filters_test.go b/test/e2e/suites/apivalidations/filters_test.go index aa8691df0e..ce0b38637b 100644 --- a/test/e2e/suites/apivalidations/filters_test.go +++ b/test/e2e/suites/apivalidations/filters_test.go @@ -61,16 +61,18 @@ var _ = Describe("Filter API validations", func() { for i := range tags { port := &ports[i] port.Network = &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}} - port.FixedIPs = []infrav1.FixedIP{{Subnet: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}}} + port.FixedIPs = []infrav1.FixedIP{{Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}, + }}} port.SecurityGroups = []infrav1.SecurityGroupFilter{{FilterByNeutronTags: tags[i]}} } Expect(k8sClient.Create(ctx, machine)).To(Succeed(), "OpenStackMachine creation should succeed") // Maximum of 2 subnets are supported nSubnets := min(len(tags), 2) - subnets := make([]infrav1.SubnetFilter, nSubnets) + subnets := make([]infrav1.SubnetParam, nSubnets) for i := 0; i < nSubnets; i++ { - subnets[i].FilterByNeutronTags = tags[i] + subnets[i].Filter = &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]} } cluster.Spec.Subnets = subnets if len(tags) > 0 { @@ -114,7 +116,9 @@ var _ = Describe("Filter API validations", func() { { machine := machine.DeepCopy() machine.Spec.Ports = []infrav1.PortOpts{ - {FixedIPs: []infrav1.FixedIP{{Subnet: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}}}}, + {FixedIPs: []infrav1.FixedIP{{Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}, + }}}}, } Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port subnet neutron tags") } @@ -132,7 +136,7 @@ var _ = Describe("Filter API validations", func() { { cluster := cluster.DeepCopy() - cluster.Spec.Subnets = []infrav1.SubnetFilter{{FilterByNeutronTags: tag}} + cluster.Spec.Subnets = []infrav1.SubnetParam{{Filter: &infrav1.SubnetFilter{FilterByNeutronTags: tag}}} Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail with invalid subnet neutron tags") } @@ -261,4 +265,46 @@ var _ = Describe("Filter API validations", func() { Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail") }) }) + + Context("SubnetParam", func() { + It("should allow setting ID", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{ + {ID: pointer.String("06c32c52-f207-4f6a-a769-bbcbe5a43f5c")}, + } + Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed") + }) + + It("should allow setting non-empty Filter", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{ + {Filter: &infrav1.SubnetFilter{Name: "foo"}}, + } + Expect(k8sClient.Create(ctx, cluster)).To(Succeed(), "OpenStackCluster creation should succeed") + }) + + It("should not allow setting empty SubnetParam", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{{}} + Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail") + }) + + It("should not allow setting invalid id", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{ + {ID: pointer.String("foo")}, + } + Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail") + }) + + It("should not allow setting empty Filter", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{ + {Filter: &infrav1.SubnetFilter{}}, + } + Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail") + }) + + It("should not allow setting both ID and Filter", func() { + cluster.Spec.Subnets = []infrav1.SubnetParam{ + {ID: pointer.String("06c32c52-f207-4f6a-a769-bbcbe5a43f5c"), Filter: &infrav1.SubnetFilter{Name: "foo"}}, + } + Expect(k8sClient.Create(ctx, cluster)).NotTo(Succeed(), "OpenStackCluster creation should fail") + }) + }) }) diff --git a/test/helpers/fuzzerfuncs.go b/test/helpers/fuzzerfuncs.go index da35973632..35720fcdd1 100644 --- a/test/helpers/fuzzerfuncs.go +++ b/test/helpers/fuzzerfuncs.go @@ -75,7 +75,7 @@ func InfraV1FuzzerFuncs() []interface{} { // length 1, but we need to also test length 2. // Ensure it is occasionally generated. if len(spec.Subnets) == 1 && c.RandBool() { - subnet := infrav1.SubnetFilter{} + subnet := infrav1.SubnetParam{} c.Fuzz(&subnet) spec.Subnets = append(spec.Subnets, subnet) } @@ -129,9 +129,13 @@ func InfraV1FuzzerFuncs() []interface{} { filter.NotTagsAny = filterInvalidTags(filter.NotTagsAny) }, - // v1beta1 network param contains exactly one of ID or filter + // v1beta1 filter params contain exactly one of ID or filter func(param *infrav1.NetworkParam, c fuzz.Continue) { fuzzFilterParam(¶m.ID, ¶m.Filter, c) }, + + func(param *infrav1.SubnetParam, c fuzz.Continue) { + fuzzFilterParam(¶m.ID, ¶m.Filter, c) + }, } }
FieldDescription
id
string
+(Optional) +

ID is the uuid of the subnet. It will not be validated.

-FilterByNeutronTags
+filter
- -FilterByNeutronTags + +SubnetFilter
-

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

+(Optional) +

Filter specifies a filter to select the subnet. It must match exactly one subnet.