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  }