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 }