github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/storage/attachments.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package storage contains the storage subsystem for the uniter, responding 5 // to changes in storage attachments (lifecycle, volume/filesystem details) 6 // by queuing hooks and managing the storage attachments' lifecycle. 7 package storage 8 9 import ( 10 "os" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/utils/set" 15 "gopkg.in/juju/charm.v6-unstable/hooks" 16 "gopkg.in/juju/names.v2" 17 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/storage" 20 "github.com/juju/juju/worker/uniter/hook" 21 "github.com/juju/juju/worker/uniter/runner/jujuc" 22 ) 23 24 var logger = loggo.GetLogger("juju.worker.uniter.storage") 25 26 // StorageAccessor is an interface for accessing information about 27 // storage attachments. 28 type StorageAccessor interface { 29 // StorageAttachment returns details of the storage attachment 30 // with the specified unit and storage tags. 31 StorageAttachment(names.StorageTag, names.UnitTag) (params.StorageAttachment, error) 32 33 // UnitStorageAttachments returns details of all of the storage 34 // attachments for the unit with the specified tag. 35 UnitStorageAttachments(names.UnitTag) ([]params.StorageAttachmentId, error) 36 37 // DestroyUnitStorageAttachments ensures that all storage 38 // attachments for the specified unit will be removed at 39 // some point in the future. 40 DestroyUnitStorageAttachments(names.UnitTag) error 41 42 // RemoveStorageAttachment removes that the storage attachment 43 // with the specified unit and storage tags. This method is only 44 // expected to succeed if the storage attachment is Dying. 45 RemoveStorageAttachment(names.StorageTag, names.UnitTag) error 46 } 47 48 type storageAttachment struct { 49 *stateFile 50 jujuc.ContextStorageAttachment 51 } 52 53 // Attachments generates storage hooks in response to changes to 54 // storage attachments, and provides access to information about 55 // storage attachments to hooks. 56 type Attachments struct { 57 st StorageAccessor 58 unitTag names.UnitTag 59 abort <-chan struct{} 60 storageStateDir string 61 62 // pending is the set of tags for storage attachments 63 // for which no hooks have been run. 64 pending set.Tags 65 66 // current storage attachments 67 storageAttachments map[names.StorageTag]storageAttachment 68 } 69 70 // NewAttachments returns a new Attachments. 71 func NewAttachments( 72 st StorageAccessor, 73 tag names.UnitTag, 74 storageStateDir string, 75 abort <-chan struct{}, 76 ) (*Attachments, error) { 77 a := &Attachments{ 78 st: st, 79 unitTag: tag, 80 abort: abort, 81 storageAttachments: make(map[names.StorageTag]storageAttachment), 82 storageStateDir: storageStateDir, 83 pending: make(set.Tags), 84 } 85 if err := a.init(); err != nil { 86 return nil, err 87 } 88 return a, nil 89 } 90 91 // init processes the storage state directory and creates storagers 92 // for the state files found. 93 func (a *Attachments) init() error { 94 if err := os.MkdirAll(a.storageStateDir, 0755); err != nil { 95 return errors.Annotate(err, "creating storage state dir") 96 } 97 // Query all remote, known storage attachments for the unit, 98 // so we can cull state files, and store current context. 99 attachmentIds, err := a.st.UnitStorageAttachments(a.unitTag) 100 if err != nil { 101 return errors.Annotate(err, "getting unit attachments") 102 } 103 attachmentsByTag := make(map[names.StorageTag]struct{}) 104 for _, attachmentId := range attachmentIds { 105 storageTag, err := names.ParseStorageTag(attachmentId.StorageTag) 106 if err != nil { 107 return errors.Trace(err) 108 } 109 attachmentsByTag[storageTag] = struct{}{} 110 } 111 stateFiles, err := readAllStateFiles(a.storageStateDir) 112 if err != nil { 113 return errors.Annotate(err, "reading storage state dirs") 114 } 115 for storageTag, stateFile := range stateFiles { 116 if _, ok := attachmentsByTag[storageTag]; !ok { 117 // We have previously removed the storage from state, 118 // but did not remove the state file. Remove the file. 119 if err := stateFile.Remove(); err != nil { 120 return errors.Trace(err) 121 } 122 continue 123 } 124 // Since there's a state file, we must previously have handled 125 // at least "storage-attached", so there is no possibility of 126 // short-circuiting the storage's removal. 127 attachment, err := a.st.StorageAttachment(storageTag, a.unitTag) 128 if err != nil { 129 return errors.Annotatef( 130 err, "querying storage attachment %q", 131 storageTag.Id(), 132 ) 133 } 134 a.storageAttachments[storageTag] = storageAttachment{ 135 stateFile, 136 &contextStorage{ 137 tag: storageTag, 138 kind: storage.StorageKind(attachment.Kind), 139 location: attachment.Location, 140 }, 141 } 142 } 143 for storageTag := range attachmentsByTag { 144 if _, ok := stateFiles[storageTag]; !ok { 145 // There is no state file for the attachment, so no 146 // hooks have been committed for it. 147 a.pending.Add(storageTag) 148 } 149 // Non-locally recorded attachments will be further handled 150 // by the resolver. 151 } 152 return nil 153 } 154 155 // SetDying ensures that any unprovisioned storage attachments are removed 156 // from state. 157 func (a *Attachments) SetDying() error { 158 if err := a.st.DestroyUnitStorageAttachments(a.unitTag); err != nil { 159 return errors.Trace(err) 160 } 161 return nil 162 } 163 164 // Pending reports the number of storage attachments whose hooks have yet 165 // to be run and committed. 166 func (a *Attachments) Pending() int { 167 return a.pending.Size() 168 } 169 170 // Empty reports whether or not there are any active storage attachments. 171 func (a *Attachments) Empty() bool { 172 return len(a.storageAttachments) == 0 173 } 174 175 // Storage returns the ContextStorage with the supplied tag if it was 176 // found, and whether it was found. 177 func (a *Attachments) Storage(tag names.StorageTag) (jujuc.ContextStorageAttachment, error) { 178 if attachment, ok := a.storageAttachments[tag]; ok { 179 return attachment, nil 180 } 181 return nil, errors.NotFoundf("storage") 182 } 183 184 // StorageTags returns the names.StorageTags for the active storage attachments. 185 func (a *Attachments) StorageTags() ([]names.StorageTag, error) { 186 tags := set.NewTags() 187 for tag := range a.storageAttachments { 188 tags.Add(tag) 189 } 190 storageTags := make([]names.StorageTag, tags.Size()) 191 for i, tag := range tags.SortedValues() { 192 storageTags[i] = tag.(names.StorageTag) 193 } 194 return storageTags, nil 195 } 196 197 // ValidateHook validates the hook against the current state. 198 func (a *Attachments) ValidateHook(hi hook.Info) error { 199 storageState, err := a.storageStateForHook(hi) 200 if err != nil { 201 return errors.Trace(err) 202 } 203 return storageState.ValidateHook(hi) 204 } 205 206 // CommitHook persists the state change encoded in the supplied storage 207 // hook, or returns an error if the hook is invalid given current state. 208 func (a *Attachments) CommitHook(hi hook.Info) error { 209 storageState, err := a.storageStateForHook(hi) 210 if err != nil { 211 return errors.Trace(err) 212 } 213 if err := storageState.CommitHook(hi); err != nil { 214 return err 215 } 216 storageTag := names.NewStorageTag(hi.StorageId) 217 switch hi.Kind { 218 case hooks.StorageAttached: 219 a.pending.Remove(storageTag) 220 case hooks.StorageDetaching: 221 if err := a.removeStorageAttachment(storageTag); err != nil { 222 return errors.Trace(err) 223 } 224 } 225 return nil 226 } 227 228 func (a *Attachments) removeStorageAttachment(tag names.StorageTag) error { 229 if err := a.st.RemoveStorageAttachment(tag, a.unitTag); err != nil { 230 return errors.Annotate(err, "removing storage attachment") 231 } 232 a.pending.Remove(tag) 233 delete(a.storageAttachments, tag) 234 return nil 235 } 236 237 func (a *Attachments) storageStateForHook(hi hook.Info) (*stateFile, error) { 238 if !hi.Kind.IsStorage() { 239 return nil, errors.Errorf("not a storage hook: %#v", hi) 240 } 241 storageAttachment, ok := a.storageAttachments[names.NewStorageTag(hi.StorageId)] 242 if !ok { 243 return nil, errors.Errorf("unknown storage %q", hi.StorageId) 244 } 245 return storageAttachment.stateFile, nil 246 }