func (s *storageResolver) nextHookOp( tag names.StorageTag, snap remotestate.StorageSnapshot, opFactory operation.Factory, ) (operation.Operation, error) { logger.Debugf("next hook op for %v: %+v", tag, snap) if !snap.Attached { return nil, resolver.ErrNoOperation } storageAttachment, ok := s.storage.storageAttachments[tag] if !ok { return nil, resolver.ErrNoOperation } switch snap.Life { case params.Alive: if storageAttachment.attached { // Storage attachments currently do not change // (apart from lifecycle) after being provisioned. // We don't process unprovisioned storage here, // so there's nothing to do. return nil, resolver.ErrNoOperation } case params.Dying: if !storageAttachment.attached { // Nothing to do: attachment is dying, but // the storage-attached hook has not been // consumed. return nil, resolver.ErrNoOperation } case params.Dead: // Storage must have been Dying to become Dead; // no further action is required. return nil, resolver.ErrNoOperation } hookInfo := hook.Info{ StorageId: tag.Id(), } if snap.Life == params.Alive { hookInfo.Kind = hooks.StorageAttached } else { hookInfo.Kind = hooks.StorageDetaching } context := &contextStorage{ tag: tag, kind: storage.StorageKind(snap.Kind), location: snap.Location, } storageAttachment.ContextStorageAttachment = context s.storage.storageAttachments[tag] = storageAttachment logger.Debugf("queued hook: %v", hookInfo) return opFactory.NewRunHook(hookInfo) }
// Update updates the hook queue with the freshly acquired information about // the storage attachment. func (s *storageHookQueue) Update(attachment params.StorageAttachment) error { switch attachment.Life { case params.Alive: if s.attached { // Storage attachments currently do not change // (apart from lifecycle) after being provisioned. // We don't process unprovisioned storage here, // so there's nothing to do. return nil } case params.Dying: if !s.attached { // Nothing to do: attachment is dying, but // the storage-attached hook has not been // consumed. s.hookInfo = nil return nil } case params.Dead: // Storage must been Dying to become Dead; // no further action is required. return nil } // Set the storage context when the first hook is generated // for this storager. Later, when we need to handle changing // storage, we'll need to have a cache in the runner like // we have for relations. if s.context == nil { s.context = &contextStorage{ tag: s.storageTag, kind: storage.StorageKind(attachment.Kind), location: attachment.Location, } } if s.hookInfo == nil { s.hookInfo = &hook.Info{ StorageId: s.storageTag.Id(), } } if attachment.Life == params.Alive { s.hookInfo.Kind = hooks.StorageAttached } else { s.hookInfo.Kind = hooks.StorageDetaching } logger.Debugf("queued hook: %v", s.hookInfo) return nil }
// init processes the storage state directory and creates storagers // for the state files found. func (a *Attachments) init() error { if err := os.MkdirAll(a.storageStateDir, 0755); err != nil { return errors.Annotate(err, "creating storage state dir") } // Query all remote, known storage attachments for the unit, // so we can cull state files, and store current context. attachmentIds, err := a.st.UnitStorageAttachments(a.unitTag) if err != nil { return errors.Annotate(err, "getting unit attachments") } attachmentsByTag := make(map[names.StorageTag]struct{}) for _, attachmentId := range attachmentIds { storageTag, err := names.ParseStorageTag(attachmentId.StorageTag) if err != nil { return errors.Trace(err) } attachmentsByTag[storageTag] = struct{}{} } stateFiles, err := readAllStateFiles(a.storageStateDir) if err != nil { return errors.Annotate(err, "reading storage state dirs") } for storageTag, stateFile := range stateFiles { if _, ok := attachmentsByTag[storageTag]; !ok { // We have previously removed the storage from state, // but did not remove the state file. Remove the file. if err := stateFile.Remove(); err != nil { return errors.Trace(err) } continue } // Since there's a state file, we must previously have handled // at least "storage-attached", so there is no possibility of // short-circuiting the storage's removal. attachment, err := a.st.StorageAttachment(storageTag, a.unitTag) if err != nil { return errors.Annotatef( err, "querying storage attachment %q", storageTag.Id(), ) } a.storageAttachments[storageTag] = storageAttachment{ stateFile, &contextStorage{ tag: storageTag, kind: storage.StorageKind(attachment.Kind), location: attachment.Location, }, } } for storageTag := range attachmentsByTag { if _, ok := stateFiles[storageTag]; !ok { // There is no state file for the attachment, so no // hooks have been committed for it. a.pending.Add(storageTag) } // Non-locally recorded attachments will be further handled // by the resolver. } return nil }
func (s *storageResolver) nextHookOp( tag names.StorageTag, snap remotestate.StorageSnapshot, opFactory operation.Factory, ) (operation.Operation, error) { logger.Debugf("next hook op for %v: %+v", tag, snap) if snap.Life == params.Dead { // Storage must have been Dying to become Dead; // no further action is required. return nil, resolver.ErrNoOperation } hookInfo := hook.Info{StorageId: tag.Id()} switch snap.Life { case params.Alive: storageAttachment, ok := s.storage.storageAttachments[tag] if ok && storageAttachment.attached { // Once the storage is attached, we only care about // lifecycle state changes. return nil, resolver.ErrNoOperation } // The storage-attached hook has not been committed, so add the // storage to the pending set. s.storage.pending.Add(tag) if !snap.Attached { // The storage attachment has not been provisioned yet, // so just ignore it for now. We'll be notified again // when it has been provisioned. return nil, resolver.ErrNoOperation } // The storage is alive, but we haven't previously run the // "storage-attached" hook. Do so now. hookInfo.Kind = hooks.StorageAttached case params.Dying: storageAttachment, ok := s.storage.storageAttachments[tag] if !ok || !storageAttachment.attached { // Nothing to do: attachment is dying, but // the storage-attached hook has not been // issued. return nil, resolver.ErrNoOperation } // The storage is dying, but we haven't previously run the // "storage-detached" hook. Do so now. hookInfo.Kind = hooks.StorageDetaching } // Update the local state to reflect what we're about to report // to a hook. stateFile, err := readStateFile(s.storage.storageStateDir, tag) if err != nil { return nil, errors.Trace(err) } s.storage.storageAttachments[tag] = storageAttachment{ stateFile, &contextStorage{ tag: tag, kind: storage.StorageKind(snap.Kind), location: snap.Location, }, } return opFactory.NewRunHook(hookInfo) }