diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go
index 2acff1d4aa..60a6ff055b 100644
--- a/api/v1alpha5/zz_generated.conversion.go
+++ b/api/v1alpha5/zz_generated.conversion.go
@@ -1352,10 +1352,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))
@@ -1371,26 +1369,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
}
@@ -1405,14 +1389,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 {
@@ -1437,29 +1417,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 a6dcc278d4..b38e4436e2 100644
--- a/api/v1alpha6/zz_generated.conversion.go
+++ b/api/v1alpha6/zz_generated.conversion.go
@@ -1387,10 +1387,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))
@@ -1406,28 +1404,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
}
@@ -1441,14 +1425,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 {
@@ -1473,29 +1453,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 3c3d0629f2..98bdd6df26 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"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
@@ -217,6 +219,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 {
@@ -241,6 +275,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 8264250f84..4d72d623f4 100644
--- a/api/v1alpha7/zz_generated.conversion.go
+++ b/api/v1alpha7/zz_generated.conversion.go
@@ -1587,10 +1587,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))
@@ -1603,29 +1601,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
}
@@ -1639,14 +1623,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 {
@@ -1661,29 +1641,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 17249383d4..b4f3babef9 100644
--- a/api/v1beta1/openstackmachine_types.go
+++ b/api/v1beta1/openstackmachine_types.go
@@ -60,7 +60,9 @@ 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 b8a524c54a..f5f8fcfa22 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"`
+
+ // NetworkID is the ID of the network the port will be created in.
+ NetworkID string `json:"networkID"`
+
+ // 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 optional.Bool `json:"trunk,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
+ // +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
@@ -616,7 +670,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 2ffc417727..98c11cbbf4 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -1093,23 +1093,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
}
@@ -1127,53 +1117,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.
@@ -1206,7 +1160,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])
}
@@ -1223,6 +1177,131 @@ 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.Trunk != nil {
+ in, out := &in.Trunk, &out.Trunk
+ *out = new(bool)
+ **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 d02a6db1b0..3130c26513 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml
@@ -5563,7 +5563,9 @@ 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
@@ -6275,6 +6277,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
@@ -6319,91 +6323,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
@@ -6416,78 +6349,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
@@ -6512,88 +6380,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: |-
@@ -6636,6 +6438,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 7d67ac96de..66b4e12174 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml
@@ -2997,7 +2997,9 @@ 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 2067fad50e..74bbae9e0b 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml
@@ -2338,7 +2338,9 @@ 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
@@ -2480,6 +2482,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
@@ -2524,91 +2528,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
@@ -2620,78 +2553,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
@@ -2716,88 +2584,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: |-
@@ -2839,6 +2641,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 b71228fa4a..12d3b1ea40 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml
@@ -2017,7 +2017,9 @@ 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 61ea41cff2..2f742c5545 100644
--- a/controllers/openstackcluster_controller.go
+++ b/controllers/openstackcluster_controller.go
@@ -52,6 +52,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 (
@@ -147,11 +148,13 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
}
+ clusterResourceName := names.ClusterResourceName(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, clusterResourceName, openStackCluster); err != nil {
return reconcile.Result{}, err
}
@@ -165,15 +168,13 @@ 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 {
return reconcile.Result{}, err
}
- if err = loadBalancerService.DeleteLoadBalancer(openStackCluster, clusterName); err != nil {
+ if err = loadBalancerService.DeleteLoadBalancer(openStackCluster, clusterResourceName); err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to delete load balancer: %w", err))
return reconcile.Result{}, fmt.Errorf("failed to delete load balancer: %w", err)
}
@@ -181,7 +182,7 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope
// if ManagedSubnets was not set, no network was created.
if len(openStackCluster.Spec.ManagedSubnets) > 0 {
- if err = networkingService.DeleteRouter(openStackCluster, clusterName); err != nil {
+ if err = networkingService.DeleteRouter(openStackCluster, clusterResourceName); err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to delete router: %w", err))
return ctrl.Result{}, fmt.Errorf("failed to delete router: %w", err)
}
@@ -191,13 +192,13 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope
return reconcile.Result{}, fmt.Errorf("failed to delete ports: %w", err)
}
- if err = networkingService.DeleteNetwork(openStackCluster, clusterName); err != nil {
+ if err = networkingService.DeleteNetwork(openStackCluster, clusterResourceName); err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to delete network: %w", err))
return ctrl.Result{}, fmt.Errorf("failed to delete network: %w", err)
}
}
- if err = networkingService.DeleteSecurityGroups(openStackCluster, clusterName); err != nil {
+ if err = networkingService.DeleteSecurityGroups(openStackCluster, clusterResourceName); err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to delete security groups: %w", err))
return reconcile.Result{}, fmt.Errorf("failed to delete security groups: %w", err)
}
@@ -217,7 +218,8 @@ func contains(arr []string, target string) bool {
return false
}
-func resolveBastionResources(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster) (bool, error) {
+func resolveBastionResources(scope *scope.WithLogger, clusterResourceName 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{}
@@ -225,7 +227,10 @@ func resolveBastionResources(scope *scope.WithLogger, openStackCluster *infrav1.
if openStackCluster.Spec.Bastion.Spec == nil {
return false, fmt.Errorf("bastion spec is nil when bastion is enabled, this shouldn't happen")
}
- changed, err := compute.ResolveReferencedMachineResources(scope, openStackCluster, openStackCluster.Spec.Bastion.Spec, &openStackCluster.Status.Bastion.ReferencedResources)
+ changed, err := compute.ResolveReferencedMachineResources(scope,
+ openStackCluster.Spec.Bastion.Spec, &openStackCluster.Status.Bastion.ReferencedResources,
+ clusterResourceName, bastionName(clusterResourceName),
+ openStackCluster, getBastionSecurityGroupID(openStackCluster))
if err != nil {
return false, err
}
@@ -235,7 +240,6 @@ func resolveBastionResources(scope *scope.WithLogger, openStackCluster *infrav1.
}
err = compute.AdoptDependentMachineResources(scope,
- bastionName(openStackCluster.Name),
&openStackCluster.Status.Bastion.ReferencedResources,
&openStackCluster.Status.Bastion.DependentResources)
if err != nil {
@@ -383,7 +387,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)
+ clusterResourceName := names.ClusterResourceName(cluster)
+ changed, err := resolveBastionResources(scope, clusterResourceName, openStackCluster)
if err != nil {
return nil, err
}
@@ -420,7 +425,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 {
@@ -436,7 +440,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)
@@ -487,10 +491,10 @@ func reconcileBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openS
return nil, err
}
- return bastionAddFloatingIP(openStackCluster, clusterName, port, networkingService)
+ return bastionAddFloatingIP(openStackCluster, clusterResourceName, port, networkingService)
}
-func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterName string, port *ports.Port, networkingService *networking.Service) (*reconcile.Result, error) {
+func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, port *ports.Port, networkingService *networking.Service) (*reconcile.Result, error) {
fp, err := networkingService.GetFloatingIPByPortID(port.ID)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to get or create floating IP for bastion: %w", err))
@@ -512,7 +516,7 @@ func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterNam
floatingIP = openStackCluster.Spec.Bastion.FloatingIP
}
// Check if there is an existing floating IP attached to bastion, in case where FloatingIP would not yet have been stored in cluster status
- fp, err = networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterName, floatingIP)
+ fp, err = networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterResourceName, floatingIP)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to get or create floating IP for bastion: %w", err))
return nil, fmt.Errorf("failed to get or create floating IP for bastion: %w", err)
@@ -529,71 +533,51 @@ func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterNam
}
func bastionToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, cluster *clusterv1.Cluster) (*compute.InstanceSpec, error) {
- if openStackCluster.Spec.Bastion == nil {
+ bastion := openStackCluster.Spec.Bastion
+ if bastion == nil {
return nil, fmt.Errorf("bastion spec is nil")
}
- if openStackCluster.Spec.Bastion.Spec == nil {
+ if bastion.Spec == nil {
// For the case when Bastion is deleted but we don't have spec, let's use an empty one.
// v1beta1 API validations prevent this from happening in normal circumstances.
- openStackCluster.Spec.Bastion.Spec = &infrav1.OpenStackMachineSpec{}
+ bastion.Spec = &infrav1.OpenStackMachineSpec{}
}
+ referencedResources := &openStackCluster.Status.Bastion.ReferencedResources
- if openStackCluster.Status.Bastion == nil {
- return nil, fmt.Errorf("bastion status is nil")
- }
+ machineSpec := bastion.Spec
instanceSpec := &compute.InstanceSpec{
- Name: bastionName(cluster.Name),
- Flavor: openStackCluster.Spec.Bastion.Spec.Flavor,
- SSHKeyName: openStackCluster.Spec.Bastion.Spec.SSHKeyName,
- ImageID: openStackCluster.Status.Bastion.ReferencedResources.ImageID,
- RootVolume: openStackCluster.Spec.Bastion.Spec.RootVolume,
+ Name: bastionName(cluster.Name),
+ Flavor: machineSpec.Flavor,
+ SSHKeyName: machineSpec.SSHKeyName,
+ ImageID: referencedResources.ImageID,
+ RootVolume: machineSpec.RootVolume,
+ ServerGroupID: referencedResources.ServerGroupID,
+ Tags: compute.InstanceTags(machineSpec, openStackCluster),
}
- if openStackCluster.Spec.Bastion.AvailabilityZone != nil {
- instanceSpec.FailureDomain = *openStackCluster.Spec.Bastion.AvailabilityZone
+ if bastion.AvailabilityZone != nil {
+ instanceSpec.FailureDomain = *bastion.AvailabilityZone
}
-
- instanceSpec.SecurityGroups = openStackCluster.Spec.Bastion.Spec.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)
-
- instanceSpec.Ports = openStackCluster.Spec.Bastion.Spec.Ports
-
return instanceSpec, nil
}
-func bastionName(clusterName string) string {
- return fmt.Sprintf("%s-bastion", clusterName)
+func bastionName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-bastion", clusterResourceName)
}
-// getBastionSecurityGroups returns a combination of openStackCluster.Spec.Bastion.Spec.SecurityGroups
-// and the security group managed by the OpenStackCluster.
-func getBastionSecurityGroups(openStackCluster *infrav1.OpenStackCluster) []infrav1.SecurityGroupFilter {
- instanceSpecSecurityGroups := openStackCluster.Spec.Bastion.Spec.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
@@ -601,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)
}
@@ -621,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)
+ clusterResourceName := names.ClusterResourceName(cluster)
networkingService, err := networking.NewService(scope)
if err != nil {
@@ -641,20 +623,20 @@ func reconcileNetworkComponents(scope *scope.WithLogger, cluster *clusterv1.Clus
return err
}
} else if len(openStackCluster.Spec.ManagedSubnets) == 1 {
- if err := reconcileProvisionedNetworkComponents(networkingService, openStackCluster, clusterName); err != nil {
+ if err := reconcileProvisionedNetworkComponents(networkingService, openStackCluster, clusterResourceName); err != nil {
return err
}
} else {
return fmt.Errorf("failed to reconcile network: ManagedSubnets only supports one element, %d provided", len(openStackCluster.Spec.ManagedSubnets))
}
- err = networkingService.ReconcileSecurityGroups(openStackCluster, clusterName)
+ err = networkingService.ReconcileSecurityGroups(openStackCluster, clusterResourceName)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile security groups: %w", err))
return fmt.Errorf("failed to reconcile security groups: %w", err)
}
- return reconcileControlPlaneEndpoint(scope, networkingService, openStackCluster, clusterName)
+ return reconcileControlPlaneEndpoint(scope, networkingService, openStackCluster, clusterResourceName)
}
// reconcilePreExistingNetworkComponents reconciles the cluster network status when the cluster is
@@ -718,18 +700,18 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe
return nil
}
-func reconcileProvisionedNetworkComponents(networkingService *networking.Service, openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
- err := networkingService.ReconcileNetwork(openStackCluster, clusterName)
+func reconcileProvisionedNetworkComponents(networkingService *networking.Service, openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
+ err := networkingService.ReconcileNetwork(openStackCluster, clusterResourceName)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile network: %w", err))
return fmt.Errorf("failed to reconcile network: %w", err)
}
- err = networkingService.ReconcileSubnet(openStackCluster, clusterName)
+ err = networkingService.ReconcileSubnet(openStackCluster, clusterResourceName)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile subnets: %w", err))
return fmt.Errorf("failed to reconcile subnets: %w", err)
}
- err = networkingService.ReconcileRouter(openStackCluster, clusterName)
+ err = networkingService.ReconcileRouter(openStackCluster, clusterResourceName)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile router: %w", err))
return fmt.Errorf("failed to reconcile router: %w", err)
@@ -741,7 +723,7 @@ func reconcileProvisionedNetworkComponents(networkingService *networking.Service
// reconcileControlPlaneEndpoint configures the control plane endpoint for the
// cluster, creating it if necessary, and updates ControlPlaneEndpoint in the
// cluster spec.
-func reconcileControlPlaneEndpoint(scope *scope.WithLogger, networkingService *networking.Service, openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
+func reconcileControlPlaneEndpoint(scope *scope.WithLogger, networkingService *networking.Service, openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
// Calculate the port that we will use for the API server
apiServerPort := getAPIServerPort(openStackCluster)
@@ -758,7 +740,7 @@ func reconcileControlPlaneEndpoint(scope *scope.WithLogger, networkingService *n
return err
}
- terminalFailure, err := loadBalancerService.ReconcileLoadBalancer(openStackCluster, clusterName, apiServerPort)
+ terminalFailure, err := loadBalancerService.ReconcileLoadBalancer(openStackCluster, clusterResourceName, apiServerPort)
if err != nil {
// if it's terminalFailure (not Transient), set the Failure reason and message
if terminalFailure {
@@ -783,7 +765,7 @@ func reconcileControlPlaneEndpoint(scope *scope.WithLogger, networkingService *n
// API server load balancer is disabled, but floating IP is not. Create
// a floating IP to be attached directly to a control plane host.
case !pointer.BoolDeref(openStackCluster.Spec.DisableAPIServerFloatingIP, false):
- fp, err := networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterName, openStackCluster.Spec.APIServerFloatingIP)
+ fp, err := networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterResourceName, openStackCluster.Spec.APIServerFloatingIP)
if err != nil {
handleUpdateOSCError(openStackCluster, fmt.Errorf("floating IP cannot be got or created: %w", err))
return fmt.Errorf("floating IP cannot be got or created: %w", err)
diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go
index 9c94b12d40..198f55a818 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"
@@ -238,11 +239,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",
},
},
},
@@ -284,16 +283,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",
},
},
},
@@ -304,7 +301,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())
})
@@ -331,11 +329,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",
},
},
},
@@ -375,11 +371,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",
},
},
},
@@ -417,11 +411,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",
},
},
},
@@ -455,11 +447,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",
},
},
},
@@ -799,40 +789,3 @@ func Test_getAPIServerPort(t *testing.T) {
})
}
}
-
-func TestGetBastionSecurityGroups(t *testing.T) {
- openStackCluster := &infrav1.OpenStackCluster{
- Spec: infrav1.OpenStackClusterSpec{
- Bastion: &infrav1.Bastion{
- Spec: &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 294959e410..230768a73a 100644
--- a/controllers/openstackmachine_controller.go
+++ b/controllers/openstackmachine_controller.go
@@ -150,22 +150,23 @@ func (r *OpenStackMachineReconciler) Reconcile(ctx context.Context, req ctrl.Req
}
scope := scope.NewWithLogger(clientScope, log)
- clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name)
+ clusterResourceName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name)
// Handle deleted machines
if !openStackMachine.DeletionTimestamp.IsZero() {
- return r.reconcileDelete(scope, clusterName, infraCluster, machine, openStackMachine)
+ return r.reconcileDelete(scope, clusterResourceName, infraCluster, machine, openStackMachine)
}
// Handle non-deleted clusters
- return r.reconcileNormal(ctx, scope, clusterName, infraCluster, machine, openStackMachine)
+ return r.reconcileNormal(ctx, scope, clusterResourceName, infraCluster, machine, openStackMachine)
}
-func resolveMachineResources(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine) (bool, error) {
+func resolveMachineResources(scope *scope.WithLogger, clusterResourceName string, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, machine *clusterv1.Machine) (bool, error) {
// Resolve and store referenced resources
changed, err := compute.ResolveReferencedMachineResources(scope,
- openStackCluster,
- &openStackMachine.Spec, &openStackMachine.Status.ReferencedResources)
+ &openStackMachine.Spec, &openStackMachine.Status.ReferencedResources,
+ clusterResourceName, openStackMachine.Name,
+ openStackCluster, getManagedSecurityGroup(openStackCluster, machine))
if err != nil {
return false, err
}
@@ -175,7 +176,7 @@ func resolveMachineResources(scope *scope.WithLogger, openStackCluster *infrav1.
}
// Adopt any existing dependent resources
- return false, compute.AdoptDependentMachineResources(scope, openStackMachine.Name, &openStackMachine.Status.ReferencedResources, &openStackMachine.Status.DependentResources)
+ return false, compute.AdoptDependentMachineResources(scope, &openStackMachine.Status.ReferencedResources, &openStackMachine.Status.DependentResources)
}
func patchMachine(ctx context.Context, patchHelper *patch.Helper, openStackMachine *infrav1.OpenStackMachine, machine *clusterv1.Machine, options ...patch.Option) error {
@@ -230,7 +231,7 @@ func (r *OpenStackMachineReconciler) SetupWithManager(ctx context.Context, mgr c
Complete(r)
}
-func (r *OpenStackMachineReconciler) reconcileDelete(scope *scope.WithLogger, clusterName string, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) (ctrl.Result, error) { //nolint:unparam
+func (r *OpenStackMachineReconciler) reconcileDelete(scope *scope.WithLogger, clusterResourceName string, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) (ctrl.Result, error) { //nolint:unparam
scope.Logger().Info("Reconciling Machine delete")
computeService, err := compute.NewService(scope)
@@ -245,7 +246,7 @@ func (r *OpenStackMachineReconciler) reconcileDelete(scope *scope.WithLogger, cl
// We may have resources to adopt if the cluster is ready
if openStackCluster.Status.Ready && openStackCluster.Status.Network != nil {
- if _, err := resolveMachineResources(scope, openStackCluster, openStackMachine); err != nil {
+ if _, err := resolveMachineResources(scope, clusterResourceName, openStackCluster, openStackMachine, machine); err != nil {
return ctrl.Result{}, err
}
}
@@ -256,7 +257,7 @@ func (r *OpenStackMachineReconciler) reconcileDelete(scope *scope.WithLogger, cl
return ctrl.Result{}, err
}
- err = loadBalancerService.DeleteLoadBalancerMember(openStackCluster, machine, openStackMachine, clusterName)
+ err = loadBalancerService.DeleteLoadBalancerMember(openStackCluster, machine, openStackMachine, clusterResourceName)
if err != nil {
conditions.MarkFalse(openStackMachine, infrav1.APIServerIngressReadyCondition, infrav1.LoadBalancerMemberErrorReason, clusterv1.ConditionSeverityWarning, "Machine could not be removed from load balancer: %v", err)
return ctrl.Result{}, err
@@ -462,7 +463,7 @@ func (r *OpenStackMachineReconciler) reconcileDeleteFloatingAddressFromPool(scop
return r.Client.Update(context.Background(), claim)
}
-func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope *scope.WithLogger, clusterName string, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) (_ ctrl.Result, reterr error) {
+func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope *scope.WithLogger, clusterResourceName string, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine) (_ ctrl.Result, reterr error) {
var err error
// If the OpenStackMachine is in an error state, return early.
@@ -483,7 +484,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope
return ctrl.Result{RequeueAfter: waitForClusterInfrastructureReadyDuration}, nil
}
- if changed, err := resolveMachineResources(scope, openStackCluster, openStackMachine); changed || err != nil {
+ if changed, err := resolveMachineResources(scope, clusterResourceName, openStackCluster, openStackMachine, machine); changed || err != nil {
return ctrl.Result{}, err
}
@@ -514,7 +515,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
}
@@ -596,7 +597,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope
return ctrl.Result{}, nil
}
- err = r.reconcileAPIServerLoadBalancer(scope, openStackCluster, openStackMachine, instanceStatus, instanceNS, clusterName)
+ err = r.reconcileAPIServerLoadBalancer(scope, openStackCluster, openStackMachine, instanceStatus, instanceNS, clusterResourceName)
if err != nil {
return ctrl.Result{}, err
}
@@ -605,7 +606,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope
return ctrl.Result{}, nil
}
-func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, instanceStatus *compute.InstanceStatus, instanceNS *compute.InstanceNetworkStatus, clusterName string) error {
+func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, instanceStatus *compute.InstanceStatus, instanceNS *compute.InstanceNetworkStatus, clusterResourceName string) error {
scope.Logger().Info("Reconciling APIServerLoadBalancer")
computeService, err := compute.NewService(scope)
if err != nil {
@@ -618,7 +619,7 @@ func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope
}
if openStackCluster.Spec.APIServerLoadBalancer.IsEnabled() {
- err = r.reconcileLoadBalancerMember(scope, openStackCluster, openStackMachine, instanceNS, clusterName)
+ err = r.reconcileLoadBalancerMember(scope, openStackCluster, openStackMachine, instanceNS, clusterResourceName)
if err != nil {
conditions.MarkFalse(openStackMachine, infrav1.APIServerIngressReadyCondition, infrav1.LoadBalancerMemberErrorReason, clusterv1.ConditionSeverityError, "Reconciling load balancer member failed: %v", err)
return fmt.Errorf("reconcile load balancer member: %w", err)
@@ -631,7 +632,7 @@ func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope
case openStackCluster.Spec.APIServerFloatingIP != nil:
floatingIPAddress = openStackCluster.Spec.APIServerFloatingIP
}
- fp, err := networkingService.GetOrCreateFloatingIP(openStackMachine, openStackCluster, clusterName, floatingIPAddress)
+ fp, err := networkingService.GetOrCreateFloatingIP(openStackMachine, openStackCluster, clusterResourceName, floatingIPAddress)
if err != nil {
conditions.MarkFalse(openStackMachine, infrav1.APIServerIngressReadyCondition, infrav1.FloatingIPErrorReason, clusterv1.ConditionSeverityError, "Floating IP cannot be obtained or created: %v", err)
return fmt.Errorf("get or create floating IP %v: %w", floatingIPAddress, err)
@@ -656,7 +657,7 @@ func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope
return 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
@@ -664,9 +665,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)
}
@@ -735,79 +734,39 @@ 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 {
+func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, instanceNS *compute.InstanceNetworkStatus, clusterResourceName string) error {
ip := instanceNS.IP(openStackCluster.Status.Network.Name)
loadbalancerService, err := loadbalancer.NewService(scope)
if err != nil {
return err
}
- return loadbalancerService.ReconcileLoadBalancerMember(openStackCluster, openStackMachine, clusterName, ip)
+ return loadbalancerService.ReconcileLoadBalancerMember(openStackCluster, openStackMachine, clusterResourceName, ip)
}
// OpenStackClusterToOpenStackMachines is a handler.ToRequestsFunc to be used to enqeue requests for reconciliation
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 fe145f612c..e4d1ac338a 100644
--- a/docs/book/src/api/v1beta1/api.md
+++ b/docs/book/src/api/v1beta1/api.md
@@ -654,7 +654,9 @@ bool
- 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!
|
@@ -977,7 +979,7 @@ additional storage options.
(Appears on:
-PortOpts)
+ResolvedPortSpecFields)
@@ -1238,7 +1240,7 @@ DependentMachineResources
(Appears on:
-PortOpts)
+ResolvedPortSpecFields)
@@ -3125,7 +3127,9 @@ bool
- 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!
|
@@ -3492,7 +3496,9 @@ bool
- 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!
|
@@ -3631,8 +3637,7 @@ OpenStackMachineTemplateResource
(Appears on:
-OpenStackMachineSpec,
-ReferencedMachineResources)
+OpenStackMachineSpec)
@@ -3661,18 +3666,6 @@ This will fail if the query returns more than one network.
-nameSuffix
-
-string
-
- |
-
-(Optional)
- 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
string
@@ -3685,26 +3678,14 @@ string
|
-adminStateUp
-
-bool
-
- |
-
-(Optional)
- AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.
- |
-
-
-
-macAddress
+nameSuffix
string
|
(Optional)
- 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.
|
@@ -3737,20 +3718,15 @@ string
-allowedAddressPairs
+tags
-
-[]AddressPair
-
+[]string
|
(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.
+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.
|
@@ -3769,118 +3745,162 @@ bastion host.
-hostID
+ResolvedPortSpecFields
-string
+
+ResolvedPortSpecFields
+
|
-(Optional)
- HostID specifies the ID of the host where the port resides.
+
+(Members of ResolvedPortSpecFields are embedded into this type.)
+
|
+
+
+PortStatus
+
+
+(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.
|
+
+
+ReferencedMachineResources
+
+
+(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.
|
+
+
+ResolvedFixedIP
+
+
+(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.
|
-PortStatus
+ResolvedPortSpec
(Appears on:
-DependentMachineResources)
+ReferencedMachineResources)
+
ResolvedPortSpec is a PortOpts with all contained references fully resolved.
@@ -3892,26 +3912,115 @@ depends on the specific OpenStack implementation.
-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.
+ |
+
+
+
+networkID
+
+string
+
+ |
+
+ NetworkID is the ID of the network the port will be created in.
+ |
+
+
+
+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.
+ |
+
+
+
+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 ResolvedPortSpecFields are embedded into this type.)
+
|
-ReferencedMachineResources
+ResolvedPortSpecFields
(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.
@@ -3923,40 +4032,136 @@ string
-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.
|
@@ -4737,7 +4942,7 @@ outside of these ranges manually.
(Appears on:
-PortOpts)
+ResolvedPortSpecFields)
ValueSpec represents a single value_spec key-value pair.
diff --git a/pkg/cloud/services/compute/dependent_resources.go b/pkg/cloud/services/compute/dependent_resources.go
index 1425c46fbf..0d45f07fb4 100644
--- a/pkg/cloud/services/compute/dependent_resources.go
+++ b/pkg/cloud/services/compute/dependent_resources.go
@@ -22,11 +22,11 @@ import (
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
)
-func AdoptDependentMachineResources(scope *scope.WithLogger, baseName string, referencedResources *infrav1.ReferencedMachineResources, dependentResources *infrav1.DependentMachineResources) error {
+func AdoptDependentMachineResources(scope *scope.WithLogger, referencedResources *infrav1.ReferencedMachineResources, dependentResources *infrav1.DependentMachineResources) error {
networkingService, err := networking.NewService(scope)
if err != nil {
return err
}
- return networkingService.AdoptPorts(scope, baseName, referencedResources.Ports, dependentResources)
+ return networkingService.AdoptPorts(scope, referencedResources.Ports, dependentResources)
}
diff --git a/pkg/cloud/services/compute/instance_test.go b/pkg/cloud/services/compute/instance_test.go
index e715171556..af9214e8a7 100644
--- a/pkg/cloud/services/compute/instance_test.go
+++ b/pkg/cloud/services/compute/instance_test.go
@@ -175,11 +175,10 @@ func getDefaultInstanceSpec() *InstanceSpec {
Metadata: map[string]string{
"test-metadata": "test-value",
},
- ConfigDrive: *pointer.Bool(true),
- FailureDomain: *pointer.String(failureDomain),
- ServerGroupID: serverGroupUUID,
- Tags: []string{"test-tag"},
- SecurityGroups: []infrav1.SecurityGroupFilter{{ID: workerSecurityGroupUUID}},
+ ConfigDrive: *pointer.Bool(true),
+ FailureDomain: *pointer.String(failureDomain),
+ ServerGroupID: serverGroupUUID,
+ Tags: []string{"test-tag"},
}
}
diff --git a/pkg/cloud/services/compute/instance_types.go b/pkg/cloud/services/compute/instance_types.go
index 5457d39bd1..c9940db7f1 100644
--- a/pkg/cloud/services/compute/instance_types.go
+++ b/pkg/cloud/services/compute/instance_types.go
@@ -43,8 +43,6 @@ type InstanceSpec struct {
ServerGroupID string
Trunk bool
Tags []string
- SecurityGroups []infrav1.SecurityGroupFilter
- Ports []infrav1.PortOpts
}
// InstanceIdentifier describes an instance which has not necessarily been fetched.
diff --git a/pkg/cloud/services/compute/referenced_resources.go b/pkg/cloud/services/compute/referenced_resources.go
index ae967b19cc..72cd91047e 100644
--- a/pkg/cloud/services/compute/referenced_resources.go
+++ b/pkg/cloud/services/compute/referenced_resources.go
@@ -18,6 +18,7 @@ package compute
import (
"fmt"
+ "slices"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking"
@@ -30,7 +31,7 @@ import (
// Note that we only set the fields in ReferencedMachineResources that are not set yet. This is ok because:
// - OpenStackMachine is immutable, so we can't change the spec after the machine is created.
// - the bastion is mutable, but we delete the bastion when the spec changes, so the bastion status will be empty.
-func ResolveReferencedMachineResources(scope *scope.WithLogger, openStackCluster *infrav1.OpenStackCluster, spec *infrav1.OpenStackMachineSpec, resources *infrav1.ReferencedMachineResources) (changed bool, err error) {
+func ResolveReferencedMachineResources(scope *scope.WithLogger, spec *infrav1.OpenStackMachineSpec, resources *infrav1.ReferencedMachineResources, clusterResourceName, baseName string, openStackCluster *infrav1.OpenStackCluster, managedSecurityGroup *string) (changed bool, err error) {
changed = false
computeService, err := NewService(scope)
@@ -72,13 +73,8 @@ func ResolveReferencedMachineResources(scope *scope.WithLogger, openStackCluster
// Network resources are required in order to get ports options.
if len(resources.Ports) == 0 {
- // For now we put this here but realistically an OpenStack administrator could enable/disable trunk
- // support at any time, so we should probably check this on every reconcile.
- trunkSupported, err := networkingService.IsTrunkExtSupported()
- if err != nil {
- return changed, err
- }
- portsOpts, err := networkingService.ConstructPorts(openStackCluster, spec.Ports, spec.Trunk, trunkSupported)
+ defaultNetwork := openStackCluster.Status.Network
+ portsOpts, err := networkingService.ConstructPorts(spec, clusterResourceName, baseName, defaultNetwork, managedSecurityGroup, InstanceTags(spec, openStackCluster))
if err != nil {
return changed, err
}
@@ -88,3 +84,20 @@ func ResolveReferencedMachineResources(scope *scope.WithLogger, openStackCluster
return changed, nil
}
+
+// InstanceTags returns the tags that should be applied to an instance.
+// The tags are a deduplicated combination of the tags specified in the
+// OpenStackMachineSpec and the ones specified on the OpenStackCluster.
+func InstanceTags(spec *infrav1.OpenStackMachineSpec, openStackCluster *infrav1.OpenStackCluster) []string {
+ machineTags := slices.Concat(spec.Tags, openStackCluster.Spec.Tags)
+
+ seen := make(map[string]struct{}, len(machineTags))
+ unique := make([]string, 0, len(machineTags))
+ for _, tag := range machineTags {
+ if _, ok := seen[tag]; !ok {
+ seen[tag] = struct{}{}
+ unique = append(unique, tag)
+ }
+ }
+ return slices.Clip(unique)
+}
diff --git a/pkg/cloud/services/compute/referenced_resources_test.go b/pkg/cloud/services/compute/referenced_resources_test.go
index 1dd50788b6..47d2a6adb9 100644
--- a/pkg/cloud/services/compute/referenced_resources_test.go
+++ b/pkg/cloud/services/compute/referenced_resources_test.go
@@ -17,6 +17,7 @@ limitations under the License.
package compute
import (
+ "reflect"
"testing"
"github.com/go-logr/logr/testr"
@@ -24,7 +25,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
- "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
. "github.com/onsi/gomega"
"k8s.io/utils/pointer"
@@ -34,154 +34,160 @@ import (
)
func Test_ResolveReferencedMachineResources(t *testing.T) {
- constFalse := false
- const serverGroupID1 = "ce96e584-7ebc-46d6-9e55-987d72e3806c"
- imageID1 := "de96e584-7ebc-46d6-9e55-987d72e3806c"
+ const (
+ serverGroupID1 = "ce96e584-7ebc-46d6-9e55-987d72e3806c"
+ imageID1 = "de96e584-7ebc-46d6-9e55-987d72e3806c"
+ networkID1 = "23ab8b71-89d4-425f-ac81-4eb83b35125a"
+ networkID2 = "cc8f75ce-6ce4-4b8a-836e-e5dac91cc9c8"
+ subnetID = "32dc0e7f-34b6-4544-a69b-248955618736"
+ )
- minimumReferences := &infrav1.ReferencedMachineResources{
- ImageID: imageID1,
+ defaultPorts := []infrav1.ResolvedPortSpec{
+ {
+ Name: "test-instance-0",
+ Description: "Created by cluster-api-provider-openstack cluster test-cluster",
+ NetworkID: networkID1,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(subnetID)},
+ },
+ },
}
tests := []struct {
- testName string
- serverGroupFilter *infrav1.ServerGroupFilter
- imageFilter *infrav1.ImageFilter
- portsOpts *[]infrav1.PortOpts
- clusterStatus *infrav1.OpenStackClusterStatus
- expectComputeMock func(m *mock.MockComputeClientMockRecorder)
- expectImageMock func(m *mock.MockImageClientMockRecorder)
- expectNetworkMock func(m *mock.MockNetworkClientMockRecorder)
- want *infrav1.ReferencedMachineResources
- wantErr bool
+ testName string
+ spec infrav1.OpenStackMachineSpec
+ managedSecurityGroup *string
+ expectComputeMock func(m *mock.MockComputeClientMockRecorder)
+ expectImageMock func(m *mock.MockImageClientMockRecorder)
+ expectNetworkMock func(m *mock.MockNetworkClientMockRecorder)
+ before *infrav1.ReferencedMachineResources
+ want *infrav1.ReferencedMachineResources
+ wantErr bool
}{
{
- testName: "Resources ID passed",
- serverGroupFilter: &infrav1.ServerGroupFilter{ID: serverGroupID1},
- imageFilter: &infrav1.ImageFilter{ID: &imageID1},
- expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {},
- expectImageMock: func(m *mock.MockImageClientMockRecorder) {},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {},
- want: &infrav1.ReferencedMachineResources{ImageID: imageID1, ServerGroupID: serverGroupID1},
- wantErr: false,
+ testName: "Resources ID passed",
+ spec: infrav1.OpenStackMachineSpec{
+ ServerGroup: &infrav1.ServerGroupFilter{ID: serverGroupID1},
+ Image: infrav1.ImageFilter{ID: pointer.String(imageID1)},
+ },
+ want: &infrav1.ReferencedMachineResources{
+ ImageID: imageID1,
+ ServerGroupID: serverGroupID1,
+ Ports: defaultPorts,
+ },
},
{
- testName: "Server group filter nil",
- serverGroupFilter: nil,
- expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {},
- expectImageMock: func(m *mock.MockImageClientMockRecorder) {},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {},
- want: minimumReferences,
- wantErr: false,
+ testName: "Only image ID passed: want image id and default ports",
+ spec: infrav1.OpenStackMachineSpec{
+ Image: infrav1.ImageFilter{ID: pointer.String(imageID1)},
+ },
+ want: &infrav1.ReferencedMachineResources{
+ ImageID: imageID1,
+ Ports: defaultPorts,
+ },
},
{
- testName: "Server group ID empty",
- serverGroupFilter: &infrav1.ServerGroupFilter{},
- expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {},
- expectImageMock: func(m *mock.MockImageClientMockRecorder) {},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {},
- want: minimumReferences,
- wantErr: false,
+ testName: "Server group empty",
+ spec: infrav1.OpenStackMachineSpec{
+ Image: infrav1.ImageFilter{ID: pointer.String(imageID1)},
+ ServerGroup: &infrav1.ServerGroupFilter{},
+ },
+ want: &infrav1.ReferencedMachineResources{
+ ImageID: imageID1,
+ Ports: defaultPorts,
+ },
},
{
- testName: "Server group by Name not found",
- serverGroupFilter: &infrav1.ServerGroupFilter{Name: "test-server-group"},
+ testName: "Server group by Name not found",
+ spec: infrav1.OpenStackMachineSpec{
+ Image: infrav1.ImageFilter{ID: pointer.String(imageID1)},
+ ServerGroup: &infrav1.ServerGroupFilter{Name: "test-server-group"},
+ },
expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {
m.ListServerGroups().Return(
[]servergroups.ServerGroup{},
nil)
},
- expectImageMock: func(m *mock.MockImageClientMockRecorder) {},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {},
- want: &infrav1.ReferencedMachineResources{},
- wantErr: true,
+ want: &infrav1.ReferencedMachineResources{},
+ wantErr: true,
},
{
- testName: "Image by Name not found",
- imageFilter: &infrav1.ImageFilter{Name: pointer.String("test-image")},
- expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {},
+ testName: "Image by Name not found",
+ spec: infrav1.OpenStackMachineSpec{
+ Image: infrav1.ImageFilter{Name: pointer.String("test-image")},
+ },
expectImageMock: func(m *mock.MockImageClientMockRecorder) {
- m.ListImages(images.ListOpts{Name: "test-image"}).Return(
- []images.Image{},
- nil)
+ m.ListImages(images.ListOpts{Name: "test-image"}).Return([]images.Image{}, nil)
},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {},
- want: &infrav1.ReferencedMachineResources{},
- wantErr: true,
+ want: &infrav1.ReferencedMachineResources{},
+ wantErr: true,
},
{
- testName: "PortsOpts set",
- clusterStatus: &infrav1.OpenStackClusterStatus{
- Network: &infrav1.NetworkStatusWithSubnets{
- Subnets: []infrav1.Subnet{
- {
- ID: "test-subnet-id",
+ testName: "Ports set",
+ spec: infrav1.OpenStackMachineSpec{
+ Image: infrav1.ImageFilter{ID: pointer.String(imageID1)},
+ Ports: []infrav1.PortOpts{
+ {
+ Network: &infrav1.NetworkFilter{
+ ID: networkID2,
},
},
},
},
- portsOpts: &[]infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{
- ID: "test-network-id",
- },
- Trunk: &constFalse,
- },
- },
- expectComputeMock: func(m *mock.MockComputeClientMockRecorder) {},
- expectImageMock: func(m *mock.MockImageClientMockRecorder) {},
- expectNetworkMock: func(m *mock.MockNetworkClientMockRecorder) {
- m.ListExtensions().Return([]extensions.Extension{}, nil)
- },
want: &infrav1.ReferencedMachineResources{
ImageID: imageID1,
- Ports: []infrav1.PortOpts{
+ Ports: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: "test-network-id",
- },
- Trunk: &constFalse,
+ Name: "test-instance-0",
+ Description: "Created by cluster-api-provider-openstack cluster test-cluster",
+ NetworkID: networkID2,
},
},
},
- wantErr: false,
},
}
- for _, tt := range tests {
+ for i, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
+ tt := &tests[i]
g := NewWithT(t)
log := testr.New(t)
mockCtrl := gomock.NewController(t)
mockScopeFactory := scope.NewMockScopeFactory(mockCtrl, "")
- tt.expectComputeMock(mockScopeFactory.ComputeClient.EXPECT())
- tt.expectImageMock(mockScopeFactory.ImageClient.EXPECT())
- tt.expectNetworkMock(mockScopeFactory.NetworkClient.EXPECT())
-
- // Set defaults for required fields
- imageFilter := &infrav1.ImageFilter{ID: pointer.String(imageID1)}
- if tt.imageFilter != nil {
- imageFilter = tt.imageFilter
+ if tt.expectComputeMock != nil {
+ tt.expectComputeMock(mockScopeFactory.ComputeClient.EXPECT())
}
- portsOpts := &[]infrav1.PortOpts{}
- if tt.portsOpts != nil {
- portsOpts = tt.portsOpts
+ if tt.expectImageMock != nil {
+ tt.expectImageMock(mockScopeFactory.ImageClient.EXPECT())
}
-
- openStackCluster := &infrav1.OpenStackCluster{}
- if tt.clusterStatus != nil {
- openStackCluster.Status = *tt.clusterStatus
+ if tt.expectNetworkMock != nil {
+ tt.expectNetworkMock(mockScopeFactory.NetworkClient.EXPECT())
}
- machineSpec := &infrav1.OpenStackMachineSpec{
- ServerGroup: tt.serverGroupFilter,
- Image: *imageFilter,
- Ports: *portsOpts,
+ openStackCluster := &infrav1.OpenStackCluster{
+ Status: infrav1.OpenStackClusterStatus{
+ Network: &infrav1.NetworkStatusWithSubnets{
+ NetworkStatus: infrav1.NetworkStatus{
+ ID: networkID1,
+ },
+ Subnets: []infrav1.Subnet{
+ {
+ ID: subnetID,
+ },
+ },
+ },
+ },
}
- resources := &infrav1.ReferencedMachineResources{}
+ resources := tt.before
+ if resources == nil {
+ resources = &infrav1.ReferencedMachineResources{}
+ }
+ clusterResourceName := "test-cluster"
+ baseName := "test-instance"
scope := scope.NewWithLogger(mockScopeFactory, log)
- _, err := ResolveReferencedMachineResources(scope, openStackCluster, machineSpec, resources)
+ _, err := ResolveReferencedMachineResources(scope, &tt.spec, resources, clusterResourceName, baseName, openStackCluster, tt.managedSecurityGroup)
if tt.wantErr {
g.Expect(err).Error()
return
@@ -191,3 +197,94 @@ func Test_ResolveReferencedMachineResources(t *testing.T) {
})
}
}
+
+func Test_getInstanceTags(t *testing.T) {
+ tests := []struct {
+ name string
+ spec func() *infrav1.OpenStackMachineSpec
+ openStackCluster func() *infrav1.OpenStackCluster
+ wantMachineTags []string
+ }{
+ {
+ name: "No tags",
+ spec: func() *infrav1.OpenStackMachineSpec {
+ return &infrav1.OpenStackMachineSpec{}
+ },
+ openStackCluster: func() *infrav1.OpenStackCluster {
+ return &infrav1.OpenStackCluster{
+ Spec: infrav1.OpenStackClusterSpec{},
+ }
+ },
+ wantMachineTags: []string{},
+ },
+ {
+ name: "Machine tags only",
+ spec: func() *infrav1.OpenStackMachineSpec {
+ return &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",
+ spec: func() *infrav1.OpenStackMachineSpec {
+ return &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",
+ spec: func() *infrav1.OpenStackMachineSpec {
+ return &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",
+ spec: func() *infrav1.OpenStackMachineSpec {
+ return &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"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotMachineTags := InstanceTags(tt.spec(), tt.openStackCluster())
+ if !reflect.DeepEqual(gotMachineTags, tt.wantMachineTags) {
+ t.Errorf("getInstanceTags() = %v, want %v", gotMachineTags, tt.wantMachineTags)
+ }
+ })
+ }
+}
diff --git a/pkg/cloud/services/loadbalancer/loadbalancer.go b/pkg/cloud/services/loadbalancer/loadbalancer.go
index 993a1dfb22..2de72c81d6 100644
--- a/pkg/cloud/services/loadbalancer/loadbalancer.go
+++ b/pkg/cloud/services/loadbalancer/loadbalancer.go
@@ -67,13 +67,13 @@ var lookupHost = func(host string) (*string, error) {
}
// ReconcileLoadBalancer reconciles the load balancer for the given cluster.
-func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string, apiServerPort int) (bool, error) {
+func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, apiServerPort int) (bool, error) {
lbSpec := openStackCluster.Spec.APIServerLoadBalancer
if !lbSpec.IsEnabled() {
return false, nil
}
- loadBalancerName := getLoadBalancerName(clusterName)
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
s.scope.Logger().Info("Reconciling load balancer", "name", loadBalancerName)
lbStatus := openStackCluster.Status.APIServerLoadBalancer
@@ -82,7 +82,7 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
openStackCluster.Status.APIServerLoadBalancer = lbStatus
}
- lb, err := s.getOrCreateAPILoadBalancer(openStackCluster, clusterName)
+ lb, err := s.getOrCreateAPILoadBalancer(openStackCluster, clusterResourceName)
if err != nil {
return false, err
}
@@ -106,7 +106,7 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
return false, err
}
- fp, err := s.networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterName, floatingIPAddress)
+ fp, err := s.networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterResourceName, floatingIPAddress)
if err != nil {
return false, err
}
@@ -136,7 +136,7 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
portList := []int{apiServerPort}
portList = append(portList, lbSpec.AdditionalPorts...)
for _, port := range portList {
- if err := s.reconcileAPILoadBalancerListener(lb, openStackCluster, clusterName, port); err != nil {
+ if err := s.reconcileAPILoadBalancerListener(lb, openStackCluster, clusterResourceName, port); err != nil {
return false, err
}
}
@@ -256,8 +256,8 @@ func (s *Service) isAllowsCIDRSSupported(lb *loadbalancers.LoadBalancer) (bool,
}
// getOrCreateAPILoadBalancer returns an existing API loadbalancer if it already exists, or creates a new one if it does not.
-func (s *Service) getOrCreateAPILoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string) (*loadbalancers.LoadBalancer, error) {
- loadBalancerName := getLoadBalancerName(clusterName)
+func (s *Service) getOrCreateAPILoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) (*loadbalancers.LoadBalancer, error) {
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
lb, err := s.checkIfLbExists(loadBalancerName)
if err != nil {
return nil, err
@@ -302,7 +302,7 @@ func (s *Service) getOrCreateAPILoadBalancer(openStackCluster *infrav1.OpenStack
lbCreateOpts := loadbalancers.CreateOpts{
Name: loadBalancerName,
VipSubnetID: subnetID,
- Description: names.GetDescription(clusterName),
+ Description: names.GetDescription(clusterResourceName),
Provider: lbProvider,
Tags: openStackCluster.Spec.Tags,
}
@@ -321,8 +321,8 @@ func (s *Service) getOrCreateAPILoadBalancer(openStackCluster *infrav1.OpenStack
}
// reconcileAPILoadBalancerListener ensures that the listener on the given port exists and is configured correctly.
-func (s *Service) reconcileAPILoadBalancerListener(lb *loadbalancers.LoadBalancer, openStackCluster *infrav1.OpenStackCluster, clusterName string, port int) error {
- loadBalancerName := getLoadBalancerName(clusterName)
+func (s *Service) reconcileAPILoadBalancerListener(lb *loadbalancers.LoadBalancer, openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, port int) error {
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
lbPortObjectsName := fmt.Sprintf("%s-%d", loadBalancerName, port)
if openStackCluster.Status.APIServerLoadBalancer == nil {
@@ -508,7 +508,7 @@ func (s *Service) getOrCreateMonitor(openStackCluster *infrav1.OpenStackCluster,
return nil
}
-func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, clusterName, ip string) error {
+func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStackCluster, openStackMachine *infrav1.OpenStackMachine, clusterResourceName, ip string) error {
if openStackCluster.Status.Network == nil {
return errors.New("network is not yet available in openStackCluster.Status")
}
@@ -522,7 +522,7 @@ func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStac
return errors.New("ControlPlaneEndpoint is not yet set in openStackCluster.Spec")
}
- loadBalancerName := getLoadBalancerName(clusterName)
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
s.scope.Logger().Info("Reconciling load balancer member", "loadBalancerName", loadBalancerName)
lbID := openStackCluster.Status.APIServerLoadBalancer.ID
@@ -598,8 +598,8 @@ func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStac
return nil
}
-func (s *Service) DeleteLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
- loadBalancerName := getLoadBalancerName(clusterName)
+func (s *Service) DeleteLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
lb, err := s.checkIfLbExists(loadBalancerName)
if err != nil {
return err
@@ -645,12 +645,12 @@ func (s *Service) DeleteLoadBalancer(openStackCluster *infrav1.OpenStackCluster,
return nil
}
-func (s *Service) DeleteLoadBalancerMember(openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, clusterName string) error {
+func (s *Service) DeleteLoadBalancerMember(openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, clusterResourceName string) error {
if openStackMachine == nil || !util.IsControlPlaneMachine(machine) {
return nil
}
- loadBalancerName := getLoadBalancerName(clusterName)
+ loadBalancerName := getLoadBalancerName(clusterResourceName)
lb, err := s.checkIfLbExists(loadBalancerName)
if err != nil {
return err
@@ -705,8 +705,8 @@ func (s *Service) DeleteLoadBalancerMember(openStackCluster *infrav1.OpenStackCl
return nil
}
-func getLoadBalancerName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterName, kubeapiLBSuffix)
+func getLoadBalancerName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterResourceName, kubeapiLBSuffix)
}
func (s *Service) checkIfLbExists(name string) (*loadbalancers.LoadBalancer, error) {
diff --git a/pkg/cloud/services/networking/floatingip.go b/pkg/cloud/services/networking/floatingip.go
index 68f6cb2aac..3e495e77d0 100644
--- a/pkg/cloud/services/networking/floatingip.go
+++ b/pkg/cloud/services/networking/floatingip.go
@@ -33,7 +33,7 @@ import (
"sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names"
)
-func (s *Service) GetOrCreateFloatingIP(eventObject runtime.Object, openStackCluster *infrav1.OpenStackCluster, clusterName string, ip *string) (*floatingips.FloatingIP, error) {
+func (s *Service) GetOrCreateFloatingIP(eventObject runtime.Object, openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, ip *string) (*floatingips.FloatingIP, error) {
var fp *floatingips.FloatingIP
var err error
var fpCreateOpts floatingips.CreateOpts
@@ -51,7 +51,7 @@ func (s *Service) GetOrCreateFloatingIP(eventObject runtime.Object, openStackClu
}
fpCreateOpts.FloatingNetworkID = openStackCluster.Status.ExternalNetwork.ID
- fpCreateOpts.Description = names.GetDescription(clusterName)
+ fpCreateOpts.Description = names.GetDescription(clusterResourceName)
s.scope.Logger().Info("Creating floating IP", "ip", fpCreateOpts.FloatingIP, "floatingNetworkID", openStackCluster.Status.ExternalNetwork.ID, "description", fpCreateOpts.Description)
diff --git a/pkg/cloud/services/networking/network.go b/pkg/cloud/services/networking/network.go
index b5c16b003c..fbee4d4b10 100644
--- a/pkg/cloud/services/networking/network.go
+++ b/pkg/cloud/services/networking/network.go
@@ -116,8 +116,8 @@ func (s *Service) ReconcileExternalNetwork(openStackCluster *infrav1.OpenStackCl
return fmt.Errorf("found %d external networks, which should not happen", len(networkList))
}
-func (s *Service) ReconcileNetwork(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
- networkName := getNetworkName(clusterName)
+func (s *Service) ReconcileNetwork(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
+ networkName := getNetworkName(clusterResourceName)
s.scope.Logger().Info("Reconciling network", "name", networkName)
res, err := s.getNetworkByName(networkName)
@@ -171,8 +171,8 @@ func (s *Service) ReconcileNetwork(openStackCluster *infrav1.OpenStackCluster, c
return nil
}
-func (s *Service) DeleteNetwork(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
- networkName := getNetworkName(clusterName)
+func (s *Service) DeleteNetwork(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
+ networkName := getNetworkName(clusterResourceName)
network, err := s.getNetworkByName(networkName)
if err != nil {
return err
@@ -191,13 +191,13 @@ func (s *Service) DeleteNetwork(openStackCluster *infrav1.OpenStackCluster, clus
return nil
}
-func (s *Service) ReconcileSubnet(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
+func (s *Service) ReconcileSubnet(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
if openStackCluster.Status.Network == nil || openStackCluster.Status.Network.ID == "" {
s.scope.Logger().V(4).Info("No need to reconcile network components since no network exists")
return nil
}
- subnetName := getSubnetName(clusterName)
+ subnetName := getSubnetName(clusterResourceName)
s.scope.Logger().Info("Reconciling subnet", "name", subnetName)
subnetList, err := s.client.ListSubnet(subnets.ListOpts{
@@ -217,7 +217,7 @@ func (s *Service) ReconcileSubnet(openStackCluster *infrav1.OpenStackCluster, cl
var subnet *subnets.Subnet
if len(subnetList) == 0 {
var err error
- subnet, err = s.createSubnet(openStackCluster, clusterName, subnetName)
+ subnet, err = s.createSubnet(openStackCluster, clusterResourceName, subnetName)
if err != nil {
return err
}
@@ -237,14 +237,14 @@ func (s *Service) ReconcileSubnet(openStackCluster *infrav1.OpenStackCluster, cl
return nil
}
-func (s *Service) createSubnet(openStackCluster *infrav1.OpenStackCluster, clusterName string, name string) (*subnets.Subnet, error) {
+func (s *Service) createSubnet(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, name string) (*subnets.Subnet, error) {
opts := subnets.CreateOpts{
NetworkID: openStackCluster.Status.Network.ID,
Name: name,
IPVersion: 4,
CIDR: openStackCluster.Spec.ManagedSubnets[0].CIDR,
DNSNameservers: openStackCluster.Spec.ManagedSubnets[0].DNSNameservers,
- Description: names.GetDescription(clusterName),
+ Description: names.GetDescription(clusterResourceName),
}
for _, pool := range openStackCluster.Spec.ManagedSubnets[0].AllocationPools {
@@ -374,12 +374,12 @@ func (s *Service) getSubnetByFilter(listOpts subnets.ListOpts) (*subnets.Subnet,
return &subnets[0], nil
}
-func getSubnetName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName)
+func getSubnetName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterResourceName)
}
-func getNetworkName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName)
+func getNetworkName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterResourceName)
}
// GetNetworkByID retrieves network by the ID.
diff --git a/pkg/cloud/services/networking/network_test.go b/pkg/cloud/services/networking/network_test.go
index 52b0fdb0d4..ff772ffe76 100644
--- a/pkg/cloud/services/networking/network_test.go
+++ b/pkg/cloud/services/networking/network_test.go
@@ -34,12 +34,15 @@ import (
"sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names"
)
+const (
+ clusterResourceName = "test-cluster"
+)
+
func Test_ReconcileNetwork(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
- clusterName := "test-cluster"
- expectedNetworkName := getNetworkName(clusterName)
+ expectedNetworkName := getNetworkName(clusterResourceName)
fakeNetworkID := "d08803fc-2fa5-4179-b9f7-8c43d0af2fe6"
tests := []struct {
@@ -195,7 +198,7 @@ func Test_ReconcileNetwork(t *testing.T) {
client: mockClient,
scope: scope.NewWithLogger(scopeFactory, log),
}
- err := s.ReconcileNetwork(tt.openStackCluster, clusterName)
+ err := s.ReconcileNetwork(tt.openStackCluster, clusterResourceName)
g.Expect(err).ShouldNot(HaveOccurred())
})
}
@@ -435,9 +438,8 @@ func Test_ReconcileSubnet(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
- clusterName := "test-cluster"
- expectedSubnetName := getSubnetName(clusterName)
- expectedSubnetDesc := names.GetDescription(clusterName)
+ expectedSubnetName := getSubnetName(clusterResourceName)
+ expectedSubnetDesc := names.GetDescription(clusterResourceName)
fakeSubnetID := "d08803fc-2fa5-4179-b9d7-8c43d0af2fe6"
fakeCIDR := "10.0.0.0/24"
fakeNetworkID := "d08803fc-2fa5-4279-b9f7-8c45d0ff2fe6"
@@ -684,7 +686,7 @@ func Test_ReconcileSubnet(t *testing.T) {
client: mockClient,
scope: scope.NewWithLogger(mockScopeFactory, log),
}
- err := s.ReconcileSubnet(tt.openStackCluster, clusterName)
+ err := s.ReconcileSubnet(tt.openStackCluster, clusterResourceName)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(tt.openStackCluster.Status).To(Equal(*tt.want))
})
diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go
index b3fd46b3c2..df5975981f 100644
--- a/pkg/cloud/services/networking/port.go
+++ b/pkg/cloud/services/networking/port.go
@@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
+ "slices"
"strings"
"time"
@@ -109,123 +110,94 @@ func (s *Service) GetPortForExternalNetwork(instanceID string, externalNetworkID
return nil, nil
}
-func (s *Service) CreatePort(eventObject runtime.Object, clusterName string, portName string, portOpts *infrav1.PortOpts, defaultSecurityGroups []string, baseTags []string) (*ports.Port, error) {
- var err error
- networkID := portOpts.Network.ID
-
- var description string
- if portOpts.Description != nil {
- description = *portOpts.Description
- } else {
- description = names.GetDescription(clusterName)
- }
-
- var securityGroups []string
- addressPairs := []ports.AddressPair{}
- if portOpts.DisablePortSecurity == nil || !*portOpts.DisablePortSecurity {
- for _, ap := range portOpts.AllowedAddressPairs {
+func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec) (*ports.Port, error) {
+ var addressPairs []ports.AddressPair
+ if !pointer.BoolDeref(portSpec.DisablePortSecurity, false) {
+ for _, ap := range portSpec.AllowedAddressPairs {
addressPairs = append(addressPairs, ports.AddressPair{
IPAddress: ap.IPAddress,
MACAddress: pointer.StringDeref(ap.MACAddress, ""),
})
}
- if portOpts.SecurityGroups != nil {
- securityGroups, err = s.GetSecurityGroups(portOpts.SecurityGroups)
- if err != nil {
- return nil, fmt.Errorf("error getting security groups: %v", err)
- }
- }
- // inherit port security groups from the instance if not explicitly specified
- if len(securityGroups) == 0 {
- securityGroups = defaultSecurityGroups
- }
}
- var fixedIPs interface{}
- if len(portOpts.FixedIPs) > 0 {
- fips := make([]ports.IP, 0, len(portOpts.FixedIPs)+1)
- for _, fixedIP := range portOpts.FixedIPs {
- subnetID, err := s.getSubnetIDForFixedIP(fixedIP.Subnet, networkID)
- if err != nil {
- return nil, err
- }
- fips = append(fips, ports.IP{
- SubnetID: subnetID,
+ var fixedIPs []ports.IP
+ if len(portSpec.FixedIPs) > 0 {
+ fixedIPs = make([]ports.IP, len(portSpec.FixedIPs))
+ for i, fixedIP := range portSpec.FixedIPs {
+ fixedIPs[i] = ports.IP{
+ SubnetID: pointer.StringDeref(fixedIP.SubnetID, ""),
IPAddress: pointer.StringDeref(fixedIP.IPAddress, ""),
- })
+ }
}
- fixedIPs = fips
}
var valueSpecs *map[string]string
- if len(portOpts.ValueSpecs) > 0 {
- vs := make(map[string]string, len(portOpts.ValueSpecs))
- for _, valueSpec := range portOpts.ValueSpecs {
+ if len(portSpec.ValueSpecs) > 0 {
+ vs := make(map[string]string, len(portSpec.ValueSpecs))
+ for _, valueSpec := range portSpec.ValueSpecs {
vs[valueSpec.Key] = valueSpec.Value
}
valueSpecs = &vs
}
- var createOpts ports.CreateOptsBuilder
-
- // Gophercloud expects a *[]string. We translate a nil slice to a nil pointer.
- var securityGroupsPtr *[]string
- if securityGroups != nil {
- securityGroupsPtr = &securityGroups
- }
-
- createOpts = ports.CreateOpts{
- Name: portName,
- NetworkID: networkID,
- Description: description,
- AdminStateUp: portOpts.AdminStateUp,
- MACAddress: pointer.StringDeref(portOpts.MACAddress, ""),
- SecurityGroups: securityGroupsPtr,
+ var builder ports.CreateOptsBuilder
+ createOpts := ports.CreateOpts{
+ Name: portSpec.Name,
+ NetworkID: portSpec.NetworkID,
+ Description: portSpec.Description,
+ AdminStateUp: portSpec.AdminStateUp,
+ MACAddress: pointer.StringDeref(portSpec.MACAddress, ""),
AllowedAddressPairs: addressPairs,
- FixedIPs: fixedIPs,
ValueSpecs: valueSpecs,
- PropagateUplinkStatus: portOpts.PropagateUplinkStatus,
+ PropagateUplinkStatus: portSpec.PropagateUplinkStatus,
+ }
+ if fixedIPs != nil {
+ createOpts.FixedIPs = fixedIPs
+ }
+ if portSpec.SecurityGroups != nil {
+ createOpts.SecurityGroups = &portSpec.SecurityGroups
}
+ builder = createOpts
- if portOpts.DisablePortSecurity != nil {
- portSecurity := !*portOpts.DisablePortSecurity
- createOpts = portsecurity.PortCreateOptsExt{
- CreateOptsBuilder: createOpts,
+ if portSpec.DisablePortSecurity != nil {
+ portSecurity := !*portSpec.DisablePortSecurity
+ portSecurityOpts := portsecurity.PortCreateOptsExt{
+ CreateOptsBuilder: builder,
PortSecurityEnabled: &portSecurity,
}
+ builder = portSecurityOpts
}
- createOpts = portsbinding.CreateOptsExt{
- CreateOptsBuilder: createOpts,
- HostID: pointer.StringDeref(portOpts.HostID, ""),
- VNICType: pointer.StringDeref(portOpts.VNICType, ""),
- Profile: getPortProfile(portOpts.Profile),
+ portsBindingOpts := portsbinding.CreateOptsExt{
+ CreateOptsBuilder: builder,
+ HostID: pointer.StringDeref(portSpec.HostID, ""),
+ VNICType: pointer.StringDeref(portSpec.VNICType, ""),
+ Profile: getPortProfile(portSpec.Profile),
}
+ builder = portsBindingOpts
- port, err := s.client.CreatePort(createOpts)
+ port, err := s.client.CreatePort(builder)
if err != nil {
- record.Warnf(eventObject, "FailedCreatePort", "Failed to create port %s: %v", portName, err)
+ record.Warnf(eventObject, "FailedCreatePort", "Failed to create port %s: %v", port.Name, err)
return nil, err
}
- var tags []string
- tags = append(tags, baseTags...)
- tags = append(tags, portOpts.Tags...)
- if len(tags) > 0 {
- if err = s.replaceAllAttributesTags(eventObject, portResource, port.ID, tags); err != nil {
- record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace port tags %s: %v", portName, err)
+ if len(portSpec.Tags) > 0 {
+ if err = s.replaceAllAttributesTags(eventObject, portResource, port.ID, portSpec.Tags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace port tags %s: %v", portSpec.Name, err)
return nil, err
}
}
record.Eventf(eventObject, "SuccessfulCreatePort", "Created port %s with id %s", port.Name, port.ID)
- if portOpts.Trunk != nil && *portOpts.Trunk {
- trunk, err := s.getOrCreateTrunk(eventObject, clusterName, port.Name, port.ID)
+ if pointer.BoolDeref(portSpec.Trunk, false) {
+ trunk, err := s.getOrCreateTrunkForPort(eventObject, port)
if err != nil {
- record.Warnf(eventObject, "FailedCreateTrunk", "Failed to create trunk for port %s: %v", portName, err)
+ record.Warnf(eventObject, "FailedCreateTrunk", "Failed to create trunk for port %s: %v", port.Name, err)
return nil, err
}
- if err = s.replaceAllAttributesTags(eventObject, trunkResource, trunk.ID, tags); err != nil {
- record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", portName, err)
+ if err = s.replaceAllAttributesTags(eventObject, trunkResource, trunk.ID, portSpec.Tags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", port.Name, err)
return nil, err
}
}
@@ -233,32 +205,6 @@ func (s *Service) CreatePort(eventObject runtime.Object, clusterName string, por
return port, nil
}
-func (s *Service) getSubnetIDForFixedIP(subnet *infrav1.SubnetFilter, networkID string) (string, error) {
- if subnet == nil {
- return "", nil
- }
- // Do not query for subnets if UUID is already provided
- if subnet.ID != "" {
- return subnet.ID, nil
- }
-
- opts := filterconvert.SubnetFilterToListOpts(subnet)
- opts.NetworkID = networkID
- subnets, err := s.client.ListSubnet(opts)
- if err != nil {
- return "", err
- }
-
- switch len(subnets) {
- case 0:
- return "", fmt.Errorf("subnet query %v, returns no subnets", *subnet)
- case 1:
- return subnets[0].ID, nil
- default:
- return "", fmt.Errorf("subnet query %v, returns too many subnets: %v", *subnet, subnets)
- }
-}
-
func getPortProfile(p *infrav1.BindingProfile) map[string]interface{} {
if p == nil {
return nil
@@ -357,30 +303,24 @@ func (s *Service) DeleteClusterPorts(openStackCluster *infrav1.OpenStackCluster)
return nil
}
-// GetPortName appends a suffix to an instance name in order to try and get a unique name per port.
-func GetPortName(instanceName string, opts *infrav1.PortOpts, netIndex int) string {
- if opts != nil && opts.NameSuffix != nil {
- return fmt.Sprintf("%s-%s", instanceName, *opts.NameSuffix)
+// getPortName appends a suffix to an instance name in order to try and get a unique name per port.
+func getPortName(baseName string, portSpec *infrav1.PortOpts, netIndex int) string {
+ if portSpec != nil && portSpec.NameSuffix != nil {
+ return fmt.Sprintf("%s-%s", baseName, *portSpec.NameSuffix)
}
- return fmt.Sprintf("%s-%d", instanceName, netIndex)
+ return fmt.Sprintf("%s-%d", baseName, netIndex)
}
-func (s *Service) CreatePorts(eventObject runtime.Object, clusterName, baseName string, securityGroups []infrav1.SecurityGroupFilter, baseTags []string, desiredPorts []infrav1.PortOpts, dependentResources *infrav1.DependentMachineResources) error {
- defaultSecurityGroups, err := s.GetSecurityGroups(securityGroups)
- if err != nil {
- return fmt.Errorf("error getting security groups: %v", err)
- }
-
+func (s *Service) CreatePorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, dependentResources *infrav1.DependentMachineResources) error {
for i := range desiredPorts {
// Skip creation of ports which already exist
if i < len(dependentResources.Ports) {
continue
}
- portOpts := &desiredPorts[i]
- portName := GetPortName(baseName, portOpts, i)
+ portSpec := &desiredPorts[i]
// Events are recorded in CreatePort
- port, err := s.CreatePort(eventObject, clusterName, portName, portOpts, defaultSecurityGroups, baseTags)
+ port, err := s.CreatePort(eventObject, portSpec)
if err != nil {
return err
}
@@ -393,112 +333,159 @@ func (s *Service) CreatePorts(eventObject runtime.Object, clusterName, baseName
return nil
}
-// ConstructPorts builds an array of ports from the instance spec.
+// ConstructPorts builds an array of ports from the machine spec.
// If no ports are in the spec, returns a single port for a network connection to the default cluster network.
-func (s *Service) ConstructPorts(openStackCluster *infrav1.OpenStackCluster, ports []infrav1.PortOpts, trunkEnabled bool, trunkSupported bool) ([]infrav1.PortOpts, error) {
- // If no network is specified, return nil
- if openStackCluster.Status.Network == nil {
- return nil, nil
+func (s *Service) ConstructPorts(spec *infrav1.OpenStackMachineSpec, clusterResourceName, baseName string, defaultNetwork *infrav1.NetworkStatusWithSubnets, managedSecurityGroup *string, baseTags []string) ([]infrav1.ResolvedPortSpec, error) {
+ ports := spec.Ports
+
+ defaultSecurityGroupIDs, err := s.GetSecurityGroups(spec.SecurityGroups)
+ if err != nil {
+ return nil, fmt.Errorf("error getting security groups: %v", err)
+ }
+ if managedSecurityGroup != nil {
+ defaultSecurityGroupIDs = append(defaultSecurityGroupIDs, *managedSecurityGroup)
}
// Ensure user-specified ports have all required fields
- ports, err := s.normalizePorts(ports, openStackCluster, trunkEnabled)
+ resolvedPorts, err := s.normalizePorts(ports, clusterResourceName, baseName, spec.Trunk, defaultSecurityGroupIDs, defaultNetwork, baseTags)
if err != nil {
return nil, err
}
// no networks or ports found in the spec, so create a port on the cluster network
- if len(ports) == 0 {
- port := infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: openStackCluster.Status.Network.ID,
- },
- Trunk: &trunkEnabled,
+ if len(resolvedPorts) == 0 {
+ resolvedPorts = make([]infrav1.ResolvedPortSpec, 1)
+ resolvedPort := &resolvedPorts[0]
+ resolvedPort.Name = getPortName(baseName, nil, 0)
+ resolvedPort.Description = names.GetDescription(clusterResourceName)
+ if len(baseTags) > 0 {
+ resolvedPort.Tags = baseTags
}
- for _, subnet := range openStackCluster.Status.Network.Subnets {
- port.FixedIPs = append(port.FixedIPs, infrav1.FixedIP{
- Subnet: &infrav1.SubnetFilter{
- ID: subnet.ID,
- },
- })
+ if spec.Trunk {
+ resolvedPort.Trunk = &spec.Trunk
}
- ports = []infrav1.PortOpts{port}
+ resolvedPort.SecurityGroups = defaultSecurityGroupIDs
+ resolvedPort.NetworkID, resolvedPort.FixedIPs, _ = defaultNetworkTarget(defaultNetwork)
}
// trunk support is required if any port has trunk enabled
portUsesTrunk := func() bool {
- for _, port := range ports {
- if port.Trunk != nil && *port.Trunk {
+ for _, port := range resolvedPorts {
+ if pointer.BoolDeref(port.Trunk, false) {
return true
}
}
return false
}
if portUsesTrunk() {
+ trunkSupported, err := s.IsTrunkExtSupported()
+ if err != nil {
+ return nil, err
+ }
+
if !trunkSupported {
return nil, fmt.Errorf("there is no trunk support. please ensure that the trunk extension is enabled in your OpenStack deployment")
}
}
- return ports, nil
+ return resolvedPorts, nil
}
// normalizePorts ensures that a user-specified PortOpts has all required fields set. Specifically it:
// - sets the Trunk field to the instance spec default if not specified
// - sets the Network ID field if not specified.
-func (s *Service) normalizePorts(ports []infrav1.PortOpts, openStackCluster *infrav1.OpenStackCluster, trunkEnabled bool) ([]infrav1.PortOpts, error) {
- normalizedPorts := make([]infrav1.PortOpts, 0, len(ports))
+func (s *Service) normalizePorts(ports []infrav1.PortOpts, clusterResourceName, baseName string, trunkEnabled bool, defaultSecurityGroupIDs []string, defaultNetwork *infrav1.NetworkStatusWithSubnets, baseTags []string) ([]infrav1.ResolvedPortSpec, error) {
+ normalizedPorts := make([]infrav1.ResolvedPortSpec, len(ports))
for i := range ports {
- // Deep copy the port to avoid mutating the original
- port := ports[i].DeepCopy()
+ port := &ports[i]
+ normalizedPort := &normalizedPorts[i]
+
+ // Copy fields which don't need to be resolved
+ normalizedPort.ResolvedPortSpecFields = port.ResolvedPortSpecFields
+
+ // Generate a standardised name
+ normalizedPort.Name = getPortName(baseName, port, i)
+
+ // Generate a description if none is provided
+ if port.Description != nil {
+ normalizedPort.Description = *port.Description
+ } else {
+ normalizedPort.Description = names.GetDescription(clusterResourceName)
+ }
+
+ // Tags are inherited base tags plus any port-specific tags
+ normalizedPort.Tags = slices.Concat(baseTags, port.Tags)
// No Trunk field specified for the port, inherit the machine default
if port.Trunk == nil {
- port.Trunk = &trunkEnabled
+ if trunkEnabled {
+ normalizedPort.Trunk = &trunkEnabled
+ }
+ } else {
+ normalizedPort.Trunk = port.Trunk
}
- if err := s.normalizePortTarget(port, openStackCluster, i); err != nil {
+ // Resolve network ID and fixed IPs
+ var err error
+ normalizedPort.NetworkID, normalizedPort.FixedIPs, err = s.normalizePortTarget(port, defaultNetwork, i)
+ if err != nil {
return nil, err
}
- normalizedPorts = append(normalizedPorts, *port)
+ // Resolve security groups
+ if len(port.SecurityGroups) == 0 {
+ normalizedPort.SecurityGroups = defaultSecurityGroupIDs
+ } else {
+ normalizedPort.SecurityGroups, err = s.GetSecurityGroups(port.SecurityGroups)
+ if err != nil {
+ return nil, fmt.Errorf("error getting security groups: %v", err)
+ }
+ }
}
return normalizedPorts, nil
}
-// normalizePortTarget ensures that the port has a network ID.
-func (s *Service) normalizePortTarget(port *infrav1.PortOpts, openStackCluster *infrav1.OpenStackCluster, portIdx int) error {
- // Treat no Network and empty Network the same
- noNetwork := port.Network.IsEmpty()
+func defaultNetworkTarget(network *infrav1.NetworkStatusWithSubnets) (string, []infrav1.ResolvedFixedIP, error) {
+ networkID := network.ID
+ fixedIPs := make([]infrav1.ResolvedFixedIP, len(network.Subnets))
+ for i := range network.Subnets {
+ subnet := &network.Subnets[i]
+ fixedIPs[i].SubnetID = &subnet.ID
+ }
+ return networkID, fixedIPs, nil
+}
+// normalizePortTarget ensures that the port has a network ID.
+func (s *Service) normalizePortTarget(port *infrav1.PortOpts, defaultNetwork *infrav1.NetworkStatusWithSubnets, portIdx int) (string, []infrav1.ResolvedFixedIP, error) {
// No network or subnets defined: use cluster defaults
- if noNetwork && len(port.FixedIPs) == 0 {
- port.Network = &infrav1.NetworkFilter{
- ID: openStackCluster.Status.Network.ID,
- }
- for _, subnet := range openStackCluster.Status.Network.Subnets {
- port.FixedIPs = append(port.FixedIPs, infrav1.FixedIP{
- Subnet: &infrav1.SubnetFilter{
- ID: subnet.ID,
- },
- })
- }
+ if port.Network == nil && len(port.FixedIPs) == 0 {
+ return defaultNetworkTarget(defaultNetwork)
+ }
- return nil
+ var networkID string
+ var resolvedFixedIPs []infrav1.ResolvedFixedIP
+ if len(port.FixedIPs) > 0 {
+ resolvedFixedIPs = make([]infrav1.ResolvedFixedIP, len(port.FixedIPs))
}
+ switch {
+ case port.Network != nil && port.Network.ID != "":
+ networkID = port.Network.ID
+
// No network, but fixed IPs are defined(we handled the no fixed
// IPs case above): try to infer network from a subnet
- if noNetwork {
+ case len(port.FixedIPs) > 0:
s.scope.Logger().V(4).Info("No network defined for port, attempting to infer from subnet", "port", portIdx)
// Look for a unique subnet defined in FixedIPs. If we find one
// we can use it to infer the network ID. We don't need to worry
// here about the case where different FixedIPs have different
- // networks because that will cause an error later when we try
- // to create the port.
- networkID, err := func() (string, error) {
+ // networks because that will cause an error later.
+ var err error
+ networkID, err = func() (string, error) {
for i, fixedIP := range port.FixedIPs {
+ resolvedFixedIP := &resolvedFixedIPs[i]
+
if fixedIP.Subnet == nil {
continue
}
@@ -514,8 +501,8 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, openStackCluster *
return "", err
}
- // Cache the subnet ID in the FixedIP
- fixedIP.Subnet.ID = subnet.ID
+ // Cache the known subnet ID in the FixedIP so we don't fetch it again later
+ resolvedFixedIP.SubnetID = &subnet.ID
return subnet.NetworkID, nil
}
@@ -523,38 +510,40 @@ func (s *Service) normalizePortTarget(port *infrav1.PortOpts, openStackCluster *
return "", fmt.Errorf("port %d has no network and unable to infer from fixed IPs", portIdx)
}()
if err != nil {
- return err
+ return "", nil, err
}
- port.Network = &infrav1.NetworkFilter{
- ID: networkID,
+ // Network is defined by filter
+ default:
+ networkListOpts := filterconvert.NetworkFilterToListOpts(port.Network)
+ netIDs, err := s.GetNetworkIDsByFilter(networkListOpts)
+ if err != nil {
+ return "", nil, err
}
- return nil
- }
-
- // Nothing to do if network ID is already set
- if port.Network.ID != "" {
- return nil
- }
-
- // Network is defined by Filter
- networkListOpts := filterconvert.NetworkFilterToListOpts(port.Network)
- netIDs, err := s.GetNetworkIDsByFilter(networkListOpts)
- if err != nil {
- return err
+ // TODO: These are spec errors: they should set the machine to failed
+ if len(netIDs) > 1 {
+ return "", nil, fmt.Errorf("network filter for port %d returns more than one result", portIdx)
+ } else if len(netIDs) == 0 {
+ return "", nil, fmt.Errorf("network filter for port %d returns no networks", portIdx)
+ }
+ networkID = netIDs[0]
}
- // TODO: These are spec errors: they should set the machine to failed
- if len(netIDs) > 1 {
- return fmt.Errorf("network filter for port %d returns more than one result", portIdx)
- } else if len(netIDs) == 0 {
- return fmt.Errorf("network filter for port %d returns no networks", portIdx)
+ // Network ID is now known. Resolve all FixedIPs
+ for i, fixedIP := range port.FixedIPs {
+ resolvedFixedIP := &resolvedFixedIPs[i]
+ resolvedFixedIP.IPAddress = fixedIP.IPAddress
+ if fixedIP.Subnet != nil && resolvedFixedIP.SubnetID == nil {
+ subnet, err := s.GetNetworkSubnetByFilter(networkID, fixedIP.Subnet)
+ if err != nil {
+ return "", nil, err
+ }
+ resolvedFixedIP.SubnetID = &subnet.ID
+ }
}
- port.Network.ID = netIDs[0]
-
- return nil
+ return networkID, resolvedFixedIPs, nil
}
// IsTrunkExtSupported verifies trunk setup on the OpenStack deployment.
@@ -571,7 +560,7 @@ func (s *Service) IsTrunkExtSupported() (trunknSupported bool, err error) {
// AdoptPorts looks for ports in desiredPorts which were previously created, and adds them to dependentResources.Ports.
// A port matches if it has the same name and network ID as the desired port.
-func (s *Service) AdoptPorts(scope *scope.WithLogger, baseName string, desiredPorts []infrav1.PortOpts, dependentResources *infrav1.DependentMachineResources) error {
+func (s *Service) AdoptPorts(scope *scope.WithLogger, desiredPorts []infrav1.ResolvedPortSpec, dependentResources *infrav1.DependentMachineResources) error {
// We can skip adoption if the ports are already in the status
if len(desiredPorts) == len(dependentResources.Ports) {
return nil
@@ -582,21 +571,20 @@ func (s *Service) AdoptPorts(scope *scope.WithLogger, baseName string, desiredPo
// We create ports in order and adopt them in order in PortsStatus.
// This means that if port N doesn't exist we know that ports >N don't exist.
// We can therefore stop searching for ports once we find one that doesn't exist.
- for i, port := range desiredPorts {
+ for i := range desiredPorts {
// check if the port is in status first and if it is, skip it
if i < len(dependentResources.Ports) {
scope.Logger().V(5).Info("Port already in status, skipping it", "port index", i)
continue
}
- portOpts := &desiredPorts[i]
- portName := GetPortName(baseName, portOpts, i)
+ portSpec := &desiredPorts[i]
ports, err := s.client.ListPort(ports.ListOpts{
- Name: portName,
- NetworkID: port.Network.ID,
+ Name: portSpec.Name,
+ NetworkID: portSpec.NetworkID,
})
if err != nil {
- return fmt.Errorf("searching for existing port %s in network %s: %v", portName, port.Network.ID, err)
+ return fmt.Errorf("searching for existing port %s in network %s: %v", portSpec.Name, portSpec.NetworkID, err)
}
// if the port is not found, we stop the adoption of ports since the rest of the ports will not be found either
// and will be created after the adoption
@@ -605,7 +593,7 @@ func (s *Service) AdoptPorts(scope *scope.WithLogger, baseName string, desiredPo
return nil
}
if len(ports) > 1 {
- return fmt.Errorf("found multiple ports with name %s", portName)
+ return fmt.Errorf("found multiple ports with name %s", portSpec.Name)
}
// The desired port was found, so we add it to the status
diff --git a/pkg/cloud/services/networking/port_test.go b/pkg/cloud/services/networking/port_test.go
index 340d78ff91..d0ae57ab6d 100644
--- a/pkg/cloud/services/networking/port_test.go
+++ b/pkg/cloud/services/networking/port_test.go
@@ -22,14 +22,17 @@ import (
"github.com/go-logr/logr/testr"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
. "github.com/onsi/gomega"
+ "github.com/onsi/gomega/types"
"k8s.io/utils/pointer"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
@@ -38,370 +41,282 @@ import (
)
func Test_CreatePort(t *testing.T) {
- // Arbitrary GUIDs used in the tests
- netID := "7fd24ceb-788a-441f-ad0a-d8e2f5d31a1d"
- subnetID1 := "d9c88a6d-0b8c-48ff-8f0e-8d85a078c194"
- subnetID2 := "d9c2346d-05gc-48er-9ut4-ig83ayt8c7h4"
- portID1 := "50214c48-c09e-4a54-914f-97b40fd22802"
- hostID := "825c1b11-3dca-4bfe-a2d8-a3cc1964c8d5"
- trunkID := "eb7541fa-5e2a-4cca-b2c3-dfa409b917ce"
- portSecurityGroupID := "f51d1206-fc5a-4f7a-a5c0-2e03e44e4dc0"
-
- // Other arbitrary variables passed in to the tests
- instanceSecurityGroups := []string{"instance-secgroup"}
- securityGroupUUIDs := []string{portSecurityGroupID}
- portSecurityGroupFilters := []infrav1.SecurityGroupFilter{{ID: portSecurityGroupID, Name: "port-secgroup"}}
- valueSpecs := map[string]string{"key": "value"}
+ // Arbitrary values used in the tests
+ const (
+ netID = "7fd24ceb-788a-441f-ad0a-d8e2f5d31a1d"
+ subnetID1 = "d9c88a6d-0b8c-48ff-8f0e-8d85a078c194"
+ subnetID2 = "d9c2346d-05gc-48er-9ut4-ig83ayt8c7h4"
+ portID = "50214c48-c09e-4a54-914f-97b40fd22802"
+ hostID = "825c1b11-3dca-4bfe-a2d8-a3cc1964c8d5"
+ trunkID = "eb7541fa-5e2a-4cca-b2c3-dfa409b917ce"
+ portSecurityGroupID = "f51d1206-fc5a-4f7a-a5c0-2e03e44e4dc0"
+ ipAddress1 = "192.0.2.1"
+ ipAddress2 = "198.51.100.1"
+ macAddress = "de:ad:be:ef:fe:ed"
+ )
tests := []struct {
- name string
- portName string
- port infrav1.PortOpts
- instanceSecurityGroups []string
- tags []string
- expect func(m *mock.MockNetworkClientMockRecorder)
+ name string
+ port infrav1.ResolvedPortSpec
+ expect func(m *mock.MockNetworkClientMockRecorder, g Gomega)
// Note the 'wanted' port isn't so important, since it will be whatever we tell ListPort or CreatePort to return.
// Mostly in this test suite, we're checking that CreatePort is called with the expected port opts.
want *ports.Port
wantErr bool
}{
{
- "creates port with defaults (description and secgroups) if not specified in portOpts",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- },
- instanceSecurityGroups,
- []string{},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- SecurityGroups: &instanceSecurityGroups,
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
- },
- }).Return(&ports.Port{ID: portID1}, nil)
- },
- &ports.Port{ID: portID1},
- false,
- },
- {
- "creates port with specified portOpts if no matching port exists",
- "foo-port-bar",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- NameSuffix: pointer.String("bar"),
- Description: pointer.String("this is a test port"),
- MACAddress: pointer.String("fe:fe:fe:fe:fe:fe"),
- AdminStateUp: pointer.Bool(true),
- FixedIPs: []infrav1.FixedIP{
+ name: "creates port correctly with all options specified except tags, trunk and disablePortSecurity",
+ port: infrav1.ResolvedPortSpec{
+ Name: "foo-port-1",
+ Description: "Created by cluster-api-provider-openstack cluster test-cluster",
+ NetworkID: netID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {
+ SubnetID: pointer.String(subnetID1),
+ IPAddress: pointer.String(ipAddress1),
+ },
{
- Subnet: &infrav1.SubnetFilter{
- Name: "subnetFoo",
+ IPAddress: pointer.String(ipAddress2),
+ },
+ {
+ SubnetID: pointer.String(subnetID2),
+ },
+ },
+ SecurityGroups: []string{portSecurityGroupID},
+ ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{
+ AdminStateUp: pointer.Bool(true),
+ MACAddress: pointer.String(macAddress),
+ AllowedAddressPairs: []infrav1.AddressPair{
+ {
+ IPAddress: ipAddress1,
+ MACAddress: pointer.String(macAddress),
+ },
+ {
+ IPAddress: ipAddress2,
},
- IPAddress: pointer.String("192.168.0.50"),
- }, {
- IPAddress: pointer.String("192.168.1.50"),
- },
- },
- SecurityGroups: portSecurityGroupFilters,
- AllowedAddressPairs: []infrav1.AddressPair{{
- IPAddress: "10.10.10.10",
- MACAddress: pointer.String("f1:f1:f1:f1:f1:f1"),
- }},
- HostID: pointer.String(hostID),
- VNICType: pointer.String("direct"),
- Profile: &infrav1.BindingProfile{
- OVSHWOffload: pointer.Bool(true),
- TrustedVF: pointer.Bool(true),
- },
- DisablePortSecurity: pointer.Bool(false),
- Tags: []string{"my-port-tag"},
- },
- nil,
- nil,
- func(m *mock.MockNetworkClientMockRecorder) {
- portCreateOpts := ports.CreateOpts{
+ },
+ HostID: pointer.String(hostID),
+ VNICType: pointer.String("normal"),
+ Profile: &infrav1.BindingProfile{
+ OVSHWOffload: pointer.Bool(true),
+ TrustedVF: pointer.Bool(true),
+ },
+ PropagateUplinkStatus: pointer.Bool(true),
+ ValueSpecs: []infrav1.ValueSpec{
+ {
+ Name: "test-valuespec",
+ Key: "test-key",
+ Value: "test-value",
+ },
+ },
+ },
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) {
+ var expectedCreateOpts ports.CreateOptsBuilder
+ expectedCreateOpts = ports.CreateOpts{
+ Name: "foo-port-1",
+ Description: "Created by cluster-api-provider-openstack cluster test-cluster",
NetworkID: netID,
- Name: "foo-port-bar",
- Description: "this is a test port",
AdminStateUp: pointer.Bool(true),
- MACAddress: "fe:fe:fe:fe:fe:fe",
+ MACAddress: macAddress,
FixedIPs: []ports.IP{
{
SubnetID: subnetID1,
- IPAddress: "192.168.0.50",
- }, {
- IPAddress: "192.168.1.50",
+ IPAddress: ipAddress1,
+ },
+ {
+ IPAddress: ipAddress2,
+ },
+ {
+ SubnetID: subnetID2,
},
},
- SecurityGroups: &securityGroupUUIDs,
- AllowedAddressPairs: []ports.AddressPair{{
- IPAddress: "10.10.10.10",
- MACAddress: "f1:f1:f1:f1:f1:f1",
- }},
- }
- portsecurityCreateOptsExt := portsecurity.PortCreateOptsExt{
- CreateOptsBuilder: portCreateOpts,
- PortSecurityEnabled: pointer.Bool(true),
+ SecurityGroups: &[]string{portSecurityGroupID},
+ AllowedAddressPairs: []ports.AddressPair{
+ {
+ IPAddress: ipAddress1,
+ MACAddress: macAddress,
+ },
+ {
+ IPAddress: ipAddress2,
+ },
+ },
+ PropagateUplinkStatus: pointer.Bool(true),
+ ValueSpecs: &map[string]string{
+ "test-key": "test-value",
+ },
}
- portbindingCreateOptsExt := portsbinding.CreateOptsExt{
- // Note for the test matching, the order in which the builders are composed
- // must be the same as in the function we are testing.
- CreateOptsBuilder: portsecurityCreateOptsExt,
+ expectedCreateOpts = portsbinding.CreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
HostID: hostID,
- VNICType: "direct",
+ VNICType: "normal",
Profile: map[string]interface{}{
"capabilities": []string{"switchdev"},
"trusted": true,
},
}
- m.
- CreatePort(portbindingCreateOptsExt).
- Return(&ports.Port{
- ID: portID1,
- }, nil)
- m.ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-port-tag"}}).Return([]string{"my-port-tag"}, nil)
- m.
- ListSubnet(subnets.ListOpts{
- Name: "subnetFoo",
- NetworkID: netID,
- }).Return([]subnets.Subnet{
- {
- ID: subnetID1,
- Name: "subnetFoo",
- NetworkID: netID,
- },
- }, nil)
- },
- &ports.Port{
- ID: portID1,
+
+ // The following allows us to use gomega to
+ // compare the argument instead of gomock.
+ // Gomock's output in the case of a mismatch is
+ // not usable for this struct.
+ m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
+ gotCreateOpts := builder.(portsbinding.CreateOptsExt)
+ g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
+ return &ports.Port{ID: portID}, nil
+ })
},
- false,
+ want: &ports.Port{ID: portID},
},
{
- "fails to create port with specified portOpts if subnet query returns more than one subnet",
- "foo-port-bar",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- NameSuffix: pointer.String("foo-port-bar"),
- Description: pointer.String("this is a test port"),
- FixedIPs: []infrav1.FixedIP{{
- Subnet: &infrav1.SubnetFilter{
- FilterByNeutronTags: infrav1.FilterByNeutronTags{
- Tags: []infrav1.NeutronTag{"Foo"},
- },
- },
- IPAddress: pointer.String("192.168.0.50"),
- }},
- },
- nil,
- nil,
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- ListSubnet(subnets.ListOpts{
- Tags: "Foo",
- NetworkID: netID,
- }).Return([]subnets.Subnet{
- {
- ID: subnetID1,
- NetworkID: netID,
- Name: "subnetFoo",
- },
- {
- ID: subnetID2,
- NetworkID: netID,
- Name: "subnetBar",
- },
- }, nil)
+ name: "creates minimum port correctly",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) {
+ var expectedCreateOpts ports.CreateOptsBuilder
+ expectedCreateOpts = ports.CreateOpts{
+ NetworkID: netID,
+ Name: "test-port",
+ }
+ expectedCreateOpts = portsbinding.CreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ }
+ m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
+ gotCreateOpts := builder.(portsbinding.CreateOptsExt)
+ g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
+ return &ports.Port{ID: portID}, nil
+ })
},
- nil,
- true,
+ want: &ports.Port{ID: portID},
},
{
- "overrides default (instance) security groups if port security groups are specified",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- SecurityGroups: portSecurityGroupFilters,
- },
- instanceSecurityGroups,
- []string{},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- SecurityGroups: &securityGroupUUIDs,
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
+ name: "disable port security also ignores allowed address pairs",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{
+ DisablePortSecurity: pointer.Bool(true),
+ AllowedAddressPairs: []infrav1.AddressPair{
+ {
+ IPAddress: ipAddress1,
+ MACAddress: pointer.String(macAddress),
},
},
- ).Return(&ports.Port{ID: portID1}, nil)
+ },
},
- &ports.Port{ID: portID1},
- false,
- },
- {
- "creates port with instance tags when port tags aren't specified",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- },
- nil,
- []string{"my-instance-tag"},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
- },
- }).Return(&ports.Port{ID: portID1}, nil)
- m.ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-instance-tag"}}).Return([]string{"my-instance-tag"}, nil)
- },
- &ports.Port{ID: portID1},
- false,
- },
- {
- "creates port with port specific tags appending to instance tags",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- Tags: []string{"my-port-tag"},
- },
- nil,
- []string{"my-instance-tag"},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
- },
- }).Return(&ports.Port{ID: portID1}, nil)
- m.
- ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-instance-tag", "my-port-tag"}}).
- Return([]string{"my-instance-tag", "my-port-tag"}, nil)
- },
- &ports.Port{ID: portID1},
- false,
+ expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) {
+ var expectedCreateOpts ports.CreateOptsBuilder
+ expectedCreateOpts = ports.CreateOpts{
+ NetworkID: netID,
+ Name: "test-port",
+ }
+ expectedCreateOpts = portsecurity.PortCreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ PortSecurityEnabled: pointer.Bool(false),
+ }
+ expectedCreateOpts = portsbinding.CreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ }
+ m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
+ gotCreateOpts := builder.(portsbinding.CreateOptsExt)
+ g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
+ return &ports.Port{ID: portID}, nil
+ })
+ },
+ want: &ports.Port{ID: portID},
},
{
- "creates port and trunk (with tags) if they aren't found",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- Trunk: pointer.Bool(true),
- },
- nil,
- []string{"my-tag"},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
+ name: "disable port security explicitly false includes allowed address pairs",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{
+ DisablePortSecurity: pointer.Bool(false),
+ AllowedAddressPairs: []infrav1.AddressPair{
+ {
+ IPAddress: ipAddress1,
+ MACAddress: pointer.String(macAddress),
},
- }).Return(&ports.Port{Name: "foo-port-1", ID: portID1}, nil)
- m.
- ListTrunk(trunks.ListOpts{
- Name: "foo-port-1",
- PortID: portID1,
- }).Return([]trunks.Trunk{}, nil)
- m.
- CreateTrunk(trunks.CreateOpts{
- Name: "foo-port-1",
- PortID: portID1,
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- }).Return(&trunks.Trunk{ID: trunkID}, nil)
-
- m.ReplaceAllAttributesTags("ports", portID1, attributestags.ReplaceAllOpts{Tags: []string{"my-tag"}}).Return([]string{"my-tag"}, nil)
- m.ReplaceAllAttributesTags("trunks", trunkID, attributestags.ReplaceAllOpts{Tags: []string{"my-tag"}}).Return([]string{"my-tag"}, nil)
- },
- &ports.Port{Name: "foo-port-1", ID: portID1},
- false,
- },
- {
- "creates port with value_specs",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
+ },
},
- ValueSpecs: []infrav1.ValueSpec{
- {
- Name: "Not important",
- Key: "key",
- Value: "value",
- },
- },
- },
- nil,
- nil,
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
- ValueSpecs: &valueSpecs,
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder, g types.Gomega) {
+ var expectedCreateOpts ports.CreateOptsBuilder
+ expectedCreateOpts = ports.CreateOpts{
+ NetworkID: netID,
+ Name: "test-port",
+ AllowedAddressPairs: []ports.AddressPair{
+ {
+ IPAddress: ipAddress1,
+ MACAddress: macAddress,
},
- }).Return(&ports.Port{ID: portID1}, nil)
+ },
+ }
+ expectedCreateOpts = portsecurity.PortCreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ PortSecurityEnabled: pointer.Bool(true),
+ }
+ expectedCreateOpts = portsbinding.CreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ }
+ m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
+ gotCreateOpts := builder.(portsbinding.CreateOptsExt)
+ g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
+ return &ports.Port{ID: portID}, nil
+ })
},
- &ports.Port{ID: portID1},
- false,
+ want: &ports.Port{ID: portID},
},
{
- "creates port with propagate uplink status",
- "foo-port-1",
- infrav1.PortOpts{
- Network: &infrav1.NetworkFilter{
- ID: netID,
- },
- PropagateUplinkStatus: pointer.Bool(true),
- },
- instanceSecurityGroups,
- []string{},
- func(m *mock.MockNetworkClientMockRecorder) {
- m.
- CreatePort(portsbinding.CreateOptsExt{
- CreateOptsBuilder: ports.CreateOpts{
- Name: "foo-port-1",
- Description: "Created by cluster-api-provider-openstack cluster test-cluster",
- SecurityGroups: &instanceSecurityGroups,
- NetworkID: netID,
- AllowedAddressPairs: []ports.AddressPair{},
- PropagateUplinkStatus: pointer.Bool(true),
- },
- }).Return(&ports.Port{ID: portID1, PropagateUplinkStatus: true}, nil)
+ name: "tags and trunk",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ Tags: []string{"tag1", "tag2"},
+ Trunk: pointer.Bool(true),
},
- &ports.Port{ID: portID1, PropagateUplinkStatus: true},
- false,
+ expect: func(m *mock.MockNetworkClientMockRecorder, g types.Gomega) {
+ var expectedCreateOpts ports.CreateOptsBuilder
+ expectedCreateOpts = ports.CreateOpts{
+ NetworkID: netID,
+ Name: "test-port",
+ }
+ expectedCreateOpts = portsbinding.CreateOptsExt{
+ CreateOptsBuilder: expectedCreateOpts,
+ }
+
+ // Create the port
+ m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
+ gotCreateOpts := builder.(portsbinding.CreateOptsExt)
+ g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
+ return &ports.Port{ID: portID, Name: "test-port"}, nil
+ })
+
+ // Tag the port
+ m.ReplaceAllAttributesTags("ports", portID, attributestags.ReplaceAllOpts{
+ Tags: []string{"tag1", "tag2"},
+ })
+
+ // Look for existing trunk
+ m.ListTrunk(trunks.ListOpts{
+ PortID: portID,
+ Name: "test-port",
+ }).Return([]trunks.Trunk{}, nil)
+
+ // Create the trunk
+ m.CreateTrunk(trunks.CreateOpts{
+ PortID: portID,
+ Name: "test-port",
+ }).Return(&trunks.Trunk{ID: trunkID}, nil)
+
+ // Tag the trunk
+ m.ReplaceAllAttributesTags("trunks", trunkID, attributestags.ReplaceAllOpts{
+ Tags: []string{"tag1", "tag2"},
+ })
+ },
+ want: &ports.Port{ID: portID, Name: "test-port"},
},
}
@@ -414,17 +329,13 @@ func Test_CreatePort(t *testing.T) {
g := NewWithT(t)
mockClient := mock.NewMockNetworkClient(mockCtrl)
- tt.expect(mockClient.EXPECT())
+ tt.expect(mockClient.EXPECT(), g)
s := Service{
client: mockClient,
}
got, err := s.CreatePort(
eventObject,
- "test-cluster",
- tt.portName,
&tt.port,
- tt.instanceSecurityGroups,
- tt.tags,
)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
@@ -436,163 +347,159 @@ func Test_CreatePort(t *testing.T) {
}
}
-func TestService_normalizePorts(t *testing.T) {
+func TestService_ConstructPorts(t *testing.T) {
const (
defaultNetworkID = "3c66f3ca-2d26-4d9d-ae3b-568f54129773"
defaultSubnetID = "d8dbba89-8c39-4192-a571-e702fca35bac"
- networkID = "afa54944-1443-4132-9ef5-ce37eb4d6ab6"
- subnetID = "d786e715-c299-4a97-911d-640c10fc0392"
+ networkID = "afa54944-1443-4132-9ef5-ce37eb4d6ab6"
+ subnetID1 = "d786e715-c299-4a97-911d-640c10fc0392"
+ subnetID2 = "41ad8201-5b2f-4e0e-b29d-3d82fad6ef10"
+ securityGroupID1 = "044f6d31-3938-4f09-ad45-47b661e2ba1c"
+ securityGroupID2 = "427b77ee-40b7-4f1b-b025-72ad1a42ee51"
+
+ defaultDescription = "Created by cluster-api-provider-openstack cluster test-cluster"
)
- openStackCluster := &infrav1.OpenStackCluster{
- Status: infrav1.OpenStackClusterStatus{
- Network: &infrav1.NetworkStatusWithSubnets{
- NetworkStatus: infrav1.NetworkStatus{
- ID: defaultNetworkID,
- },
- Subnets: []infrav1.Subnet{
- {ID: defaultSubnetID},
- },
- },
- },
+ expectListExtensions := func(m *mock.MockNetworkClientMockRecorder) {
+ trunkExtension := extensions.Extension{}
+ trunkExtension.Alias = "trunk"
+ m.ListExtensions().Return([]extensions.Extension{trunkExtension}, nil)
}
tests := []struct {
- name string
- ports []infrav1.PortOpts
- instanceTrunk bool
- expectNetwork func(m *mock.MockNetworkClientMockRecorder)
- want []infrav1.PortOpts
- wantErr bool
+ name string
+ spec infrav1.OpenStackMachineSpec
+ managedSecurityGroup *string
+ expectNetwork func(m *mock.MockNetworkClientMockRecorder)
+ want []infrav1.ResolvedPortSpec
+ wantErr bool
}{
{
- name: "No ports: no ports",
- ports: []infrav1.PortOpts{},
- want: []infrav1.PortOpts{},
- },
- {
- name: "Nil network, no fixed IPs: cluster defaults",
- ports: []infrav1.PortOpts{
- {
- Network: nil,
- FixedIPs: nil,
- },
- },
- want: []infrav1.PortOpts{
+ name: "No ports creates port on default network",
+ spec: infrav1.OpenStackMachineSpec{},
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: defaultNetworkID,
- },
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- ID: defaultSubnetID,
- },
- },
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
},
- Trunk: pointer.Bool(false),
},
},
},
{
- name: "Empty network, no fixed IPs: cluster defaults",
- ports: []infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{},
- FixedIPs: nil,
+ name: "Nil network, no fixed IPs: cluster defaults",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ NameSuffix: pointer.String("custom"),
+ Network: nil,
+ FixedIPs: nil,
+ },
},
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: defaultNetworkID,
- },
- FixedIPs: []infrav1.FixedIP{
+ Name: "test-instance-custom",
+ Description: defaultDescription,
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
{
- Subnet: &infrav1.SubnetFilter{
- ID: defaultSubnetID,
- },
+ SubnetID: pointer.String(defaultSubnetID),
},
},
- Trunk: pointer.Bool(false),
+ Tags: []string{"test-tag"},
},
},
},
{
name: "Port inherits trunk from instance",
- ports: []infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{},
- FixedIPs: nil,
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ NameSuffix: pointer.String("custom"),
+ Network: nil,
+ FixedIPs: nil,
+ },
},
+ Trunk: true,
+ },
+ expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
+ expectListExtensions(m)
},
- instanceTrunk: true,
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: defaultNetworkID,
- },
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- ID: defaultSubnetID,
- },
- },
+ Name: "test-instance-custom",
+ Description: defaultDescription,
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
},
+ Tags: []string{"test-tag"},
Trunk: pointer.Bool(true),
},
},
+ wantErr: false,
},
{
name: "Port overrides trunk from instance",
- ports: []infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{},
- FixedIPs: nil,
- Trunk: pointer.Bool(true),
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ Trunk: pointer.Bool(true),
+ },
},
+ Trunk: false,
},
- want: []infrav1.PortOpts{
+ expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
+ expectListExtensions(m)
+ },
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: defaultNetworkID,
- },
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- ID: defaultSubnetID,
- },
- },
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
},
+ Tags: []string{"test-tag"},
Trunk: pointer.Bool(true),
},
},
},
{
- name: "Network defined by ID: unchanged",
- ports: []infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{
- ID: networkID,
+ name: "Network defined by ID: no lookup",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ Network: &infrav1.NetworkFilter{
+ ID: networkID,
+ },
},
},
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: networkID,
- },
- Trunk: pointer.Bool(false),
+ NetworkID: networkID,
+
+ // Defaults
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
},
},
},
{
name: "Network defined by filter: add ID from network lookup",
- ports: []infrav1.PortOpts{
- {
- Network: &infrav1.NetworkFilter{
- Name: "test-network",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ Network: &infrav1.NetworkFilter{
+ Name: "test-network",
+ },
},
},
},
@@ -601,56 +508,59 @@ func TestService_normalizePorts(t *testing.T) {
{ID: networkID},
}, nil)
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: networkID,
- Name: "test-network",
- },
- Trunk: pointer.Bool(false),
+ NetworkID: networkID,
+
+ // Defaults
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
},
},
},
{
name: "No network, fixed IP has subnet by ID: add ID from subnet",
- ports: []infrav1.PortOpts{
- {
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- ID: subnetID,
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ FixedIPs: []infrav1.FixedIP{
+ {
+ Subnet: &infrav1.SubnetFilter{
+ ID: subnetID1,
+ },
},
},
},
},
},
expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
- m.GetSubnet(subnetID).Return(&subnets.Subnet{ID: subnetID, NetworkID: networkID}, nil)
+ m.GetSubnet(subnetID1).Return(&subnets.Subnet{ID: subnetID1, NetworkID: networkID}, nil)
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: networkID,
+ NetworkID: networkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(subnetID1)},
},
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- ID: subnetID,
- },
- },
- },
- Trunk: pointer.Bool(false),
+
+ // Defaults
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
},
},
},
{
name: "No network, fixed IP has subnet by filter: add ID from subnet",
- ports: []infrav1.PortOpts{
- {
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ FixedIPs: []infrav1.FixedIP{
+ {
+ Subnet: &infrav1.SubnetFilter{
+ Name: "test-subnet",
+ },
},
},
},
@@ -658,34 +568,35 @@ func TestService_normalizePorts(t *testing.T) {
},
expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
m.ListSubnet(subnets.ListOpts{Name: "test-subnet"}).Return([]subnets.Subnet{
- {ID: subnetID, NetworkID: networkID},
+ {ID: subnetID1, NetworkID: networkID},
}, nil)
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: networkID,
- },
- FixedIPs: []infrav1.FixedIP{
+ NetworkID: networkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
{
- Subnet: &infrav1.SubnetFilter{
- ID: subnetID,
- Name: "test-subnet",
- },
+ SubnetID: pointer.String(subnetID1),
},
},
- Trunk: pointer.Bool(false),
+
+ // Defaults
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
},
},
},
{
name: "No network, fixed IP subnet returns no matches: error",
- ports: []infrav1.PortOpts{
- {
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ FixedIPs: []infrav1.FixedIP{
+ {
+ Subnet: &infrav1.SubnetFilter{
+ Name: "test-subnet",
+ },
},
},
},
@@ -698,12 +609,14 @@ func TestService_normalizePorts(t *testing.T) {
},
{
name: "No network, only fixed IP subnet returns multiple matches: error",
- ports: []infrav1.PortOpts{
- {
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ FixedIPs: []infrav1.FixedIP{
+ {
+ Subnet: &infrav1.SubnetFilter{
+ Name: "test-subnet",
+ },
},
},
},
@@ -711,7 +624,7 @@ func TestService_normalizePorts(t *testing.T) {
},
expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
m.ListSubnet(subnets.ListOpts{Name: "test-subnet"}).Return([]subnets.Subnet{
- {ID: subnetID, NetworkID: networkID},
+ {ID: subnetID1, NetworkID: networkID},
{ID: "8008494c-301e-4e5c-951b-a8ab568447fd", NetworkID: "5d48bfda-db28-42ee-8374-50e13d1fe5ea"},
}, nil)
},
@@ -719,17 +632,19 @@ func TestService_normalizePorts(t *testing.T) {
},
{
name: "No network, first fixed IP subnet returns multiple matches: used ID from second fixed IP",
- ports: []infrav1.PortOpts{
- {
- FixedIPs: []infrav1.FixedIP{
- {
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet1",
+ spec: infrav1.OpenStackMachineSpec{
+ Ports: []infrav1.PortOpts{
+ {
+ FixedIPs: []infrav1.FixedIP{
+ {
+ Subnet: &infrav1.SubnetFilter{
+ Name: "test-subnet1",
+ },
},
- },
- {
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet2",
+ {
+ Subnet: &infrav1.SubnetFilter{
+ Name: "test-subnet2",
+ },
},
},
},
@@ -737,43 +652,143 @@ func TestService_normalizePorts(t *testing.T) {
},
expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
m.ListSubnet(subnets.ListOpts{Name: "test-subnet1"}).Return([]subnets.Subnet{
- {ID: subnetID, NetworkID: networkID},
+ {ID: subnetID1, NetworkID: networkID},
{ID: "8008494c-301e-4e5c-951b-a8ab568447fd", NetworkID: "5d48bfda-db28-42ee-8374-50e13d1fe5ea"},
}, nil)
m.ListSubnet(subnets.ListOpts{Name: "test-subnet2"}).Return([]subnets.Subnet{
- {ID: subnetID, NetworkID: networkID},
+ {ID: subnetID2, NetworkID: networkID},
+ }, nil)
+ // Fetch the first subnet again, this time with network ID from the second subnet
+ m.ListSubnet(subnets.ListOpts{NetworkID: networkID, Name: "test-subnet1"}).Return([]subnets.Subnet{
+ {ID: subnetID1, NetworkID: networkID},
}, nil)
},
- want: []infrav1.PortOpts{
+ want: []infrav1.ResolvedPortSpec{
{
- Network: &infrav1.NetworkFilter{
- ID: networkID,
- },
- FixedIPs: []infrav1.FixedIP{
+ NetworkID: networkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
{
- Subnet: &infrav1.SubnetFilter{
- Name: "test-subnet1",
- },
+ SubnetID: pointer.String(subnetID1),
},
{
- Subnet: &infrav1.SubnetFilter{
- ID: subnetID,
- Name: "test-subnet2",
- },
+ SubnetID: pointer.String(subnetID2),
},
},
- Trunk: pointer.Bool(false),
+
+ // Defaults
+ Name: "test-instance-0",
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ },
+ },
+ },
+ {
+ name: "machine spec security groups added to defaults",
+ spec: infrav1.OpenStackMachineSpec{
+ SecurityGroups: []infrav1.SecurityGroupFilter{
+ {Name: "test-security-group"},
+ },
+ },
+ expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
+ m.ListSecGroup(groups.ListOpts{Name: "test-security-group"}).Return([]groups.SecGroup{
+ {ID: securityGroupID1},
+ }, nil)
+ },
+ want: []infrav1.ResolvedPortSpec{
+ {
+ Name: "test-instance-0",
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
+ },
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ SecurityGroups: []string{securityGroupID1},
+ },
+ },
+ },
+ {
+ name: "port security groups override machine spec security groups",
+ spec: infrav1.OpenStackMachineSpec{
+ SecurityGroups: []infrav1.SecurityGroupFilter{
+ {Name: "machine-security-group"},
+ },
+ Ports: []infrav1.PortOpts{
+ {SecurityGroups: []infrav1.SecurityGroupFilter{{Name: "port-security-group"}}},
+ },
+ },
+ expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
+ m.ListSecGroup(groups.ListOpts{Name: "machine-security-group"}).Return([]groups.SecGroup{
+ {ID: securityGroupID1},
+ }, nil)
+ m.ListSecGroup(groups.ListOpts{Name: "port-security-group"}).Return([]groups.SecGroup{
+ {ID: securityGroupID2},
+ }, nil)
+ },
+ want: []infrav1.ResolvedPortSpec{
+ {
+ Name: "test-instance-0",
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
+ },
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ SecurityGroups: []string{securityGroupID2},
+ },
+ },
+ },
+ {
+ name: "managed security group added to port",
+ spec: infrav1.OpenStackMachineSpec{},
+ managedSecurityGroup: pointer.String(securityGroupID1),
+ want: []infrav1.ResolvedPortSpec{
+ {
+ Name: "test-instance-0",
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
+ },
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ SecurityGroups: []string{securityGroupID1},
+ },
+ },
+ },
+ {
+ name: "managed security group and machine security groups added to port",
+ spec: infrav1.OpenStackMachineSpec{
+ SecurityGroups: []infrav1.SecurityGroupFilter{{Name: "machine-security-group"}},
+ },
+ managedSecurityGroup: pointer.String(securityGroupID1),
+ expectNetwork: func(m *mock.MockNetworkClientMockRecorder) {
+ m.ListSecGroup(groups.ListOpts{Name: "machine-security-group"}).Return([]groups.SecGroup{
+ {ID: securityGroupID2},
+ }, nil)
+ },
+ want: []infrav1.ResolvedPortSpec{
+ {
+ Name: "test-instance-0",
+ NetworkID: defaultNetworkID,
+ FixedIPs: []infrav1.ResolvedFixedIP{
+ {SubnetID: pointer.String(defaultSubnetID)},
+ },
+ Description: defaultDescription,
+ Tags: []string{"test-tag"},
+ SecurityGroups: []string{securityGroupID2, securityGroupID1},
},
},
},
}
- for _, tt := range tests {
+ for i := range tests {
+ tt := &tests[i]
t.Run(tt.name, func(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+
g := NewWithT(t)
log := testr.New(t)
- mockCtrl := gomock.NewController(t)
- defer mockCtrl.Finish()
mockClient := mock.NewMockNetworkClient(mockCtrl)
if tt.expectNetwork != nil {
tt.expectNetwork(mockClient.EXPECT())
@@ -784,7 +799,19 @@ func TestService_normalizePorts(t *testing.T) {
scope: scope.NewWithLogger(mockScopeFactory, log),
}
- got, err := s.normalizePorts(tt.ports, openStackCluster, tt.instanceTrunk)
+ defaultNetwork := &infrav1.NetworkStatusWithSubnets{
+ NetworkStatus: infrav1.NetworkStatus{
+ ID: defaultNetworkID,
+ },
+ Subnets: []infrav1.Subnet{
+ {ID: defaultSubnetID},
+ },
+ }
+
+ clusterResourceName := "test-cluster"
+ baseName := "test-instance"
+ baseTags := []string{"test-tag"}
+ got, err := s.ConstructPorts(&tt.spec, clusterResourceName, baseName, defaultNetwork, tt.managedSecurityGroup, baseTags)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
@@ -797,40 +824,52 @@ func TestService_normalizePorts(t *testing.T) {
}
func Test_getPortName(t *testing.T) {
- type args struct {
+ tests := []struct {
+ name string
instanceName string
- opts *infrav1.PortOpts
+ spec *infrav1.PortOpts
netIndex int
- }
- tests := []struct {
- name string
- args args
- want string
+ want string
}{
{
- name: "with nil PortOpts",
- args: args{"test-1-instance", nil, 2},
- want: "test-1-instance-2",
+ name: "with nil PortOpts",
+ instanceName: "test-1-instance",
+ netIndex: 2,
+ want: "test-1-instance-2",
},
{
- name: "with PortOpts name suffix",
- args: args{"test-1-instance", &infrav1.PortOpts{NameSuffix: pointer.String("foo")}, 4},
- want: "test-1-instance-foo",
+ name: "with PortOpts name suffix",
+ instanceName: "test-1-instance",
+ spec: &infrav1.PortOpts{
+ NameSuffix: pointer.String("foo"),
+ },
+ netIndex: 4,
+ want: "test-1-instance-foo",
},
{
- name: "without PortOpts name suffix",
- args: args{"test-1-instance", &infrav1.PortOpts{}, 4},
- want: "test-1-instance-4",
+ name: "without PortOpts name suffix",
+ instanceName: "test-1-instance",
+ spec: &infrav1.PortOpts{},
+ netIndex: 4,
+ want: "test-1-instance-4",
},
{
- name: "with PortOpts name suffix",
- args: args{"test-1-instance", &infrav1.PortOpts{NameSuffix: pointer.String("foo2"), Network: &infrav1.NetworkFilter{ID: "bar"}, DisablePortSecurity: pointer.Bool(true)}, 4},
- want: "test-1-instance-foo2",
+ name: "with PortOpts name suffix",
+ instanceName: "test-1-instance",
+ spec: &infrav1.PortOpts{
+ NameSuffix: pointer.String("foo2"),
+ Network: &infrav1.NetworkFilter{ID: "bar"},
+ ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{
+ DisablePortSecurity: pointer.Bool(true),
+ },
+ },
+ netIndex: 4,
+ want: "test-1-instance-foo2",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := GetPortName(tt.args.instanceName, tt.args.opts, tt.args.netIndex); got != tt.want {
+ if got := getPortName(tt.instanceName, tt.spec, tt.netIndex); got != tt.want {
t.Errorf("getPortName() = %v, want %v", got, tt.want)
}
})
@@ -848,7 +887,7 @@ func Test_AdoptPorts(t *testing.T) {
tests := []struct {
testName string
- desiredPorts []infrav1.PortOpts
+ desiredPorts []infrav1.ResolvedPortSpec
dependentResources infrav1.DependentMachineResources
expect func(*mock.MockNetworkClientMockRecorder)
want infrav1.DependentMachineResources
@@ -859,8 +898,8 @@ func Test_AdoptPorts(t *testing.T) {
},
{
testName: "desired port already in status: no-op",
- desiredPorts: []infrav1.PortOpts{
- {Network: &infrav1.NetworkFilter{ID: networkID1}},
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {NetworkID: networkID1},
},
dependentResources: infrav1.DependentMachineResources{
Ports: []infrav1.PortStatus{
@@ -879,8 +918,8 @@ func Test_AdoptPorts(t *testing.T) {
},
{
testName: "desired port not in status, exists: adopt",
- desiredPorts: []infrav1.PortOpts{
- {Network: &infrav1.NetworkFilter{ID: networkID1}},
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {Name: "test-machine-0", NetworkID: networkID1},
},
expect: func(m *mock.MockNetworkClientMockRecorder) {
m.ListPort(ports.ListOpts{Name: "test-machine-0", NetworkID: networkID1}).
@@ -896,8 +935,8 @@ func Test_AdoptPorts(t *testing.T) {
},
{
testName: "desired port not in status, does not exist: ignore",
- desiredPorts: []infrav1.PortOpts{
- {Network: &infrav1.NetworkFilter{ID: networkID1}},
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {Name: "test-machine-0", NetworkID: networkID1},
},
expect: func(m *mock.MockNetworkClientMockRecorder) {
m.ListPort(ports.ListOpts{Name: "test-machine-0", NetworkID: networkID1}).
@@ -907,9 +946,9 @@ func Test_AdoptPorts(t *testing.T) {
},
{
testName: "2 desired ports, first in status, second exists: adopt second",
- desiredPorts: []infrav1.PortOpts{
- {Network: &infrav1.NetworkFilter{ID: networkID1}},
- {Network: &infrav1.NetworkFilter{ID: networkID2}},
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {Name: "test-machine-0", NetworkID: networkID1},
+ {Name: "test-machine-1", NetworkID: networkID2},
},
dependentResources: infrav1.DependentMachineResources{
Ports: []infrav1.PortStatus{
@@ -931,10 +970,10 @@ func Test_AdoptPorts(t *testing.T) {
},
{
testName: "3 desired ports, first in status, second does not exist: ignore, do no look for third",
- desiredPorts: []infrav1.PortOpts{
- {Network: &infrav1.NetworkFilter{ID: networkID1}},
- {Network: &infrav1.NetworkFilter{ID: networkID2}},
- {Network: &infrav1.NetworkFilter{ID: networkID3}},
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {Name: "test-machine-0", NetworkID: networkID1},
+ {Name: "test-machine-1", NetworkID: networkID2},
+ {Name: "test-machine-2", NetworkID: networkID3},
},
dependentResources: infrav1.DependentMachineResources{
Ports: []infrav1.PortStatus{
@@ -953,6 +992,30 @@ func Test_AdoptPorts(t *testing.T) {
},
},
},
+ {
+ testName: "3 desired ports with arbitrary names, first in status, second does not exist: ignore, do no look for third",
+ desiredPorts: []infrav1.ResolvedPortSpec{
+ {Name: "test-machine-foo", NetworkID: networkID1},
+ {Name: "test-machine-bar", NetworkID: networkID2},
+ {Name: "test-machine-baz", NetworkID: networkID3},
+ },
+ dependentResources: infrav1.DependentMachineResources{
+ Ports: []infrav1.PortStatus{
+ {
+ ID: portID1,
+ },
+ },
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder) {
+ m.ListPort(ports.ListOpts{Name: "test-machine-bar", NetworkID: networkID2}).
+ Return(nil, nil)
+ },
+ want: infrav1.DependentMachineResources{
+ Ports: []infrav1.PortStatus{
+ {ID: portID1},
+ },
+ },
+ },
}
for i := range tests {
tt := &tests[i]
@@ -972,7 +1035,6 @@ func Test_AdoptPorts(t *testing.T) {
}
err := s.AdoptPorts(scope.NewWithLogger(mockScopeFactory, log),
- "test-machine",
tt.desiredPorts, &tt.dependentResources)
if tt.wantErr {
g.Expect(err).Error()
diff --git a/pkg/cloud/services/networking/router.go b/pkg/cloud/services/networking/router.go
index 749d5c3be9..e6267fdb30 100644
--- a/pkg/cloud/services/networking/router.go
+++ b/pkg/cloud/services/networking/router.go
@@ -31,7 +31,7 @@ import (
"sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names"
)
-func (s *Service) ReconcileRouter(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
+func (s *Service) ReconcileRouter(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
if openStackCluster.Status.Network == nil || openStackCluster.Status.Network.ID == "" {
s.scope.Logger().V(3).Info("No need to reconcile router since no network exists")
return nil
@@ -45,8 +45,8 @@ func (s *Service) ReconcileRouter(openStackCluster *infrav1.OpenStackCluster, cl
return nil
}
- s.scope.Logger().Info("Reconciling router", "cluster", clusterName)
- routerName := getRouterName(clusterName)
+ s.scope.Logger().Info("Reconciling router", "cluster", clusterResourceName)
+ routerName := getRouterName(clusterResourceName)
routerListOpts := routers.ListOpts{Name: routerName}
existingRouter := false
if openStackCluster.Spec.Router != nil {
@@ -65,7 +65,7 @@ func (s *Service) ReconcileRouter(openStackCluster *infrav1.OpenStackCluster, cl
if router.ID == "" {
var err error
- createdRouter, err := s.createRouter(openStackCluster, clusterName, routerName)
+ createdRouter, err := s.createRouter(openStackCluster, clusterResourceName, routerName)
if err != nil {
return err
}
@@ -125,9 +125,9 @@ func (s *Service) ReconcileRouter(openStackCluster *infrav1.OpenStackCluster, cl
return nil
}
-func (s *Service) createRouter(openStackCluster *infrav1.OpenStackCluster, clusterName, name string) (*routers.Router, error) {
+func (s *Service) createRouter(openStackCluster *infrav1.OpenStackCluster, clusterResourceName, name string) (*routers.Router, error) {
opts := routers.CreateOpts{
- Description: names.GetDescription(clusterName),
+ Description: names.GetDescription(clusterResourceName),
Name: name,
}
// only set the GatewayInfo right now when no externalIPs
@@ -192,8 +192,8 @@ func (s *Service) setRouterExternalIPs(openStackCluster *infrav1.OpenStackCluste
return nil
}
-func (s *Service) DeleteRouter(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
- routerName := getRouterName(clusterName)
+func (s *Service) DeleteRouter(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
+ routerName := getRouterName(clusterResourceName)
listOpts := routers.ListOpts{Name: routerName}
existingRouter := false
if openStackCluster.Spec.Router != nil {
@@ -206,7 +206,7 @@ func (s *Service) DeleteRouter(openStackCluster *infrav1.OpenStackCluster, clust
return err
}
- subnetName := getSubnetName(clusterName)
+ subnetName := getSubnetName(clusterResourceName)
subnet, err := s.getSubnetByName(subnetName)
if err != nil {
return err
@@ -285,6 +285,6 @@ func (s *Service) getSubnetByName(subnetName string) (subnets.Subnet, error) {
return subnets.Subnet{}, fmt.Errorf("found %d subnets with the name %s, which should not happen", len(subnetList), subnetName)
}
-func getRouterName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName)
+func getRouterName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterResourceName)
}
diff --git a/pkg/cloud/services/networking/securitygroups.go b/pkg/cloud/services/networking/securitygroups.go
index 76cc8e349e..ecde85fe84 100644
--- a/pkg/cloud/services/networking/securitygroups.go
+++ b/pkg/cloud/services/networking/securitygroups.go
@@ -39,7 +39,7 @@ const (
)
// ReconcileSecurityGroups reconcile the security groups.
-func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
+func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
s.scope.Logger().Info("Reconciling security groups")
if openStackCluster.Spec.ManagedSecurityGroups == nil {
s.scope.Logger().V(4).Info("No need to reconcile security groups")
@@ -48,15 +48,15 @@ func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackClu
bastionEnabled := openStackCluster.Spec.Bastion != nil && openStackCluster.Spec.Bastion.Enabled
- secControlPlaneGroupName := getSecControlPlaneGroupName(clusterName)
- secWorkerGroupName := getSecWorkerGroupName(clusterName)
+ secControlPlaneGroupName := getSecControlPlaneGroupName(clusterResourceName)
+ secWorkerGroupName := getSecWorkerGroupName(clusterResourceName)
suffixToNameMap := map[string]string{
controlPlaneSuffix: secControlPlaneGroupName,
workerSuffix: secWorkerGroupName,
}
if bastionEnabled {
- secBastionGroupName := getSecBastionGroupName(clusterName)
+ secBastionGroupName := getSecBastionGroupName(clusterResourceName)
suffixToNameMap[bastionSuffix] = secBastionGroupName
}
@@ -345,14 +345,14 @@ func (s *Service) GetSecurityGroups(securityGroupParams []infrav1.SecurityGroupF
return sgIDs, nil
}
-func (s *Service) DeleteSecurityGroups(openStackCluster *infrav1.OpenStackCluster, clusterName string) error {
+func (s *Service) DeleteSecurityGroups(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error {
secGroupNames := []string{
- getSecControlPlaneGroupName(clusterName),
- getSecWorkerGroupName(clusterName),
+ getSecControlPlaneGroupName(clusterResourceName),
+ getSecWorkerGroupName(clusterResourceName),
}
if openStackCluster.Spec.Bastion != nil && openStackCluster.Spec.Bastion.Enabled {
- secGroupNames = append(secGroupNames, getSecBastionGroupName(clusterName))
+ secGroupNames = append(secGroupNames, getSecBastionGroupName(clusterResourceName))
}
for _, secGroupName := range secGroupNames {
@@ -523,16 +523,16 @@ func (s *Service) createRule(securityGroupID string, r resolvedSecurityGroupRule
return nil
}
-func getSecControlPlaneGroupName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, controlPlaneSuffix)
+func getSecControlPlaneGroupName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterResourceName, controlPlaneSuffix)
}
-func getSecWorkerGroupName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, workerSuffix)
+func getSecWorkerGroupName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterResourceName, workerSuffix)
}
-func getSecBastionGroupName(clusterName string) string {
- return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, bastionSuffix)
+func getSecBastionGroupName(clusterResourceName string) string {
+ return fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterResourceName, bastionSuffix)
}
func convertOSSecGroupToConfigSecGroup(osSecGroup *groups.SecGroup) *infrav1.SecurityGroupStatus {
diff --git a/pkg/cloud/services/networking/securitygroups_test.go b/pkg/cloud/services/networking/securitygroups_test.go
index 04d4ff870d..f40e4a5d75 100644
--- a/pkg/cloud/services/networking/securitygroups_test.go
+++ b/pkg/cloud/services/networking/securitygroups_test.go
@@ -480,7 +480,7 @@ func TestReconcileGroupRules(t *testing.T) {
func TestService_ReconcileSecurityGroups(t *testing.T) {
const (
- clusterName = "test-cluster"
+ clusterResourceName = "test-cluster"
controlPlaneSGName = "k8s-cluster-test-cluster-secgroup-controlplane"
workerSGName = "k8s-cluster-test-cluster-secgroup-worker"
@@ -590,7 +590,7 @@ func TestService_ReconcileSecurityGroups(t *testing.T) {
openStackCluster := &infrav1.OpenStackCluster{
Spec: tt.openStackClusterSpec,
}
- err := s.ReconcileSecurityGroups(openStackCluster, clusterName)
+ err := s.ReconcileSecurityGroups(openStackCluster, clusterResourceName)
if tt.wantErr {
g.Expect(err).ToNot(BeNil(), "ReconcileSecurityGroups")
} else {
diff --git a/pkg/cloud/services/networking/trunk.go b/pkg/cloud/services/networking/trunk.go
index 6743478ffd..12ef3f4462 100644
--- a/pkg/cloud/services/networking/trunk.go
+++ b/pkg/cloud/services/networking/trunk.go
@@ -22,12 +22,12 @@ import (
"time"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/record"
capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors"
- "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names"
)
const (
@@ -49,10 +49,10 @@ func (s *Service) GetTrunkSupport() (bool, error) {
return false, nil
}
-func (s *Service) getOrCreateTrunk(eventObject runtime.Object, clusterName, trunkName, portID string) (*trunks.Trunk, error) {
+func (s *Service) getOrCreateTrunkForPort(eventObject runtime.Object, port *ports.Port) (*trunks.Trunk, error) {
trunkList, err := s.client.ListTrunk(trunks.ListOpts{
- Name: trunkName,
- PortID: portID,
+ Name: port.Name,
+ PortID: port.ID,
})
if err != nil {
return nil, fmt.Errorf("searching for existing trunk for server: %v", err)
@@ -63,9 +63,9 @@ func (s *Service) getOrCreateTrunk(eventObject runtime.Object, clusterName, trun
}
trunkCreateOpts := trunks.CreateOpts{
- Name: trunkName,
- PortID: portID,
- Description: names.GetDescription(clusterName),
+ Name: port.Name,
+ PortID: port.ID,
+ Description: port.Description,
}
trunk, err := s.client.CreateTrunk(trunkCreateOpts)
diff --git a/pkg/cloud/services/networking/trunk_test.go b/pkg/cloud/services/networking/trunk_test.go
index a75519d8a8..079f5e45f9 100644
--- a/pkg/cloud/services/networking/trunk_test.go
+++ b/pkg/cloud/services/networking/trunk_test.go
@@ -21,6 +21,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
. "github.com/onsi/gomega"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
@@ -31,54 +32,63 @@ func Test_GetOrCreateTrunk(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
+ const portID = "021e5dbe-a27b-4824-839e-239d5a027c7f"
+
tests := []struct {
- name string
- trunkName string
- portID string
- expect func(m *mock.MockNetworkClientMockRecorder)
+ name string
+ port *ports.Port
+ expect func(m *mock.MockNetworkClientMockRecorder)
// Note the 'wanted' port isn't so important, since it will be whatever we tell ListPort or CreatePort to return.
// Mostly in this test suite, we're checking that ListPort/CreatePort is called with the expected port opts.
want *trunks.Trunk
wantErr bool
}{
{
- "return trunk if found",
- "trunk-1",
- "port-1",
- func(m *mock.MockNetworkClientMockRecorder) {
+ name: "return trunk if found",
+ port: &ports.Port{
+ ID: portID,
+ Name: "trunk-1",
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder) {
m.
ListTrunk(trunks.ListOpts{
Name: "trunk-1",
- PortID: "port-1",
+ PortID: portID,
}).Return([]trunks.Trunk{{
Name: "trunk-1",
- ID: "port-1",
+ ID: portID,
}}, nil)
},
- &trunks.Trunk{Name: "trunk-1", ID: "port-1"},
- false,
+ want: &trunks.Trunk{
+ Name: "trunk-1",
+ ID: portID,
+ },
+ wantErr: false,
},
{
- "creates trunk if not found",
- "trunk-1",
- "port-1",
- func(m *mock.MockNetworkClientMockRecorder) {
+ name: "creates trunk if not found",
+ port: &ports.Port{
+ ID: portID,
+ Name: "trunk-1",
+ Description: "Created by cluster-api-provider-openstack cluster test-cluster",
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder) {
// No ports found
m.
ListTrunk(trunks.ListOpts{
Name: "trunk-1",
- PortID: "port-1",
+ PortID: portID,
}).Return([]trunks.Trunk{}, nil)
m.
CreateTrunk(trunks.CreateOpts{
Name: "trunk-1",
- PortID: "port-1",
+ PortID: portID,
Description: "Created by cluster-api-provider-openstack cluster test-cluster",
},
).Return(&trunks.Trunk{Name: "trunk-1", ID: "port-1"}, nil)
},
- &trunks.Trunk{Name: "trunk-1", ID: "port-1"},
- false,
+ want: &trunks.Trunk{Name: "trunk-1", ID: "port-1"},
+ wantErr: false,
},
}
@@ -91,12 +101,7 @@ func Test_GetOrCreateTrunk(t *testing.T) {
s := Service{
client: mockClient,
}
- got, err := s.getOrCreateTrunk(
- eventObject,
- "test-cluster",
- tt.trunkName,
- tt.portID,
- )
+ got, err := s.getOrCreateTrunkForPort(eventObject, tt.port)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
} else {
diff --git a/pkg/utils/names/names.go b/pkg/utils/names/names.go
index 95c1bd455c..5f2ae23045 100644
--- a/pkg/utils/names/names.go
+++ b/pkg/utils/names/names.go
@@ -19,14 +19,16 @@ package names
import (
"fmt"
"strings"
+
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)
const (
FloatingAddressIPClaimNameSuffix = "floating-ip-address"
)
-func GetDescription(clusterName string) string {
- return fmt.Sprintf("Created by cluster-api-provider-openstack cluster %s", clusterName)
+func GetDescription(clusterResourceName string) string {
+ return fmt.Sprintf("Created by cluster-api-provider-openstack cluster %s", clusterResourceName)
}
func GetFloatingAddressClaimName(openStackMachineName string) string {
@@ -36,3 +38,9 @@ func GetFloatingAddressClaimName(openStackMachineName string) string {
func GetOpenStackMachineNameFromClaimName(claimName string) string {
return strings.TrimSuffix(claimName, fmt.Sprintf("-%s", FloatingAddressIPClaimNameSuffix))
}
+
+// ClusterResourceName returns a string which is used as the base of all
+// OpenStack resources created for the cluster.
+func ClusterResourceName(cluster *clusterv1.Cluster) string {
+ return fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name)
+}
diff --git a/test/e2e/suites/e2e/e2e_test.go b/test/e2e/suites/e2e/e2e_test.go
index c8fdc0aea6..ff0cb9c7ba 100644
--- a/test/e2e/suites/e2e/e2e_test.go
+++ b/test/e2e/suites/e2e/e2e_test.go
@@ -123,11 +123,10 @@ var _ = Describe("e2e tests [PR-Blocking]", func() {
openStackCluster, err := shared.ClusterForSpec(ctx, e2eCtx, namespace)
Expect(err).NotTo(HaveOccurred())
- // Tag: clusterName is declared on OpenStackCluster and gets propagated to all machines
- // except the bastion host
+ // Tag: clusterName is declared on OpenStackCluster and gets propagated to all machines, including the bastion.
allServers, err := shared.DumpOpenStackServers(e2eCtx, servers.ListOpts{Tags: clusterName})
Expect(err).NotTo(HaveOccurred())
- Expect(allServers).To(HaveLen(2))
+ Expect(allServers).To(HaveLen(3))
// When listing servers with multiple tags, nova api requires a single, comma-separated string
// with all the tags