github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/backups/files_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build !windows
     5  
     6  package backups_test
     7  
     8  import (
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"syscall"
    13  
    14  	"github.com/juju/collections/set"
    15  	"github.com/juju/errors"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/mongo"
    20  	"github.com/juju/juju/state/backups"
    21  	"github.com/juju/juju/testing"
    22  )
    23  
    24  var _ = gc.Suite(&filesSuite{})
    25  
    26  type filesSuite struct {
    27  	testing.BaseSuite
    28  	root string
    29  }
    30  
    31  func (s *filesSuite) SetUpTest(c *gc.C) {
    32  	s.BaseSuite.SetUpTest(c)
    33  
    34  	// Set the process' umask to 0 so tests that check permission bits don't
    35  	// fail due to the users umask being an unexpected value.
    36  	oldUmask := syscall.Umask(0)
    37  	s.AddCleanup(func(_ *gc.C) {
    38  		syscall.Umask(oldUmask)
    39  	})
    40  
    41  	s.root = c.MkDir()
    42  }
    43  
    44  func (s *filesSuite) TearDownTest(c *gc.C) {
    45  	s.BaseSuite.TearDownTest(c)
    46  }
    47  
    48  // createFiles preps the fake FS. The files are all created relative to
    49  // the given root.
    50  func (s *filesSuite) createFiles(c *gc.C, paths backups.Paths, root, machineID string) {
    51  	mkdir := func(path string) string {
    52  		dirname := filepath.Join(root, path)
    53  		os.MkdirAll(dirname, 0777)
    54  		return dirname
    55  	}
    56  	touch := func(dirname, name string) {
    57  		path := filepath.Join(dirname, name)
    58  		file, err := os.Create(path)
    59  		c.Assert(err, jc.ErrorIsNil)
    60  		file.Close()
    61  	}
    62  
    63  	dirname := mkdir(paths.DataDir)
    64  	touch(dirname, "system-identity")
    65  	touch(dirname, "nonce.txt")
    66  	touch(dirname, "server.pem")
    67  	touch(dirname, "shared-secret")
    68  	mkdir(filepath.Join(paths.DataDir, "tools"))
    69  
    70  	dirname = mkdir(filepath.Join(paths.DataDir, "agents"))
    71  	touch(dirname, "machine-"+machineID+".conf")
    72  
    73  	dirname = mkdir("/home/ubuntu/.ssh")
    74  	touch(dirname, "authorized_keys")
    75  
    76  	dirname = mkdir(filepath.Join(paths.DataDir, "init", "juju-db"))
    77  	touch(dirname, "juju-db.service")
    78  }
    79  
    80  func (s *filesSuite) checkSameStrings(c *gc.C, actual, expected []string) {
    81  	sActual := set.NewStrings(actual...)
    82  	sExpected := set.NewStrings(expected...)
    83  
    84  	sActualOnly := sActual.Difference(sExpected)
    85  	sExpectedOnly := sExpected.Difference(sActual)
    86  
    87  	if !sActualOnly.IsEmpty() || !sExpectedOnly.IsEmpty() {
    88  		c.Error("strings mismatch")
    89  		onlyActual := sActualOnly.Values()
    90  		onlyExpected := sExpectedOnly.Values()
    91  		sort.Strings(onlyActual)
    92  		sort.Strings(onlyExpected)
    93  
    94  		if !sActualOnly.IsEmpty() {
    95  			c.Log("...unexpected values:")
    96  			for _, str := range onlyActual {
    97  				c.Log(" " + str)
    98  			}
    99  		}
   100  		if !sExpectedOnly.IsEmpty() {
   101  			c.Log("...missing values:")
   102  			for _, str := range onlyExpected {
   103  				c.Log(" " + str)
   104  			}
   105  		}
   106  	}
   107  }
   108  
   109  func (s *filesSuite) TestGetFilesToBackUpMachine0(c *gc.C) {
   110  	paths := backups.Paths{
   111  		DataDir: "/var/lib/juju",
   112  		LogsDir: "/var/log/juju",
   113  	}
   114  	s.createFiles(c, paths, s.root, "0")
   115  
   116  	files, err := backups.GetFilesToBackUp(s.root, &paths, "0")
   117  	c.Assert(err, jc.ErrorIsNil)
   118  
   119  	expected := []string{
   120  		filepath.Join(s.root, "/home/ubuntu/.ssh/authorized_keys"),
   121  		filepath.Join(s.root, "/var/lib/juju/agents/machine-0.conf"),
   122  		filepath.Join(s.root, "/var/lib/juju/nonce.txt"),
   123  		filepath.Join(s.root, "/var/lib/juju/server.pem"),
   124  		filepath.Join(s.root, "/var/lib/juju/shared-secret"),
   125  		filepath.Join(s.root, "/var/lib/juju/system-identity"),
   126  		filepath.Join(s.root, "/var/lib/juju/tools"),
   127  		filepath.Join(s.root, "/var/lib/juju/init/juju-db"),
   128  	}
   129  	c.Check(files, jc.SameContents, expected)
   130  	s.checkSameStrings(c, files, expected)
   131  }
   132  
   133  func (s *filesSuite) TestDirectoriesCleaned(c *gc.C) {
   134  	recreatableFolder := filepath.Join(s.root, "recreate_me")
   135  	os.MkdirAll(recreatableFolder, os.FileMode(0755))
   136  	recreatableFolderInfo, err := os.Stat(recreatableFolder)
   137  	c.Assert(err, jc.ErrorIsNil)
   138  
   139  	recreatableFolder1 := filepath.Join(recreatableFolder, "recreate_me_too")
   140  	os.MkdirAll(recreatableFolder1, os.FileMode(0755))
   141  	recreatableFolder1Info, err := os.Stat(recreatableFolder1)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  
   144  	deletableFolder := filepath.Join(recreatableFolder, "dont_recreate_me")
   145  	os.MkdirAll(deletableFolder, os.FileMode(0755))
   146  
   147  	deletableFile := filepath.Join(recreatableFolder, "delete_me")
   148  	fh, err := os.Create(deletableFile)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	defer fh.Close()
   151  
   152  	deletableFile1 := filepath.Join(recreatableFolder1, "delete_me.too")
   153  	fhr, err := os.Create(deletableFile1)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	defer fhr.Close()
   156  
   157  	s.PatchValue(backups.ReplaceableFolders, func(_ string, _ mongo.Version) (map[string]os.FileMode, error) {
   158  		replaceables := map[string]os.FileMode{}
   159  		for _, replaceable := range []string{
   160  			recreatableFolder,
   161  			recreatableFolder1,
   162  		} {
   163  			dirStat, err := os.Stat(replaceable)
   164  			if err != nil {
   165  				return map[string]os.FileMode{}, errors.Annotatef(err, "cannot stat %q", replaceable)
   166  			}
   167  			replaceables[replaceable] = dirStat.Mode()
   168  		}
   169  		return replaceables, nil
   170  	})
   171  
   172  	err = backups.PrepareMachineForRestore(mongo.Version{})
   173  	c.Assert(err, jc.ErrorIsNil)
   174  
   175  	_, err = os.Stat(deletableFolder)
   176  	c.Assert(err, gc.Not(gc.IsNil))
   177  	c.Assert(os.IsNotExist(err), gc.Equals, true)
   178  
   179  	recreatedFolderInfo, err := os.Stat(recreatableFolder)
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	c.Assert(recreatableFolderInfo.Mode(), gc.Equals, recreatedFolderInfo.Mode())
   182  	c.Assert(recreatableFolderInfo.Sys().(*syscall.Stat_t).Ino, gc.Not(gc.Equals), recreatedFolderInfo.Sys().(*syscall.Stat_t).Ino)
   183  
   184  	recreatedFolder1Info, err := os.Stat(recreatableFolder1)
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(recreatableFolder1Info.Mode(), gc.Equals, recreatedFolder1Info.Mode())
   187  	c.Assert(recreatableFolder1Info.Sys().(*syscall.Stat_t).Ino, gc.Not(gc.Equals), recreatedFolder1Info.Sys().(*syscall.Stat_t).Ino)
   188  }
   189  
   190  func (s *filesSuite) setupReplaceableFolders(c *gc.C) string {
   191  	dataDir := c.MkDir()
   192  	c.Assert(os.Mkdir(filepath.Join(dataDir, "init"), 0640), jc.ErrorIsNil)
   193  	c.Assert(os.Mkdir(filepath.Join(dataDir, "tools"), 0660), jc.ErrorIsNil)
   194  	c.Assert(os.Mkdir(filepath.Join(dataDir, "agents"), 0600), jc.ErrorIsNil)
   195  	c.Assert(os.Mkdir(filepath.Join(dataDir, "db"), 0600), jc.ErrorIsNil)
   196  	return dataDir
   197  }
   198  
   199  func (s *filesSuite) TestReplaceableFoldersMongo2(c *gc.C) {
   200  	dataDir := s.setupReplaceableFolders(c)
   201  
   202  	result, err := (*backups.ReplaceableFolders)(dataDir, mongo.Version{Major: 2, Minor: 4})
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	c.Assert(result, jc.DeepEquals, map[string]os.FileMode{
   205  		filepath.Join(dataDir, "init"):   0640 | os.ModeDir,
   206  		filepath.Join(dataDir, "tools"):  0660 | os.ModeDir,
   207  		filepath.Join(dataDir, "agents"): 0600 | os.ModeDir,
   208  		filepath.Join(dataDir, "db"):     0600 | os.ModeDir,
   209  	})
   210  }
   211  
   212  func (s *filesSuite) TestReplaceableFoldersMongo3(c *gc.C) {
   213  	dataDir := s.setupReplaceableFolders(c)
   214  
   215  	result, err := (*backups.ReplaceableFolders)(dataDir, mongo.Version{Major: 3, Minor: 2})
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	c.Assert(result, jc.DeepEquals, map[string]os.FileMode{
   218  		filepath.Join(dataDir, "init"):   0640 | os.ModeDir,
   219  		filepath.Join(dataDir, "tools"):  0660 | os.ModeDir,
   220  		filepath.Join(dataDir, "agents"): 0600 | os.ModeDir,
   221  	})
   222  }
   223  
   224  func (s *filesSuite) TestGetFilesToBackUpMachine10(c *gc.C) {
   225  	paths := backups.Paths{
   226  		DataDir: "/var/lib/juju",
   227  		LogsDir: "/var/log/juju",
   228  	}
   229  	s.createFiles(c, paths, s.root, "10")
   230  
   231  	files, err := backups.GetFilesToBackUp(s.root, &paths, "10")
   232  	c.Assert(err, jc.ErrorIsNil)
   233  
   234  	expected := []string{
   235  		filepath.Join(s.root, "/home/ubuntu/.ssh/authorized_keys"),
   236  		filepath.Join(s.root, "/var/lib/juju/agents/machine-10.conf"),
   237  		filepath.Join(s.root, "/var/lib/juju/nonce.txt"),
   238  		filepath.Join(s.root, "/var/lib/juju/server.pem"),
   239  		filepath.Join(s.root, "/var/lib/juju/shared-secret"),
   240  		filepath.Join(s.root, "/var/lib/juju/system-identity"),
   241  		filepath.Join(s.root, "/var/lib/juju/tools"),
   242  		filepath.Join(s.root, "/var/lib/juju/init/juju-db"),
   243  	}
   244  	c.Check(files, jc.SameContents, expected)
   245  	s.checkSameStrings(c, files, expected)
   246  }
   247  
   248  func (s *filesSuite) TestGetFilesToBackUpMissing(c *gc.C) {
   249  	paths := backups.Paths{
   250  		DataDir: "/var/lib/juju",
   251  		LogsDir: "/var/log/juju",
   252  	}
   253  	s.createFiles(c, paths, s.root, "0")
   254  
   255  	missing := []string{
   256  		"/var/lib/juju/nonce.txt",
   257  		"/home/ubuntu/.ssh/authorized_keys",
   258  	}
   259  	for _, filename := range missing {
   260  		err := os.Remove(filepath.Join(s.root, filename))
   261  		c.Assert(err, jc.ErrorIsNil)
   262  	}
   263  
   264  	files, err := backups.GetFilesToBackUp(s.root, &paths, "0")
   265  	c.Assert(err, jc.ErrorIsNil)
   266  
   267  	expected := []string{
   268  		filepath.Join(s.root, "/var/lib/juju/agents/machine-0.conf"),
   269  		filepath.Join(s.root, "/var/lib/juju/server.pem"),
   270  		filepath.Join(s.root, "/var/lib/juju/shared-secret"),
   271  		filepath.Join(s.root, "/var/lib/juju/system-identity"),
   272  		filepath.Join(s.root, "/var/lib/juju/tools"),
   273  		filepath.Join(s.root, "/var/lib/juju/init/juju-db"),
   274  	}
   275  	// This got re-created.
   276  	expected = append(expected, filepath.Join(s.root, "/home/ubuntu/.ssh/authorized_keys"))
   277  	c.Check(files, jc.SameContents, expected)
   278  	s.checkSameStrings(c, files, expected)
   279  }