5
5
package upgrade
6
6
7
7
import (
8
+ "bufio"
9
+ "context"
8
10
"fmt"
9
11
"os"
12
+ "os/exec"
10
13
"path/filepath"
14
+ "runtime"
15
+ "strings"
11
16
"testing"
12
17
"time"
13
18
@@ -299,11 +304,11 @@ func Test_markUpgradeLocking(t *testing.T) {
299
304
}{
300
305
{
301
306
name : "Lock file is created when writing update marker" , args : args {
302
- agent : newAgent456 ,
303
- previousAgent : prevAgent123 ,
304
- action : nil ,
305
- upgradeDetails : nil ,
306
- },
307
+ agent : newAgent456 ,
308
+ previousAgent : prevAgent123 ,
309
+ action : nil ,
310
+ upgradeDetails : nil ,
311
+ },
307
312
afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
308
313
assert .FileExists (t , markerFilePath (dataDir ), "Update marker file must exist" )
309
314
assert .FileExists (t , markerFilePath (dataDir )+ ".lock" , "Update marker lock file must exist" )
@@ -316,11 +321,11 @@ func Test_markUpgradeLocking(t *testing.T) {
316
321
},
317
322
{
318
323
name : "Update marker is re-lockable after writing" , args : args {
319
- agent : newAgent456 ,
320
- previousAgent : prevAgent123 ,
321
- action : nil ,
322
- upgradeDetails : nil ,
323
- },
324
+ agent : newAgent456 ,
325
+ previousAgent : prevAgent123 ,
326
+ action : nil ,
327
+ upgradeDetails : nil ,
328
+ },
324
329
afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
325
330
assert .FileExists (t , markerFilePath (dataDir ), "Update marker file must exist" )
326
331
assert .FileExists (t , markerFilePath (dataDir )+ ".lock" , "Update marker lock file must exist" )
@@ -335,11 +340,11 @@ func Test_markUpgradeLocking(t *testing.T) {
335
340
},
336
341
{
337
342
name : "Update marker creation should not fail if marker is already locked by the same process" , args : args {
338
- agent : newAgent456 ,
339
- previousAgent : prevAgent123 ,
340
- action : nil ,
341
- upgradeDetails : nil ,
342
- },
343
+ agent : newAgent456 ,
344
+ previousAgent : prevAgent123 ,
345
+ action : nil ,
346
+ upgradeDetails : nil ,
347
+ },
343
348
beforeUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
344
349
// write some fake data in update marker file
345
350
updateMarkerFilePath := markerFilePath (dataDir )
@@ -362,6 +367,62 @@ func Test_markUpgradeLocking(t *testing.T) {
362
367
},
363
368
wantErr : assert .NoError ,
364
369
},
370
+ {
371
+ name : "Update marker creation should fail if marker is already locked by another process" , args : args {
372
+ agent : newAgent456 ,
373
+ previousAgent : prevAgent123 ,
374
+ action : nil ,
375
+ upgradeDetails : nil ,
376
+ },
377
+ beforeUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
378
+ // write some fake data in update marker file
379
+ updateMarkerFilePath := markerFilePath (dataDir )
380
+ err := os .WriteFile (updateMarkerFilePath , []byte ("this: is not a real update marker" ), 0o664 )
381
+ require .NoError (t , err , "error creating fake update marker" )
382
+
383
+ // lock the fake update marker using an external process
384
+ lockFilePath := updateMarkerFilePath + ".lock"
385
+ cmdCancel , lockFileCmd := createFileLockerCmd (t , lockFilePath )
386
+
387
+ fileLockerStdErr , err := lockFileCmd .StderrPipe ()
388
+ require .NoError (t , err , "Error getting stderr pipe from filelocker" )
389
+
390
+ fileLockedCh := make (chan struct {})
391
+
392
+ // consume stderr to check for locking
393
+ go func () {
394
+ scanner := bufio .NewScanner (fileLockerStdErr )
395
+ for scanner .Scan () {
396
+ line := scanner .Text ()
397
+ if strings .Contains (line , "Acquired lock on file" ) {
398
+ fileLockedCh <- struct {}{}
399
+ }
400
+ }
401
+ }()
402
+
403
+ err = lockFileCmd .Start ()
404
+ require .NoError (t , err , "running filelocker should not fail" )
405
+
406
+ t .Cleanup (func () {
407
+ cmdCancel ()
408
+ _ = lockFileCmd .Wait ()
409
+ })
410
+
411
+ select {
412
+ case <- fileLockedCh :
413
+ // file was locked from the external process: all good
414
+ t .Log ("external filelocker acquired the lock!" )
415
+ case <- time .After (30 * time .Second ):
416
+ t .Fatalf ("timed out waiting for file locker to lock the file" )
417
+ }
418
+ },
419
+ afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
420
+ // verify we can't read the actual update marker since it's still locked
421
+ _ , err := LoadMarker (dataDir )
422
+ require .Error (t , err , "loading update marker should fail" )
423
+ },
424
+ wantErr : assert .Error ,
425
+ },
365
426
}
366
427
for _ , tt := range tests {
367
428
t .Run (tt .name , func (t * testing.T ) {
@@ -378,6 +439,24 @@ func Test_markUpgradeLocking(t *testing.T) {
378
439
}
379
440
}
380
441
442
+ func createFileLockerCmd (t * testing.T , lockFilePath string ) (context.CancelFunc , * exec.Cmd ) {
443
+ executableName := "filelocker"
444
+ if runtime .GOOS == "windows" {
445
+ executableName += ".exe"
446
+ }
447
+ filelockerExecutablePath := filepath .Join ("test" , "filelocker" , executableName )
448
+ require .FileExistsf (
449
+ t ,
450
+ filelockerExecutablePath ,
451
+ "filelocker executable %s should exist. Please ensure that mage build:testbinaries has been executed." ,
452
+ filelockerExecutablePath ,
453
+ )
454
+
455
+ cmdCtx , cmdCancel := context .WithCancel (t .Context ())
456
+ lockFileCmd := exec .CommandContext (cmdCtx , filelockerExecutablePath , "-lockfile" , lockFilePath )
457
+ return cmdCancel , lockFileCmd
458
+ }
459
+
381
460
func checkUpgradeMarker (t * testing.T , updateMarker * UpdateMarker , prevAgent agentInstall , newAgent agentInstall ) {
382
461
t .Helper ()
383
462
require .NotNil (t , updateMarker , "update marker should not be nil" )
0 commit comments