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 }