Esempio n. 1
0
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`)
}
Esempio n. 2
0
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"),
	})
}
Esempio n. 3
0
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))
}
Esempio n. 4
0
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: &params.Error{Message: msg},
		},
		params.StorageInfo{
			params.StorageDetails{
				StorageTag: "storage-db-dir-1000",
				Status:     "attached",
				Persistent: true},
			nil},
	}

	c.Assert(found, jc.DeepEquals, expected)
}
Esempio n. 5
0
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},
			{&params.Error{Message: "badness"}},
			{&params.Error{Code: params.CodeUnauthorized, Message: "permission denied"}},
			{&params.Error{Message: `"unit-mysql-0" is not a valid storage tag`}},
			{&params.Error{Message: `"storage-data-0" is not a valid unit tag`}},
		},
	})
}
Esempio n. 6
0
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,
	})
}
Esempio n. 7
0
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'`)
}
Esempio n. 8
0
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: &params.StorageDetails{StorageTag: oneTag.String()},
					},
					params.StorageDetailsResult{
						Result: &params.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)
}
Esempio n. 9
0
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)
	})
}
Esempio n. 10
0
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)
}
Esempio n. 11
0
// 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
}
Esempio n. 12
0
// 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
}
Esempio n. 13
0
// 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
}
Esempio n. 14
0
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",
		},
	})
}
Esempio n. 15
0
// 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
}
Esempio n. 16
0
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)
	}
}
Esempio n. 17
0
func newHookQueue(attached bool) storage.StorageHookQueue {
	return storage.NewStorageHookQueue(
		names.NewUnitTag("mysql/0"),
		names.NewStorageTag("data/0"),
		attached,
	)
}
Esempio n. 18
0
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)
}
Esempio n. 19
0
// 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
}
Esempio n. 20
0
// 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
}
Esempio n. 21
0
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, &params.ErrorResults{})
		*(result.(*params.ErrorResults)) = params.ErrorResults{
			Results: []params.ErrorResult{{
				Error: &params.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")
}
Esempio n. 22
0
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, &params.NotifyWatchResults{})
		*(result.(*params.NotifyWatchResults)) = params.NotifyWatchResults{
			Results: []params.NotifyWatchResult{{
				Error: &params.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)
}
Esempio n. 23
0
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)
}
Esempio n. 24
0
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
		},
	}
}
Esempio n. 25
0
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)
}
Esempio n. 26
0
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)
}
Esempio n. 27
0
// 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
}
Esempio n. 28
0
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",
			},
		},
	}
}
Esempio n. 29
0
// 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)
}
Esempio n. 30
0
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")
}