github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/errors" 10 "github.com/juju/loggo" 11 "gopkg.in/tomb.v1" 12 13 "github.com/juju/juju/api/base" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/core/migration" 16 "github.com/juju/juju/watcher" 17 ) 18 19 var logger = loggo.GetLogger("juju.api.watcher") 20 21 // commonWatcher implements common watcher logic in one place to 22 // reduce code duplication, but it's not in fact a complete watcher; 23 // it's intended for embedding. 24 type commonWatcher struct { 25 tomb tomb.Tomb 26 in chan interface{} 27 28 // These fields must be set by the embedding watcher, before 29 // calling init(). 30 31 // newResult must return a pointer to a value of the type returned 32 // by the watcher's Next call. 33 newResult func() interface{} 34 35 // call should invoke the given API method, placing the call's 36 // returned value in result (if any). 37 call watcherAPICall 38 } 39 40 // watcherAPICall wraps up the information about what facade and what watcher 41 // Id we are calling, and just gives us a simple way to call a common method 42 // with a given return value. 43 type watcherAPICall func(method string, result interface{}) error 44 45 // makeWatcherAPICaller creates a watcherAPICall function for a given facade name 46 // and watcherId. 47 func makeWatcherAPICaller(caller base.APICaller, facadeName, watcherId string) watcherAPICall { 48 bestVersion := caller.BestFacadeVersion(facadeName) 49 return func(request string, result interface{}) error { 50 return caller.APICall(facadeName, bestVersion, 51 watcherId, request, nil, &result) 52 } 53 } 54 55 // init must be called to initialize an embedded commonWatcher's 56 // fields. Make sure newResult and call fields are set beforehand. 57 func (w *commonWatcher) init() { 58 w.in = make(chan interface{}) 59 if w.newResult == nil { 60 panic("newResult must me set") 61 } 62 if w.call == nil { 63 panic("call must be set") 64 } 65 } 66 67 // commonLoop implements the loop structure common to the client 68 // watchers. It should be started in a separate goroutine by any 69 // watcher that embeds commonWatcher. It kills the commonWatcher's 70 // tomb when an error occurs. 71 func (w *commonWatcher) commonLoop() { 72 defer close(w.in) 73 var wg sync.WaitGroup 74 wg.Add(1) 75 go func() { 76 // When the watcher has been stopped, we send a Stop request 77 // to the server, which will remove the watcher and return a 78 // CodeStopped error to any currently outstanding call to 79 // Next. If a call to Next happens just after the watcher has 80 // been stopped, we'll get a CodeNotFound error; Either way 81 // we'll return, wait for the stop request to complete, and 82 // the watcher will die with all resources cleaned up. 83 defer wg.Done() 84 <-w.tomb.Dying() 85 if err := w.call("Stop", nil); err != nil { 86 logger.Errorf("error trying to stop watcher: %v", err) 87 } 88 }() 89 wg.Add(1) 90 go func() { 91 // Because Next blocks until there are changes, we need to 92 // call it in a separate goroutine, so the watcher can be 93 // stopped normally. 94 defer wg.Done() 95 for { 96 result := w.newResult() 97 err := w.call("Next", &result) 98 if err != nil { 99 if params.IsCodeStopped(err) || params.IsCodeNotFound(err) { 100 if w.tomb.Err() != tomb.ErrStillAlive { 101 // The watcher has been stopped at the client end, so we're 102 // expecting one of the above two kinds of error. 103 // We might see the same errors if the server itself 104 // has been shut down, in which case we leave them 105 // untouched. 106 err = tomb.ErrDying 107 } 108 } 109 // Something went wrong, just report the error and bail out. 110 w.tomb.Kill(err) 111 return 112 } 113 select { 114 case <-w.tomb.Dying(): 115 return 116 case w.in <- result: 117 // Report back the result we just got. 118 } 119 } 120 }() 121 wg.Wait() 122 } 123 124 // Kill is part of the worker.Worker interface. 125 func (w *commonWatcher) Kill() { 126 w.tomb.Kill(nil) 127 } 128 129 // Wait is part of the worker.Worker interface. 130 func (w *commonWatcher) Wait() error { 131 return w.tomb.Wait() 132 } 133 134 // notifyWatcher will send events when something changes. 135 // It does not send content for those changes. 136 type notifyWatcher struct { 137 commonWatcher 138 caller base.APICaller 139 notifyWatcherId string 140 out chan struct{} 141 } 142 143 // If an API call returns a NotifyWatchResult, you can use this to turn it into 144 // a local Watcher. 145 func NewNotifyWatcher(caller base.APICaller, result params.NotifyWatchResult) watcher.NotifyWatcher { 146 w := ¬ifyWatcher{ 147 caller: caller, 148 notifyWatcherId: result.NotifyWatcherId, 149 out: make(chan struct{}), 150 } 151 go func() { 152 defer w.tomb.Done() 153 w.tomb.Kill(w.loop()) 154 }() 155 return w 156 } 157 158 func (w *notifyWatcher) loop() error { 159 // No results for this watcher type. 160 w.newResult = func() interface{} { return nil } 161 w.call = makeWatcherAPICaller(w.caller, "NotifyWatcher", w.notifyWatcherId) 162 w.commonWatcher.init() 163 go w.commonLoop() 164 165 for { 166 select { 167 // Since for a notifyWatcher there are no changes to send, we 168 // just set the event (initial first, then after each change). 169 case w.out <- struct{}{}: 170 case <-w.tomb.Dying(): 171 return nil 172 } 173 if _, ok := <-w.in; !ok { 174 // The tomb is already killed with the correct 175 // error at this point, so just return. 176 return nil 177 } 178 } 179 } 180 181 // Changes returns a channel that receives a value when a given entity 182 // changes in some way. 183 func (w *notifyWatcher) Changes() watcher.NotifyChannel { 184 return w.out 185 } 186 187 // stringsWatcher will send events when something changes. 188 // The content of the changes is a list of strings. 189 type stringsWatcher struct { 190 commonWatcher 191 caller base.APICaller 192 stringsWatcherId string 193 out chan []string 194 } 195 196 func NewStringsWatcher(caller base.APICaller, result params.StringsWatchResult) watcher.StringsWatcher { 197 w := &stringsWatcher{ 198 caller: caller, 199 stringsWatcherId: result.StringsWatcherId, 200 out: make(chan []string), 201 } 202 go func() { 203 defer w.tomb.Done() 204 w.tomb.Kill(w.loop(result.Changes)) 205 }() 206 return w 207 } 208 209 func (w *stringsWatcher) loop(initialChanges []string) error { 210 changes := initialChanges 211 w.newResult = func() interface{} { return new(params.StringsWatchResult) } 212 w.call = makeWatcherAPICaller(w.caller, "StringsWatcher", w.stringsWatcherId) 213 w.commonWatcher.init() 214 go w.commonLoop() 215 216 for { 217 select { 218 // Send the initial event or subsequent change. 219 case w.out <- changes: 220 case <-w.tomb.Dying(): 221 return nil 222 } 223 // Read the next change. 224 data, ok := <-w.in 225 if !ok { 226 // The tomb is already killed with the correct error 227 // at this point, so just return. 228 return nil 229 } 230 changes = data.(*params.StringsWatchResult).Changes 231 } 232 } 233 234 // Changes returns a channel that receives a list of strings of watched 235 // entites with changes. 236 func (w *stringsWatcher) Changes() watcher.StringsChannel { 237 return w.out 238 } 239 240 // relationUnitsWatcher will sends notifications of units entering and 241 // leaving the scope of a RelationUnit, and changes to the settings of 242 // those units known to have entered. 243 type relationUnitsWatcher struct { 244 commonWatcher 245 caller base.APICaller 246 relationUnitsWatcherId string 247 out chan watcher.RelationUnitsChange 248 } 249 250 func NewRelationUnitsWatcher(caller base.APICaller, result params.RelationUnitsWatchResult) watcher.RelationUnitsWatcher { 251 w := &relationUnitsWatcher{ 252 caller: caller, 253 relationUnitsWatcherId: result.RelationUnitsWatcherId, 254 out: make(chan watcher.RelationUnitsChange), 255 } 256 go func() { 257 defer w.tomb.Done() 258 w.tomb.Kill(w.loop(result.Changes)) 259 }() 260 return w 261 } 262 263 func copyRelationUnitsChanged(src params.RelationUnitsChange) watcher.RelationUnitsChange { 264 dst := watcher.RelationUnitsChange{ 265 Departed: src.Departed, 266 } 267 if src.Changed != nil { 268 dst.Changed = make(map[string]watcher.UnitSettings) 269 for name, unitSettings := range src.Changed { 270 dst.Changed[name] = watcher.UnitSettings{ 271 Version: unitSettings.Version, 272 } 273 } 274 } 275 return dst 276 } 277 278 func (w *relationUnitsWatcher) loop(initialChanges params.RelationUnitsChange) error { 279 changes := copyRelationUnitsChanged(initialChanges) 280 w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) } 281 w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId) 282 w.commonWatcher.init() 283 go w.commonLoop() 284 285 for { 286 select { 287 // Send the initial event or subsequent change. 288 case w.out <- changes: 289 case <-w.tomb.Dying(): 290 return nil 291 } 292 // Read the next change. 293 data, ok := <-w.in 294 if !ok { 295 // The tomb is already killed with the correct error 296 // at this point, so just return. 297 return nil 298 } 299 changes = copyRelationUnitsChanged(data.(*params.RelationUnitsWatchResult).Changes) 300 } 301 } 302 303 // Changes returns a channel that will receive the changes to 304 // counterpart units in a relation. The first event on the channel 305 // holds the initial state of the relation in its Changed field. 306 func (w *relationUnitsWatcher) Changes() watcher.RelationUnitsChannel { 307 return w.out 308 } 309 310 // machineAttachmentsWatcher will sends notifications of units entering and 311 // leaving the scope of a MachineStorageId, and changes to the settings of 312 // those units known to have entered. 313 type machineAttachmentsWatcher struct { 314 commonWatcher 315 caller base.APICaller 316 machineAttachmentsWatcherId string 317 out chan []watcher.MachineStorageId 318 } 319 320 // NewVolumeAttachmentsWatcher returns a MachineStorageIdsWatcher which 321 // communicates with the VolumeAttachmentsWatcher API facade to watch 322 // volume attachments. 323 func NewVolumeAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 324 return newMachineStorageIdsWatcher("VolumeAttachmentsWatcher", caller, result) 325 } 326 327 // NewFilesystemAttachmentsWatcher returns a MachineStorageIdsWatcher which 328 // communicates with the FilesystemAttachmentsWatcher API facade to watch 329 // filesystem attachments. 330 func NewFilesystemAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 331 return newMachineStorageIdsWatcher("FilesystemAttachmentsWatcher", caller, result) 332 } 333 334 func newMachineStorageIdsWatcher(facade string, caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 335 w := &machineAttachmentsWatcher{ 336 caller: caller, 337 machineAttachmentsWatcherId: result.MachineStorageIdsWatcherId, 338 out: make(chan []watcher.MachineStorageId), 339 } 340 go func() { 341 defer w.tomb.Done() 342 w.tomb.Kill(w.loop(facade, result.Changes)) 343 }() 344 return w 345 } 346 347 func copyMachineStorageIds(src []params.MachineStorageId) []watcher.MachineStorageId { 348 dst := make([]watcher.MachineStorageId, len(src)) 349 for i, msi := range src { 350 dst[i] = watcher.MachineStorageId{ 351 MachineTag: msi.MachineTag, 352 AttachmentTag: msi.AttachmentTag, 353 } 354 } 355 return dst 356 } 357 358 func (w *machineAttachmentsWatcher) loop(facade string, initialChanges []params.MachineStorageId) error { 359 changes := copyMachineStorageIds(initialChanges) 360 w.newResult = func() interface{} { return new(params.MachineStorageIdsWatchResult) } 361 w.call = makeWatcherAPICaller(w.caller, facade, w.machineAttachmentsWatcherId) 362 w.commonWatcher.init() 363 go w.commonLoop() 364 365 for { 366 select { 367 // Send the initial event or subsequent change. 368 case w.out <- changes: 369 case <-w.tomb.Dying(): 370 return nil 371 } 372 // Read the next change. 373 data, ok := <-w.in 374 if !ok { 375 // The tomb is already killed with the correct error 376 // at this point, so just return. 377 return nil 378 } 379 changes = copyMachineStorageIds(data.(*params.MachineStorageIdsWatchResult).Changes) 380 } 381 } 382 383 // Changes returns a channel that will receive the IDs of machine 384 // storage entity attachments which have changed. 385 func (w *machineAttachmentsWatcher) Changes() watcher.MachineStorageIdsChannel { 386 return w.out 387 } 388 389 // EntitiesWatcher will send events when something changes. 390 // The content for the changes is a list of tag strings. 391 type entitiesWatcher struct { 392 commonWatcher 393 caller base.APICaller 394 entitiesWatcherId string 395 out chan []string 396 } 397 398 func NewEntitiesWatcher(caller base.APICaller, result params.EntitiesWatchResult) watcher.EntitiesWatcher { 399 w := &entitiesWatcher{ 400 caller: caller, 401 entitiesWatcherId: result.EntitiesWatcherId, 402 out: make(chan []string), 403 } 404 go func() { 405 defer w.tomb.Done() 406 w.tomb.Kill(w.loop(result.Changes)) 407 }() 408 return w 409 } 410 411 func (w *entitiesWatcher) loop(initialChanges []string) error { 412 changes := initialChanges 413 w.newResult = func() interface{} { return new(params.EntitiesWatchResult) } 414 w.call = makeWatcherAPICaller(w.caller, "EntityWatcher", w.entitiesWatcherId) 415 w.commonWatcher.init() 416 go w.commonLoop() 417 418 for { 419 select { 420 // Send the initial event or subsequent change. 421 case w.out <- changes: 422 case <-w.tomb.Dying(): 423 return nil 424 } 425 // Read the next change. 426 data, ok := <-w.in 427 if !ok { 428 // The tomb is already killed with the correct error 429 // at this point, so just return. 430 return nil 431 } 432 // Changes have been transformed at the server side already. 433 changes = data.(*params.EntitiesWatchResult).Changes 434 } 435 } 436 437 // Changes returns a channel that receives a list of changes 438 // as tags (converted to strings) of the watched entities 439 // with changes. 440 func (w *entitiesWatcher) Changes() watcher.StringsChannel { 441 return w.out 442 } 443 444 // NewMigrationStatusWatcher takes the NotifyWatcherId returns by the 445 // MigrationSlave.Watch API and returns a watcher which will report 446 // status changes for any migration of the model associated with the 447 // API connection. 448 func NewMigrationStatusWatcher(caller base.APICaller, watcherId string) watcher.MigrationStatusWatcher { 449 w := &migrationStatusWatcher{ 450 caller: caller, 451 id: watcherId, 452 out: make(chan watcher.MigrationStatus), 453 } 454 go func() { 455 defer w.tomb.Done() 456 w.tomb.Kill(w.loop()) 457 }() 458 return w 459 } 460 461 type migrationStatusWatcher struct { 462 commonWatcher 463 caller base.APICaller 464 id string 465 out chan watcher.MigrationStatus 466 } 467 468 func (w *migrationStatusWatcher) loop() error { 469 w.newResult = func() interface{} { return new(params.MigrationStatus) } 470 w.call = makeWatcherAPICaller(w.caller, "MigrationStatusWatcher", w.id) 471 w.commonWatcher.init() 472 go w.commonLoop() 473 474 for { 475 var data interface{} 476 var ok bool 477 478 select { 479 case data, ok = <-w.in: 480 if !ok { 481 // The tomb is already killed with the correct error 482 // at this point, so just return. 483 return nil 484 } 485 case <-w.tomb.Dying(): 486 return nil 487 } 488 489 inStatus := *data.(*params.MigrationStatus) 490 phase, ok := migration.ParsePhase(inStatus.Phase) 491 if !ok { 492 return errors.Errorf("invalid phase %q", inStatus.Phase) 493 } 494 outStatus := watcher.MigrationStatus{ 495 MigrationId: inStatus.MigrationId, 496 Attempt: inStatus.Attempt, 497 Phase: phase, 498 SourceAPIAddrs: inStatus.SourceAPIAddrs, 499 SourceCACert: inStatus.SourceCACert, 500 TargetAPIAddrs: inStatus.TargetAPIAddrs, 501 TargetCACert: inStatus.TargetCACert, 502 } 503 select { 504 case w.out <- outStatus: 505 case <-w.tomb.Dying(): 506 return nil 507 } 508 } 509 } 510 511 // Changes returns a channel that reports the latest status of the 512 // migration of a model. 513 func (w *migrationStatusWatcher) Changes() <-chan watcher.MigrationStatus { 514 return w.out 515 }