diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index 44dbc571e2..628339325d 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -1351,10 +1351,8 @@ func autoConvert_v1alpha5_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta if err := optional.Convert_string_To_optional_String(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_string_To_optional_String(&in.MACAddress, &out.MACAddress, s); err != nil { - return err - } + // WARNING: in.AdminStateUp requires manual conversion: does not exist in peer-type + // WARNING: in.MACAddress requires manual conversion: does not exist in peer-type if in.FixedIPs != nil { in, out := &in.FixedIPs, &out.FixedIPs *out = make([]v1beta1.FixedIP, len(*in)) @@ -1370,26 +1368,12 @@ func autoConvert_v1alpha5_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta // WARNING: in.ProjectID requires manual conversion: does not exist in peer-type // INFO: in.SecurityGroups opted out of conversion generation // INFO: in.SecurityGroupFilters opted out of conversion generation - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]v1beta1.AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1alpha5_AddressPair_To_v1beta1_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } + // WARNING: in.AllowedAddressPairs requires manual conversion: does not exist in peer-type out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_string_To_optional_String(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_string_To_optional_String(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (map[string]string vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) + // WARNING: in.HostID requires manual conversion: does not exist in peer-type + // WARNING: in.VNICType requires manual conversion: does not exist in peer-type + // WARNING: in.Profile requires manual conversion: does not exist in peer-type + // WARNING: in.DisablePortSecurity requires manual conversion: does not exist in peer-type out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) return nil } @@ -1404,14 +1388,10 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha5_PortOpts(in *v1beta1.PortOpts, out } else { out.Network = nil } - if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { - return err - } if err := optional.Convert_optional_String_To_string(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_optional_String_To_string(&in.MACAddress, &out.MACAddress, s); err != nil { + if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { return err } if in.FixedIPs != nil { @@ -1436,29 +1416,9 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha5_PortOpts(in *v1beta1.PortOpts, out } else { out.SecurityGroups = nil } - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1beta1_AddressPair_To_v1alpha5_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } - out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_optional_String_To_string(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_optional_String_To_string(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile vs map[string]string) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) - // WARNING: in.PropagateUplinkStatus requires manual conversion: does not exist in peer-type out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) - // WARNING: in.ValueSpecs requires manual conversion: does not exist in peer-type + out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) + // WARNING: in.ResolvedPortSpecFields requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1alpha6/conversion_test.go b/api/v1alpha6/conversion_test.go index 984d7ffb46..3e5484cafd 100644 --- a/api/v1alpha6/conversion_test.go +++ b/api/v1alpha6/conversion_test.go @@ -570,7 +570,9 @@ func TestPortOptsConvertTo(t *testing.T) { SecurityGroups: uuids, }}, hubPortOpts: []infrav1.PortOpts{{ - Profile: &convertedPortProfile, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + Profile: &convertedPortProfile, + }, SecurityGroups: securityGroupsUuids, }}, }, @@ -582,7 +584,9 @@ func TestPortOptsConvertTo(t *testing.T) { SecurityGroupFilters: securityGroupFilter, }}, hubPortOpts: []infrav1.PortOpts{{ - Profile: &convertedPortProfile, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + Profile: &convertedPortProfile, + }, SecurityGroups: securityGroupFilterMerged, }}, }, diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index e3d5251fae..097ed5e67e 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -1386,10 +1386,8 @@ func autoConvert_v1alpha6_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta if err := optional.Convert_string_To_optional_String(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_string_To_optional_String(&in.MACAddress, &out.MACAddress, s); err != nil { - return err - } + // WARNING: in.AdminStateUp requires manual conversion: does not exist in peer-type + // WARNING: in.MACAddress requires manual conversion: does not exist in peer-type if in.FixedIPs != nil { in, out := &in.FixedIPs, &out.FixedIPs *out = make([]v1beta1.FixedIP, len(*in)) @@ -1405,28 +1403,14 @@ func autoConvert_v1alpha6_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta // WARNING: in.ProjectID requires manual conversion: does not exist in peer-type // INFO: in.SecurityGroups opted out of conversion generation // INFO: in.SecurityGroupFilters opted out of conversion generation - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]v1beta1.AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1alpha6_AddressPair_To_v1beta1_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } + // WARNING: in.AllowedAddressPairs requires manual conversion: does not exist in peer-type out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_string_To_optional_String(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_string_To_optional_String(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (map[string]string vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) + // WARNING: in.HostID requires manual conversion: does not exist in peer-type + // WARNING: in.VNICType requires manual conversion: does not exist in peer-type + // WARNING: in.Profile requires manual conversion: does not exist in peer-type + // WARNING: in.DisablePortSecurity requires manual conversion: does not exist in peer-type out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) - out.ValueSpecs = *(*[]v1beta1.ValueSpec)(unsafe.Pointer(&in.ValueSpecs)) + // WARNING: in.ValueSpecs requires manual conversion: does not exist in peer-type return nil } @@ -1440,14 +1424,10 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha6_PortOpts(in *v1beta1.PortOpts, out } else { out.Network = nil } - if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { - return err - } if err := optional.Convert_optional_String_To_string(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_optional_String_To_string(&in.MACAddress, &out.MACAddress, s); err != nil { + if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { return err } if in.FixedIPs != nil { @@ -1472,29 +1452,9 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha6_PortOpts(in *v1beta1.PortOpts, out } else { out.SecurityGroups = nil } - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1beta1_AddressPair_To_v1alpha6_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } - out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_optional_String_To_string(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_optional_String_To_string(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile vs map[string]string) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) - // WARNING: in.PropagateUplinkStatus requires manual conversion: does not exist in peer-type out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) - out.ValueSpecs = *(*[]ValueSpec)(unsafe.Pointer(&in.ValueSpecs)) + out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) + // WARNING: in.ResolvedPortSpecFields requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1alpha7/types_conversion.go b/api/v1alpha7/types_conversion.go index 7b966d1320..fbc6fde3e2 100644 --- a/api/v1alpha7/types_conversion.go +++ b/api/v1alpha7/types_conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v1alpha7 import ( + "errors" + apiconversion "k8s.io/apimachinery/pkg/conversion" "k8s.io/utils/pointer" @@ -253,6 +255,38 @@ func Convert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *infrav1.Po return err } + // Copy members of ResolvedPortSpecFields + var allowedAddressPairs []infrav1.AddressPair + if len(in.AllowedAddressPairs) > 0 { + allowedAddressPairs = make([]infrav1.AddressPair, len(in.AllowedAddressPairs)) + for i := range in.AllowedAddressPairs { + aap := &in.AllowedAddressPairs[i] + allowedAddressPairs[i] = infrav1.AddressPair{ + MACAddress: &aap.MACAddress, + IPAddress: aap.IPAddress, + } + } + } + var valueSpecs []infrav1.ValueSpec + if len(in.ValueSpecs) > 0 { + valueSpecs = make([]infrav1.ValueSpec, len(in.ValueSpecs)) + for i, vs := range in.ValueSpecs { + valueSpecs[i] = infrav1.ValueSpec(vs) + } + } + out.AdminStateUp = in.AdminStateUp + out.AllowedAddressPairs = allowedAddressPairs + out.DisablePortSecurity = in.DisablePortSecurity + out.PropagateUplinkStatus = in.PropagateUplinkStatus + out.ValueSpecs = valueSpecs + if err := errors.Join( + optional.Convert_string_To_optional_String(&in.MACAddress, &out.MACAddress, s), + optional.Convert_string_To_optional_String(&in.HostID, &out.HostID, s), + optional.Convert_string_To_optional_String(&in.VNICType, &out.VNICType, s), + ); err != nil { + return err + } + if len(in.SecurityGroupFilters) > 0 { out.SecurityGroups = make([]infrav1.SecurityGroupFilter, len(in.SecurityGroupFilters)) for i := range in.SecurityGroupFilters { @@ -277,6 +311,39 @@ func Convert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *infrav1.PortOpts, out *Po return err } + // Copy members of ResolvedPortSpecFields + var allowedAddressPairs []AddressPair + if len(in.AllowedAddressPairs) > 0 { + allowedAddressPairs = make([]AddressPair, len(in.AllowedAddressPairs)) + for i := range in.AllowedAddressPairs { + inAAP := &in.AllowedAddressPairs[i] + outAAP := &allowedAddressPairs[i] + if err := optional.Convert_optional_String_To_string(&inAAP.MACAddress, &outAAP.MACAddress, s); err != nil { + return err + } + outAAP.IPAddress = inAAP.IPAddress + } + } + var valueSpecs []ValueSpec + if len(in.ValueSpecs) > 0 { + valueSpecs = make([]ValueSpec, len(in.ValueSpecs)) + for i, vs := range in.ValueSpecs { + valueSpecs[i] = ValueSpec(vs) + } + } + out.AdminStateUp = in.AdminStateUp + out.AllowedAddressPairs = allowedAddressPairs + out.DisablePortSecurity = in.DisablePortSecurity + out.PropagateUplinkStatus = in.PropagateUplinkStatus + out.ValueSpecs = valueSpecs + if err := errors.Join( + optional.Convert_optional_String_To_string(&in.MACAddress, &out.MACAddress, s), + optional.Convert_optional_String_To_string(&in.HostID, &out.HostID, s), + optional.Convert_optional_String_To_string(&in.VNICType, &out.VNICType, s), + ); err != nil { + return err + } + if len(in.SecurityGroups) > 0 { out.SecurityGroupFilters = make([]SecurityGroupFilter, len(in.SecurityGroups)) for i := range in.SecurityGroups { diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 5f364226bb..a9acd45ad4 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -1586,10 +1586,8 @@ func autoConvert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta if err := optional.Convert_string_To_optional_String(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_string_To_optional_String(&in.MACAddress, &out.MACAddress, s); err != nil { - return err - } + // WARNING: in.AdminStateUp requires manual conversion: does not exist in peer-type + // WARNING: in.MACAddress requires manual conversion: does not exist in peer-type if in.FixedIPs != nil { in, out := &in.FixedIPs, &out.FixedIPs *out = make([]v1beta1.FixedIP, len(*in)) @@ -1602,29 +1600,15 @@ func autoConvert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta out.FixedIPs = nil } // WARNING: in.SecurityGroupFilters requires manual conversion: does not exist in peer-type - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]v1beta1.AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1alpha7_AddressPair_To_v1beta1_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } + // WARNING: in.AllowedAddressPairs requires manual conversion: does not exist in peer-type out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_string_To_optional_String(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_string_To_optional_String(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.BindingProfile vs *sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) - out.PropagateUplinkStatus = (*bool)(unsafe.Pointer(in.PropagateUplinkStatus)) + // WARNING: in.HostID requires manual conversion: does not exist in peer-type + // WARNING: in.VNICType requires manual conversion: does not exist in peer-type + // WARNING: in.Profile requires manual conversion: does not exist in peer-type + // WARNING: in.DisablePortSecurity requires manual conversion: does not exist in peer-type + // WARNING: in.PropagateUplinkStatus requires manual conversion: does not exist in peer-type out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) - out.ValueSpecs = *(*[]v1beta1.ValueSpec)(unsafe.Pointer(&in.ValueSpecs)) + // WARNING: in.ValueSpecs requires manual conversion: does not exist in peer-type return nil } @@ -1638,14 +1622,10 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *v1beta1.PortOpts, out } else { out.Network = nil } - if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { - return err - } if err := optional.Convert_optional_String_To_string(&in.Description, &out.Description, s); err != nil { return err } - out.AdminStateUp = (*bool)(unsafe.Pointer(in.AdminStateUp)) - if err := optional.Convert_optional_String_To_string(&in.MACAddress, &out.MACAddress, s); err != nil { + if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { return err } if in.FixedIPs != nil { @@ -1660,29 +1640,9 @@ func autoConvert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *v1beta1.PortOpts, out out.FixedIPs = nil } // WARNING: in.SecurityGroups requires manual conversion: does not exist in peer-type - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]AddressPair, len(*in)) - for i := range *in { - if err := Convert_v1beta1_AddressPair_To_v1alpha7_AddressPair(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.AllowedAddressPairs = nil - } - out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - if err := optional.Convert_optional_String_To_string(&in.HostID, &out.HostID, s); err != nil { - return err - } - if err := optional.Convert_optional_String_To_string(&in.VNICType, &out.VNICType, s); err != nil { - return err - } - // WARNING: in.Profile requires manual conversion: inconvertible types (*sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile vs sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7.BindingProfile) - out.DisablePortSecurity = (*bool)(unsafe.Pointer(in.DisablePortSecurity)) - out.PropagateUplinkStatus = (*bool)(unsafe.Pointer(in.PropagateUplinkStatus)) out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) - out.ValueSpecs = *(*[]ValueSpec)(unsafe.Pointer(&in.ValueSpecs)) + out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) + // WARNING: in.ResolvedPortSpecFields requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1beta1/openstackmachine_types.go b/api/v1beta1/openstackmachine_types.go index 743468399a..9ecfc356c0 100644 --- a/api/v1beta1/openstackmachine_types.go +++ b/api/v1beta1/openstackmachine_types.go @@ -59,7 +59,10 @@ type OpenStackMachineSpec struct { // Whether the server instance is created on a trunk port or not. Trunk bool `json:"trunk,omitempty"` - // Machine tags + // Tags which will be added to the machine and all dependent resources + // which support them. These are in addition to Tags defined on the + // cluster. + // // Requires Nova api 2.52 minimum! // +listType=set Tags []string `json:"tags,omitempty"` diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index 94625b20db..a1b20fdd6b 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -171,21 +171,13 @@ type PortOpts struct { // +optional Network *NetworkFilter `json:"network,omitempty"` - // NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used. - // +optional - NameSuffix optional.String `json:"nameSuffix,omitempty"` - // Description is a human-readable description for the port. // +optional Description optional.String `json:"description,omitempty"` - // AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up. - // +optional - AdminStateUp *bool `json:"adminStateUp,omitempty"` - - // MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated. + // NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used. // +optional - MACAddress optional.String `json:"macAddress,omitempty"` + NameSuffix optional.String `json:"nameSuffix,omitempty"` // FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network. // +optional @@ -197,13 +189,11 @@ type PortOpts struct { // +listType=atomic SecurityGroups []SecurityGroupFilter `json:"securityGroups,omitempty"` - // AllowedAddressPairs is a list of address pairs which Neutron will - // allow the port to send traffic from in addition to the port's - // addresses. If not specified, the MAC Address will be the MAC Address - // of the port. Depending on the configuration of Neutron, it may be - // supported to specify a CIDR instead of a specific IP address. + // Tags applied to the port (and corresponding trunk, if a trunk is configured.) + // These tags are applied in addition to the instance's tags, which will also be applied to the port. + // +listType=set // +optional - AllowedAddressPairs []AddressPair `json:"allowedAddressPairs,omitempty"` + Tags []string `json:"tags,omitempty"` // Trunk specifies whether trunking is enabled at the port level. If not // provided the value is inherited from the machine, or false for a @@ -211,6 +201,29 @@ type PortOpts struct { // +optional Trunk *bool `json:"trunk,omitempty"` + ResolvedPortSpecFields `json:",inline"` +} + +// ResolvePortSpecFields is a convenience struct containing all fields of a +// PortOpts which don't contain references which need to be resolved, and can +// therefore be shared with ResolvedPortSpec. +type ResolvedPortSpecFields struct { + // AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up. + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated. + // +optional + MACAddress optional.String `json:"macAddress,omitempty"` + + // AllowedAddressPairs is a list of address pairs which Neutron will + // allow the port to send traffic from in addition to the port's + // addresses. If not specified, the MAC Address will be the MAC Address + // of the port. Depending on the configuration of Neutron, it may be + // supported to specify a CIDR instead of a specific IP address. + // +optional + AllowedAddressPairs []AddressPair `json:"allowedAddressPairs,omitempty"` + // HostID specifies the ID of the host where the port resides. // +optional HostID optional.String `json:"hostID,omitempty"` @@ -245,12 +258,6 @@ type PortOpts struct { // +optional PropagateUplinkStatus *bool `json:"propagateUplinkStatus,omitempty"` - // Tags applied to the port (and corresponding trunk, if a trunk is configured.) - // These tags are applied in addition to the instance's tags, which will also be applied to the port. - // +listType=set - // +optional - Tags []string `json:"tags,omitempty"` - // Value specs are extra parameters to include in the API request with OpenStack. // This is an extension point for the API, so what they do and if they are supported, // depends on the specific OpenStack implementation. @@ -260,6 +267,39 @@ type PortOpts struct { ValueSpecs []ValueSpec `json:"valueSpecs,omitempty"` } +// ResolvedPortSpec is a PortOpts with all contained references fully resolved. +type ResolvedPortSpec struct { + // Name is the name of the port. + Name string `json:"name"` + + // Description is a human-readable description for the port. + Description string `json:"description"` + + // Tags applied to the port (and corresponding trunk, if a trunk is configured.) + // +listType=set + // +optional + Tags []string `json:"tags,omitempty"` + + // Trunk specifies whether trunking is enabled at the port level. + // +optional + Trunk bool `json:"trunk,omitempty"` + + // NetworkID is the ID of the network the port will be created in. + NetworkID string `json:"networkID"` + + // FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network. + // +optional + // +listType=atomic + FixedIPs []ResolvedFixedIP `json:"fixedIPs,omitempty"` + + // SecurityGroups is a list of security group IDs to assign to the port. + // +optional + // +listType=atomic + SecurityGroups []string `json:"securityGroups,omitempty"` + + ResolvedPortSpecFields `json:",inline"` +} + type PortStatus struct { // ID is the unique identifier of the port. // +required @@ -290,6 +330,20 @@ type FixedIP struct { IPAddress optional.String `json:"ipAddress,omitempty"` } +// ResolvedFixedIP is a FixedIP with the Subnet resolved to an ID. +type ResolvedFixedIP struct { + // SubnetID is the id of a subnet to create the fixed IP of a port in. + // +optional + SubnetID optional.String `json:"subnet,omitempty"` + + // IPAddress is a specific IP address to assign to the port. If SubnetID + // is also specified, IPAddress must be a valid IP address in the + // subnet. If Subnet is not specified, IPAddress must be a valid IP + // address in any subnet of the port's network. + // +optional + IPAddress optional.String `json:"ipAddress,omitempty"` +} + type AddressPair struct { // IPAddress is the IP address of the allowed address pair. Depending on // the configuration of Neutron, it may be supported to specify a CIDR @@ -663,7 +717,7 @@ type ReferencedMachineResources struct { // Ports is the fully resolved list of ports to create for the machine. // +optional - Ports []PortOpts `json:"ports,omitempty"` + Ports []ResolvedPortSpec `json:"ports,omitempty"` } type DependentMachineResources struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 2fe078f3dd..40c60308fc 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1074,23 +1074,13 @@ func (in *PortOpts) DeepCopyInto(out *PortOpts) { *out = new(NetworkFilter) (*in).DeepCopyInto(*out) } - if in.NameSuffix != nil { - in, out := &in.NameSuffix, &out.NameSuffix - *out = new(string) - **out = **in - } if in.Description != nil { in, out := &in.Description, &out.Description *out = new(string) **out = **in } - if in.AdminStateUp != nil { - in, out := &in.AdminStateUp, &out.AdminStateUp - *out = new(bool) - **out = **in - } - if in.MACAddress != nil { - in, out := &in.MACAddress, &out.MACAddress + if in.NameSuffix != nil { + in, out := &in.NameSuffix, &out.NameSuffix *out = new(string) **out = **in } @@ -1108,53 +1098,17 @@ func (in *PortOpts) DeepCopyInto(out *PortOpts) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.AllowedAddressPairs != nil { - in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs - *out = make([]AddressPair, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Trunk != nil { - in, out := &in.Trunk, &out.Trunk - *out = new(bool) - **out = **in - } - if in.HostID != nil { - in, out := &in.HostID, &out.HostID - *out = new(string) - **out = **in - } - if in.VNICType != nil { - in, out := &in.VNICType, &out.VNICType - *out = new(string) - **out = **in - } - if in.Profile != nil { - in, out := &in.Profile, &out.Profile - *out = new(BindingProfile) - (*in).DeepCopyInto(*out) - } - if in.DisablePortSecurity != nil { - in, out := &in.DisablePortSecurity, &out.DisablePortSecurity - *out = new(bool) - **out = **in - } - if in.PropagateUplinkStatus != nil { - in, out := &in.PropagateUplinkStatus, &out.PropagateUplinkStatus - *out = new(bool) - **out = **in - } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]string, len(*in)) copy(*out, *in) } - if in.ValueSpecs != nil { - in, out := &in.ValueSpecs, &out.ValueSpecs - *out = make([]ValueSpec, len(*in)) - copy(*out, *in) + if in.Trunk != nil { + in, out := &in.Trunk, &out.Trunk + *out = new(bool) + **out = **in } + in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortOpts. @@ -1187,7 +1141,7 @@ func (in *ReferencedMachineResources) DeepCopyInto(out *ReferencedMachineResourc *out = *in if in.Ports != nil { in, out := &in.Ports, &out.Ports - *out = make([]PortOpts, len(*in)) + *out = make([]ResolvedPortSpec, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1204,6 +1158,126 @@ func (in *ReferencedMachineResources) DeepCopy() *ReferencedMachineResources { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedFixedIP) DeepCopyInto(out *ResolvedFixedIP) { + *out = *in + if in.SubnetID != nil { + in, out := &in.SubnetID, &out.SubnetID + *out = new(string) + **out = **in + } + if in.IPAddress != nil { + in, out := &in.IPAddress, &out.IPAddress + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedFixedIP. +func (in *ResolvedFixedIP) DeepCopy() *ResolvedFixedIP { + if in == nil { + return nil + } + out := new(ResolvedFixedIP) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedPortSpec) DeepCopyInto(out *ResolvedPortSpec) { + *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.FixedIPs != nil { + in, out := &in.FixedIPs, &out.FixedIPs + *out = make([]ResolvedFixedIP, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SecurityGroups != nil { + in, out := &in.SecurityGroups, &out.SecurityGroups + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedPortSpec. +func (in *ResolvedPortSpec) DeepCopy() *ResolvedPortSpec { + if in == nil { + return nil + } + out := new(ResolvedPortSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedPortSpecFields) DeepCopyInto(out *ResolvedPortSpecFields) { + *out = *in + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(string) + **out = **in + } + if in.AllowedAddressPairs != nil { + in, out := &in.AllowedAddressPairs, &out.AllowedAddressPairs + *out = make([]AddressPair, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.HostID != nil { + in, out := &in.HostID, &out.HostID + *out = new(string) + **out = **in + } + if in.VNICType != nil { + in, out := &in.VNICType, &out.VNICType + *out = new(string) + **out = **in + } + if in.Profile != nil { + in, out := &in.Profile, &out.Profile + *out = new(BindingProfile) + (*in).DeepCopyInto(*out) + } + if in.DisablePortSecurity != nil { + in, out := &in.DisablePortSecurity, &out.DisablePortSecurity + *out = new(bool) + **out = **in + } + if in.PropagateUplinkStatus != nil { + in, out := &in.PropagateUplinkStatus, &out.PropagateUplinkStatus + *out = new(bool) + **out = **in + } + if in.ValueSpecs != nil { + in, out := &in.ValueSpecs, &out.ValueSpecs + *out = make([]ValueSpec, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedPortSpecFields. +func (in *ResolvedPortSpecFields) DeepCopy() *ResolvedPortSpecFields { + if in == nil { + return nil + } + out := new(ResolvedPortSpecFields) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RootVolume) DeepCopyInto(out *RootVolume) { *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 50fca991d1..60a63a71b3 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -5535,7 +5535,11 @@ spec: type: string tags: description: |- - Machine tags + Tags which will be added to the machine and all dependent resources + which support them. These are in addition to Tags defined on the + cluster. + + Requires Nova api 2.52 minimum! items: type: string @@ -6242,6 +6246,8 @@ spec: description: Ports is the fully resolved list of ports to create for the machine. items: + description: ResolvedPortSpec is a PortOpts with all contained + references fully resolved. properties: adminStateUp: description: AdminStateUp specifies whether the port @@ -6286,91 +6292,20 @@ spec: IP address to assign to the port. If specified, these must be subnets of the port's network. items: + description: ResolvedFixedIP is a FixedIP with the + Subnet resolved to an ID. properties: ipAddress: description: |- - IPAddress is a specific IP address to assign to the port. If Subnet + IPAddress is a specific IP address to assign to the port. If SubnetID is also specified, IPAddress must be a valid IP address in the subnet. If Subnet is not specified, IPAddress must be a valid IP address in any subnet of the port's network. type: string subnet: - 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. - properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string - id: - 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 + description: SubnetID is the id of a subnet to + create the fixed IP of a port in. + type: string type: object type: array x-kubernetes-list-type: atomic @@ -6383,78 +6318,13 @@ spec: the port. If not specified, the MAC address will be generated. type: string - nameSuffix: - description: NameSuffix will be appended to the name - of the port if specified. If unspecified, instead - the 0-based index of the port in the list is used. + name: + description: Name is the name of the port. + type: string + networkID: + description: NetworkID is the ID of the network the + port will be created in. type: string - network: - description: |- - Network is a query for an openstack network that the port will be created or discovered on. - This will fail if the query returns more than one network. - properties: - description: - type: string - id: - type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - type: object profile: description: |- Profile is a set of key-value pairs that are used for binding @@ -6479,88 +6349,22 @@ spec: the propagate uplink status on the port. type: boolean securityGroups: - description: SecurityGroups is a list of the names, - uuids, filters or any combination these of the security - groups to assign to the instance. + description: SecurityGroups is a list of security group + IDs to assign to the port. items: - properties: - description: - type: string - id: - 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: string type: array x-kubernetes-list-type: atomic tags: - description: |- - Tags applied to the port (and corresponding trunk, if a trunk is configured.) - These tags are applied in addition to the instance's tags, which will also be applied to the port. + description: Tags applied to the port (and corresponding + trunk, if a trunk is configured.) items: type: string type: array x-kubernetes-list-type: set trunk: - description: |- - Trunk specifies whether trunking is enabled at the port level. If not - provided the value is inherited from the machine, or false for a - bastion host. + description: Trunk specifies whether trunking is enabled + at the port level. type: boolean valueSpecs: description: |- @@ -6603,6 +6407,10 @@ spec: implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used. type: string + required: + - description + - name + - networkID type: object type: array serverGroupID: 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 acb1556d63..2f6d16392b 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2967,7 +2967,11 @@ spec: type: string tags: description: |- - Machine tags + Tags which will be added to the machine and all dependent resources + which support them. These are in addition to Tags defined on the + cluster. + + Requires Nova api 2.52 minimum! items: type: string 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 475ff16f1b..8a4b8bf1fa 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -2317,7 +2317,11 @@ spec: type: string tags: description: |- - Machine tags + Tags which will be added to the machine and all dependent resources + which support them. These are in addition to Tags defined on the + cluster. + + Requires Nova api 2.52 minimum! items: type: string @@ -2459,6 +2463,8 @@ spec: description: Ports is the fully resolved list of ports to create for the machine. items: + description: ResolvedPortSpec is a PortOpts with all contained + references fully resolved. properties: adminStateUp: description: AdminStateUp specifies whether the port should @@ -2503,91 +2509,20 @@ spec: IP address to assign to the port. If specified, these must be subnets of the port's network. items: + description: ResolvedFixedIP is a FixedIP with the Subnet + resolved to an ID. properties: ipAddress: description: |- - IPAddress is a specific IP address to assign to the port. If Subnet + IPAddress is a specific IP address to assign to the port. If SubnetID is also specified, IPAddress must be a valid IP address in the subnet. If Subnet is not specified, IPAddress must be a valid IP address in any subnet of the port's network. type: string subnet: - 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. - properties: - cidr: - type: string - description: - type: string - gatewayIP: - type: string - id: - 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 + description: SubnetID is the id of a subnet to create + the fixed IP of a port in. + type: string type: object type: array x-kubernetes-list-type: atomic @@ -2599,78 +2534,13 @@ spec: description: MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated. type: string - nameSuffix: - description: NameSuffix will be appended to the name of - the port if specified. If unspecified, instead the 0-based - index of the port in the list is used. + name: + description: Name is the name of the port. + type: string + networkID: + description: NetworkID is the ID of the network the port + will be created in. type: string - network: - description: |- - Network is a query for an openstack network that the port will be created or discovered on. - This will fail if the query returns more than one network. - properties: - description: - type: string - id: - type: string - name: - type: string - notTags: - description: |- - NotTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - NotTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - projectID: - type: string - tags: - description: |- - Tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - TagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - minLength: 1 - pattern: ^[^,]+$ - type: string - type: array - x-kubernetes-list-type: set - type: object profile: description: |- Profile is a set of key-value pairs that are used for binding @@ -2695,88 +2565,22 @@ spec: propagate uplink status on the port. type: boolean securityGroups: - description: SecurityGroups is a list of the names, uuids, - filters or any combination these of the security groups - to assign to the instance. + description: SecurityGroups is a list of security group + IDs to assign to the port. items: - properties: - description: - type: string - id: - 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: string type: array x-kubernetes-list-type: atomic tags: - description: |- - Tags applied to the port (and corresponding trunk, if a trunk is configured.) - These tags are applied in addition to the instance's tags, which will also be applied to the port. + description: Tags applied to the port (and corresponding + trunk, if a trunk is configured.) items: type: string type: array x-kubernetes-list-type: set trunk: - description: |- - Trunk specifies whether trunking is enabled at the port level. If not - provided the value is inherited from the machine, or false for a - bastion host. + description: Trunk specifies whether trunking is enabled + at the port level. type: boolean valueSpecs: description: |- @@ -2818,6 +2622,10 @@ spec: implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used. type: string + required: + - description + - name + - networkID type: object type: array serverGroupID: 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 6c982124ae..a716f124a6 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -1996,7 +1996,11 @@ spec: type: string tags: description: |- - Machine tags + Tags which will be added to the machine and all dependent resources + which support them. These are in addition to Tags defined on the + cluster. + + Requires Nova api 2.52 minimum! items: type: string diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index 64488cfc48..f3395236a4 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -55,6 +55,7 @@ import ( "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" utils "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/controllers" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/filterconvert" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names" ) const ( @@ -150,11 +151,13 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } + clusterName := names.ClusterName(cluster) + // A bastion may have been created if cluster initialisation previously reached populating the network status // We attempt to delete it even if no status was written, just in case if openStackCluster.Status.Network != nil { // Attempt to resolve bastion resources before delete. We don't need to worry about starting if the resources have changed on update. - if _, err := resolveBastionResources(scope, openStackCluster); err != nil { + if _, err := resolveBastionResources(scope, clusterName, openStackCluster); err != nil { return reconcile.Result{}, err } @@ -168,8 +171,6 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope return reconcile.Result{}, err } - clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name) - if openStackCluster.Spec.APIServerLoadBalancer.IsEnabled() { loadBalancerService, err := loadbalancer.NewService(scope) if err != nil { @@ -220,12 +221,16 @@ func contains(arr []string, target string) bool { return false } -func resolveBastionResources(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster) (bool, error) { +func resolveBastionResources(scope *scope.WithLogger, clusterName string, openStackCluster *infrav1.OpenStackCluster) (bool, error) { + // Resolve and store referenced & dependent resources for the bastion if openStackCluster.Spec.Bastion != nil && openStackCluster.Spec.Bastion.Enabled { if openStackCluster.Status.Bastion == nil { openStackCluster.Status.Bastion = &infrav1.BastionStatus{} } - changed, err := compute.ResolveReferencedMachineResources(scope, openStackCluster, &openStackCluster.Spec.Bastion.Instance, &openStackCluster.Status.Bastion.ReferencedResources) + changed, err := compute.ResolveReferencedMachineResources(scope, + &openStackCluster.Spec.Bastion.Instance, &openStackCluster.Status.Bastion.ReferencedResources, + clusterName, bastionName(clusterName), + openStackCluster, getBastionSecurityGroupID(openStackCluster)) if err != nil { return false, err } @@ -235,7 +240,6 @@ func resolveBastionResources(scope *scope.WithLogger, openStackCluster *infrav1. } changed, err = compute.AdoptDependentMachineResources(scope, - bastionName(openStackCluster.Name), &openStackCluster.Status.Bastion.ReferencedResources, &openStackCluster.Status.Bastion.DependentResources) if err != nil { @@ -387,7 +391,8 @@ func reconcileNormal(scope *scope.WithLogger, cluster *clusterv1.Cluster, openSt func reconcileBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster) (*ctrl.Result, error) { scope.Logger().V(4).Info("Reconciling Bastion") - changed, err := resolveBastionResources(scope, openStackCluster) + clusterName := names.ClusterName(cluster) + changed, err := resolveBastionResources(scope, clusterName, openStackCluster) if err != nil { return nil, err } @@ -424,7 +429,6 @@ func reconcileBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openS if err != nil { return nil, err } - clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name) bastionHash, err := compute.HashInstanceSpec(instanceSpec) if err != nil { @@ -440,7 +444,7 @@ func reconcileBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openS return &reconcile.Result{}, nil } - err = getOrCreateBastionPorts(openStackCluster, networkingService, cluster.Name) + err = getOrCreateBastionPorts(openStackCluster, networkingService) if err != nil { handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to get or create ports for bastion: %w", err)) return nil, fmt.Errorf("failed to get or create ports for bastion: %w", err) @@ -533,64 +537,47 @@ func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterNam } func bastionToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, cluster *clusterv1.Cluster) (*compute.InstanceSpec, error) { - if openStackCluster.Spec.Bastion == nil { + bastionSpec := openStackCluster.Spec.Bastion + if bastionSpec == nil { return nil, fmt.Errorf("bastion spec is nil") } if openStackCluster.Status.Bastion == nil { return nil, fmt.Errorf("bastion status is nil") } - instanceSpec := &compute.InstanceSpec{ - Name: bastionName(cluster.Name), - Flavor: openStackCluster.Spec.Bastion.Instance.Flavor, - SSHKeyName: openStackCluster.Spec.Bastion.Instance.SSHKeyName, - ImageID: openStackCluster.Status.Bastion.ReferencedResources.ImageID, - FailureDomain: openStackCluster.Spec.Bastion.AvailabilityZone, - RootVolume: openStackCluster.Spec.Bastion.Instance.RootVolume, - } - - instanceSpec.SecurityGroups = openStackCluster.Spec.Bastion.Instance.SecurityGroups - if openStackCluster.Spec.ManagedSecurityGroups != nil { - if openStackCluster.Status.BastionSecurityGroup != nil { - instanceSpec.SecurityGroups = append(instanceSpec.SecurityGroups, infrav1.SecurityGroupFilter{ - ID: openStackCluster.Status.BastionSecurityGroup.ID, - }) - } - } - instanceSpec.SecurityGroups = getBastionSecurityGroups(openStackCluster) + referencedResources := &openStackCluster.Status.Bastion.ReferencedResources - instanceSpec.Ports = openStackCluster.Spec.Bastion.Instance.Ports - - return instanceSpec, nil + machineSpec := &bastionSpec.Instance + return &compute.InstanceSpec{ + Name: bastionName(cluster.Name), + Flavor: machineSpec.Flavor, + SSHKeyName: machineSpec.SSHKeyName, + ImageID: referencedResources.ImageID, + FailureDomain: bastionSpec.AvailabilityZone, + RootVolume: machineSpec.RootVolume, + ServerGroupID: referencedResources.ServerGroupID, + Tags: compute.InstanceTags(machineSpec, openStackCluster), + }, nil } func bastionName(clusterName string) string { return fmt.Sprintf("%s-bastion", clusterName) } -// getBastionSecurityGroups returns a combination of openStackCluster.Spec.Bastion.Instance.SecurityGroups -// and the security group managed by the OpenStackCluster. -func getBastionSecurityGroups(openStackCluster *infrav1.OpenStackCluster) []infrav1.SecurityGroupFilter { - instanceSpecSecurityGroups := openStackCluster.Spec.Bastion.Instance.SecurityGroups - +// getBastionSecurityGroupID returns the ID of the bastion security group if +// managed security groups is enabled. +func getBastionSecurityGroupID(openStackCluster *infrav1.OpenStackCluster) *string { if openStackCluster.Spec.ManagedSecurityGroups == nil { - return instanceSpecSecurityGroups + return nil } - var managedSecurityGroup string if openStackCluster.Status.BastionSecurityGroup != nil { - managedSecurityGroup = openStackCluster.Status.BastionSecurityGroup.ID + return &openStackCluster.Status.BastionSecurityGroup.ID } - - if managedSecurityGroup != "" { - instanceSpecSecurityGroups = append(instanceSpecSecurityGroups, infrav1.SecurityGroupFilter{ - ID: managedSecurityGroup, - }) - } - return instanceSpecSecurityGroups + return nil } -func getOrCreateBastionPorts(openStackCluster *infrav1.OpenStackCluster, networkingService *networking.Service, clusterName string) error { +func getOrCreateBastionPorts(openStackCluster *infrav1.OpenStackCluster, networkingService *networking.Service) error { desiredPorts := openStackCluster.Status.Bastion.ReferencedResources.Ports dependentResources := &openStackCluster.Status.Bastion.DependentResources @@ -598,9 +585,7 @@ func getOrCreateBastionPorts(openStackCluster *infrav1.OpenStackCluster, network return nil } - securityGroups := getBastionSecurityGroups(openStackCluster) - bastionTags := []string{} - err := networkingService.CreatePorts(openStackCluster, clusterName, bastionName(clusterName), securityGroups, bastionTags, desiredPorts, dependentResources) + err := networkingService.CreatePorts(openStackCluster, desiredPorts, dependentResources) if err != nil { return fmt.Errorf("failed to create ports for bastion %s: %w", bastionName(openStackCluster.Name), err) } @@ -618,7 +603,7 @@ func bastionHashHasChanged(computeHash string, clusterAnnotations map[string]str } func reconcileNetworkComponents(scope *scope.WithLogger, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster) error { - clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name) + clusterName := names.ClusterName(cluster) networkingService, err := networking.NewService(scope) if err != nil { diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go index 3f3ec703d7..3de0b5c175 100644 --- a/controllers/openstackcluster_controller_test.go +++ b/controllers/openstackcluster_controller_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/golang/mock/gomock" + "github.com/google/go-cmp/cmp" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" @@ -234,11 +235,9 @@ var _ = Describe("OpenStackCluster controller", func() { Bastion: &infrav1.BastionStatus{ ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -280,16 +279,14 @@ var _ = Describe("OpenStackCluster controller", func() { networkClientRecorder.ListFloatingIP(floatingips.ListOpts{PortID: "portID1"}).Return(make([]floatingips.FloatingIP, 1), nil) res, err := reconcileBastion(scope, capiCluster, testCluster) - Expect(testCluster.Status.Bastion).To(Equal(&infrav1.BastionStatus{ + expectedStatus := &infrav1.BastionStatus{ ID: "adopted-bastion-uuid", State: "ACTIVE", ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -300,7 +297,8 @@ var _ = Describe("OpenStackCluster controller", func() { }, }, }, - })) + } + Expect(testCluster.Status.Bastion).To(Equal(expectedStatus), cmp.Diff(testCluster.Status.Bastion, expectedStatus)) Expect(err).To(BeNil()) Expect(res).To(BeNil()) }) @@ -326,11 +324,9 @@ var _ = Describe("OpenStackCluster controller", func() { ID: "adopted-fip-bastion-uuid", ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -370,11 +366,9 @@ var _ = Describe("OpenStackCluster controller", func() { State: "ACTIVE", ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -411,11 +405,9 @@ var _ = Describe("OpenStackCluster controller", func() { ID: "requeue-bastion-uuid", ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -449,11 +441,9 @@ var _ = Describe("OpenStackCluster controller", func() { State: "BUILD", ReferencedResources: infrav1.ReferencedMachineResources{ ImageID: "imageID", - Ports: []infrav1.PortOpts{ + Ports: []infrav1.ResolvedPortSpec{ { - Network: &infrav1.NetworkFilter{ - ID: "network-id", - }, + NetworkID: "network-id", }, }, }, @@ -791,40 +781,3 @@ func Test_getAPIServerPort(t *testing.T) { }) } } - -func TestGetBastionSecurityGroups(t *testing.T) { - openStackCluster := &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{ - Bastion: &infrav1.Bastion{ - Instance: infrav1.OpenStackMachineSpec{ - SecurityGroups: []infrav1.SecurityGroupFilter{ - { - ID: "sg-123", - }, - }, - }, - }, - ManagedSecurityGroups: &infrav1.ManagedSecurityGroups{}, - }, - Status: infrav1.OpenStackClusterStatus{ - BastionSecurityGroup: &infrav1.SecurityGroupStatus{ - ID: "sg-456", - }, - }, - } - - expectedSecurityGroups := []infrav1.SecurityGroupFilter{ - { - ID: "sg-123", - }, - { - ID: "sg-456", - }, - } - - securityGroups := getBastionSecurityGroups(openStackCluster) - - if !reflect.DeepEqual(securityGroups, expectedSecurityGroups) { - t.Errorf("Expected security groups %v, but got %v", expectedSecurityGroups, securityGroups) - } -} diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 750900b267..1565aaf608 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -53,6 +53,7 @@ import ( "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/loadbalancer" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names" ) // OpenStackMachineReconciler reconciles a OpenStackMachine object. @@ -149,7 +150,10 @@ func (r *OpenStackMachineReconciler) Reconcile(ctx context.Context, req ctrl.Req scope := scope.NewWithLogger(clientScope, log) // Resolve and store referenced resources - changed, err := compute.ResolveReferencedMachineResources(scope, infraCluster, &openStackMachine.Spec, &openStackMachine.Status.ReferencedResources) + changed, err := compute.ResolveReferencedMachineResources(scope, + &openStackMachine.Spec, &openStackMachine.Status.ReferencedResources, + names.ClusterName(cluster), openStackMachine.Name, + infraCluster, getManagedSecurityGroup(infraCluster, machine)) if err != nil { return reconcile.Result{}, err } @@ -159,7 +163,7 @@ func (r *OpenStackMachineReconciler) Reconcile(ctx context.Context, req ctrl.Req } // Adopt any existing dependent resources - changed, err = compute.AdoptDependentMachineResources(scope, openStackMachine.Name, &openStackMachine.Status.ReferencedResources, &openStackMachine.Status.DependentResources) + changed, err = compute.AdoptDependentMachineResources(scope, &openStackMachine.Status.ReferencedResources, &openStackMachine.Status.DependentResources) if err != nil { return reconcile.Result{}, err } @@ -244,7 +248,7 @@ func (r *OpenStackMachineReconciler) SetupWithManager(ctx context.Context, mgr c func (r *OpenStackMachineReconciler) reconcileDelete(scope *scope.WithLogger, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) (ctrl.Result, error) { //nolint:unparam scope.Logger().Info("Reconciling Machine delete") - clusterName := fmt.Sprintf("%s-%s", cluster.ObjectMeta.Namespace, cluster.Name) + clusterName := names.ClusterName(cluster) computeService, err := compute.NewService(scope) if err != nil { @@ -367,7 +371,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope } scope.Logger().Info("Reconciling Machine") - clusterName := fmt.Sprintf("%s-%s", cluster.ObjectMeta.Namespace, cluster.Name) + clusterName := names.ClusterName(cluster) computeService, err := compute.NewService(scope) if err != nil { @@ -379,7 +383,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope return ctrl.Result{}, err } - err = getOrCreateMachinePorts(openStackCluster, machine, openStackMachine, networkingService, clusterName) + err = getOrCreateMachinePorts(openStackMachine, networkingService) if err != nil { return ctrl.Result{}, err } @@ -493,7 +497,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope return ctrl.Result{}, nil } -func getOrCreateMachinePorts(openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, networkingService *networking.Service, clusterName string) error { +func getOrCreateMachinePorts(openStackMachine *infrav1.OpenStackMachine, networkingService *networking.Service) error { desiredPorts := openStackMachine.Status.ReferencedResources.Ports dependentResources := &openStackMachine.Status.DependentResources @@ -501,9 +505,7 @@ func getOrCreateMachinePorts(openStackCluster *infrav1.OpenStackCluster, machine return nil } - instanceTags := getInstanceTags(openStackMachine, openStackCluster) - managedSecurityGroups := getManagedSecurityGroups(openStackCluster, machine, openStackMachine) - if err := networkingService.CreatePorts(openStackMachine, clusterName, openStackMachine.Name, managedSecurityGroups, instanceTags, desiredPorts, dependentResources); err != nil { + if err := networkingService.CreatePorts(openStackMachine, desiredPorts, dependentResources); err != nil { return fmt.Errorf("creating ports: %w", err) } @@ -572,69 +574,29 @@ func machineToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, machine * instanceSpec.FailureDomain = *machine.Spec.FailureDomain } - instanceSpec.Tags = getInstanceTags(openStackMachine, openStackCluster) - instanceSpec.SecurityGroups = getManagedSecurityGroups(openStackCluster, machine, openStackMachine) - instanceSpec.Ports = openStackMachine.Spec.Ports + instanceSpec.Tags = compute.InstanceTags(&openStackMachine.Spec, openStackCluster) return &instanceSpec } -// getInstanceTags returns the tags that should be applied to the instance. -// The tags are a combination of the tags specified on the OpenStackMachine and -// the ones specified on the OpenStackCluster. -func getInstanceTags(openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) []string { - machineTags := []string{} - - // Append machine specific tags - machineTags = append(machineTags, openStackMachine.Spec.Tags...) - - // Append cluster scope tags - machineTags = append(machineTags, openStackCluster.Spec.Tags...) - - // tags need to be unique or the "apply tags" call will fail. - deduplicate := func(tags []string) []string { - seen := make(map[string]struct{}, len(machineTags)) - unique := make([]string, 0, len(machineTags)) - for _, tag := range tags { - if _, ok := seen[tag]; !ok { - seen[tag] = struct{}{} - unique = append(unique, tag) - } - } - return unique - } - machineTags = deduplicate(machineTags) - - return machineTags -} - -// getManagedSecurityGroups returns a combination of OpenStackMachine.Spec.SecurityGroups -// and the security group managed by the OpenStackCluster whether it's a control plane or a worker machine. -func getManagedSecurityGroups(openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) []infrav1.SecurityGroupFilter { - machineSpecSecurityGroups := openStackMachine.Spec.SecurityGroups - +// getManagedSecurityGroup returns the ID of the security group managed by the +// OpenStackCluster whether it's a control plane or a worker machine. +func getManagedSecurityGroup(openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine) *string { if openStackCluster.Spec.ManagedSecurityGroups == nil { - return machineSpecSecurityGroups + return nil } - var managedSecurityGroup string if util.IsControlPlaneMachine(machine) { if openStackCluster.Status.ControlPlaneSecurityGroup != nil { - managedSecurityGroup = openStackCluster.Status.ControlPlaneSecurityGroup.ID + return &openStackCluster.Status.ControlPlaneSecurityGroup.ID } } else { if openStackCluster.Status.WorkerSecurityGroup != nil { - managedSecurityGroup = openStackCluster.Status.WorkerSecurityGroup.ID + return &openStackCluster.Status.WorkerSecurityGroup.ID } } - if managedSecurityGroup != "" { - machineSpecSecurityGroups = append(machineSpecSecurityGroups, infrav1.SecurityGroupFilter{ - ID: managedSecurityGroup, - }) - } - - return machineSpecSecurityGroups + return nil } func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, instanceNS *compute.InstanceNetworkStatus, clusterName string) error { diff --git a/controllers/openstackmachine_controller_test.go b/controllers/openstackmachine_controller_test.go index 648b1abc0d..1351f3e2f6 100644 --- a/controllers/openstackmachine_controller_test.go +++ b/controllers/openstackmachine_controller_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" @@ -112,11 +113,10 @@ func getDefaultInstanceSpec() *compute.InstanceSpec { Metadata: map[string]string{ "test-metadata": "test-value", }, - ConfigDrive: *pointer.Bool(true), - FailureDomain: *pointer.String(failureDomain), - ServerGroupID: serverGroupUUID, - SecurityGroups: []infrav1.SecurityGroupFilter{}, - Tags: []string{"test-tag"}, + ConfigDrive: *pointer.Bool(true), + FailureDomain: *pointer.String(failureDomain), + ServerGroupID: serverGroupUUID, + Tags: []string{"test-tag"}, } } @@ -137,102 +137,6 @@ func Test_machineToInstanceSpec(t *testing.T) { openStackMachine: getDefaultOpenStackMachine, wantInstanceSpec: getDefaultInstanceSpec, }, - { - name: "Control plane security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - return c - }, - machine: func() *clusterv1.Machine { - m := getDefaultMachine() - m.Labels = map[string]string{ - clusterv1.MachineControlPlaneLabel: "true", - } - return m - }, - openStackMachine: getDefaultOpenStackMachine, - wantInstanceSpec: func() *compute.InstanceSpec { - i := getDefaultInstanceSpec() - i.SecurityGroups = []infrav1.SecurityGroupFilter{{ID: controlPlaneSecurityGroupUUID}} - return i - }, - }, - { - name: "Worker security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - return c - }, - machine: getDefaultMachine, - openStackMachine: getDefaultOpenStackMachine, - wantInstanceSpec: func() *compute.InstanceSpec { - i := getDefaultInstanceSpec() - i.SecurityGroups = []infrav1.SecurityGroupFilter{{ID: workerSecurityGroupUUID}} - return i - }, - }, - { - name: "Control plane security group not applied to worker", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.WorkerSecurityGroup = nil - return c - }, - machine: getDefaultMachine, - openStackMachine: getDefaultOpenStackMachine, - wantInstanceSpec: func() *compute.InstanceSpec { - i := getDefaultInstanceSpec() - i.SecurityGroups = []infrav1.SecurityGroupFilter{} - return i - }, - }, - { - name: "Worker security group not applied to control plane", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.ControlPlaneSecurityGroup = nil - return c - }, - machine: func() *clusterv1.Machine { - m := getDefaultMachine() - m.Labels = map[string]string{ - clusterv1.MachineControlPlaneLabel: "true", - } - return m - }, - openStackMachine: getDefaultOpenStackMachine, - wantInstanceSpec: func() *compute.InstanceSpec { - i := getDefaultInstanceSpec() - i.SecurityGroups = []infrav1.SecurityGroupFilter{} - return i - }, - }, - { - name: "Extra security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - return c - }, - machine: getDefaultMachine, - openStackMachine: func() *infrav1.OpenStackMachine { - m := getDefaultOpenStackMachine() - m.Spec.SecurityGroups = []infrav1.SecurityGroupFilter{{ID: extraSecurityGroupUUID}} - return m - }, - wantInstanceSpec: func() *compute.InstanceSpec { - i := getDefaultInstanceSpec() - i.SecurityGroups = []infrav1.SecurityGroupFilter{ - {ID: extraSecurityGroupUUID}, - {ID: workerSecurityGroupUUID}, - } - return i - }, - }, { name: "Tags", openStackCluster: func() *infrav1.OpenStackCluster { @@ -255,220 +159,11 @@ func Test_machineToInstanceSpec(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) got := machineToInstanceSpec(tt.openStackCluster(), tt.machine(), tt.openStackMachine(), "user-data") - Expect(got).To(Equal(tt.wantInstanceSpec())) - }) - } -} - -func Test_getInstanceTags(t *testing.T) { - tests := []struct { - name string - openStackMachine func() *infrav1.OpenStackMachine - openStackCluster func() *infrav1.OpenStackCluster - wantMachineTags []string - }{ - { - name: "No tags", - openStackMachine: func() *infrav1.OpenStackMachine { - return &infrav1.OpenStackMachine{ - Spec: infrav1.OpenStackMachineSpec{}, - } - }, - openStackCluster: func() *infrav1.OpenStackCluster { - return &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{}, - } - }, - wantMachineTags: []string{}, - }, - { - name: "Machine tags only", - openStackMachine: func() *infrav1.OpenStackMachine { - return &infrav1.OpenStackMachine{ - Spec: infrav1.OpenStackMachineSpec{ - Tags: []string{"machine-tag1", "machine-tag2"}, - }, - } - }, - openStackCluster: func() *infrav1.OpenStackCluster { - return &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{}, - } - }, - wantMachineTags: []string{"machine-tag1", "machine-tag2"}, - }, - { - name: "Cluster tags only", - openStackMachine: func() *infrav1.OpenStackMachine { - return &infrav1.OpenStackMachine{ - Spec: infrav1.OpenStackMachineSpec{}, - } - }, - openStackCluster: func() *infrav1.OpenStackCluster { - return &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{ - Tags: []string{"cluster-tag1", "cluster-tag2"}, - }, - } - }, - wantMachineTags: []string{"cluster-tag1", "cluster-tag2"}, - }, - { - name: "Machine and cluster tags", - openStackMachine: func() *infrav1.OpenStackMachine { - return &infrav1.OpenStackMachine{ - Spec: infrav1.OpenStackMachineSpec{ - Tags: []string{"machine-tag1", "machine-tag2"}, - }, - } - }, - openStackCluster: func() *infrav1.OpenStackCluster { - return &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{ - Tags: []string{"cluster-tag1", "cluster-tag2"}, - }, - } - }, - wantMachineTags: []string{"machine-tag1", "machine-tag2", "cluster-tag1", "cluster-tag2"}, - }, - { - name: "Duplicate tags", - openStackMachine: func() *infrav1.OpenStackMachine { - return &infrav1.OpenStackMachine{ - Spec: infrav1.OpenStackMachineSpec{ - Tags: []string{"tag1", "tag2", "tag1"}, - }, - } - }, - openStackCluster: func() *infrav1.OpenStackCluster { - return &infrav1.OpenStackCluster{ - Spec: infrav1.OpenStackClusterSpec{ - Tags: []string{"tag2", "tag3", "tag3"}, - }, - } - }, - wantMachineTags: []string{"tag1", "tag2", "tag3"}, - }, - } + wanted := tt.wantInstanceSpec() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotMachineTags := getInstanceTags(tt.openStackMachine(), tt.openStackCluster()) - if !reflect.DeepEqual(gotMachineTags, tt.wantMachineTags) { - t.Errorf("getInstanceTags() = %v, want %v", gotMachineTags, tt.wantMachineTags) - } - }) - } -} - -func Test_getManagedSecurityGroups(t *testing.T) { - tests := []struct { - name string - openStackCluster func() *infrav1.OpenStackCluster - machine func() *clusterv1.Machine - openStackMachine func() *infrav1.OpenStackMachine - wantSecurityGroups []infrav1.SecurityGroupFilter - }{ - { - name: "Defaults", - openStackCluster: getDefaultOpenStackCluster, - machine: getDefaultMachine, - openStackMachine: getDefaultOpenStackMachine, - wantSecurityGroups: []infrav1.SecurityGroupFilter{}, - }, - { - name: "Control plane machine with control plane security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.ControlPlaneSecurityGroup = &infrav1.SecurityGroupStatus{ID: controlPlaneSecurityGroupUUID} - return c - }, - machine: func() *clusterv1.Machine { - m := getDefaultMachine() - m.Labels = map[string]string{ - clusterv1.MachineControlPlaneLabel: "true", - } - return m - }, - openStackMachine: getDefaultOpenStackMachine, - wantSecurityGroups: []infrav1.SecurityGroupFilter{ - {ID: controlPlaneSecurityGroupUUID}, - }, - }, - { - name: "Worker machine with worker security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.WorkerSecurityGroup = &infrav1.SecurityGroupStatus{ID: workerSecurityGroupUUID} - return c - }, - machine: getDefaultMachine, - openStackMachine: getDefaultOpenStackMachine, - wantSecurityGroups: []infrav1.SecurityGroupFilter{ - {ID: workerSecurityGroupUUID}, - }, - }, - { - name: "Control plane machine without control plane security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.ControlPlaneSecurityGroup = nil - return c - }, - machine: func() *clusterv1.Machine { - m := getDefaultMachine() - m.Labels = map[string]string{ - clusterv1.MachineControlPlaneLabel: "true", - } - return m - }, - openStackMachine: getDefaultOpenStackMachine, - wantSecurityGroups: []infrav1.SecurityGroupFilter{}, - }, - { - name: "Worker machine without worker security group", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.WorkerSecurityGroup = nil - return c - }, - machine: getDefaultMachine, - openStackMachine: getDefaultOpenStackMachine, - wantSecurityGroups: []infrav1.SecurityGroupFilter{}, - }, - { - name: "Machine with additional security groups", - openStackCluster: func() *infrav1.OpenStackCluster { - c := getDefaultOpenStackCluster() - c.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{} - c.Status.ControlPlaneSecurityGroup = &infrav1.SecurityGroupStatus{ID: controlPlaneSecurityGroupUUID} - c.Status.WorkerSecurityGroup = &infrav1.SecurityGroupStatus{ID: workerSecurityGroupUUID} - return c - }, - machine: getDefaultMachine, - openStackMachine: func() *infrav1.OpenStackMachine { - m := getDefaultOpenStackMachine() - m.Spec.SecurityGroups = []infrav1.SecurityGroupFilter{{ID: extraSecurityGroupUUID}} - return m - }, - wantSecurityGroups: []infrav1.SecurityGroupFilter{ - {ID: extraSecurityGroupUUID}, - {ID: workerSecurityGroupUUID}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotMachineSecurity := getManagedSecurityGroups(tt.openStackCluster(), tt.machine(), tt.openStackMachine()) - if !reflect.DeepEqual(gotMachineSecurity, tt.wantSecurityGroups) { - t.Errorf("getManagedSecurityGroups() = %v, want %v", gotMachineSecurity, tt.wantSecurityGroups) - } + g.Expect(got).To(Equal(wanted), cmp.Diff(got, wanted)) }) } } diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index 5ac1613daf..a07608dcc6 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -654,8 +654,10 @@ bool
Machine tags -Requires Nova api 2.52 minimum!
+Tags which will be added to the machine and all dependent resources +which support them. These are in addition to Tags defined on the +cluster.
+Requires Nova api 2.52 minimum!
(Appears on: -PortOpts) +ResolvedPortSpecFields)
@@ -1218,7 +1220,7 @@ DependentMachineResources
(Appears on: -PortOpts) +ResolvedPortSpecFields)
@@ -3105,8 +3107,10 @@ bool
Machine tags -Requires Nova api 2.52 minimum!
+Tags which will be added to the machine and all dependent resources +which support them. These are in addition to Tags defined on the +cluster.
+Requires Nova api 2.52 minimum!
Machine tags -Requires Nova api 2.52 minimum!
+Tags which will be added to the machine and all dependent resources +which support them. These are in addition to Tags defined on the +cluster.
+Requires Nova api 2.52 minimum!
(Appears on: -OpenStackMachineSpec, -ReferencedMachineResources) +OpenStackMachineSpec)
@@ -3613,18 +3618,6 @@ This will fail if the query returns more than one network.
nameSuffix
NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used.
-description
adminStateUp
AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.
-macAddress
nameSuffix
MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated.
+NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used.
allowedAddressPairs
tags
AllowedAddressPairs is a list of address pairs which Neutron will -allow the port to send traffic from in addition to the port’s -addresses. If not specified, the MAC Address will be the MAC Address -of the port. Depending on the configuration of Neutron, it may be -supported to specify a CIDR instead of a specific IP address.
+Tags applied to the port (and corresponding trunk, if a trunk is configured.) +These tags are applied in addition to the instance’s tags, which will also be applied to the port.
hostID
ResolvedPortSpecFields
HostID specifies the ID of the host where the port resides.
+
+(Members of ResolvedPortSpecFields
are embedded into this type.)
+
+(Appears on: +DependentMachineResources) +
++
+Field | +Description | +
---|---|
-vnicType + id string |
-(Optional)
- VNICType specifies the type of vNIC which this port should be -attached to. This is used to determine which mechanism driver(s) to -be used to bind the port. The valid values are normal, macvtap, -direct, baremetal, direct-physical, virtio-forwarder, smart-nic and -remote-managed, although these values will not be validated in this -API to ensure compatibility with future neutron changes or custom -implementations. What type of vNIC is actually available depends on -deployments. If not specified, the Neutron default value is used. +ID is the unique identifier of the port. |
+(Appears on: +BastionStatus, +OpenStackMachineStatus) +
++
ReferencedMachineResources contains resolved references to resources required by the machine.
+ +Field | +Description | +
---|---|
-profile + serverGroupID - -BindingProfile - +string |
(Optional)
- Profile is a set of key-value pairs that are used for binding -details. We intentionally don’t expose this as a map[string]string -because we only want to enable the users to set the values of the -keys that are known to work in OpenStack Networking API. See -https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port -To set profiles, your tenant needs permissions rule:create_port, and -rule:create_port:binding:profile +ServerGroupID is the ID of the server group the machine should be added to and is calculated based on ServerGroupFilter. |
-disablePortSecurity + imageID -bool +string |
(Optional)
- DisablePortSecurity enables or disables the port security when set. -When not set, it takes the value of the corresponding field at the network level. +ImageID is the ID of the image to use for the machine and is calculated based on ImageFilter. |
-propagateUplinkStatus + ports -bool + +[]ResolvedPortSpec + |
(Optional)
- PropageteUplinkStatus enables or disables the propagate uplink status on the port. +Ports is the fully resolved list of ports to create for the machine. |
+(Appears on: +ResolvedPortSpec) +
++
ResolvedFixedIP is a FixedIP with the Subnet resolved to an ID.
+ +Field | +Description | +
---|---|
-tags + subnet -[]string +string |
(Optional)
- Tags applied to the port (and corresponding trunk, if a trunk is configured.) -These tags are applied in addition to the instance’s tags, which will also be applied to the port. +SubnetID is the id of a subnet to create the fixed IP of a port in. |
-valueSpecs + ipAddress - -[]ValueSpec - +string |
(Optional)
- Value specs are extra parameters to include in the API request with OpenStack. -This is an extension point for the API, so what they do and if they are supported, -depends on the specific OpenStack implementation. +IPAddress is a specific IP address to assign to the port. If SubnetID +is also specified, IPAddress must be a valid IP address in the +subnet. If Subnet is not specified, IPAddress must be a valid IP +address in any subnet of the port’s network. |
(Appears on: -DependentMachineResources) +ReferencedMachineResources)
+
ResolvedPortSpec is a PortOpts with all contained references fully resolved.
-id + name string |
- ID is the unique identifier of the port. +Name is the name of the port. + |
+
+description + +string + + |
+
+ Description is a human-readable description for the port. + |
+
+tags + +[]string + + |
+
+(Optional)
+ Tags applied to the port (and corresponding trunk, if a trunk is configured.) + |
+
+trunk + +bool + + |
+
+(Optional)
+ Trunk specifies whether trunking is enabled at the port level. + |
+
+networkID + +string + + |
+
+ NetworkID is the ID of the network the port will be created in. + |
+
+fixedIPs + + +[]ResolvedFixedIP + + + |
+
+(Optional)
+ FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port’s network. + |
+
+securityGroups + +[]string + + |
+
+(Optional)
+ SecurityGroups is a list of security group IDs to assign to the port. + |
+
+ResolvedPortSpecFields + + +ResolvedPortSpecFields + + + |
+
+
+(Members of |
(Appears on: -BastionStatus, -OpenStackMachineStatus) +PortOpts, +ResolvedPortSpec)
-
ReferencedMachineResources contains resolved references to resources required by the machine.
+ResolvePortSpecFields is a convenience struct containing all fields of a +PortOpts which don’t contain references which need to be resolved, and can +therefore be shared with ResolvedPortSpec.
-serverGroupID + adminStateUp + +bool + + |
+
+(Optional)
+ AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up. + |
+
+macAddress string |
(Optional)
- ServerGroupID is the ID of the server group the machine should be added to and is calculated based on ServerGroupFilter. +MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated. |
-imageID + allowedAddressPairs + + +[]AddressPair + + + |
+
+(Optional)
+ AllowedAddressPairs is a list of address pairs which Neutron will +allow the port to send traffic from in addition to the port’s +addresses. If not specified, the MAC Address will be the MAC Address +of the port. Depending on the configuration of Neutron, it may be +supported to specify a CIDR instead of a specific IP address. + |
+
+hostID string |
(Optional)
- ImageID is the ID of the image to use for the machine and is calculated based on ImageFilter. +HostID specifies the ID of the host where the port resides. |
-ports + vnicType - -[]PortOpts +string + + |
+
+(Optional)
+ VNICType specifies the type of vNIC which this port should be +attached to. This is used to determine which mechanism driver(s) to +be used to bind the port. The valid values are normal, macvtap, +direct, baremetal, direct-physical, virtio-forwarder, smart-nic and +remote-managed, although these values will not be validated in this +API to ensure compatibility with future neutron changes or custom +implementations. What type of vNIC is actually available depends on +deployments. If not specified, the Neutron default value is used. + |
+
+profile + + +BindingProfile |
(Optional)
- Ports is the fully resolved list of ports to create for the machine. +Profile is a set of key-value pairs that are used for binding +details. We intentionally don’t expose this as a map[string]string +because we only want to enable the users to set the values of the +keys that are known to work in OpenStack Networking API. See +https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port +To set profiles, your tenant needs permissions rule:create_port, and +rule:create_port:binding:profile + |
+
+disablePortSecurity + +bool + + |
+
+(Optional)
+ DisablePortSecurity enables or disables the port security when set. +When not set, it takes the value of the corresponding field at the network level. + |
+
+propagateUplinkStatus + +bool + + |
+
+(Optional)
+ PropageteUplinkStatus enables or disables the propagate uplink status on the port. + |
+
+valueSpecs + + +[]ValueSpec + + + |
+
+(Optional)
+ Value specs are extra parameters to include in the API request with OpenStack. +This is an extension point for the API, so what they do and if they are supported, +depends on the specific OpenStack implementation. |