github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/backups/files.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package backups 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "sort" 11 12 "github.com/juju/errors" 13 ) 14 15 // TODO(ericsnow) lp-1392876 16 // Pull these from authoritative sources (see 17 // github.com/juju/juju/juju/paths, etc.): 18 const ( 19 dataDir = "/var/lib/juju" 20 logsDir = "/var/log/juju" 21 sshDir = "/home/ubuntu/.ssh" 22 23 agentsDir = "agents" 24 agentsConfs = "machine-*" 25 toolsDir = "tools" 26 27 sshIdentFile = "system-identity" 28 nonceFile = "nonce.txt" 29 machineLog = "machine-%s.log" 30 authKeysFile = "authorized_keys" 31 32 dbPEM = "server.pem" 33 dbSecret = "shared-secret" 34 ) 35 36 // Paths holds the paths that backups needs. 37 type Paths struct { 38 DataDir string 39 LogsDir string 40 } 41 42 // GetFilesToBackUp returns the paths that should be included in the 43 // backup archive. 44 func GetFilesToBackUp(rootDir string, paths *Paths, oldmachine string) ([]string, error) { 45 var glob string 46 47 glob = filepath.Join(rootDir, paths.DataDir, agentsDir, agentsConfs) 48 agentConfs, err := filepath.Glob(glob) 49 if err != nil { 50 return nil, errors.Annotate(err, "failed to fetch agent config files") 51 } 52 53 backupFiles := []string{ 54 filepath.Join(rootDir, paths.DataDir, toolsDir), 55 56 filepath.Join(rootDir, paths.DataDir, sshIdentFile), 57 58 filepath.Join(rootDir, paths.DataDir, dbPEM), 59 filepath.Join(rootDir, paths.DataDir, dbSecret), 60 } 61 backupFiles = append(backupFiles, agentConfs...) 62 63 // TODO(ericsnow) It might not be machine 0... 64 machinelog := filepath.Join(rootDir, paths.LogsDir, fmt.Sprintf(machineLog, oldmachine)) 65 if _, err := os.Stat(machinelog); err != nil { 66 if !os.IsNotExist(err) { 67 return nil, errors.Trace(err) 68 } 69 logger.Errorf("skipping missing file %q", machinelog) 70 } else { 71 backupFiles = append(backupFiles, machinelog) 72 } 73 74 // Handle nonce.txt (might not exist). 75 nonce := filepath.Join(rootDir, paths.DataDir, nonceFile) 76 if _, err := os.Stat(nonce); err != nil { 77 if !os.IsNotExist(err) { 78 return nil, errors.Trace(err) 79 } 80 logger.Errorf("skipping missing file %q", nonce) 81 } else { 82 backupFiles = append(backupFiles, nonce) 83 } 84 85 // Handle user SSH files (might not exist). 86 SSHDir := filepath.Join(rootDir, sshDir) 87 if _, err := os.Stat(SSHDir); err != nil { 88 if !os.IsNotExist(err) { 89 return nil, errors.Trace(err) 90 } 91 logger.Errorf("skipping missing dir %q", SSHDir) 92 } else { 93 backupFiles = append(backupFiles, filepath.Join(SSHDir, authKeysFile)) 94 } 95 96 return backupFiles, nil 97 } 98 99 // replaceableFolders for testing purposes. 100 var replaceableFolders = replaceableFoldersFunc 101 102 // replaceableFoldersFunc will return a map with the folders that need to 103 // be replaced so they can be deleted prior to a restore. 104 func replaceableFoldersFunc() (map[string]os.FileMode, error) { 105 replaceables := map[string]os.FileMode{} 106 107 for _, replaceable := range []string{ 108 filepath.Join(dataDir, "db"), 109 dataDir, 110 logsDir, 111 } { 112 dirStat, err := os.Stat(replaceable) 113 if err != nil { 114 return map[string]os.FileMode{}, errors.Annotatef(err, "cannot stat %q", replaceable) 115 } 116 replaceables[replaceable] = dirStat.Mode() 117 } 118 return replaceables, nil 119 } 120 121 // TODO (perrito666) make this version sensitive when these files change, it would 122 // also be a good idea to save these instead of deleting them. 123 124 // PrepareMachineForRestore deletes all files from the re-bootstrapped 125 // machine that are to be replaced by the backup and recreates those 126 // directories that are to contain new files; this is to avoid 127 // possible mixup from new/old files that lead to an inconsistent 128 // restored state machine. 129 func PrepareMachineForRestore() error { 130 replaceFolders, err := replaceableFolders() 131 if err != nil { 132 return errors.Annotate(err, "cannot retrieve the list of folders to be cleaned before restore") 133 } 134 var keys []string 135 for k := range replaceFolders { 136 keys = append(keys, k) 137 } 138 // sort to avoid trying to create subfolders before folders. 139 sort.Strings(keys) 140 for _, toBeRecreated := range keys { 141 fmode := replaceFolders[toBeRecreated] 142 _, err := os.Stat(toBeRecreated) 143 if err != nil && !os.IsNotExist(err) { 144 return errors.Trace(err) 145 } 146 if !fmode.IsDir() { 147 continue 148 } 149 if err := os.RemoveAll(toBeRecreated); err != nil { 150 return errors.Trace(err) 151 } 152 if err := os.MkdirAll(toBeRecreated, fmode); err != nil { 153 return errors.Trace(err) 154 } 155 } 156 return nil 157 }