| 
5 | 5 | package cmd  | 
6 | 6 | 
 
  | 
7 | 7 | import (  | 
 | 8 | +	"fmt"  | 
 | 9 | +	"os"  | 
 | 10 | +	"runtime"  | 
8 | 11 | 	"testing"  | 
 | 12 | +	"time"  | 
9 | 13 | 
 
  | 
 | 14 | +	"github.com/stretchr/testify/assert"  | 
 | 15 | +	"github.com/stretchr/testify/mock"  | 
10 | 16 | 	"go.uber.org/zap/zapcore"  | 
11 | 17 | 
 
  | 
 | 18 | +	"github.com/stretchr/testify/require"  | 
 | 19 | + | 
 | 20 | +	"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"  | 
12 | 21 | 	"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"  | 
 | 22 | +	"github.com/elastic/elastic-agent/internal/pkg/agent/configuration"  | 
13 | 23 | 	"github.com/elastic/elastic-agent/internal/pkg/agent/errors"  | 
14 | 24 | 	"github.com/elastic/elastic-agent/internal/pkg/fleetapi"  | 
 | 25 | +	"github.com/elastic/elastic-agent/internal/pkg/release"  | 
15 | 26 | 	"github.com/elastic/elastic-agent/pkg/core/logger/loggertest"  | 
16 | 27 | 
 
  | 
17 |  | -	"github.com/stretchr/testify/require"  | 
18 |  | - | 
19 | 28 | 	"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade"  | 
 | 29 | +	cmdmocks "github.com/elastic/elastic-agent/testing/mocks/internal_/pkg/agent/cmd"  | 
20 | 30 | )  | 
21 | 31 | 
 
  | 
22 | 32 | func TestInitUpgradeDetails(t *testing.T) {  | 
@@ -74,3 +84,202 @@ func TestInitUpgradeDetails(t *testing.T) {  | 
74 | 84 | 	require.Equal(t, zapcore.ErrorLevel, logs[0].Level)  | 
75 | 85 | 	require.Equal(t, `unable to save upgrade marker after clearing upgrade details: some error`, logs[0].Message)  | 
76 | 86 | }  | 
 | 87 | + | 
 | 88 | +func Test_watchCmd(t *testing.T) {  | 
 | 89 | +	type args struct {  | 
 | 90 | +		cfg *configuration.UpgradeWatcherConfig  | 
 | 91 | +	}  | 
 | 92 | +	tests := []struct {  | 
 | 93 | +		name               string  | 
 | 94 | +		setupUpgradeMarker func(t *testing.T, tmpDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier)  | 
 | 95 | +		args               args  | 
 | 96 | +		wantErr            assert.ErrorAssertionFunc  | 
 | 97 | +	}{  | 
 | 98 | +		{  | 
 | 99 | +			name: "no upgrade marker, no party",  | 
 | 100 | +			setupUpgradeMarker: func(t *testing.T, topDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier) {  | 
 | 101 | +				dataDirPath := paths.DataFrom(topDir)  | 
 | 102 | +				err := os.MkdirAll(dataDirPath, 0755)  | 
 | 103 | +				require.NoError(t, err)  | 
 | 104 | +			},  | 
 | 105 | +			args: args{  | 
 | 106 | +				cfg: configuration.DefaultUpgradeConfig().Watcher,  | 
 | 107 | +			},  | 
 | 108 | +			wantErr: assert.NoError,  | 
 | 109 | +		},  | 
 | 110 | +		{  | 
 | 111 | +			name: "happy path: no error watching, cleanup prev install",  | 
 | 112 | +			setupUpgradeMarker: func(t *testing.T, topDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier) {  | 
 | 113 | +				dataDirPath := paths.DataFrom(topDir)  | 
 | 114 | +				err := os.MkdirAll(dataDirPath, 0755)  | 
 | 115 | +				require.NoError(t, err)  | 
 | 116 | +				err = upgrade.SaveMarker(  | 
 | 117 | +					dataDirPath,  | 
 | 118 | +					&upgrade.UpdateMarker{  | 
 | 119 | +						Version:           "4.5.6",  | 
 | 120 | +						Hash:              "newver",  | 
 | 121 | +						VersionedHome:     "elastic-agent-4.5.6-newver",  | 
 | 122 | +						UpdatedOn:         time.Now(),  | 
 | 123 | +						PrevVersion:       "1.2.3",  | 
 | 124 | +						PrevHash:          "prvver",  | 
 | 125 | +						PrevVersionedHome: "elastic-agent-prvver",  | 
 | 126 | +						Acked:             false,  | 
 | 127 | +						Action:            nil,  | 
 | 128 | +						Details:           nil, //details.NewDetails("4.5.6", details.StateReplacing, ""),  | 
 | 129 | +						DesiredOutcome:    upgrade.OUTCOME_UPGRADE,  | 
 | 130 | +					},  | 
 | 131 | +					true,  | 
 | 132 | +				)  | 
 | 133 | +				require.NoError(t, err)  | 
 | 134 | + | 
 | 135 | +				watcher.EXPECT().  | 
 | 136 | +					Watch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).  | 
 | 137 | +					Return(nil)  | 
 | 138 | + | 
 | 139 | +				expectedRemoveMarkerFlag := true  | 
 | 140 | +				if runtime.GOOS == "windows" {  | 
 | 141 | +					// on windows the marker is not removed immediately to allow for cleanup on restart  | 
 | 142 | +					expectedRemoveMarkerFlag = false  | 
 | 143 | +				}  | 
 | 144 | +				installModifier.EXPECT().  | 
 | 145 | +					Cleanup(mock.Anything, topDir, "elastic-agent-4.5.6-newver", "newver", expectedRemoveMarkerFlag, false).  | 
 | 146 | +					Return(nil)  | 
 | 147 | +			},  | 
 | 148 | +			args: args{  | 
 | 149 | +				cfg: configuration.DefaultUpgradeConfig().Watcher,  | 
 | 150 | +			},  | 
 | 151 | +			wantErr: assert.NoError,  | 
 | 152 | +		},  | 
 | 153 | +		{  | 
 | 154 | +			name: "unhappy path: error watching, rollback to previous install",  | 
 | 155 | +			setupUpgradeMarker: func(t *testing.T, topDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier) {  | 
 | 156 | +				dataDirPath := paths.DataFrom(topDir)  | 
 | 157 | +				err := os.MkdirAll(dataDirPath, 0755)  | 
 | 158 | +				require.NoError(t, err)  | 
 | 159 | +				err = upgrade.SaveMarker(  | 
 | 160 | +					dataDirPath,  | 
 | 161 | +					&upgrade.UpdateMarker{  | 
 | 162 | +						Version:           "4.5.6",  | 
 | 163 | +						Hash:              "newver",  | 
 | 164 | +						VersionedHome:     "elastic-agent-4.5.6-newver",  | 
 | 165 | +						UpdatedOn:         time.Now(),  | 
 | 166 | +						PrevVersion:       "1.2.3",  | 
 | 167 | +						PrevHash:          "prvver",  | 
 | 168 | +						PrevVersionedHome: "elastic-agent-prvver",  | 
 | 169 | +						Acked:             false,  | 
 | 170 | +						Action:            nil,  | 
 | 171 | +						Details:           nil, //details.NewDetails("4.5.6", details.StateReplacing, ""),  | 
 | 172 | +						DesiredOutcome:    upgrade.OUTCOME_UPGRADE,  | 
 | 173 | +					},  | 
 | 174 | +					true,  | 
 | 175 | +				)  | 
 | 176 | +				require.NoError(t, err)  | 
 | 177 | + | 
 | 178 | +				watcher.EXPECT().  | 
 | 179 | +					Watch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).  | 
 | 180 | +					Return(errors.New("some watch error due to agent misbehaving"))  | 
 | 181 | +				installModifier.EXPECT().  | 
 | 182 | +					Rollback(mock.Anything, mock.Anything, mock.Anything, paths.Top(), "elastic-agent-prvver", "prvver").  | 
 | 183 | +					Return(nil)  | 
 | 184 | +			},  | 
 | 185 | +			args: args{  | 
 | 186 | +				cfg: configuration.DefaultUpgradeConfig().Watcher,  | 
 | 187 | +			},  | 
 | 188 | +			wantErr: assert.NoError,  | 
 | 189 | +		},  | 
 | 190 | +		{  | 
 | 191 | +			name: "upgrade rolled back: no watching, cleanup must be called",  | 
 | 192 | +			setupUpgradeMarker: func(t *testing.T, topDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier) {  | 
 | 193 | +				dataDirPath := paths.DataFrom(topDir)  | 
 | 194 | +				err := os.MkdirAll(dataDirPath, 0755)  | 
 | 195 | +				require.NoError(t, err)  | 
 | 196 | +				err = upgrade.SaveMarker(  | 
 | 197 | +					dataDirPath,  | 
 | 198 | +					&upgrade.UpdateMarker{  | 
 | 199 | +						Version:           "4.5.6",  | 
 | 200 | +						Hash:              "newver",  | 
 | 201 | +						VersionedHome:     "elastic-agent-4.5.6-newver",  | 
 | 202 | +						UpdatedOn:         time.Now(),  | 
 | 203 | +						PrevVersion:       "1.2.3",  | 
 | 204 | +						PrevHash:          "prvver",  | 
 | 205 | +						PrevVersionedHome: "elastic-agent-prvver",  | 
 | 206 | +						Acked:             false,  | 
 | 207 | +						Action:            nil,  | 
 | 208 | +						Details: &details.Details{  | 
 | 209 | +							TargetVersion: "4.5.6",  | 
 | 210 | +							State:         details.StateRollback,  | 
 | 211 | +							Metadata: details.Metadata{  | 
 | 212 | +								Reason: "automatic rollback",  | 
 | 213 | +							},  | 
 | 214 | +						},  | 
 | 215 | +						DesiredOutcome: upgrade.OUTCOME_UPGRADE,  | 
 | 216 | +					},  | 
 | 217 | +					true,  | 
 | 218 | +				)  | 
 | 219 | +				require.NoError(t, err)  | 
 | 220 | +				// topdir, prevVersionedHome and prevHash are not taken from the upgrade marker, otherwise they would be  | 
 | 221 | +				// <topDir, "topDir/data/elastic-agent-prvver", "prvver">  | 
 | 222 | +				installModifier.EXPECT().  | 
 | 223 | +					Cleanup(mock.Anything, paths.Top(), paths.VersionedHome(topDir), release.ShortCommit(), true, false).  | 
 | 224 | +					Return(nil)  | 
 | 225 | +			},  | 
 | 226 | +			args: args{  | 
 | 227 | +				cfg: configuration.DefaultUpgradeConfig().Watcher,  | 
 | 228 | +			},  | 
 | 229 | +			wantErr: assert.NoError,  | 
 | 230 | +		},  | 
 | 231 | +		{  | 
 | 232 | +			name: "after grace period: no watching, cleanup must be called",  | 
 | 233 | +			setupUpgradeMarker: func(t *testing.T, topDir string, watcher *cmdmocks.AgentWatcher, installModifier *cmdmocks.InstallationModifier) {  | 
 | 234 | +				dataDirPath := paths.DataFrom(topDir)  | 
 | 235 | +				err := os.MkdirAll(dataDirPath, 0755)  | 
 | 236 | +				require.NoError(t, err)  | 
 | 237 | +				updatedOn := time.Now().Add(-5 * time.Minute)  | 
 | 238 | +				err = upgrade.SaveMarker(  | 
 | 239 | +					dataDirPath,  | 
 | 240 | +					&upgrade.UpdateMarker{  | 
 | 241 | +						Version:           "4.5.6",  | 
 | 242 | +						Hash:              "newver",  | 
 | 243 | +						VersionedHome:     "elastic-agent-4.5.6-newver",  | 
 | 244 | +						UpdatedOn:         updatedOn,  | 
 | 245 | +						PrevVersion:       "1.2.3",  | 
 | 246 | +						PrevHash:          "prvver",  | 
 | 247 | +						PrevVersionedHome: "elastic-agent-prvver",  | 
 | 248 | +						Acked:             false,  | 
 | 249 | +						Action:            nil,  | 
 | 250 | +						Details:           nil,  | 
 | 251 | +						DesiredOutcome:    upgrade.OUTCOME_UPGRADE,  | 
 | 252 | +					},  | 
 | 253 | +					true,  | 
 | 254 | +				)  | 
 | 255 | +				require.NoError(t, err)  | 
 | 256 | + | 
 | 257 | +				// topdir, prevVersionedHome and prevHash are not taken from the upgrade marker, otherwise they would be  | 
 | 258 | +				// <topDir, "topDir/data/elastic-agent-prvver", "prvver">  | 
 | 259 | +				installModifier.EXPECT().  | 
 | 260 | +					Cleanup(mock.Anything, paths.Top(), paths.VersionedHome(topDir), release.ShortCommit(), true, false).  | 
 | 261 | +					Return(nil)  | 
 | 262 | +			},  | 
 | 263 | +			args: args{  | 
 | 264 | +				cfg: &configuration.UpgradeWatcherConfig{  | 
 | 265 | +					GracePeriod: 2 * time.Minute,  | 
 | 266 | +					ErrorCheck: configuration.UpgradeWatcherCheckConfig{  | 
 | 267 | +						Interval: time.Second,  | 
 | 268 | +					},  | 
 | 269 | +				},  | 
 | 270 | +			},  | 
 | 271 | +			wantErr: assert.NoError,  | 
 | 272 | +		},  | 
 | 273 | +	}  | 
 | 274 | +	for _, tt := range tests {  | 
 | 275 | +		t.Run(tt.name, func(t *testing.T) {  | 
 | 276 | +			log, obs := loggertest.New(t.Name())  | 
 | 277 | +			tmpDir := t.TempDir()  | 
 | 278 | +			mockWatcher := cmdmocks.NewAgentWatcher(t)  | 
 | 279 | +			mockInstallModifier := cmdmocks.NewInstallationModifier(t)  | 
 | 280 | +			tt.setupUpgradeMarker(t, tmpDir, mockWatcher, mockInstallModifier)  | 
 | 281 | +			tt.wantErr(t, watchCmd(log, tmpDir, tt.args.cfg, mockWatcher, mockInstallModifier), fmt.Sprintf("watchCmd(%v, ...)", tt.args.cfg))  | 
 | 282 | +			t.Logf("watchCmd logs:\n%v", obs.All())  | 
 | 283 | +		})  | 
 | 284 | +	}  | 
 | 285 | +}  | 
0 commit comments