// 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, }) }
// 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 }
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}}}}, }} }
// 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) }
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 }
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 }
// 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) }
// 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) }
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, }} }
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 }
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, }} }
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 }
// 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 }
// FilesystemStatus returns the status of the specified filesystem. func (st *State) FilesystemStatus(tag names.FilesystemTag) (StatusInfo, error) { return getStatus(st, filesystemGlobalKey(tag.Id()), "filesystem") }
// 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) }
func (s *tmpfsFilesystemSource) filesystemInfoFile(tag names.FilesystemTag) string { return filepath.Join(s.storageDir, tag.Id()+".info") }
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) }
// 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) }