github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "github.com/juju/names" 9 "launchpad.net/tomb" 10 11 apiwatcher "github.com/juju/juju/api/watcher" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/state/watcher" 14 ) 15 16 func newStorageAttachmentWatcher( 17 st StorageAccessor, 18 in apiwatcher.NotifyWatcher, 19 unitTag names.UnitTag, 20 storageTag names.StorageTag, 21 changes chan<- storageAttachmentChange, 22 ) *storageAttachmentWatcher { 23 s := &storageAttachmentWatcher{ 24 st: st, 25 watcher: in, 26 changes: changes, 27 storageTag: storageTag, 28 unitTag: unitTag, 29 } 30 go func() { 31 defer s.tomb.Done() 32 defer watcher.Stop(in, &s.tomb) 33 s.tomb.Kill(s.loop()) 34 }() 35 return s 36 } 37 38 type StorageAccessor interface { 39 // StorageAttachment returns the storage attachment with the specified 40 // unit and storage tags. 41 StorageAttachment(names.StorageTag, names.UnitTag) (params.StorageAttachment, error) 42 } 43 44 // storageAttachmentWatcher watches for changes to the attachment status of 45 // the storage with the specified tag and sends the tag to the specified channel 46 // when a change occurs. 47 type storageAttachmentWatcher struct { 48 tomb tomb.Tomb 49 50 st StorageAccessor 51 watcher apiwatcher.NotifyWatcher 52 storageTag names.StorageTag 53 unitTag names.UnitTag 54 changes chan<- storageAttachmentChange 55 } 56 57 type storageAttachmentChange struct { 58 Tag names.StorageTag 59 Snapshot StorageSnapshot 60 } 61 62 func getStorageSnapshot( 63 st StorageAccessor, 64 storageTag names.StorageTag, 65 unitTag names.UnitTag, 66 ) (StorageSnapshot, error) { 67 attachment, err := st.StorageAttachment(storageTag, unitTag) 68 if err != nil { 69 return StorageSnapshot{}, errors.Annotate(err, "refreshing storage details") 70 } 71 snapshot := StorageSnapshot{ 72 Life: attachment.Life, 73 Kind: attachment.Kind, 74 Attached: true, 75 Location: attachment.Location, 76 } 77 return snapshot, nil 78 } 79 80 func (s *storageAttachmentWatcher) loop() error { 81 for { 82 select { 83 case <-s.tomb.Dying(): 84 return tomb.ErrDying 85 case _, ok := <-s.watcher.Changes(): 86 if !ok { 87 return watcher.EnsureErr(s.watcher) 88 } 89 snapshot, err := getStorageSnapshot( 90 s.st, s.storageTag, s.unitTag, 91 ) 92 if params.IsCodeNotFound(err) { 93 // The storage attachment was removed 94 // from state, so we can stop watching. 95 return nil 96 } else if params.IsCodeNotProvisioned(err) { 97 // We do not care about unattached 98 // storage here. 99 continue 100 } else if err != nil { 101 return err 102 } 103 change := storageAttachmentChange{ 104 s.storageTag, 105 snapshot, 106 } 107 select { 108 case <-s.tomb.Dying(): 109 return tomb.ErrDying 110 case s.changes <- change: 111 } 112 } 113 } 114 } 115 116 func (s *storageAttachmentWatcher) Stop() error { 117 s.tomb.Kill(nil) 118 return s.tomb.Wait() 119 }