refactor: task 'updateBootLoader' as controller

Fixes #7232

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2023-06-09 00:00:46 +04:00
parent e7be6ee7c3
commit f5e3272fce
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
6 changed files with 197 additions and 28 deletions

View File

@ -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()

View File

@ -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
}
}

View File

@ -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
})
}

View File

@ -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

View File

@ -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) {

View File

@ -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(),
},