github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/storage/provider/rootfs_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provider_test 5 6 import ( 7 "errors" 8 "path/filepath" 9 "runtime" 10 11 "github.com/juju/names" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/storage" 16 "github.com/juju/juju/storage/provider" 17 "github.com/juju/juju/testing" 18 ) 19 20 var _ = gc.Suite(&rootfsSuite{}) 21 22 type rootfsSuite struct { 23 testing.BaseSuite 24 storageDir string 25 commands *mockRunCommand 26 mockDirFuncs *provider.MockDirFuncs 27 } 28 29 func (s *rootfsSuite) SetUpTest(c *gc.C) { 30 if runtime.GOOS == "windows" { 31 c.Skip("Tests relevant only on *nix systems") 32 } 33 s.BaseSuite.SetUpTest(c) 34 s.storageDir = c.MkDir() 35 } 36 37 func (s *rootfsSuite) TearDownTest(c *gc.C) { 38 if s.commands != nil { 39 s.commands.assertDrained() 40 } 41 s.BaseSuite.TearDownTest(c) 42 } 43 44 func (s *rootfsSuite) rootfsProvider(c *gc.C) storage.Provider { 45 s.commands = &mockRunCommand{c: c} 46 return provider.RootfsProvider(s.commands.run) 47 } 48 49 func (s *rootfsSuite) TestFilesystemSource(c *gc.C) { 50 p := s.rootfsProvider(c) 51 cfg, err := storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{}) 52 c.Assert(err, jc.ErrorIsNil) 53 _, err = p.FilesystemSource(nil, cfg) 54 c.Assert(err, gc.ErrorMatches, "storage directory not specified") 55 cfg, err = storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{ 56 "storage-dir": c.MkDir(), 57 }) 58 c.Assert(err, jc.ErrorIsNil) 59 _, err = p.FilesystemSource(nil, cfg) 60 c.Assert(err, jc.ErrorIsNil) 61 } 62 63 func (s *rootfsSuite) TestValidateConfig(c *gc.C) { 64 p := s.rootfsProvider(c) 65 cfg, err := storage.NewConfig("name", provider.RootfsProviderType, map[string]interface{}{}) 66 c.Assert(err, jc.ErrorIsNil) 67 err = p.ValidateConfig(cfg) 68 // The rootfs provider does not have any user 69 // configuration, so an empty map will pass. 70 c.Assert(err, jc.ErrorIsNil) 71 } 72 73 func (s *rootfsSuite) TestSupports(c *gc.C) { 74 p := s.rootfsProvider(c) 75 c.Assert(p.Supports(storage.StorageKindBlock), jc.IsFalse) 76 c.Assert(p.Supports(storage.StorageKindFilesystem), jc.IsTrue) 77 } 78 79 func (s *rootfsSuite) TestScope(c *gc.C) { 80 p := s.rootfsProvider(c) 81 c.Assert(p.Scope(), gc.Equals, storage.ScopeMachine) 82 } 83 84 func (s *rootfsSuite) rootfsFilesystemSource(c *gc.C) storage.FilesystemSource { 85 s.commands = &mockRunCommand{c: c} 86 source, d := provider.RootfsFilesystemSource(s.storageDir, s.commands.run) 87 s.mockDirFuncs = d 88 return source 89 } 90 91 func (s *rootfsSuite) TestCreateFilesystems(c *gc.C) { 92 source := s.rootfsFilesystemSource(c) 93 cmd := s.commands.expect("df", "--output=size", s.storageDir) 94 cmd.respond("1K-blocks\n2048", nil) 95 cmd = s.commands.expect("df", "--output=size", s.storageDir) 96 cmd.respond("1K-blocks\n4096", nil) 97 98 results, err := source.CreateFilesystems([]storage.FilesystemParams{{ 99 Tag: names.NewFilesystemTag("6"), 100 Size: 2, 101 }, { 102 Tag: names.NewFilesystemTag("7"), 103 Size: 4, 104 }}) 105 c.Assert(err, jc.ErrorIsNil) 106 107 c.Assert(results, jc.DeepEquals, []storage.CreateFilesystemsResult{{ 108 Filesystem: &storage.Filesystem{ 109 Tag: names.NewFilesystemTag("6"), 110 FilesystemInfo: storage.FilesystemInfo{ 111 FilesystemId: "6", 112 Size: 2, 113 }, 114 }, 115 }, { 116 Filesystem: &storage.Filesystem{ 117 Tag: names.NewFilesystemTag("7"), 118 FilesystemInfo: storage.FilesystemInfo{ 119 FilesystemId: "7", 120 Size: 4, 121 }, 122 }, 123 }}) 124 } 125 126 func (s *rootfsSuite) TestCreateFilesystemsIsUse(c *gc.C) { 127 source := s.rootfsFilesystemSource(c) 128 results, err := source.CreateFilesystems([]storage.FilesystemParams{{ 129 Tag: names.NewFilesystemTag("666"), // magic; see mockDirFuncs 130 Size: 1, 131 }}) 132 c.Assert(err, jc.ErrorIsNil) 133 c.Assert(results[0].Error, gc.ErrorMatches, "\".*/666\" is not empty") 134 } 135 136 func (s *rootfsSuite) TestAttachFilesystemsPathNotDir(c *gc.C) { 137 source := s.rootfsFilesystemSource(c) 138 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 139 Filesystem: names.NewFilesystemTag("6"), 140 FilesystemId: "6", 141 Path: "file", 142 }}) 143 c.Assert(err, jc.ErrorIsNil) 144 c.Assert(results[0].Error, gc.ErrorMatches, `path "file" must be a directory`) 145 } 146 147 func (s *rootfsSuite) TestCreateFilesystemsNotEnoughSpace(c *gc.C) { 148 source := s.rootfsFilesystemSource(c) 149 cmd := s.commands.expect("df", "--output=size", s.storageDir) 150 cmd.respond("1K-blocks\n2048", nil) 151 152 results, err := source.CreateFilesystems([]storage.FilesystemParams{{ 153 Tag: names.NewFilesystemTag("6"), 154 Size: 4, 155 }}) 156 c.Assert(err, jc.ErrorIsNil) 157 c.Assert(results[0].Error, gc.ErrorMatches, "filesystem is not big enough \\(2M < 4M\\)") 158 } 159 160 func (s *rootfsSuite) TestCreateFilesystemsInvalidPath(c *gc.C) { 161 source := s.rootfsFilesystemSource(c) 162 cmd := s.commands.expect("df", "--output=size", s.storageDir) 163 cmd.respond("", errors.New("error creating directory")) 164 165 results, err := source.CreateFilesystems([]storage.FilesystemParams{{ 166 Tag: names.NewFilesystemTag("6"), 167 Size: 2, 168 }}) 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(results[0].Error, gc.ErrorMatches, "getting size: error creating directory") 171 } 172 173 func (s *rootfsSuite) TestAttachFilesystemsNoPathSpecified(c *gc.C) { 174 source := s.rootfsFilesystemSource(c) 175 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 176 Filesystem: names.NewFilesystemTag("6"), 177 FilesystemId: "6", 178 }}) 179 c.Assert(err, jc.ErrorIsNil) 180 c.Assert(results[0].Error, gc.ErrorMatches, "filesystem mount point not specified") 181 } 182 183 func (s *rootfsSuite) TestAttachFilesystemsBind(c *gc.C) { 184 source := s.rootfsFilesystemSource(c) 185 186 cmd := s.commands.expect("df", "--output=source", "/srv") 187 cmd.respond("headers\n/src/of/root", nil) 188 189 cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv") 190 cmd.respond("", nil) 191 192 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 193 Filesystem: names.NewFilesystemTag("6"), 194 FilesystemId: "6", 195 Path: "/srv", 196 }}) 197 c.Assert(err, jc.ErrorIsNil) 198 c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{ 199 FilesystemAttachment: &storage.FilesystemAttachment{ 200 Filesystem: names.NewFilesystemTag("6"), 201 FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{ 202 Path: "/srv", 203 }, 204 }, 205 }}) 206 } 207 208 func (s *rootfsSuite) TestAttachFilesystemsBound(c *gc.C) { 209 source := s.rootfsFilesystemSource(c) 210 211 // already bind-mounted storage-dir/6 to the target 212 cmd := s.commands.expect("df", "--output=source", "/srv") 213 cmd.respond("headers\n"+filepath.Join(s.storageDir, "6"), nil) 214 215 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 216 Filesystem: names.NewFilesystemTag("6"), 217 FilesystemId: "6", 218 Path: "/srv", 219 }}) 220 c.Assert(err, jc.ErrorIsNil) 221 c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{ 222 FilesystemAttachment: &storage.FilesystemAttachment{ 223 Filesystem: names.NewFilesystemTag("6"), 224 FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{ 225 Path: "/srv", 226 }, 227 }, 228 }}) 229 } 230 231 func (s *rootfsSuite) TestAttachFilesystemsBindFailsDifferentFS(c *gc.C) { 232 source := s.rootfsFilesystemSource(c) 233 234 cmd := s.commands.expect("df", "--output=source", "/srv") 235 cmd.respond("headers\n/src/of/root", nil) 236 237 cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv") 238 cmd.respond("", errors.New("mount --bind fails")) 239 240 cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6")) 241 cmd.respond("headers\n/dev", nil) 242 243 cmd = s.commands.expect("df", "--output=target", "/srv") 244 cmd.respond("headers\n/proc", nil) 245 246 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 247 Filesystem: names.NewFilesystemTag("6"), 248 FilesystemId: "6", 249 Path: "/srv", 250 }}) 251 c.Assert(err, jc.ErrorIsNil) 252 c.Assert(results[0].Error, gc.ErrorMatches, `".*/6" \("/dev"\) and "/srv" \("/proc"\) are on different filesystems`) 253 } 254 255 func (s *rootfsSuite) TestAttachFilesystemsBindSameFSEmptyDir(c *gc.C) { 256 source := s.rootfsFilesystemSource(c) 257 258 cmd := s.commands.expect("df", "--output=source", "/srv") 259 cmd.respond("headers\n/src/of/root", nil) 260 261 cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv") 262 cmd.respond("", errors.New("mount --bind fails")) 263 264 cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6")) 265 cmd.respond("headers\n/dev", nil) 266 267 cmd = s.commands.expect("df", "--output=target", "/srv") 268 cmd.respond("headers\n/dev", nil) 269 270 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 271 Filesystem: names.NewFilesystemTag("6"), 272 FilesystemId: "6", 273 Path: "/srv", 274 }}) 275 c.Assert(err, jc.ErrorIsNil) 276 c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{ 277 FilesystemAttachment: &storage.FilesystemAttachment{ 278 Filesystem: names.NewFilesystemTag("6"), 279 FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{ 280 Path: "/srv", 281 }, 282 }, 283 }}) 284 } 285 286 func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirUnclaimed(c *gc.C) { 287 source := s.rootfsFilesystemSource(c) 288 289 cmd := s.commands.expect("df", "--output=source", "/srv/666") 290 cmd.respond("headers\n/src/of/root", nil) 291 292 cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666") 293 cmd.respond("", errors.New("mount --bind fails")) 294 295 cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6")) 296 cmd.respond("headers\n/dev", nil) 297 298 cmd = s.commands.expect("df", "--output=target", "/srv/666") 299 cmd.respond("headers\n/dev", nil) 300 301 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 302 Filesystem: names.NewFilesystemTag("6"), 303 FilesystemId: "6", 304 Path: "/srv/666", 305 }}) 306 c.Assert(err, jc.ErrorIsNil) 307 c.Assert(results[0].Error, gc.ErrorMatches, `"/srv/666" is not empty`) 308 } 309 310 func (s *rootfsSuite) TestAttachFilesystemsBindSameFSNonEmptyDirClaimed(c *gc.C) { 311 source := s.rootfsFilesystemSource(c) 312 313 cmd := s.commands.expect("df", "--output=source", "/srv/666") 314 cmd.respond("headers\n/src/of/root", nil) 315 316 cmd = s.commands.expect("mount", "--bind", filepath.Join(s.storageDir, "6"), "/srv/666") 317 cmd.respond("", errors.New("mount --bind fails")) 318 319 cmd = s.commands.expect("df", "--output=target", filepath.Join(s.storageDir, "6")) 320 cmd.respond("headers\n/dev", nil) 321 322 cmd = s.commands.expect("df", "--output=target", "/srv/666") 323 cmd.respond("headers\n/dev", nil) 324 325 s.mockDirFuncs.Dirs.Add(filepath.Join(s.storageDir, "6", "juju-target-claimed")) 326 327 results, err := source.AttachFilesystems([]storage.FilesystemAttachmentParams{{ 328 Filesystem: names.NewFilesystemTag("6"), 329 FilesystemId: "6", 330 Path: "/srv/666", 331 }}) 332 c.Assert(err, jc.ErrorIsNil) 333 c.Assert(results, jc.DeepEquals, []storage.AttachFilesystemsResult{{ 334 FilesystemAttachment: &storage.FilesystemAttachment{ 335 Filesystem: names.NewFilesystemTag("6"), 336 FilesystemAttachmentInfo: storage.FilesystemAttachmentInfo{ 337 Path: "/srv/666", 338 }, 339 }, 340 }}) 341 } 342 343 func (s *rootfsSuite) TestDetachFilesystems(c *gc.C) { 344 source := s.rootfsFilesystemSource(c) 345 testDetachFilesystems(c, s.commands, source, true) 346 } 347 348 func (s *rootfsSuite) TestDetachFilesystemsUnattached(c *gc.C) { 349 // The "unattached" case covers both idempotency, and 350 // also the scenario where bind-mounting failed. In 351 // either case, there is no attachment-specific filesystem 352 // mount. 353 source := s.rootfsFilesystemSource(c) 354 testDetachFilesystems(c, s.commands, source, false) 355 }