github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/storage/resolver.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/charm.v6/hooks" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/core/model" 13 "github.com/juju/juju/storage" 14 "github.com/juju/juju/worker/uniter/hook" 15 "github.com/juju/juju/worker/uniter/operation" 16 "github.com/juju/juju/worker/uniter/remotestate" 17 "github.com/juju/juju/worker/uniter/resolver" 18 ) 19 20 // StorageResolverOperations instances know how to make operations 21 // required by the resolver. 22 type StorageResolverOperations interface { 23 NewUpdateStorage(tags []names.StorageTag) (operation.Operation, error) 24 NewRunHook(hookInfo hook.Info) (operation.Operation, error) 25 } 26 27 type storageResolver struct { 28 storage *Attachments 29 dying bool 30 life map[names.StorageTag]params.Life 31 modelType model.ModelType 32 } 33 34 // NewResolver returns a new storage resolver. 35 func NewResolver(storage *Attachments, modelType model.ModelType) resolver.Resolver { 36 return &storageResolver{ 37 storage: storage, 38 modelType: modelType, 39 life: make(map[names.StorageTag]params.Life), 40 } 41 } 42 43 // NextOp is defined on the Resolver interface. 44 func (s *storageResolver) NextOp( 45 localState resolver.LocalState, 46 remoteState remotestate.Snapshot, 47 opFactory operation.Factory, 48 ) (operation.Operation, error) { 49 50 // Only IAAS models need to first run the install hook. 51 // For CAAS models, storage is specified in the pod config 52 // and mounted as the pod in started. 53 blockedWaitingForInstall := !localState.Installed && s.modelType == model.IAAS 54 55 if remoteState.Life == params.Dying { 56 // The unit is dying, so destroy all of its storage. 57 if !s.dying { 58 if err := s.storage.SetDying(); err != nil { 59 return nil, errors.Trace(err) 60 } 61 s.dying = true 62 } 63 for tag, snap := range remoteState.Storage { 64 snap.Life = params.Dying 65 remoteState.Storage[tag] = snap 66 } 67 } 68 69 if err := s.maybeShortCircuitRemoval(remoteState.Storage); err != nil { 70 return nil, errors.Trace(err) 71 } 72 73 var runStorageHooks bool 74 switch { 75 case localState.Kind == operation.Continue: 76 // There's nothing in progress. 77 runStorageHooks = true 78 case blockedWaitingForInstall && localState.Kind == operation.RunHook && localState.Step == operation.Queued: 79 // The install operation completed, and there's an install 80 // hook queued. Run storage-attached hooks first. 81 runStorageHooks = true 82 } 83 if !runStorageHooks { 84 return nil, resolver.ErrNoOperation 85 } 86 87 if !localState.Installed && s.storage.Pending() == 0 { 88 logger.Infof("initial storage attachments ready") 89 } 90 91 for tag, snap := range remoteState.Storage { 92 op, err := s.nextHookOp(tag, snap, opFactory) 93 if errors.Cause(err) == resolver.ErrNoOperation { 94 continue 95 } 96 return op, err 97 } 98 if s.storage.Pending() > 0 { 99 logger.Debugf("still pending %v", s.storage.pending.SortedValues()) 100 if blockedWaitingForInstall { 101 // We only wait for pending storage before 102 // the install hook runs; we should not block 103 // other hooks from running while storage is 104 // being provisioned. 105 return nil, resolver.ErrWaiting 106 } 107 } 108 return nil, resolver.ErrNoOperation 109 } 110 111 // maybeShortCircuitRemoval removes any storage that is not alive, 112 // and has not had a storage-attached hook committed. 113 func (s *storageResolver) maybeShortCircuitRemoval(remote map[names.StorageTag]remotestate.StorageSnapshot) error { 114 for tag, snap := range remote { 115 local, ok := s.storage.storageAttachments[tag] 116 if (ok && local.attached) || snap.Life == params.Alive { 117 continue 118 } 119 if err := s.storage.removeStorageAttachment(tag); err != nil { 120 return errors.Trace(err) 121 } 122 delete(remote, tag) 123 } 124 return nil 125 } 126 127 func (s *storageResolver) nextHookOp( 128 tag names.StorageTag, 129 snap remotestate.StorageSnapshot, 130 opFactory operation.Factory, 131 ) (operation.Operation, error) { 132 133 logger.Debugf("next hook op for %v: %+v", tag, snap) 134 135 if snap.Life == params.Dead { 136 // Storage must have been Dying to become Dead; 137 // no further action is required. 138 return nil, resolver.ErrNoOperation 139 } 140 141 hookInfo := hook.Info{StorageId: tag.Id()} 142 switch snap.Life { 143 case params.Alive: 144 storageAttachment, ok := s.storage.storageAttachments[tag] 145 if ok && storageAttachment.attached { 146 // Once the storage is attached, we only care about 147 // lifecycle state changes. 148 return nil, resolver.ErrNoOperation 149 } 150 // The storage-attached hook has not been committed, so add the 151 // storage to the pending set. 152 s.storage.pending.Add(tag) 153 if !snap.Attached { 154 // The storage attachment has not been provisioned yet, 155 // so just ignore it for now. We'll be notified again 156 // when it has been provisioned. 157 return nil, resolver.ErrNoOperation 158 } 159 // The storage is alive, but we haven't previously run the 160 // "storage-attached" hook. Do so now. 161 hookInfo.Kind = hooks.StorageAttached 162 case params.Dying: 163 storageAttachment, ok := s.storage.storageAttachments[tag] 164 if !ok || !storageAttachment.attached { 165 // Nothing to do: attachment is dying, but 166 // the storage-attached hook has not been 167 // issued. 168 return nil, resolver.ErrNoOperation 169 } 170 // The storage is dying, but we haven't previously run the 171 // "storage-detached" hook. Do so now. 172 hookInfo.Kind = hooks.StorageDetaching 173 } 174 175 // Update the local state to reflect what we're about to report 176 // to a hook. 177 stateFile, err := readStateFile(s.storage.storageStateDir, tag) 178 if err != nil { 179 return nil, errors.Trace(err) 180 } 181 s.storage.storageAttachments[tag] = storageAttachment{ 182 stateFile, &contextStorage{ 183 tag: tag, 184 kind: storage.StorageKind(snap.Kind), 185 location: snap.Location, 186 }, 187 } 188 189 return opFactory.NewRunHook(hookInfo) 190 }