Example #1
0
// SetFilesystemStatus sets the status of the specified filesystem.
func (st *State) SetFilesystemStatus(tag names.FilesystemTag, status Status, info string, data map[string]interface{}) error {
	switch status {
	case StatusAttaching, StatusAttached, StatusDetaching, StatusDetached, StatusDestroying:
	case StatusError:
		if info == "" {
			return errors.Errorf("cannot set status %q without info", status)
		}
	case StatusPending:
		// If a filesystem is not yet provisioned, we allow its status
		// to be set back to pending (when a retry is to occur).
		v, err := st.Filesystem(tag)
		if err != nil {
			return errors.Trace(err)
		}
		_, err = v.Info()
		if errors.IsNotProvisioned(err) {
			break
		}
		return errors.Errorf("cannot set status %q", status)
	default:
		return errors.Errorf("cannot set invalid status %q", status)
	}
	return setStatus(st, setStatusParams{
		badge:     "filesystem",
		globalKey: filesystemGlobalKey(tag.Id()),
		status:    status,
		message:   info,
		rawData:   data,
	})
}
Example #2
0
// FilesystemAttachments returns all of the FilesystemAttachments for the
// specified filesystem.
func (st *State) FilesystemAttachments(filesystem names.FilesystemTag) ([]FilesystemAttachment, error) {
	attachments, err := st.filesystemAttachments(bson.D{{"filesystemid", filesystem.Id()}})
	if err != nil {
		return nil, errors.Annotatef(err, "getting attachments for filesystem %q", filesystem.Id())
	}
	return attachments, nil
}
Example #3
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}}}},
	}}
}
Example #4
0
// SetFilesystemInfo sets the FilesystemInfo for the specified filesystem.
func (st *State) SetFilesystemInfo(tag names.FilesystemTag, info FilesystemInfo) (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set info for filesystem %q", tag.Id())
	if info.FilesystemId == "" {
		return errors.New("filesystem ID not set")
	}
	fs, err := st.Filesystem(tag)
	if err != nil {
		return errors.Trace(err)
	}
	// If the filesystem is volume-backed, the volume must be provisioned
	// and attachment first.
	if volumeTag, err := fs.Volume(); err == nil {
		machineTag, ok := names.FilesystemMachine(tag)
		if !ok {
			return errors.Errorf("filesystem %s is not machine-scoped, but volume-backed", tag.Id())
		}
		volumeAttachment, err := st.VolumeAttachment(machineTag, volumeTag)
		if err != nil {
			return errors.Trace(err)
		}
		if _, err := volumeAttachment.Info(); err != nil {
			return errors.Trace(err)
		}
	} else if errors.Cause(err) != ErrNoBackingVolume {
		return errors.Trace(err)
	}
	buildTxn := func(attempt int) ([]txn.Op, error) {
		if attempt > 0 {
			fs, err = st.Filesystem(tag)
			if err != nil {
				return nil, errors.Trace(err)
			}
		}
		// If the filesystem has parameters, unset them
		// when we set info for the first time, ensuring
		// that params and info are mutually exclusive.
		var unsetParams bool
		if params, ok := fs.Params(); ok {
			info.Pool = params.Pool
			unsetParams = true
		} else {
			// Ensure immutable properties do not change.
			oldInfo, err := fs.Info()
			if err != nil {
				return nil, err
			}
			if err := validateFilesystemInfoChange(info, oldInfo); err != nil {
				return nil, err
			}
		}
		ops := setFilesystemInfoOps(tag, info, unsetParams)
		return ops, nil
	}
	return st.run(buildTxn)
}
Example #5
0
func (s *tmpfsFilesystemSource) readFilesystemInfo(tag names.FilesystemTag) (storage.FilesystemInfo, error) {
	var info filesystemInfo
	if err := utils.ReadYaml(s.filesystemInfoFile(tag), &info); err != nil {
		return storage.FilesystemInfo{}, errors.Annotate(err, "reading filesystem info from disk")
	}
	if info.Size == nil {
		return storage.FilesystemInfo{}, errors.New("invalid filesystem info: missing size")
	}
	return storage.FilesystemInfo{
		FilesystemId: tag.String(),
		Size:         *info.Size,
	}, nil
}
Example #6
0
func (s *tmpfsFilesystemSource) writeFilesystemInfo(tag names.FilesystemTag, info storage.FilesystemInfo) error {
	filename := s.filesystemInfoFile(tag)
	if _, err := os.Stat(filename); err == nil {
		return errors.Errorf("filesystem %v already exists", tag.Id())
	}
	if err := ensureDir(s.dirFuncs, filepath.Dir(filename)); err != nil {
		return errors.Trace(err)
	}
	err := utils.WriteYaml(filename, filesystemInfo{&info.Size})
	if err != nil {
		return errors.Annotate(err, "writing filesystem info to disk")
	}
	return err
}
Example #7
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)
}
Example #8
0
// RemoveFilesystem removes the filesystem from state. RemoveFilesystem will
// fail if there are any attachments remaining, or if the filesystem is not
// Dying. Removing a volume-backed filesystem will cause the volume to be
// destroyed.
func (st *State) RemoveFilesystem(tag names.FilesystemTag) (err error) {
	defer errors.DeferredAnnotatef(&err, "removing filesystem %s", tag.Id())
	buildTxn := func(attempt int) ([]txn.Op, error) {
		filesystem, err := st.Filesystem(tag)
		if errors.IsNotFound(err) {
			return nil, jujutxn.ErrNoOperations
		} else if err != nil {
			return nil, errors.Trace(err)
		}
		if filesystem.Life() != Dead {
			return nil, errors.New("filesystem is not dead")
		}
		return removeFilesystemOps(st, filesystem)
	}
	return st.run(buildTxn)
}
Example #9
0
func setFilesystemInfoOps(tag names.FilesystemTag, info FilesystemInfo, 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:      filesystemsC,
		Id:     tag.Id(),
		Assert: asserts,
		Update: update,
	}}
}
Example #10
0
func (s *rootfsFilesystemSource) mount(tag names.FilesystemTag, target string) error {
	fsPath := filepath.Join(s.storageDir, tag.Id())
	if target == fsPath {
		return nil
	}
	logger.Debugf("mounting filesystem %q at %q", fsPath, target)

	if err := ensureDir(s.dirFuncs, target); err != nil {
		return errors.Trace(err)
	}

	mounted, err := s.tryBindMount(fsPath, target)
	if err != nil {
		return errors.Trace(err)
	}
	if mounted {
		return nil
	}
	// We couldn't bind-mount over the designated directory;
	// carry on and check if it's on the same filesystem. If
	// it is, and it's empty, then claim it as our own.

	if err := s.validateSameMountPoints(fsPath, target); err != nil {
		return err
	}

	// The first time we try to take the existing directory, we'll
	// ensure that it's empty and create a file to "claim" it.
	// Future attachments will simply ensure that the claim file
	// exists.
	targetClaimPath := filepath.Join(fsPath, "juju-target-claimed")
	_, err = s.dirFuncs.lstat(targetClaimPath)
	if err == nil {
		return nil
	} else if !os.IsNotExist(err) {
		return errors.Trace(err)
	}
	if err := ensureEmptyDir(s.dirFuncs, target); err != nil {
		return errors.Trace(err)
	}
	if err := s.dirFuncs.mkDirAll(targetClaimPath, 0755); err != nil {
		return errors.Annotate(err, "writing claim file")
	}
	return nil
}
Example #11
0
func setFilesystemAttachmentInfoOps(
	machine names.MachineTag,
	filesystem names.FilesystemTag,
	info FilesystemAttachmentInfo,
	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:      filesystemAttachmentsC,
		Id:     filesystemAttachmentId(machine.Id(), filesystem.Id()),
		Assert: asserts,
		Update: update,
	}}
}
Example #12
0
func (m *mockFilesystemAccessor) provisionFilesystem(tag names.FilesystemTag) params.Filesystem {
	f := params.Filesystem{
		FilesystemTag: tag.String(),
		Info: params.FilesystemInfo{
			FilesystemId: "vol-" + tag.Id(),
		},
	}
	m.provisionedFilesystems[tag.String()] = f
	return f
}
Example #13
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
}
Example #14
0
// FilesystemStatus returns the status of the specified filesystem.
func (st *State) FilesystemStatus(tag names.FilesystemTag) (StatusInfo, error) {
	return getStatus(st, filesystemGlobalKey(tag.Id()), "filesystem")
}
Example #15
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)
}
Example #16
0
func (s *tmpfsFilesystemSource) filesystemInfoFile(tag names.FilesystemTag) string {
	return filepath.Join(s.storageDir, tag.Id()+".info")
}
Example #17
0
func (st *State) filesystemByTag(tag names.FilesystemTag) (*filesystem, error) {
	query := bson.D{{"_id", tag.Id()}}
	description := fmt.Sprintf("filesystem %q", tag.Id())
	return st.filesystem(query, description)
}
Example #18
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)
}