88 "log/slog"
99 "os"
1010 "os/exec"
11- "path/filepath"
1211 "time"
1312
1413 "postgresus-backend/internal/config"
@@ -19,10 +18,7 @@ import (
1918 encryption_secrets "postgresus-backend/internal/features/encryption/secrets"
2019 "postgresus-backend/internal/features/storages"
2120 util_encryption "postgresus-backend/internal/util/encryption"
22- files_utils "postgresus-backend/internal/util/files"
2321 "postgresus-backend/internal/util/tools"
24-
25- "github.com/google/uuid"
2622)
2723
2824type ValidateMongodbBackupUsecase struct {
@@ -58,15 +54,33 @@ func (uc *ValidateMongodbBackupUsecase) Execute(
5854 }, nil
5955 }
6056
61- // Download backup to temporary file
62- tempFile , cleanupFunc , err := uc .downloadBackupToTempFile (ctx , backup , storage )
57+ // Get backup data from storage
58+ fieldEncryptor := util_encryption .GetFieldEncryptor ()
59+ rawReader , err := storage .GetFile (fieldEncryptor , backup .ID )
6360 if err != nil {
6461 return & ValidationResult {
6562 IsValid : false ,
66- Error : stringPtr (fmt .Sprintf ("failed to download backup: %v" , err )),
63+ Error : stringPtr (fmt .Sprintf ("failed to get backup file from storage : %v" , err )),
6764 }, nil
6865 }
69- defer cleanupFunc ()
66+ defer func () {
67+ if err := rawReader .Close (); err != nil {
68+ uc .logger .Error ("Failed to close backup reader" , "error" , err )
69+ }
70+ }()
71+
72+ // Create a reader that handles decryption if needed
73+ var backupReader io.Reader = rawReader
74+ if backup .Encryption == backups_config .BackupEncryptionEncrypted {
75+ decryptReader , err := uc .setupDecryption (rawReader , backup )
76+ if err != nil {
77+ return & ValidationResult {
78+ IsValid : false ,
79+ Error : stringPtr (fmt .Sprintf ("failed to setup decryption: %v" , err )),
80+ }, nil
81+ }
82+ backupReader = decryptReader
83+ }
7084
7185 // Use mongorestore --dryRun to validate archive
7286 mongorestoreBin := tools .GetMongodbExecutable (
@@ -75,21 +89,48 @@ func (uc *ValidateMongodbBackupUsecase) Execute(
7589 config .GetEnv ().MongodbInstallDir ,
7690 )
7791
78- // Run mongorestore --dryRun to validate the archive
92+ // Run mongorestore --dryRun with stdin input (like restore does)
7993 cmd := exec .CommandContext (
8094 ctx ,
8195 mongorestoreBin ,
82- "--archive=" + tempFile ,
96+ "--archive" ,
8397 "--gzip" ,
8498 "--dryRun" ,
8599 "--quiet" ,
86100 )
87101
88- output , err := cmd .CombinedOutput ()
102+ cmd .Stdin = backupReader
103+ cmd .Env = os .Environ ()
104+ cmd .Env = append (cmd .Env , "LC_ALL=C.UTF-8" , "LANG=C.UTF-8" )
105+
106+ stderrPipe , err := cmd .StderrPipe ()
89107 if err != nil {
90- errorMsg := string (output )
108+ return & ValidationResult {
109+ IsValid : false ,
110+ Error : stringPtr (fmt .Sprintf ("failed to create stderr pipe: %v" , err )),
111+ }, nil
112+ }
113+
114+ stderrCh := make (chan []byte , 1 )
115+ go func () {
116+ output , _ := io .ReadAll (stderrPipe )
117+ stderrCh <- output
118+ }()
119+
120+ if err = cmd .Start (); err != nil {
121+ return & ValidationResult {
122+ IsValid : false ,
123+ Error : stringPtr (fmt .Sprintf ("failed to start mongorestore: %v" , err )),
124+ }, nil
125+ }
126+
127+ waitErr := cmd .Wait ()
128+ stderrOutput := <- stderrCh
129+
130+ if waitErr != nil {
131+ errorMsg := string (stderrOutput )
91132 if errorMsg == "" {
92- errorMsg = err .Error ()
133+ errorMsg = waitErr .Error ()
93134 }
94135 return & ValidationResult {
95136 IsValid : false ,
@@ -105,81 +146,6 @@ func (uc *ValidateMongodbBackupUsecase) Execute(
105146 }, nil
106147}
107148
108- // downloadBackupToTempFile downloads backup data from storage to a temporary file
109- func (uc * ValidateMongodbBackupUsecase ) downloadBackupToTempFile (
110- ctx context.Context ,
111- backup * usecases_common.BackupInfo ,
112- storage * storages.Storage ,
113- ) (string , func (), error ) {
114- err := files_utils .EnsureDirectories ([]string {
115- config .GetEnv ().TempFolder ,
116- })
117- if err != nil {
118- return "" , nil , fmt .Errorf ("failed to ensure directories: %w" , err )
119- }
120-
121- tempDir , err := os .MkdirTemp (config .GetEnv ().TempFolder , "validate_" + uuid .New ().String ())
122- if err != nil {
123- return "" , nil , fmt .Errorf ("failed to create temporary directory: %w" , err )
124- }
125-
126- cleanupFunc := func () {
127- _ = os .RemoveAll (tempDir )
128- }
129-
130- tempBackupFile := filepath .Join (tempDir , "backup.archive.gz" )
131-
132- uc .logger .Info (
133- "Downloading backup file from storage to temporary file" ,
134- "backupId" , backup .ID ,
135- "tempFile" , tempBackupFile ,
136- "encrypted" , backup .Encryption == backups_config .BackupEncryptionEncrypted ,
137- )
138-
139- fieldEncryptor := util_encryption .GetFieldEncryptor ()
140- rawReader , err := storage .GetFile (fieldEncryptor , backup .ID )
141- if err != nil {
142- cleanupFunc ()
143- return "" , nil , fmt .Errorf ("failed to get backup file from storage: %w" , err )
144- }
145- defer func () {
146- if err := rawReader .Close (); err != nil {
147- uc .logger .Error ("Failed to close backup reader" , "error" , err )
148- }
149- }()
150-
151- // Create a reader that handles decryption if needed
152- var backupReader io.Reader = rawReader
153- if backup .Encryption == backups_config .BackupEncryptionEncrypted {
154- decryptReader , err := uc .setupDecryption (rawReader , backup )
155- if err != nil {
156- cleanupFunc ()
157- return "" , nil , fmt .Errorf ("failed to setup decryption: %w" , err )
158- }
159- backupReader = decryptReader
160- }
161-
162- tempFile , err := os .Create (tempBackupFile )
163- if err != nil {
164- cleanupFunc ()
165- return "" , nil , fmt .Errorf ("failed to create temporary backup file: %w" , err )
166- }
167- defer func () {
168- if err := tempFile .Close (); err != nil {
169- uc .logger .Error ("Failed to close temporary file" , "error" , err )
170- }
171- }()
172-
173- _ , err = io .Copy (tempFile , backupReader )
174- if err != nil {
175- cleanupFunc ()
176- return "" , nil , fmt .Errorf ("failed to write backup to temporary file: %w" , err )
177- }
178-
179- uc .logger .Info ("Backup file written to temporary location" , "tempFile" , tempBackupFile )
180- return tempBackupFile , cleanupFunc , nil
181- }
182-
183149func (uc * ValidateMongodbBackupUsecase ) setupDecryption (
184150 reader io.Reader ,
185151 backup * usecases_common.BackupInfo ,
0 commit comments