Skip to content

Commit 2822486

Browse files
committed
Add a pre-restart hook to Rollback operation
1 parent dac5bb2 commit 2822486

File tree

8 files changed

+254
-199
lines changed

8 files changed

+254
-199
lines changed

.mockery.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ packages:
3030
interfaces:
3131
Agent:
3232
github.com/elastic/elastic-agent/internal/pkg/agent/cmd:
33+
config:
34+
inpackage: True
35+
with-expecter: True
36+
dir: "{{.InterfaceDirRelative}}"
37+
mockname: "{{.Mock}}{{.InterfaceName | firstUpper}}"
38+
outpkg: "{{.PackageName}}"
39+
filename: "{{.Mock | lower}}_{{.InterfaceName | lower}}_test.go"
3340
interfaces:
3441
agentWatcher:
35-
config:
36-
mockname: "AgentWatcher"
3742
installationModifier:
38-
config:
39-
mockname: "InstallationModifier"
4043
github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade:
4144
config:
4245
inpackage: True

internal/pkg/agent/application/upgrade/rollback.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,36 @@ const (
3535

3636
// Rollback rollbacks to previous version which was functioning before upgrade.
3737
func Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPath, prevVersionedHome, prevHash string) error {
38+
return RollbackWithOpts(ctx, log, c, topDirPath, prevVersionedHome, prevHash)
39+
}
40+
41+
var FatalRollbackError = errors.New("Fatal rollback error")
42+
43+
type RollbackHook func(ctx context.Context, log *logger.Logger, topDirPath string) error
44+
type rollbackSettings struct {
45+
preRestartHook RollbackHook
46+
}
47+
48+
func newRollbackSettings(opts ...RollbackOpt) *rollbackSettings {
49+
rs := new(rollbackSettings)
50+
for _, opt := range opts {
51+
opt(rs)
52+
}
53+
return rs
54+
}
55+
56+
type RollbackOpt func(*rollbackSettings)
57+
58+
func WithPreRestartHook(h RollbackHook) RollbackOpt {
59+
return func(s *rollbackSettings) {
60+
s.preRestartHook = h
61+
}
62+
}
63+
64+
func RollbackWithOpts(ctx context.Context, log *logger.Logger, c client.Client, topDirPath string, prevVersionedHome string, prevHash string, opts ...RollbackOpt) error {
65+
66+
settings := newRollbackSettings(opts...)
67+
3868
symlinkPath := filepath.Join(topDirPath, agentName)
3969

4070
var symlinkTarget string
@@ -56,6 +86,18 @@ func Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPa
5686
return err
5787
}
5888

89+
// Hook
90+
if settings.preRestartHook != nil {
91+
hookErr := settings.preRestartHook(ctx, log, topDirPath)
92+
if hookErr != nil {
93+
if errors.Is(hookErr, FatalRollbackError) {
94+
return fmt.Errorf("pre-restart hook failed: %w", hookErr)
95+
} else {
96+
log.Warnf("pre-restart hook failed: %v", hookErr)
97+
}
98+
}
99+
}
100+
59101
// Restart
60102
log.Info("Restarting the agent after rollback")
61103
if err := restartAgent(ctx, log, c); err != nil {

testing/mocks/internal_/pkg/agent/cmd/agent_watcher_mock.go renamed to internal/pkg/agent/cmd/mock_agentwatcher_test.go

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/pkg/agent/cmd/mock_installationmodifier_test.go

Lines changed: 147 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/pkg/agent/cmd/watch.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ type agentWatcher interface {
8686
Watch(ctx context.Context, tilGrace, errorCheckInterval time.Duration, log *logp.Logger) error
8787
}
8888

89+
type rollbackHook func(ctx context.Context, log *logger.Logger, topDirPath string) error
90+
8991
type installationModifier interface {
9092
Cleanup(log *logger.Logger, topDirPath, currentVersionedHome, currentHash string, removeMarker, keepLogs bool) error
91-
Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPath, prevVersionedHome, prevHash string) error
93+
Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPath, prevVersionedHome, prevHash string, preRestart rollbackHook) error
9294
}
9395

9496
func watchCmd(log *logp.Logger, topDir string, cfg *configuration.UpgradeWatcherConfig, watcher agentWatcher, installModifier installationModifier) error {
@@ -120,25 +122,29 @@ func watchCmd(log *logp.Logger, topDir string, cfg *configuration.UpgradeWatcher
120122
_ = locker.Unlock()
121123
}()
122124

123-
if marker.DesiredOutcome == upgrade.OUTCOME_ROLLBACK && marker.Details.State != details.StateRollback {
125+
if marker.DesiredOutcome == upgrade.OUTCOME_ROLLBACK && marker.Details != nil && marker.Details.State != details.StateRollback {
124126
// TODO: there should be some sanity check in rollback functions like the installation we are going back to should exist and work
125127
log.Infof("rolling back because of DesiredOutcome=%s", marker.DesiredOutcome.String())
126-
err = installModifier.Rollback(context.Background(), log, client.New(), paths.Top(), marker.PrevVersionedHome, marker.PrevHash)
127-
if err != nil {
128-
return fmt.Errorf("rolling back: %w", err)
129-
}
130128

131-
if marker.Details == nil {
132-
actionID := ""
133-
if marker.Action != nil {
134-
actionID = marker.Action.ActionID
129+
updateMarkerAndDetails := func(_ context.Context, _ *logger.Logger, _ string) error {
130+
if marker.Details == nil {
131+
actionID := ""
132+
if marker.Action != nil {
133+
actionID = marker.Action.ActionID
134+
}
135+
marker.Details = details.NewDetails(marker.Version, details.StateRollback, actionID)
135136
}
136-
marker.Details = details.NewDetails(marker.Version, details.StateRollback, actionID)
137+
marker.Details.SetStateWithReason(details.StateRollback, details.ReasonManualRollback)
138+
err = upgrade.SaveMarker(dataDir, marker, true)
139+
if err != nil {
140+
return fmt.Errorf("saving marker after rolling back: %w", err)
141+
}
142+
return nil
137143
}
138-
marker.Details.SetStateWithReason(details.StateRollback, details.ReasonManualRollback)
139-
err = upgrade.SaveMarker(dataDir, marker, true)
144+
145+
err = installModifier.Rollback(context.Background(), log, client.New(), paths.Top(), marker.PrevVersionedHome, marker.PrevHash, updateMarkerAndDetails)
140146
if err != nil {
141-
return fmt.Errorf("saving marker after rolling back: %w", err)
147+
return fmt.Errorf("rolling back: %w", err)
142148
}
143149

144150
return nil
@@ -180,7 +186,7 @@ func watchCmd(log *logp.Logger, topDir string, cfg *configuration.UpgradeWatcher
180186
log.Error("Error detected, proceeding to rollback: %v", err)
181187

182188
upgradeDetails.SetStateWithReason(details.StateRollback, details.ReasonWatchFailed)
183-
err = installModifier.Rollback(ctx, log, client.New(), paths.Top(), marker.PrevVersionedHome, marker.PrevHash)
189+
err = installModifier.Rollback(ctx, log, client.New(), paths.Top(), marker.PrevVersionedHome, marker.PrevHash, nil)
184190
if err != nil {
185191
log.Error("rollback failed", err)
186192
upgradeDetails.Fail(err)

internal/pkg/agent/cmd/watch_impl.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ func (a upgradeInstallationModifier) Cleanup(log *logger.Logger, topDirPath, cur
2929
return upgrade.Cleanup(log, topDirPath, currentVersionedHome, currentHash, removeMarker, keepLogs)
3030
}
3131

32-
func (a upgradeInstallationModifier) Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPath, prevVersionedHome, prevHash string) error {
33-
return upgrade.Rollback(ctx, log, c, topDirPath, prevVersionedHome, prevHash)
32+
func (a upgradeInstallationModifier) Rollback(ctx context.Context, log *logger.Logger, c client.Client, topDirPath, prevVersionedHome, prevHash string, preRestart rollbackHook) error {
33+
var opts []upgrade.RollbackOpt
34+
if preRestart != nil {
35+
opts = append(opts, upgrade.WithPreRestartHook(upgrade.RollbackHook(preRestart)))
36+
}
37+
return upgrade.RollbackWithOpts(ctx, log, c, topDirPath, prevVersionedHome, prevHash, opts...)
3438
}
3539

3640
func watch(ctx context.Context, tilGrace time.Duration, errorCheckInterval time.Duration, log *logger.Logger) error {

0 commit comments

Comments
 (0)