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  }