github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/remotestate/storagewatcher.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package remotestate 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/watcher" 12 "github.com/juju/juju/worker" 13 "github.com/juju/juju/worker/catacomb" 14 ) 15 16 type StorageAccessor interface { 17 // StorageAttachment returns the storage attachment with the specified 18 // unit and storage tags. 19 StorageAttachment(names.StorageTag, names.UnitTag) (params.StorageAttachment, error) 20 } 21 22 // newStorageAttachmentsWatcher creates a new worker that wakes on input from 23 // the supplied watcher's Changes chan, finds out more about them, and delivers 24 // them on the supplied out chan. 25 // 26 // The caller releases responsibility for stopping the supplied watcher and 27 // waiting for errors, *whether or not this method succeeds*. 28 func newStorageAttachmentWatcher( 29 st StorageAccessor, 30 watcher watcher.NotifyWatcher, 31 unitTag names.UnitTag, 32 storageTag names.StorageTag, 33 out chan<- storageAttachmentChange, 34 ) (*storageAttachmentWatcher, error) { 35 s := &storageAttachmentWatcher{ 36 st: st, 37 changes: watcher.Changes(), 38 out: out, 39 storageTag: storageTag, 40 unitTag: unitTag, 41 } 42 err := catacomb.Invoke(catacomb.Plan{ 43 Site: &s.catacomb, 44 Work: s.loop, 45 Init: []worker.Worker{watcher}, 46 }) 47 if err != nil { 48 return nil, errors.Trace(err) 49 } 50 return s, nil 51 } 52 53 // storageAttachmentWatcher watches for changes to the attachment status of 54 // the storage with the specified tag and sends the tag to the specified channel 55 // when a change occurs. 56 type storageAttachmentWatcher struct { 57 catacomb catacomb.Catacomb 58 59 st StorageAccessor 60 changes watcher.NotifyChannel 61 storageTag names.StorageTag 62 unitTag names.UnitTag 63 out chan<- storageAttachmentChange 64 } 65 66 type storageAttachmentChange struct { 67 Tag names.StorageTag 68 Snapshot StorageSnapshot 69 } 70 71 func getStorageSnapshot( 72 st StorageAccessor, 73 storageTag names.StorageTag, 74 unitTag names.UnitTag, 75 ) (StorageSnapshot, error) { 76 attachment, err := st.StorageAttachment(storageTag, unitTag) 77 if err != nil { 78 return StorageSnapshot{}, errors.Annotate(err, "refreshing storage details") 79 } 80 snapshot := StorageSnapshot{ 81 Life: attachment.Life, 82 Kind: attachment.Kind, 83 Attached: true, 84 Location: attachment.Location, 85 } 86 return snapshot, nil 87 } 88 89 func (s *storageAttachmentWatcher) loop() error { 90 for { 91 select { 92 case <-s.catacomb.Dying(): 93 return s.catacomb.ErrDying() 94 case _, ok := <-s.changes: 95 if !ok { 96 return errors.New("storage attachment watcher closed") 97 } 98 snapshot, err := getStorageSnapshot( 99 s.st, s.storageTag, s.unitTag, 100 ) 101 if params.IsCodeNotFound(err) { 102 // The storage attachment was removed 103 // from state, so we can stop watching. 104 return nil 105 } else if params.IsCodeNotProvisioned(err) { 106 // We do not care about unattached 107 // storage here. 108 continue 109 } else if err != nil { 110 return err 111 } 112 change := storageAttachmentChange{ 113 s.storageTag, 114 snapshot, 115 } 116 select { 117 case <-s.catacomb.Dying(): 118 return s.catacomb.ErrDying() 119 case s.out <- change: 120 } 121 } 122 } 123 } 124 125 // Kill is part of the worker.Worker interface. 126 func (s *storageAttachmentWatcher) Kill() { 127 s.catacomb.Kill(nil) 128 } 129 130 // Wait is part of the worker.Worker interface. 131 func (s *storageAttachmentWatcher) Wait() error { 132 return s.catacomb.Wait() 133 }