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