@@ -11,6 +11,7 @@ import (
11
11
"testing"
12
12
"time"
13
13
14
+ "github.com/stretchr/testify/mock"
14
15
"github.com/stretchr/testify/require"
15
16
16
17
"github.com/elastic/elastic-agent/internal/pkg/agent/application/coordinator"
@@ -68,7 +69,11 @@ func (u *mockUpgradeManager) Upgrade(
68
69
pgpBytes ... )
69
70
}
70
71
71
- func (u * mockUpgradeManager ) Ack (ctx context.Context , acker acker.Acker ) error {
72
+ func (u * mockUpgradeManager ) Ack (_ context.Context , _ acker.Acker ) error {
73
+ return nil
74
+ }
75
+
76
+ func (u * mockUpgradeManager ) AckAction (_ context.Context , _ acker.Acker , _ fleetapi.Action ) error {
72
77
return nil
73
78
}
74
79
@@ -110,7 +115,7 @@ func TestUpgradeHandler(t *testing.T) {
110
115
return nil , nil
111
116
},
112
117
},
113
- nil , nil , nil , nil , nil , false , nil )
118
+ nil , nil , nil , nil , nil , false , nil , nil )
114
119
//nolint:errcheck // We don't need the termination state of the Coordinator
115
120
go c .Run (ctx )
116
121
@@ -169,7 +174,7 @@ func TestUpgradeHandlerSameVersion(t *testing.T) {
169
174
return nil , err
170
175
},
171
176
},
172
- nil , nil , nil , nil , nil , false , nil )
177
+ nil , nil , nil , nil , nil , false , nil , nil )
173
178
//nolint:errcheck // We don't need the termination state of the Coordinator
174
179
go c .Run (ctx )
175
180
@@ -190,6 +195,98 @@ func TestUpgradeHandlerSameVersion(t *testing.T) {
190
195
}
191
196
}
192
197
198
+ func TestDuplicateActionsHandled (t * testing.T ) {
199
+ // Create a cancellable context that will shut down the coordinator after
200
+ // the test.
201
+ ctx , cancel := context .WithCancel (context .Background ())
202
+ defer cancel ()
203
+
204
+ log , _ := logger .New ("" , false )
205
+ upgradeCalledChan := make (chan string )
206
+
207
+ agentInfo := & info.AgentInfo {}
208
+ acker := & fakeAcker {}
209
+
210
+ // Create and start the Coordinator
211
+ c := coordinator .New (
212
+ log ,
213
+ configuration .DefaultConfiguration (),
214
+ logger .DefaultLogLevel ,
215
+ agentInfo ,
216
+ component.RuntimeSpecs {},
217
+ nil ,
218
+ & mockUpgradeManager {
219
+ UpgradeFn : func (
220
+ ctx context.Context ,
221
+ version string ,
222
+ sourceURI string ,
223
+ action * fleetapi.ActionUpgrade ,
224
+ details * details.Details ,
225
+ skipVerifyOverride bool ,
226
+ skipDefaultPgp bool ,
227
+ pgpBytes ... string ) (reexec.ShutdownCallbackFn , error ) {
228
+
229
+ defer func () {
230
+ upgradeCalledChan <- action .ActionID
231
+ }()
232
+
233
+ return nil , nil
234
+ },
235
+ },
236
+ nil , nil , nil , nil , nil , false , nil , acker )
237
+ //nolint:errcheck // We don't need the termination state of the Coordinator
238
+ go c .Run (ctx )
239
+
240
+ u := NewUpgrade (log , c )
241
+ a1 := fleetapi.ActionUpgrade {
242
+ ActionID : "action-8.5-1" ,
243
+ Data : fleetapi.ActionUpgradeData {
244
+ Version : "8.5.0" , SourceURI : "http://localhost" ,
245
+ },
246
+ }
247
+ a2 := fleetapi.ActionUpgrade {
248
+ ActionID : "action-8.5-2" ,
249
+ Data : fleetapi.ActionUpgradeData {
250
+ Version : "8.5.0" , SourceURI : "http://localhost" ,
251
+ },
252
+ }
253
+
254
+ checkMsg := func (c <- chan string , expected , errMsg string ) error {
255
+ t .Helper ()
256
+ // Make sure this test does not dead lock or wait for too long
257
+ // For some reason < 1s sometimes makes the test fail.
258
+ select {
259
+ case <- time .Tick (1500 * time .Millisecond ):
260
+ return errors .New ("timed out waiting for Upgrade to return" )
261
+ case msg := <- c :
262
+ require .Equal (t , expected , msg , errMsg )
263
+ }
264
+
265
+ return nil
266
+ }
267
+
268
+ acker .On ("Ack" , mock .Anything , mock .Anything ).Return (nil )
269
+ acker .On ("Commit" , mock .Anything ).Return (nil )
270
+
271
+ t .Log ("First upgrade action should be processed" )
272
+ require .NoError (t , u .Handle (ctx , & a1 , acker ))
273
+ require .Nil (t , checkMsg (upgradeCalledChan , a1 .ActionID , "action was not processed" ))
274
+ c .ClearOverrideState () // it's upgrading, normally we would restart
275
+
276
+ t .Log ("Action with different ID but same version should not be propagated to upgrader but acked" )
277
+ require .NoError (t , u .Handle (ctx , & a2 , acker ))
278
+ require .NotNil (t , checkMsg (upgradeCalledChan , a2 .ActionID , "action was not processed" ))
279
+ acker .AssertCalled (t , "Ack" , ctx , & a2 )
280
+ acker .AssertCalled (t , "Commit" , ctx )
281
+
282
+ c .ClearOverrideState () // it's upgrading, normally we would restart
283
+
284
+ t .Log ("Resending action with same ID should be skipped" )
285
+ require .NoError (t , u .Handle (ctx , & a1 , acker ))
286
+ require .NotNil (t , checkMsg (upgradeCalledChan , a1 .ActionID , "action was not processed" ))
287
+ acker .AssertNotCalled (t , "Ack" , ctx , & a1 )
288
+ }
289
+
193
290
func TestUpgradeHandlerNewVersion (t * testing.T ) {
194
291
// Create a cancellable context that will shut down the coordinator after
195
292
// the test.
@@ -230,15 +327,23 @@ func TestUpgradeHandlerNewVersion(t *testing.T) {
230
327
return nil , nil
231
328
},
232
329
},
233
- nil , nil , nil , nil , nil , false , nil )
330
+ nil , nil , nil , nil , nil , false , nil , nil )
234
331
//nolint:errcheck // We don't need the termination state of the Coordinator
235
332
go c .Run (ctx )
236
333
237
334
u := NewUpgrade (log , c )
238
- a1 := fleetapi.ActionUpgrade {Data : fleetapi.ActionUpgradeData {
239
- Version : "8.2.0" , SourceURI : "http://localhost" }}
240
- a2 := fleetapi.ActionUpgrade {Data : fleetapi.ActionUpgradeData {
241
- Version : "8.5.0" , SourceURI : "http://localhost" }}
335
+ a1 := fleetapi.ActionUpgrade {
336
+ ActionID : "action-8.2" ,
337
+ Data : fleetapi.ActionUpgradeData {
338
+ Version : "8.2.0" , SourceURI : "http://localhost" ,
339
+ },
340
+ }
341
+ a2 := fleetapi.ActionUpgrade {
342
+ ActionID : "action-8.5" ,
343
+ Data : fleetapi.ActionUpgradeData {
344
+ Version : "8.5.0" , SourceURI : "http://localhost" ,
345
+ },
346
+ }
242
347
ack := noopacker .New ()
243
348
244
349
checkMsg := func (c <- chan string , expected , errMsg string ) {
@@ -262,3 +367,17 @@ func TestUpgradeHandlerNewVersion(t *testing.T) {
262
367
require .NoError (t , err2 )
263
368
checkMsg (upgradeCalledChan , "8.5.0" , "second call to Upgrade must be with version 8.5.0" )
264
369
}
370
+
371
+ type fakeAcker struct {
372
+ mock.Mock
373
+ }
374
+
375
+ func (f * fakeAcker ) Ack (ctx context.Context , action fleetapi.Action ) error {
376
+ args := f .Called (ctx , action )
377
+ return args .Error (0 )
378
+ }
379
+
380
+ func (f * fakeAcker ) Commit (ctx context.Context ) error {
381
+ args := f .Called (ctx )
382
+ return args .Error (0 )
383
+ }
0 commit comments