@@ -44,7 +44,7 @@ agent.upgrade.watcher:
4444const fastWatcherCfgWithRollbackWindow = `
4545agent.upgrade:
4646 watcher:
47- grace_period: 2m
47+ grace_period: 1m
4848 error_check.interval: 5s
4949 rollback:
5050 window: 10m
@@ -338,6 +338,8 @@ func TestFleetManagedUpgradeRollbackOnRestarts(t *testing.T) {
338338 }
339339}
340340
341+ type rollbackTriggerFunc func (ctx context.Context , t * testing.T , client client.Client , startFixture , endFixture * atesting.Fixture )
342+
341343// TestStandaloneUpgradeManualRollback tests the scenario where, after upgrading to a new version
342344// of Agent, a manual rollback is triggered. It checks that the Agent is rolled back to the previous version.
343345func TestStandaloneUpgradeManualRollback (t * testing.T ) {
@@ -348,12 +350,16 @@ func TestStandaloneUpgradeManualRollback(t *testing.T) {
348350 })
349351
350352 type fixturesSetupFunc func (t * testing.T ) (from * atesting.Fixture , to * atesting.Fixture )
353+
351354 testcases := []struct {
352- name string
353- fixturesSetup fixturesSetupFunc
355+ name string
356+ fixturesSetup fixturesSetupFunc
357+ agentConfig string
358+ rollbackTrigger rollbackTriggerFunc
354359 }{
355360 {
356- name : "upgrade to a repackaged agent built from the same commit" ,
361+ name : "upgrade to a repackaged agent built from the same commit, rollback during grace period" ,
362+ agentConfig : fastWatcherCfgWithRollbackWindow ,
357363 fixturesSetup : func (t * testing.T ) (from * atesting.Fixture , to * atesting.Fixture ) {
358364 // Upgrade from the current build to the same build as Independent Agent Release.
359365
@@ -389,23 +395,96 @@ func TestStandaloneUpgradeManualRollback(t *testing.T) {
389395
390396 return fromFixture , toFixture
391397 },
398+ rollbackTrigger : func (ctx context.Context , t * testing.T , client client.Client , startFixture , endFixture * atesting.Fixture ) {
399+ t .Logf ("sending version=%s rollback=%v upgrade to agent" , startFixture .Version (), true )
400+ retVal , err := client .Upgrade (ctx , startFixture .Version (), true , "" , false , false )
401+ require .NoError (t , err , "error triggering manual rollback to version %s" , startFixture .Version ())
402+ t .Logf ("received output %s from upgrade command" , retVal )
403+ },
392404 },
393- }
405+ {
406+ name : "upgrade to a repackaged agent built from the same commit, rollback after grace period" ,
407+ agentConfig : fastWatcherCfgWithRollbackWindow ,
408+ fixturesSetup : func (t * testing.T ) (from * atesting.Fixture , to * atesting.Fixture ) {
409+ // Upgrade from the current build to the same build as Independent Agent Release.
394410
395- // set up start ficture with a rollback window of 1h
396- rollbackWindowConfig := `
397- agent.upgrade.rollback.window: 1h
398- `
411+ // Start from the build under test.
412+ fromFixture , err := define .NewFixtureFromLocalBuild (t , define .Version ())
413+ require .NoError (t , err )
414+
415+ // Create a new package with a different version (IAR-style)
416+ // modify the version with the "+buildYYYYMMDDHHMMSS"
417+ currentVersion , err := version .ParseVersion (define .Version ())
418+ require .NoErrorf (t , err , "define.Version() %q is not parsable." , define .Version ())
419+
420+ newVersionBuildMetadata := "build" + time .Now ().Format ("20060102150405" )
421+ parsedNewVersion := version .NewParsedSemVer (currentVersion .Major (), currentVersion .Minor (), currentVersion .Patch (), "" , newVersionBuildMetadata )
422+
423+ err = fromFixture .EnsurePrepared (t .Context ())
424+ require .NoErrorf (t , err , "fixture should be prepared" )
425+
426+ // retrieve the compressed package file location
427+ srcPackage , err := fromFixture .SrcPackage (t .Context ())
428+ require .NoErrorf (t , err , "error retrieving start fixture source package" )
429+
430+ versionForFixture , repackagedArchiveFile , err := repackageArchive (t , srcPackage , newVersionBuildMetadata , currentVersion , parsedNewVersion )
431+ require .NoError (t , err , "error repackaging the archive built from the same commit" )
432+
433+ repackagedLocalFetcher := atesting .LocalFetcher (filepath .Dir (repackagedArchiveFile ))
434+ toFixture , err := atesting .NewFixture (
435+ t ,
436+ versionForFixture .String (),
437+ atesting .WithFetcher (repackagedLocalFetcher ),
438+ )
439+ require .NoError (t , err )
440+
441+ return fromFixture , toFixture
442+ },
443+ rollbackTrigger : func (ctx context.Context , t * testing.T , client client.Client , startFixture , endFixture * atesting.Fixture ) {
444+ // trim -SNAPSHOT at the end of the fixture version as that is reported as a separate flag
445+ expectedVersion := endFixture .Version ()
446+ expectedSnapshot := false
447+ if strings .HasSuffix (expectedVersion , "-SNAPSHOT" ) {
448+ expectedVersion = strings .TrimSuffix (endFixture .Version (), "-SNAPSHOT" )
449+ expectedSnapshot = true
450+ }
451+
452+ // It will take at least 2 minutes before the agent exits the grace period (see fastWatcherCfgWithRollbackWindow)
453+ // let's shoot for up to 4 minutes to exit grace period, checking every 10 seconds
454+ require .EventuallyWithT (t , func (collect * assert.CollectT ) {
455+ state , err := client .State (ctx )
456+ require .NoError (collect , err )
457+ t .Logf ("checking agent state: %+v" , state )
458+ require .NotNil (collect , state )
459+ assert .Nil (collect , state .UpgradeDetails )
460+ assert .Equal (t , cproto .State_HEALTHY , state .State )
461+ assert .Equal (collect , expectedVersion , state .Info .Version )
462+ assert .Equal (collect , expectedSnapshot , state .Info .Snapshot )
463+ if runtime .GOOS != "windows" {
464+ // on windows the update marker is not removed when cleaning up
465+ assert .NoFileExists (collect , filepath .Join (startFixture .WorkDir (), "data" , ".update-marker" ))
466+ }
467+ }, 4 * time .Minute , 10 * time .Second )
468+ t .Log ("elastic agent is out of grace period." )
469+ t .Logf ("sending version=%s rollback=%v upgrade to agent" , startFixture .Version (), true )
470+ retVal , err := client .Upgrade (ctx , startFixture .Version (), true , "" , false , false )
471+ require .NoError (t , err , "error triggering manual rollback to version %s" , startFixture .Version ())
472+ t .Logf ("received output %s from upgrade command" , retVal )
473+ },
474+ },
475+ }
399476
400477 for _ , tc := range testcases {
401478 t .Run (tc .name , func (t * testing.T ) {
402479 ctx , cancel := testcontext .WithDeadline (t , t .Context (), time .Now ().Add (10 * time .Minute ))
403480 defer cancel ()
404481 from , to := tc .fixturesSetup (t )
405482
406- err := from .Configure (ctx , []byte (rollbackWindowConfig ))
407- require .NoError (t , err , "error setting up rollback window" )
408- standaloneManualRollbackTest (ctx , t , from , to )
483+ err := from .Configure (ctx , []byte (tc .agentConfig ))
484+ require .NoError (t , err , "error configuring starting fixture" )
485+ standaloneRollbackTest (
486+ ctx , t , from , to , fastWatcherCfgWithRollbackWindow , fmt .Sprintf (details .ReasonManualRollbackPattern , from .Version ()),
487+ tc .rollbackTrigger )
409488 })
410489 }
411490
@@ -490,23 +569,12 @@ func managedRollbackRestartTest(ctx context.Context, t *testing.T, info *define.
490569
491570func standaloneRollbackRestartTest (ctx context.Context , t * testing.T , startFixture * atesting.Fixture , endFixture * atesting.Fixture ) {
492571 standaloneRollbackTest (ctx , t , startFixture , endFixture , reallyFastWatcherCfg , details .ReasonWatchFailed ,
493- func (t * testing.T , _ client.Client ) {
572+ func (ctx context. Context , t * testing.T , _ client.Client , _ * atesting. Fixture , _ * atesting. Fixture ) {
494573 restartAgentNTimes (t , 3 , 10 * time .Second )
495574 })
496575}
497576
498- func standaloneManualRollbackTest (ctx context.Context , t * testing.T , startFixture * atesting.Fixture , endFixture * atesting.Fixture ) {
499- standaloneRollbackTest (ctx , t , startFixture , endFixture , fastWatcherCfgWithRollbackWindow , fmt .Sprintf (details .ReasonManualRollbackPattern , startFixture .Version ()),
500- func (t * testing.T , client client.Client ) {
501- t .Logf ("sending version=%s rollback=%v upgrade to agent" , startFixture .Version (), true )
502- retVal , err := client .Upgrade (ctx , startFixture .Version (), true , "" , false , false )
503- require .NoError (t , err , "error triggering manual rollback to version %s" , startFixture .Version ())
504- t .Logf ("received output %s from upgrade command" , retVal )
505- },
506- )
507- }
508-
509- func standaloneRollbackTest (ctx context.Context , t * testing.T , startFixture * atesting.Fixture , endFixture * atesting.Fixture , customConfig string , rollbackReason string , rollbackTrigger func (t * testing.T , client client.Client )) {
577+ func standaloneRollbackTest (ctx context.Context , t * testing.T , startFixture * atesting.Fixture , endFixture * atesting.Fixture , customConfig string , rollbackReason string , rollbackTrigger rollbackTriggerFunc ) {
510578
511579 startVersionInfo , err := startFixture .ExecVersion (ctx )
512580 require .NoError (t , err , "failed to get start agent build version info" )
@@ -539,7 +607,7 @@ func standaloneRollbackTest(ctx context.Context, t *testing.T, startFixture *ate
539607 defer elasticAgentClient .Disconnect ()
540608
541609 // A few seconds after the upgrade, trigger a rollback using the passed trigger
542- rollbackTrigger (t , elasticAgentClient )
610+ rollbackTrigger (ctx , t , elasticAgentClient , startFixture , endFixture )
543611
544612 // wait for the agent to be healthy and back at the start version
545613 err = upgradetest .WaitHealthyAndVersion (ctx , startFixture , startVersionInfo .Binary , 2 * time .Minute , 10 * time .Second , t )
0 commit comments