Skip to content

Commit 193954b

Browse files
authored
Fix cleanup manual rollback (#11236)
* Fix error handling during cleanup * Fix versionedHomes to always be relative paths * Exit watcher when finished rolling back * update watchCmd unit tests to user versioned homes relative to topDir * fixup! update watchCmd unit tests to user versioned homes relative to topDir * fixup! Fix versionedHomes to always be relative paths * Fix versionedHomesToKeep handling in watcher cleanup
1 parent f5ed777 commit 193954b

File tree

4 files changed

+67
-30
lines changed

4 files changed

+67
-30
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ func rollbackUsingAgentInstalls(log *logger.Logger, watcherHelper WatcherHelper,
102102
return "", "", fmt.Errorf("version %q not listed among the available rollbacks: %w", rollbackVersion, ErrNoRollbacksAvailable)
103103
}
104104

105+
if filepath.IsAbs(targetInstall) {
106+
targetInstall, err = filepath.Rel(topDir, targetInstall)
107+
if err != nil {
108+
return "", "", fmt.Errorf("error calculating path of install %q relative to %q: %w", targetInstall, topDir, err)
109+
}
110+
}
111+
105112
prevAgentParsedVersion, err := version.ParseVersion(targetTTLMarker.Version)
106113
if err != nil {
107114
return "", "", fmt.Errorf("parsing version of target install %+v: %w", targetInstall, err)

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,22 @@ func cleanup(log *logger.Logger, topDirPath string, removeMarker, keepLogs bool,
174174

175175
dirPrefix := fmt.Sprintf("%s-", AgentName)
176176

177-
relativeHomePaths := make([]string, len(versionedHomesToKeep))
178-
for i, h := range versionedHomesToKeep {
179-
relHomePath, err := filepath.Rel("data", h)
177+
log.Infof("versioned homes to keep: %v", versionedHomesToKeep)
178+
179+
var cumulativeError error
180+
relativeHomePaths := make([]string, 0, len(versionedHomesToKeep))
181+
for _, h := range versionedHomesToKeep {
182+
relHomePath, err := filepath.Rel(dataDirPath, filepath.Join(topDirPath, h))
180183
if err != nil {
181-
return fmt.Errorf("extracting elastic-agent path relative to data directory from %s: %w", h, err)
184+
cumulativeError = goerrors.Join(cumulativeError, fmt.Errorf("extracting elastic-agent path relative to data directory from %s: %w", h, err))
185+
// best effort: try to use the entry as-is, without calculating the path relative to `data`
186+
relHomePath = h
182187
}
183-
relativeHomePaths[i] = relHomePath
188+
relativeHomePaths = append(relativeHomePaths, relHomePath)
184189
}
185190

186-
var errs []error
191+
log.Infof("Starting cleanup of versioned homes. Keeping: %v", relativeHomePaths)
192+
187193
for _, dir := range subdirs {
188194
if slices.Contains(relativeHomePaths, dir) {
189195
continue
@@ -200,11 +206,11 @@ func cleanup(log *logger.Logger, topDirPath string, removeMarker, keepLogs bool,
200206
ignoredDirs = append(ignoredDirs, "logs")
201207
}
202208
if cleanupErr := install.RemoveBut(hashedDir, true, ignoredDirs...); cleanupErr != nil {
203-
errs = append(errs, cleanupErr)
209+
cumulativeError = goerrors.Join(cumulativeError, cleanupErr)
204210
}
205211
}
206212

207-
return goerrors.Join(errs...)
213+
return cumulativeError
208214
}
209215

210216
// InvokeWatcher invokes an agent instance using watcher argument for watching behavior of

internal/pkg/agent/cmd/watch.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func newWatchCommandWithArgs(_ []string, streams *cli.IOStreams) *cobra.Command
102102
fmt.Fprintf(streams.Err, "Rollback command failed: %v\n", err)
103103
os.Exit(errorRollbackFailed)
104104
}
105+
return
105106
}
106107

107108
if err = withAppLocker(log, func() error {
@@ -200,7 +201,19 @@ func watchCmd(log *logp.Logger, topDir string, cfg *configuration.UpgradeWatcher
200201
// hash is the same as hash of agent which initiated watcher.
201202
versionedHomesToKeep := make([]string, 0, len(marker.RollbacksAvailable)+1)
202203
// current version needs to be kept
203-
versionedHomesToKeep = append(versionedHomesToKeep, paths.VersionedHome(topDir))
204+
if marker.Details != nil && marker.Details.State == details.StateRollback {
205+
// we need to keep the previous versioned home (we have rolled back)
206+
versionedHomesToKeep = append(versionedHomesToKeep, marker.PrevVersionedHome)
207+
} else {
208+
// we need to keep the upgraded version, since it has not been rolled back
209+
absCurrentVersionedHome := paths.VersionedHome(topDir)
210+
currentVersionedHome, err := filepath.Rel(topDir, absCurrentVersionedHome)
211+
if err != nil {
212+
return fmt.Errorf("extracting current home path %q relative to %q: %w", absCurrentVersionedHome, topDir, err)
213+
}
214+
versionedHomesToKeep = append(versionedHomesToKeep, currentVersionedHome)
215+
}
216+
204217
versionedHomesToKeep = appendAvailableRollbacks(log, marker, versionedHomesToKeep)
205218
log.Infof("About to clean up upgrade. Keeping versioned homes: %v", versionedHomesToKeep)
206219
if err := installModifier.Cleanup(log, paths.Top(), true, false, versionedHomesToKeep...); err != nil {
@@ -320,6 +333,14 @@ func rollback(log *logp.Logger, topDir string, client client.Client, installModi
320333
// FIXME get the hash from the list of installs or the manifest or the versioned home
321334
// This is only a placeholder in case there is no versionedHome defined (which we always have)
322335
hash := ""
336+
if filepath.IsAbs(versionedHome) {
337+
// if the versioned home is an absolute path we need to normalize it relative to the current topDir as the
338+
// cleanup() will expect relative paths
339+
versionedHome, err = filepath.Rel(topDir, versionedHome)
340+
if err != nil {
341+
return fmt.Errorf("extract from %q a path relative to %q: %w", versionedHome, topDir, err)
342+
}
343+
}
323344
err = installModifier.Rollback(context.Background(), log, client, topDir, versionedHome, hash, WithPreRestartHook(updateMarkerAndDetails))
324345
if err != nil {
325346
return fmt.Errorf("rolling back: %w", err)

internal/pkg/agent/cmd/watch_test.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,11 @@ func Test_watchCmd(t *testing.T) {
122122
&upgrade.UpdateMarker{
123123
Version: "4.5.6",
124124
Hash: "newver",
125-
VersionedHome: "elastic-agent-4.5.6-newver",
125+
VersionedHome: filepath.Join("data", "elastic-agent-4.5.6-newver"),
126126
UpdatedOn: time.Now(),
127127
PrevVersion: "1.2.3",
128128
PrevHash: "prvver",
129-
PrevVersionedHome: "elastic-agent-prvver",
129+
PrevVersionedHome: filepath.Join("data", "elastic-agent-prvver"),
130130
Acked: false,
131131
Action: nil,
132132
Details: nil, //details.NewDetails("4.5.6", details.StateReplacing, ""),
@@ -143,7 +143,7 @@ func Test_watchCmd(t *testing.T) {
143143
expectedRemoveMarkerFlag := runtime.GOOS != "windows"
144144

145145
installModifier.EXPECT().
146-
Cleanup(mock.Anything, topDir, expectedRemoveMarkerFlag, false, "elastic-agent-4.5.6-newver").
146+
Cleanup(mock.Anything, topDir, expectedRemoveMarkerFlag, false, filepath.Join("data", "elastic-agent-4.5.6-newver")).
147147
Return(nil)
148148
},
149149
args: args{
@@ -157,16 +157,17 @@ func Test_watchCmd(t *testing.T) {
157157
dataDirPath := paths.DataFrom(topDir)
158158
err := os.MkdirAll(dataDirPath, 0755)
159159
require.NoError(t, err)
160+
previousVersionedHome := filepath.Join("data", "elastic-agent-9.2.0-prvver")
160161
err = upgrade.SaveMarker(
161162
dataDirPath,
162163
&upgrade.UpdateMarker{
163164
Version: "9.3.0",
164165
Hash: "newver",
165-
VersionedHome: "elastic-agent-9.3.0-newver",
166+
VersionedHome: filepath.Join("data", "elastic-agent-9.3.0-newver"),
166167
UpdatedOn: time.Now(),
167168
PrevVersion: "9.2.0",
168169
PrevHash: "prvver",
169-
PrevVersionedHome: "elastic-agent-9.2.0-prvver",
170+
PrevVersionedHome: previousVersionedHome,
170171
Acked: false,
171172
Action: nil,
172173
Details: nil,
@@ -179,7 +180,7 @@ func Test_watchCmd(t *testing.T) {
179180
Watch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
180181
Return(errors.New("some watch error due to agent misbehaving"))
181182
installModifier.EXPECT().
182-
Rollback(mock.Anything, mock.Anything, mock.Anything, paths.Top(), "elastic-agent-9.2.0-prvver", "prvver", mock.MatchedBy(func(opt upgrade.RollbackOption) bool {
183+
Rollback(mock.Anything, mock.Anything, mock.Anything, paths.Top(), previousVersionedHome, "prvver", mock.MatchedBy(func(opt upgrade.RollbackOption) bool {
183184
settings := upgrade.NewRollbackSettings()
184185
opt(settings)
185186

@@ -198,16 +199,17 @@ func Test_watchCmd(t *testing.T) {
198199
dataDirPath := paths.DataFrom(topDir)
199200
err := os.MkdirAll(dataDirPath, 0755)
200201
require.NoError(t, err)
202+
previousVersionedHome := filepath.Join("data", "elastic-agent-1.2.3-prvver")
201203
err = upgrade.SaveMarker(
202204
dataDirPath,
203205
&upgrade.UpdateMarker{
204206
Version: "4.5.6",
205207
Hash: "newver",
206-
VersionedHome: "elastic-agent-4.5.6-newver",
208+
VersionedHome: filepath.Join("data", "elastic-agent-4.5.6-newver"),
207209
UpdatedOn: time.Now(),
208210
PrevVersion: "1.2.3",
209211
PrevHash: "prvver",
210-
PrevVersionedHome: "elastic-agent-1.2.3-prvver",
212+
PrevVersionedHome: previousVersionedHome,
211213
Acked: false,
212214
Action: nil,
213215
Details: nil, //details.NewDetails("4.5.6", details.StateReplacing, ""),
@@ -225,7 +227,7 @@ func Test_watchCmd(t *testing.T) {
225227
mock.Anything,
226228
mock.Anything,
227229
paths.Top(),
228-
"elastic-agent-1.2.3-prvver",
230+
previousVersionedHome,
229231
"prvver",
230232
mock.MatchedBy(func(opt upgrade.RollbackOption) bool {
231233
settings := upgrade.NewRollbackSettings()
@@ -245,16 +247,17 @@ func Test_watchCmd(t *testing.T) {
245247
dataDirPath := paths.DataFrom(topDir)
246248
err := os.MkdirAll(dataDirPath, 0755)
247249
require.NoError(t, err)
250+
previousVersionedHome := filepath.Join("data", "elastic-agent-prvver")
248251
err = upgrade.SaveMarker(
249252
dataDirPath,
250253
&upgrade.UpdateMarker{
251254
Version: "4.5.6",
252255
Hash: "newver",
253-
VersionedHome: "elastic-agent-4.5.6-newver",
256+
VersionedHome: filepath.Join("data", "elastic-agent-4.5.6-newver"),
254257
UpdatedOn: time.Now(),
255258
PrevVersion: "1.2.3",
256259
PrevHash: "prvver",
257-
PrevVersionedHome: "elastic-agent-prvver",
260+
PrevVersionedHome: previousVersionedHome,
258261
Acked: false,
259262
Action: nil,
260263
Details: &details.Details{
@@ -271,7 +274,7 @@ func Test_watchCmd(t *testing.T) {
271274
// topdir, prevVersionedHome and prevHash are not taken from the upgrade marker, otherwise they would be
272275
// <topDir, "topDir/data/elastic-agent-prvver", "prvver">
273276
installModifier.EXPECT().
274-
Cleanup(mock.Anything, paths.Top(), true, false, paths.VersionedHome(topDir)).
277+
Cleanup(mock.Anything, paths.Top(), true, false, previousVersionedHome).
275278
Return(nil)
276279
},
277280
args: args{
@@ -291,11 +294,11 @@ func Test_watchCmd(t *testing.T) {
291294
&upgrade.UpdateMarker{
292295
Version: "4.5.6",
293296
Hash: "newver",
294-
VersionedHome: "elastic-agent-4.5.6-newver",
297+
VersionedHome: filepath.Join("data", "elastic-agent-4.5.6-newver"),
295298
UpdatedOn: updatedOn,
296299
PrevVersion: "1.2.3",
297300
PrevHash: "prvver",
298-
PrevVersionedHome: "elastic-agent-prvver",
301+
PrevVersionedHome: filepath.Join("data", "elastic-agent-prvver"),
299302
Acked: false,
300303
Action: nil,
301304
Details: nil,
@@ -304,10 +307,10 @@ func Test_watchCmd(t *testing.T) {
304307
)
305308
require.NoError(t, err)
306309

307-
// topdir, prevVersionedHome and prevHash are not taken from the upgrade marker, otherwise they would be
308-
// <topDir, "topDir/data/elastic-agent-prvver", "prvver">
310+
// topdir, versionedHome and hash are not taken from the upgrade marker, otherwise they would be
311+
// <topDir, "data/elastic-agent-4.5.6-newver">
309312
installModifier.EXPECT().
310-
Cleanup(mock.Anything, paths.Top(), true, false, paths.VersionedHome(topDir)).
313+
Cleanup(mock.Anything, paths.Top(), true, false, filepath.Join("data", "elastic-agent-unknow")).
311314
Return(nil)
312315
},
313316
args: args{
@@ -321,7 +324,7 @@ func Test_watchCmd(t *testing.T) {
321324
wantErr: assert.NoError,
322325
},
323326
{
324-
name: "Desired outcome is rollback no upgrade details, no rollback and simple cleanup",
327+
name: "Default config, no rollback and simple cleanup",
325328
setupUpgradeMarker: func(t *testing.T, tmpDir string, watcher *mockAgentWatcher, installModifier *mockInstallationModifier) {
326329
dataDirPath := paths.DataFrom(tmpDir)
327330
err := os.MkdirAll(dataDirPath, 0755)
@@ -333,11 +336,11 @@ func Test_watchCmd(t *testing.T) {
333336
&upgrade.UpdateMarker{
334337
Version: "4.5.6",
335338
Hash: "newver",
336-
VersionedHome: "elastic-agent-4.5.6-newver",
339+
VersionedHome: filepath.Join("data", "elastic-agent-4.5.6-newver"),
337340
UpdatedOn: updatedOn,
338341
PrevVersion: "1.2.3",
339342
PrevHash: "prvver",
340-
PrevVersionedHome: "elastic-agent-prvver",
343+
PrevVersionedHome: filepath.Join("data", "elastic-agent-prvver"),
341344
Acked: false,
342345
Action: &fleetapi.ActionUpgrade{
343346
ActionID: "action-id",
@@ -351,7 +354,7 @@ func Test_watchCmd(t *testing.T) {
351354
require.NoError(t, err)
352355

353356
installModifier.EXPECT().
354-
Cleanup(mock.Anything, paths.Top(), true, false, paths.VersionedHome(tmpDir)).
357+
Cleanup(mock.Anything, paths.Top(), true, false, filepath.Join("data", "elastic-agent-unknow")).
355358
Return(nil)
356359
},
357360
args: args{

0 commit comments

Comments
 (0)