From f5e3272fce641a878eefa66437d28d3ed9917ab6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Jun 2023 00:00:46 +0400 Subject: [PATCH] refactor: task 'updateBootLoader' as controller Fixes #7232 Signed-off-by: Andrey Smirnov --- .../machined/pkg/controllers/ctest/ctest.go | 6 +- .../runtime/drop_upgrade_fallback.go | 94 ++++++++++++++++++ .../runtime/drop_upgrade_fallback_test.go | 98 +++++++++++++++++++ .../runtime/v1alpha1/v1alpha1_sequencer.go | 4 - .../v1alpha1/v1alpha1_sequencer_tasks.go | 20 ---- .../runtime/v1alpha2/v1alpha2_controller.go | 3 + 6 files changed, 197 insertions(+), 28 deletions(-) create mode 100644 internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback.go create mode 100644 internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback_test.go diff --git a/internal/app/machined/pkg/controllers/ctest/ctest.go b/internal/app/machined/pkg/controllers/ctest/ctest.go index cd3c1852c..19a1f55e6 100644 --- a/internal/app/machined/pkg/controllers/ctest/ctest.go +++ b/internal/app/machined/pkg/controllers/ctest/ctest.go @@ -7,7 +7,6 @@ package ctest import ( "context" - "log" "sync" "testing" "time" @@ -23,8 +22,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - - "github.com/siderolabs/talos/pkg/logging" + "go.uber.org/zap/zaptest" ) // DefaultSuite is a base suite for controller testing. @@ -56,7 +54,7 @@ func (suite *DefaultSuite) SetupTest() { var err error - suite.runtime, err = runtime.NewRuntime(suite.state, logging.Wrap(log.Writer())) + suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.startRuntime() diff --git a/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback.go b/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback.go new file mode 100644 index 000000000..aa6766de8 --- /dev/null +++ b/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback.go @@ -0,0 +1,94 @@ +// 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 runtime + +import ( + "context" + "fmt" + + "github.com/cosi-project/runtime/pkg/controller" + "github.com/cosi-project/runtime/pkg/safe" + "github.com/cosi-project/runtime/pkg/state" + "github.com/siderolabs/go-pointer" + "go.uber.org/zap" + + machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" + "github.com/siderolabs/talos/internal/pkg/meta" + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +// MetaProvider wraps acquiring meta. +type MetaProvider interface { + Meta() machineruntime.Meta +} + +// DropUpgradeFallbackController removes upgrade fallback key once machine reaches ready & running. +type DropUpgradeFallbackController struct { + MetaProvider MetaProvider +} + +// Name implements controller.Controller interface. +func (ctrl *DropUpgradeFallbackController) Name() string { + return "runtime.DropUpgradeFallbackController" +} + +// Inputs implements controller.Controller interface. +func (ctrl *DropUpgradeFallbackController) Inputs() []controller.Input { + return []controller.Input{ + { + Namespace: runtime.NamespaceName, + Type: runtime.MachineStatusType, + ID: pointer.To(runtime.MachineStatusID), + Kind: controller.InputWeak, + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *DropUpgradeFallbackController) Outputs() []controller.Output { + return nil +} + +// Run implements controller.Controller interface. +// +//nolint:gocyclo +func (ctrl *DropUpgradeFallbackController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { + for { + select { + case <-ctx.Done(): + return nil + case <-r.EventCh(): + } + + machineStatus, err := safe.ReaderGetByID[*runtime.MachineStatus](ctx, r, runtime.MachineStatusID) + if err != nil { + if state.IsNotFoundError(err) { + continue + } + + return fmt.Errorf("error getting machine status: %w", err) + } + + if !(machineStatus.TypedSpec().Stage == runtime.MachineStageRunning && machineStatus.TypedSpec().Status.Ready) { + continue + } + + ok, err := ctrl.MetaProvider.Meta().DeleteTag(ctx, meta.Upgrade) + if err != nil { + return err + } + + if ok { + logger.Info("removing fallback entry") + + if err = ctrl.MetaProvider.Meta().Flush(); err != nil { + return err + } + } + + // terminating the controller here, as removing fallback is required only once on boot after upgrade + return nil + } +} diff --git a/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback_test.go b/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback_test.go new file mode 100644 index 000000000..790c5ee29 --- /dev/null +++ b/internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback_test.go @@ -0,0 +1,98 @@ +// 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 runtime_test + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/cosi-project/runtime/pkg/state" + "github.com/cosi-project/runtime/pkg/state/impl/inmem" + "github.com/cosi-project/runtime/pkg/state/impl/namespaced" + "github.com/siderolabs/go-retry/retry" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" + "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" + machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" + "github.com/siderolabs/talos/internal/pkg/meta" + runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +type DropUpgradeFallbackControllerSuite struct { + ctest.DefaultSuite + + meta *meta.Meta +} + +type metaProvider struct { + meta *meta.Meta +} + +func (m metaProvider) Meta() machineruntime.Meta { + return m.meta +} + +func TestUpgradeFallbackControllerSuite(t *testing.T) { + tmpDir := t.TempDir() + + path := filepath.Join(tmpDir, "meta") + + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Truncate(1024*1024)) + require.NoError(t, f.Close()) + + st := state.WrapCore(namespaced.NewState(inmem.Build)) + + m, err := meta.New(context.Background(), st, meta.WithFixedPath(path)) + require.NoError(t, err) + + suite.Run(t, &DropUpgradeFallbackControllerSuite{ + meta: m, + DefaultSuite: ctest.DefaultSuite{ + AfterSetup: func(s *ctest.DefaultSuite) { + s.Require().NoError(s.Runtime().RegisterController(&runtime.DropUpgradeFallbackController{ + MetaProvider: metaProvider{meta: m}, + })) + }, + }, + }) +} + +func (suite *DropUpgradeFallbackControllerSuite) TestDropUpgradeFallback() { + _, err := suite.meta.SetTag(suite.Ctx(), meta.Upgrade, "A") + suite.Require().NoError(err) + + machineStatus := runtimeres.NewMachineStatus() + machineStatus.TypedSpec().Stage = runtimeres.MachineStageBooting + machineStatus.TypedSpec().Status.Ready = false + suite.Require().NoError(suite.State().Create(suite.Ctx(), machineStatus)) + + time.Sleep(time.Second) + + // controller should not remove the tag + val, ok := suite.meta.ReadTag(meta.Upgrade) + suite.Require().True(ok) + suite.Require().Equal("A", val) + + // update machine status to ready + machineStatus.TypedSpec().Status.Ready = true + machineStatus.TypedSpec().Stage = runtimeres.MachineStageRunning + suite.Require().NoError(suite.State().Update(suite.Ctx(), machineStatus)) + + suite.AssertWithin(time.Second, 10*time.Millisecond, func() error { + _, ok = suite.meta.ReadTag(meta.Upgrade) + if ok { + return retry.ExpectedErrorf("tag is still present") + } + + return nil + }) +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go index 387e1ecea..3040a838e 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go @@ -284,10 +284,6 @@ func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase { r.State().Platform().Mode() != runtime.ModeContainer && !r.Config().Machine().Kubelet().SkipNodeRegistration(), "uncordon", UncordonNode, - ).AppendWhen( - r.State().Platform().Mode() != runtime.ModeContainer, - "bootloader", - UpdateBootloader, ) return phases diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index ec8d0e98b..79ea9b659 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -1971,26 +1971,6 @@ func LabelNodeAsControlPlane(runtime.Sequence, any) (runtime.TaskExecutionFunc, }, "labelNodeAsControlPlane" } -// UpdateBootloader represents the UpdateBootloader task. -func UpdateBootloader(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - ok, err := r.State().Machine().Meta().DeleteTag(ctx, meta.Upgrade) - if err != nil { - return err - } - - if ok { - logger.Println("removing fallback") - - if err = r.State().Machine().Meta().Flush(); err != nil { - return err - } - } - - return nil - }, "updateBootloader" -} - // Reboot represents the Reboot task. func Reboot(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index 3879b7294..f1a7277be 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -217,6 +217,9 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &runtimecontrollers.DevicesStatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, + &runtimecontrollers.DropUpgradeFallbackController{ + MetaProvider: ctrl.v1alpha1Runtime.State().Machine(), + }, &runtimecontrollers.EventsSinkConfigController{ Cmdline: procfs.ProcCmdline(), },