diff --git a/api/v1alpha7/conversion_test.go b/api/v1alpha7/conversion_test.go
index 743a3d6599..00ee842743 100644
--- a/api/v1alpha7/conversion_test.go
+++ b/api/v1alpha7/conversion_test.go
@@ -17,6 +17,7 @@ limitations under the License.
package v1alpha7
import (
+ "slices"
"testing"
"github.com/google/go-cmp/cmp"
@@ -31,7 +32,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/conversion"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
- "sigs.k8s.io/cluster-api-provider-openstack/internal/futures"
testhelpers "sigs.k8s.io/cluster-api-provider-openstack/test/helpers"
)
@@ -98,7 +98,7 @@ func TestFuzzyConversion(t *testing.T) {
},
}
- return futures.SlicesConcat(v1alpha7FuzzerFuncs, testhelpers.InfraV1FuzzerFuncs())
+ return slices.Concat(v1alpha7FuzzerFuncs, testhelpers.InfraV1FuzzerFuncs())
}
t.Run("for OpenStackCluster", runParallel(utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
diff --git a/api/v1beta1/openstackmachine_types.go b/api/v1beta1/openstackmachine_types.go
index 00025db7b6..7d271e78b5 100644
--- a/api/v1beta1/openstackmachine_types.go
+++ b/api/v1beta1/openstackmachine_types.go
@@ -199,6 +199,8 @@ type OpenStackMachineStatus struct {
Addresses []corev1.NodeAddress `json:"addresses,omitempty"`
// InstanceState is the state of the OpenStack instance for this machine.
+ // This field is not set anymore by the OpenStackMachine controller.
+ // Instead, it's set by the OpenStackServer controller.
// +optional
InstanceState *InstanceState `json:"instanceState,omitempty"`
@@ -241,7 +243,6 @@ type OpenStackMachineStatus struct {
// +kubebuilder:resource:path=openstackmachines,scope=Namespaced,categories=cluster-api,shortName=osm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this OpenStackMachine belongs"
-// +kubebuilder:printcolumn:name="InstanceState",type="string",JSONPath=".status.instanceState",description="OpenStack instance state"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Machine ready status"
// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="OpenStack instance ID"
// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object which owns with this OpenStackMachine"
diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go
index 74c0415d8e..4a577852c7 100644
--- a/cmd/models-schema/zz_generated.openapi.go
+++ b/cmd/models-schema/zz_generated.openapi.go
@@ -21537,7 +21537,7 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackMachin
},
"instanceState": {
SchemaProps: spec.SchemaProps{
- Description: "InstanceState is the state of the OpenStack instance for this machine.",
+ Description: "InstanceState is the state of the OpenStack instance for this machine. This field is not set anymore by the OpenStackMachine controller. Instead, it's set by the OpenStackServer controller.",
Type: []string{"string"},
Format: "",
},
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 f8cb20e56a..96975431f7 100644
--- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml
+++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml
@@ -547,10 +547,6 @@ spec:
jsonPath: .metadata.labels.cluster\.x-k8s\.io/cluster-name
name: Cluster
type: string
- - description: OpenStack instance state
- jsonPath: .status.instanceState
- name: InstanceState
- type: string
- description: Machine ready status
jsonPath: .status.ready
name: Ready
@@ -1555,8 +1551,10 @@ spec:
description: InstanceID is the OpenStack instance ID for this machine.
type: string
instanceState:
- description: InstanceState is the state of the OpenStack instance
- for this machine.
+ description: |-
+ InstanceState is the state of the OpenStack instance for this machine.
+ This field is not set anymore by the OpenStackMachine controller.
+ Instead, it's set by the OpenStackServer controller.
type: string
ready:
description: Ready is true when the provider resource is ready.
diff --git a/controllers/openstackserver_controller.go b/controllers/openstackserver_controller.go
index 89b3d5977a..f5061462fe 100644
--- a/controllers/openstackserver_controller.go
+++ b/controllers/openstackserver_controller.go
@@ -419,11 +419,7 @@ func getOrCreateServerPorts(openStackServer *infrav1alpha1.OpenStackServer, netw
}
desiredPorts := resolved.Ports
- if len(desiredPorts) == len(resources.Ports) {
- return nil
- }
-
- if err := networkingService.CreatePorts(openStackServer, desiredPorts, resources); err != nil {
+ if err := networkingService.EnsurePorts(openStackServer, desiredPorts, resources); err != nil {
return fmt.Errorf("creating ports: %w", err)
}
diff --git a/controllers/openstackserver_controller_test.go b/controllers/openstackserver_controller_test.go
index ce0eb8e34d..deefefc1ae 100644
--- a/controllers/openstackserver_controller_test.go
+++ b/controllers/openstackserver_controller_test.go
@@ -114,6 +114,18 @@ var listDefaultPorts = func(r *recorders) {
}, nil)
}
+var listDefaultPortsWithID = func(r *recorders) {
+ r.network.ListPort(ports.ListOpts{
+ Name: openStackServerName + "-0",
+ ID: portUUID,
+ NetworkID: networkUUID,
+ }).Return([]ports.Port{
+ {
+ ID: portUUID,
+ },
+ }, nil)
+}
+
var listDefaultPortsNotFound = func(r *recorders) {
r.network.ListPort(ports.ListOpts{
Name: openStackServerName + "-0",
@@ -479,6 +491,7 @@ func Test_OpenStackServerReconcileCreate(t *testing.T) {
listDefaultPortsNotFound(r)
createDefaultPort(r)
listDefaultServerNotFound(r)
+ listDefaultPortsNotFound(r)
createDefaultServer(r)
},
},
@@ -500,6 +513,7 @@ func Test_OpenStackServerReconcileCreate(t *testing.T) {
},
expect: func(r *recorders) {
listDefaultPorts(r)
+ listDefaultPortsWithID(r)
listDefaultServerFound(r)
},
},
diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md
index fa23c4cd24..00e763491e 100644
--- a/docs/book/src/api/v1beta1/api.md
+++ b/docs/book/src/api/v1beta1/api.md
@@ -3548,7 +3548,9 @@ InstanceState
(Optional)
- InstanceState is the state of the OpenStack instance for this machine.
+InstanceState is the state of the OpenStack instance for this machine.
+This field is not set anymore by the OpenStackMachine controller.
+Instead, it’s set by the OpenStackServer controller.
|
diff --git a/go.mod b/go.mod
index 44d13df6ad..0b5a4d8f07 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module sigs.k8s.io/cluster-api-provider-openstack
-go 1.23.4
+go 1.23.0
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
@@ -15,7 +15,7 @@ require (
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
github.com/prometheus/client_golang v1.20.5
- github.com/spf13/pflag v1.0.5
+ github.com/spf13/pflag v1.0.6
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.32.0
golang.org/x/text v0.21.0
@@ -31,7 +31,7 @@ require (
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/cluster-api v1.9.4
sigs.k8s.io/cluster-api/test v1.9.4
- sigs.k8s.io/controller-runtime v0.19.4
+ sigs.k8s.io/controller-runtime v0.19.5
sigs.k8s.io/structured-merge-diff/v4 v4.4.1
sigs.k8s.io/yaml v1.4.0
)
diff --git a/go.sum b/go.sum
index 9f45f249ea..3f8c7bba3c 100644
--- a/go.sum
+++ b/go.sum
@@ -252,8 +252,9 @@ github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
@@ -443,8 +444,8 @@ sigs.k8s.io/cluster-api v1.9.4 h1:pa2Ho50F9Js/Vv/Jy11TcpmGiqY2ukXCoDj/dY25Y7M=
sigs.k8s.io/cluster-api v1.9.4/go.mod h1:9DjpPCxJJo7/mH+KceINNJHr9c5X9S9HEp2B8JG3Uv8=
sigs.k8s.io/cluster-api/test v1.9.4 h1:ZZ+IPK/lfyc4d/QPtompt+cxXYC6tGJ4kTHhhocgbIM=
sigs.k8s.io/cluster-api/test v1.9.4/go.mod h1:dHLUcNc9vBNyQyY6NTcqcfpFvIiXmcL5Iqe2sETFD1c=
-sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo=
-sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
+sigs.k8s.io/controller-runtime v0.19.5 h1:rsE2cRYe0hK/rAAwiS1bwqgEcgCxTz9lavs3FMgLW0c=
+sigs.k8s.io/controller-runtime v0.19.5/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kind v0.25.0 h1:ugUvgesHKKA0yKmD6QtYTiEev+kPUpGxdTPbMGf8VTU=
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 19684bdb86..3bcf930fb6 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -1,6 +1,6 @@
module sigs.k8s.io/cluster-api-provider-openstack/hack/tools
-go 1.23.4
+go 1.23.0
require (
github.com/a8m/envsubst v1.4.2
@@ -84,7 +84,7 @@ require (
github.com/sergi/go-diff v1.2.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
- github.com/spf13/pflag v1.0.5 // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
@@ -133,7 +133,7 @@ require (
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect
sigs.k8s.io/cluster-api v1.9.4 // indirect
- sigs.k8s.io/controller-runtime v0.19.4 // indirect
+ sigs.k8s.io/controller-runtime v0.19.5 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index c622b40398..bb5991715c 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -272,8 +272,9 @@ github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
@@ -470,8 +471,8 @@ sigs.k8s.io/cluster-api/hack/tools v0.0.0-20221129083400-679ae3e9e6b6 h1:YF+g/Mr
sigs.k8s.io/cluster-api/hack/tools v0.0.0-20221129083400-679ae3e9e6b6/go.mod h1:7luenhlsUTb9obnAferuDFEvhtITw7JjHpXkiDmCmKY=
sigs.k8s.io/cluster-api/test v1.9.4 h1:ZZ+IPK/lfyc4d/QPtompt+cxXYC6tGJ4kTHhhocgbIM=
sigs.k8s.io/cluster-api/test v1.9.4/go.mod h1:dHLUcNc9vBNyQyY6NTcqcfpFvIiXmcL5Iqe2sETFD1c=
-sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo=
-sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
+sigs.k8s.io/controller-runtime v0.19.5 h1:rsE2cRYe0hK/rAAwiS1bwqgEcgCxTz9lavs3FMgLW0c=
+sigs.k8s.io/controller-runtime v0.19.5/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20230926180527-c93e2abcb28e h1:xYNzzoK+cwgBnaRqrYFLQCSwMAYcR6a06gf3FJ369Kw=
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20230926180527-c93e2abcb28e/go.mod h1:B6HLcvOy2S1qq2eWOFm9xepiKPMIc8Z9OXSPsnUDaR4=
sigs.k8s.io/controller-tools v0.16.5 h1:5k9FNRqziBPwqr17AMEPPV/En39ZBplLAdOwwQHruP4=
diff --git a/hack/tools/vendor/github.com/spf13/pflag/.editorconfig b/hack/tools/vendor/github.com/spf13/pflag/.editorconfig
new file mode 100644
index 0000000000..4492e9f9fe
--- /dev/null
+++ b/hack/tools/vendor/github.com/spf13/pflag/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.go]
+indent_style = tab
diff --git a/hack/tools/vendor/github.com/spf13/pflag/.golangci.yaml b/hack/tools/vendor/github.com/spf13/pflag/.golangci.yaml
new file mode 100644
index 0000000000..b274f24845
--- /dev/null
+++ b/hack/tools/vendor/github.com/spf13/pflag/.golangci.yaml
@@ -0,0 +1,4 @@
+linters:
+ disable-all: true
+ enable:
+ - nolintlint
diff --git a/hack/tools/vendor/github.com/spf13/pflag/flag.go b/hack/tools/vendor/github.com/spf13/pflag/flag.go
index 24a5036e95..7c058de374 100644
--- a/hack/tools/vendor/github.com/spf13/pflag/flag.go
+++ b/hack/tools/vendor/github.com/spf13/pflag/flag.go
@@ -160,7 +160,7 @@ type FlagSet struct {
args []string // arguments after flags
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
errorHandling ErrorHandling
- output io.Writer // nil means stderr; use out() accessor
+ output io.Writer // nil means stderr; use Output() accessor
interspersed bool // allow interspersed option/non-option args
normalizeNameFunc func(f *FlagSet, name string) NormalizedName
@@ -255,13 +255,20 @@ func (f *FlagSet) normalizeFlagName(name string) NormalizedName {
return n(f, name)
}
-func (f *FlagSet) out() io.Writer {
+// Output returns the destination for usage and error messages. os.Stderr is returned if
+// output was not set or was set to nil.
+func (f *FlagSet) Output() io.Writer {
if f.output == nil {
return os.Stderr
}
return f.output
}
+// Name returns the name of the flag set.
+func (f *FlagSet) Name() string {
+ return f.name
+}
+
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (f *FlagSet) SetOutput(output io.Writer) {
@@ -358,7 +365,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag {
}
if len(name) > 1 {
msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
c := name[0]
@@ -482,7 +489,7 @@ func (f *FlagSet) Set(name, value string) error {
}
if flag.Deprecated != "" {
- fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
+ fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
}
return nil
}
@@ -523,7 +530,7 @@ func Set(name, value string) error {
// otherwise, the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() {
usages := f.FlagUsages()
- fmt.Fprint(f.out(), usages)
+ fmt.Fprint(f.Output(), usages)
}
// defaultIsZeroValue returns true if the default value for this flag represents
@@ -758,7 +765,7 @@ func PrintDefaults() {
// defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) {
- fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
+ fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
f.PrintDefaults()
}
@@ -844,7 +851,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
_, alreadyThere := f.formal[normalizedFlagName]
if alreadyThere {
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
- fmt.Fprintln(f.out(), msg)
+ fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
@@ -860,7 +867,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
}
if len(flag.Shorthand) > 1 {
msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
if f.shorthands == nil {
@@ -870,7 +877,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
used, alreadyThere := f.shorthands[c]
if alreadyThere {
msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
f.shorthands[c] = flag
@@ -909,7 +916,7 @@ func VarP(value Value, name, shorthand, usage string) {
func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
if f.errorHandling != ContinueOnError {
- fmt.Fprintln(f.out(), err)
+ fmt.Fprintln(f.Output(), err)
f.usage()
}
return err
@@ -1060,7 +1067,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
}
if flag.ShorthandDeprecated != "" {
- fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
+ fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
}
err = fn(flag, value)
diff --git a/hack/tools/vendor/github.com/spf13/pflag/ip.go b/hack/tools/vendor/github.com/spf13/pflag/ip.go
index 3d414ba69f..06b8bcb572 100644
--- a/hack/tools/vendor/github.com/spf13/pflag/ip.go
+++ b/hack/tools/vendor/github.com/spf13/pflag/ip.go
@@ -16,6 +16,9 @@ func newIPValue(val net.IP, p *net.IP) *ipValue {
func (i *ipValue) String() string { return net.IP(*i).String() }
func (i *ipValue) Set(s string) error {
+ if s == "" {
+ return nil
+ }
ip := net.ParseIP(strings.TrimSpace(s))
if ip == nil {
return fmt.Errorf("failed to parse IP: %q", s)
diff --git a/hack/tools/vendor/github.com/spf13/pflag/ipnet_slice.go b/hack/tools/vendor/github.com/spf13/pflag/ipnet_slice.go
new file mode 100644
index 0000000000..6b541aa879
--- /dev/null
+++ b/hack/tools/vendor/github.com/spf13/pflag/ipnet_slice.go
@@ -0,0 +1,147 @@
+package pflag
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "strings"
+)
+
+// -- ipNetSlice Value
+type ipNetSliceValue struct {
+ value *[]net.IPNet
+ changed bool
+}
+
+func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue {
+ ipnsv := new(ipNetSliceValue)
+ ipnsv.value = p
+ *ipnsv.value = val
+ return ipnsv
+}
+
+// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag.
+// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended.
+func (s *ipNetSliceValue) Set(val string) error {
+
+ // remove all quote characters
+ rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
+
+ // read flag arguments with CSV parser
+ ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val))
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ // parse ip values into slice
+ out := make([]net.IPNet, 0, len(ipNetStrSlice))
+ for _, ipNetStr := range ipNetStrSlice {
+ _, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr))
+ if err != nil {
+ return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr)
+ }
+ out = append(out, *n)
+ }
+
+ if !s.changed {
+ *s.value = out
+ } else {
+ *s.value = append(*s.value, out...)
+ }
+
+ s.changed = true
+
+ return nil
+}
+
+// Type returns a string that uniquely represents this flag's type.
+func (s *ipNetSliceValue) Type() string {
+ return "ipNetSlice"
+}
+
+// String defines a "native" format for this net.IPNet slice flag value.
+func (s *ipNetSliceValue) String() string {
+
+ ipNetStrSlice := make([]string, len(*s.value))
+ for i, n := range *s.value {
+ ipNetStrSlice[i] = n.String()
+ }
+
+ out, _ := writeAsCSV(ipNetStrSlice)
+ return "[" + out + "]"
+}
+
+func ipNetSliceConv(val string) (interface{}, error) {
+ val = strings.Trim(val, "[]")
+ // Emtpy string would cause a slice with one (empty) entry
+ if len(val) == 0 {
+ return []net.IPNet{}, nil
+ }
+ ss := strings.Split(val, ",")
+ out := make([]net.IPNet, len(ss))
+ for i, sval := range ss {
+ _, n, err := net.ParseCIDR(strings.TrimSpace(sval))
+ if err != nil {
+ return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval)
+ }
+ out[i] = *n
+ }
+ return out, nil
+}
+
+// GetIPNetSlice returns the []net.IPNet value of a flag with the given name
+func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) {
+ val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv)
+ if err != nil {
+ return []net.IPNet{}, err
+ }
+ return val.([]net.IPNet), nil
+}
+
+// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string.
+// The argument p points to a []net.IPNet variable in which to store the value of the flag.
+func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
+ f.VarP(newIPNetSliceValue(value, p), name, "", usage)
+}
+
+// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
+ f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
+}
+
+// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string.
+// The argument p points to a []net.IPNet variable in which to store the value of the flag.
+func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
+ CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage)
+}
+
+// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
+func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
+ CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
+}
+
+// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
+// The return value is the address of a []net.IPNet variable that stores the value of that flag.
+func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
+ p := []net.IPNet{}
+ f.IPNetSliceVarP(&p, name, "", value, usage)
+ return &p
+}
+
+// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
+ p := []net.IPNet{}
+ f.IPNetSliceVarP(&p, name, shorthand, value, usage)
+ return &p
+}
+
+// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
+// The return value is the address of a []net.IP variable that stores the value of the flag.
+func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
+ return CommandLine.IPNetSliceP(name, "", value, usage)
+}
+
+// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
+func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
+ return CommandLine.IPNetSliceP(name, shorthand, value, usage)
+}
diff --git a/hack/tools/vendor/github.com/spf13/pflag/string_array.go b/hack/tools/vendor/github.com/spf13/pflag/string_array.go
index 4894af8180..d1ff0a96ba 100644
--- a/hack/tools/vendor/github.com/spf13/pflag/string_array.go
+++ b/hack/tools/vendor/github.com/spf13/pflag/string_array.go
@@ -31,11 +31,7 @@ func (s *stringArrayValue) Append(val string) error {
func (s *stringArrayValue) Replace(val []string) error {
out := make([]string, len(val))
for i, d := range val {
- var err error
out[i] = d
- if err != nil {
- return err
- }
}
*s.value = out
return nil
diff --git a/hack/tools/vendor/modules.txt b/hack/tools/vendor/modules.txt
index af7834ad53..1d0379d074 100644
--- a/hack/tools/vendor/modules.txt
+++ b/hack/tools/vendor/modules.txt
@@ -343,7 +343,7 @@ github.com/spf13/afero/mem
# github.com/spf13/cobra v1.8.1
## explicit; go 1.15
github.com/spf13/cobra
-# github.com/spf13/pflag v1.0.5
+# github.com/spf13/pflag v1.0.6
## explicit; go 1.12
github.com/spf13/pflag
# github.com/stoewer/go-strcase v1.3.0
@@ -1264,13 +1264,12 @@ sigs.k8s.io/cluster-api/util/predicates
sigs.k8s.io/cluster-api/util/topology
sigs.k8s.io/cluster-api/util/version
# sigs.k8s.io/cluster-api-provider-openstack v0.0.0 => ../..
-## explicit; go 1.23.4
+## explicit; go 1.23.0
sigs.k8s.io/cluster-api-provider-openstack
sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1
sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7
sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1
sigs.k8s.io/cluster-api-provider-openstack/controllers
-sigs.k8s.io/cluster-api-provider-openstack/internal/futures
sigs.k8s.io/cluster-api-provider-openstack/pkg/clients
sigs.k8s.io/cluster-api-provider-openstack/pkg/clients/mock
sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute
@@ -1295,7 +1294,7 @@ sigs.k8s.io/cluster-api-provider-openstack/version
sigs.k8s.io/cluster-api/hack/tools/mdbook/embed
sigs.k8s.io/cluster-api/hack/tools/mdbook/releaselink
sigs.k8s.io/cluster-api/hack/tools/release
-# sigs.k8s.io/controller-runtime v0.19.4
+# sigs.k8s.io/controller-runtime v0.19.5
## explicit; go 1.22.0
sigs.k8s.io/controller-runtime
sigs.k8s.io/controller-runtime/pkg/builder
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
index 00025db7b6..7d271e78b5 100644
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
+++ b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
@@ -199,6 +199,8 @@ type OpenStackMachineStatus struct {
Addresses []corev1.NodeAddress `json:"addresses,omitempty"`
// InstanceState is the state of the OpenStack instance for this machine.
+ // This field is not set anymore by the OpenStackMachine controller.
+ // Instead, it's set by the OpenStackServer controller.
// +optional
InstanceState *InstanceState `json:"instanceState,omitempty"`
@@ -241,7 +243,6 @@ type OpenStackMachineStatus struct {
// +kubebuilder:resource:path=openstackmachines,scope=Namespaced,categories=cluster-api,shortName=osm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this OpenStackMachine belongs"
-// +kubebuilder:printcolumn:name="InstanceState",type="string",JSONPath=".status.instanceState",description="OpenStack instance state"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Machine ready status"
// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="OpenStack instance ID"
// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object which owns with this OpenStackMachine"
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/controllers/openstackserver_controller.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/controllers/openstackserver_controller.go
index 89b3d5977a..f5061462fe 100644
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/controllers/openstackserver_controller.go
+++ b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/controllers/openstackserver_controller.go
@@ -419,11 +419,7 @@ func getOrCreateServerPorts(openStackServer *infrav1alpha1.OpenStackServer, netw
}
desiredPorts := resolved.Ports
- if len(desiredPorts) == len(resources.Ports) {
- return nil
- }
-
- if err := networkingService.CreatePorts(openStackServer, desiredPorts, resources); err != nil {
+ if err := networkingService.EnsurePorts(openStackServer, desiredPorts, resources); err != nil {
return fmt.Errorf("creating ports: %w", err)
}
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/internal/futures/slices.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/internal/futures/slices.go
deleted file mode 100644
index b8d310605a..0000000000
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/internal/futures/slices.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package futures
-
-import stdlib_slices "slices"
-
-// SlicesConcat vendors the Go v1.22 slices.Concat function.
-func SlicesConcat[S ~[]E, E any](slices ...S) S {
- size := 0
- for _, s := range slices {
- size += len(s)
- if size < 0 {
- panic("len out of range")
- }
- }
- newslice := stdlib_slices.Grow[S](nil, size)
- for _, s := range slices {
- newslice = append(newslice, s...)
- }
- return newslice
-}
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute/referenced_resources.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute/referenced_resources.go
index a16614fdd5..3a504089f5 100644
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute/referenced_resources.go
+++ b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute/referenced_resources.go
@@ -28,7 +28,6 @@ import (
infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
- "sigs.k8s.io/cluster-api-provider-openstack/internal/futures"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
)
@@ -161,7 +160,7 @@ func ResolveServerSpec(ctx context.Context, scope *scope.WithLogger, k8sClient c
// 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 := futures.SlicesConcat(spec.Tags, openStackCluster.Spec.Tags)
+ machineTags := slices.Concat(spec.Tags, openStackCluster.Spec.Tags)
seen := make(map[string]struct{}, len(machineTags))
unique := make([]string, 0, len(machineTags))
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/port.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/port.go
index b763cc446f..59e1e5a8b3 100644
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/port.go
+++ b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/port.go
@@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
+ "slices"
"strings"
"time"
@@ -33,7 +34,6 @@ import (
infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
- "sigs.k8s.io/cluster-api-provider-openstack/internal/futures"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/record"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors"
@@ -124,7 +124,61 @@ func (s *Service) GetPortForExternalNetwork(instanceID string, externalNetworkID
return nil, nil
}
-func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec) (*ports.Port, error) {
+// ensurePortTagsAndTrunk ensures that the provided port has the tags and trunk defined in portSpec.
+func (s *Service) ensurePortTagsAndTrunk(port *ports.Port, eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec) error {
+ wantedTags := uniqueSortedTags(portSpec.Tags)
+ actualTags := uniqueSortedTags(port.Tags)
+ // Only replace tags if there is a difference
+ if !slices.Equal(wantedTags, actualTags) && len(wantedTags) > 0 {
+ if err := s.replaceAllAttributesTags(eventObject, portResource, port.ID, wantedTags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace port tags %s: %v", port.Name, err)
+ return err
+ }
+ }
+ if ptr.Deref(portSpec.Trunk, false) {
+ trunk, err := s.getOrCreateTrunkForPort(eventObject, port)
+ if err != nil {
+ record.Warnf(eventObject, "FailedCreateTrunk", "Failed to create trunk for port %s: %v", port.Name, err)
+ return err
+ }
+
+ if !slices.Equal(wantedTags, trunk.Tags) {
+ if err = s.replaceAllAttributesTags(eventObject, trunkResource, trunk.ID, wantedTags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", port.Name, err)
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// EnsurePort ensure that a port defined with portSpec Name and NetworkID exists,
+// and that the port has suitable tags and trunk. If the PortStatus is already known,
+// use the ID when filtering for existing ports.
+func (s *Service) EnsurePort(eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec, portStatus infrav1.PortStatus) (*ports.Port, error) {
+ opts := ports.ListOpts{
+ Name: portSpec.Name,
+ NetworkID: portSpec.NetworkID,
+ }
+ if portStatus.ID != "" {
+ opts.ID = portStatus.ID
+ }
+
+ existingPorts, err := s.client.ListPort(opts)
+ if err != nil {
+ return nil, fmt.Errorf("searching for existing port for server: %v", err)
+ }
+ if len(existingPorts) > 1 {
+ return nil, fmt.Errorf("multiple ports found with name \"%s\"", portSpec.Name)
+ }
+
+ if len(existingPorts) == 1 {
+ port := &existingPorts[0]
+ if err = s.ensurePortTagsAndTrunk(port, eventObject, portSpec); err != nil {
+ return nil, err
+ }
+ return port, nil
+ }
var addressPairs []ports.AddressPair
if !ptr.Deref(portSpec.DisablePortSecurity, false) {
for _, ap := range portSpec.AllowedAddressPairs {
@@ -200,24 +254,10 @@ func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.Resol
return nil, 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
- }
+ if err = s.ensurePortTagsAndTrunk(port, eventObject, portSpec); err != nil {
+ return nil, err
}
record.Eventf(eventObject, "SuccessfulCreatePort", "Created port %s with id %s", port.Name, port.ID)
- if ptr.Deref(portSpec.Trunk, false) {
- trunk, err := s.getOrCreateTrunkForPort(eventObject, port)
- if err != nil {
- 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, portSpec.Tags); err != nil {
- record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", port.Name, err)
- return nil, err
- }
- }
return port, nil
}
@@ -310,7 +350,10 @@ func (s *Service) DeleteClusterPorts(openStackCluster *infrav1.OpenStackCluster)
}
for _, port := range portList {
- if strings.HasPrefix(port.Name, openStackCluster.Name) {
+ // The bastion port in 0.10 was prefixed with the namespace and then the current port name
+ // so in order to cleanup the old bastion port we need to check for the old format.
+ bastionLegacyPortPrefix := fmt.Sprintf("%s-%s", openStackCluster.Namespace, openStackCluster.Name)
+ if strings.HasPrefix(port.Name, openStackCluster.Name) || strings.HasPrefix(port.Name, bastionLegacyPortPrefix) {
if err := s.DeletePort(openStackCluster, port.ID); err != nil {
return fmt.Errorf("error deleting port %s: %v", port.ID, err)
}
@@ -328,23 +371,30 @@ func getPortName(baseName string, portSpec *infrav1.PortOpts, netIndex int) stri
return fmt.Sprintf("%s-%d", baseName, netIndex)
}
-func (s *Service) CreatePorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, resources *infrav1alpha1.ServerResources) error {
+// EnsurePorts ensures that every one of desiredPorts is created and has
+// expected trunk and tags.
+func (s *Service) EnsurePorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, resources *infrav1alpha1.ServerResources) error {
for i := range desiredPorts {
- // Skip creation of ports which already exist
+ // If we already created the port, make use of the status
+ portStatus := infrav1.PortStatus{}
if i < len(resources.Ports) {
- continue
+ portStatus = resources.Ports[i]
}
-
- portSpec := &desiredPorts[i]
- // Events are recorded in CreatePort
- port, err := s.CreatePort(eventObject, portSpec)
+ // Events are recorded in EnsurePort
+ port, err := s.EnsurePort(eventObject, &desiredPorts[i], portStatus)
if err != nil {
return err
}
- resources.Ports = append(resources.Ports, infrav1.PortStatus{
- ID: port.ID,
- })
+ // If we already have the status, replace it,
+ // otherwise append it.
+ if i < len(resources.Ports) {
+ resources.Ports[i] = portStatus
+ } else {
+ resources.Ports = append(resources.Ports, infrav1.PortStatus{
+ ID: port.ID,
+ })
+ }
}
return nil
@@ -419,7 +469,7 @@ func (s *Service) normalizePorts(ports []infrav1.PortOpts, clusterResourceName,
}
// Tags are inherited base tags plus any port-specific tags
- normalizedPort.Tags = futures.SlicesConcat(baseTags, port.Tags)
+ normalizedPort.Tags = slices.Concat(baseTags, port.Tags)
// No Trunk field specified for the port, inherit the machine default
if port.Trunk == nil {
@@ -604,3 +654,19 @@ func (s *Service) AdoptPortsServer(scope *scope.WithLogger, desiredPorts []infra
return nil
}
+
+// uniqueSortedTags returns a new, sorted slice where any duplicates have been removed.
+func uniqueSortedTags(tags []string) []string {
+ // remove duplicate values from tags
+ tagsMap := make(map[string]string)
+ for _, t := range tags {
+ tagsMap[t] = t
+ }
+
+ uniqueTags := []string{}
+ for k := range tagsMap {
+ uniqueTags = append(uniqueTags, k)
+ }
+ slices.Sort(uniqueTags)
+ return uniqueTags
+}
diff --git a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/service.go b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/service.go
index f6a3570998..e8a5b117ad 100644
--- a/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/service.go
+++ b/hack/tools/vendor/sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking/service.go
@@ -18,7 +18,6 @@ package networking
import (
"fmt"
- "sort"
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags"
"k8s.io/apimachinery/pkg/runtime"
@@ -65,28 +64,15 @@ func (s *Service) replaceAllAttributesTags(eventObject runtime.Object, resourceT
record.Warnf(eventObject, "FailedReplaceAllAttributesTags", "Invalid resourceType argument in function call")
panic(fmt.Errorf("invalid argument: resourceType, %s, does not match allowed arguments: %s or %s", resourceType, trunkResource, portResource))
}
- // remove duplicate values from tags
- tagsMap := make(map[string]string)
- for _, t := range tags {
- tagsMap[t] = t
- }
-
- uniqueTags := []string{}
- for k := range tagsMap {
- uniqueTags = append(uniqueTags, k)
- }
-
- // Sort the tags so that we always get fixed order of tags to make UT easier
- sort.Strings(uniqueTags)
_, err := s.client.ReplaceAllAttributesTags(resourceType, resourceID, attributestags.ReplaceAllOpts{
- Tags: uniqueTags,
+ Tags: tags,
})
if err != nil {
record.Warnf(eventObject, "FailedReplaceAllAttributesTags", "Failed to replace all attributestags, %s: %v", resourceID, err)
return err
}
- record.Eventf(eventObject, "SuccessfulReplaceAllAttributeTags", "Replaced all attributestags for %s with tags %s", resourceID, uniqueTags)
+ record.Eventf(eventObject, "SuccessfulReplaceAllAttributeTags", "Replaced all attributestags for %s with tags %s", resourceID, tags)
return nil
}
diff --git a/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
index 406380d329..9c7bf23255 100644
--- a/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
+++ b/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
@@ -467,6 +467,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
}
}
+ opts.ByObject = maps.Clone(opts.ByObject)
+ opts.DefaultNamespaces = maps.Clone(opts.DefaultNamespaces)
for obj, byObject := range opts.ByObject {
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
if err != nil {
@@ -478,6 +480,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
if isNamespaced && byObject.Namespaces == nil {
byObject.Namespaces = maps.Clone(opts.DefaultNamespaces)
+ } else {
+ byObject.Namespaces = maps.Clone(byObject.Namespaces)
}
// Default the namespace-level configs first, because they need to use the undefaulted type-level config
@@ -485,7 +489,6 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
for namespace, config := range byObject.Namespaces {
// 1. Default from the undefaulted type-level config
config = defaultConfig(config, byObjectToConfig(byObject))
-
// 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
// might not have an entry for the current namespace.
if defaultNamespaceSettings, hasDefaultNamespace := opts.DefaultNamespaces[namespace]; hasDefaultNamespace {
diff --git a/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
index dfe407f3b8..1f7752ba6e 100644
--- a/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
+++ b/hack/tools/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
@@ -183,7 +183,7 @@ func (c *Controller[request]) Start(ctx context.Context) error {
c.LogConstructor(nil).Info("Starting Controller")
for _, watch := range c.startWatches {
- syncingSource, ok := watch.(source.SyncingSource)
+ syncingSource, ok := watch.(source.TypedSyncingSource[request])
if !ok {
continue
}
diff --git a/internal/futures/slices.go b/internal/futures/slices.go
deleted file mode 100644
index b8d310605a..0000000000
--- a/internal/futures/slices.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package futures
-
-import stdlib_slices "slices"
-
-// SlicesConcat vendors the Go v1.22 slices.Concat function.
-func SlicesConcat[S ~[]E, E any](slices ...S) S {
- size := 0
- for _, s := range slices {
- size += len(s)
- if size < 0 {
- panic("len out of range")
- }
- }
- newslice := stdlib_slices.Grow[S](nil, size)
- for _, s := range slices {
- newslice = append(newslice, s...)
- }
- return newslice
-}
diff --git a/openshift/go.mod b/openshift/go.mod
index 763c7dc640..9f4cca64bb 100644
--- a/openshift/go.mod
+++ b/openshift/go.mod
@@ -1,8 +1,6 @@
module github.com/openshift/cluster-api-provider-openstack/openshift
-go 1.23.4
-
-toolchain go1.23.6
+go 1.23.0
require (
github.com/go-logr/logr v1.4.2
@@ -17,7 +15,7 @@ require (
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/cluster-api v1.9.4
sigs.k8s.io/cluster-api-provider-openstack v0.8.0
- sigs.k8s.io/controller-runtime v0.19.4
+ sigs.k8s.io/controller-runtime v0.19.5
sigs.k8s.io/yaml v1.4.0
)
@@ -61,7 +59,7 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
- github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
diff --git a/openshift/go.sum b/openshift/go.sum
index 4eeaaef392..cc906c56c6 100644
--- a/openshift/go.sum
+++ b/openshift/go.sum
@@ -108,8 +108,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA=
-github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -205,8 +205,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/cluster-api v1.9.4 h1:pa2Ho50F9Js/Vv/Jy11TcpmGiqY2ukXCoDj/dY25Y7M=
sigs.k8s.io/cluster-api v1.9.4/go.mod h1:9DjpPCxJJo7/mH+KceINNJHr9c5X9S9HEp2B8JG3Uv8=
-sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo=
-sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
+sigs.k8s.io/controller-runtime v0.19.5 h1:rsE2cRYe0hK/rAAwiS1bwqgEcgCxTz9lavs3FMgLW0c=
+sigs.k8s.io/controller-runtime v0.19.5/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
diff --git a/openshift/manifests/0000_30_cluster-api-provider-openstack_04_infrastructure-components.yaml b/openshift/manifests/0000_30_cluster-api-provider-openstack_04_infrastructure-components.yaml
index 9bd00dba12..608ec255c6 100644
--- a/openshift/manifests/0000_30_cluster-api-provider-openstack_04_infrastructure-components.yaml
+++ b/openshift/manifests/0000_30_cluster-api-provider-openstack_04_infrastructure-components.yaml
@@ -7337,10 +7337,6 @@ data:
jsonPath: .metadata.labels.cluster\.x-k8s\.io/cluster-name
name: Cluster
type: string
- - description: OpenStack instance state
- jsonPath: .status.instanceState
- name: InstanceState
- type: string
- description: Machine ready status
jsonPath: .status.ready
name: Ready
@@ -8345,8 +8341,10 @@ data:
description: InstanceID is the OpenStack instance ID for this machine.
type: string
instanceState:
- description: InstanceState is the state of the OpenStack instance
- for this machine.
+ description: |-
+ InstanceState is the state of the OpenStack instance for this machine.
+ This field is not set anymore by the OpenStackMachine controller.
+ Instead, it's set by the OpenStackServer controller.
type: string
ready:
description: Ready is true when the provider resource is ready.
diff --git a/openshift/vendor/github.com/spf13/pflag/.editorconfig b/openshift/vendor/github.com/spf13/pflag/.editorconfig
new file mode 100644
index 0000000000..4492e9f9fe
--- /dev/null
+++ b/openshift/vendor/github.com/spf13/pflag/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.go]
+indent_style = tab
diff --git a/openshift/vendor/github.com/spf13/pflag/.golangci.yaml b/openshift/vendor/github.com/spf13/pflag/.golangci.yaml
new file mode 100644
index 0000000000..b274f24845
--- /dev/null
+++ b/openshift/vendor/github.com/spf13/pflag/.golangci.yaml
@@ -0,0 +1,4 @@
+linters:
+ disable-all: true
+ enable:
+ - nolintlint
diff --git a/openshift/vendor/modules.txt b/openshift/vendor/modules.txt
index d8c31adabb..fe191192b5 100644
--- a/openshift/vendor/modules.txt
+++ b/openshift/vendor/modules.txt
@@ -239,7 +239,7 @@ github.com/prometheus/common/model
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
-# github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace
+# github.com/spf13/pflag v1.0.6
## explicit; go 1.12
github.com/spf13/pflag
# github.com/x448/float16 v0.8.4
@@ -792,7 +792,7 @@ sigs.k8s.io/cluster-api/util/conditions
sigs.k8s.io/cluster-api/util/contract
sigs.k8s.io/cluster-api/util/labels/format
# sigs.k8s.io/cluster-api-provider-openstack v0.8.0 => ../
-## explicit; go 1.23.4
+## explicit; go 1.23.0
sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1
sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1
sigs.k8s.io/cluster-api-provider-openstack/pkg/clients
@@ -803,7 +803,7 @@ sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors
sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/openstack
sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/optional
sigs.k8s.io/cluster-api-provider-openstack/version
-# sigs.k8s.io/controller-runtime v0.19.4
+# sigs.k8s.io/controller-runtime v0.19.5
## explicit; go 1.22.0
sigs.k8s.io/controller-runtime
sigs.k8s.io/controller-runtime/pkg/builder
diff --git a/openshift/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go b/openshift/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
index 00025db7b6..7d271e78b5 100644
--- a/openshift/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
+++ b/openshift/vendor/sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1/openstackmachine_types.go
@@ -199,6 +199,8 @@ type OpenStackMachineStatus struct {
Addresses []corev1.NodeAddress `json:"addresses,omitempty"`
// InstanceState is the state of the OpenStack instance for this machine.
+ // This field is not set anymore by the OpenStackMachine controller.
+ // Instead, it's set by the OpenStackServer controller.
// +optional
InstanceState *InstanceState `json:"instanceState,omitempty"`
@@ -241,7 +243,6 @@ type OpenStackMachineStatus struct {
// +kubebuilder:resource:path=openstackmachines,scope=Namespaced,categories=cluster-api,shortName=osm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this OpenStackMachine belongs"
-// +kubebuilder:printcolumn:name="InstanceState",type="string",JSONPath=".status.instanceState",description="OpenStack instance state"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Machine ready status"
// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="OpenStack instance ID"
// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object which owns with this OpenStackMachine"
diff --git a/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
index 406380d329..9c7bf23255 100644
--- a/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
+++ b/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
@@ -467,6 +467,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
}
}
+ opts.ByObject = maps.Clone(opts.ByObject)
+ opts.DefaultNamespaces = maps.Clone(opts.DefaultNamespaces)
for obj, byObject := range opts.ByObject {
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
if err != nil {
@@ -478,6 +480,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
if isNamespaced && byObject.Namespaces == nil {
byObject.Namespaces = maps.Clone(opts.DefaultNamespaces)
+ } else {
+ byObject.Namespaces = maps.Clone(byObject.Namespaces)
}
// Default the namespace-level configs first, because they need to use the undefaulted type-level config
@@ -485,7 +489,6 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
for namespace, config := range byObject.Namespaces {
// 1. Default from the undefaulted type-level config
config = defaultConfig(config, byObjectToConfig(byObject))
-
// 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
// might not have an entry for the current namespace.
if defaultNamespaceSettings, hasDefaultNamespace := opts.DefaultNamespaces[namespace]; hasDefaultNamespace {
diff --git a/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
index dfe407f3b8..1f7752ba6e 100644
--- a/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
+++ b/openshift/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
@@ -183,7 +183,7 @@ func (c *Controller[request]) Start(ctx context.Context) error {
c.LogConstructor(nil).Info("Starting Controller")
for _, watch := range c.startWatches {
- syncingSource, ok := watch.(source.SyncingSource)
+ syncingSource, ok := watch.(source.TypedSyncingSource[request])
if !ok {
continue
}
diff --git a/pkg/cloud/services/compute/referenced_resources.go b/pkg/cloud/services/compute/referenced_resources.go
index a16614fdd5..3a504089f5 100644
--- a/pkg/cloud/services/compute/referenced_resources.go
+++ b/pkg/cloud/services/compute/referenced_resources.go
@@ -28,7 +28,6 @@ import (
infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
- "sigs.k8s.io/cluster-api-provider-openstack/internal/futures"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
)
@@ -161,7 +160,7 @@ func ResolveServerSpec(ctx context.Context, scope *scope.WithLogger, k8sClient c
// 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 := futures.SlicesConcat(spec.Tags, openStackCluster.Spec.Tags)
+ machineTags := slices.Concat(spec.Tags, openStackCluster.Spec.Tags)
seen := make(map[string]struct{}, len(machineTags))
unique := make([]string, 0, len(machineTags))
diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go
index b763cc446f..59e1e5a8b3 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"
@@ -33,7 +34,6 @@ import (
infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
- "sigs.k8s.io/cluster-api-provider-openstack/internal/futures"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/record"
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors"
@@ -124,7 +124,61 @@ func (s *Service) GetPortForExternalNetwork(instanceID string, externalNetworkID
return nil, nil
}
-func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec) (*ports.Port, error) {
+// ensurePortTagsAndTrunk ensures that the provided port has the tags and trunk defined in portSpec.
+func (s *Service) ensurePortTagsAndTrunk(port *ports.Port, eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec) error {
+ wantedTags := uniqueSortedTags(portSpec.Tags)
+ actualTags := uniqueSortedTags(port.Tags)
+ // Only replace tags if there is a difference
+ if !slices.Equal(wantedTags, actualTags) && len(wantedTags) > 0 {
+ if err := s.replaceAllAttributesTags(eventObject, portResource, port.ID, wantedTags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace port tags %s: %v", port.Name, err)
+ return err
+ }
+ }
+ if ptr.Deref(portSpec.Trunk, false) {
+ trunk, err := s.getOrCreateTrunkForPort(eventObject, port)
+ if err != nil {
+ record.Warnf(eventObject, "FailedCreateTrunk", "Failed to create trunk for port %s: %v", port.Name, err)
+ return err
+ }
+
+ if !slices.Equal(wantedTags, trunk.Tags) {
+ if err = s.replaceAllAttributesTags(eventObject, trunkResource, trunk.ID, wantedTags); err != nil {
+ record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", port.Name, err)
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// EnsurePort ensure that a port defined with portSpec Name and NetworkID exists,
+// and that the port has suitable tags and trunk. If the PortStatus is already known,
+// use the ID when filtering for existing ports.
+func (s *Service) EnsurePort(eventObject runtime.Object, portSpec *infrav1.ResolvedPortSpec, portStatus infrav1.PortStatus) (*ports.Port, error) {
+ opts := ports.ListOpts{
+ Name: portSpec.Name,
+ NetworkID: portSpec.NetworkID,
+ }
+ if portStatus.ID != "" {
+ opts.ID = portStatus.ID
+ }
+
+ existingPorts, err := s.client.ListPort(opts)
+ if err != nil {
+ return nil, fmt.Errorf("searching for existing port for server: %v", err)
+ }
+ if len(existingPorts) > 1 {
+ return nil, fmt.Errorf("multiple ports found with name \"%s\"", portSpec.Name)
+ }
+
+ if len(existingPorts) == 1 {
+ port := &existingPorts[0]
+ if err = s.ensurePortTagsAndTrunk(port, eventObject, portSpec); err != nil {
+ return nil, err
+ }
+ return port, nil
+ }
var addressPairs []ports.AddressPair
if !ptr.Deref(portSpec.DisablePortSecurity, false) {
for _, ap := range portSpec.AllowedAddressPairs {
@@ -200,24 +254,10 @@ func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.Resol
return nil, 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
- }
+ if err = s.ensurePortTagsAndTrunk(port, eventObject, portSpec); err != nil {
+ return nil, err
}
record.Eventf(eventObject, "SuccessfulCreatePort", "Created port %s with id %s", port.Name, port.ID)
- if ptr.Deref(portSpec.Trunk, false) {
- trunk, err := s.getOrCreateTrunkForPort(eventObject, port)
- if err != nil {
- 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, portSpec.Tags); err != nil {
- record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", port.Name, err)
- return nil, err
- }
- }
return port, nil
}
@@ -310,7 +350,10 @@ func (s *Service) DeleteClusterPorts(openStackCluster *infrav1.OpenStackCluster)
}
for _, port := range portList {
- if strings.HasPrefix(port.Name, openStackCluster.Name) {
+ // The bastion port in 0.10 was prefixed with the namespace and then the current port name
+ // so in order to cleanup the old bastion port we need to check for the old format.
+ bastionLegacyPortPrefix := fmt.Sprintf("%s-%s", openStackCluster.Namespace, openStackCluster.Name)
+ if strings.HasPrefix(port.Name, openStackCluster.Name) || strings.HasPrefix(port.Name, bastionLegacyPortPrefix) {
if err := s.DeletePort(openStackCluster, port.ID); err != nil {
return fmt.Errorf("error deleting port %s: %v", port.ID, err)
}
@@ -328,23 +371,30 @@ func getPortName(baseName string, portSpec *infrav1.PortOpts, netIndex int) stri
return fmt.Sprintf("%s-%d", baseName, netIndex)
}
-func (s *Service) CreatePorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, resources *infrav1alpha1.ServerResources) error {
+// EnsurePorts ensures that every one of desiredPorts is created and has
+// expected trunk and tags.
+func (s *Service) EnsurePorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, resources *infrav1alpha1.ServerResources) error {
for i := range desiredPorts {
- // Skip creation of ports which already exist
+ // If we already created the port, make use of the status
+ portStatus := infrav1.PortStatus{}
if i < len(resources.Ports) {
- continue
+ portStatus = resources.Ports[i]
}
-
- portSpec := &desiredPorts[i]
- // Events are recorded in CreatePort
- port, err := s.CreatePort(eventObject, portSpec)
+ // Events are recorded in EnsurePort
+ port, err := s.EnsurePort(eventObject, &desiredPorts[i], portStatus)
if err != nil {
return err
}
- resources.Ports = append(resources.Ports, infrav1.PortStatus{
- ID: port.ID,
- })
+ // If we already have the status, replace it,
+ // otherwise append it.
+ if i < len(resources.Ports) {
+ resources.Ports[i] = portStatus
+ } else {
+ resources.Ports = append(resources.Ports, infrav1.PortStatus{
+ ID: port.ID,
+ })
+ }
}
return nil
@@ -419,7 +469,7 @@ func (s *Service) normalizePorts(ports []infrav1.PortOpts, clusterResourceName,
}
// Tags are inherited base tags plus any port-specific tags
- normalizedPort.Tags = futures.SlicesConcat(baseTags, port.Tags)
+ normalizedPort.Tags = slices.Concat(baseTags, port.Tags)
// No Trunk field specified for the port, inherit the machine default
if port.Trunk == nil {
@@ -604,3 +654,19 @@ func (s *Service) AdoptPortsServer(scope *scope.WithLogger, desiredPorts []infra
return nil
}
+
+// uniqueSortedTags returns a new, sorted slice where any duplicates have been removed.
+func uniqueSortedTags(tags []string) []string {
+ // remove duplicate values from tags
+ tagsMap := make(map[string]string)
+ for _, t := range tags {
+ tagsMap[t] = t
+ }
+
+ uniqueTags := []string{}
+ for k := range tagsMap {
+ uniqueTags = append(uniqueTags, k)
+ }
+ slices.Sort(uniqueTags)
+ return uniqueTags
+}
diff --git a/pkg/cloud/services/networking/port_test.go b/pkg/cloud/services/networking/port_test.go
index 4a247e89af..a215e2efe8 100644
--- a/pkg/cloud/services/networking/port_test.go
+++ b/pkg/cloud/services/networking/port_test.go
@@ -41,7 +41,7 @@ import (
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
)
-func Test_CreatePort(t *testing.T) {
+func Test_EnsurePort(t *testing.T) {
// Arbitrary values used in the tests
const (
netID = "7fd24ceb-788a-441f-ad0a-d8e2f5d31a1d"
@@ -60,8 +60,8 @@ func Test_CreatePort(t *testing.T) {
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.
+ // Note the 'wanted' port isn't so important, since it will be whatever we tell ListPort or EnsurePort to return.
+ // Mostly in this test suite, we're checking that EnsurePort is called with the expected port opts.
want *ports.Port
wantErr bool
}{
@@ -157,6 +157,10 @@ func Test_CreatePort(t *testing.T) {
},
}
+ m.ListPort(ports.ListOpts{
+ Name: "foo-port-1",
+ NetworkID: netID,
+ }).Return(nil, nil)
// The following allows us to use gomega to
// compare the argument instead of gomock.
// Gomock's output in the case of a mismatch is
@@ -184,6 +188,10 @@ func Test_CreatePort(t *testing.T) {
expectedCreateOpts = portsbinding.CreateOptsExt{
CreateOptsBuilder: expectedCreateOpts,
}
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return(nil, nil)
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))
@@ -203,6 +211,10 @@ func Test_CreatePort(t *testing.T) {
SecurityGroups: []string{portSecurityGroupID},
},
expect: func(m *mock.MockNetworkClientMockRecorder, _ Gomega) {
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return(nil, nil)
m.CreatePort(gomock.Any()).Times(0)
},
wantErr: true,
@@ -235,6 +247,10 @@ func Test_CreatePort(t *testing.T) {
expectedCreateOpts = portsbinding.CreateOptsExt{
CreateOptsBuilder: expectedCreateOpts,
}
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return(nil, nil)
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))
@@ -277,6 +293,10 @@ func Test_CreatePort(t *testing.T) {
expectedCreateOpts = portsbinding.CreateOptsExt{
CreateOptsBuilder: expectedCreateOpts,
}
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return(nil, nil)
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))
@@ -286,7 +306,7 @@ func Test_CreatePort(t *testing.T) {
want: &ports.Port{ID: portID},
},
{
- name: "tags and trunk",
+ name: "create port with tags and trunk",
port: infrav1.ResolvedPortSpec{
Name: "test-port",
NetworkID: netID,
@@ -303,6 +323,10 @@ func Test_CreatePort(t *testing.T) {
CreateOptsBuilder: expectedCreateOpts,
}
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return(nil, nil)
// Create the port
m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
gotCreateOpts := builder.(portsbinding.CreateOptsExt)
@@ -334,6 +358,87 @@ func Test_CreatePort(t *testing.T) {
},
want: &ports.Port{ID: portID, Name: "test-port"},
},
+ {
+ name: "port with tags and trunk already exists",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ Tags: []string{"tag1", "tag2"},
+ Trunk: ptr.To(true),
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder, _ types.Gomega) {
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return([]ports.Port{{
+ ID: portID,
+ Name: "test-port",
+ NetworkID: netID,
+ Tags: []string{"tag1", "tag2"},
+ }}, nil)
+
+ // Look for existing trunk
+ m.ListTrunk(trunks.ListOpts{
+ PortID: portID,
+ Name: "test-port",
+ }).Return([]trunks.Trunk{{
+ ID: trunkID,
+ Tags: []string{"tag1", "tag2"},
+ }}, nil)
+ },
+ want: &ports.Port{
+ ID: portID,
+ Name: "test-port",
+ NetworkID: netID,
+ Tags: []string{"tag1", "tag2"},
+ },
+ },
+ {
+ name: "partial port missing tags and trunk",
+ port: infrav1.ResolvedPortSpec{
+ Name: "test-port",
+ NetworkID: netID,
+ Tags: []string{"tag1", "tag2"},
+ Trunk: ptr.To(true),
+ },
+ expect: func(m *mock.MockNetworkClientMockRecorder, _ types.Gomega) {
+ m.ListPort(ports.ListOpts{
+ Name: "test-port",
+ NetworkID: netID,
+ }).Return([]ports.Port{{
+ ID: portID,
+ Name: "test-port",
+ NetworkID: netID,
+ }}, 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",
+ NetworkID: netID,
+ },
+ },
}
eventObject := &infrav1.OpenStackMachine{}
@@ -349,9 +454,10 @@ func Test_CreatePort(t *testing.T) {
s := Service{
client: mockClient,
}
- got, err := s.CreatePort(
+ got, err := s.EnsurePort(
eventObject,
&tt.port,
+ infrav1.PortStatus{},
)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
diff --git a/pkg/cloud/services/networking/service.go b/pkg/cloud/services/networking/service.go
index f6a3570998..e8a5b117ad 100644
--- a/pkg/cloud/services/networking/service.go
+++ b/pkg/cloud/services/networking/service.go
@@ -18,7 +18,6 @@ package networking
import (
"fmt"
- "sort"
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags"
"k8s.io/apimachinery/pkg/runtime"
@@ -65,28 +64,15 @@ func (s *Service) replaceAllAttributesTags(eventObject runtime.Object, resourceT
record.Warnf(eventObject, "FailedReplaceAllAttributesTags", "Invalid resourceType argument in function call")
panic(fmt.Errorf("invalid argument: resourceType, %s, does not match allowed arguments: %s or %s", resourceType, trunkResource, portResource))
}
- // remove duplicate values from tags
- tagsMap := make(map[string]string)
- for _, t := range tags {
- tagsMap[t] = t
- }
-
- uniqueTags := []string{}
- for k := range tagsMap {
- uniqueTags = append(uniqueTags, k)
- }
-
- // Sort the tags so that we always get fixed order of tags to make UT easier
- sort.Strings(uniqueTags)
_, err := s.client.ReplaceAllAttributesTags(resourceType, resourceID, attributestags.ReplaceAllOpts{
- Tags: uniqueTags,
+ Tags: tags,
})
if err != nil {
record.Warnf(eventObject, "FailedReplaceAllAttributesTags", "Failed to replace all attributestags, %s: %v", resourceID, err)
return err
}
- record.Eventf(eventObject, "SuccessfulReplaceAllAttributeTags", "Replaced all attributestags for %s with tags %s", resourceID, uniqueTags)
+ record.Eventf(eventObject, "SuccessfulReplaceAllAttributeTags", "Replaced all attributestags for %s with tags %s", resourceID, tags)
return nil
}
diff --git a/test/e2e/data/e2e_conf.yaml b/test/e2e/data/e2e_conf.yaml
index 1d9be80af5..be1be4042c 100644
--- a/test/e2e/data/e2e_conf.yaml
+++ b/test/e2e/data/e2e_conf.yaml
@@ -1,4 +1,3 @@
----
# E2E test scenario using local dev images and manifests built from the source tree for following providers:
# - openstack
@@ -189,7 +188,7 @@ variables:
SSH_USER_MACHINE: "ubuntu"
EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION: "true"
# The Flatcar image produced by the image-builder
- OPENSTACK_FLATCAR_IMAGE_NAME: "flatcar-stable-4081.2.0-kube-v1.31.2"
+ OPENSTACK_FLATCAR_IMAGE_NAME: "flatcar-stable-4152.2.0-kube-v1.31.2"
# A plain Flatcar from the Flatcar releases server
FLATCAR_IMAGE_NAME: "flatcar_production_openstack_image"
diff --git a/vendor/github.com/spf13/pflag/.editorconfig b/vendor/github.com/spf13/pflag/.editorconfig
new file mode 100644
index 0000000000..4492e9f9fe
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.go]
+indent_style = tab
diff --git a/vendor/github.com/spf13/pflag/.golangci.yaml b/vendor/github.com/spf13/pflag/.golangci.yaml
new file mode 100644
index 0000000000..b274f24845
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/.golangci.yaml
@@ -0,0 +1,4 @@
+linters:
+ disable-all: true
+ enable:
+ - nolintlint
diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go
index 24a5036e95..7c058de374 100644
--- a/vendor/github.com/spf13/pflag/flag.go
+++ b/vendor/github.com/spf13/pflag/flag.go
@@ -160,7 +160,7 @@ type FlagSet struct {
args []string // arguments after flags
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
errorHandling ErrorHandling
- output io.Writer // nil means stderr; use out() accessor
+ output io.Writer // nil means stderr; use Output() accessor
interspersed bool // allow interspersed option/non-option args
normalizeNameFunc func(f *FlagSet, name string) NormalizedName
@@ -255,13 +255,20 @@ func (f *FlagSet) normalizeFlagName(name string) NormalizedName {
return n(f, name)
}
-func (f *FlagSet) out() io.Writer {
+// Output returns the destination for usage and error messages. os.Stderr is returned if
+// output was not set or was set to nil.
+func (f *FlagSet) Output() io.Writer {
if f.output == nil {
return os.Stderr
}
return f.output
}
+// Name returns the name of the flag set.
+func (f *FlagSet) Name() string {
+ return f.name
+}
+
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (f *FlagSet) SetOutput(output io.Writer) {
@@ -358,7 +365,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag {
}
if len(name) > 1 {
msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
c := name[0]
@@ -482,7 +489,7 @@ func (f *FlagSet) Set(name, value string) error {
}
if flag.Deprecated != "" {
- fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
+ fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
}
return nil
}
@@ -523,7 +530,7 @@ func Set(name, value string) error {
// otherwise, the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() {
usages := f.FlagUsages()
- fmt.Fprint(f.out(), usages)
+ fmt.Fprint(f.Output(), usages)
}
// defaultIsZeroValue returns true if the default value for this flag represents
@@ -758,7 +765,7 @@ func PrintDefaults() {
// defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) {
- fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
+ fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
f.PrintDefaults()
}
@@ -844,7 +851,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
_, alreadyThere := f.formal[normalizedFlagName]
if alreadyThere {
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
- fmt.Fprintln(f.out(), msg)
+ fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
@@ -860,7 +867,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
}
if len(flag.Shorthand) > 1 {
msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
if f.shorthands == nil {
@@ -870,7 +877,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
used, alreadyThere := f.shorthands[c]
if alreadyThere {
msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
- fmt.Fprintf(f.out(), msg)
+ fmt.Fprintf(f.Output(), msg)
panic(msg)
}
f.shorthands[c] = flag
@@ -909,7 +916,7 @@ func VarP(value Value, name, shorthand, usage string) {
func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
if f.errorHandling != ContinueOnError {
- fmt.Fprintln(f.out(), err)
+ fmt.Fprintln(f.Output(), err)
f.usage()
}
return err
@@ -1060,7 +1067,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
}
if flag.ShorthandDeprecated != "" {
- fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
+ fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
}
err = fn(flag, value)
diff --git a/vendor/github.com/spf13/pflag/ip.go b/vendor/github.com/spf13/pflag/ip.go
index 3d414ba69f..06b8bcb572 100644
--- a/vendor/github.com/spf13/pflag/ip.go
+++ b/vendor/github.com/spf13/pflag/ip.go
@@ -16,6 +16,9 @@ func newIPValue(val net.IP, p *net.IP) *ipValue {
func (i *ipValue) String() string { return net.IP(*i).String() }
func (i *ipValue) Set(s string) error {
+ if s == "" {
+ return nil
+ }
ip := net.ParseIP(strings.TrimSpace(s))
if ip == nil {
return fmt.Errorf("failed to parse IP: %q", s)
diff --git a/vendor/github.com/spf13/pflag/ipnet_slice.go b/vendor/github.com/spf13/pflag/ipnet_slice.go
new file mode 100644
index 0000000000..6b541aa879
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/ipnet_slice.go
@@ -0,0 +1,147 @@
+package pflag
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "strings"
+)
+
+// -- ipNetSlice Value
+type ipNetSliceValue struct {
+ value *[]net.IPNet
+ changed bool
+}
+
+func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue {
+ ipnsv := new(ipNetSliceValue)
+ ipnsv.value = p
+ *ipnsv.value = val
+ return ipnsv
+}
+
+// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag.
+// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended.
+func (s *ipNetSliceValue) Set(val string) error {
+
+ // remove all quote characters
+ rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
+
+ // read flag arguments with CSV parser
+ ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val))
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ // parse ip values into slice
+ out := make([]net.IPNet, 0, len(ipNetStrSlice))
+ for _, ipNetStr := range ipNetStrSlice {
+ _, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr))
+ if err != nil {
+ return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr)
+ }
+ out = append(out, *n)
+ }
+
+ if !s.changed {
+ *s.value = out
+ } else {
+ *s.value = append(*s.value, out...)
+ }
+
+ s.changed = true
+
+ return nil
+}
+
+// Type returns a string that uniquely represents this flag's type.
+func (s *ipNetSliceValue) Type() string {
+ return "ipNetSlice"
+}
+
+// String defines a "native" format for this net.IPNet slice flag value.
+func (s *ipNetSliceValue) String() string {
+
+ ipNetStrSlice := make([]string, len(*s.value))
+ for i, n := range *s.value {
+ ipNetStrSlice[i] = n.String()
+ }
+
+ out, _ := writeAsCSV(ipNetStrSlice)
+ return "[" + out + "]"
+}
+
+func ipNetSliceConv(val string) (interface{}, error) {
+ val = strings.Trim(val, "[]")
+ // Emtpy string would cause a slice with one (empty) entry
+ if len(val) == 0 {
+ return []net.IPNet{}, nil
+ }
+ ss := strings.Split(val, ",")
+ out := make([]net.IPNet, len(ss))
+ for i, sval := range ss {
+ _, n, err := net.ParseCIDR(strings.TrimSpace(sval))
+ if err != nil {
+ return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval)
+ }
+ out[i] = *n
+ }
+ return out, nil
+}
+
+// GetIPNetSlice returns the []net.IPNet value of a flag with the given name
+func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) {
+ val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv)
+ if err != nil {
+ return []net.IPNet{}, err
+ }
+ return val.([]net.IPNet), nil
+}
+
+// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string.
+// The argument p points to a []net.IPNet variable in which to store the value of the flag.
+func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
+ f.VarP(newIPNetSliceValue(value, p), name, "", usage)
+}
+
+// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
+ f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
+}
+
+// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string.
+// The argument p points to a []net.IPNet variable in which to store the value of the flag.
+func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
+ CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage)
+}
+
+// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
+func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
+ CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
+}
+
+// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
+// The return value is the address of a []net.IPNet variable that stores the value of that flag.
+func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
+ p := []net.IPNet{}
+ f.IPNetSliceVarP(&p, name, "", value, usage)
+ return &p
+}
+
+// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
+ p := []net.IPNet{}
+ f.IPNetSliceVarP(&p, name, shorthand, value, usage)
+ return &p
+}
+
+// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
+// The return value is the address of a []net.IP variable that stores the value of the flag.
+func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
+ return CommandLine.IPNetSliceP(name, "", value, usage)
+}
+
+// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
+func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
+ return CommandLine.IPNetSliceP(name, shorthand, value, usage)
+}
diff --git a/vendor/github.com/spf13/pflag/string_array.go b/vendor/github.com/spf13/pflag/string_array.go
index 4894af8180..d1ff0a96ba 100644
--- a/vendor/github.com/spf13/pflag/string_array.go
+++ b/vendor/github.com/spf13/pflag/string_array.go
@@ -31,11 +31,7 @@ func (s *stringArrayValue) Append(val string) error {
func (s *stringArrayValue) Replace(val []string) error {
out := make([]string, len(val))
for i, d := range val {
- var err error
out[i] = d
- if err != nil {
- return err
- }
}
*s.value = out
return nil
diff --git a/vendor/modules.txt b/vendor/modules.txt
index e2c7255b57..171104843a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -494,7 +494,7 @@ github.com/spf13/cast
# github.com/spf13/cobra v1.8.1
## explicit; go 1.15
github.com/spf13/cobra
-# github.com/spf13/pflag v1.0.5
+# github.com/spf13/pflag v1.0.6
## explicit; go 1.12
github.com/spf13/pflag
# github.com/spf13/viper v1.19.0
@@ -1542,7 +1542,7 @@ sigs.k8s.io/cluster-api/test/infrastructure/container
sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1
sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1
sigs.k8s.io/cluster-api/test/infrastructure/kind
-# sigs.k8s.io/controller-runtime v0.19.4
+# sigs.k8s.io/controller-runtime v0.19.5
## explicit; go 1.22.0
sigs.k8s.io/controller-runtime
sigs.k8s.io/controller-runtime/pkg/builder
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
index 406380d329..9c7bf23255 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
@@ -467,6 +467,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
}
}
+ opts.ByObject = maps.Clone(opts.ByObject)
+ opts.DefaultNamespaces = maps.Clone(opts.DefaultNamespaces)
for obj, byObject := range opts.ByObject {
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
if err != nil {
@@ -478,6 +480,8 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
if isNamespaced && byObject.Namespaces == nil {
byObject.Namespaces = maps.Clone(opts.DefaultNamespaces)
+ } else {
+ byObject.Namespaces = maps.Clone(byObject.Namespaces)
}
// Default the namespace-level configs first, because they need to use the undefaulted type-level config
@@ -485,7 +489,6 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
for namespace, config := range byObject.Namespaces {
// 1. Default from the undefaulted type-level config
config = defaultConfig(config, byObjectToConfig(byObject))
-
// 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
// might not have an entry for the current namespace.
if defaultNamespaceSettings, hasDefaultNamespace := opts.DefaultNamespaces[namespace]; hasDefaultNamespace {
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
index dfe407f3b8..1f7752ba6e 100644
--- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
@@ -183,7 +183,7 @@ func (c *Controller[request]) Start(ctx context.Context) error {
c.LogConstructor(nil).Info("Starting Controller")
for _, watch := range c.startWatches {
- syncingSource, ok := watch.(source.SyncingSource)
+ syncingSource, ok := watch.(source.TypedSyncingSource[request])
if !ok {
continue
}