71
71
fileToUpdate * mpi.File ,
72
72
) error
73
73
SetIsConnected (isConnected bool )
74
- MoveFilesFromTempDirectory (ctx context.Context , fileAction * model. FileCache , tempDir string ) error
74
+ RenameFile (ctx context.Context , hash , fileName , tempDir string ) error
75
75
UpdateClient (ctx context.Context , fileServiceClient mpi.FileServiceClient )
76
76
}
77
77
@@ -80,14 +80,15 @@ type (
80
80
err error )
81
81
Rollback (ctx context.Context , instanceID string ) error
82
82
ClearCache ()
83
+ SetConfigPath (configPath string )
83
84
ConfigUpload (ctx context.Context , configUploadRequest * mpi.ConfigUploadRequest ) error
84
85
ConfigUpdate (ctx context.Context , nginxConfigContext * model.NginxConfigContext )
85
86
UpdateCurrentFilesOnDisk (ctx context.Context , updateFiles map [string ]* mpi.File , referenced bool ) error
86
87
DetermineFileActions (
87
88
ctx context.Context ,
88
89
currentFiles map [string ]* mpi.File ,
89
90
modifiedFiles map [string ]* model.FileCache ,
90
- ) (map [string ]* model.FileCache , map [ string ][] byte , error )
91
+ ) (map [string ]* model.FileCache , error )
91
92
IsConnected () bool
92
93
SetIsConnected (isConnected bool )
93
94
ResetClient (ctx context.Context , fileServiceClient mpi.FileServiceClient )
@@ -101,12 +102,13 @@ type FileManagerService struct {
101
102
fileServiceOperator fileServiceOperatorInterface
102
103
// map of files and the actions performed on them during config apply
103
104
fileActions map [string ]* model.FileCache // key is file path
104
- // map of the contents of files which have been updated or deleted during config apply, used during rollback
105
- rollbackFileContents map [string ][]byte // key is file path
106
105
// map of the files currently on disk, used to determine the file action during config apply
107
106
currentFilesOnDisk map [string ]* mpi.File // key is file path
108
107
previousManifestFiles map [string ]* model.ManifestFile
109
108
manifestFilePath string
109
+ tempConfigDir string
110
+ tempRollbackDir string
111
+ configPath string
110
112
rollbackManifest bool
111
113
filesMutex sync.RWMutex
112
114
}
@@ -119,15 +121,19 @@ func NewFileManagerService(fileServiceClient mpi.FileServiceClient, agentConfig
119
121
fileOperator : NewFileOperator (manifestLock ),
120
122
fileServiceOperator : NewFileServiceOperator (agentConfig , fileServiceClient , manifestLock ),
121
123
fileActions : make (map [string ]* model.FileCache ),
122
- rollbackFileContents : make (map [string ][]byte ),
123
124
currentFilesOnDisk : make (map [string ]* mpi.File ),
124
125
previousManifestFiles : make (map [string ]* model.ManifestFile ),
125
126
rollbackManifest : true ,
126
127
manifestFilePath : agentConfig .LibDir + "/manifest.json" ,
128
+ configPath : "/etc/nginx/" ,
127
129
manifestLock : manifestLock ,
128
130
}
129
131
}
130
132
133
+ func (fms * FileManagerService ) SetConfigPath (configPath string ) {
134
+ fms .configPath = filepath .Dir (configPath )
135
+ }
136
+
131
137
func (fms * FileManagerService ) ResetClient (ctx context.Context , fileServiceClient mpi.FileServiceClient ) {
132
138
fms .fileServiceOperator .UpdateClient (ctx , fileServiceClient )
133
139
slog .DebugContext (ctx , "File manager service reset client successfully" )
@@ -144,6 +150,9 @@ func (fms *FileManagerService) SetIsConnected(isConnected bool) {
144
150
func (fms * FileManagerService ) ConfigApply (ctx context.Context ,
145
151
configApplyRequest * mpi.ConfigApplyRequest ,
146
152
) (status model.WriteStatus , err error ) {
153
+ var configTempErr error
154
+ var rollbackTempErr error
155
+
147
156
fms .rollbackManifest = true
148
157
fileOverview := configApplyRequest .GetOverview ()
149
158
@@ -156,7 +165,7 @@ func (fms *FileManagerService) ConfigApply(ctx context.Context,
156
165
return model .Error , allowedErr
157
166
}
158
167
159
- diffFiles , fileContent , compareErr := fms .DetermineFileActions (
168
+ diffFiles , compareErr := fms .DetermineFileActions (
160
169
ctx ,
161
170
fms .currentFilesOnDisk ,
162
171
ConvertToMapOfFileCache (fileOverview .GetFiles ()),
@@ -170,15 +179,24 @@ func (fms *FileManagerService) ConfigApply(ctx context.Context,
170
179
return model .NoChange , nil
171
180
}
172
181
173
- fms .rollbackFileContents = fileContent
174
182
fms .fileActions = diffFiles
175
183
176
- tempDir , tempDirError := fms .createTempConfigDirectory (ctx )
177
- if tempDirError != nil {
178
- return model .Error , tempDirError
184
+ fms .tempConfigDir , configTempErr = fms .createTempConfigDirectory ("config" )
185
+ if configTempErr != nil {
186
+ return model .Error , configTempErr
187
+ }
188
+
189
+ fms .tempRollbackDir , rollbackTempErr = fms .createTempConfigDirectory ("rollback" )
190
+ if rollbackTempErr != nil {
191
+ return model .Error , rollbackTempErr
192
+ }
193
+
194
+ rollbackTempFilesErr := fms .backupFiles (ctx )
195
+ if rollbackTempFilesErr != nil {
196
+ return model .Error , rollbackTempFilesErr
179
197
}
180
198
181
- fileErr := fms .executeFileActions (ctx , tempDir )
199
+ fileErr := fms .executeFileActions (ctx )
182
200
if fileErr != nil {
183
201
fms .rollbackManifest = false
184
202
return model .RollbackRequired , fileErr
@@ -194,12 +212,22 @@ func (fms *FileManagerService) ConfigApply(ctx context.Context,
194
212
}
195
213
196
214
func (fms * FileManagerService ) ClearCache () {
197
- clear ( fms . rollbackFileContents )
215
+ slog . Debug ( "Clearing cache and temp files after config apply" )
198
216
clear (fms .fileActions )
199
217
clear (fms .previousManifestFiles )
218
+
219
+ configErr := os .RemoveAll (fms .tempConfigDir )
220
+ if configErr != nil {
221
+ slog .Error ("Error removing temp config directory" , "path" , fms .tempConfigDir , "err" , configErr )
222
+ }
223
+
224
+ rollbackErr := os .RemoveAll (fms .tempRollbackDir )
225
+ if rollbackErr != nil {
226
+ slog .Error ("Error removing temp rollback directory" , "path" , fms .tempRollbackDir , "err" , rollbackErr )
227
+ }
200
228
}
201
229
202
- //nolint:revive // cognitive-complexity of 13 max is 12, loop is needed cant be broken up
230
+ //nolint:revive,cyclop // cognitive-complexity of 13 max is 12, loop is needed cant be broken up
203
231
func (fms * FileManagerService ) Rollback (ctx context.Context , instanceID string ) error {
204
232
slog .InfoContext (ctx , "Rolling back config for instance" , "instance_id" , instanceID )
205
233
@@ -217,16 +245,14 @@ func (fms *FileManagerService) Rollback(ctx context.Context, instanceID string)
217
245
218
246
continue
219
247
case model .Delete , model .Update :
220
- fileMeta := fileAction .File .GetFileMeta ()
221
- content := fms .rollbackFileContents [fileMeta .GetName ()]
222
- err := fms .fileOperator .Write (ctx , content , fileMeta .GetName (), fileMeta .GetPermissions ())
248
+ content , err := fms .restoreFiles (fileAction )
223
249
if err != nil {
224
250
return err
225
251
}
226
252
227
253
// currentFilesOnDisk needs to be updated after rollback action is performed
228
- fileMeta .Hash = files .GenerateHash (content )
229
- fms .currentFilesOnDisk [fileMeta .GetName ()] = fileAction .File
254
+ fileAction . File . FileMeta .Hash = files .GenerateHash (content )
255
+ fms .currentFilesOnDisk [fileAction . File . GetFileMeta () .GetName ()] = fileAction .File
230
256
case model .Unchanged :
231
257
fallthrough
232
258
default :
@@ -308,20 +334,18 @@ func (fms *FileManagerService) DetermineFileActions(
308
334
modifiedFiles map [string ]* model.FileCache ,
309
335
) (
310
336
map [string ]* model.FileCache ,
311
- map [string ][]byte ,
312
337
error ,
313
338
) {
314
339
fms .filesMutex .Lock ()
315
340
defer fms .filesMutex .Unlock ()
316
341
317
342
fileDiff := make (map [string ]* model.FileCache ) // Files that have changed, key is file name
318
- fileContents := make (map [string ][]byte ) // contents of the file, key is file name
319
343
320
344
_ , filesMap , manifestFileErr := fms .manifestFile ()
321
345
322
346
if manifestFileErr != nil {
323
347
if ! errors .Is (manifestFileErr , os .ErrNotExist ) {
324
- return nil , nil , manifestFileErr
348
+ return nil , manifestFileErr
325
349
}
326
350
filesMap = currentFiles
327
351
}
@@ -331,25 +355,16 @@ func (fms *FileManagerService) DetermineFileActions(
331
355
for fileName , manifestFile := range filesMap {
332
356
_ , exists := modifiedFiles [fileName ]
333
357
334
- if ! exists {
335
- // Read file contents before marking it deleted
336
- fileContent , readErr := os .ReadFile (fileName )
337
- if readErr != nil {
338
- if errors .Is (readErr , os .ErrNotExist ) {
339
- slog .DebugContext (ctx , "Unable to backup file contents since file does not exist" , "file" , fileName )
340
- continue
341
- }
342
-
343
- return nil , nil , fmt .Errorf ("error reading file %s: %w" , fileName , readErr )
344
- }
345
- fileContents [fileName ] = fileContent
358
+ if ! fms .agentConfig .IsDirectoryAllowed (fileName ) {
359
+ return nil , fmt .Errorf ("error deleting file %s: file not in allowed directories" , fileName )
360
+ }
346
361
347
- // Allowed directories could have been modified since file was created,
348
- // so we should check before marking for deletion
349
- if ! fms .agentConfig .IsDirectoryAllowed (fileName ) {
350
- return nil , nil , fmt .Errorf ("error deleting file %s: file not in allowed directories" , fileName )
351
- }
362
+ if _ , err := os .Stat (fileName ); os .IsNotExist (err ) {
363
+ slog .DebugContext (ctx , "File already deleted, skipping" , "file" , fileName )
364
+ continue
365
+ }
352
366
367
+ if ! exists {
353
368
fileDiff [fileName ] = & model.FileCache {
354
369
File : manifestFile ,
355
370
Action : model .Delete ,
@@ -359,7 +374,7 @@ func (fms *FileManagerService) DetermineFileActions(
359
374
360
375
for _ , modifiedFile := range modifiedFiles {
361
376
fileName := modifiedFile .File .GetFileMeta ().GetName ()
362
- currentFile , ok := filesMap [modifiedFile . File . GetFileMeta (). GetName () ]
377
+ currentFile , ok := filesMap [fileName ]
363
378
// default to unchanged action
364
379
modifiedFile .Action = model .Unchanged
365
380
@@ -369,25 +384,20 @@ func (fms *FileManagerService) DetermineFileActions(
369
384
}
370
385
// if file doesn't exist in the current files, file has been added
371
386
// set file action
372
- if _ , statErr := os .Stat (modifiedFile . File . GetFileMeta (). GetName () ); errors .Is (statErr , os .ErrNotExist ) {
387
+ if _ , statErr := os .Stat (fileName ); errors .Is (statErr , os .ErrNotExist ) {
373
388
modifiedFile .Action = model .Add
374
- fileDiff [modifiedFile . File . GetFileMeta (). GetName () ] = modifiedFile
389
+ fileDiff [fileName ] = modifiedFile
375
390
376
391
continue
377
392
// if file currently exists and file hash is different, file has been updated
378
393
// copy contents, set file action
379
394
} else if ok && modifiedFile .File .GetFileMeta ().GetHash () != currentFile .GetFileMeta ().GetHash () {
380
- fileContent , readErr := os .ReadFile (fileName )
381
- if readErr != nil {
382
- return nil , nil , fmt .Errorf ("error reading file %s, error: %w" , fileName , readErr )
383
- }
384
395
modifiedFile .Action = model .Update
385
- fileContents [fileName ] = fileContent
386
- fileDiff [modifiedFile .File .GetFileMeta ().GetName ()] = modifiedFile
396
+ fileDiff [fileName ] = modifiedFile
387
397
}
388
398
}
389
399
390
- return fileDiff , fileContents , nil
400
+ return fileDiff , nil
391
401
}
392
402
393
403
// UpdateCurrentFilesOnDisk updates the FileManagerService currentFilesOnDisk slice which contains the files
@@ -463,6 +473,59 @@ func (fms *FileManagerService) UpdateManifestFile(ctx context.Context,
463
473
return fms .fileOperator .WriteManifestFile (ctx , updatedFiles , fms .agentConfig .LibDir , fms .manifestFilePath )
464
474
}
465
475
476
+ func (fms * FileManagerService ) backupFiles (ctx context.Context ) error {
477
+ for _ , file := range fms .fileActions {
478
+ if file .Action == model .Add || file .Action == model .Unchanged {
479
+ continue
480
+ }
481
+
482
+ filePath := file .File .GetFileMeta ().GetName ()
483
+
484
+ if _ , err := os .Stat (filePath ); os .IsNotExist (err ) {
485
+ slog .DebugContext (ctx , "Unable to backup file content since file does not exist" ,
486
+ "file" , filePath )
487
+
488
+ continue
489
+ }
490
+
491
+ tempFilePath := filepath .Join (fms .tempRollbackDir , filePath )
492
+ slog .DebugContext (ctx , "Attempting to backup file content since file exists" , "temp_path" , tempFilePath )
493
+
494
+ moveErr := fms .fileOperator .MoveFile (ctx , filePath , tempFilePath )
495
+
496
+ if moveErr != nil {
497
+ return moveErr
498
+ }
499
+ }
500
+
501
+ return nil
502
+ }
503
+
504
+ func (fms * FileManagerService ) restoreFiles (fileAction * model.FileCache ) ([]byte , error ) {
505
+ fileMeta := fileAction .File .GetFileMeta ()
506
+ fileName := fileMeta .GetName ()
507
+
508
+ tempFilePath := filepath .Join (fms .tempRollbackDir , fileName )
509
+
510
+ // Create parent directories for the target file if they don't exist
511
+ if err := os .MkdirAll (filepath .Dir (fileName ), dirPerm ); err != nil {
512
+ return nil , fmt .Errorf ("failed to create directories for %s: %w" , fileName , err )
513
+ }
514
+
515
+ moveErr := os .Rename (tempFilePath , fileName )
516
+ if moveErr != nil {
517
+ return nil , fmt .Errorf ("failed to rename file, %s to %s: %w" , tempFilePath , fileName , moveErr )
518
+ }
519
+
520
+ content , readErr := os .ReadFile (fileMeta .GetName ())
521
+ if readErr != nil {
522
+ return nil , fmt .Errorf ("error reading file, unable to generate hash: %s error: %w" ,
523
+ fileMeta .GetName (), readErr )
524
+ }
525
+
526
+ return content , nil
527
+ }
528
+
466
529
func (fms * FileManagerService ) manifestFile () (map [string ]* model.ManifestFile , map [string ]* mpi.File , error ) {
467
530
if _ , err := os .Stat (fms .manifestFilePath ); err != nil {
468
531
return nil , nil , err
@@ -491,17 +554,17 @@ func (fms *FileManagerService) manifestFile() (map[string]*model.ManifestFile, m
491
554
return manifestFiles , fileMap , nil
492
555
}
493
556
494
- func (fms * FileManagerService ) executeFileActions (ctx context.Context , tempDir string ) (actionError error ) {
557
+ func (fms * FileManagerService ) executeFileActions (ctx context.Context ) (actionError error ) {
495
558
// Download files to temporary location
496
- downloadError := fms .downloadUpdatedFilesToTempLocation (ctx , tempDir )
559
+ downloadError := fms .downloadUpdatedFilesToTempLocation (ctx , fms . tempConfigDir )
497
560
if downloadError != nil {
498
561
return downloadError
499
562
}
500
563
501
564
// Remove temp files if there is a failure moving or deleting files
502
- actionError = fms .moveOrDeleteFiles (ctx , tempDir , actionError )
565
+ actionError = fms .moveOrDeleteFiles (ctx , fms . tempConfigDir , actionError )
503
566
if actionError != nil {
504
- fms .deleteTempFiles (ctx , tempDir )
567
+ fms .deleteTempFiles (ctx , fms . tempConfigDir )
505
568
}
506
569
507
570
return actionError
@@ -528,11 +591,6 @@ func (fms *FileManagerService) downloadUpdatedFilesToTempLocation(
528
591
}
529
592
}
530
593
531
- // Remove temp files if there is an error downloading any files
532
- if updateError != nil {
533
- fms .deleteTempFiles (ctx , tempDir )
534
- }
535
-
536
594
return updateError
537
595
}
538
596
@@ -551,7 +609,8 @@ actionsLoop:
551
609
552
610
continue
553
611
case model .Add , model .Update :
554
- err := fms .fileServiceOperator .MoveFilesFromTempDirectory (ctx , fileAction , tempDir )
612
+ fileMeta := fileAction .File .GetFileMeta ()
613
+ err := fms .fileServiceOperator .RenameFile (ctx , fileMeta .GetHash (), fileMeta .GetName (), tempDir )
555
614
if err != nil {
556
615
actionError = err
557
616
@@ -649,17 +708,11 @@ func (fms *FileManagerService) convertToFile(manifestFile *model.ManifestFile) *
649
708
}
650
709
}
651
710
652
- func (fms * FileManagerService ) createTempConfigDirectory (ctx context. Context ) (string , error ) {
653
- tempDir , tempDirError := os .MkdirTemp (fms .agentConfig . LibDir , "config" )
711
+ func (fms * FileManagerService ) createTempConfigDirectory (pattern string ) (string , error ) {
712
+ tempDir , tempDirError := os .MkdirTemp (fms .configPath , pattern )
654
713
if tempDirError != nil {
655
714
return "" , fmt .Errorf ("failed creating temp config directory: %w" , tempDirError )
656
715
}
657
- defer func (path string ) {
658
- err := os .RemoveAll (path )
659
- if err != nil {
660
- slog .ErrorContext (ctx , "error removing temp config directory" , "path" , path , "err" , err )
661
- }
662
- }(tempDir )
663
716
664
717
return tempDir , nil
665
718
}
0 commit comments