Skip to content

Commit 761f4b4

Browse files
pchilablakerouse
andauthored
Always launch more recent watcher (#4491)
* remove GetPreviousVersion() * implement correct prerelease comparison of parsed versions * WIP select newer watcher * changelog * Create a parsed semver when reading agent package version and use it for comparison * Apply suggestions from code review Co-authored-by: Blake Rouse <blake.rouse@elastic.co> * fix linter errors --------- Co-authored-by: Blake Rouse <blake.rouse@elastic.co>
1 parent 4be1461 commit 761f4b4

File tree

7 files changed

+397
-86
lines changed

7 files changed

+397
-86
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: bug-fix
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Always select the more recent watcher during agent upgrade/downgrade
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; a word indicating the component this changeset affects.
22+
component: elastic-agent
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/4491
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
#issue: https://github.com/owner/repo/1234

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

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
1717
"github.com/elastic/elastic-agent/internal/pkg/fleetapi"
1818
"github.com/elastic/elastic-agent/pkg/core/logger"
19+
"github.com/elastic/elastic-agent/pkg/version"
1920
)
2021

2122
const markerFilename = ".update-marker"
@@ -116,6 +117,7 @@ func newMarkerSerializer(m *UpdateMarker) *updateMarkerSerializer {
116117
}
117118

118119
type agentInstall struct {
120+
parsedVersion *version.ParsedSemVer
119121
version string
120122
hash string
121123
versionedHome string

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

+18-11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/elastic/elastic-agent/pkg/control/v2/cproto"
3737
"github.com/elastic/elastic-agent/pkg/core/logger"
3838
agtversion "github.com/elastic/elastic-agent/pkg/version"
39+
currentagtversion "github.com/elastic/elastic-agent/version"
3940
)
4041

4142
const (
@@ -272,12 +273,15 @@ func (u *Upgrader) Upgrade(ctx context.Context, version string, sourceURI string
272273
// while the `previous` install is the currently executing elastic-agent that is no longer reachable via the symlink.
273274
// After the restart at the end of the function, everything lines up correctly.
274275
current := agentInstall{
276+
parsedVersion: parsedVersion,
275277
version: version,
276278
hash: unpackRes.Hash,
277279
versionedHome: unpackRes.VersionedHome,
278280
}
279281

282+
previousParsedVersion := currentagtversion.GetParsedAgentPackageVersion()
280283
previous := agentInstall{
284+
parsedVersion: previousParsedVersion,
281285
version: release.VersionWithSnapshot(),
282286
hash: release.Commit(),
283287
versionedHome: currentVersionedHome,
@@ -293,15 +297,8 @@ func (u *Upgrader) Upgrade(ctx context.Context, version string, sourceURI string
293297
return nil, goerrors.Join(err, rollbackErr)
294298
}
295299

296-
minParsedVersionForNewUpdateMarker := agtversion.NewParsedSemVer(8, 13, 0, "", "")
297-
var watcherExecutable string
298-
if parsedVersion.Less(*minParsedVersionForNewUpdateMarker) {
299-
// use the current agent executable for watch, if downgrading the old agent doesn't understand the current agent's path structure.
300-
watcherExecutable = paths.BinaryPath(paths.VersionedHome(paths.Top()), agentName)
301-
} else {
302-
// use the new agent executable as it should be able to parse the new update marker
303-
watcherExecutable = paths.BinaryPath(filepath.Join(paths.Top(), unpackRes.VersionedHome), agentName)
304-
}
300+
var watcherExecutable = selectWatcherExecutable(paths.Top(), previous, current)
301+
305302
var watcherCmd *exec.Cmd
306303
if watcherCmd, err = InvokeWatcher(u.log, watcherExecutable); err != nil {
307304
u.log.Errorw("Rolling back: starting watcher failed", "error.message", err)
@@ -328,6 +325,17 @@ func (u *Upgrader) Upgrade(ctx context.Context, version string, sourceURI string
328325
return cb, nil
329326
}
330327

328+
func selectWatcherExecutable(topDir string, previous agentInstall, current agentInstall) string {
329+
// check if the upgraded version is less than the previous (currently installed) version
330+
if current.parsedVersion.Less(*previous.parsedVersion) {
331+
// use the current agent executable for watch, if downgrading the old agent doesn't understand the current agent's path structure.
332+
return paths.BinaryPath(filepath.Join(topDir, previous.versionedHome), agentName)
333+
} else {
334+
// use the new agent executable as it should be able to parse the new update marker
335+
return paths.BinaryPath(filepath.Join(topDir, current.versionedHome), agentName)
336+
}
337+
}
338+
331339
func waitForWatcher(ctx context.Context, log *logger.Logger, markerFilePath string, waitTime time.Duration) error {
332340
return waitForWatcherWithTimeoutCreationFunc(ctx, log, markerFilePath, waitTime, context.WithTimeout)
333341
}
@@ -417,8 +425,7 @@ func isSameVersion(log *logger.Logger, current agentVersion, metadata packageMet
417425
} else {
418426
// extract version info from the version string (we can ignore parsing errors as it would have never passed the download step)
419427
parsedVersion, _ := agtversion.ParseVersion(upgradeVersion)
420-
newVersion.version = strings.TrimSuffix(parsedVersion.VersionWithPrerelease(), snapshotSuffix)
421-
newVersion.snapshot = parsedVersion.IsSnapshot()
428+
newVersion.version, newVersion.snapshot = parsedVersion.ExtractSnapshotFromVersionString()
422429
}
423430
newVersion.hash = metadata.hash
424431

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

+80-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/elastic/elastic-agent-libs/transport/httpcommon"
2424
"github.com/elastic/elastic-agent-libs/transport/tlscommon"
25+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
2526
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact"
2627
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
2728
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
@@ -32,6 +33,7 @@ import (
3233
"github.com/elastic/elastic-agent/pkg/control/v2/client/mocks"
3334
"github.com/elastic/elastic-agent/pkg/control/v2/cproto"
3435
"github.com/elastic/elastic-agent/pkg/core/logger"
36+
agtversion "github.com/elastic/elastic-agent/pkg/version"
3537
)
3638

3739
func Test_CopyFile(t *testing.T) {
@@ -693,7 +695,7 @@ func TestIsSameVersion(t *testing.T) {
693695
manifest: nil,
694696
hash: "abcdef",
695697
},
696-
version: "1.2.3-repackaged-SNAPSHOT",
698+
version: "1.2.3-SNAPSHOT.repackaged",
697699
},
698700
want: want{
699701
same: false,
@@ -903,3 +905,80 @@ func writeState(t *testing.T, path string, state details.State) {
903905
assert.NoError(t, err, "error writing out the test upgrade marker")
904906
}
905907
}
908+
909+
func Test_selectWatcherExecutable(t *testing.T) {
910+
type args struct {
911+
previous agentInstall
912+
current agentInstall
913+
}
914+
tests := []struct {
915+
name string
916+
args args
917+
want string
918+
}{
919+
{
920+
name: "Simple upgrade, we should launch the new (current) watcher",
921+
args: args{
922+
previous: agentInstall{
923+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "", ""),
924+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-somehash"),
925+
},
926+
current: agentInstall{
927+
parsedVersion: agtversion.NewParsedSemVer(4, 5, 6, "", ""),
928+
versionedHome: filepath.Join("data", "elastic-agent-4.5.6-someotherhash"),
929+
},
930+
},
931+
want: filepath.Join("data", "elastic-agent-4.5.6-someotherhash"),
932+
},
933+
{
934+
name: "Simple downgrade, we should launch the currently installed (previous) watcher",
935+
args: args{
936+
previous: agentInstall{
937+
parsedVersion: agtversion.NewParsedSemVer(4, 5, 6, "", ""),
938+
versionedHome: filepath.Join("data", "elastic-agent-4.5.6-someotherhash"),
939+
},
940+
current: agentInstall{
941+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "", ""),
942+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-somehash"),
943+
},
944+
},
945+
want: filepath.Join("data", "elastic-agent-4.5.6-someotherhash"),
946+
},
947+
{
948+
name: "Upgrade from snapshot to released version, we should launch the new (current) watcher",
949+
args: args{
950+
previous: agentInstall{
951+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "SNAPSHOT", ""),
952+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-SNAPSHOT-somehash"),
953+
},
954+
current: agentInstall{
955+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "", ""),
956+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-someotherhash"),
957+
},
958+
},
959+
want: filepath.Join("data", "elastic-agent-1.2.3-someotherhash"),
960+
},
961+
{
962+
name: "Downgrade from released version to SNAPSHOT, we should launch the currently installed (previous) watcher",
963+
args: args{
964+
previous: agentInstall{
965+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "", ""),
966+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-somehash"),
967+
},
968+
current: agentInstall{
969+
parsedVersion: agtversion.NewParsedSemVer(1, 2, 3, "SNAPSHOT", ""),
970+
versionedHome: filepath.Join("data", "elastic-agent-1.2.3-SNAPSHOT-someotherhash"),
971+
},
972+
},
973+
974+
want: filepath.Join("data", "elastic-agent-1.2.3-somehash"),
975+
},
976+
}
977+
// Just need a top dir path. This test does not make any operation on the filesystem, so a temp dir path is as good as any
978+
fakeTopDir := filepath.Join(t.TempDir(), "Elastic", "Agent")
979+
for _, tt := range tests {
980+
t.Run(tt.name, func(t *testing.T) {
981+
assert.Equalf(t, paths.BinaryPath(filepath.Join(fakeTopDir, tt.want), agentName), selectWatcherExecutable(fakeTopDir, tt.args.previous, tt.args.current), "selectWatcherExecutable(%v, %v)", tt.args.previous, tt.args.current)
982+
})
983+
}
984+
}

0 commit comments

Comments
 (0)