func (s *resolverSuite) SetUpTest(c *gc.C) { s.stub = testing.Stub{} s.charmURL = charm.MustParseURL("cs:precise/mysql-2") s.remoteState = remotestate.Snapshot{ CharmModifiedVersion: s.charmModifiedVersion, CharmURL: s.charmURL, } s.opFactory = operation.NewFactory(operation.FactoryParams{}) attachments, err := storage.NewAttachments(&dummyStorageAccessor{}, names.NewUnitTag("u/0"), c.MkDir(), nil) c.Assert(err, jc.ErrorIsNil) s.clearResolved = func() error { return errors.New("unexpected resolved") } s.reportHookError = func(hook.Info) error { return errors.New("unexpected report hook error") } s.resolverConfig = uniter.ResolverConfig{ ClearResolved: func() error { return s.clearResolved() }, ReportHookError: func(info hook.Info) error { return s.reportHookError(info) }, StartRetryHookTimer: func() { s.stub.AddCall("StartRetryHookTimer") }, StopRetryHookTimer: func() { s.stub.AddCall("StopRetryHookTimer") }, ShouldRetryHooks: true, Leadership: leadership.NewResolver(), Actions: uniteractions.NewResolver(), Relations: relation.NewRelationsResolver(&dummyRelations{}), Storage: storage.NewResolver(attachments), Commands: nopResolver{}, } s.resolver = uniter.NewUniterResolver(s.resolverConfig) }
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 *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") }
func (s *attachmentsSuite) TestAttachmentsCommitHook(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) var removed bool 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 }, storageAttachment: func(s names.StorageTag, u names.UnitTag) (params.StorageAttachment, error) { c.Assert(s, gc.Equals, storageTag) return attachment, nil }, remove: func(s names.StorageTag, u names.UnitTag) error { removed = true c.Assert(s, gc.Equals, storageTag) 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(att.Pending(), gc.Equals, 1) stateFile := filepath.Join(stateDir, "data-0") c.Assert(stateFile, jc.DoesNotExist) err = att.CommitHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) data, err := ioutil.ReadFile(stateFile) c.Assert(err, jc.ErrorIsNil) c.Assert(string(data), gc.Equals, "attached: true\n") c.Assert(att.Pending(), gc.Equals, 0) c.Assert(removed, jc.IsFalse) err = att.CommitHook(hook.Info{ Kind: hooks.StorageDetaching, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) c.Assert(stateFile, jc.DoesNotExist) c.Assert(removed, jc.IsTrue) }
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 *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 *attachmentsSuite) TestNewAttachments(c *gc.C) { stateDir := filepath.Join(c.MkDir(), "nonexistent") unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return nil, nil }, } _, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) // state dir should have been created. c.Assert(stateDir, jc.IsDirectory) }
func (s *attachmentsSuite) TestAttachmentsStorage(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { return nil, nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) r := storage.NewResolver(att) storageTag := names.NewStorageTag("data/0") _, err = att.Storage(storageTag) c.Assert(err, jc.Satisfies, errors.IsNotFound) assertStorageTags(c, att) // Inform the resolver of an attachment. localState := resolver.LocalState{State: operation.State{ Kind: operation.Continue, }} op, err := r.NextOp(localState, remotestate.Snapshot{ Life: params.Alive, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: { Kind: params.StorageKindBlock, Life: params.Alive, Location: "/dev/sdb", Attached: true, }, }, }, &mockOperations{}) c.Assert(err, jc.ErrorIsNil) c.Assert(op.String(), gc.Equals, "run hook storage-attached") assertStorageTags(c, att, storageTag) 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.Kind(), gc.Equals, corestorage.StorageKindBlock) c.Assert(ctx.Location(), gc.Equals, "/dev/sdb") }
func (s *attachmentsSuite) TestAttachmentsWaitPending(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) storageTag := names.NewStorageTag("data/0") st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { return nil, nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) r := storage.NewResolver(att) nextOp := func(installed bool) error { localState := resolver.LocalState{State: operation.State{ Installed: installed, Kind: operation.Continue, }} _, err := r.NextOp(localState, remotestate.Snapshot{ Life: params.Alive, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: { Life: params.Alive, Attached: false, }, }, }, &mockOperations{}) return err } // Inform the resolver of a new, unprovisioned storage attachment. // Before install, we should wait for its completion; after install, // we should not. err = nextOp(false /* workload not installed */) c.Assert(att.Pending(), gc.Equals, 1) c.Assert(err, gc.Equals, resolver.ErrWaiting) err = nextOp(true /* workload installed */) c.Assert(err, gc.Equals, resolver.ErrNoOperation) }
func (s *resolverSuite) SetUpTest(c *gc.C) { s.charmURL = charm.MustParseURL("cs:precise/mysql-2") s.remoteState = remotestate.Snapshot{ CharmURL: s.charmURL, } s.opFactory = operation.NewFactory(operation.FactoryParams{}) attachments, err := storage.NewAttachments(&dummyStorageAccessor{}, names.NewUnitTag("u/0"), c.MkDir(), nil) c.Assert(err, jc.ErrorIsNil) s.resolver = uniter.NewUniterResolver( func() error { return errors.New("unexpected resolved") }, func(_ hook.Info) error { return errors.New("unexpected report hook error") }, func() error { return nil }, uniteractions.NewResolver(), leadership.NewResolver(), relation.NewRelationsResolver(&dummyRelations{}), storage.NewResolver(attachments), ) }
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) 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 }, 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) // There should be no context for data/0 until a required remote state change occurs. _, ok := att.Storage(storageTag) c.Assert(ok, jc.Satisfies, errors.IsNotFound) assertStorageTags(c, att) err = att.UpdateStorage([]names.StorageTag{storageTag}) c.Assert(err, jc.ErrorIsNil) assertStorageTags(c, att, storageTag) storageResolver := storage.NewResolver(att) storage.SetStorageLife(storageResolver, map[names.StorageTag]params.Life{ storageTag: params.Alive, }) localState := resolver.LocalState{ State: operation.State{ Kind: operation.Continue, }, } remoteState := remotestate.Snapshot{ Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: remotestate.StorageSnapshot{ Kind: params.StorageKindBlock, Life: params.Alive, Location: "/dev/sdb", Attached: true, }, }, } op, err := storageResolver.NextOp(localState, remoteState, &mockOperations{}) c.Assert(err, jc.ErrorIsNil) c.Assert(op.String(), gc.Equals, "run hook storage-attached") 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.Kind(), gc.Equals, corestorage.StorageKindBlock) c.Assert(ctx.Location(), gc.Equals, "/dev/sdb") }
func (s *attachmentsSuite) TestAttachmentsSetDying(c *gc.C) { stateDir := c.MkDir() unitTag := names.NewUnitTag("mysql/0") abort := make(chan struct{}) storageTag := names.NewStorageTag("data/0") var destroyed, removed bool st := &mockStorageAccessor{ unitStorageAttachments: func(u names.UnitTag) ([]params.StorageAttachmentId, error) { c.Assert(u, gc.Equals, unitTag) return []params.StorageAttachmentId{{ StorageTag: storageTag.String(), UnitTag: unitTag.String(), }}, nil }, storageAttachment: func(s names.StorageTag, u names.UnitTag) (params.StorageAttachment, error) { c.Assert(u, gc.Equals, unitTag) c.Assert(s, gc.Equals, storageTag) return params.StorageAttachment{}, ¶ms.Error{ Message: "not provisioned", Code: params.CodeNotProvisioned, } }, 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, storageTag) c.Assert(u, gc.Equals, unitTag) removed = true return nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) c.Assert(att.Pending(), gc.Equals, 1) r := storage.NewResolver(att) // Inform the resolver that the unit is Dying. The storage is still // Alive, and is now provisioned, but will be destroyed and removed // by the resolver. localState := resolver.LocalState{State: operation.State{ Kind: operation.Continue, }} _, err = r.NextOp(localState, remotestate.Snapshot{ Life: params.Dying, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: { Kind: params.StorageKindBlock, Life: params.Alive, Location: "/dev/sdb", Attached: true, }, }, }, &mockOperations{}) c.Assert(err, gc.Equals, resolver.ErrNoOperation) c.Assert(destroyed, jc.IsTrue) c.Assert(att.Pending(), gc.Equals, 0) c.Assert(removed, jc.IsTrue) }
func (s *attachmentsSuite) TestAttachmentsCommitHook(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) { return nil, nil }, remove: func(s names.StorageTag, u names.UnitTag) error { removed = true c.Assert(s, gc.Equals, storageTag) return nil }, } att, err := storage.NewAttachments(st, unitTag, stateDir, abort) c.Assert(err, jc.ErrorIsNil) r := storage.NewResolver(att) // Inform the resolver of an attachment. localState := resolver.LocalState{State: operation.State{ Kind: operation.Continue, }} _, err = r.NextOp(localState, remotestate.Snapshot{ Life: params.Alive, Storage: map[names.StorageTag]remotestate.StorageSnapshot{ storageTag: { Kind: params.StorageKindBlock, Life: params.Alive, Location: "/dev/sdb", Attached: true, }, }, }, &mockOperations{}) c.Assert(err, jc.ErrorIsNil) c.Assert(att.Pending(), gc.Equals, 1) // No file exists until storage-attached is committed. stateFile := filepath.Join(stateDir, "data-0") c.Assert(stateFile, jc.DoesNotExist) err = att.CommitHook(hook.Info{ Kind: hooks.StorageAttached, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) data, err := ioutil.ReadFile(stateFile) c.Assert(err, jc.ErrorIsNil) c.Assert(string(data), gc.Equals, "attached: true\n") c.Assert(att.Pending(), gc.Equals, 0) c.Assert(removed, jc.IsFalse) err = att.CommitHook(hook.Info{ Kind: hooks.StorageDetaching, StorageId: storageTag.Id(), }) c.Assert(err, jc.ErrorIsNil) c.Assert(stateFile, jc.DoesNotExist) c.Assert(removed, jc.IsTrue) }
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) }
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 (u *Uniter) init(unitTag names.UnitTag) (err error) { u.unit, err = u.st.Unit(unitTag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } if err = u.setupLocks(); err != nil { return err } if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { return err } if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { return errors.Trace(err) } relations, err := newRelations(u.st, unitTag, u.paths, u.tomb.Dying()) if err != nil { return errors.Annotatef(err, "cannot create relations") } u.relations = relations storageAttachments, err := storage.NewAttachments( u.st, unitTag, u.paths.State.StorageDir, u.tomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create storage hook source") } u.storage = storageAttachments u.addCleanup(storageAttachments.Stop) deployer, err := charm.NewDeployer( u.paths.State.CharmDir, u.paths.State.DeployerDir, charm.NewBundlesDir(u.paths.State.BundlesDir), ) if err != nil { return errors.Annotatef(err, "cannot create deployer") } u.deployer = &deployerProxy{deployer} contextFactory, err := runner.NewContextFactory( u.st, unitTag, u.leadershipTracker, u.relations.GetInfo, u.storage, u.paths, ) if err != nil { return err } runnerFactory, err := runner.NewFactory( u.st, u.paths, contextFactory, ) if err != nil { return err } u.operationFactory = operation.NewFactory(operation.FactoryParams{ Deployer: u.deployer, RunnerFactory: runnerFactory, Callbacks: &operationCallbacks{u}, StorageUpdater: u.storage, Abort: u.tomb.Dying(), MetricSender: u.unit, MetricSpoolDir: u.paths.GetMetricsSpoolDir(), }) operationExecutor, err := u.newOperationExecutor(u.paths.State.OperationsFile, u.getServiceCharmURL, u.acquireExecutionLock) if err != nil { return err } u.operationExecutor = operationExecutor logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) u.runListener, err = NewRunListener(u, u.paths.Runtime.JujuRunSocket) if err != nil { return err } u.addCleanup(func() error { // TODO(fwereade): RunListener returns no error on Close. This seems wrong. u.runListener.Close() return nil }) // The socket needs to have permissions 777 in order for other users to use it. if version.Current.OS != version.Windows { return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) } return nil }
func (u *Uniter) init(unitTag names.UnitTag) (err error) { u.unit, err = u.st.Unit(unitTag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } if err = u.setupLocks(); err != nil { return err } if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { return err } if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { return errors.Trace(err) } relations, err := relation.NewRelations( u.st, unitTag, u.paths.State.CharmDir, u.paths.State.RelationsDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create relations") } u.relations = relations storageAttachments, err := storage.NewAttachments( u.st, unitTag, u.paths.State.StorageDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create storage hook source") } u.storage = storageAttachments u.commands = runcommands.NewCommands() u.commandChannel = make(chan string) deployer, err := charm.NewDeployer( u.paths.State.CharmDir, u.paths.State.DeployerDir, charm.NewBundlesDir(u.paths.State.BundlesDir, u.downloader), ) if err != nil { return errors.Annotatef(err, "cannot create deployer") } u.deployer = &deployerProxy{deployer} contextFactory, err := context.NewContextFactory( u.st, unitTag, u.leadershipTracker, u.relations.GetInfo, u.storage, u.paths, u.clock, ) if err != nil { return err } runnerFactory, err := runner.NewFactory( u.st, u.paths, contextFactory, ) if err != nil { return errors.Trace(err) } u.operationFactory = operation.NewFactory(operation.FactoryParams{ Deployer: u.deployer, RunnerFactory: runnerFactory, Callbacks: &operationCallbacks{u}, Abort: u.catacomb.Dying(), MetricSpoolDir: u.paths.GetMetricsSpoolDir(), }) operationExecutor, err := u.newOperationExecutor(u.paths.State.OperationsFile, u.getServiceCharmURL, u.acquireExecutionLock) if err != nil { return errors.Trace(err) } u.operationExecutor = operationExecutor logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) commandRunner, err := NewChannelCommandRunner(ChannelCommandRunnerConfig{ Abort: u.catacomb.Dying(), Commands: u.commands, CommandChannel: u.commandChannel, }) if err != nil { return errors.Annotate(err, "creating command runner") } u.runListener, err = NewRunListener(RunListenerConfig{ SocketPath: u.paths.Runtime.JujuRunSocket, CommandRunner: commandRunner, }) if err != nil { return errors.Trace(err) } rlw := newRunListenerWrapper(u.runListener) if err := u.catacomb.Add(rlw); err != nil { return errors.Trace(err) } // The socket needs to have permissions 777 in order for other users to use it. if jujuos.HostOS() != jujuos.Windows { return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) } return nil }
func (u *Uniter) init(unitTag names.UnitTag) (err error) { u.unit, err = u.st.Unit(unitTag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } // If initialising for the first time after deploying, update the status. currentStatus, err := u.unit.UnitStatus() if err != nil { return err } // TODO(fwereade/wallyworld): we should have an explicit place in the model // to tell us when we've hit this point, instead of piggybacking on top of // status and/or status history. // If the previous status was waiting for machine, we transition to the next step. if currentStatus.Status == string(status.Waiting) && (currentStatus.Info == status.MessageWaitForMachine || currentStatus.Info == status.MessageInstallingAgent) { if err := u.unit.SetUnitStatus(status.Waiting, status.MessageInitializingAgent, nil); err != nil { return errors.Trace(err) } } if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { return err } if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { return errors.Trace(err) } relations, err := relation.NewRelations( u.st, unitTag, u.paths.State.CharmDir, u.paths.State.RelationsDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create relations") } u.relations = relations storageAttachments, err := storage.NewAttachments( u.st, unitTag, u.paths.State.StorageDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create storage hook source") } u.storage = storageAttachments u.commands = runcommands.NewCommands() u.commandChannel = make(chan string) deployer, err := charm.NewDeployer( u.paths.State.CharmDir, u.paths.State.DeployerDir, charm.NewBundlesDir(u.paths.State.BundlesDir, u.downloader), ) if err != nil { return errors.Annotatef(err, "cannot create deployer") } contextFactory, err := context.NewContextFactory( u.st, unitTag, u.leadershipTracker, u.relations.GetInfo, u.storage, u.paths, u.clock, ) if err != nil { return err } runnerFactory, err := runner.NewFactory( u.st, u.paths, contextFactory, ) if err != nil { return errors.Trace(err) } u.operationFactory = operation.NewFactory(operation.FactoryParams{ Deployer: deployer, RunnerFactory: runnerFactory, Callbacks: &operationCallbacks{u}, Abort: u.catacomb.Dying(), MetricSpoolDir: u.paths.GetMetricsSpoolDir(), }) operationExecutor, err := u.newOperationExecutor(u.paths.State.OperationsFile, u.getServiceCharmURL, u.acquireExecutionLock) if err != nil { return errors.Trace(err) } u.operationExecutor = operationExecutor logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) commandRunner, err := NewChannelCommandRunner(ChannelCommandRunnerConfig{ Abort: u.catacomb.Dying(), Commands: u.commands, CommandChannel: u.commandChannel, }) if err != nil { return errors.Annotate(err, "creating command runner") } u.runListener, err = NewRunListener(RunListenerConfig{ SocketPath: u.paths.Runtime.JujuRunSocket, CommandRunner: commandRunner, }) if err != nil { return errors.Trace(err) } rlw := newRunListenerWrapper(u.runListener) if err := u.catacomb.Add(rlw); err != nil { return errors.Trace(err) } // The socket needs to have permissions 777 in order for other users to use it. if jujuos.HostOS() != jujuos.Windows { return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) } return nil }