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