github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/watch.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 "gopkg.in/tomb.v1" 13 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/state/watcher" 18 ) 19 20 // AgentEntityWatcher implements a common Watch method for use by 21 // various facades. 22 type AgentEntityWatcher struct { 23 st state.EntityFinder 24 resources facade.Resources 25 getCanWatch GetAuthFunc 26 } 27 28 // NewAgentEntityWatcher returns a new AgentEntityWatcher. The 29 // GetAuthFunc will be used on each invocation of Watch to determine 30 // current permissions. 31 func NewAgentEntityWatcher(st state.EntityFinder, resources facade.Resources, getCanWatch GetAuthFunc) *AgentEntityWatcher { 32 return &AgentEntityWatcher{ 33 st: st, 34 resources: resources, 35 getCanWatch: getCanWatch, 36 } 37 } 38 39 func (a *AgentEntityWatcher) watchEntity(tag names.Tag) (string, error) { 40 entity0, err := a.st.FindEntity(tag) 41 if err != nil { 42 return "", err 43 } 44 entity, ok := entity0.(state.NotifyWatcherFactory) 45 if !ok { 46 return "", NotSupportedError(tag, "watching") 47 } 48 watch := entity.Watch() 49 // Consume the initial event. Technically, API 50 // calls to Watch 'transmit' the initial event 51 // in the Watch response. But NotifyWatchers 52 // have no state to transmit. 53 if _, ok := <-watch.Changes(); ok { 54 return a.resources.Register(watch), nil 55 } 56 return "", watcher.EnsureErr(watch) 57 } 58 59 // Watch starts an NotifyWatcher for each given entity. 60 func (a *AgentEntityWatcher) Watch(args params.Entities) (params.NotifyWatchResults, error) { 61 result := params.NotifyWatchResults{ 62 Results: make([]params.NotifyWatchResult, len(args.Entities)), 63 } 64 if len(args.Entities) == 0 { 65 return result, nil 66 } 67 canWatch, err := a.getCanWatch() 68 if err != nil { 69 return params.NotifyWatchResults{}, errors.Trace(err) 70 } 71 for i, entity := range args.Entities { 72 tag, err := names.ParseTag(entity.Tag) 73 if err != nil { 74 result.Results[i].Error = ServerError(ErrPerm) 75 continue 76 } 77 err = ErrPerm 78 watcherId := "" 79 if canWatch(tag) { 80 watcherId, err = a.watchEntity(tag) 81 } 82 result.Results[i].NotifyWatcherId = watcherId 83 result.Results[i].Error = ServerError(err) 84 } 85 return result, nil 86 } 87 88 // MultiNotifyWatcher implements state.NotifyWatcher, combining 89 // multiple NotifyWatchers. 90 type MultiNotifyWatcher struct { 91 tomb tomb.Tomb 92 watchers []state.NotifyWatcher 93 changes chan struct{} 94 } 95 96 // NewMultiNotifyWatcher creates a NotifyWatcher that combines 97 // each of the NotifyWatchers passed in. Each watcher's initial 98 // event is consumed, and a single initial event is sent. 99 // Subsequent events are not coalesced. 100 func NewMultiNotifyWatcher(w ...state.NotifyWatcher) *MultiNotifyWatcher { 101 m := &MultiNotifyWatcher{ 102 watchers: w, 103 changes: make(chan struct{}), 104 } 105 var wg sync.WaitGroup 106 wg.Add(len(w)) 107 staging := make(chan struct{}) 108 for _, w := range w { 109 // Consume the first event of each watcher. 110 <-w.Changes() 111 go func(w state.NotifyWatcher) { 112 defer wg.Done() 113 m.tomb.Kill(w.Wait()) 114 }(w) 115 // Copy events from the watcher to the staging channel. 116 go copyEvents(staging, w.Changes(), &m.tomb) 117 } 118 go func() { 119 defer m.tomb.Done() 120 m.loop(staging) 121 wg.Wait() 122 }() 123 return m 124 } 125 126 // loop copies events from the input channel to the output channel, 127 // coalescing events by waiting a short time between receiving and 128 // sending. 129 func (w *MultiNotifyWatcher) loop(in <-chan struct{}) { 130 defer close(w.changes) 131 // out is initialised to m.changes to send the inital event. 132 out := w.changes 133 var timer <-chan time.Time 134 for { 135 select { 136 case <-w.tomb.Dying(): 137 return 138 case <-in: 139 if timer == nil { 140 // TODO(fwereade): 2016-03-17 lp:1558657 141 timer = time.After(10 * time.Millisecond) 142 } 143 case <-timer: 144 timer = nil 145 out = w.changes 146 case out <- struct{}{}: 147 out = nil 148 } 149 } 150 } 151 152 // copyEvents copies channel events from "in" to "out", coalescing. 153 func copyEvents(out chan<- struct{}, in <-chan struct{}, tomb *tomb.Tomb) { 154 var outC chan<- struct{} 155 for { 156 select { 157 case <-tomb.Dying(): 158 return 159 case _, ok := <-in: 160 if !ok { 161 return 162 } 163 outC = out 164 case outC <- struct{}{}: 165 outC = nil 166 } 167 } 168 } 169 170 func (w *MultiNotifyWatcher) Kill() { 171 w.tomb.Kill(nil) 172 for _, w := range w.watchers { 173 w.Kill() 174 } 175 } 176 177 func (w *MultiNotifyWatcher) Wait() error { 178 return w.tomb.Wait() 179 } 180 181 func (w *MultiNotifyWatcher) Stop() error { 182 w.Kill() 183 return w.Wait() 184 } 185 186 func (w *MultiNotifyWatcher) Err() error { 187 return w.tomb.Err() 188 } 189 190 func (w *MultiNotifyWatcher) Changes() <-chan struct{} { 191 return w.changes 192 }