44package backup
55
66import (
7+ "errors"
78 "fmt"
89 "os"
9- "os/exec"
1010 "path/filepath"
11+ "strings"
1112 "time"
1213
1314 "github.com/CodeMonkeyCybersecurity/eos/pkg/backup"
@@ -122,84 +123,93 @@ Restore:
122123func ensureQuickBackupRepo (rc * eos_io.RuntimeContext ) error {
123124 logger := otelzap .Ctx (rc .Ctx )
124125
125- // Load config
126126 config , err := backup .LoadConfig (rc )
127127 if err != nil {
128- // Config doesn't exist yet, create it
129128 config = & backup.Config {
130- Repositories : make (map [string ]backup.Repository ),
131- Profiles : make (map [string ]backup.Profile ),
132- DefaultRepository : "quick-backups" ,
129+ Repositories : make (map [string ]backup.Repository ),
130+ Profiles : make (map [string ]backup.Profile ),
133131 }
134132 }
135133
136- // Check if quick-backups repo already exists
137- if _ , exists := config .Repositories ["quick-backups" ]; exists {
138- logger .Debug ("Quick backup repository already exists" )
139- return nil
140- }
134+ repoName := backup .QuickBackupRepositoryName
135+ repoConfig , exists := config .Repositories [repoName ]
141136
142- // Create repository directory
143137 homeDir , err := os .UserHomeDir ()
144138 if err != nil {
145139 return fmt .Errorf ("getting home directory: %w" , err )
146140 }
147141
148- repoPath := filepath .Join (homeDir , ".eos" , "quick-backups" )
149- if err := os .MkdirAll (repoPath , 0700 ); err != nil {
150- return fmt .Errorf ("creating repository directory: %w" , err )
151- }
152-
153- logger .Info ("Creating quick backup repository" ,
154- zap .String ("path" , repoPath ))
142+ defaultRepoPath := filepath .Join (homeDir , backup .QuickBackupRelativePath )
155143
156- // Generate secure password
157- password , err := generateQuickBackupPassword (rc , repoPath )
158- if err != nil {
159- return fmt .Errorf ("generating password: %w" , err )
144+ if repoConfig .URL == "" {
145+ repoConfig .URL = defaultRepoPath
146+ }
147+ if repoConfig .Backend == "" {
148+ repoConfig .Backend = "local"
149+ }
150+ if repoConfig .Name == "" {
151+ repoConfig .Name = repoName
160152 }
161153
162- // Initialize restic repository
163- if err := initializeResticRepo (rc , repoPath , password ); err != nil {
164- return fmt .Errorf ("initializing restic repository: %w" , err )
154+ if err := os .MkdirAll (repoConfig .URL , 0700 ); err != nil {
155+ return fmt .Errorf ("creating repository directory: %w" , err )
165156 }
166157
167- // Add to config
168- config .Repositories ["quick-backups" ] = backup.Repository {
169- Name : "quick-backups" ,
170- Backend : "local" ,
171- URL : repoPath ,
158+ if _ , err := ensureQuickBackupPassword (rc , repoConfig .URL ); err != nil {
159+ return fmt .Errorf ("ensuring password: %w" , err )
172160 }
173161
174- config .DefaultRepository = "quick-backups"
162+ config .Repositories [repoName ] = repoConfig
163+ config .DefaultRepository = repoName
175164
176- // Save config
177165 if err := backup .SaveConfig (rc , config ); err != nil {
178166 return fmt .Errorf ("saving configuration: %w" , err )
179167 }
180168
181- logger .Info ("terminal prompt:" , zap .String ("output" , "✓ Quick backup repository created at ~/.eos/quick-backups" ))
169+ client , err := backup .NewClient (rc , repoName )
170+ if err != nil {
171+ return fmt .Errorf ("creating backup client: %w" , err )
172+ }
173+
174+ configPath := filepath .Join (repoConfig .URL , "config" )
175+ _ , statErr := os .Stat (configPath )
182176
177+ if err := client .InitRepository (); err != nil {
178+ if errors .Is (err , backup .ErrResticNotInstalled ) {
179+ logger .Info ("terminal prompt:" , zap .String ("output" ,
180+ "Restic is not installed. Install restic (e.g., sudo apt-get install restic) and rerun eos backup ." ))
181+ }
182+ return err
183+ }
184+
185+ if ! exists || os .IsNotExist (statErr ) {
186+ logger .Info ("terminal prompt:" , zap .String ("output" , "✓ Quick backup repository created at ~/.eos/quick-backups" ))
187+ }
183188 return nil
184189}
185190
186- // generateQuickBackupPassword generates and stores password for quick backups
187- func generateQuickBackupPassword (rc * eos_io.RuntimeContext , repoPath string ) (string , error ) {
191+ // ensureQuickBackupPassword retrieves or generates the password for quick backups.
192+ func ensureQuickBackupPassword (rc * eos_io.RuntimeContext , repoPath string ) (string , error ) {
188193 logger := otelzap .Ctx (rc .Ctx )
189194
190- // Try Vault first
195+ passwordFile := filepath .Join (repoPath , ".password" )
196+ if data , err := os .ReadFile (passwordFile ); err == nil {
197+ password := strings .TrimSpace (string (data ))
198+ if password != "" {
199+ return password , nil
200+ }
201+ logger .Warn ("Quick backup password file is empty, generating new password" ,
202+ zap .String ("path" , passwordFile ))
203+ }
204+
191205 vaultAddr := os .Getenv ("VAULT_ADDR" )
192206 if vaultAddr == "" {
193207 vaultAddr = "https://localhost:8200"
194208 }
195-
196- // TODO: Fix Vault client API - WriteKV method investigation needed
197- // Temporarily disabled pending vault client method fix
209+ // TODO: Implement Vault password storage for quick backups once client supports WriteKV.
198210 _ , _ = vault .NewClient (vaultAddr , logger .Logger ().Logger )
199211
200- // Fallback to local file
201- passwordFile := filepath .Join (repoPath , ".password" )
202- password , err := crypto .GeneratePassword (32 )
212+ password , err := crypto .GeneratePassword (backup .QuickBackupPasswordLength )
203213 if err != nil {
204214 return "" , fmt .Errorf ("generating password: %w" , err )
205215 }
@@ -214,37 +224,6 @@ func generateQuickBackupPassword(rc *eos_io.RuntimeContext, repoPath string) (st
214224 return password , nil
215225}
216226
217- // initializeResticRepo initializes a new restic repository
218- func initializeResticRepo (rc * eos_io.RuntimeContext , repoPath , password string ) error {
219- logger := otelzap .Ctx (rc .Ctx )
220-
221- // Check if already initialized
222- if _ , err := os .Stat (filepath .Join (repoPath , "config" )); err == nil {
223- logger .Debug ("Repository already initialized" )
224- return nil
225- }
226-
227- logger .Info ("Initializing restic repository" , zap .String ("path" , repoPath ))
228-
229- // Build init command
230- cmd := exec .CommandContext (rc .Ctx , "restic" , "init" )
231- cmd .Env = append (os .Environ (),
232- fmt .Sprintf ("RESTIC_REPOSITORY=%s" , repoPath ),
233- fmt .Sprintf ("RESTIC_PASSWORD=%s" , password ),
234- )
235-
236- output , err := cmd .CombinedOutput ()
237- if err != nil {
238- logger .Error ("Failed to initialize repository" ,
239- zap .Error (err ),
240- zap .String ("output" , string (output )))
241- return fmt .Errorf ("restic init failed: %w\n %s" , err , output )
242- }
243-
244- logger .Info ("Repository initialized successfully" )
245- return nil
246- }
247-
248227func init () {
249228 // Add as top-level backup subcommand for quick access
250229 BackupCmd .AddCommand (quickBackupCmd )
0 commit comments