From 807a3cd291e2e2cb22946826bccb64671a29d901 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 24 Jan 2025 22:39:26 +0400 Subject: [PATCH] refactor: all network merge controllers They had lots of common (and broken) code, so use generics to implement the core logic once, and use that in all kinds of merge controllers. This removes lots of code duplication. Fixes flaky unit-tests, and also improves controller performance on conflicts. Signed-off-by: Andrey Smirnov --- .../pkg/controllers/network/address_merge.go | 139 +++-------------- .../controllers/network/address_merge_test.go | 4 +- .../pkg/controllers/network/generic_merge.go | 142 +++++++++++++++++ .../pkg/controllers/network/hostname_merge.go | 119 +++------------ .../network/hostname_merge_test.go | 2 +- .../pkg/controllers/network/link_merge.go | 144 +++--------------- .../controllers/network/link_merge_test.go | 6 +- .../pkg/controllers/network/operator_merge.go | 139 +++-------------- .../network/operator_merge_test.go | 2 +- .../pkg/controllers/network/resolver_merge.go | 138 ++++------------- .../network/resolver_merge_test.go | 2 +- .../pkg/controllers/network/route_merge.go | 137 +++-------------- .../controllers/network/route_merge_test.go | 2 +- .../controllers/network/timeserver_merge.go | 129 ++++------------ .../network/timeserver_merge_test.go | 2 +- .../runtime/v1alpha2/v1alpha2_controller.go | 14 +- 16 files changed, 328 insertions(+), 793 deletions(-) create mode 100644 internal/app/machined/pkg/controllers/network/generic_merge.go diff --git a/internal/app/machined/pkg/controllers/network/address_merge.go b/internal/app/machined/pkg/controllers/network/address_merge.go index fea039806c..6c2f7dad7a 100644 --- a/internal/app/machined/pkg/controllers/network/address_merge.go +++ b/internal/app/machined/pkg/controllers/network/address_merge.go @@ -2,138 +2,41 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Package network provides controllers which manage network resources. -// -//nolint:dupl package network import ( - "context" - "fmt" - "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// AddressMergeController merges network.AddressSpec in network.ConfigNamespace and produces final network.AddressSpec in network.Namespace. -type AddressMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *AddressMergeController) Name() string { - return "network.AddressMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *AddressMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.AddressSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.AddressSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *AddressMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.AddressSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewAddressMergeController initializes a AddressMergeController. // -//nolint:gocyclo -func (ctrl *AddressMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.AddressSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network addresses: %w", err) - } - - // address is allowed as long as it's not duplicate, for duplicate higher layer takes precedence - addresses := map[string]*network.AddressSpec{} - - for _, res := range list.Items { - address := res.(*network.AddressSpec) //nolint:forcetypeassert - id := network.AddressID(address.TypedSpec().LinkName, address.TypedSpec().Address) - - existing, ok := addresses[id] - if ok && existing.TypedSpec().ConfigLayer > address.TypedSpec().ConfigLayer { - // skip this address, as existing one is higher layer - continue - } - - addresses[id] = address - } - - conflictsDetected := 0 - - for id, address := range addresses { - if err = safe.WriterModify(ctx, r, network.NewAddressSpec(network.NamespaceName, id), func(addr *network.AddressSpec) error { - *addr.TypedSpec() = *address.TypedSpec() - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // phase conflict, resource is being torn down, skip updating it and trigger reconcile - // later by failing the - conflictsDetected++ - - delete(addresses, id) - } else { - return fmt.Errorf("error updating resource: %w", err) - } - } - } - - // list addresses for cleanup - list, err = r.List(ctx, resource.NewMetadata(network.NamespaceName, network.AddressSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing resources: %w", err) - } - - for _, res := range list.Items { - if _, ok := addresses[res.Metadata().ID()]; !ok { - var okToDestroy bool - - okToDestroy, err = r.Teardown(ctx, res.Metadata()) - if err != nil { - return fmt.Errorf("error cleaning up addresses: %w", err) +// AddressMergeController merges network.AddressSpec in network.ConfigNamespace and produces final network.AddressSpec in network.Namespace. +func NewAddressMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.AddressSpec]) map[resource.ID]*network.AddressSpecSpec { + // address is allowed as long as it's not duplicate, for duplicate higher layer takes precedence + addresses := map[resource.ID]*network.AddressSpecSpec{} + + for address := range list.All() { + id := network.AddressID(address.TypedSpec().LinkName, address.TypedSpec().Address) + + existing, ok := addresses[id] + if ok && existing.ConfigLayer > address.TypedSpec().ConfigLayer { + // skip this address, as existing one is higher layer + continue } - if okToDestroy { - if err = r.Destroy(ctx, res.Metadata()); err != nil { - return fmt.Errorf("error cleaning up addresses: %w", err) - } - } + addresses[id] = address.TypedSpec() } - } - if conflictsDetected > 0 { - return fmt.Errorf("%d conflict(s) detected", conflictsDetected) - } - - r.ResetRestartBackoff() - } + return addresses + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/address_merge_test.go b/internal/app/machined/pkg/controllers/network/address_merge_test.go index 0a49e4b595..9064cd220f 100644 --- a/internal/app/machined/pkg/controllers/network/address_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/address_merge_test.go @@ -52,7 +52,7 @@ func (suite *AddressMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.AddressMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewAddressMergeController())) suite.startRuntime() } @@ -152,7 +152,7 @@ func (suite *AddressMergeSuite) TestMerge() { suite.assertAddresses( []string{ "lo/127.0.0.1/8", - "eth0/10.0.0.35/32", + "eth0/10.0.0.1/8", }, func(*network.AddressSpec, *assert.Assertions) {}, ) suite.Assert().NoError( diff --git a/internal/app/machined/pkg/controllers/network/generic_merge.go b/internal/app/machined/pkg/controllers/network/generic_merge.go new file mode 100644 index 0000000000..39ba17d2dc --- /dev/null +++ b/internal/app/machined/pkg/controllers/network/generic_merge.go @@ -0,0 +1,142 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package network + +import ( + "context" + "fmt" + "strings" + + "github.com/cosi-project/runtime/pkg/controller" + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/resource/typed" + "github.com/cosi-project/runtime/pkg/safe" + "go.uber.org/zap" +) + +type genericMergeFunc[T typed.DeepCopyable[T], E typed.Extension, R typed.Resource[T, E]] func(logger *zap.Logger, in safe.List[*R]) map[resource.ID]*T + +// GenericMergeController initializes a generic merge controller for network resources. +func GenericMergeController[T typed.DeepCopyable[T], E typed.Extension](namespaceIn, namespaceOut resource.Namespace, mergeFunc genericMergeFunc[T, E, typed.Resource[T, E]]) controller.Controller { + var zeroE E + + controllerName := strings.ReplaceAll(zeroE.ResourceDefinition().Type, "Spec", "MergeController") + + return &genericMergeController[T, E]{ + controllerName: controllerName, + resourceType: zeroE.ResourceDefinition().Type, + namespaceIn: namespaceIn, + namespaceOut: namespaceOut, + mergeFunc: mergeFunc, + } +} + +type genericMergeController[T typed.DeepCopyable[T], E typed.Extension] struct { + controllerName string + resourceType resource.Type + namespaceIn resource.Namespace + namespaceOut resource.Namespace + mergeFunc genericMergeFunc[T, E, typed.Resource[T, E]] +} + +func (ctrl *genericMergeController[T, E]) Name() string { + return ctrl.controllerName +} + +func (ctrl *genericMergeController[T, E]) Inputs() []controller.Input { + return []controller.Input{ + { + Namespace: ctrl.namespaceIn, + Type: ctrl.resourceType, + Kind: controller.InputWeak, + }, + { + Namespace: ctrl.namespaceOut, + Type: ctrl.resourceType, + Kind: controller.InputDestroyReady, + }, + } +} + +func (ctrl *genericMergeController[T, E]) Outputs() []controller.Output { + return []controller.Output{ + { + Type: ctrl.resourceType, + Kind: controller.OutputShared, + }, + } +} + +//nolint:gocyclo +func (ctrl *genericMergeController[T, E]) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { + for { + select { + case <-ctx.Done(): + return nil + case <-r.EventCh(): + } + + type R = typed.Resource[T, E] + + // list source network configuration resources + in, err := safe.ReaderList[*R](ctx, r, resource.NewMetadata(ctrl.namespaceIn, ctrl.resourceType, "", resource.VersionUndefined)) + if err != nil { + return fmt.Errorf("error listing source network resources: %w", err) + } + + merged := ctrl.mergeFunc(logger, in) + + // cleanup resources, detecting conflicts on the way + out, err := safe.ReaderList[*R](ctx, r, resource.NewMetadata(ctrl.namespaceOut, ctrl.resourceType, "", resource.VersionUndefined)) + if err != nil { + return fmt.Errorf("error listing output resources: %w", err) + } + + for res := range out.All() { + shouldBeDestroyed := false + if _, ok := merged[res.Metadata().ID()]; !ok { + shouldBeDestroyed = true + } + + isTearingDown := res.Metadata().Phase() == resource.PhaseTearingDown + + if shouldBeDestroyed || isTearingDown { + var okToDestroy bool + + okToDestroy, err = r.Teardown(ctx, res.Metadata()) + if err != nil { + return fmt.Errorf("error cleaning up addresses: %w", err) + } + + if okToDestroy { + if err = r.Destroy(ctx, res.Metadata()); err != nil { + return fmt.Errorf("error cleaning up addresses: %w", err) + } + } else if !shouldBeDestroyed { + // resource is not ready to be destroyed yet, skip it + delete(merged, res.Metadata().ID()) + } + } + } + + var zeroT T + + for id, spec := range merged { + if err = safe.WriterModify(ctx, r, + typed.NewResource[T, E](resource.NewMetadata(ctrl.namespaceOut, ctrl.resourceType, id, resource.VersionUndefined), zeroT), + func(r *R) error { + *r.TypedSpec() = *spec + + return nil + }); err != nil { + return fmt.Errorf("error updating resource: %w", err) + } + + logger.Debug("merged spec", zap.String("id", id), zap.Any("spec", spec)) + } + + r.ResetRestartBackoff() + } +} diff --git a/internal/app/machined/pkg/controllers/network/hostname_merge.go b/internal/app/machined/pkg/controllers/network/hostname_merge.go index 551dc24e55..9ee8fde7db 100644 --- a/internal/app/machined/pkg/controllers/network/hostname_merge.go +++ b/internal/app/machined/pkg/controllers/network/hostname_merge.go @@ -6,118 +6,41 @@ package network import ( - "context" - "fmt" - "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// HostnameMergeController merges network.HostnameSpec in network.ConfigNamespace and produces final network.HostnameSpec in network.Namespace. -type HostnameMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *HostnameMergeController) Name() string { - return "network.HostnameMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *HostnameMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.HostnameSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.HostnameSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *HostnameMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.HostnameSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewHostnameMergeController initializes a HostnameMergeController. // -//nolint:gocyclo -func (ctrl *HostnameMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.HostnameSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network addresses: %w", err) - } - - // simply merge by layers, overriding with the next configuration layer - var final network.HostnameSpecSpec - - for _, res := range list.Items { - spec := res.(*network.HostnameSpec) //nolint:forcetypeassert - - if final.Hostname != "" && spec.TypedSpec().ConfigLayer <= final.ConfigLayer { - // skip this spec, as existing one is higher layer - continue - } - - final = *spec.TypedSpec() - } - - if final.Hostname != "" { - if err = safe.WriterModify(ctx, r, network.NewHostnameSpec(network.NamespaceName, network.HostnameID), func(spec *network.HostnameSpec) error { - *spec.TypedSpec() = final - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // conflict - final.Hostname = "" - - r.QueueReconcile() - } else { - return fmt.Errorf("error updating resource: %w", err) +// HostnameMergeController merges network.HostnameSpec in network.ConfigNamespace and produces final network.HostnameSpec in network.Namespace. +func NewHostnameMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.HostnameSpec]) map[resource.ID]*network.HostnameSpecSpec { + // simply merge by layers, overriding with the next configuration layer + var final network.HostnameSpecSpec + + for spec := range list.All() { + if final.Hostname != "" && spec.TypedSpec().ConfigLayer <= final.ConfigLayer { + // skip this spec, as existing one is higher layer + continue } - } - } - - if final.Hostname == "" { - // remove existing - var okToDestroy bool - md := resource.NewMetadata(network.NamespaceName, network.HostnameSpecType, network.HostnameID, resource.VersionUndefined) - - okToDestroy, err = r.Teardown(ctx, md) - if err != nil && !state.IsNotFoundError(err) { - return fmt.Errorf("error cleaning up specs: %w", err) + final = *spec.TypedSpec() } - if okToDestroy { - if err = r.Destroy(ctx, md); err != nil { - return fmt.Errorf("error cleaning up specs: %w", err) + if final.Hostname != "" { + return map[resource.ID]*network.HostnameSpecSpec{ + network.HostnameID: &final, } } - } - r.ResetRestartBackoff() - } + return nil + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/hostname_merge_test.go b/internal/app/machined/pkg/controllers/network/hostname_merge_test.go index 846118b2aa..e770e9cd79 100644 --- a/internal/app/machined/pkg/controllers/network/hostname_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/hostname_merge_test.go @@ -46,7 +46,7 @@ func (suite *HostnameMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.HostnameMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewHostnameMergeController())) suite.startRuntime() } diff --git a/internal/app/machined/pkg/controllers/network/link_merge.go b/internal/app/machined/pkg/controllers/network/link_merge.go index ff660f1619..9b673a9622 100644 --- a/internal/app/machined/pkg/controllers/network/link_merge.go +++ b/internal/app/machined/pkg/controllers/network/link_merge.go @@ -7,141 +7,47 @@ package network import ( "cmp" - "context" - "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// LinkMergeController merges network.LinkSpec in network.ConfigNamespace and produces final network.LinkSpec in network.Namespace. -type LinkMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *LinkMergeController) Name() string { - return "network.LinkMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *LinkMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.LinkSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.LinkSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *LinkMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.LinkSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewLinkMergeController initializes a LinkMergeController. // -//nolint:gocyclo -func (ctrl *LinkMergeController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := safe.ReaderList[*network.LinkSpec](ctx, r, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network routes: %w", err) - } - - // sort by link name, configuration layer - list.SortFunc(func(left, right *network.LinkSpec) int { - if res := cmp.Compare(left.TypedSpec().Name, right.TypedSpec().Name); res != 0 { - return res - } - - return cmp.Compare(left.TypedSpec().ConfigLayer, right.TypedSpec().ConfigLayer) - }) - - // build final link definition merging multiple layers - links := make(map[string]*network.LinkSpecSpec, list.Len()) - - for link := range list.All() { - id := network.LinkID(link.TypedSpec().Name) - - existing, ok := links[id] - if !ok { - links[id] = link.TypedSpec() - } else if err = existing.Merge(link.TypedSpec()); err != nil { - logger.Warn("error merging links", zap.Error(err)) - } - } - - conflictsDetected := 0 - - for id, link := range links { - if err = safe.WriterModify(ctx, r, network.NewLinkSpec(network.NamespaceName, id), func(l *network.LinkSpec) error { - *l.TypedSpec() = *link - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // phase conflict, resource is being torn down, skip updating it and trigger reconcile - // later by failing the - conflictsDetected++ - - delete(links, id) - } else { - return fmt.Errorf("error updating resource: %w", err) +// LinkMergeController merges network.LinkSpec in network.ConfigNamespace and produces final network.AddressSpec in network.Namespace. +func NewLinkMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.LinkSpec]) map[resource.ID]*network.LinkSpecSpec { + // sort by link name, configuration layer + list.SortFunc(func(left, right *network.LinkSpec) int { + if res := cmp.Compare(left.TypedSpec().Name, right.TypedSpec().Name); res != 0 { + return res } - } - - logger.Debug("merged link spec", zap.String("id", id), zap.Any("spec", link)) - } - // list link for cleanup - list, err = safe.ReaderList[*network.LinkSpec](ctx, r, resource.NewMetadata(network.NamespaceName, network.LinkSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing resources: %w", err) - } + return cmp.Compare(left.TypedSpec().ConfigLayer, right.TypedSpec().ConfigLayer) + }) - for res := range list.All() { - if _, ok := links[res.Metadata().ID()]; !ok { - var okToDestroy bool + // build final link definition merging multiple layers + links := make(map[string]*network.LinkSpecSpec, list.Len()) - okToDestroy, err = r.Teardown(ctx, res.Metadata()) - if err != nil { - return fmt.Errorf("error cleaning up addresses: %w", err) - } + for link := range list.All() { + id := network.LinkID(link.TypedSpec().Name) - if okToDestroy { - if err = r.Destroy(ctx, res.Metadata()); err != nil { - return fmt.Errorf("error cleaning up addresses: %w", err) - } + existing, ok := links[id] + if !ok { + links[id] = link.TypedSpec() + } else if err := existing.Merge(link.TypedSpec()); err != nil { + logger.Warn("error merging links", zap.Error(err)) } } - } - - if conflictsDetected > 0 { - return fmt.Errorf("%d conflict(s) detected", conflictsDetected) - } - r.ResetRestartBackoff() - } + return links + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/link_merge_test.go b/internal/app/machined/pkg/controllers/network/link_merge_test.go index 124174f032..bf5141689e 100644 --- a/internal/app/machined/pkg/controllers/network/link_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/link_merge_test.go @@ -49,7 +49,7 @@ func (suite *LinkMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.LinkMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewLinkMergeController())) suite.startRuntime() } @@ -334,6 +334,10 @@ func (suite *LinkMergeSuite) TestMergeWireguard() { asrt.Equal(1234, r.TypedSpec().Wireguard.ListenPort) asrt.Len(r.TypedSpec().Wireguard.Peers, 2) + if len(r.TypedSpec().Wireguard.Peers) != 2 { + return + } + asrt.Equal( network.WireguardPeer{ PublicKey: "RXdQkMTD1Jcxd/Wizr9k8syw8ANs57l5jTormDVHAVs=", diff --git a/internal/app/machined/pkg/controllers/network/operator_merge.go b/internal/app/machined/pkg/controllers/network/operator_merge.go index 4b2dadd270..fb402b417f 100644 --- a/internal/app/machined/pkg/controllers/network/operator_merge.go +++ b/internal/app/machined/pkg/controllers/network/operator_merge.go @@ -2,138 +2,41 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Package network provides controllers which manage network resources. -// -//nolint:dupl package network import ( - "context" - "fmt" - "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// OperatorMergeController merges network.OperatorSpec in network.ConfigNamespace and produces final network.OperatorSpec in network.Namespace. -type OperatorMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *OperatorMergeController) Name() string { - return "network.OperatorMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *OperatorMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.OperatorSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.OperatorSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *OperatorMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.OperatorSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewOperatorMergeController initializes a OperatorMergeController. // -//nolint:gocyclo -func (ctrl *OperatorMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.OperatorSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network operators: %w", err) - } - - // operator is allowed as long as it's not duplicate, for duplicate higher layer takes precedence - operators := map[string]*network.OperatorSpec{} - - for _, res := range list.Items { - operator := res.(*network.OperatorSpec) //nolint:forcetypeassert - id := network.OperatorID(operator.TypedSpec().Operator, operator.TypedSpec().LinkName) - - existing, ok := operators[id] - if ok && existing.TypedSpec().ConfigLayer > operator.TypedSpec().ConfigLayer { - // skip this operator, as existing one is higher layer - continue - } - - operators[id] = operator - } - - conflictsDetected := 0 - - for id, operator := range operators { - if err = safe.WriterModify(ctx, r, network.NewOperatorSpec(network.NamespaceName, id), func(op *network.OperatorSpec) error { - *op.TypedSpec() = *operator.TypedSpec() - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // phase conflict, resource is being torn down, skip updating it and trigger reconcile - // later by failing the loop after all processing is done - conflictsDetected++ - - delete(operators, id) - } else { - return fmt.Errorf("error updating resource: %w", err) - } - } - } - - // list operators for cleanup - list, err = r.List(ctx, resource.NewMetadata(network.NamespaceName, network.OperatorSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing resources: %w", err) - } - - for _, res := range list.Items { - if _, ok := operators[res.Metadata().ID()]; !ok { - var okToDestroy bool - - okToDestroy, err = r.Teardown(ctx, res.Metadata()) - if err != nil { - return fmt.Errorf("error cleaning up operators: %w", err) +// OperatorMergeController merges network.OperatorSpec in network.ConfigNamespace and produces final network.OperatorSpec in network.Namespace. +func NewOperatorMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.OperatorSpec]) map[resource.ID]*network.OperatorSpecSpec { + // operator is allowed as long as it's not duplicate, for duplicate higher layer takes precedence + operators := map[string]*network.OperatorSpecSpec{} + + for operator := range list.All() { + id := network.OperatorID(operator.TypedSpec().Operator, operator.TypedSpec().LinkName) + + existing, ok := operators[id] + if ok && existing.ConfigLayer > operator.TypedSpec().ConfigLayer { + // skip this operator, as existing one is higher layer + continue } - if okToDestroy { - if err = r.Destroy(ctx, res.Metadata()); err != nil { - return fmt.Errorf("error cleaning up operators: %w", err) - } - } + operators[id] = operator.TypedSpec() } - } - if conflictsDetected > 0 { - return fmt.Errorf("%d conflict(s) detected", conflictsDetected) - } - - r.ResetRestartBackoff() - } + return operators + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/operator_merge_test.go b/internal/app/machined/pkg/controllers/network/operator_merge_test.go index 0133154f13..dd0db29219 100644 --- a/internal/app/machined/pkg/controllers/network/operator_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/operator_merge_test.go @@ -47,7 +47,7 @@ func (suite *OperatorMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.OperatorMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewOperatorMergeController())) suite.startRuntime() } diff --git a/internal/app/machined/pkg/controllers/network/resolver_merge.go b/internal/app/machined/pkg/controllers/network/resolver_merge.go index 4f49796d50..cf61cd34b7 100644 --- a/internal/app/machined/pkg/controllers/network/resolver_merge.go +++ b/internal/app/machined/pkg/controllers/network/resolver_merge.go @@ -7,132 +7,58 @@ package network import ( "cmp" - "context" - "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// ResolverMergeController merges network.ResolverSpec in network.ConfigNamespace and produces final network.ResolverSpec in network.Namespace. -type ResolverMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *ResolverMergeController) Name() string { - return "network.ResolverMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *ResolverMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.ResolverSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.ResolverSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *ResolverMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.ResolverSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewResolverMergeController initializes a ResolverMergeController. // -//nolint:gocyclo -func (ctrl *ResolverMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := safe.ReaderList[*network.ResolverSpec](ctx, r, resource.NewMetadata(network.ConfigNamespaceName, network.ResolverSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network addresses: %w", err) - } - - // sort by config layer - list.SortFunc(func(l, r *network.ResolverSpec) int { - return cmp.Compare(l.TypedSpec().ConfigLayer, r.TypedSpec().ConfigLayer) - }) - - // simply merge by layers, overriding with the next configuration layer - var final network.ResolverSpecSpec - - for res := range list.All() { - spec := res.TypedSpec() - - final.SearchDomains = slices.Insert(final.SearchDomains, 0, spec.SearchDomains...) - - if spec.ConfigLayer == final.ConfigLayer { - // simply append server lists on the same layer - final.DNSServers = append(final.DNSServers, spec.DNSServers...) - } else { - // otherwise, do a smart merge across IPv4/IPv6 - final.ConfigLayer = spec.ConfigLayer - mergeDNSServers(&final.DNSServers, spec.DNSServers) - } - } - - if final.DNSServers != nil { - if err = safe.WriterModify(ctx, r, network.NewResolverSpec(network.NamespaceName, network.ResolverID), func(spec *network.ResolverSpec) error { - *spec.TypedSpec() = final - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // conflict - final.DNSServers = nil - - r.QueueReconcile() +// ResolverMergeController merges network.ResolverSpec in network.ConfigNamespace and produces final network.ResolverSpec in network.Namespace. +func NewResolverMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.ResolverSpec]) map[resource.ID]*network.ResolverSpecSpec { + // sort by config layer + list.SortFunc(func(l, r *network.ResolverSpec) int { + return cmp.Compare(l.TypedSpec().ConfigLayer, r.TypedSpec().ConfigLayer) + }) + + // simply merge by layers, overriding with the next configuration layer + var final network.ResolverSpecSpec + + for res := range list.All() { + spec := res.TypedSpec() + + final.SearchDomains = slices.Insert(final.SearchDomains, 0, spec.SearchDomains...) + + if spec.ConfigLayer == final.ConfigLayer { + // simply append server lists on the same layer + final.DNSServers = append(final.DNSServers, spec.DNSServers...) } else { - return fmt.Errorf("error updating resource: %w", err) + // otherwise, do a smart merge across IPv4/IPv6 + final.ConfigLayer = spec.ConfigLayer + mergeDNSServers(&final.DNSServers, spec.DNSServers) } } - } - - if final.DNSServers == nil { - // remove existing - var okToDestroy bool - - md := resource.NewMetadata(network.NamespaceName, network.ResolverSpecType, network.ResolverID, resource.VersionUndefined) - - okToDestroy, err = r.Teardown(ctx, md) - if err != nil && !state.IsNotFoundError(err) { - return fmt.Errorf("error cleaning up specs: %w", err) - } - if okToDestroy { - if err = r.Destroy(ctx, md); err != nil { - return fmt.Errorf("error cleaning up specs: %w", err) + if final.DNSServers != nil { + return map[resource.ID]*network.ResolverSpecSpec{ + network.ResolverID: &final, } } - } - r.ResetRestartBackoff() - } + return nil + }, + ) } func mergeDNSServers(dst *[]netip.Addr, src []netip.Addr) { diff --git a/internal/app/machined/pkg/controllers/network/resolver_merge_test.go b/internal/app/machined/pkg/controllers/network/resolver_merge_test.go index 88144a7166..0b85737e4e 100644 --- a/internal/app/machined/pkg/controllers/network/resolver_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/resolver_merge_test.go @@ -49,7 +49,7 @@ func (suite *ResolverMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.ResolverMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewResolverMergeController())) suite.startRuntime() } diff --git a/internal/app/machined/pkg/controllers/network/route_merge.go b/internal/app/machined/pkg/controllers/network/route_merge.go index 9aeb852c02..ec14c84ab6 100644 --- a/internal/app/machined/pkg/controllers/network/route_merge.go +++ b/internal/app/machined/pkg/controllers/network/route_merge.go @@ -2,136 +2,41 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -// Package network provides controllers which manage network resources. package network import ( - "context" - "fmt" - "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// RouteMergeController merges network.RouteSpec in network.ConfigNamespace and produces final network.RouteSpec in network.Namespace. -type RouteMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *RouteMergeController) Name() string { - return "network.RouteMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *RouteMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.RouteSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.RouteSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *RouteMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.RouteSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewRouteMergeController initializes a RouteMergeController. // -//nolint:gocyclo -func (ctrl *RouteMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.RouteSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network routes: %w", err) - } - - // route is allowed as long as it's not duplicate, for duplicate higher layer takes precedence - routes := map[string]*network.RouteSpec{} - - for _, res := range list.Items { - route := res.(*network.RouteSpec) //nolint:forcetypeassert - id := network.RouteID(route.TypedSpec().Table, route.TypedSpec().Family, route.TypedSpec().Destination, route.TypedSpec().Gateway, route.TypedSpec().Priority, route.TypedSpec().OutLinkName) - - existing, ok := routes[id] - if ok && existing.TypedSpec().ConfigLayer > route.TypedSpec().ConfigLayer { - // skip this route, as existing one is higher layer - continue - } - - routes[id] = route - } - - conflictsDetected := 0 - - for id, route := range routes { - if err = safe.WriterModify(ctx, r, network.NewRouteSpec(network.NamespaceName, id), func(rt *network.RouteSpec) error { - *rt.TypedSpec() = *route.TypedSpec() - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // phase conflict, resource is being torn down, skip updating it and trigger reconcile - // later by failing the - conflictsDetected++ - - delete(routes, id) - } else { - return fmt.Errorf("error updating resource: %w", err) - } - } - } - - // list routes for cleanup - list, err = r.List(ctx, resource.NewMetadata(network.NamespaceName, network.RouteSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing resources: %w", err) - } - - for _, res := range list.Items { - if _, ok := routes[res.Metadata().ID()]; !ok { - var okToDestroy bool - - okToDestroy, err = r.Teardown(ctx, res.Metadata()) - if err != nil { - return fmt.Errorf("error cleaning up routes: %w", err) +// RouteMergeController merges network.RouteSpec in network.ConfigNamespace and produces final network.RouteSpec in network.Namespace. +func NewRouteMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.RouteSpec]) map[resource.ID]*network.RouteSpecSpec { + // route is allowed as long as it's not duplicate, for duplicate higher layer takes precedence + routes := map[string]*network.RouteSpecSpec{} + + for route := range list.All() { + id := network.RouteID(route.TypedSpec().Table, route.TypedSpec().Family, route.TypedSpec().Destination, route.TypedSpec().Gateway, route.TypedSpec().Priority, route.TypedSpec().OutLinkName) + + existing, ok := routes[id] + if ok && existing.ConfigLayer > route.TypedSpec().ConfigLayer { + // skip this route, as existing one is higher layer + continue } - if okToDestroy { - if err = r.Destroy(ctx, res.Metadata()); err != nil { - return fmt.Errorf("error cleaning up routes: %w", err) - } - } + routes[id] = route.TypedSpec() } - } - - if conflictsDetected > 0 { - return fmt.Errorf("%d conflict(s) detected", conflictsDetected) - } - r.ResetRestartBackoff() - } + return routes + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/route_merge_test.go b/internal/app/machined/pkg/controllers/network/route_merge_test.go index 970abead3b..e3f0bb4df6 100644 --- a/internal/app/machined/pkg/controllers/network/route_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/route_merge_test.go @@ -49,7 +49,7 @@ func (suite *RouteMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.RouteMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewRouteMergeController())) suite.startRuntime() } diff --git a/internal/app/machined/pkg/controllers/network/timeserver_merge.go b/internal/app/machined/pkg/controllers/network/timeserver_merge.go index 6c0b825772..fb38b51223 100644 --- a/internal/app/machined/pkg/controllers/network/timeserver_merge.go +++ b/internal/app/machined/pkg/controllers/network/timeserver_merge.go @@ -6,124 +6,47 @@ package network import ( - "context" - "fmt" - "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" - "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) -// TimeServerMergeController merges network.TimeServerSpec in network.ConfigNamespace and produces final network.TimeServerSpec in network.Namespace. -type TimeServerMergeController struct{} - -// Name implements controller.Controller interface. -func (ctrl *TimeServerMergeController) Name() string { - return "network.TimeServerMergeController" -} - -// Inputs implements controller.Controller interface. -func (ctrl *TimeServerMergeController) Inputs() []controller.Input { - return []controller.Input{ - { - Namespace: network.ConfigNamespaceName, - Type: network.TimeServerSpecType, - Kind: controller.InputWeak, - }, - { - Namespace: network.NamespaceName, - Type: network.TimeServerSpecType, - Kind: controller.InputDestroyReady, - }, - } -} - -// Outputs implements controller.Controller interface. -func (ctrl *TimeServerMergeController) Outputs() []controller.Output { - return []controller.Output{ - { - Type: network.TimeServerSpecType, - Kind: controller.OutputShared, - }, - } -} - -// Run implements controller.Controller interface. +// NewTimeServerMergeController initializes a TimeServerMergeController. // -//nolint:gocyclo -func (ctrl *TimeServerMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { - for { - select { - case <-ctx.Done(): - return nil - case <-r.EventCh(): - } - - // list source network configuration resources - list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.TimeServerSpecType, "", resource.VersionUndefined)) - if err != nil { - return fmt.Errorf("error listing source network addresses: %w", err) - } - - // simply merge by layers, overriding with the next configuration layer - var final network.TimeServerSpecSpec - - for _, res := range list.Items { - spec := res.(*network.TimeServerSpec) //nolint:forcetypeassert - - if final.NTPServers != nil && spec.TypedSpec().ConfigLayer < final.ConfigLayer { - // skip this spec, as existing one is higher layer - continue - } - - if spec.TypedSpec().ConfigLayer == final.ConfigLayer { - // merge server lists on the same level - final.NTPServers = append(final.NTPServers, spec.TypedSpec().NTPServers...) - } else { - // otherwise, replace the lists - final = *spec.TypedSpec() - } - } - - if final.NTPServers != nil { - if err = safe.WriterModify(ctx, r, network.NewTimeServerSpec(network.NamespaceName, network.TimeServerID), func(spec *network.TimeServerSpec) error { - *spec.TypedSpec() = final - - return nil - }); err != nil { - if state.IsPhaseConflictError(err) { - // conflict - final.NTPServers = nil +// TimeServerMergeController merges network.TimeServerSpec in network.ConfigNamespace and produces final network.TimeServerSpec in network.Namespace. +func NewTimeServerMergeController() controller.Controller { + return GenericMergeController( + network.ConfigNamespaceName, + network.NamespaceName, + func(logger *zap.Logger, list safe.List[*network.TimeServerSpec]) map[resource.ID]*network.TimeServerSpecSpec { + // simply merge by layers, overriding with the next configuration layer + var final network.TimeServerSpecSpec + + for spec := range list.All() { + if final.NTPServers != nil && spec.TypedSpec().ConfigLayer < final.ConfigLayer { + // skip this spec, as existing one is higher layer + continue + } - r.QueueReconcile() + if spec.TypedSpec().ConfigLayer == final.ConfigLayer { + // merge server lists on the same level + final.NTPServers = append(final.NTPServers, spec.TypedSpec().NTPServers...) } else { - return fmt.Errorf("error updating resource: %w", err) + // otherwise, replace the lists + final = *spec.TypedSpec() } } - } - - if final.NTPServers == nil { - // remove existing - var okToDestroy bool - md := resource.NewMetadata(network.NamespaceName, network.TimeServerSpecType, network.TimeServerID, resource.VersionUndefined) - - okToDestroy, err = r.Teardown(ctx, md) - if err != nil && !state.IsNotFoundError(err) { - return fmt.Errorf("error cleaning up specs: %w", err) - } - - if okToDestroy { - if err = r.Destroy(ctx, md); err != nil { - return fmt.Errorf("error cleaning up specs: %w", err) + if final.NTPServers != nil { + return map[resource.ID]*network.TimeServerSpecSpec{ + network.TimeServerID: &final, } } - } - r.ResetRestartBackoff() - } + return nil + }, + ) } diff --git a/internal/app/machined/pkg/controllers/network/timeserver_merge_test.go b/internal/app/machined/pkg/controllers/network/timeserver_merge_test.go index f1470662ca..17d2d033a7 100644 --- a/internal/app/machined/pkg/controllers/network/timeserver_merge_test.go +++ b/internal/app/machined/pkg/controllers/network/timeserver_merge_test.go @@ -47,7 +47,7 @@ func (suite *TimeServerMergeSuite) SetupTest() { suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) - suite.Require().NoError(suite.runtime.RegisterController(&netctrl.TimeServerMergeController{})) + suite.Require().NoError(suite.runtime.RegisterController(netctrl.NewTimeServerMergeController())) suite.startRuntime() } diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index 08ebbaa092..c0ca3c76e1 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -224,7 +224,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &network.AddressEventController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), }, - &network.AddressMergeController{}, + network.NewAddressMergeController(), &network.AddressSpecController{}, &network.AddressStatusController{}, &network.DeviceConfigController{}, @@ -242,14 +242,14 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &network.HostnameConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.HostnameMergeController{}, + network.NewHostnameMergeController(), &network.HostnameSpecController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &network.LinkConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.LinkMergeController{}, + network.NewLinkMergeController(), &network.LinkSpecController{}, &network.LinkStatusController{}, &network.NfTablesChainConfigController{}, @@ -259,7 +259,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &network.OperatorConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.OperatorMergeController{}, + network.NewOperatorMergeController(), &network.OperatorSpecController{ V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(), State: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), @@ -275,12 +275,12 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &network.ResolverConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.ResolverMergeController{}, + network.NewResolverMergeController(), &network.ResolverSpecController{}, &network.RouteConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.RouteMergeController{}, + network.NewRouteMergeController(), &network.RouteSpecController{}, &network.RouteStatusController{}, &network.StatusController{ @@ -289,7 +289,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &network.TimeServerConfigController{ Cmdline: procfs.ProcCmdline(), }, - &network.TimeServerMergeController{}, + network.NewTimeServerMergeController(), &network.TimeServerSpecController{}, &perf.StatsController{}, cri.NewRegistriesConfigController(),