github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/uniter/storage_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 gc "gopkg.in/check.v1" 10 "gopkg.in/juju/names.v2" 11 "gopkg.in/tomb.v2" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/facades/agent/uniter" 15 "github.com/juju/juju/apiserver/params" 16 apiservertesting "github.com/juju/juju/apiserver/testing" 17 "github.com/juju/juju/state" 18 statetesting "github.com/juju/juju/state/testing" 19 "github.com/juju/juju/testing" 20 ) 21 22 type storageSuite struct { 23 testing.BaseSuite 24 called []string 25 } 26 27 var _ = gc.Suite(&storageSuite{}) 28 29 func (s *storageSuite) TestWatchUnitStorageAttachments(c *gc.C) { 30 resources := common.NewResources() 31 getCanAccess := func() (common.AuthFunc, error) { 32 return func(names.Tag) bool { 33 return true 34 }, nil 35 } 36 unitTag := names.NewUnitTag("mysql/0") 37 watcher := &mockStringsWatcher{ 38 changes: make(chan []string, 1), 39 } 40 watcher.changes <- []string{"storage/0", "storage/1"} 41 st := &mockStorageState{ 42 watchStorageAttachments: func(u names.UnitTag) state.StringsWatcher { 43 c.Assert(u, gc.DeepEquals, unitTag) 44 return watcher 45 }, 46 } 47 48 storage, err := uniter.NewStorageAPI(st, st, resources, getCanAccess) 49 c.Assert(err, jc.ErrorIsNil) 50 watches, err := storage.WatchUnitStorageAttachments(params.Entities{ 51 Entities: []params.Entity{{unitTag.String()}}, 52 }) 53 c.Assert(err, jc.ErrorIsNil) 54 c.Assert(watches, gc.DeepEquals, params.StringsWatchResults{ 55 Results: []params.StringsWatchResult{{ 56 StringsWatcherId: "1", 57 Changes: []string{"storage/0", "storage/1"}, 58 }}, 59 }) 60 c.Assert(resources.Get("1"), gc.Equals, watcher) 61 } 62 63 func (s *storageSuite) TestWatchStorageAttachmentVolume(c *gc.C) { 64 resources := common.NewResources() 65 getCanAccess := func() (common.AuthFunc, error) { 66 return func(names.Tag) bool { 67 return true 68 }, nil 69 } 70 unitTag := names.NewUnitTag("mysql/0") 71 storageTag := names.NewStorageTag("data/0") 72 machineTag := names.NewMachineTag("66") 73 volumeTag := names.NewVolumeTag("104") 74 volume := &mockVolume{tag: volumeTag} 75 storageInstance := &mockStorageInstance{kind: state.StorageKindBlock} 76 storageWatcher := &mockNotifyWatcher{ 77 changes: make(chan struct{}, 1), 78 } 79 storageWatcher.changes <- struct{}{} 80 volumeWatcher := &mockNotifyWatcher{ 81 changes: make(chan struct{}, 1), 82 } 83 volumeWatcher.changes <- struct{}{} 84 blockDevicesWatcher := &mockNotifyWatcher{ 85 changes: make(chan struct{}, 1), 86 } 87 blockDevicesWatcher.changes <- struct{}{} 88 var calls []string 89 st := &mockStorageState{ 90 assignedMachine: "66", 91 storageInstance: func(s names.StorageTag) (state.StorageInstance, error) { 92 calls = append(calls, "StorageInstance") 93 c.Assert(s, gc.DeepEquals, storageTag) 94 return storageInstance, nil 95 }, 96 storageInstanceVolume: func(s names.StorageTag) (state.Volume, error) { 97 calls = append(calls, "StorageInstanceVolume") 98 c.Assert(s, gc.DeepEquals, storageTag) 99 return volume, nil 100 }, 101 watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) state.NotifyWatcher { 102 calls = append(calls, "WatchStorageAttachment") 103 c.Assert(s, gc.DeepEquals, storageTag) 104 c.Assert(u, gc.DeepEquals, unitTag) 105 return storageWatcher 106 }, 107 watchVolumeAttachment: func(host names.Tag, v names.VolumeTag) state.NotifyWatcher { 108 calls = append(calls, "WatchVolumeAttachment") 109 c.Assert(host, gc.DeepEquals, machineTag) 110 c.Assert(v, gc.DeepEquals, volumeTag) 111 return volumeWatcher 112 }, 113 watchBlockDevices: func(m names.MachineTag) state.NotifyWatcher { 114 calls = append(calls, "WatchBlockDevices") 115 c.Assert(m, gc.DeepEquals, machineTag) 116 return blockDevicesWatcher 117 }, 118 } 119 120 storage, err := uniter.NewStorageAPI(st, st, resources, getCanAccess) 121 c.Assert(err, jc.ErrorIsNil) 122 watches, err := storage.WatchStorageAttachments(params.StorageAttachmentIds{ 123 Ids: []params.StorageAttachmentId{{ 124 StorageTag: storageTag.String(), 125 UnitTag: unitTag.String(), 126 }}, 127 }) 128 c.Assert(err, jc.ErrorIsNil) 129 c.Assert(watches, gc.DeepEquals, params.NotifyWatchResults{ 130 Results: []params.NotifyWatchResult{{ 131 NotifyWatcherId: "1", 132 }}, 133 }) 134 c.Assert(calls, gc.DeepEquals, []string{ 135 "StorageInstance", 136 "StorageInstanceVolume", 137 "WatchVolumeAttachment", 138 "WatchBlockDevices", 139 "WatchStorageAttachment", 140 }) 141 } 142 143 func (s *storageSuite) TestCAASWatchStorageAttachmentFilesystem(c *gc.C) { 144 s.assertWatchStorageAttachmentFilesystem(c, "") 145 } 146 147 func (s *storageSuite) TestIAASWatchStorageAttachmentFilesystem(c *gc.C) { 148 s.assertWatchStorageAttachmentFilesystem(c, "66") 149 } 150 151 func (s *storageSuite) assertWatchStorageAttachmentFilesystem(c *gc.C, assignedMachine string) { 152 resources := common.NewResources() 153 getCanAccess := func() (common.AuthFunc, error) { 154 return func(names.Tag) bool { 155 return true 156 }, nil 157 } 158 unitTag := names.NewUnitTag("mysql/0") 159 storageTag := names.NewStorageTag("data/0") 160 var hostTag names.Tag 161 hostTag = unitTag 162 if assignedMachine != "" { 163 hostTag = names.NewMachineTag(assignedMachine) 164 } 165 filesystemTag := names.NewFilesystemTag("104") 166 filesystem := &mockFilesystem{tag: filesystemTag} 167 storageInstance := &mockStorageInstance{kind: state.StorageKindFilesystem} 168 storageWatcher := &mockNotifyWatcher{ 169 changes: make(chan struct{}, 1), 170 } 171 storageWatcher.changes <- struct{}{} 172 filesystemWatcher := &mockNotifyWatcher{ 173 changes: make(chan struct{}, 1), 174 } 175 filesystemWatcher.changes <- struct{}{} 176 var calls []string 177 st := &mockStorageState{ 178 assignedMachine: assignedMachine, 179 storageInstance: func(s names.StorageTag) (state.StorageInstance, error) { 180 calls = append(calls, "StorageInstance") 181 c.Assert(s, gc.DeepEquals, storageTag) 182 return storageInstance, nil 183 }, 184 storageInstanceFilesystem: func(s names.StorageTag) (state.Filesystem, error) { 185 calls = append(calls, "StorageInstanceFilesystem") 186 c.Assert(s, gc.DeepEquals, storageTag) 187 return filesystem, nil 188 }, 189 watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) state.NotifyWatcher { 190 calls = append(calls, "WatchStorageAttachment") 191 c.Assert(s, gc.DeepEquals, storageTag) 192 c.Assert(u, gc.DeepEquals, unitTag) 193 return storageWatcher 194 }, 195 watchFilesystemAttachment: func(host names.Tag, f names.FilesystemTag) state.NotifyWatcher { 196 calls = append(calls, "WatchFilesystemAttachment") 197 c.Assert(host, gc.DeepEquals, hostTag) 198 c.Assert(f, gc.DeepEquals, filesystemTag) 199 return filesystemWatcher 200 }, 201 } 202 203 storage, err := uniter.NewStorageAPI(st, st, resources, getCanAccess) 204 c.Assert(err, jc.ErrorIsNil) 205 watches, err := storage.WatchStorageAttachments(params.StorageAttachmentIds{ 206 Ids: []params.StorageAttachmentId{{ 207 StorageTag: storageTag.String(), 208 UnitTag: unitTag.String(), 209 }}, 210 }) 211 c.Assert(err, jc.ErrorIsNil) 212 c.Assert(watches, gc.DeepEquals, params.NotifyWatchResults{ 213 Results: []params.NotifyWatchResult{{ 214 NotifyWatcherId: "1", 215 }}, 216 }) 217 c.Assert(calls, gc.DeepEquals, []string{ 218 "StorageInstance", 219 "StorageInstanceFilesystem", 220 "WatchFilesystemAttachment", 221 "WatchStorageAttachment", 222 }) 223 } 224 225 func (s *storageSuite) TestDestroyUnitStorageAttachments(c *gc.C) { 226 resources := common.NewResources() 227 getCanAccess := func() (common.AuthFunc, error) { 228 return func(names.Tag) bool { 229 return true 230 }, nil 231 } 232 unitTag := names.NewUnitTag("mysql/0") 233 var calls []string 234 st := &mockStorageState{ 235 destroyUnitStorageAttachments: func(u names.UnitTag) error { 236 calls = append(calls, "DestroyUnitStorageAttachments") 237 c.Assert(u, gc.DeepEquals, unitTag) 238 return nil 239 }, 240 } 241 242 storage, err := uniter.NewStorageAPI(st, st, resources, getCanAccess) 243 c.Assert(err, jc.ErrorIsNil) 244 destroyErrors, err := storage.DestroyUnitStorageAttachments(params.Entities{ 245 Entities: []params.Entity{{ 246 Tag: unitTag.String(), 247 }}, 248 }) 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(calls, jc.DeepEquals, []string{"DestroyUnitStorageAttachments"}) 251 c.Assert(destroyErrors, jc.DeepEquals, params.ErrorResults{ 252 []params.ErrorResult{{}}, 253 }) 254 } 255 256 func (s *storageSuite) TestRemoveStorageAttachments(c *gc.C) { 257 setMock := func(st *mockStorageState, f func(s names.StorageTag, u names.UnitTag) error) { 258 st.remove = f 259 } 260 261 unitTag0 := names.NewUnitTag("mysql/0") 262 unitTag1 := names.NewUnitTag("mysql/1") 263 storageTag0 := names.NewStorageTag("data/0") 264 storageTag1 := names.NewStorageTag("data/1") 265 266 resources := common.NewResources() 267 getCanAccess := func() (common.AuthFunc, error) { 268 return func(tag names.Tag) bool { 269 return tag == unitTag0 270 }, nil 271 } 272 273 st := &mockStorageState{} 274 setMock(st, func(s names.StorageTag, u names.UnitTag) error { 275 c.Assert(u, gc.DeepEquals, unitTag0) 276 if s == storageTag1 { 277 return errors.New("badness") 278 } 279 return nil 280 }) 281 282 storage, err := uniter.NewStorageAPI(st, st, resources, getCanAccess) 283 c.Assert(err, jc.ErrorIsNil) 284 removeErrors, err := storage.RemoveStorageAttachments(params.StorageAttachmentIds{ 285 Ids: []params.StorageAttachmentId{{ 286 StorageTag: storageTag0.String(), 287 UnitTag: unitTag0.String(), 288 }, { 289 StorageTag: storageTag1.String(), 290 UnitTag: unitTag0.String(), 291 }, { 292 StorageTag: storageTag0.String(), 293 UnitTag: unitTag1.String(), 294 }, { 295 StorageTag: unitTag0.String(), // oops 296 UnitTag: unitTag0.String(), 297 }, { 298 StorageTag: storageTag0.String(), 299 UnitTag: storageTag0.String(), // oops 300 }}, 301 }) 302 c.Assert(err, jc.ErrorIsNil) 303 c.Assert(removeErrors, jc.DeepEquals, params.ErrorResults{ 304 Results: []params.ErrorResult{ 305 {nil}, 306 {¶ms.Error{Message: "badness"}}, 307 {¶ms.Error{Code: params.CodeUnauthorized, Message: "permission denied"}}, 308 {¶ms.Error{Message: `"unit-mysql-0" is not a valid storage tag`}}, 309 {¶ms.Error{Message: `"storage-data-0" is not a valid unit tag`}}, 310 }, 311 }) 312 } 313 314 const ( 315 addStorageCall = "mockAdd" 316 ) 317 318 func (s *storageSuite) TestAddUnitStorageConstraintsErrors(c *gc.C) { 319 unitTag0 := names.NewUnitTag("mysql/0") 320 storageName0 := "data" 321 storageName1 := "store" 322 323 resources := common.NewResources() 324 getCanAccess := func() (common.AuthFunc, error) { 325 return func(tag names.Tag) bool { 326 return tag == unitTag0 327 }, nil 328 } 329 330 s.called = []string{} 331 mockState := &mockStorageState{ 332 unitStorageConstraints: map[string]state.StorageConstraints{ 333 storageName0: {}, 334 }, 335 } 336 337 storage, err := uniter.NewStorageAPI(mockState, mockState, resources, getCanAccess) 338 c.Assert(err, jc.ErrorIsNil) 339 size := uint64(10) 340 count := uint64(0) 341 addErrors, err := storage.AddUnitStorage(params.StoragesAddParams{ 342 Storages: []params.StorageAddParams{ 343 { 344 UnitTag: unitTag0.String(), 345 StorageName: storageName0, 346 Constraints: params.StorageConstraints{Pool: "matter"}, 347 }, { 348 UnitTag: unitTag0.String(), 349 StorageName: storageName0, 350 Constraints: params.StorageConstraints{Size: &size}, 351 }, { 352 UnitTag: unitTag0.String(), 353 StorageName: storageName0, 354 Constraints: params.StorageConstraints{}, 355 }, { 356 UnitTag: unitTag0.String(), 357 StorageName: storageName0, 358 Constraints: params.StorageConstraints{Count: &count}, 359 }, { 360 UnitTag: unitTag0.String(), 361 StorageName: storageName1, 362 Constraints: params.StorageConstraints{}, 363 }, 364 }}, 365 ) 366 c.Assert(err, jc.ErrorIsNil) 367 c.Assert(s.called, gc.HasLen, 0) 368 c.Assert(addErrors, jc.DeepEquals, params.ErrorResults{ 369 Results: []params.ErrorResult{ 370 {¶ms.Error{Message: `adding storage data for unit-mysql-0: only count can be specified`}}, 371 {¶ms.Error{Message: `adding storage data for unit-mysql-0: only count can be specified`}}, 372 {¶ms.Error{Message: `adding storage data for unit-mysql-0: count must be specified`}}, 373 {¶ms.Error{Message: `adding storage data for unit-mysql-0: count must be specified`}}, 374 {¶ms.Error{ 375 Code: "not found", 376 Message: "adding storage store for unit-mysql-0: storage \"store\" not found"}}, 377 }, 378 }) 379 } 380 381 func (s *storageSuite) TestAddUnitStorage(c *gc.C) { 382 setMockAdd := func(st *mockStorageState, f func(tag names.UnitTag, name string, cons state.StorageConstraints) error) { 383 st.addUnitStorage = f 384 } 385 386 unitTag0 := names.NewUnitTag("mysql/0") 387 storageName0 := "data" 388 storageName1 := "store" 389 390 unitPool := "real" 391 size := uint64(3) 392 unitSize := size * 2 393 unitCount := uint64(100) 394 testCount := uint64(10) 395 396 resources := common.NewResources() 397 getCanAccess := func() (common.AuthFunc, error) { 398 return func(tag names.Tag) bool { 399 return tag == unitTag0 400 }, nil 401 } 402 403 s.called = []string{} 404 mockState := &mockStorageState{ 405 unitStorageConstraints: map[string]state.StorageConstraints{ 406 storageName0: { 407 Pool: unitPool, 408 Size: unitSize, 409 Count: unitCount, 410 }, 411 storageName1: {}, 412 }, 413 } 414 415 setMockAdd(mockState, func(u names.UnitTag, name string, cons state.StorageConstraints) error { 416 s.called = append(s.called, addStorageCall) 417 c.Assert(u, gc.DeepEquals, unitTag0) 418 if name == storageName1 { 419 return errors.New("badness") 420 } 421 c.Assert(cons.Count, gc.Not(gc.Equals), unitCount) 422 c.Assert(cons.Count, jc.DeepEquals, testCount) 423 c.Assert(cons.Pool, jc.DeepEquals, unitPool) 424 c.Assert(cons.Size, jc.DeepEquals, unitSize) 425 return nil 426 }) 427 428 storage, err := uniter.NewStorageAPI(mockState, mockState, resources, getCanAccess) 429 c.Assert(err, jc.ErrorIsNil) 430 addErrors, err := storage.AddUnitStorage(params.StoragesAddParams{ 431 Storages: []params.StorageAddParams{ 432 { 433 UnitTag: unitTag0.String(), 434 StorageName: storageName0, 435 Constraints: params.StorageConstraints{Count: &testCount}, 436 }, { 437 UnitTag: unitTag0.String(), 438 StorageName: storageName1, 439 Constraints: params.StorageConstraints{Count: &testCount}, 440 }, 441 }}, 442 ) 443 c.Assert(err, jc.ErrorIsNil) 444 c.Assert(s.called, jc.SameContents, []string{ 445 addStorageCall, 446 addStorageCall, 447 }) 448 c.Assert(addErrors, jc.DeepEquals, params.ErrorResults{ 449 Results: []params.ErrorResult{ 450 {nil}, 451 {¶ms.Error{Message: "adding storage store for unit-mysql-0: badness"}}, 452 }, 453 }) 454 } 455 456 type mockUnit struct { 457 assignedMachine string 458 storageConstraints map[string]state.StorageConstraints 459 } 460 461 func (u *mockUnit) ShouldBeAssigned() bool { 462 return u.assignedMachine != "" 463 } 464 465 func (u *mockUnit) AssignedMachineId() (string, error) { 466 if u.assignedMachine == "" { 467 return "", errors.NotAssignedf("unit not assigned") 468 } 469 return u.assignedMachine, nil 470 } 471 472 func (u *mockUnit) StorageConstraints() (map[string]state.StorageConstraints, error) { 473 return u.storageConstraints, nil 474 } 475 476 type mockStorageState struct { 477 unitStorageConstraints map[string]state.StorageConstraints 478 assignedMachine string 479 480 uniter.Backend 481 uniter.StorageStateInterface 482 uniter.StorageVolumeInterface 483 uniter.StorageFilesystemInterface 484 destroyUnitStorageAttachments func(names.UnitTag) error 485 remove func(names.StorageTag, names.UnitTag) error 486 storageInstance func(names.StorageTag) (state.StorageInstance, error) 487 storageInstanceFilesystem func(names.StorageTag) (state.Filesystem, error) 488 storageInstanceVolume func(names.StorageTag) (state.Volume, error) 489 watchStorageAttachments func(names.UnitTag) state.StringsWatcher 490 watchStorageAttachment func(names.StorageTag, names.UnitTag) state.NotifyWatcher 491 watchFilesystemAttachment func(names.Tag, names.FilesystemTag) state.NotifyWatcher 492 watchVolumeAttachment func(names.Tag, names.VolumeTag) state.NotifyWatcher 493 watchBlockDevices func(names.MachineTag) state.NotifyWatcher 494 addUnitStorage func(u names.UnitTag, name string, cons state.StorageConstraints) error 495 } 496 497 func (m *mockStorageState) VolumeAccess() uniter.StorageVolumeInterface { 498 return m 499 } 500 501 func (m *mockStorageState) FilesystemAccess() uniter.StorageFilesystemInterface { 502 return m 503 } 504 505 func (m *mockStorageState) Unit(name string) (uniter.Unit, error) { 506 return &mockUnit{ 507 assignedMachine: m.assignedMachine, 508 storageConstraints: m.unitStorageConstraints}, nil 509 } 510 511 func (m *mockStorageState) DestroyUnitStorageAttachments(u names.UnitTag) error { 512 return m.destroyUnitStorageAttachments(u) 513 } 514 515 func (m *mockStorageState) RemoveStorageAttachment(s names.StorageTag, u names.UnitTag) error { 516 return m.remove(s, u) 517 } 518 519 func (m *mockStorageState) StorageInstance(s names.StorageTag) (state.StorageInstance, error) { 520 return m.storageInstance(s) 521 } 522 523 func (m *mockStorageState) StorageInstanceFilesystem(s names.StorageTag) (state.Filesystem, error) { 524 return m.storageInstanceFilesystem(s) 525 } 526 527 func (m *mockStorageState) StorageInstanceVolume(s names.StorageTag) (state.Volume, error) { 528 return m.storageInstanceVolume(s) 529 } 530 531 func (m *mockStorageState) WatchStorageAttachments(u names.UnitTag) state.StringsWatcher { 532 return m.watchStorageAttachments(u) 533 } 534 535 func (m *mockStorageState) WatchStorageAttachment(s names.StorageTag, u names.UnitTag) state.NotifyWatcher { 536 return m.watchStorageAttachment(s, u) 537 } 538 539 func (m *mockStorageState) WatchFilesystemAttachment(hostTag names.Tag, f names.FilesystemTag) state.NotifyWatcher { 540 return m.watchFilesystemAttachment(hostTag, f) 541 } 542 543 func (m *mockStorageState) WatchVolumeAttachment(hostTag names.Tag, v names.VolumeTag) state.NotifyWatcher { 544 return m.watchVolumeAttachment(hostTag, v) 545 } 546 547 func (m *mockStorageState) WatchBlockDevices(mtag names.MachineTag) state.NotifyWatcher { 548 return m.watchBlockDevices(mtag) 549 } 550 551 func (m *mockStorageState) AddStorageForUnit(tag names.UnitTag, name string, cons state.StorageConstraints) ([]names.StorageTag, error) { 552 return nil, m.addUnitStorage(tag, name, cons) 553 } 554 555 type mockStringsWatcher struct { 556 state.StringsWatcher 557 changes chan []string 558 } 559 560 func (m *mockStringsWatcher) Changes() <-chan []string { 561 return m.changes 562 } 563 564 type mockNotifyWatcher struct { 565 tomb tomb.Tomb 566 changes chan struct{} 567 } 568 569 func (m *mockNotifyWatcher) Stop() error { 570 m.Kill() 571 return m.Wait() 572 } 573 574 func (m *mockNotifyWatcher) Kill() { 575 m.tomb.Kill(nil) 576 } 577 578 func (m *mockNotifyWatcher) Wait() error { 579 return m.tomb.Wait() 580 } 581 582 func (m *mockNotifyWatcher) Err() error { 583 return m.tomb.Err() 584 } 585 586 func (m *mockNotifyWatcher) Changes() <-chan struct{} { 587 return m.changes 588 } 589 590 type mockVolume struct { 591 state.Volume 592 tag names.VolumeTag 593 } 594 595 func (m *mockVolume) VolumeTag() names.VolumeTag { 596 return m.tag 597 } 598 599 type mockFilesystem struct { 600 state.Filesystem 601 tag names.FilesystemTag 602 } 603 604 func (m *mockFilesystem) FilesystemTag() names.FilesystemTag { 605 return m.tag 606 } 607 608 type mockStorageInstance struct { 609 state.StorageInstance 610 kind state.StorageKind 611 } 612 613 func (m *mockStorageInstance) Kind() state.StorageKind { 614 return m.kind 615 } 616 617 type watchStorageAttachmentSuite struct { 618 storageTag names.StorageTag 619 machineTag names.MachineTag 620 unitTag names.UnitTag 621 st *fakeStorage 622 storageInstance *fakeStorageInstance 623 volume *fakeVolume 624 volumeAttachmentWatcher *apiservertesting.FakeNotifyWatcher 625 blockDevicesWatcher *apiservertesting.FakeNotifyWatcher 626 storageAttachmentWatcher *apiservertesting.FakeNotifyWatcher 627 } 628 629 var _ = gc.Suite(&watchStorageAttachmentSuite{}) 630 631 func (s *watchStorageAttachmentSuite) SetUpTest(c *gc.C) { 632 s.storageTag = names.NewStorageTag("osd-devices/0") 633 s.machineTag = names.NewMachineTag("0") 634 s.unitTag = names.NewUnitTag("ceph/0") 635 s.storageInstance = &fakeStorageInstance{ 636 tag: s.storageTag, 637 owner: s.machineTag, 638 kind: state.StorageKindBlock, 639 } 640 s.volume = &fakeVolume{tag: names.NewVolumeTag("0")} 641 s.volumeAttachmentWatcher = apiservertesting.NewFakeNotifyWatcher() 642 s.blockDevicesWatcher = apiservertesting.NewFakeNotifyWatcher() 643 s.storageAttachmentWatcher = apiservertesting.NewFakeNotifyWatcher() 644 s.st = &fakeStorage{ 645 storageInstance: func(tag names.StorageTag) (state.StorageInstance, error) { 646 return s.storageInstance, nil 647 }, 648 storageInstanceVolume: func(tag names.StorageTag) (state.Volume, error) { 649 return s.volume, nil 650 }, 651 watchVolumeAttachment: func(names.Tag, names.VolumeTag) state.NotifyWatcher { 652 return s.volumeAttachmentWatcher 653 }, 654 watchBlockDevices: func(names.MachineTag) state.NotifyWatcher { 655 return s.blockDevicesWatcher 656 }, 657 watchStorageAttachment: func(names.StorageTag, names.UnitTag) state.NotifyWatcher { 658 return s.storageAttachmentWatcher 659 }, 660 } 661 } 662 663 func (s *watchStorageAttachmentSuite) TestWatchStorageAttachmentVolumeAttachmentChanges(c *gc.C) { 664 s.testWatchBlockStorageAttachment(c, func() { 665 s.volumeAttachmentWatcher.C <- struct{}{} 666 }) 667 } 668 669 func (s *watchStorageAttachmentSuite) TestWatchStorageAttachmentStorageAttachmentChanges(c *gc.C) { 670 s.testWatchBlockStorageAttachment(c, func() { 671 s.storageAttachmentWatcher.C <- struct{}{} 672 }) 673 } 674 675 func (s *watchStorageAttachmentSuite) TestWatchStorageAttachmentBlockDevicesChange(c *gc.C) { 676 s.testWatchBlockStorageAttachment(c, func() { 677 s.blockDevicesWatcher.C <- struct{}{} 678 }) 679 } 680 681 func (s *watchStorageAttachmentSuite) testWatchBlockStorageAttachment(c *gc.C, change func()) { 682 s.testWatchStorageAttachment(c, change) 683 s.st.CheckCallNames(c, 684 "StorageInstance", 685 "StorageInstanceVolume", 686 "WatchVolumeAttachment", 687 "WatchBlockDevices", 688 "WatchStorageAttachment", 689 ) 690 } 691 692 func (s *watchStorageAttachmentSuite) testWatchStorageAttachment(c *gc.C, change func()) { 693 w, err := uniter.WatchStorageAttachment( 694 s.st, 695 s.st, 696 s.st, 697 s.storageTag, 698 s.machineTag, 699 s.unitTag, 700 ) 701 c.Assert(err, jc.ErrorIsNil) 702 wc := statetesting.NewNotifyWatcherC(c, nopSyncStarter{}, w) 703 wc.AssertOneChange() 704 change() 705 wc.AssertOneChange() 706 }