refactor: task 'updateBootLoader' as controller
Fixes #7232 Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
parent
e7be6ee7c3
commit
f5e3272fce
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
})
|
||||
}
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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(),
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user