@@ -7,6 +7,7 @@ package file
7
7
8
8
import (
9
9
"context"
10
+ "encoding/json"
10
11
"errors"
11
12
"fmt"
12
13
"log/slog"
@@ -37,7 +38,16 @@ import (
37
38
//go:generate go run github.com/maxbrunsfeld/counterfeiter/[email protected] -generate
38
39
//counterfeiter:generate . fileManagerServiceInterface
39
40
40
- const maxAttempts = 5
41
+ const (
42
+ maxAttempts = 5
43
+ dirPerm = 0o755
44
+ filePerm = 0o600
45
+ )
46
+
47
+ var (
48
+ manifestDirPath = "/var/lib/nginx-agent"
49
+ manifestFilePath = manifestDirPath + "/manifest.json"
50
+ )
41
51
42
52
type (
43
53
fileOperator interface {
@@ -313,9 +323,13 @@ func (fms *FileManagerService) ConfigApply(ctx context.Context,
313
323
if fileErr != nil {
314
324
return model .RollbackRequired , fileErr
315
325
}
316
-
326
+ fileOverviewFiles := files . ConvertToMapOfFiles ( fileOverview . GetFiles ())
317
327
// Update map of current files on disk
318
- fms .UpdateCurrentFilesOnDisk (files .ConvertToMapOfFiles (fileOverview .GetFiles ()))
328
+ fms .UpdateCurrentFilesOnDisk (fileOverviewFiles )
329
+ manifestFileErr := fms .UpdateManifestFile (fileOverviewFiles )
330
+ if manifestFileErr != nil {
331
+ return model .RollbackRequired , manifestFileErr
332
+ }
319
333
320
334
return model .OK , nil
321
335
}
@@ -325,8 +339,10 @@ func (fms *FileManagerService) ClearCache() {
325
339
clear (fms .fileActions )
326
340
}
327
341
342
+ // nolint:revive,cyclop
328
343
func (fms * FileManagerService ) Rollback (ctx context.Context , instanceID string ) error {
329
344
slog .InfoContext (ctx , "Rolling back config for instance" , "instanceid" , instanceID )
345
+ areFilesUpdated := false
330
346
fms .filesMutex .Lock ()
331
347
defer fms .filesMutex .Unlock ()
332
348
for _ , file := range fms .fileActions {
@@ -338,11 +354,11 @@ func (fms *FileManagerService) Rollback(ctx context.Context, instanceID string)
338
354
339
355
// currentFilesOnDisk needs to be updated after rollback action is performed
340
356
delete (fms .currentFilesOnDisk , file .GetFileMeta ().GetName ())
357
+ areFilesUpdated = true
341
358
342
359
continue
343
360
case mpi .File_FILE_ACTION_DELETE , mpi .File_FILE_ACTION_UPDATE :
344
361
content := fms .rollbackFileContents [file .GetFileMeta ().GetName ()]
345
-
346
362
err := fms .fileOperator .Write (ctx , content , file .GetFileMeta ())
347
363
if err != nil {
348
364
return err
@@ -351,13 +367,21 @@ func (fms *FileManagerService) Rollback(ctx context.Context, instanceID string)
351
367
// currentFilesOnDisk needs to be updated after rollback action is performed
352
368
file .GetFileMeta ().Hash = files .GenerateHash (content )
353
369
fms .currentFilesOnDisk [file .GetFileMeta ().GetName ()] = file
370
+ areFilesUpdated = true
354
371
case mpi .File_FILE_ACTION_UNSPECIFIED , mpi .File_FILE_ACTION_UNCHANGED :
355
372
fallthrough
356
373
default :
357
374
slog .DebugContext (ctx , "File Action not implemented" )
358
375
}
359
376
}
360
377
378
+ if areFilesUpdated {
379
+ manifestFileErr := fms .UpdateManifestFile (fms .currentFilesOnDisk )
380
+ if manifestFileErr != nil {
381
+ return manifestFileErr
382
+ }
383
+ }
384
+
361
385
return nil
362
386
}
363
387
@@ -446,7 +470,7 @@ func (fms *FileManagerService) checkAllowedDirectory(checkFiles []*mpi.File) err
446
470
447
471
// DetermineFileActions compares two sets of files to determine the file action for each file. Returns a map of files
448
472
// that have changed and a map of the contents for each updated and deleted file. Key to both maps is file path
449
- // nolint: revive
473
+ // nolint: revive,cyclop
450
474
func (fms * FileManagerService ) DetermineFileActions (currentFiles , modifiedFiles map [string ]* mpi.File ) (
451
475
map [string ]* mpi.File , map [string ][]byte , error ,
452
476
) {
@@ -461,26 +485,31 @@ func (fms *FileManagerService) DetermineFileActions(currentFiles, modifiedFiles
461
485
fileDiff := make (map [string ]* mpi.File ) // Files that have changed, key is file name
462
486
fileContents := make (map [string ][]byte ) // contents of the file, key is file name
463
487
464
- // if file is in currentFiles but not in modified files, file has been deleted
488
+ manifestFiles , manifestFileErr := fms .manifestFile (currentFiles )
489
+
490
+ if manifestFileErr != nil && manifestFiles == nil {
491
+ return nil , nil , manifestFileErr
492
+ }
493
+ // if file is in manifestFiles but not in modified files, file has been deleted
465
494
// copy contents, set file action
466
- for _ , currentFile := range currentFiles {
467
- fileName := currentFile .GetFileMeta ().GetName ()
468
- _ , ok := modifiedFiles [fileName ]
495
+ for fileName , currentFile := range manifestFiles {
496
+ _ , exists := modifiedFiles [fileName ]
469
497
470
- if ! ok {
498
+ if ! exists {
499
+ // Read file contents before marking it deleted
471
500
fileContent , readErr := os .ReadFile (fileName )
472
501
if readErr != nil {
473
502
return nil , nil , fmt .Errorf ("error reading file %s, error: %w" , fileName , readErr )
474
503
}
475
504
fileContents [fileName ] = fileContent
476
505
currentFile .Action = & deleteAction
477
- fileDiff [currentFile . GetFileMeta (). GetName () ] = currentFile
506
+ fileDiff [fileName ] = currentFile
478
507
}
479
508
}
480
509
481
510
for _ , file := range modifiedFiles {
482
511
fileName := file .GetFileMeta ().GetName ()
483
- currentFile , ok := currentFiles [file .GetFileMeta ().GetName ()]
512
+ currentFile , ok := manifestFiles [file .GetFileMeta ().GetName ()]
484
513
// default to unchanged action
485
514
file .Action = & unchangedAction
486
515
@@ -521,3 +550,98 @@ func (fms *FileManagerService) UpdateCurrentFilesOnDisk(currentFiles map[string]
521
550
fms .currentFilesOnDisk [file .GetFileMeta ().GetName ()] = file
522
551
}
523
552
}
553
+
554
+ func (fms * FileManagerService ) UpdateManifestFile (currentFiles map [string ]* mpi.File ) (err error ) {
555
+ manifestFiles := fms .convertToManifestFileMap (currentFiles )
556
+ manifestJSON , err := json .MarshalIndent (manifestFiles , "" , " " )
557
+ if err != nil {
558
+ return fmt .Errorf ("unable to marshal manifest file json: %w" , err )
559
+ }
560
+
561
+ // 0755 allows read/execute for all, write for owner
562
+ if err = os .MkdirAll (manifestDirPath , dirPerm ); err != nil {
563
+ return fmt .Errorf ("unable to create directory %s: %w" , manifestDirPath , err )
564
+ }
565
+
566
+ // 0600 ensures only root can read/write
567
+ newFile , err := os .OpenFile (manifestFilePath , os .O_WRONLY | os .O_CREATE | os .O_TRUNC , filePerm )
568
+ if err != nil {
569
+ return fmt .Errorf ("failed to read manifest file: %w" , err )
570
+ }
571
+ defer newFile .Close ()
572
+
573
+ _ , err = newFile .Write (manifestJSON )
574
+ if err != nil {
575
+ return fmt .Errorf ("failed to write manifest file: %w" , err )
576
+ }
577
+
578
+ return nil
579
+ }
580
+
581
+ func (fms * FileManagerService ) manifestFile (currentFiles map [string ]* mpi.File ) (map [string ]* mpi.File , error ) {
582
+ if _ , err := os .Stat (manifestFilePath ); err != nil {
583
+ return currentFiles , err // Return current files if manifest directory still doesn't exist
584
+ }
585
+
586
+ file , err := os .ReadFile (manifestFilePath )
587
+ if err != nil {
588
+ return nil , fmt .Errorf ("failed to read manifest file: %w" , err )
589
+ }
590
+
591
+ var manifestFiles map [string ]* model.ManifestFile
592
+
593
+ err = json .Unmarshal (file , & manifestFiles )
594
+ if err != nil {
595
+ return nil , fmt .Errorf ("failed to parse manifest file: %w" , err )
596
+ }
597
+
598
+ fileMap := fms .convertToFileMap (manifestFiles )
599
+
600
+ return fileMap , nil
601
+ }
602
+
603
+ func (fms * FileManagerService ) convertToManifestFileMap (
604
+ currentFiles map [string ]* mpi.File ,
605
+ ) map [string ]* model.ManifestFile {
606
+ manifestFileMap := make (map [string ]* model.ManifestFile )
607
+
608
+ for name , file := range currentFiles {
609
+ if file == nil || file .GetFileMeta () == nil {
610
+ continue
611
+ }
612
+ manifestFile := fms .convertToManifestFile (file )
613
+ manifestFileMap [name ] = manifestFile
614
+ }
615
+
616
+ return manifestFileMap
617
+ }
618
+
619
+ func (fms * FileManagerService ) convertToManifestFile (file * mpi.File ) * model.ManifestFile {
620
+ return & model.ManifestFile {
621
+ ManifestFileMeta : & model.ManifestFileMeta {
622
+ Name : file .GetFileMeta ().GetName (),
623
+ Size : file .GetFileMeta ().GetSize (),
624
+ Hash : file .GetFileMeta ().GetHash (),
625
+ },
626
+ }
627
+ }
628
+
629
+ func (fms * FileManagerService ) convertToFileMap (manifestFiles map [string ]* model.ManifestFile ) map [string ]* mpi.File {
630
+ currentFileMap := make (map [string ]* mpi.File )
631
+ for name , manifestFile := range manifestFiles {
632
+ currentFile := fms .convertToFile (manifestFile )
633
+ currentFileMap [name ] = currentFile
634
+ }
635
+
636
+ return currentFileMap
637
+ }
638
+
639
+ func (fms * FileManagerService ) convertToFile (manifestFile * model.ManifestFile ) * mpi.File {
640
+ return & mpi.File {
641
+ FileMeta : & mpi.FileMeta {
642
+ Name : manifestFile .ManifestFileMeta .Name ,
643
+ Hash : manifestFile .ManifestFileMeta .Hash ,
644
+ Size : manifestFile .ManifestFileMeta .Size ,
645
+ },
646
+ }
647
+ }
0 commit comments