github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/api/watcher/watcher.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package watcher 5 6 import ( 7 "sync" 8 9 "github.com/juju/loggo" 10 "launchpad.net/tomb" 11 12 "github.com/juju/juju/api/base" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/state/multiwatcher" 15 ) 16 17 var logger = loggo.GetLogger("juju.api.watcher") 18 19 // commonWatcher implements common watcher logic in one place to 20 // reduce code duplication, but it's not in fact a complete watcher; 21 // it's intended for embedding. 22 type commonWatcher struct { 23 tomb tomb.Tomb 24 in chan interface{} 25 26 // These fields must be set by the embedding watcher, before 27 // calling init(). 28 29 // newResult must return a pointer to a value of the type returned 30 // by the watcher's Next call. 31 newResult func() interface{} 32 33 // call should invoke the given API method, placing the call's 34 // returned value in result (if any). 35 call watcherAPICall 36 } 37 38 // watcherAPICall wraps up the information about what facade and what watcher 39 // Id we are calling, and just gives us a simple way to call a common method 40 // with a given return value. 41 type watcherAPICall func(method string, result interface{}) error 42 43 // makeWatcherAPICaller creates a watcherAPICall function for a given facade name 44 // and watcherId. 45 func makeWatcherAPICaller(caller base.APICaller, facadeName, watcherId string) watcherAPICall { 46 bestVersion := caller.BestFacadeVersion(facadeName) 47 return func(request string, result interface{}) error { 48 return caller.APICall(facadeName, bestVersion, 49 watcherId, request, nil, &result) 50 } 51 } 52 53 // init must be called to initialize an embedded commonWatcher's 54 // fields. Make sure newResult and call fields are set beforehand. 55 func (w *commonWatcher) init() { 56 w.in = make(chan interface{}) 57 if w.newResult == nil { 58 panic("newResult must me set") 59 } 60 if w.call == nil { 61 panic("call must be set") 62 } 63 } 64 65 // commonLoop implements the loop structure common to the client 66 // watchers. It should be started in a separate goroutine by any 67 // watcher that embeds commonWatcher. It kills the commonWatcher's 68 // tomb when an error occurs. 69 func (w *commonWatcher) commonLoop() { 70 defer close(w.in) 71 var wg sync.WaitGroup 72 wg.Add(1) 73 go func() { 74 // When the watcher has been stopped, we send a Stop request 75 // to the server, which will remove the watcher and return a 76 // CodeStopped error to any currently outstanding call to 77 // Next. If a call to Next happens just after the watcher has 78 // been stopped, we'll get a CodeNotFound error; Either way 79 // we'll return, wait for the stop request to complete, and 80 // the watcher will die with all resources cleaned up. 81 defer wg.Done() 82 <-w.tomb.Dying() 83 if err := w.call("Stop", nil); err != nil { 84 logger.Errorf("error trying to stop watcher: %v", err) 85 } 86 }() 87 wg.Add(1) 88 go func() { 89 // Because Next blocks until there are changes, we need to 90 // call it in a separate goroutine, so the watcher can be 91 // stopped normally. 92 defer wg.Done() 93 for { 94 result := w.newResult() 95 err := w.call("Next", &result) 96 if err != nil { 97 if params.IsCodeStopped(err) || params.IsCodeNotFound(err) { 98 if w.tomb.Err() != tomb.ErrStillAlive { 99 // The watcher has been stopped at the client end, so we're 100 // expecting one of the above two kinds of error. 101 // We might see the same errors if the server itself 102 // has been shut down, in which case we leave them 103 // untouched. 104 err = tomb.ErrDying 105 } 106 } 107 // Something went wrong, just report the error and bail out. 108 w.tomb.Kill(err) 109 return 110 } 111 select { 112 case <-w.tomb.Dying(): 113 return 114 case w.in <- result: 115 // Report back the result we just got. 116 } 117 } 118 }() 119 wg.Wait() 120 } 121 122 func (w *commonWatcher) Stop() error { 123 w.tomb.Kill(nil) 124 return w.tomb.Wait() 125 } 126 127 func (w *commonWatcher) Err() error { 128 return w.tomb.Err() 129 } 130 131 // notifyWatcher will send events when something changes. 132 // It does not send content for those changes. 133 type notifyWatcher struct { 134 commonWatcher 135 caller base.APICaller 136 notifyWatcherId string 137 out chan struct{} 138 } 139 140 // If an API call returns a NotifyWatchResult, you can use this to turn it into 141 // a local Watcher. 142 func NewNotifyWatcher(caller base.APICaller, result params.NotifyWatchResult) NotifyWatcher { 143 w := ¬ifyWatcher{ 144 caller: caller, 145 notifyWatcherId: result.NotifyWatcherId, 146 out: make(chan struct{}), 147 } 148 go func() { 149 defer w.tomb.Done() 150 defer close(w.out) 151 w.tomb.Kill(w.loop()) 152 }() 153 return w 154 } 155 156 func (w *notifyWatcher) loop() error { 157 // No results for this watcher type. 158 w.newResult = func() interface{} { return nil } 159 w.call = makeWatcherAPICaller(w.caller, "NotifyWatcher", w.notifyWatcherId) 160 w.commonWatcher.init() 161 go w.commonLoop() 162 163 for { 164 select { 165 // Since for a notifyWatcher there are no changes to send, we 166 // just set the event (initial first, then after each change). 167 case w.out <- struct{}{}: 168 case <-w.tomb.Dying(): 169 return nil 170 } 171 if _, ok := <-w.in; !ok { 172 // The tomb is already killed with the correct 173 // error at this point, so just return. 174 return nil 175 } 176 } 177 } 178 179 // Changes returns a channel that receives a value when a given entity 180 // changes in some way. 181 func (w *notifyWatcher) Changes() <-chan struct{} { 182 return w.out 183 } 184 185 // stringsWatcher will send events when something changes. 186 // The content of the changes is a list of strings. 187 type stringsWatcher struct { 188 commonWatcher 189 caller base.APICaller 190 stringsWatcherId string 191 out chan []string 192 } 193 194 func NewStringsWatcher(caller base.APICaller, result params.StringsWatchResult) StringsWatcher { 195 w := &stringsWatcher{ 196 caller: caller, 197 stringsWatcherId: result.StringsWatcherId, 198 out: make(chan []string), 199 } 200 go func() { 201 defer w.tomb.Done() 202 defer close(w.out) 203 w.tomb.Kill(w.loop(result.Changes)) 204 }() 205 return w 206 } 207 208 func (w *stringsWatcher) loop(initialChanges []string) error { 209 changes := initialChanges 210 w.newResult = func() interface{} { return new(params.StringsWatchResult) } 211 w.call = makeWatcherAPICaller(w.caller, "StringsWatcher", w.stringsWatcherId) 212 w.commonWatcher.init() 213 go w.commonLoop() 214 215 for { 216 select { 217 // Send the initial event or subsequent change. 218 case w.out <- changes: 219 case <-w.tomb.Dying(): 220 return nil 221 } 222 // Read the next change. 223 data, ok := <-w.in 224 if !ok { 225 // The tomb is already killed with the correct error 226 // at this point, so just return. 227 return nil 228 } 229 changes = data.(*params.StringsWatchResult).Changes 230 } 231 } 232 233 // Changes returns a channel that receives a list of strings of watched 234 // entites with changes. 235 func (w *stringsWatcher) Changes() <-chan []string { 236 return w.out 237 } 238 239 // relationUnitsWatcher will sends notifications of units entering and 240 // leaving the scope of a RelationUnit, and changes to the settings of 241 // those units known to have entered. 242 type relationUnitsWatcher struct { 243 commonWatcher 244 caller base.APICaller 245 relationUnitsWatcherId string 246 out chan multiwatcher.RelationUnitsChange 247 } 248 249 func NewRelationUnitsWatcher(caller base.APICaller, result params.RelationUnitsWatchResult) RelationUnitsWatcher { 250 w := &relationUnitsWatcher{ 251 caller: caller, 252 relationUnitsWatcherId: result.RelationUnitsWatcherId, 253 out: make(chan multiwatcher.RelationUnitsChange), 254 } 255 go func() { 256 defer w.tomb.Done() 257 defer close(w.out) 258 w.tomb.Kill(w.loop(result.Changes)) 259 }() 260 return w 261 } 262 263 func (w *relationUnitsWatcher) loop(initialChanges multiwatcher.RelationUnitsChange) error { 264 changes := initialChanges 265 w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) } 266 w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId) 267 w.commonWatcher.init() 268 go w.commonLoop() 269 270 for { 271 select { 272 // Send the initial event or subsequent change. 273 case w.out <- changes: 274 case <-w.tomb.Dying(): 275 return nil 276 } 277 // Read the next change. 278 data, ok := <-w.in 279 if !ok { 280 // The tomb is already killed with the correct error 281 // at this point, so just return. 282 return nil 283 } 284 changes = data.(*params.RelationUnitsWatchResult).Changes 285 } 286 } 287 288 // Changes returns a channel that will receive the changes to 289 // counterpart units in a relation. The first event on the channel 290 // holds the initial state of the relation in its Changed field. 291 func (w *relationUnitsWatcher) Changes() <-chan multiwatcher.RelationUnitsChange { 292 return w.out 293 } 294 295 // machineAttachmentsWatcher will sends notifications of units entering and 296 // leaving the scope of a MachineStorageId, and changes to the settings of 297 // those units known to have entered. 298 type machineAttachmentsWatcher struct { 299 commonWatcher 300 caller base.APICaller 301 machineAttachmentsWatcherId string 302 out chan []params.MachineStorageId 303 } 304 305 // NewVolumeAttachmentsWatcher returns a MachineStorageIdsWatcher which 306 // communicates with the VolumeAttachmentsWatcher API facade to watch 307 // volume attachments. 308 func NewVolumeAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher { 309 return newMachineStorageIdsWatcher("VolumeAttachmentsWatcher", caller, result) 310 } 311 312 // NewFilesystemAttachmentsWatcher returns a MachineStorageIdsWatcher which 313 // communicates with the FilesystemAttachmentsWatcher API facade to watch 314 // filesystem attachments. 315 func NewFilesystemAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher { 316 return newMachineStorageIdsWatcher("FilesystemAttachmentsWatcher", caller, result) 317 } 318 319 func newMachineStorageIdsWatcher(facade string, caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher { 320 w := &machineAttachmentsWatcher{ 321 caller: caller, 322 machineAttachmentsWatcherId: result.MachineStorageIdsWatcherId, 323 out: make(chan []params.MachineStorageId), 324 } 325 go func() { 326 defer w.tomb.Done() 327 defer close(w.out) 328 w.tomb.Kill(w.loop(facade, result.Changes)) 329 }() 330 return w 331 } 332 333 func (w *machineAttachmentsWatcher) loop(facade string, initialChanges []params.MachineStorageId) error { 334 changes := initialChanges 335 w.newResult = func() interface{} { return new(params.MachineStorageIdsWatchResult) } 336 w.call = makeWatcherAPICaller(w.caller, facade, w.machineAttachmentsWatcherId) 337 w.commonWatcher.init() 338 go w.commonLoop() 339 340 for { 341 select { 342 // Send the initial event or subsequent change. 343 case w.out <- changes: 344 case <-w.tomb.Dying(): 345 return nil 346 } 347 // Read the next change. 348 data, ok := <-w.in 349 if !ok { 350 // The tomb is already killed with the correct error 351 // at this point, so just return. 352 return nil 353 } 354 changes = data.(*params.MachineStorageIdsWatchResult).Changes 355 } 356 } 357 358 // Changes returns a channel that will receive the IDs of machine 359 // storage entity attachments which have changed. 360 func (w *machineAttachmentsWatcher) Changes() <-chan []params.MachineStorageId { 361 return w.out 362 } 363 364 // EntityWatcher will send events when something changes. 365 // The content for the changes is a list of tag strings. 366 type entityWatcher struct { 367 commonWatcher 368 caller base.APICaller 369 entityWatcherId string 370 out chan []string 371 } 372 373 func NewEntityWatcher(caller base.APICaller, result params.EntityWatchResult) EntityWatcher { 374 w := &entityWatcher{ 375 caller: caller, 376 entityWatcherId: result.EntityWatcherId, 377 out: make(chan []string), 378 } 379 go func() { 380 defer w.tomb.Done() 381 defer close(w.out) 382 w.tomb.Kill(w.loop(result.Changes)) 383 }() 384 return w 385 } 386 387 func (w *entityWatcher) loop(initialChanges []string) error { 388 changes := initialChanges 389 w.newResult = func() interface{} { return new(params.EntityWatchResult) } 390 w.call = makeWatcherAPICaller(w.caller, "EntityWatcher", w.entityWatcherId) 391 w.commonWatcher.init() 392 go w.commonLoop() 393 394 for { 395 select { 396 // Send the initial event or subsequent change. 397 case w.out <- changes: 398 case <-w.tomb.Dying(): 399 return nil 400 } 401 // Read the next change. 402 data, ok := <-w.in 403 if !ok { 404 // The tomb is already killed with the correct error 405 // at this point, so just return. 406 return nil 407 } 408 // Changes have been transformed at the server side already. 409 changes = data.(*params.EntityWatchResult).Changes 410 } 411 } 412 413 // Changes returns a channel that receives a list of changes 414 // as tags (converted to strings) of the watched entities 415 // with changes. 416 func (w *entityWatcher) Changes() <-chan []string { 417 return w.out 418 }