github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "github.com/juju/names" 9 "gopkg.in/juju/charm.v6-unstable/hooks" 10 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/storage" 13 "github.com/juju/juju/worker/uniter/hook" 14 "github.com/juju/juju/worker/uniter/operation" 15 "github.com/juju/juju/worker/uniter/remotestate" 16 "github.com/juju/juju/worker/uniter/resolver" 17 ) 18 19 // StorageResolverOperations instances know how to make operations 20 // required by the resolver. 21 type StorageResolverOperations interface { 22 NewUpdateStorage(tags []names.StorageTag) (operation.Operation, error) 23 NewRunHook(hookInfo hook.Info) (operation.Operation, error) 24 } 25 26 type storageResolver struct { 27 storage *Attachments 28 dying bool 29 life map[names.StorageTag]params.Life 30 } 31 32 // NewResolver returns a new storage resolver. 33 func NewResolver(storage *Attachments) resolver.Resolver { 34 return &storageResolver{ 35 storage: storage, 36 life: make(map[names.StorageTag]params.Life), 37 } 38 } 39 40 // NextOp is defined on the Resolver interface. 41 func (s *storageResolver) NextOp( 42 localState resolver.LocalState, 43 remoteState remotestate.Snapshot, 44 opFactory operation.Factory, 45 ) (operation.Operation, error) { 46 47 var changed []names.StorageTag 48 for tag, storage := range remoteState.Storage { 49 life, ok := s.life[tag] 50 if !ok || life != storage.Life { 51 s.life[tag] = storage.Life 52 changed = append(changed, tag) 53 } 54 } 55 for tag := range s.life { 56 if _, ok := remoteState.Storage[tag]; !ok { 57 changed = append(changed, tag) 58 delete(s.life, tag) 59 } 60 } 61 if len(changed) > 0 { 62 return opFactory.NewUpdateStorage(changed) 63 } 64 if !localState.Installed && s.storage.Pending() == 0 { 65 logger.Infof("initial storage attachments ready") 66 } 67 return s.nextOp(localState, remoteState, opFactory) 68 } 69 70 func (s *storageResolver) nextOp( 71 localState resolver.LocalState, 72 remoteState remotestate.Snapshot, 73 opFactory operation.Factory, 74 ) (operation.Operation, error) { 75 if remoteState.Life == params.Dying { 76 if !s.dying { 77 if err := s.storage.SetDying(); err != nil { 78 return nil, errors.Trace(err) 79 } 80 s.dying = true 81 } 82 for tag, snap := range remoteState.Storage { 83 snap.Life = params.Dying 84 remoteState.Storage[tag] = snap 85 } 86 } 87 88 var runStorageHooks bool 89 switch { 90 case localState.Kind == operation.Continue: 91 // There's nothing in progress. 92 runStorageHooks = true 93 case !localState.Installed && localState.Kind == operation.RunHook && localState.Step == operation.Queued: 94 // The install operation completed, and there's an install 95 // hook queued. Run storage-attached hooks first. 96 runStorageHooks = true 97 } 98 99 if runStorageHooks { 100 for tag, snap := range remoteState.Storage { 101 op, err := s.nextHookOp(tag, snap, opFactory) 102 if errors.Cause(err) == resolver.ErrNoOperation { 103 continue 104 } 105 return op, err 106 } 107 if s.storage.Pending() > 0 { 108 logger.Debugf("still pending %v", s.storage.pending) 109 if !localState.Installed { 110 return nil, resolver.ErrWaiting 111 } 112 } 113 } 114 return nil, resolver.ErrNoOperation 115 } 116 117 func (s *storageResolver) nextHookOp( 118 tag names.StorageTag, 119 snap remotestate.StorageSnapshot, 120 opFactory operation.Factory, 121 ) (operation.Operation, error) { 122 logger.Debugf("next hook op for %v: %+v", tag, snap) 123 if !snap.Attached { 124 return nil, resolver.ErrNoOperation 125 } 126 storageAttachment, ok := s.storage.storageAttachments[tag] 127 if !ok { 128 return nil, resolver.ErrNoOperation 129 } 130 switch snap.Life { 131 case params.Alive: 132 if storageAttachment.attached { 133 // Storage attachments currently do not change 134 // (apart from lifecycle) after being provisioned. 135 // We don't process unprovisioned storage here, 136 // so there's nothing to do. 137 return nil, resolver.ErrNoOperation 138 } 139 case params.Dying: 140 if !storageAttachment.attached { 141 // Nothing to do: attachment is dying, but 142 // the storage-attached hook has not been 143 // consumed. 144 return nil, resolver.ErrNoOperation 145 } 146 case params.Dead: 147 // Storage must have been Dying to become Dead; 148 // no further action is required. 149 return nil, resolver.ErrNoOperation 150 } 151 152 hookInfo := hook.Info{ 153 StorageId: tag.Id(), 154 } 155 if snap.Life == params.Alive { 156 hookInfo.Kind = hooks.StorageAttached 157 } else { 158 hookInfo.Kind = hooks.StorageDetaching 159 } 160 context := &contextStorage{ 161 tag: tag, 162 kind: storage.StorageKind(snap.Kind), 163 location: snap.Location, 164 } 165 storageAttachment.ContextStorageAttachment = context 166 s.storage.storageAttachments[tag] = storageAttachment 167 168 logger.Debugf("queued hook: %v", hookInfo) 169 return opFactory.NewRunHook(hookInfo) 170 }