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