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