func (s *stateSuite) TestValidateHook(c *gc.C) { const unattached = false const attached = true err := storage.ValidateHook( names.NewStorageTag("data/0"), unattached, hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}, ) c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`) validate := func(attached bool, kind hooks.Kind) error { return storage.ValidateHook( names.NewStorageTag("data/0"), attached, hook.Info{Kind: kind, StorageId: "data/0"}, ) } assertValidates := func(attached bool, kind hooks.Kind) { err := validate(attached, kind) c.Assert(err, jc.ErrorIsNil) } assertValidateFails := func(attached bool, kind hooks.Kind, expect string) { err := validate(attached, kind) c.Assert(err, gc.ErrorMatches, expect) } assertValidates(false, hooks.StorageAttached) assertValidates(true, hooks.StorageDetaching) assertValidateFails(false, hooks.StorageDetaching, `inappropriate "storage-detaching" hook for storage "data/0": storage not attached`) assertValidateFails(true, hooks.StorageAttached, `inappropriate "storage-attached" hook for storage "data/0": storage already attached`) }
func (s *FilterSuite) TestStorageEvents(c *gc.C) { storageCharm := s.AddTestingCharm(c, "storage-block2") svc := s.AddTestingServiceWithStorage(c, "storage-block2", storageCharm, map[string]state.StorageConstraints{ "multi1to10": state.StorageConstraints{Pool: "loop", Size: 1024, Count: 1}, "multi2up": state.StorageConstraints{Pool: "loop", Size: 2048, Count: 2}, }) unit, err := svc.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToNewMachine() c.Assert(err, jc.ErrorIsNil) s.APILogin(c, unit) f, err := filter.NewFilter(s.uniter, unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) storageC := s.contentAsserterC(c, f.StorageEvents()) c.Assert(storageC.AssertOneReceive(), gc.DeepEquals, []names.StorageTag{ names.NewStorageTag("multi1to10/0"), names.NewStorageTag("multi2up/1"), names.NewStorageTag("multi2up/2"), }) err = s.State.DestroyStorageInstance(names.NewStorageTag("multi2up/1")) c.Assert(err, jc.ErrorIsNil) err = s.State.Cleanup() c.Assert(err, jc.ErrorIsNil) c.Assert(storageC.AssertOneReceive(), gc.DeepEquals, []names.StorageTag{ names.NewStorageTag("multi2up/1"), }) }
func (s *storageSuite) TestParseStorageTag(c *gc.C) { assertParseStorageTag(c, "storage-shared-fs-0", names.NewStorageTag("shared-fs/0")) assertParseStorageTag(c, "storage-store-88", names.NewStorageTag("store/88")) assertParseStorageTagInvalid(c, "", names.InvalidTagError("", "")) assertParseStorageTagInvalid(c, "one", names.InvalidTagError("one", "")) assertParseStorageTagInvalid(c, "storage-", names.InvalidTagError("storage-", names.StorageTagKind)) assertParseStorageTagInvalid(c, "machine-0", names.InvalidTagError("machine-0", names.StorageTagKind)) }
func (s *storageMockSuite) TestList(c *gc.C) { one := "shared-fs/0" oneTag := names.NewStorageTag(one) two := "db-dir/1000" twoTag := names.NewStorageTag(two) msg := "call failure" apiCaller := basetesting.APICallerFunc( func(objType string, version int, id, request string, a, result interface{}, ) error { c.Check(objType, gc.Equals, "Storage") c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "List") c.Check(a, gc.IsNil) if results, k := result.(*params.StorageInfosResult); k { instances := []params.StorageInfo{ params.StorageInfo{ params.StorageDetails{StorageTag: oneTag.String()}, common.ServerError(errors.New(msg)), }, params.StorageInfo{ params.StorageDetails{ StorageTag: twoTag.String(), Status: "attached", Persistent: true, }, nil, }, } results.Results = instances } return nil }) storageClient := storage.NewClient(apiCaller) found, err := storageClient.List() c.Check(err, jc.ErrorIsNil) c.Assert(found, gc.HasLen, 2) expected := []params.StorageInfo{ params.StorageInfo{ StorageDetails: params.StorageDetails{ StorageTag: "storage-shared-fs-0"}, Error: ¶ms.Error{Message: msg}, }, params.StorageInfo{ params.StorageDetails{ StorageTag: "storage-db-dir-1000", Status: "attached", Persistent: true}, nil}, } c.Assert(found, jc.DeepEquals, expected) }
func (s *storageSuite) TestRemoveStorageAttachments(c *gc.C) { setMock := func(st *mockStorageState, f func(s names.StorageTag, u names.UnitTag) error) { st.remove = f } unitTag0 := names.NewUnitTag("mysql/0") unitTag1 := names.NewUnitTag("mysql/1") storageTag0 := names.NewStorageTag("data/0") storageTag1 := names.NewStorageTag("data/1") resources := common.NewResources() getCanAccess := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { return tag == unitTag0 }, nil } state := &mockStorageState{} setMock(state, func(s names.StorageTag, u names.UnitTag) error { c.Assert(u, gc.DeepEquals, unitTag0) if s == storageTag1 { return errors.New("badness") } return nil }) storage, err := uniter.NewStorageAPI(state, resources, getCanAccess) c.Assert(err, jc.ErrorIsNil) errors, err := storage.RemoveStorageAttachments(params.StorageAttachmentIds{ Ids: []params.StorageAttachmentId{{ StorageTag: storageTag0.String(), UnitTag: unitTag0.String(), }, { StorageTag: storageTag1.String(), UnitTag: unitTag0.String(), }, { StorageTag: storageTag0.String(), UnitTag: unitTag1.String(), }, { StorageTag: unitTag0.String(), // oops UnitTag: unitTag0.String(), }, { StorageTag: storageTag0.String(), UnitTag: storageTag0.String(), // oops }}, }) c.Assert(err, jc.ErrorIsNil) c.Assert(errors, jc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {¶ms.Error{Message: "badness"}}, {¶ms.Error{Code: params.CodeUnauthorized, Message: "permission denied"}}, {¶ms.Error{Message: `"unit-mysql-0" is not a valid storage tag`}}, {¶ms.Error{Message: `"storage-data-0" is not a valid unit tag`}}, }, }) }
func (s *attachmentsSuite) TestAttachmentsUpdateShortCircuitDeath(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) storageTag0 := names.NewStorageTag("data/0") storageTag1 := names.NewStorageTag("data/1") removed := set.NewTags() st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { return nil, nil }, remove: func(s names.StorageTag, u names.UnitTag) error { c.Assert(u, gc.Equals, unitTag) removed.Add(s) return nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) r := storage.NewResolver(att) // First make sure we create a storage-attached hook operation for // data/0. We do this to show that until the hook is *committed*, // we will still short-circuit removal. localState := resolver.LocalState{State: operation.State{ Kind: operation.Continue, }} _, err = r.NextOp(localState, remotestate.Snapshot{ Life: params.Alive, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag0: { Life: params.Alive, Kind: params.StorageKindBlock, Location: "/dev/sdb", Attached: true, }, }, }, &mockOperations{}) c.Assert(err, jc.ErrorIsNil) for _, storageTag := range []names.StorageTag{storageTag0, storageTag1} { _, err = r.NextOp(localState, remotestate.Snapshot{ Life: params.Alive, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: {Life: params.Dying}, }, }, nil) c.Assert(err, gc.Equals, resolver.ErrNoOperation) } c.Assert(removed.SortedValues(), jc.DeepEquals, []names.Tag{ storageTag0, storageTag1, }) }
func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) { dir := c.MkDir() writeFile(c, filepath.Join(dir, "data-0"), "!@#") _, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": yaml: did not find expected whitespace or line break`) writeFile(c, filepath.Join(dir, "data-0"), "icantbelieveitsnotattached: true\n") _, err = storage.ReadStateFile(dir, names.NewStorageTag("data/0")) c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`) }
func (s *storageMockSuite) TestShow(c *gc.C) { one := "shared-fs/0" oneTag := names.NewStorageTag(one) two := "db-dir/1000" twoTag := names.NewStorageTag(two) expected := set.NewStrings(oneTag.String(), twoTag.String()) msg := "call failure" apiCaller := basetesting.APICallerFunc( func(objType string, version int, id, request string, a, result interface{}, ) error { c.Check(objType, gc.Equals, "Storage") c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "Show") args, ok := a.(params.Entities) c.Assert(ok, jc.IsTrue) c.Assert(args.Entities, gc.HasLen, 2) if results, k := result.(*params.StorageDetailsResults); k { instances := []params.StorageDetailsResult{ params.StorageDetailsResult{ Result: ¶ms.StorageDetails{StorageTag: oneTag.String()}, }, params.StorageDetailsResult{ Result: ¶ms.StorageDetails{ StorageTag: twoTag.String(), Status: params.EntityStatus{ Status: "attached", }, Persistent: true, }, }, params.StorageDetailsResult{ Error: common.ServerError(errors.New(msg)), }, } results.Results = instances } return nil }) storageClient := storage.NewClient(apiCaller) tags := []names.StorageTag{oneTag, twoTag} found, err := storageClient.Show(tags) c.Assert(err, jc.ErrorIsNil) c.Assert(found, gc.HasLen, 3) c.Assert(expected.Contains(found[0].Result.StorageTag), jc.IsTrue) c.Assert(expected.Contains(found[1].Result.StorageTag), jc.IsTrue) c.Assert(found[2].Error, gc.ErrorMatches, msg) }
func (s *attachmentsUpdateSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) s.unitTag = names.NewUnitTag("mysql/0") s.storageTag0 = names.NewStorageTag("data/0") s.storageTag1 = names.NewStorageTag("data/1") s.attachmentsByTag = map[names.StorageTag]*params.StorageAttachment{ s.storageTag0: { StorageTag: s.storageTag0.String(), UnitTag: s.unitTag.String(), Life: params.Alive, Kind: params.StorageKindBlock, Location: "/dev/sdb", }, s.storageTag1: { StorageTag: s.storageTag1.String(), UnitTag: s.unitTag.String(), Life: params.Dying, Kind: params.StorageKindBlock, Location: "/dev/sdb", }, } s.unitAttachmentIds = nil st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, s.unitTag) return s.unitAttachmentIds[u], nil }, watchStorageAttachment: func(storageTag names.StorageTag, u names.UnitTag) (watcher.NotifyWatcher, error) { w := newMockNotifyWatcher() w.changes <- struct{}{} return w, nil }, storageAttachment: func(storageTag names.StorageTag, u names.UnitTag) (params.StorageAttachment, error) { att, ok := s.attachmentsByTag[storageTag] c.Assert(ok, jc.IsTrue) return *att, nil }, remove: func(storageTag names.StorageTag, u names.UnitTag) error { c.Assert(storageTag, gc.Equals, s.storageTag1) return nil }, } stateDir := c.MkDir() abort := make(chan struct{}) var err error s.att, err = storage.NewAttachments(st, s.unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) s.AddCleanup(func(c *gc.C) { err := s.att.Stop() c.Assert(err, jc.ErrorIsNil) }) }
func (s *CleanupSuite) TestCleanupStorageAttachments(c *gc.C) { s.assertDoesNotNeedCleanup(c) ch := s.AddTestingCharm(c, "storage-block") storage := map[string]state.StorageConstraints{ "data": makeStorageCons("loop", 1024, 1), } service := s.AddTestingServiceWithStorage(c, "storage-block", ch, storage) u, err := service.AddUnit() c.Assert(err, jc.ErrorIsNil) // check no cleanups s.assertDoesNotNeedCleanup(c) // this tag matches the storage instance created for the unit above. storageTag := names.NewStorageTag("data/0") sa, err := s.State.StorageAttachment(storageTag, u.UnitTag()) c.Assert(err, jc.ErrorIsNil) c.Assert(sa.Life(), gc.Equals, state.Alive) // destroy unit and run cleanups; the attachment should be marked dying err = u.Destroy() c.Assert(err, jc.ErrorIsNil) s.assertCleanupRuns(c) // After running the cleanup, the attachment should be dying. sa, err = s.State.StorageAttachment(storageTag, u.UnitTag()) c.Assert(err, jc.ErrorIsNil) c.Assert(sa.Life(), gc.Equals, state.Dying) // check no cleanups s.assertDoesNotNeedCleanup(c) }
// Storage is required to implement Filesystem. func (f *filesystem) Storage() (names.StorageTag, error) { if f.doc.StorageId == "" { msg := fmt.Sprintf("filesystem %q is not assigned to any storage instance", f.Tag().Id()) return names.StorageTag{}, errors.NewNotAssigned(nil, msg) } return names.NewStorageTag(f.doc.StorageId), nil }
// HookContext is part of the ContextFactory interface. func (f *contextFactory) HookContext(hookInfo hook.Info) (*HookContext, error) { ctx, err := f.coreContext() if err != nil { return nil, errors.Trace(err) } hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { ctx.relationId = hookInfo.RelationId ctx.remoteUnitName = hookInfo.RemoteUnit relation, found := ctx.relations[hookInfo.RelationId] if !found { return nil, errors.Errorf("unknown relation id: %v", hookInfo.RelationId) } if hookInfo.Kind == hooks.RelationDeparted { relation.cache.RemoveMember(hookInfo.RemoteUnit) } else if hookInfo.RemoteUnit != "" { // Clear remote settings cache for changing remote unit. relation.cache.InvalidateMember(hookInfo.RemoteUnit) } hookName = fmt.Sprintf("%s-%s", relation.Name(), hookInfo.Kind) } if hookInfo.Kind.IsStorage() { ctx.storageTag = names.NewStorageTag(hookInfo.StorageId) if _, err := ctx.storage.Storage(ctx.storageTag); err != nil { return nil, errors.Annotatef(err, "could not retrieve storage for id: %v", hookInfo.StorageId) } storageName, err := names.StorageName(hookInfo.StorageId) if err != nil { return nil, errors.Trace(err) } hookName = fmt.Sprintf("%s-%s", storageName, hookName) } ctx.id = f.newId(hookName) return ctx, nil }
// readAllStateFiles loads and returns every stateFile persisted inside // the supplied dirPath. If dirPath does not exist, no error is returned. func readAllStateFiles(dirPath string) (files map[names.StorageTag]*stateFile, err error) { defer errors.DeferredAnnotatef(&err, "cannot load storage state from %q", dirPath) if _, err := os.Stat(dirPath); os.IsNotExist(err) { return nil, nil } else if err != nil { return nil, err } fis, err := ioutil.ReadDir(dirPath) if err != nil { return nil, err } files = make(map[names.StorageTag]*stateFile) for _, fi := range fis { if fi.IsDir() { continue } storageId := strings.Replace(fi.Name(), "-", "/", -1) if !names.IsValidStorage(storageId) { continue } tag := names.NewStorageTag(storageId) f, err := readStateFile(dirPath, tag) if err != nil { return nil, err } files[tag] = f } return files, nil }
func (*volumesSuite) TestVolumeParamsStorageTags(c *gc.C) { volumeTag := names.NewVolumeTag("100") storageTag := names.NewStorageTag("mystore/0") unitTag := names.NewUnitTag("mysql/123") p, err := storagecommon.VolumeParams( &fakeVolume{tag: volumeTag, params: &state.VolumeParams{ Pool: "loop", Size: 1024, }}, &fakeStorageInstance{tag: storageTag, owner: unitTag}, testing.CustomModelConfig(c, nil), &fakePoolManager{}, ) c.Assert(err, jc.ErrorIsNil) c.Assert(p, jc.DeepEquals, params.VolumeParams{ VolumeTag: "volume-100", Provider: "loop", Size: 1024, Tags: map[string]string{ tags.JujuController: testing.ModelTag.Id(), tags.JujuModel: testing.ModelTag.Id(), tags.JujuStorageInstance: "mystore/0", tags.JujuStorageOwner: "mysql/123", }, }) }
// SetStorageTag sets the storage tag to the given ID. func (s *Storage) SetStorageTag(id string) { tag := names.NewStorageTag(id) if _, ok := s.Storage[tag]; !ok { panic(fmt.Sprintf("storage %q not added yet", id)) } s.StorageTag = tag }
func (s *stateSuite) TestCommitHook(c *gc.C) { dir := c.MkDir() state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) c.Assert(err, jc.ErrorIsNil) c.Assert(state, gc.NotNil) stateFile := filepath.Join(dir, "data-0") // CommitHook must be idempotent, so test each operation // twice in a row. for i := 0; i < 2; i++ { err := state.CommitHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: "data-0", }) c.Assert(err, jc.ErrorIsNil) c.Assert(stateFile, jc.IsNonEmptyFile) } for i := 0; i < 2; i++ { err := state.CommitHook(hook.Info{ Kind: hooks.StorageDetaching, StorageId: "data-0", }) c.Assert(err, jc.ErrorIsNil) c.Assert(stateFile, jc.DoesNotExist) } }
func newHookQueue(attached bool) storage.StorageHookQueue { return storage.NewStorageHookQueue( names.NewUnitTag("mysql/0"), names.NewStorageTag("data/0"), attached, ) }
func (s *EnvironSuite) TestDestroyEnvironmentWithPersistentVolumesFails(c *gc.C) { // Create a persistent volume. // TODO(wallyworld) - consider moving this to factory registry.RegisterEnvironStorageProviders("someprovider", ec2.EBS_ProviderType) pm := poolmanager.New(state.NewStateSettings(s.State)) _, err := pm.Create("persistent-block", ec2.EBS_ProviderType, map[string]interface{}{"persistent": "true"}) c.Assert(err, jc.ErrorIsNil) ch := s.AddTestingCharm(c, "storage-block2") storage := map[string]state.StorageConstraints{ "multi1to10": makeStorageCons("persistent-block", 1024, 1), } service := s.AddTestingServiceWithStorage(c, "storage-block2", ch, storage) unit, err := service.AddUnit() c.Assert(err, jc.ErrorIsNil) err = s.State.AssignUnit(unit, state.AssignCleanEmpty) c.Assert(err, jc.ErrorIsNil) volume1, err := s.State.StorageInstanceVolume(names.NewStorageTag("multi1to10/0")) c.Assert(err, jc.ErrorIsNil) volumeInfoSet := state.VolumeInfo{Size: 123, Persistent: true, VolumeId: "vol-ume"} err = s.State.SetVolumeInfo(volume1.VolumeTag(), volumeInfoSet) c.Assert(err, jc.ErrorIsNil) env, err := s.State.Environment() c.Assert(err, jc.ErrorIsNil) // TODO(wallyworld) when we can destroy/remove volume, ensure env can then be destroyed c.Assert(errors.Cause(env.Destroy()), gc.Equals, state.ErrPersistentVolumesExist) }
// StorageInstance is required to implement Volume. func (v *volume) StorageInstance() (names.StorageTag, error) { if v.doc.StorageId == "" { msg := fmt.Sprintf("volume %q is not assigned to any storage instance", v.Tag().Id()) return names.StorageTag{}, errors.NewNotAssigned(nil, msg) } return names.NewStorageTag(v.doc.StorageId), nil }
// storageChanged responds to unit storage changes. func (w *RemoteStateWatcher) storageChanged(keys []string) error { tags := make([]names.StorageTag, len(keys)) for i, key := range keys { tags[i] = names.NewStorageTag(key) } ids := make([]params.StorageAttachmentId, len(keys)) for i, tag := range tags { ids[i] = params.StorageAttachmentId{ StorageTag: tag.String(), UnitTag: w.unit.Tag().String(), } } results, err := w.st.StorageAttachmentLife(ids) if err != nil { return errors.Trace(err) } w.mu.Lock() defer w.mu.Unlock() for i, result := range results { tag := tags[i] if result.Error == nil { if storageSnapshot, ok := w.current.Storage[tag]; ok { // We've previously started a watcher for this storage // attachment, so all we needed to do was update the // lifecycle state. storageSnapshot.Life = result.Life w.current.Storage[tag] = storageSnapshot continue } // We haven't seen this storage attachment before, so start // a watcher now; add it to our catacomb in case of mishap; // and wait for the initial event. saw, err := w.st.WatchStorageAttachment(tag, w.unit.Tag()) if err != nil { return errors.Annotate(err, "watching storage attachment") } if err := w.catacomb.Add(saw); err != nil { return errors.Trace(err) } if err := w.watchStorageAttachment(tag, result.Life, saw); err != nil { return errors.Trace(err) } } else if params.IsCodeNotFound(result.Error) { if watcher, ok := w.storageAttachmentWatchers[tag]; ok { // already under catacomb management, any error tracked already worker.Stop(watcher) delete(w.storageAttachmentWatchers, tag) } delete(w.current.Storage, tag) } else { return errors.Annotatef( result.Error, "getting life of %s attachment", names.ReadableString(tag), ) } } return nil }
func (s *storageSuite) TestRemoveStorageAttachment(c *gc.C) { apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { c.Check(objType, gc.Equals, "Uniter") c.Check(version, gc.Equals, 2) c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "RemoveStorageAttachments") c.Check(arg, gc.DeepEquals, params.StorageAttachmentIds{ Ids: []params.StorageAttachmentId{{ StorageTag: "storage-data-0", UnitTag: "unit-mysql-0", }}, }) c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) *(result.(*params.ErrorResults)) = params.ErrorResults{ Results: []params.ErrorResult{{ Error: ¶ms.Error{Message: "yoink"}, }}, } return nil }) st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0")) err := st.RemoveStorageAttachment(names.NewStorageTag("data/0"), names.NewUnitTag("mysql/0")) c.Check(err, gc.ErrorMatches, "yoink") }
func (s *storageSuite) TestWatchStorageAttachments(c *gc.C) { var called bool apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { c.Check(objType, gc.Equals, "Uniter") c.Check(version, gc.Equals, 2) c.Check(id, gc.Equals, "") c.Check(request, gc.Equals, "WatchStorageAttachments") c.Check(arg, gc.DeepEquals, params.StorageAttachmentIds{ Ids: []params.StorageAttachmentId{{ StorageTag: "storage-data-0", UnitTag: "unit-mysql-0", }}, }) c.Assert(result, gc.FitsTypeOf, ¶ms.NotifyWatchResults{}) *(result.(*params.NotifyWatchResults)) = params.NotifyWatchResults{ Results: []params.NotifyWatchResult{{ Error: ¶ms.Error{Message: "FAIL"}, }}, } called = true return nil }) st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0")) _, err := st.WatchStorageAttachment(names.NewStorageTag("data/0"), names.NewUnitTag("mysql/0")) c.Check(err, gc.ErrorMatches, "FAIL") c.Check(called, jc.IsTrue) }
func (s *attachmentsSuite) TestAttachmentsUpdateShortCircuitDeath(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) var removed bool storageTag := names.NewStorageTag("data/0") st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return nil, nil }, storageAttachmentLife: func(ids []params.StorageAttachmentId) ([]params.LifeResult, error) { return []params.LifeResult{{Life: params.Dying}}, nil }, remove: func(s names.StorageTag, u names.UnitTag) error { removed = true c.Assert(s, gc.Equals, storageTag) c.Assert(u, gc.Equals, unitTag) return nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) err = att.UpdateStorage([]names.StorageTag{storageTag}) c.Assert(err, jc.ErrorIsNil) c.Assert(removed, jc.IsTrue) }
func (s *watchStorageAttachmentSuite) SetUpTest(c *gc.C) { s.storageTag = names.NewStorageTag("osd-devices/0") s.machineTag = names.NewMachineTag("0") s.unitTag = names.NewUnitTag("ceph/0") s.storageInstance = &fakeStorageInstance{ tag: s.storageTag, owner: s.machineTag, kind: state.StorageKindBlock, } s.volume = &fakeVolume{tag: names.NewVolumeTag("0")} s.volumeAttachmentWatcher = apiservertesting.NewFakeNotifyWatcher() s.volumeAttachmentWatcher.C <- struct{}{} s.blockDevicesWatcher = apiservertesting.NewFakeNotifyWatcher() s.blockDevicesWatcher.C <- struct{}{} s.storageAttachmentWatcher = apiservertesting.NewFakeNotifyWatcher() s.storageAttachmentWatcher.C <- struct{}{} s.st = &fakeStorage{ storageInstance: func(tag names.StorageTag) (state.StorageInstance, error) { return s.storageInstance, nil }, storageInstanceVolume: func(tag names.StorageTag) (state.Volume, error) { return s.volume, nil }, watchVolumeAttachment: func(names.MachineTag, names.VolumeTag) state.NotifyWatcher { return s.volumeAttachmentWatcher }, watchBlockDevices: func(names.MachineTag) state.NotifyWatcher { return s.blockDevicesWatcher }, watchStorageAttachment: func(names.StorageTag, names.UnitTag) state.NotifyWatcher { return s.storageAttachmentWatcher }, } }
func (s *FilesystemStateSuite) testFilesystemAttachmentParams( c *gc.C, countMin, countMax int, location string, expect state.FilesystemAttachmentParams, ) { ch := s.createStorageCharm(c, "storage-filesystem", charm.Storage{ Name: "data", Type: charm.StorageFilesystem, CountMin: countMin, CountMax: countMax, Location: location, }) storage := map[string]state.StorageConstraints{ "data": makeStorageCons("rootfs", 1024, 1), } service := s.AddTestingServiceWithStorage(c, "storage-filesystem", ch, storage) unit, err := service.AddUnit() c.Assert(err, jc.ErrorIsNil) err = s.State.AssignUnit(unit, state.AssignCleanEmpty) c.Assert(err, jc.ErrorIsNil) machineId, err := unit.AssignedMachineId() c.Assert(err, jc.ErrorIsNil) storageTag := names.NewStorageTag("data/0") filesystem := s.storageInstanceFilesystem(c, storageTag) filesystemAttachment := s.filesystemAttachment( c, names.NewMachineTag(machineId), filesystem.FilesystemTag(), ) params, ok := filesystemAttachment.Params() c.Assert(ok, jc.IsTrue) c.Assert(params, jc.DeepEquals, expect) }
func (s *cmdStorageSuite) TestStoragePersistentProvisioned(c *gc.C) { createUnitWithStorage(c, &s.JujuConnSuite, testPool) vol, err := s.State.StorageInstanceVolume(names.NewStorageTag("data/0")) c.Assert(err, jc.ErrorIsNil) err = s.State.SetVolumeInfo(vol.VolumeTag(), state.VolumeInfo{ Size: 1024, Persistent: true, VolumeId: "vol-ume", }) c.Assert(err, jc.ErrorIsNil) expected := ` data/0: kind: block status: current: pending since: .* persistent: true attachments: units: storage-block/0: machine: "0" `[1:] context, err := runJujuCommand(c, "storage", "show", "data/0") c.Assert(err, jc.ErrorIsNil) c.Assert(testing.Stdout(context), gc.Matches, expected) }
// HookContext is part of the ContextFactory interface. func (f *contextFactory) HookContext(hookInfo hook.Info) (*HookContext, error) { ctx, err := f.coreContext() if err != nil { return nil, errors.Trace(err) } hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { ctx.relationId = hookInfo.RelationId ctx.remoteUnitName = hookInfo.RemoteUnit relation, found := ctx.relations[hookInfo.RelationId] if !found { return nil, errors.Errorf("unknown relation id: %v", hookInfo.RelationId) } if hookInfo.Kind == hooks.RelationDeparted { relation.cache.RemoveMember(hookInfo.RemoteUnit) } else if hookInfo.RemoteUnit != "" { // Clear remote settings cache for changing remote unit. relation.cache.InvalidateMember(hookInfo.RemoteUnit) } hookName = fmt.Sprintf("%s-%s", relation.Name(), hookInfo.Kind) } if hookInfo.Kind.IsStorage() { ctx.storageTag = names.NewStorageTag(hookInfo.StorageId) if _, found := ctx.storage.Storage(ctx.storageTag); !found { return nil, errors.Errorf("unknown storage id: %v", hookInfo.StorageId) } storageName, err := names.StorageName(hookInfo.StorageId) if err != nil { return nil, errors.Trace(err) } hookName = fmt.Sprintf("%s-%s", storageName, hookName) } // Metrics are only sent from the collect-metrics hook. if hookInfo.Kind == hooks.CollectMetrics { ch, err := getCharm(f.paths.GetCharmDir()) if err != nil { return nil, errors.Trace(err) } ctx.definedMetrics = ch.Metrics() chURL, err := f.unit.CharmURL() if err != nil { return nil, errors.Trace(err) } charmMetrics := map[string]charm.Metric{} if ch.Metrics() != nil { charmMetrics = ch.Metrics().Metrics } ctx.metricsRecorder, err = metrics.NewJSONMetricRecorder( f.paths.GetMetricsSpoolDir(), charmMetrics, chURL.String()) if err != nil { return nil, errors.Trace(err) } } ctx.id = f.newId(hookName) return ctx, nil }
func (s *HookContextSuite) SetUpTest(c *gc.C) { var err error s.JujuConnSuite.SetUpTest(c) s.BlockHelper = NewBlockHelper(s.APIState) c.Assert(s.BlockHelper, gc.NotNil) s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() }) // reset s.machine = nil sch := s.AddTestingCharm(c, "wordpress") s.service = s.AddTestingService(c, "u", sch) s.unit = s.AddUnit(c, s.service) s.meteredCharm = s.AddTestingCharm(c, "metered") meteredService := s.AddTestingService(c, "m", s.meteredCharm) meteredUnit := s.addUnit(c, meteredService) err = meteredUnit.SetCharmURL(s.meteredCharm.URL()) c.Assert(err, jc.ErrorIsNil) password, err := utils.RandomPassword() err = s.unit.SetPassword(password) c.Assert(err, jc.ErrorIsNil) s.st = s.OpenAPIAs(c, s.unit.Tag(), password) s.uniter, err = s.st.Uniter() c.Assert(err, jc.ErrorIsNil) c.Assert(s.uniter, gc.NotNil) s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) err = meteredUnit.SetPassword(password) c.Assert(err, jc.ErrorIsNil) meteredState := s.OpenAPIAs(c, meteredUnit.Tag(), password) meteredUniter, err := meteredState.Uniter() s.meteredApiUnit, err = meteredUniter.Unit(meteredUnit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) // Note: The unit must always have a charm URL set, because this // happens as part of the installation process (that happens // before the initial install hook). err = s.unit.SetCharmURL(sch.URL()) c.Assert(err, jc.ErrorIsNil) s.relch = s.AddTestingCharm(c, "mysql") s.relunits = map[int]*state.RelationUnit{} s.apiRelunits = map[int]*uniter.RelationUnit{} s.AddContextRelation(c, "db0") s.AddContextRelation(c, "db1") storageData0 := names.NewStorageTag("data/0") s.storage = &runnertesting.StorageContextAccessor{ map[names.StorageTag]*runnertesting.ContextStorage{ storageData0: &runnertesting.ContextStorage{ storageData0, storage.StorageKindBlock, "/dev/sdb", }, }, } }
// SetNewAttachment adds the attachment to the storage. func (s *Storage) SetNewAttachment(name, location string, kind storage.StorageKind, stub *testing.Stub) { tag := names.NewStorageTag(name) attachment := &ContextStorageAttachment{ info: &StorageAttachment{tag, kind, location}, } attachment.stub = stub s.SetAttachment(attachment) }
func (s *attachmentsSuite) TestAttachmentsStorage(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) storageTag := names.NewStorageTag("data/0") attachment := params.StorageAttachment{ StorageTag: storageTag.String(), UnitTag: unitTag.String(), Life: params.Alive, Kind: params.StorageKindBlock, Location: "/dev/sdb", } st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return nil, nil }, watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) (watcher.NotifyWatcher, error) { w := newMockNotifyWatcher() w.changes <- struct{}{} return w, nil }, storageAttachment: func(s names.StorageTag, u names.UnitTag) (params.StorageAttachment, error) { c.Assert(s, gc.Equals, storageTag) return attachment, nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) defer func() { err := att.Stop() c.Assert(err, jc.ErrorIsNil) }() // There should be no context for data/0 until a hook is queued. _, ok := att.Storage(storageTag) c.Assert(ok, jc.IsFalse) assertStorageTags(c, att) err = att.UpdateStorage([]names.StorageTag{storageTag}) c.Assert(err, jc.ErrorIsNil) hi := waitOneHook(c, att.Hooks()) c.Assert(hi, gc.Equals, hook.Info{ Kind: hooks.StorageAttached, StorageId: storageTag.Id(), }) assertStorageTags(c, att, storageTag) ctx, ok := att.Storage(storageTag) c.Assert(ok, jc.IsTrue) c.Assert(ctx, gc.NotNil) c.Assert(ctx.Tag(), gc.Equals, storageTag) c.Assert(ctx.Kind(), gc.Equals, corestorage.StorageKindBlock) c.Assert(ctx.Location(), gc.Equals, "/dev/sdb") }