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 *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 (s *stateSuite) TestReadStateFileDirNotExist(c *gc.C) { dir := filepath.Join(c.MkDir(), "doesnotexist") state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) c.Assert(err, jc.ErrorIsNil) c.Assert(state, gc.NotNil) // CommitHook will fail if the directory does not exist. The uniter // must ensure the directory is created before committing any hooks // to the storage state. err = state.CommitHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: "data-0", }) c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) }
func (s *stateSuite) TestReadStateFileFileNotExist(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) data, err := ioutil.ReadFile(filepath.Join(dir, "data-0")) c.Assert(err, jc.Satisfies, os.IsNotExist) err = state.CommitHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: "data-0", }) c.Assert(err, jc.ErrorIsNil) data, err = ioutil.ReadFile(filepath.Join(dir, "data-0")) c.Assert(err, jc.ErrorIsNil) c.Assert(string(data), gc.Equals, "attached: true\n") }
func (s *attachmentsSuite) TestNewAttachmentsInit(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) // Simulate remote state returning a single Alive storage attachment. storageTag := names.NewStorageTag("data/0") attachmentIds := []params.StorageAttachmentId{{ StorageTag: storageTag.String(), UnitTag: unitTag.String(), }} 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 attachmentIds, nil }, storageAttachment: func(s names.StorageTag, u names.UnitTag) (params.StorageAttachment, error) { c.Assert(s, gc.Equals, storageTag) return attachment, nil }, } withAttachments := func(f func(*storage.Attachments)) { att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) f(att) } // No state files, so no storagers will be started. var called int withAttachments(func(att *storage.Attachments) { called++ c.Assert(att.Pending(), gc.Equals, 1) err := att.ValidateHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: storageTag.Id(), }) c.Assert(err, gc.ErrorMatches, `unknown storage "data/0"`) assertStorageTags(c, att) // no active attachment }) c.Assert(called, gc.Equals, 1) // Commit a storage-attached to local state and try again. state0, err := storage.ReadStateFile(stateDir, storageTag) c.Assert(err, jc.ErrorIsNil) err = state0.CommitHook(hook.Info{Kind: hooks.StorageAttached, StorageId: "data/0"}) c.Assert(err, jc.ErrorIsNil) // Create an extra one so we can make sure it gets removed. state1, err := storage.ReadStateFile(stateDir, names.NewStorageTag("data/1")) c.Assert(err, jc.ErrorIsNil) err = state1.CommitHook(hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}) c.Assert(err, jc.ErrorIsNil) withAttachments(func(att *storage.Attachments) { // We should be able to get the initial storage context // for existing storage immediately, without having to // wait for any hooks to fire. ctx, err := att.Storage(storageTag) c.Assert(err, jc.ErrorIsNil) c.Assert(ctx, gc.NotNil) c.Assert(ctx.Tag(), gc.Equals, storageTag) c.Assert(ctx.Tag(), gc.Equals, storageTag) c.Assert(ctx.Kind(), gc.Equals, corestorage.StorageKindBlock) c.Assert(ctx.Location(), gc.Equals, "/dev/sdb") called++ c.Assert(att.Pending(), gc.Equals, 0) err = att.ValidateHook(hook.Info{ Kind: hooks.StorageDetaching, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) err = att.ValidateHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: "data/1", }) c.Assert(err, gc.ErrorMatches, `unknown storage "data/1"`) assertStorageTags(c, att, storageTag) }) c.Assert(called, gc.Equals, 2) c.Assert(filepath.Join(stateDir, "data-0"), jc.IsNonEmptyFile) c.Assert(filepath.Join(stateDir, "data-1"), jc.DoesNotExist) }
func (s *attachmentsSuite) TestNewAttachmentsInit(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) // Simulate remote state returning a single Alive storage attachment. attachmentIds := []params.StorageAttachmentId{{ StorageTag: "storage-data-0", UnitTag: unitTag.String(), }} st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return attachmentIds, nil }, watchStorageAttachment: func(s names.StorageTag, u names.UnitTag) (watcher.NotifyWatcher, error) { return newMockNotifyWatcher(), nil }, } storageTag := names.NewStorageTag("data/0") withAttachments := func(f func(*storage.Attachments)) { att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) defer func() { err := att.Stop() c.Assert(err, jc.ErrorIsNil) }() f(att) } // No state files, so no storagers will be started. var called int withAttachments(func(att *storage.Attachments) { called++ c.Assert(att.Pending(), gc.Equals, 1) err := att.ValidateHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: storageTag.Id(), }) c.Assert(err, gc.ErrorMatches, `unknown storage "data/0"`) assertStorageTags(c, att) // no active attachment }) c.Assert(called, gc.Equals, 1) // Commit a storage-attached to local state and try again. state0, err := storage.ReadStateFile(stateDir, storageTag) c.Assert(err, jc.ErrorIsNil) err = state0.CommitHook(hook.Info{Kind: hooks.StorageAttached, StorageId: "data/0"}) c.Assert(err, jc.ErrorIsNil) // Create an extra one so we can make sure it gets removed. state1, err := storage.ReadStateFile(stateDir, names.NewStorageTag("data/1")) c.Assert(err, jc.ErrorIsNil) err = state1.CommitHook(hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}) c.Assert(err, jc.ErrorIsNil) withAttachments(func(att *storage.Attachments) { called++ c.Assert(att.Pending(), gc.Equals, 0) err := att.ValidateHook(hook.Info{ Kind: hooks.StorageDetaching, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) err = att.ValidateHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: "data/1", }) c.Assert(err, gc.ErrorMatches, `unknown storage "data/1"`) assertStorageTags(c, att, storageTag) }) c.Assert(called, gc.Equals, 2) c.Assert(filepath.Join(stateDir, "data-0"), jc.IsNonEmptyFile) c.Assert(filepath.Join(stateDir, "data-1"), jc.DoesNotExist) }
func (s *attachmentsSuite) TestAttachmentsSetDying(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") storageTag0 := names.NewStorageTag("data/0") storageTag1 := names.NewStorageTag("data/1") abort := make(chan struct{}) var destroyed, removed bool st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return []params.StorageAttachmentId{{ StorageTag: storageTag0.String(), UnitTag: unitTag.String(), }, { StorageTag: storageTag1.String(), UnitTag: unitTag.String(), }}, 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(u, gc.Equals, unitTag) if s == storageTag0 { return params.StorageAttachment{}, ¶ms.Error{ Message: "not provisioned", Code: params.CodeNotProvisioned, } } c.Assert(s, gc.Equals, storageTag1) return params.StorageAttachment{ StorageTag: storageTag1.String(), UnitTag: unitTag.String(), Life: params.Dying, Kind: params.StorageKindBlock, Location: "/dev/sdb", }, nil }, storageAttachmentLife: func(ids []params.StorageAttachmentId) ([]params.LifeResult, error) { results := make([]params.LifeResult, len(ids)) for i := range ids { results[i].Life = params.Dying } return results, nil }, destroyUnitStorageAttachments: func(u names.UnitTag) error { c.Assert(u, gc.Equals, unitTag) destroyed = true return nil }, remove: func(s names.StorageTag, u names.UnitTag) error { c.Assert(removed, jc.IsFalse) c.Assert(s, gc.Equals, storageTag0) c.Assert(u, gc.Equals, unitTag) removed = true return nil }, } state1, err := storage.ReadStateFile(stateDir, storageTag1) c.Assert(err, jc.ErrorIsNil) err = state1.CommitHook(hook.Info{Kind: hooks.StorageAttached, StorageId: storageTag1.Id()}) c.Assert(err, jc.ErrorIsNil) att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) defer func() { err := att.Stop() c.Assert(err, jc.ErrorIsNil) }() c.Assert(att.Pending(), gc.Equals, 1) err = att.SetDying() c.Assert(err, jc.ErrorIsNil) c.Assert(att.Pending(), gc.Equals, 0) c.Assert(destroyed, jc.IsTrue) c.Assert(removed, jc.IsTrue) }