Beispiel #1
0
// assertMachineStorageRefs ensures that the specified machine's set of volume
// and filesystem references corresponds exactly to the volume and filesystem
// attachments that relate to the machine.
func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) {
	machines, closer := state.GetRawCollection(st, state.MachinesC)
	defer closer()

	var doc struct {
		Volumes     []string `bson:"volumes,omitempty"`
		Filesystems []string `bson:"filesystems,omitempty"`
	}
	err := machines.FindId(state.DocID(st, m.Id())).One(&doc)
	c.Assert(err, jc.ErrorIsNil)

	have := make(set.Tags)
	for _, v := range doc.Volumes {
		have.Add(names.NewVolumeTag(v))
	}
	for _, f := range doc.Filesystems {
		have.Add(names.NewFilesystemTag(f))
	}

	expect := make(set.Tags)
	volumeAttachments, err := st.MachineVolumeAttachments(m)
	c.Assert(err, jc.ErrorIsNil)
	for _, a := range volumeAttachments {
		expect.Add(a.Volume())
	}
	filesystemAttachments, err := st.MachineFilesystemAttachments(m)
	c.Assert(err, jc.ErrorIsNil)
	for _, a := range filesystemAttachments {
		expect.Add(a.Filesystem())
	}

	c.Assert(have, jc.DeepEquals, expect)
}
Beispiel #2
0
// MachineFilesystemAttachments returns all of the FilesystemAttachments for the
// specified machine.
func (st *State) MachineFilesystemAttachments(machine names.MachineTag) ([]FilesystemAttachment, error) {
	attachments, err := st.filesystemAttachments(bson.D{{"machineid", machine.Id()}})
	if err != nil {
		return nil, errors.Annotatef(err, "getting filesystem attachments for machine %q", machine.Id())
	}
	return attachments, nil
}
Beispiel #3
0
// MachineVolumeAttachments returns all of the VolumeAttachments for the
// specified machine.
func (st *State) MachineVolumeAttachments(machine names.MachineTag) ([]VolumeAttachment, error) {
	attachments, err := st.volumeAttachments(bson.D{{"machineid", machine.Id()}})
	if err != nil {
		return nil, errors.Annotatef(err, "getting volume attachments for machine %q", machine.Id())
	}
	return attachments, nil
}
Beispiel #4
0
// removeMachineFilesystemsOps returns txn.Ops to remove non-persistent filesystems
// attached to the specified machine. This is used when the given machine is
// being removed from state.
func (st *State) removeMachineFilesystemsOps(machine names.MachineTag) ([]txn.Op, error) {
	attachments, err := st.MachineFilesystemAttachments(machine)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ops := make([]txn.Op, 0, len(attachments))
	for _, a := range attachments {
		filesystemTag := a.Filesystem()
		// When removing the machine, there should only remain
		// non-persistent storage. This will be implicitly
		// removed when the machine is removed, so we do not
		// use removeFilesystemAttachmentOps or removeFilesystemOps,
		// which track and update related documents.
		ops = append(ops, txn.Op{
			C:      filesystemAttachmentsC,
			Id:     filesystemAttachmentId(machine.Id(), filesystemTag.Id()),
			Assert: txn.DocExists,
			Remove: true,
		})
		canRemove, err := isFilesystemInherentlyMachineBound(st, filesystemTag)
		if err != nil {
			return nil, errors.Trace(err)
		}
		if !canRemove {
			return nil, errors.Errorf("machine has non-machine bound filesystem %v", filesystemTag.Id())
		}
		ops = append(ops, txn.Op{
			C:      filesystemsC,
			Id:     filesystemTag.Id(),
			Assert: txn.DocExists,
			Remove: true,
		})
	}
	return ops, nil
}
Beispiel #5
0
// refreshMachine refreshes the specified machine's instance ID. If it is set,
// then the machine watcher is stopped and pending entities' parameters are
// updated. If the machine is not provisioned yet, this method is a no-op.
func refreshMachine(ctx *context, tag names.MachineTag) error {
	w, ok := ctx.machines[tag]
	if !ok {
		return errors.Errorf("machine %s is not being watched", tag.Id())
	}
	stopAndRemove := func() error {
		if err := w.stop(); err != nil {
			return errors.Annotate(err, "stopping machine watcher")
		}
		delete(ctx.machines, tag)
		return nil
	}
	results, err := ctx.machineAccessor.InstanceIds([]names.MachineTag{tag})
	if err != nil {
		return errors.Annotate(err, "getting machine instance ID")
	}
	if err := results[0].Error; err != nil {
		if params.IsCodeNotProvisioned(err) {
			return nil
		} else if params.IsCodeNotFound(err) {
			// Machine is gone, so stop watching.
			return stopAndRemove()
		}
		return errors.Annotate(err, "getting machine instance ID")
	}
	machineProvisioned(ctx, tag, instance.Id(results[0].Result))
	// machine provisioning is the only thing we care about;
	// stop the watcher.
	return stopAndRemove()
}
Beispiel #6
0
func (s stateShim) WatchMachine(tag names.MachineTag) (state.NotifyWatcher, error) {
	m, err := s.Machine(tag.Id())
	if err != nil {
		return nil, errors.Trace(err)
	}
	return m.Watch(), nil
}
Beispiel #7
0
func (s stateShim) MachineInstanceId(tag names.MachineTag) (instance.Id, error) {
	m, err := s.Machine(tag.Id())
	if err != nil {
		return "", errors.Trace(err)
	}
	return m.InstanceId()
}
Beispiel #8
0
func detachVolumeOps(m names.MachineTag, v names.VolumeTag) []txn.Op {
	return []txn.Op{{
		C:      volumeAttachmentsC,
		Id:     volumeAttachmentId(m.Id(), v.Id()),
		Assert: isAliveDoc,
		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
	}}
}
Beispiel #9
0
func detachFilesystemOps(m names.MachineTag, f names.FilesystemTag) []txn.Op {
	return []txn.Op{{
		C:      filesystemAttachmentsC,
		Id:     filesystemAttachmentId(m.Id(), f.Id()),
		Assert: isAliveDoc,
		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
	}}
}
Beispiel #10
0
// ReleaseContainerAddresses releases a static IP address allocated to a
// container.
func (st *State) ReleaseContainerAddresses(containerTag names.MachineTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot release static addresses for %q", containerTag.Id())
	var result params.ErrorResults
	args := params.Entities{
		Entities: []params.Entity{{Tag: containerTag.String()}},
	}
	if err := st.facade.FacadeCall("ReleaseContainerAddresses", args, &result); err != nil {
		return err
	}
	return result.OneError()
}
Beispiel #11
0
// GetSSHHostKeys retrieves the SSH host keys stored for an entity.
///
// NOTE: Currently only machines are supported. This can be
// generalised to take other tag types later, if and when we need it.
func (st *State) GetSSHHostKeys(tag names.MachineTag) (SSHHostKeys, error) {
	coll, closer := st.getCollection(sshHostKeysC)
	defer closer()

	var doc sshHostKeysDoc
	err := coll.FindId(machineGlobalKey(tag.Id())).One(&doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("SSH host keys for %s", tag)
	} else if err != nil {
		return nil, errors.Annotate(err, "SSH host key lookup failed")
	}
	return SSHHostKeys(doc.Keys), nil
}
Beispiel #12
0
// FilesystemAttachment returns the FilesystemAttachment corresponding to
// the specified filesystem and machine.
func (st *State) FilesystemAttachment(machine names.MachineTag, filesystem names.FilesystemTag) (FilesystemAttachment, error) {
	coll, cleanup := st.getCollection(filesystemAttachmentsC)
	defer cleanup()

	var att filesystemAttachment
	err := coll.FindId(filesystemAttachmentId(machine.Id(), filesystem.Id())).One(&att.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("filesystem %q on machine %q", filesystem.Id(), machine.Id())
	} else if err != nil {
		return nil, errors.Annotatef(err, "getting filesystem %q on machine %q", filesystem.Id(), machine.Id())
	}
	return &att, nil
}
Beispiel #13
0
// NewAPI returns a new API client for the Singular facade. It exposes methods
// for claiming and observing administration responsibility for the apiCaller's
// model, on behalf of the supplied controller machine.
func NewAPI(apiCaller base.APICaller, controllerTag names.MachineTag) (*API, error) {
	controllerId := controllerTag.Id()
	if !names.IsValidMachine(controllerId) {
		return nil, errors.NotValidf("controller tag")
	}
	modelTag, err := apiCaller.ModelTag()
	if err != nil {
		return nil, errors.Trace(err)
	}
	facadeCaller := base.NewFacadeCaller(apiCaller, "Singular")
	return &API{
		modelTag:      modelTag,
		controllerTag: controllerTag,
		facadeCaller:  facadeCaller,
	}, nil
}
Beispiel #14
0
func setVolumeAttachmentInfoOps(machine names.MachineTag, volume names.VolumeTag, info VolumeAttachmentInfo, unsetParams bool) []txn.Op {
	asserts := isAliveDoc
	update := bson.D{
		{"$set", bson.D{{"info", &info}}},
	}
	if unsetParams {
		asserts = append(asserts, bson.DocElem{"info", bson.D{{"$exists", false}}})
		asserts = append(asserts, bson.DocElem{"params", bson.D{{"$exists", true}}})
		update = append(update, bson.DocElem{"$unset", bson.D{{"params", nil}}})
	}
	return []txn.Op{{
		C:      volumeAttachmentsC,
		Id:     volumeAttachmentId(machine.Id(), volume.Id()),
		Assert: asserts,
		Update: update,
	}}
}
Beispiel #15
0
// SetSSHHostKeys updates the stored SSH host keys for an entity.
//
// See the note for GetSSHHostKeys regarding supported entities.
func (st *State) SetSSHHostKeys(tag names.MachineTag, keys SSHHostKeys) error {
	id := machineGlobalKey(tag.Id())
	doc := sshHostKeysDoc{
		Keys: keys,
	}
	err := st.runTransaction([]txn.Op{
		{
			C:      sshHostKeysC,
			Id:     id,
			Insert: doc,
		}, {
			C:      sshHostKeysC,
			Id:     id,
			Update: bson.M{"$set": doc},
		},
	})
	return errors.Annotate(err, "SSH host key update failed")
}
Beispiel #16
0
func removeFilesystemAttachmentOps(m names.MachineTag, f *filesystem) []txn.Op {
	decrefFilesystemOp := machineStorageDecrefOp(
		filesystemsC, f.doc.FilesystemId,
		f.doc.AttachmentCount, f.doc.Life,
		m, f.doc.Binding,
	)
	return []txn.Op{{
		C:      filesystemAttachmentsC,
		Id:     filesystemAttachmentId(m.Id(), f.doc.FilesystemId),
		Assert: bson.D{{"life", Dying}},
		Remove: true,
	}, decrefFilesystemOp, {
		C:      machinesC,
		Id:     m.Id(),
		Assert: txn.DocExists,
		Update: bson.D{{"$pull", bson.D{{"filesystems", f.doc.FilesystemId}}}},
	}}
}
Beispiel #17
0
func removeVolumeAttachmentOps(m names.MachineTag, v *volume) []txn.Op {
	decrefVolumeOp := machineStorageDecrefOp(
		volumesC, v.doc.Name,
		v.doc.AttachmentCount, v.doc.Life,
		m, v.doc.Binding,
	)
	return []txn.Op{{
		C:      volumeAttachmentsC,
		Id:     volumeAttachmentId(m.Id(), v.doc.Name),
		Assert: bson.D{{"life", Dying}},
		Remove: true,
	}, decrefVolumeOp, {
		C:      machinesC,
		Id:     m.Id(),
		Assert: txn.DocExists,
		Update: bson.D{{"$pull", bson.D{{"volumes", v.doc.Name}}}},
	}}
}
Beispiel #18
0
func isMachineMaster(st *state.State, tag names.MachineTag) (bool, error) {
	if st == nil {
		// If there is no state, we aren't a master.
		return false, nil
	}
	// Not calling the agent openState method as it does other checks
	// we really don't care about here.  All we need here is the machine
	// so we can determine if we are the master or not.
	machine, err := st.Machine(tag.Id())
	if err != nil {
		// This shouldn't happen, and if it does, the state worker will have
		// found out before us, and already errored, or is likely to error out
		// very shortly.  All we do here is return the error. The state worker
		// returns an error that will cause the agent to be terminated.
		return false, errors.Trace(err)
	}
	isMaster, err := mongo.IsMaster(st.MongoSession(), machine)
	if err != nil {
		return false, errors.Trace(err)
	}
	return isMaster, nil
}
Beispiel #19
0
// SetVolumeAttachmentInfo sets the VolumeAttachmentInfo for the specified
// volume attachment.
func (st *State) SetVolumeAttachmentInfo(machineTag names.MachineTag, volumeTag names.VolumeTag, info VolumeAttachmentInfo) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set info for volume attachment %s:%s", volumeTag.Id(), machineTag.Id())
	v, err := st.Volume(volumeTag)
	if err != nil {
		return errors.Trace(err)
	}
	// Ensure volume is provisioned before setting attachment info.
	// A volume cannot go from being provisioned to unprovisioned,
	// so there is no txn.Op for this below.
	if _, err := v.Info(); err != nil {
		return errors.Trace(err)
	}
	// Also ensure the machine is provisioned.
	m, err := st.Machine(machineTag.Id())
	if err != nil {
		return errors.Trace(err)
	}
	if _, err := m.InstanceId(); err != nil {
		return errors.Trace(err)
	}
	return st.setVolumeAttachmentInfo(machineTag, volumeTag, info)
}
Beispiel #20
0
// SetFilesystemAttachmentInfo sets the FilesystemAttachmentInfo for the
// specified filesystem attachment.
func (st *State) SetFilesystemAttachmentInfo(
	machineTag names.MachineTag,
	filesystemTag names.FilesystemTag,
	info FilesystemAttachmentInfo,
) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set info for filesystem attachment %s:%s", filesystemTag.Id(), machineTag.Id())
	f, err := st.Filesystem(filesystemTag)
	if err != nil {
		return errors.Trace(err)
	}
	// Ensure filesystem is provisioned before setting attachment info.
	// A filesystem cannot go from being provisioned to unprovisioned,
	// so there is no txn.Op for this below.
	if _, err := f.Info(); err != nil {
		return errors.Trace(err)
	}
	// Also ensure the machine is provisioned.
	m, err := st.Machine(machineTag.Id())
	if err != nil {
		return errors.Trace(err)
	}
	if _, err := m.InstanceId(); err != nil {
		return errors.Trace(err)
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		fsa, err := st.FilesystemAttachment(machineTag, filesystemTag)
		if err != nil {
			return nil, errors.Trace(err)
		}
		// If the filesystem attachment has parameters, unset them
		// when we set info for the first time, ensuring that params
		// and info are mutually exclusive.
		_, unsetParams := fsa.Params()
		ops := setFilesystemAttachmentInfoOps(machineTag, filesystemTag, info, unsetParams)
		return ops, nil
	}
	return st.run(buildTxn)
}
Beispiel #21
0
// VolumeAttachment returns the VolumeAttachment corresponding to
// the specified volume and machine.
func (st *State) VolumeAttachment(machine names.MachineTag, volume names.VolumeTag) (VolumeAttachment, error) {
	coll, cleanup := st.getCollection(volumeAttachmentsC)
	defer cleanup()

	var att volumeAttachment
	err := coll.FindId(volumeAttachmentId(machine.Id(), volume.Id())).One(&att.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("volume %q on machine %q", volume.Id(), machine.Id())
	} else if err != nil {
		return nil, errors.Annotatef(err, "getting volume %q on machine %q", volume.Id(), machine.Id())
	}
	return &att, nil
}
Beispiel #22
0
func (u *UniterAPIV1) getMachine(tag names.MachineTag) (*state.Machine, error) {
	return u.st.Machine(tag.Id())
}
Beispiel #23
0
// RemoveFilesystemAttachment removes the filesystem attachment from state.
// Removing a volume-backed filesystem attachment will cause the volume to
// be detached.
func (st *State) RemoveFilesystemAttachment(machine names.MachineTag, filesystem names.FilesystemTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "removing attachment of filesystem %s from machine %s", filesystem.Id(), machine.Id())
	buildTxn := func(attempt int) ([]txn.Op, error) {
		attachment, err := st.FilesystemAttachment(machine, filesystem)
		if errors.IsNotFound(err) && attempt > 0 {
			// We only ignore IsNotFound on attempts after the
			// first, since we expect the filesystem attachment to
			// be there initially.
			return nil, jujutxn.ErrNoOperations
		}
		if err != nil {
			return nil, errors.Trace(err)
		}
		if attachment.Life() != Dying {
			return nil, errors.New("filesystem attachment is not dying")
		}
		f, err := st.filesystemByTag(filesystem)
		if err != nil {
			return nil, errors.Trace(err)
		}
		ops := removeFilesystemAttachmentOps(machine, f)
		// If the filesystem is backed by a volume, the volume
		// attachment can and should be destroyed once the
		// filesystem attachment is removed.
		volumeAttachment, err := st.filesystemVolumeAttachment(machine, filesystem)
		if err != nil {
			if errors.Cause(err) != ErrNoBackingVolume && !errors.IsNotFound(err) {
				return nil, errors.Trace(err)
			}
		} else if volumeAttachment.Life() == Alive {
			ops = append(ops, detachVolumeOps(machine, volumeAttachment.Volume())...)
		}
		return ops, nil
	}
	return st.run(buildTxn)
}
Beispiel #24
0
// WatchBlockDevices returns a new NotifyWatcher watching for
// changes to block devices associated with the specified machine.
func (st *State) WatchBlockDevices(machine names.MachineTag) NotifyWatcher {
	return newBlockDevicesWatcher(st, machine.Id())
}
Beispiel #25
0
// DetachFilesystem marks the filesystem attachment identified by the specified machine
// and filesystem tags as Dying, if it is Alive.
func (st *State) DetachFilesystem(machine names.MachineTag, filesystem names.FilesystemTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "detaching filesystem %s from machine %s", filesystem.Id(), machine.Id())
	buildTxn := func(attempt int) ([]txn.Op, error) {
		fsa, err := st.FilesystemAttachment(machine, filesystem)
		if err != nil {
			return nil, errors.Trace(err)
		}
		if fsa.Life() != Alive {
			return nil, jujutxn.ErrNoOperations
		}
		ops := detachFilesystemOps(machine, filesystem)
		return ops, nil
	}
	return st.run(buildTxn)
}
Beispiel #26
0
// BlockDevices returns the BlockDeviceInfo for the specified machine.
func (st *State) BlockDevices(machine names.MachineTag) ([]BlockDeviceInfo, error) {
	return st.blockDevices(machine.Id())
}
Beispiel #27
0
// DetachVolume marks the volume attachment identified by the specified machine
// and volume tags as Dying, if it is Alive. DetachVolume will fail with a
// IsContainsFilesystem error if the volume contains an attached filesystem; the
// filesystem attachment must be removed first.
func (st *State) DetachVolume(machine names.MachineTag, volume names.VolumeTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "detaching volume %s from machine %s", volume.Id(), machine.Id())
	// If the volume is backing a filesystem, the volume cannot be detached
	// until the filesystem has been detached.
	if _, err := st.volumeFilesystemAttachment(machine, volume); err == nil {
		return &errContainsFilesystem{errors.New("volume contains attached filesystem")}
	} else if !errors.IsNotFound(err) {
		return errors.Trace(err)
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		va, err := st.VolumeAttachment(machine, volume)
		if err != nil {
			return nil, errors.Trace(err)
		}
		if va.Life() != Alive {
			return nil, jujutxn.ErrNoOperations
		}
		return detachVolumeOps(machine, volume), nil
	}
	return st.run(buildTxn)
}
Beispiel #28
0
// RemoveVolumeAttachment removes the volume attachment from state.
// RemoveVolumeAttachment will fail if the attachment is not Dying.
func (st *State) RemoveVolumeAttachment(machine names.MachineTag, volume names.VolumeTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "removing attachment of volume %s from machine %s", volume.Id(), machine.Id())
	buildTxn := func(attempt int) ([]txn.Op, error) {
		attachment, err := st.VolumeAttachment(machine, volume)
		if errors.IsNotFound(err) && attempt > 0 {
			// We only ignore IsNotFound on attempts after the
			// first, since we expect the volume attachment to
			// be there initially.
			return nil, jujutxn.ErrNoOperations
		}
		if err != nil {
			return nil, errors.Trace(err)
		}
		if attachment.Life() != Dying {
			return nil, errors.New("volume attachment is not dying")
		}
		v, err := st.volumeByTag(volume)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return removeVolumeAttachmentOps(machine, v), nil
	}
	return st.run(buildTxn)
}