github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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 "github.com/juju/names/v5" 12 "github.com/kr/pretty" 13 "gopkg.in/tomb.v2" 14 15 "github.com/juju/juju/api/base" 16 "github.com/juju/juju/core/migration" 17 "github.com/juju/juju/core/secrets" 18 "github.com/juju/juju/core/status" 19 "github.com/juju/juju/core/watcher" 20 "github.com/juju/juju/rpc" 21 "github.com/juju/juju/rpc/params" 22 ) 23 24 var logger = loggo.GetLogger("juju.api.watcher") 25 26 // commonWatcher implements common watcher logic in one place to 27 // reduce code duplication, but it's not in fact a complete watcher; 28 // it's intended for embedding. 29 type commonWatcher struct { 30 tomb tomb.Tomb 31 in chan interface{} 32 33 // These fields must be set by the embedding watcher, before 34 // calling init(). 35 36 // newResult must return a pointer to a value of the type returned 37 // by the watcher's Next call. 38 newResult func() interface{} 39 40 // call should invoke the given API method, placing the call's 41 // returned value in result (if any). 42 call watcherAPICall 43 } 44 45 // watcherAPICall wraps up the information about what facade and what watcher 46 // Id we are calling, and just gives us a simple way to call a common method 47 // with a given return value. 48 type watcherAPICall func(method string, result interface{}) error 49 50 // makeWatcherAPICaller creates a watcherAPICall function for a given facade name 51 // and watcherId. 52 func makeWatcherAPICaller(caller base.APICaller, facadeName, watcherId string) watcherAPICall { 53 bestVersion := caller.BestFacadeVersion(facadeName) 54 return func(request string, result interface{}) error { 55 return caller.APICall(facadeName, bestVersion, 56 watcherId, request, nil, &result) 57 } 58 } 59 60 // init must be called to initialize an embedded commonWatcher's 61 // fields. Make sure newResult and call fields are set beforehand. 62 func (w *commonWatcher) init() { 63 w.in = make(chan interface{}) 64 if w.newResult == nil { 65 panic("newResult must be set") 66 } 67 if w.call == nil { 68 panic("call must be set") 69 } 70 } 71 72 // commonLoop implements the loop structure common to the client 73 // watchers. It should be started in a separate goroutine by any 74 // watcher that embeds commonWatcher. It kills the commonWatcher's 75 // tomb when an error occurs. 76 func (w *commonWatcher) commonLoop() { 77 defer close(w.in) 78 var wg sync.WaitGroup 79 wg.Add(1) 80 go func() { 81 // When the watcher has been stopped, we send a Stop request 82 // to the server, which will remove the watcher and return a 83 // CodeStopped error to any currently outstanding call to 84 // Next. If a call to Next happens just after the watcher has 85 // been stopped, we'll get a CodeNotFound error; Either way 86 // we'll return, wait for the stop request to complete, and 87 // the watcher will die with all resources cleaned up. 88 defer wg.Done() 89 <-w.tomb.Dying() 90 if err := w.call("Stop", nil); err != nil { 91 // Don't log an error if a watcher is stopped due to an agent restart, 92 // or if the entity being watched is already removed. 93 if !isAgentRestartError(err) && 94 err.Error() != rpc.ErrShutdown.Error() && !params.IsCodeNotFound(err) { 95 logger.Errorf("error trying to stop watcher: %v", err) 96 } 97 } 98 }() 99 wg.Add(1) 100 go func() { 101 // Because Next blocks until there are changes, we need to 102 // call it in a separate goroutine, so the watcher can be 103 // stopped normally. 104 defer wg.Done() 105 for { 106 result := w.newResult() 107 err := w.call("Next", &result) 108 if err != nil { 109 if params.IsCodeStopped(err) || params.IsCodeNotFound(err) { 110 if w.tomb.Err() != tomb.ErrStillAlive { 111 // The watcher has been stopped at the client end, so we're 112 // expecting one of the above two kinds of error. 113 // We might see the same errors if the server itself 114 // has been shut down, in which case we leave them 115 // untouched. 116 err = tomb.ErrDying 117 } 118 } 119 // Something went wrong, just report the error and bail out. 120 w.tomb.Kill(err) 121 return 122 } 123 select { 124 case <-w.tomb.Dying(): 125 return 126 case w.in <- result: 127 // Report back the result we just got. 128 } 129 } 130 }() 131 wg.Wait() 132 } 133 134 var ( 135 // ErrRestartArgent matches juju/juju/worker/error.go ErrRestartAgent 136 // and is used to indicate that the watcher should be restarted. 137 ErrRestartAgent = errors.New("agent should be restarted") 138 ) 139 140 func isAgentRestartError(err error) bool { 141 return err.Error() == ErrRestartAgent.Error() 142 } 143 144 // Kill is part of the worker.Worker interface. 145 func (w *commonWatcher) Kill() { 146 w.tomb.Kill(nil) 147 } 148 149 // Wait is part of the worker.Worker interface. 150 func (w *commonWatcher) Wait() error { 151 return w.tomb.Wait() 152 } 153 154 // notifyWatcher will send events when something changes. 155 // It does not send content for those changes. 156 type notifyWatcher struct { 157 commonWatcher 158 caller base.APICaller 159 notifyWatcherId string 160 out chan struct{} 161 } 162 163 // If an API call returns a NotifyWatchResult, you can use this to turn it into 164 // a local Watcher. 165 func NewNotifyWatcher(caller base.APICaller, result params.NotifyWatchResult) watcher.NotifyWatcher { 166 w := ¬ifyWatcher{ 167 caller: caller, 168 notifyWatcherId: result.NotifyWatcherId, 169 out: make(chan struct{}), 170 } 171 w.tomb.Go(w.loop) 172 return w 173 } 174 175 func (w *notifyWatcher) loop() error { 176 // No results for this watcher type. 177 w.newResult = func() interface{} { return nil } 178 w.call = makeWatcherAPICaller(w.caller, "NotifyWatcher", w.notifyWatcherId) 179 w.commonWatcher.init() 180 go w.commonLoop() 181 182 for { 183 select { 184 // Since for a notifyWatcher there are no changes to send, we 185 // just set the event (initial first, then after each change). 186 case w.out <- struct{}{}: 187 case <-w.tomb.Dying(): 188 return nil 189 } 190 if _, ok := <-w.in; !ok { 191 // The tomb is already killed with the correct 192 // error at this point, so just return. 193 return nil 194 } 195 } 196 } 197 198 // Changes returns a channel that receives a value when a given entity 199 // changes in some way. 200 func (w *notifyWatcher) Changes() watcher.NotifyChannel { 201 return w.out 202 } 203 204 // stringsWatcher will send events when something changes. 205 // The content of the changes is a list of strings. 206 type stringsWatcher struct { 207 commonWatcher 208 caller base.APICaller 209 stringsWatcherId string 210 out chan []string 211 } 212 213 func NewStringsWatcher(caller base.APICaller, result params.StringsWatchResult) watcher.StringsWatcher { 214 w := &stringsWatcher{ 215 caller: caller, 216 stringsWatcherId: result.StringsWatcherId, 217 out: make(chan []string), 218 } 219 w.tomb.Go(func() error { 220 return w.loop(result.Changes) 221 }) 222 return w 223 } 224 225 func (w *stringsWatcher) loop(initialChanges []string) error { 226 changes := initialChanges 227 w.newResult = func() interface{} { return new(params.StringsWatchResult) } 228 w.call = makeWatcherAPICaller(w.caller, "StringsWatcher", w.stringsWatcherId) 229 w.commonWatcher.init() 230 go w.commonLoop() 231 232 for { 233 select { 234 // Send the initial event or subsequent change. 235 case w.out <- changes: 236 case <-w.tomb.Dying(): 237 return nil 238 } 239 // Read the next change. 240 data, ok := <-w.in 241 if !ok { 242 // The tomb is already killed with the correct error 243 // at this point, so just return. 244 return nil 245 } 246 changes = data.(*params.StringsWatchResult).Changes 247 } 248 } 249 250 // Changes returns a channel that receives a list of strings of watched 251 // entities with changes. 252 func (w *stringsWatcher) Changes() watcher.StringsChannel { 253 return w.out 254 } 255 256 // relationUnitsWatcher will sends notifications of units entering and 257 // leaving the scope of a RelationUnit, and changes to the settings of 258 // those units known to have entered. 259 type relationUnitsWatcher struct { 260 commonWatcher 261 caller base.APICaller 262 logger loggo.Logger 263 relationUnitsWatcherId string 264 out chan watcher.RelationUnitsChange 265 } 266 267 func NewRelationUnitsWatcher(caller base.APICaller, result params.RelationUnitsWatchResult) watcher.RelationUnitsWatcher { 268 w := &relationUnitsWatcher{ 269 caller: caller, 270 logger: logger.Child("relationunits"), 271 relationUnitsWatcherId: result.RelationUnitsWatcherId, 272 out: make(chan watcher.RelationUnitsChange), 273 } 274 w.tomb.Go(func() error { 275 return w.loop(result.Changes) 276 }) 277 return w 278 } 279 280 func copyRelationUnitsChanged(src params.RelationUnitsChange) watcher.RelationUnitsChange { 281 dst := watcher.RelationUnitsChange{ 282 Departed: src.Departed, 283 } 284 if src.Changed != nil { 285 dst.Changed = make(map[string]watcher.UnitSettings, len(src.Changed)) 286 for name, unitSettings := range src.Changed { 287 dst.Changed[name] = watcher.UnitSettings{ 288 Version: unitSettings.Version, 289 } 290 } 291 } 292 if src.AppChanged != nil { 293 dst.AppChanged = make(map[string]int64, len(src.AppChanged)) 294 for name, appVersion := range src.AppChanged { 295 dst.AppChanged[name] = appVersion 296 } 297 } 298 return dst 299 } 300 301 func (w *relationUnitsWatcher) loop(initialChanges params.RelationUnitsChange) error { 302 changes := copyRelationUnitsChanged(initialChanges) 303 w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) } 304 w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId) 305 w.commonWatcher.init() 306 go w.commonLoop() 307 308 for { 309 select { 310 // Send the initial event or subsequent change. 311 case w.out <- changes: 312 if w.logger.IsTraceEnabled() { 313 w.logger.Tracef("sent relation units changes %# v", pretty.Formatter(changes)) 314 } 315 case <-w.tomb.Dying(): 316 return nil 317 } 318 // Read the next change. 319 data, ok := <-w.in 320 if !ok { 321 // The tomb is already killed with the correct error 322 // at this point, so just return. 323 return nil 324 } 325 changes = copyRelationUnitsChanged(data.(*params.RelationUnitsWatchResult).Changes) 326 } 327 } 328 329 // Changes returns a channel that will receive the changes to 330 // counterpart units in a relation. The first event on the channel 331 // holds the initial state of the relation in its Changed field. 332 func (w *relationUnitsWatcher) Changes() watcher.RelationUnitsChannel { 333 return w.out 334 } 335 336 // RemoteRelationWatcher is a worker that emits remote relation change 337 // events. It's not defined in core/watcher because it emits params 338 // structs - this makes more sense than converting to a core struct 339 // just to convert back when the event is published to the other 340 // model's API. 341 type RemoteRelationWatcher interface { 342 watcher.CoreWatcher 343 Changes() <-chan params.RemoteRelationChangeEvent 344 } 345 346 // remoteRelationWatcher sends notifications of units entering and 347 // leaving scope of a relation and changes to unit/application 348 // settings, but yielding fleshed-out events so the caller doesn't 349 // need to call back to get the settings values. 350 type remoteRelationWatcher struct { 351 commonWatcher 352 caller base.APICaller 353 logger loggo.Logger 354 remoteRelationWatcherId string 355 out chan params.RemoteRelationChangeEvent 356 } 357 358 // NewRemoteRelationWatcher returns a RemoteRelationWatcher receiving 359 // events from the one running on the API server. 360 func NewRemoteRelationWatcher(caller base.APICaller, result params.RemoteRelationWatchResult) RemoteRelationWatcher { 361 w := &remoteRelationWatcher{ 362 caller: caller, 363 logger: logger.Child("remoterelations"), 364 remoteRelationWatcherId: result.RemoteRelationWatcherId, 365 out: make(chan params.RemoteRelationChangeEvent), 366 } 367 w.tomb.Go(func() error { 368 return w.loop(result.Changes) 369 }) 370 return w 371 } 372 373 func (w *remoteRelationWatcher) loop(initialChange params.RemoteRelationChangeEvent) error { 374 change := initialChange 375 w.newResult = func() interface{} { return new(params.RemoteRelationWatchResult) } 376 w.call = makeWatcherAPICaller(w.caller, "RemoteRelationWatcher", w.remoteRelationWatcherId) 377 w.commonWatcher.init() 378 w.tomb.Go(func() error { 379 w.commonLoop() 380 return nil 381 }) 382 383 for { 384 select { 385 // Send out the initial event or subsequent change. 386 case w.out <- change: 387 if w.logger.IsTraceEnabled() { 388 w.logger.Tracef("sent remote relation change %# v", pretty.Formatter(change)) 389 } 390 case <-w.tomb.Dying(): 391 return nil 392 } 393 394 data, ok := <-w.in 395 if !ok { 396 // The tomb is already killed with the correct error at 397 // this point, so just return. 398 return nil 399 } 400 result, ok := data.(*params.RemoteRelationWatchResult) 401 if !ok { 402 return errors.Errorf("expected *params.RemoteRelationWatchResult, got %#v", data) 403 } 404 if result.Error != nil { 405 return errors.Trace(result.Error) 406 } 407 change = result.Changes 408 } 409 } 410 411 // Changes returns a channel that will emit changes to the remote 412 // relation units. 413 func (w *remoteRelationWatcher) Changes() <-chan params.RemoteRelationChangeEvent { 414 return w.out 415 } 416 417 // SettingsGetter is a callback function the remote relation 418 // compatibility watcher calls to get unit settings when expanding 419 // events. 420 type SettingsGetter func([]string) ([]params.SettingsResult, error) 421 422 // remoteRelationCompatWatcher is a compatibility adapter that calls 423 // back to the v1 crossmodelrelations API methods to wrap a 424 // server-side RelationUnitsWatcher into a RemoteRelationWatcher. It 425 // needs the relation and application token to include in the outgoing 426 // change events. 427 type remoteRelationCompatWatcher struct { 428 commonWatcher 429 caller base.APICaller 430 relationUnitsWatcherId string 431 relationToken string 432 appToken string 433 getSettings SettingsGetter 434 out chan params.RemoteRelationChangeEvent 435 } 436 437 // NewRemoteRelationCompatWatcher returns a RemoteRelationWatcher 438 // based on a server-side RelationUnitsWatcher. 439 func NewRemoteRelationCompatWatcher( 440 caller base.APICaller, 441 result params.RelationUnitsWatchResult, 442 relationToken string, 443 appToken string, 444 getSettings SettingsGetter, 445 ) RemoteRelationWatcher { 446 w := &remoteRelationCompatWatcher{ 447 caller: caller, 448 relationUnitsWatcherId: result.RelationUnitsWatcherId, 449 relationToken: relationToken, 450 appToken: appToken, 451 getSettings: getSettings, 452 out: make(chan params.RemoteRelationChangeEvent), 453 } 454 w.tomb.Go(func() error { 455 return w.loop(result.Changes) 456 }) 457 return w 458 } 459 460 func (w *remoteRelationCompatWatcher) loop(initialChange params.RelationUnitsChange) error { 461 change := initialChange 462 w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) } 463 w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId) 464 w.commonWatcher.init() 465 466 // Ensure the worker loop waits for the commonLoop to stop before 467 // being fully finished. 468 w.tomb.Go(func() error { 469 w.commonLoop() 470 return nil 471 }) 472 473 for { 474 expanded, err := w.expandChange(change) 475 if err != nil { 476 return errors.Trace(err) 477 } 478 select { 479 // Send out the initial event or subsequent change. 480 case w.out <- expanded: 481 case <-w.tomb.Dying(): 482 return nil 483 } 484 data, ok := <-w.in 485 if !ok { 486 // The tomb is already killed with the correct error at 487 // this point, so just return. 488 return nil 489 } 490 result, ok := data.(*params.RelationUnitsWatchResult) 491 if !ok { 492 return errors.Errorf("expected *params.RelationUnitsWatchResult, got %#v", data) 493 } 494 if result.Error != nil { 495 return errors.Trace(result.Error) 496 } 497 change = result.Changes 498 } 499 } 500 501 func (w *remoteRelationCompatWatcher) expandChange(change params.RelationUnitsChange) (params.RemoteRelationChangeEvent, error) { 502 var empty params.RemoteRelationChangeEvent 503 var unitNames []string 504 for unit := range change.Changed { 505 unitNames = append(unitNames, unit) 506 } 507 var changedUnits []params.RemoteRelationUnitChange 508 if len(unitNames) > 0 { 509 results, err := w.getSettings(unitNames) 510 if err != nil { 511 return empty, errors.Annotatef(err, "getting relation unit settings for %q", unitNames) 512 } 513 for i, result := range results { 514 num, err := names.UnitNumber(unitNames[i]) 515 if err != nil { 516 return empty, errors.Trace(err) 517 } 518 unitChange := params.RemoteRelationUnitChange{ 519 UnitId: num, 520 Settings: make(map[string]interface{}), 521 } 522 for k, v := range result.Settings { 523 unitChange.Settings[k] = v 524 } 525 changedUnits = append(changedUnits, unitChange) 526 } 527 } 528 529 var departedUnits []int 530 for _, unit := range change.Departed { 531 num, err := names.UnitNumber(unit) 532 if err != nil { 533 return empty, errors.Trace(err) 534 } 535 departedUnits = append(departedUnits, num) 536 } 537 538 expanded := params.RemoteRelationChangeEvent{ 539 RelationToken: w.relationToken, 540 ApplicationToken: w.appToken, 541 ChangedUnits: changedUnits, 542 DepartedUnits: departedUnits, 543 } 544 // No need to handle AppChanged here - the v1 API can't tell us 545 // app settings. 546 return expanded, nil 547 548 } 549 550 // Changes returns a channel that will emit changes to the remote 551 // relation units. 552 func (w *remoteRelationCompatWatcher) Changes() <-chan params.RemoteRelationChangeEvent { 553 return w.out 554 } 555 556 // relationStatusWatcher will sends notifications of changes to 557 // relation life and suspended status. 558 type relationStatusWatcher struct { 559 commonWatcher 560 caller base.APICaller 561 relationStatusWatcherId string 562 out chan []watcher.RelationStatusChange 563 } 564 565 // NewRelationStatusWatcher returns a watcher notifying of changes to 566 // relation life and suspended status. 567 func NewRelationStatusWatcher( 568 caller base.APICaller, result params.RelationLifeSuspendedStatusWatchResult, 569 ) watcher.RelationStatusWatcher { 570 w := &relationStatusWatcher{ 571 caller: caller, 572 relationStatusWatcherId: result.RelationStatusWatcherId, 573 out: make(chan []watcher.RelationStatusChange), 574 } 575 w.tomb.Go(func() error { 576 return w.loop(result.Changes) 577 }) 578 return w 579 } 580 581 // mergeChanges combines the status changes in current and new, such that we end up with 582 // only one change per offer in the result; the most recent change wins. 583 func (w *relationStatusWatcher) mergeChanges(current, new []watcher.RelationStatusChange) []watcher.RelationStatusChange { 584 chMap := make(map[string]watcher.RelationStatusChange) 585 for _, c := range current { 586 chMap[c.Key] = c 587 } 588 for _, c := range new { 589 chMap[c.Key] = c 590 } 591 var result []watcher.RelationStatusChange 592 for _, c := range chMap { 593 result = append(result, c) 594 } 595 return result 596 } 597 598 func (w *relationStatusWatcher) loop(initialChanges []params.RelationLifeSuspendedStatusChange) error { 599 w.newResult = func() interface{} { return new(params.RelationLifeSuspendedStatusWatchResult) } 600 w.call = makeWatcherAPICaller(w.caller, "RelationStatusWatcher", w.relationStatusWatcherId) 601 w.commonWatcher.init() 602 go w.commonLoop() 603 604 copyChanges := func(changes []params.RelationLifeSuspendedStatusChange) []watcher.RelationStatusChange { 605 result := make([]watcher.RelationStatusChange, len(changes)) 606 for i, ch := range changes { 607 result[i] = watcher.RelationStatusChange{ 608 Key: ch.Key, 609 Life: ch.Life, 610 Suspended: ch.Suspended, 611 SuspendedReason: ch.SuspendedReason, 612 } 613 } 614 return result 615 } 616 out := w.out 617 changes := copyChanges(initialChanges) 618 for { 619 select { 620 case <-w.tomb.Dying(): 621 return tomb.ErrDying 622 // Read the next change. 623 case data, ok := <-w.in: 624 if !ok { 625 // The tomb is already killed with the correct error 626 // at this point, so just return. 627 return nil 628 } 629 newChanges := copyChanges(data.(*params.RelationLifeSuspendedStatusWatchResult).Changes) 630 changes = w.mergeChanges(changes, newChanges) 631 out = w.out 632 case out <- changes: 633 out = nil 634 changes = nil 635 } 636 } 637 } 638 639 // Changes returns a channel that will receive the changes to 640 // the life and status of a relation. The first event reflects the current 641 // values of these attributes. 642 func (w *relationStatusWatcher) Changes() watcher.RelationStatusChannel { 643 return w.out 644 } 645 646 // offerStatusWatcher will send notifications of changes to offer status. 647 type offerStatusWatcher struct { 648 commonWatcher 649 caller base.APICaller 650 offerStatusWatcherId string 651 out chan []watcher.OfferStatusChange 652 } 653 654 // NewOfferStatusWatcher returns a watcher notifying of changes to 655 // offer status. 656 func NewOfferStatusWatcher( 657 caller base.APICaller, result params.OfferStatusWatchResult, 658 ) watcher.OfferStatusWatcher { 659 w := &offerStatusWatcher{ 660 caller: caller, 661 offerStatusWatcherId: result.OfferStatusWatcherId, 662 out: make(chan []watcher.OfferStatusChange), 663 } 664 w.tomb.Go(func() error { 665 return w.loop(result.Changes) 666 }) 667 return w 668 } 669 670 // mergeChanges combines the status changes in current and new, such that we end up with 671 // only one change per offer in the result; the most recent change wins. 672 func (w *offerStatusWatcher) mergeChanges(current, new []watcher.OfferStatusChange) []watcher.OfferStatusChange { 673 chMap := make(map[string]watcher.OfferStatusChange) 674 for _, c := range current { 675 chMap[c.Name] = c 676 } 677 for _, c := range new { 678 chMap[c.Name] = c 679 } 680 var result []watcher.OfferStatusChange 681 for _, c := range chMap { 682 result = append(result, c) 683 } 684 return result 685 } 686 687 func (w *offerStatusWatcher) loop(initialChanges []params.OfferStatusChange) error { 688 w.newResult = func() interface{} { return new(params.OfferStatusWatchResult) } 689 w.call = makeWatcherAPICaller(w.caller, "OfferStatusWatcher", w.offerStatusWatcherId) 690 w.commonWatcher.init() 691 go w.commonLoop() 692 693 copyChanges := func(changes []params.OfferStatusChange) []watcher.OfferStatusChange { 694 result := make([]watcher.OfferStatusChange, len(changes)) 695 for i, ch := range changes { 696 result[i] = watcher.OfferStatusChange{ 697 Name: ch.OfferName, 698 Status: status.StatusInfo{ 699 Status: ch.Status.Status, 700 Message: ch.Status.Info, 701 Data: ch.Status.Data, 702 Since: ch.Status.Since, 703 }, 704 } 705 } 706 return result 707 } 708 out := w.out 709 changes := copyChanges(initialChanges) 710 for { 711 select { 712 case <-w.tomb.Dying(): 713 return tomb.ErrDying 714 // Read the next change. 715 case data, ok := <-w.in: 716 if !ok { 717 // The tomb is already killed with the correct error 718 // at this point, so just return. 719 return nil 720 } 721 newChanges := copyChanges(data.(*params.OfferStatusWatchResult).Changes) 722 changes = w.mergeChanges(changes, newChanges) 723 out = w.out 724 case out <- changes: 725 out = nil 726 changes = nil 727 } 728 } 729 } 730 731 // Changes returns a channel that will receive the changes to 732 // the status of an offer. The first event reflects the current 733 // values of these attributes. 734 func (w *offerStatusWatcher) Changes() watcher.OfferStatusChannel { 735 return w.out 736 } 737 738 // machineAttachmentsWatcher will sends notifications of units entering and 739 // leaving the scope of a MachineStorageId, and changes to the settings of 740 // those units known to have entered. 741 type machineAttachmentsWatcher struct { 742 commonWatcher 743 caller base.APICaller 744 machineAttachmentsWatcherId string 745 out chan []watcher.MachineStorageId 746 } 747 748 // NewVolumeAttachmentsWatcher returns a MachineStorageIdsWatcher which 749 // communicates with the VolumeAttachmentsWatcher API facade to watch 750 // volume attachments. 751 func NewVolumeAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 752 return newMachineStorageIdsWatcher("VolumeAttachmentsWatcher", caller, result) 753 } 754 755 // NewVolumeAttachmentPlansWatcher returns a MachineStorageIdsWatcher which 756 // communicates with the VolumeAttachmentPlansWatcher API facade to watch 757 // volume attachments. 758 func NewVolumeAttachmentPlansWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 759 return newMachineStorageIdsWatcher("VolumeAttachmentPlansWatcher", caller, result) 760 } 761 762 // NewFilesystemAttachmentsWatcher returns a MachineStorageIdsWatcher which 763 // communicates with the FilesystemAttachmentsWatcher API facade to watch 764 // filesystem attachments. 765 func NewFilesystemAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 766 return newMachineStorageIdsWatcher("FilesystemAttachmentsWatcher", caller, result) 767 } 768 769 func newMachineStorageIdsWatcher(facade string, caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher { 770 w := &machineAttachmentsWatcher{ 771 caller: caller, 772 machineAttachmentsWatcherId: result.MachineStorageIdsWatcherId, 773 out: make(chan []watcher.MachineStorageId), 774 } 775 w.tomb.Go(func() error { 776 return w.loop(facade, result.Changes) 777 }) 778 return w 779 } 780 781 func copyMachineStorageIds(src []params.MachineStorageId) []watcher.MachineStorageId { 782 dst := make([]watcher.MachineStorageId, len(src)) 783 for i, msi := range src { 784 dst[i] = watcher.MachineStorageId{ 785 MachineTag: msi.MachineTag, 786 AttachmentTag: msi.AttachmentTag, 787 } 788 } 789 return dst 790 } 791 792 func (w *machineAttachmentsWatcher) loop(facade string, initialChanges []params.MachineStorageId) error { 793 changes := copyMachineStorageIds(initialChanges) 794 w.newResult = func() interface{} { return new(params.MachineStorageIdsWatchResult) } 795 w.call = makeWatcherAPICaller(w.caller, facade, w.machineAttachmentsWatcherId) 796 w.commonWatcher.init() 797 go w.commonLoop() 798 799 for { 800 select { 801 // Send the initial event or subsequent change. 802 case w.out <- changes: 803 case <-w.tomb.Dying(): 804 return nil 805 } 806 // Read the next change. 807 data, ok := <-w.in 808 if !ok { 809 // The tomb is already killed with the correct error 810 // at this point, so just return. 811 return nil 812 } 813 changes = copyMachineStorageIds(data.(*params.MachineStorageIdsWatchResult).Changes) 814 } 815 } 816 817 // Changes returns a channel that will receive the IDs of machine 818 // storage entity attachments which have changed. 819 func (w *machineAttachmentsWatcher) Changes() watcher.MachineStorageIdsChannel { 820 return w.out 821 } 822 823 // NewMigrationStatusWatcher takes the NotifyWatcherId returns by the 824 // MigrationSlave.Watch API and returns a watcher which will report 825 // status changes for any migration of the model associated with the 826 // API connection. 827 func NewMigrationStatusWatcher(caller base.APICaller, watcherId string) watcher.MigrationStatusWatcher { 828 w := &migrationStatusWatcher{ 829 caller: caller, 830 id: watcherId, 831 out: make(chan watcher.MigrationStatus), 832 } 833 w.tomb.Go(w.loop) 834 return w 835 } 836 837 type migrationStatusWatcher struct { 838 commonWatcher 839 caller base.APICaller 840 id string 841 out chan watcher.MigrationStatus 842 } 843 844 func (w *migrationStatusWatcher) loop() error { 845 w.newResult = func() interface{} { return new(params.MigrationStatus) } 846 w.call = makeWatcherAPICaller(w.caller, "MigrationStatusWatcher", w.id) 847 w.commonWatcher.init() 848 go w.commonLoop() 849 850 for { 851 var data interface{} 852 var ok bool 853 854 select { 855 case data, ok = <-w.in: 856 if !ok { 857 // The tomb is already killed with the correct error 858 // at this point, so just return. 859 return nil 860 } 861 case <-w.tomb.Dying(): 862 return nil 863 } 864 865 inStatus := *data.(*params.MigrationStatus) 866 phase, ok := migration.ParsePhase(inStatus.Phase) 867 if !ok { 868 return errors.Errorf("invalid phase %q", inStatus.Phase) 869 } 870 outStatus := watcher.MigrationStatus{ 871 MigrationId: inStatus.MigrationId, 872 Attempt: inStatus.Attempt, 873 Phase: phase, 874 SourceAPIAddrs: inStatus.SourceAPIAddrs, 875 SourceCACert: inStatus.SourceCACert, 876 TargetAPIAddrs: inStatus.TargetAPIAddrs, 877 TargetCACert: inStatus.TargetCACert, 878 } 879 select { 880 case w.out <- outStatus: 881 case <-w.tomb.Dying(): 882 return nil 883 } 884 } 885 } 886 887 // Changes returns a channel that reports the latest status of the 888 // migration of a model. 889 func (w *migrationStatusWatcher) Changes() <-chan watcher.MigrationStatus { 890 return w.out 891 } 892 893 // secretsTriggerWatcher will send notifications of changes to secret trigger times. 894 type secretsTriggerWatcher struct { 895 commonWatcher 896 caller base.APICaller 897 apiFacade string 898 watcherId string 899 out chan []watcher.SecretTriggerChange 900 } 901 902 // NewSecretsTriggerWatcher returns a new secrets trigger watcher. 903 func NewSecretsTriggerWatcher( 904 caller base.APICaller, result params.SecretTriggerWatchResult, 905 ) watcher.SecretTriggerWatcher { 906 w := &secretsTriggerWatcher{ 907 caller: caller, 908 apiFacade: "SecretsTriggerWatcher", 909 watcherId: result.WatcherId, 910 out: make(chan []watcher.SecretTriggerChange), 911 } 912 w.newResult = func() interface{} { return new(params.SecretTriggerWatchResult) } 913 w.tomb.Go(func() error { 914 defer close(w.out) 915 return w.loop(result.Changes) 916 }) 917 return w 918 } 919 920 // mergeChanges combines the changes in current and newChanges, such that we end up with 921 // only one change per trigger change in the result; the most recent change wins. 922 func (w *secretsTriggerWatcher) mergeChanges(current, newChanges []watcher.SecretTriggerChange) []watcher.SecretTriggerChange { 923 chMap := make(map[string]watcher.SecretTriggerChange) 924 for _, c := range current { 925 chMap[c.URI.ID] = c 926 } 927 for _, c := range newChanges { 928 chMap[c.URI.ID] = c 929 } 930 result := make([]watcher.SecretTriggerChange, len(chMap)) 931 i := 0 932 for _, c := range chMap { 933 result[i] = c 934 i++ 935 } 936 return result 937 } 938 939 func (w *secretsTriggerWatcher) loop(initialChanges []params.SecretTriggerChange) error { 940 w.call = makeWatcherAPICaller(w.caller, w.apiFacade, w.watcherId) 941 w.commonWatcher.init() 942 go w.commonLoop() 943 944 copyChanges := func(changes []params.SecretTriggerChange) []watcher.SecretTriggerChange { 945 result := make([]watcher.SecretTriggerChange, len(changes)) 946 for i, ch := range changes { 947 uri, err := secrets.ParseURI(ch.URI) 948 if err != nil { 949 logger.Errorf("ignoring invalid secret URI: %q", ch.URI) 950 continue 951 } 952 result[i] = watcher.SecretTriggerChange{ 953 URI: uri, 954 Revision: ch.Revision, 955 NextTriggerTime: ch.NextTriggerTime, 956 } 957 } 958 return result 959 } 960 out := w.out 961 changes := copyChanges(initialChanges) 962 for { 963 select { 964 case <-w.tomb.Dying(): 965 return tomb.ErrDying 966 // Read the next change. 967 case data, ok := <-w.in: 968 if !ok { 969 // The tomb is already killed with the correct error 970 // at this point, so just return. 971 return nil 972 } 973 newChanges := copyChanges(data.(*params.SecretTriggerWatchResult).Changes) 974 changes = w.mergeChanges(changes, newChanges) 975 out = w.out 976 case out <- changes: 977 out = nil 978 changes = nil 979 } 980 } 981 } 982 983 // Changes returns a channel that will receive the changes to 984 // a secret trigger. The first event reflects the current 985 // values of these attributes. 986 func (w *secretsTriggerWatcher) Changes() watcher.SecretTriggerChannel { 987 return w.out 988 } 989 990 // secretBackendRotateWatcher will send notifications of changes to secret trigger times. 991 type secretBackendRotateWatcher struct { 992 commonWatcher 993 caller base.APICaller 994 watcherId string 995 out chan []watcher.SecretBackendRotateChange 996 } 997 998 // NewSecretBackendRotateWatcher returns a new secret backend rotate watcher. 999 func NewSecretBackendRotateWatcher( 1000 caller base.APICaller, result params.SecretBackendRotateWatchResult, 1001 ) watcher.SecretBackendRotateWatcher { 1002 w := &secretBackendRotateWatcher{ 1003 caller: caller, 1004 watcherId: result.WatcherId, 1005 out: make(chan []watcher.SecretBackendRotateChange), 1006 } 1007 w.newResult = func() interface{} { return new(params.SecretBackendRotateWatchResult) } 1008 w.tomb.Go(func() error { 1009 defer close(w.out) 1010 return w.loop(result.Changes) 1011 }) 1012 return w 1013 } 1014 1015 // mergeChanges combines the changes in current and newChanges, such that we end up with 1016 // only one change per trigger change in the result; the most recent change wins. 1017 func (w *secretBackendRotateWatcher) mergeChanges(current, newChanges []watcher.SecretBackendRotateChange) []watcher.SecretBackendRotateChange { 1018 chMap := make(map[string]watcher.SecretBackendRotateChange) 1019 for _, c := range current { 1020 chMap[c.ID] = c 1021 } 1022 for _, c := range newChanges { 1023 chMap[c.ID] = c 1024 } 1025 result := make([]watcher.SecretBackendRotateChange, len(chMap)) 1026 i := 0 1027 for _, c := range chMap { 1028 result[i] = c 1029 i++ 1030 } 1031 return result 1032 } 1033 1034 func (w *secretBackendRotateWatcher) loop(initialChanges []params.SecretBackendRotateChange) error { 1035 w.call = makeWatcherAPICaller(w.caller, "SecretBackendsRotateWatcher", w.watcherId) 1036 w.commonWatcher.init() 1037 go w.commonLoop() 1038 1039 copyChanges := func(changes []params.SecretBackendRotateChange) []watcher.SecretBackendRotateChange { 1040 result := make([]watcher.SecretBackendRotateChange, len(changes)) 1041 for i, ch := range changes { 1042 result[i] = watcher.SecretBackendRotateChange{ 1043 ID: ch.ID, 1044 Name: ch.Name, 1045 NextTriggerTime: ch.NextTriggerTime, 1046 } 1047 } 1048 return result 1049 } 1050 out := w.out 1051 changes := copyChanges(initialChanges) 1052 for { 1053 select { 1054 case <-w.tomb.Dying(): 1055 return tomb.ErrDying 1056 // Read the next change. 1057 case data, ok := <-w.in: 1058 if !ok { 1059 // The tomb is already killed with the correct error 1060 // at this point, so just return. 1061 return nil 1062 } 1063 newChanges := copyChanges(data.(*params.SecretBackendRotateWatchResult).Changes) 1064 changes = w.mergeChanges(changes, newChanges) 1065 out = w.out 1066 case out <- changes: 1067 out = nil 1068 changes = nil 1069 } 1070 } 1071 } 1072 1073 // Changes returns a channel that will receive the changes to 1074 // a secret trigger. The first event reflects the current 1075 // values of these attributes. 1076 func (w *secretBackendRotateWatcher) Changes() watcher.SecretBackendRotateChannel { 1077 return w.out 1078 } 1079 1080 // SecretsRevisionWatcher will send notifications of changes to secret revisions. 1081 type SecretsRevisionWatcher struct { 1082 commonWatcher 1083 caller base.APICaller 1084 watcherId string 1085 out chan []watcher.SecretRevisionChange 1086 } 1087 1088 // NewSecretsRevisionWatcher returns a watcher notifying of changes to 1089 // secret revisions. 1090 func NewSecretsRevisionWatcher( 1091 caller base.APICaller, result params.SecretRevisionWatchResult, 1092 ) watcher.SecretsRevisionWatcher { 1093 w := &SecretsRevisionWatcher{ 1094 caller: caller, 1095 watcherId: result.WatcherId, 1096 out: make(chan []watcher.SecretRevisionChange), 1097 } 1098 w.tomb.Go(func() error { 1099 return w.loop(result.Changes) 1100 }) 1101 return w 1102 } 1103 1104 // mergeChanges combines the revision in current and new, such that we end up with 1105 // only one change per secret in the result; the most recent change wins. 1106 func (w *SecretsRevisionWatcher) mergeChanges(current, new []watcher.SecretRevisionChange) []watcher.SecretRevisionChange { 1107 chMap := make(map[string]watcher.SecretRevisionChange) 1108 for _, c := range current { 1109 chMap[c.URI.ID] = c 1110 } 1111 for _, c := range new { 1112 chMap[c.URI.ID] = c 1113 } 1114 var result []watcher.SecretRevisionChange 1115 for _, c := range chMap { 1116 result = append(result, c) 1117 } 1118 return result 1119 } 1120 1121 func (w *SecretsRevisionWatcher) loop(initialChanges []params.SecretRevisionChange) error { 1122 w.newResult = func() interface{} { return new(params.SecretRevisionWatchResult) } 1123 w.call = makeWatcherAPICaller(w.caller, "SecretsRevisionWatcher", w.watcherId) 1124 w.commonWatcher.init() 1125 go w.commonLoop() 1126 1127 copyChanges := func(changes []params.SecretRevisionChange) []watcher.SecretRevisionChange { 1128 var result []watcher.SecretRevisionChange 1129 for _, ch := range changes { 1130 uri, err := secrets.ParseURI(ch.URI) 1131 if err != nil { 1132 logger.Warningf("invalid secret URI: %v", ch.URI) 1133 continue 1134 } 1135 result = append(result, watcher.SecretRevisionChange{ 1136 URI: uri, 1137 Revision: ch.Revision, 1138 }) 1139 } 1140 return result 1141 } 1142 out := w.out 1143 changes := copyChanges(initialChanges) 1144 for { 1145 select { 1146 case <-w.tomb.Dying(): 1147 return tomb.ErrDying 1148 // Read the next change. 1149 case data, ok := <-w.in: 1150 if !ok { 1151 // The tomb is already killed with the correct error 1152 // at this point, so just return. 1153 return nil 1154 } 1155 newChanges := copyChanges(data.(*params.SecretRevisionWatchResult).Changes) 1156 changes = w.mergeChanges(changes, newChanges) 1157 out = w.out 1158 case out <- changes: 1159 out = nil 1160 changes = nil 1161 } 1162 } 1163 } 1164 1165 // Changes returns a channel that will receive the changes to 1166 // a secret revision. The first event reflects the current 1167 // values of these attributes. 1168 func (w *SecretsRevisionWatcher) Changes() watcher.SecretRevisionChannel { 1169 return w.out 1170 }