github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 loggingConfDir = "/etc/rsyslog.d" 21 logsDir = "/var/log/juju" 22 sshDir = "/home/ubuntu/.ssh" 23 24 agentsDir = "agents" 25 agentsConfs = "machine-*" 26 loggingConfs = "*juju.conf" 27 toolsDir = "tools" 28 29 sshIdentFile = "system-identity" 30 nonceFile = "nonce.txt" 31 allMachinesLog = "all-machines.log" 32 machineLog = "machine-%s.log" 33 authKeysFile = "authorized_keys" 34 35 dbStartupConf = "juju-db.conf" 36 dbPEM = "server.pem" 37 dbSecret = "shared-secret" 38 ) 39 40 // Paths holds the paths that backups needs. 41 type Paths struct { 42 DataDir string 43 LogsDir string 44 } 45 46 // GetFilesToBackUp returns the paths that should be included in the 47 // backup archive. 48 func GetFilesToBackUp(rootDir string, paths *Paths, oldmachine string) ([]string, error) { 49 var glob string 50 51 glob = filepath.Join(rootDir, paths.DataDir, agentsDir, agentsConfs) 52 agentConfs, err := filepath.Glob(glob) 53 if err != nil { 54 return nil, errors.Annotate(err, "failed to fetch agent config files") 55 } 56 57 glob = filepath.Join(rootDir, loggingConfDir, loggingConfs) 58 jujuLogConfs, err := filepath.Glob(glob) 59 if err != nil { 60 return nil, errors.Annotate(err, "failed to fetch juju log conf files") 61 } 62 63 backupFiles := []string{ 64 filepath.Join(rootDir, paths.DataDir, toolsDir), 65 66 filepath.Join(rootDir, paths.DataDir, sshIdentFile), 67 68 filepath.Join(rootDir, paths.DataDir, dbPEM), 69 filepath.Join(rootDir, paths.DataDir, dbSecret), 70 } 71 backupFiles = append(backupFiles, agentConfs...) 72 backupFiles = append(backupFiles, jujuLogConfs...) 73 74 // Handle logs (might not exist). 75 // TODO(ericsnow) We should consider dropping these entirely. 76 allmachines := filepath.Join(rootDir, paths.LogsDir, allMachinesLog) 77 if _, err := os.Stat(allmachines); err != nil { 78 if !os.IsNotExist(err) { 79 return nil, errors.Trace(err) 80 } 81 logger.Errorf("skipping missing file %q", allmachines) 82 } else { 83 backupFiles = append(backupFiles, allmachines) 84 } 85 // TODO(ericsnow) It might not be machine 0... 86 machinelog := filepath.Join(rootDir, paths.LogsDir, fmt.Sprintf(machineLog, oldmachine)) 87 if _, err := os.Stat(machinelog); err != nil { 88 if !os.IsNotExist(err) { 89 return nil, errors.Trace(err) 90 } 91 logger.Errorf("skipping missing file %q", machinelog) 92 } else { 93 backupFiles = append(backupFiles, machinelog) 94 } 95 96 // Handle nonce.txt (might not exist). 97 nonce := filepath.Join(rootDir, paths.DataDir, nonceFile) 98 if _, err := os.Stat(nonce); err != nil { 99 if !os.IsNotExist(err) { 100 return nil, errors.Trace(err) 101 } 102 logger.Errorf("skipping missing file %q", nonce) 103 } else { 104 backupFiles = append(backupFiles, nonce) 105 } 106 107 // Handle user SSH files (might not exist). 108 SSHDir := filepath.Join(rootDir, sshDir) 109 if _, err := os.Stat(SSHDir); err != nil { 110 if !os.IsNotExist(err) { 111 return nil, errors.Trace(err) 112 } 113 logger.Errorf("skipping missing dir %q", SSHDir) 114 } else { 115 backupFiles = append(backupFiles, filepath.Join(SSHDir, authKeysFile)) 116 } 117 118 return backupFiles, nil 119 } 120 121 // replaceableFolders for testing purposes. 122 var replaceableFolders = replaceableFoldersFunc 123 124 // replaceableFoldersFunc will return a map with the folders that need to 125 // be replaced so they can be deleted prior to a restore. 126 func replaceableFoldersFunc() (map[string]os.FileMode, error) { 127 replaceables := map[string]os.FileMode{} 128 129 for _, replaceable := range []string{ 130 filepath.Join(dataDir, "db"), 131 dataDir, 132 logsDir, 133 } { 134 dirStat, err := os.Stat(replaceable) 135 if err != nil { 136 return map[string]os.FileMode{}, errors.Annotatef(err, "cannot stat %q", replaceable) 137 } 138 replaceables[replaceable] = dirStat.Mode() 139 } 140 return replaceables, nil 141 } 142 143 // TODO (perrito666) make this version sensitive when these files change, it would 144 // also be a good idea to save these instead of deleting them. 145 146 // PrepareMachineForRestore deletes all files from the re-bootstrapped 147 // machine that are to be replaced by the backup and recreates those 148 // directories that are to contain new files; this is to avoid 149 // possible mixup from new/old files that lead to an inconsistent 150 // restored state machine. 151 func PrepareMachineForRestore() error { 152 replaceFolders, err := replaceableFolders() 153 if err != nil { 154 return errors.Annotate(err, "cannot retrieve the list of folders to be cleaned before restore") 155 } 156 var keys []string 157 for k := range replaceFolders { 158 keys = append(keys, k) 159 } 160 // sort to avoid trying to create subfolders before folders. 161 sort.Strings(keys) 162 for _, toBeRecreated := range keys { 163 fmode := replaceFolders[toBeRecreated] 164 _, err := os.Stat(toBeRecreated) 165 if err != nil && !os.IsNotExist(err) { 166 return errors.Trace(err) 167 } 168 if err := os.RemoveAll(toBeRecreated); err != nil { 169 return errors.Trace(err) 170 } 171 if err := os.MkdirAll(toBeRecreated, fmode); err != nil { 172 return errors.Trace(err) 173 } 174 } 175 return nil 176 }