github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/remotestate/watcher.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package remotestate 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "gopkg.in/juju/names.v2" 13 "gopkg.in/juju/worker.v1" 14 "gopkg.in/juju/worker.v1/catacomb" 15 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/core/leadership" 18 "github.com/juju/juju/core/model" 19 "github.com/juju/juju/core/watcher" 20 jworker "github.com/juju/juju/worker" 21 ) 22 23 var logger = loggo.GetLogger("juju.worker.uniter.remotestate") 24 25 // RemoteStateWatcher collects unit, application, and application config information 26 // from separate state watchers, and updates a Snapshot which is sent on a 27 // channel upon change. 28 type RemoteStateWatcher struct { 29 st State 30 unit Unit 31 application Application 32 modelType model.ModelType 33 relations map[names.RelationTag]*relationUnitsWatcher 34 relationUnitsChanges chan relationUnitsChange 35 storageAttachmentWatchers map[names.StorageTag]*storageAttachmentWatcher 36 storageAttachmentChanges chan storageAttachmentChange 37 leadershipTracker leadership.Tracker 38 updateStatusChannel UpdateStatusTimerFunc 39 commandChannel <-chan string 40 retryHookChannel watcher.NotifyChannel 41 applicationChannel watcher.NotifyChannel 42 43 catacomb catacomb.Catacomb 44 45 out chan struct{} 46 mu sync.Mutex 47 current Snapshot 48 } 49 50 // WatcherConfig holds configuration parameters for the 51 // remote state watcher. 52 type WatcherConfig struct { 53 State State 54 LeadershipTracker leadership.Tracker 55 UpdateStatusChannel UpdateStatusTimerFunc 56 CommandChannel <-chan string 57 RetryHookChannel watcher.NotifyChannel 58 ApplicationChannel watcher.NotifyChannel 59 UnitTag names.UnitTag 60 ModelType model.ModelType 61 } 62 63 func (w WatcherConfig) validate() error { 64 if w.ModelType == model.CAAS && w.ApplicationChannel == nil { 65 return errors.NotValidf("watcher config for CAAS model with nil application channel") 66 } 67 return nil 68 } 69 70 // NewWatcher returns a RemoteStateWatcher that handles state changes pertaining to the 71 // supplied unit. 72 func NewWatcher(config WatcherConfig) (*RemoteStateWatcher, error) { 73 if err := config.validate(); err != nil { 74 return nil, errors.Trace(err) 75 } 76 w := &RemoteStateWatcher{ 77 st: config.State, 78 relations: make(map[names.RelationTag]*relationUnitsWatcher), 79 relationUnitsChanges: make(chan relationUnitsChange), 80 storageAttachmentWatchers: make(map[names.StorageTag]*storageAttachmentWatcher), 81 storageAttachmentChanges: make(chan storageAttachmentChange), 82 leadershipTracker: config.LeadershipTracker, 83 updateStatusChannel: config.UpdateStatusChannel, 84 commandChannel: config.CommandChannel, 85 retryHookChannel: config.RetryHookChannel, 86 applicationChannel: config.ApplicationChannel, 87 modelType: config.ModelType, 88 // Note: it is important that the out channel be buffered! 89 // The remote state watcher will perform a non-blocking send 90 // on the channel to wake up the observer. It is non-blocking 91 // so that we coalesce events while the observer is busy. 92 out: make(chan struct{}, 1), 93 current: Snapshot{ 94 Relations: make(map[int]RelationSnapshot), 95 Storage: make(map[names.StorageTag]StorageSnapshot), 96 }, 97 } 98 err := catacomb.Invoke(catacomb.Plan{ 99 Site: &w.catacomb, 100 Work: func() error { 101 return w.loop(config.UnitTag) 102 }, 103 }) 104 if err != nil { 105 return nil, errors.Trace(err) 106 } 107 return w, nil 108 } 109 110 // Kill is part of the worker.Worker interface. 111 func (w *RemoteStateWatcher) Kill() { 112 w.catacomb.Kill(nil) 113 } 114 115 // Wait is part of the worker.Worker interface. 116 func (w *RemoteStateWatcher) Wait() error { 117 return w.catacomb.Wait() 118 } 119 120 func (w *RemoteStateWatcher) RemoteStateChanged() <-chan struct{} { 121 return w.out 122 } 123 124 func (w *RemoteStateWatcher) Snapshot() Snapshot { 125 w.mu.Lock() 126 defer w.mu.Unlock() 127 snapshot := w.current 128 snapshot.Relations = make(map[int]RelationSnapshot) 129 for id, relationSnapshot := range w.current.Relations { 130 relationSnapshotCopy := RelationSnapshot{ 131 Life: relationSnapshot.Life, 132 Suspended: relationSnapshot.Suspended, 133 Members: make(map[string]int64), 134 } 135 for name, version := range relationSnapshot.Members { 136 relationSnapshotCopy.Members[name] = version 137 } 138 snapshot.Relations[id] = relationSnapshotCopy 139 } 140 snapshot.Storage = make(map[names.StorageTag]StorageSnapshot) 141 for tag, storageSnapshot := range w.current.Storage { 142 snapshot.Storage[tag] = storageSnapshot 143 } 144 snapshot.Actions = make([]string, len(w.current.Actions)) 145 copy(snapshot.Actions, w.current.Actions) 146 snapshot.Commands = make([]string, len(w.current.Commands)) 147 copy(snapshot.Commands, w.current.Commands) 148 return snapshot 149 } 150 151 func (w *RemoteStateWatcher) ClearResolvedMode() { 152 w.mu.Lock() 153 w.current.ResolvedMode = params.ResolvedNone 154 w.mu.Unlock() 155 } 156 157 func (w *RemoteStateWatcher) CommandCompleted(completed string) { 158 w.mu.Lock() 159 defer w.mu.Unlock() 160 for i, id := range w.current.Commands { 161 if id != completed { 162 continue 163 } 164 w.current.Commands = append( 165 w.current.Commands[:i], 166 w.current.Commands[i+1:]..., 167 ) 168 break 169 } 170 } 171 172 func (w *RemoteStateWatcher) setUp(unitTag names.UnitTag) error { 173 // TODO(axw) move this logic 174 var err error 175 defer func() { 176 cause := errors.Cause(err) 177 if params.IsCodeNotFoundOrCodeUnauthorized(cause) { 178 // We only want to terminate the agent for IAAS models. 179 if w.modelType == model.IAAS { 180 err = jworker.ErrTerminateAgent 181 } 182 } 183 }() 184 if w.unit, err = w.st.Unit(unitTag); err != nil { 185 return errors.Trace(err) 186 } 187 w.application, err = w.unit.Application() 188 if err != nil { 189 return errors.Trace(err) 190 } 191 return nil 192 } 193 194 func (w *RemoteStateWatcher) loop(unitTag names.UnitTag) (err error) { 195 if err := w.setUp(unitTag); err != nil { 196 return errors.Trace(err) 197 } 198 199 var requiredEvents int 200 201 var seenUnitChange bool 202 unitw, err := w.unit.Watch() 203 if err != nil { 204 return errors.Trace(err) 205 } 206 if err := w.catacomb.Add(unitw); err != nil { 207 return errors.Trace(err) 208 } 209 requiredEvents++ 210 211 var seenConfigChange bool 212 charmConfigw, err := w.unit.WatchConfigSettingsHash() 213 if err != nil { 214 return errors.Trace(err) 215 } 216 217 if err := w.catacomb.Add(charmConfigw); err != nil { 218 return errors.Trace(err) 219 } 220 requiredEvents++ 221 222 var seenTrustConfigChange bool 223 trustConfigw, err := w.unit.WatchTrustConfigSettingsHash() 224 if err != nil { 225 return errors.Trace(err) 226 } 227 if err := w.catacomb.Add(trustConfigw); err != nil { 228 return errors.Trace(err) 229 } 230 requiredEvents++ 231 232 var seenRelationsChange bool 233 relationsw, err := w.unit.WatchRelations() 234 if err != nil { 235 return errors.Trace(err) 236 } 237 if err := w.catacomb.Add(relationsw); err != nil { 238 return errors.Trace(err) 239 } 240 requiredEvents++ 241 242 var seenAddressesChange bool 243 addressesw, err := w.unit.WatchAddressesHash() 244 if err != nil { 245 return errors.Trace(err) 246 } 247 addressesChanges := addressesw.Changes() 248 if err := w.catacomb.Add(addressesw); err != nil { 249 return errors.Trace(err) 250 } 251 requiredEvents++ 252 253 var ( 254 seenApplicationChange bool 255 256 seenUpgradeSeriesChange bool 257 upgradeSeriesChanges watcher.NotifyChannel 258 259 seenLXDProfileChange bool 260 lxdProfileChanges watcher.StringsChannel 261 ) 262 263 // CAAS models don't use an application watcher 264 // which fires an initial event. 265 if w.modelType == model.CAAS { 266 seenApplicationChange = true 267 } 268 269 if w.modelType == model.IAAS { 270 // This is in IAAS model so we need to watch state for application 271 // charm changes instead of being informed by the operator. 272 applicationw, err := w.application.Watch() 273 if err != nil { 274 return errors.Trace(err) 275 } 276 if err := w.catacomb.Add(applicationw); err != nil { 277 return errors.Trace(err) 278 } 279 w.applicationChannel = applicationw.Changes() 280 requiredEvents++ 281 282 // Only IAAS models support upgrading the machine series. 283 // TODO(externalreality) This pattern should probably be extracted 284 upgradeSeriesw, err := w.unit.WatchUpgradeSeriesNotifications() 285 if err != nil { 286 return errors.Trace(err) 287 } 288 if err := w.catacomb.Add(upgradeSeriesw); err != nil { 289 return errors.Trace(err) 290 } 291 upgradeSeriesChanges = upgradeSeriesw.Changes() 292 requiredEvents++ 293 294 lxdProfilew, err := w.unit.WatchLXDProfileUpgradeNotifications() 295 if err != nil { 296 return errors.Trace(err) 297 } 298 if err := w.catacomb.Add(lxdProfilew); err != nil { 299 return errors.Trace(err) 300 } 301 lxdProfileChanges = lxdProfilew.Changes() 302 requiredEvents++ 303 } 304 305 var seenStorageChange bool 306 storagew, err := w.unit.WatchStorage() 307 if err != nil { 308 return errors.Trace(err) 309 } 310 if err := w.catacomb.Add(storagew); err != nil { 311 return errors.Trace(err) 312 } 313 requiredEvents++ 314 315 var seenLeaderSettingsChange bool 316 leaderSettingsw, err := w.application.WatchLeadershipSettings() 317 if err != nil { 318 return errors.Trace(err) 319 } 320 if err := w.catacomb.Add(leaderSettingsw); err != nil { 321 return errors.Trace(err) 322 } 323 requiredEvents++ 324 325 var seenActionsChange bool 326 actionsw, err := w.unit.WatchActionNotifications() 327 if err != nil { 328 return errors.Trace(err) 329 } 330 if err := w.catacomb.Add(actionsw); err != nil { 331 return errors.Trace(err) 332 } 333 requiredEvents++ 334 335 var seenUpdateStatusIntervalChange bool 336 updateStatusIntervalw, err := w.st.WatchUpdateStatusHookInterval() 337 if err != nil { 338 return errors.Trace(err) 339 } 340 if err := w.catacomb.Add(updateStatusIntervalw); err != nil { 341 return errors.Trace(err) 342 } 343 requiredEvents++ 344 345 var seenLeadershipChange bool 346 // There's no watcher for this per se; we wait on a channel 347 // returned by the leadership tracker. 348 requiredEvents++ 349 350 var eventsObserved int 351 observedEvent := func(flag *bool) { 352 if flag != nil && !*flag { 353 *flag = true 354 eventsObserved++ 355 } 356 } 357 358 // fire will, once the first event for each watcher has 359 // been observed, send a signal on the out channel. 360 fire := func() { 361 if eventsObserved != requiredEvents { 362 return 363 } 364 select { 365 case w.out <- struct{}{}: 366 default: 367 } 368 } 369 370 // Check the initial leadership status, and then we can flip-flop 371 // waiting on leader or minion to trigger the changed event. 372 var waitLeader, waitMinion <-chan struct{} 373 claimLeader := w.leadershipTracker.ClaimLeader() 374 select { 375 case <-w.catacomb.Dying(): 376 return w.catacomb.ErrDying() 377 case <-claimLeader.Ready(): 378 isLeader := claimLeader.Wait() 379 w.leadershipChanged(isLeader) 380 if isLeader { 381 waitMinion = w.leadershipTracker.WaitMinion().Ready() 382 } else { 383 waitLeader = w.leadershipTracker.WaitLeader().Ready() 384 } 385 observedEvent(&seenLeadershipChange) 386 } 387 388 var updateStatusInterval time.Duration 389 var updateStatusTimer <-chan time.Time 390 resetUpdateStatusTimer := func() { 391 updateStatusTimer = w.updateStatusChannel(updateStatusInterval).After() 392 } 393 394 for { 395 select { 396 case <-w.catacomb.Dying(): 397 return w.catacomb.ErrDying() 398 399 case _, ok := <-unitw.Changes(): 400 logger.Debugf("got unit change") 401 if !ok { 402 return errors.New("unit watcher closed") 403 } 404 if err := w.unitChanged(); err != nil { 405 return errors.Trace(err) 406 } 407 observedEvent(&seenUnitChange) 408 409 case _, ok := <-w.applicationChannel: 410 logger.Debugf("got application change") 411 if !ok { 412 return errors.New("application watcher closed") 413 } 414 if err := w.applicationChanged(); err != nil { 415 return errors.Trace(err) 416 } 417 observedEvent(&seenApplicationChange) 418 419 case hashes, ok := <-charmConfigw.Changes(): 420 logger.Debugf("got config change: ok=%t, hashes=%v", ok, hashes) 421 if !ok { 422 return errors.New("config watcher closed") 423 } 424 if len(hashes) != 1 { 425 return errors.New("expected one hash in config change") 426 } 427 w.configHashChanged(hashes[0]) 428 observedEvent(&seenConfigChange) 429 430 case hashes, ok := <-trustConfigw.Changes(): 431 logger.Debugf("got trust config change: ok=%t, hashes=%v", ok, hashes) 432 if !ok { 433 return errors.New("trust config watcher closed") 434 } 435 if len(hashes) != 1 { 436 return errors.New("expected one hash in trust config change") 437 } 438 w.trustHashChanged(hashes[0]) 439 observedEvent(&seenTrustConfigChange) 440 441 case _, ok := <-upgradeSeriesChanges: 442 logger.Debugf("got upgrade series change") 443 if !ok { 444 return errors.New("upgrades series watcher closed") 445 } 446 if err := w.upgradeSeriesStatusChanged(); err != nil { 447 return errors.Trace(err) 448 } 449 observedEvent(&seenUpgradeSeriesChange) 450 451 case changes, ok := <-lxdProfileChanges: 452 logger.Debugf("got lxd profile change") 453 if !ok { 454 return errors.New("lxd profile watcher closed") 455 } 456 if len(changes) != 1 { 457 return errors.New("expected one change in lxd profile watcher") 458 } 459 if err := w.lxdProfileStatusChanged(changes[0]); err != nil { 460 return errors.Trace(err) 461 } 462 observedEvent(&seenLXDProfileChange) 463 464 case hashes, ok := <-addressesChanges: 465 logger.Debugf("got address change: ok=%t, hashes=%v", ok, hashes) 466 if !ok { 467 return errors.New("addresses watcher closed") 468 } 469 if len(hashes) != 1 { 470 return errors.New("expected one hash in addresses change") 471 } 472 w.addressesHashChanged(hashes[0]) 473 observedEvent(&seenAddressesChange) 474 475 case _, ok := <-leaderSettingsw.Changes(): 476 logger.Debugf("got leader settings change: ok=%t", ok) 477 if !ok { 478 return errors.New("leader settings watcher closed") 479 } 480 if err := w.leaderSettingsChanged(); err != nil { 481 return errors.Trace(err) 482 } 483 observedEvent(&seenLeaderSettingsChange) 484 485 case actions, ok := <-actionsw.Changes(): 486 logger.Debugf("got action change: %v ok=%t", actions, ok) 487 if !ok { 488 return errors.New("actions watcher closed") 489 } 490 w.actionsChanged(actions) 491 observedEvent(&seenActionsChange) 492 493 case keys, ok := <-relationsw.Changes(): 494 logger.Debugf("got relations change: ok=%t", ok) 495 if !ok { 496 return errors.New("relations watcher closed") 497 } 498 if err := w.relationsChanged(keys); err != nil { 499 return errors.Trace(err) 500 } 501 observedEvent(&seenRelationsChange) 502 503 case keys, ok := <-storagew.Changes(): 504 logger.Debugf("got storage change: %v ok=%t", keys, ok) 505 if !ok { 506 return errors.New("storage watcher closed") 507 } 508 if err := w.storageChanged(keys); err != nil { 509 return errors.Trace(err) 510 } 511 observedEvent(&seenStorageChange) 512 513 case _, ok := <-updateStatusIntervalw.Changes(): 514 logger.Debugf("got update status interval change: ok=%t", ok) 515 if !ok { 516 return errors.New("update status interval watcher closed") 517 } 518 observedEvent(&seenUpdateStatusIntervalChange) 519 520 var err error 521 updateStatusInterval, err = w.st.UpdateStatusHookInterval() 522 if err != nil { 523 return errors.Trace(err) 524 } 525 wasActive := updateStatusTimer != nil 526 resetUpdateStatusTimer() 527 if wasActive { 528 // This is not the first time we've seen an update 529 // status interval change, so there's no need to 530 // fall out and fire an initial change event. 531 continue 532 } 533 534 case <-waitMinion: 535 logger.Debugf("got leadership change for %v: minion", unitTag.Id()) 536 w.leadershipChanged(false) 537 waitMinion = nil 538 waitLeader = w.leadershipTracker.WaitLeader().Ready() 539 540 case <-waitLeader: 541 logger.Debugf("got leadership change for %v: leader", unitTag.Id()) 542 w.leadershipChanged(true) 543 waitLeader = nil 544 waitMinion = w.leadershipTracker.WaitMinion().Ready() 545 546 case change := <-w.storageAttachmentChanges: 547 logger.Debugf("storage attachment change %v", change) 548 w.storageAttachmentChanged(change) 549 550 case change := <-w.relationUnitsChanges: 551 logger.Debugf("got a relation units change: %v", change) 552 if err := w.relationUnitsChanged(change); err != nil { 553 return errors.Trace(err) 554 } 555 556 case <-updateStatusTimer: 557 logger.Debugf("update status timer triggered") 558 w.updateStatusChanged() 559 resetUpdateStatusTimer() 560 561 case id, ok := <-w.commandChannel: 562 if !ok { 563 return errors.New("commandChannel closed") 564 } 565 logger.Debugf("command enqueued: %v", id) 566 w.commandsChanged(id) 567 568 case _, ok := <-w.retryHookChannel: 569 if !ok { 570 return errors.New("retryHookChannel closed") 571 } 572 logger.Debugf("retry hook timer triggered") 573 w.retryHookTimerTriggered() 574 } 575 576 // Something changed. 577 fire() 578 } 579 } 580 581 // upgradeSeriesStatusChanged is called when the remote status of a series 582 // upgrade changes. 583 func (w *RemoteStateWatcher) upgradeSeriesStatusChanged() error { 584 w.mu.Lock() 585 defer w.mu.Unlock() 586 587 status, err := w.upgradeSeriesStatus() 588 if errors.IsNotFound(err) { 589 // There is no remote state so no upgrade is started. 590 logger.Debugf("no upgrade series in progress, reinitializing local upgrade series state") 591 w.current.UpgradeSeriesStatus = model.UpgradeSeriesNotStarted 592 return nil 593 } 594 if err != nil { 595 return err 596 } 597 w.current.UpgradeSeriesStatus = status 598 return nil 599 } 600 601 func (w *RemoteStateWatcher) upgradeSeriesStatus() (model.UpgradeSeriesStatus, error) { 602 rawStatus, err := w.unit.UpgradeSeriesStatus() 603 if err != nil { 604 return "", err 605 } 606 status, err := model.ValidateUpgradeSeriesStatus(rawStatus) 607 if err != nil { 608 return "", err 609 } 610 return status, nil 611 } 612 613 // updateStatusChanged is called when the update status timer expires. 614 func (w *RemoteStateWatcher) updateStatusChanged() { 615 w.mu.Lock() 616 w.current.UpdateStatusVersion++ 617 w.mu.Unlock() 618 } 619 620 func (w *RemoteStateWatcher) lxdProfileStatusChanged(status string) error { 621 w.mu.Lock() 622 defer w.mu.Unlock() 623 624 w.current.UpgradeCharmProfileStatus = status 625 return nil 626 } 627 628 // commandsChanged is called when a command is enqueued. 629 func (w *RemoteStateWatcher) commandsChanged(id string) { 630 w.mu.Lock() 631 w.current.Commands = append(w.current.Commands, id) 632 w.mu.Unlock() 633 } 634 635 // retryHookTimerTriggered is called when the retry hook timer expires. 636 func (w *RemoteStateWatcher) retryHookTimerTriggered() { 637 w.mu.Lock() 638 w.current.RetryHookVersion++ 639 w.mu.Unlock() 640 } 641 642 // unitChanged responds to changes in the unit. 643 func (w *RemoteStateWatcher) unitChanged() error { 644 if err := w.unit.Refresh(); err != nil { 645 return errors.Trace(err) 646 } 647 w.mu.Lock() 648 defer w.mu.Unlock() 649 w.current.Life = w.unit.Life() 650 w.current.ResolvedMode = w.unit.Resolved() 651 return nil 652 } 653 654 // applicationChanged responds to changes in the application. 655 func (w *RemoteStateWatcher) applicationChanged() error { 656 if err := w.application.Refresh(); err != nil { 657 return errors.Trace(err) 658 } 659 url, force, err := w.application.CharmURL() 660 if err != nil { 661 return errors.Trace(err) 662 } 663 ver, err := w.application.CharmModifiedVersion() 664 if err != nil { 665 return errors.Trace(err) 666 } 667 w.mu.Lock() 668 w.current.CharmURL = url 669 w.current.ForceCharmUpgrade = force 670 w.current.CharmModifiedVersion = ver 671 w.mu.Unlock() 672 return nil 673 } 674 675 func (w *RemoteStateWatcher) configHashChanged(value string) { 676 w.mu.Lock() 677 w.current.ConfigHash = value 678 w.mu.Unlock() 679 } 680 681 func (w *RemoteStateWatcher) trustHashChanged(value string) { 682 w.mu.Lock() 683 w.current.TrustHash = value 684 w.mu.Unlock() 685 } 686 687 func (w *RemoteStateWatcher) addressesHashChanged(value string) { 688 w.mu.Lock() 689 w.current.AddressesHash = value 690 w.mu.Unlock() 691 } 692 693 func (w *RemoteStateWatcher) leaderSettingsChanged() error { 694 w.mu.Lock() 695 w.current.LeaderSettingsVersion++ 696 w.mu.Unlock() 697 return nil 698 } 699 700 func (w *RemoteStateWatcher) leadershipChanged(isLeader bool) { 701 w.mu.Lock() 702 w.current.Leader = isLeader 703 w.mu.Unlock() 704 } 705 706 // relationsChanged responds to application relation changes. 707 func (w *RemoteStateWatcher) relationsChanged(keys []string) error { 708 w.mu.Lock() 709 defer w.mu.Unlock() 710 for _, key := range keys { 711 relationTag := names.NewRelationTag(key) 712 rel, err := w.st.Relation(relationTag) 713 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 714 // If it's actually gone, this unit cannot have entered 715 // scope, and therefore never needs to know about it. 716 if ruw, ok := w.relations[relationTag]; ok { 717 worker.Stop(ruw) 718 delete(w.relations, relationTag) 719 delete(w.current.Relations, ruw.relationId) 720 } 721 } else if err != nil { 722 return errors.Trace(err) 723 } else { 724 if _, ok := w.relations[relationTag]; ok { 725 relationSnapshot := w.current.Relations[rel.Id()] 726 relationSnapshot.Life = rel.Life() 727 relationSnapshot.Suspended = rel.Suspended() 728 w.current.Relations[rel.Id()] = relationSnapshot 729 if rel.Suspended() { 730 // Relation has been suspended, so stop the listeners here. 731 // The relation itself is retained in the current relations 732 // in the suspended state so that departed/broken hooks can run. 733 if ruw, ok := w.relations[relationTag]; ok { 734 worker.Stop(ruw) 735 delete(w.relations, relationTag) 736 } 737 } 738 continue 739 } 740 // If the relation is suspended, we don't need to watch it. 741 if rel.Suspended() { 742 continue 743 } 744 ruw, err := w.st.WatchRelationUnits(relationTag, w.unit.Tag()) 745 if err != nil { 746 return errors.Trace(err) 747 } 748 // Because of the delay before handing off responsibility to 749 // newRelationUnitsWatcher below, add to our own catacomb to 750 // ensure errors get picked up if they happen. 751 if err := w.catacomb.Add(ruw); err != nil { 752 return errors.Trace(err) 753 } 754 if err := w.watchRelationUnits(rel, relationTag, ruw); err != nil { 755 return errors.Trace(err) 756 } 757 } 758 } 759 return nil 760 } 761 762 // watchRelationUnits starts watching the relation units for the given 763 // relation, waits for its first event, and records the information in 764 // the current snapshot. 765 func (w *RemoteStateWatcher) watchRelationUnits( 766 rel Relation, relationTag names.RelationTag, ruw watcher.RelationUnitsWatcher, 767 ) error { 768 relationSnapshot := RelationSnapshot{ 769 Life: rel.Life(), 770 Suspended: rel.Suspended(), 771 Members: make(map[string]int64), 772 } 773 select { 774 case <-w.catacomb.Dying(): 775 return w.catacomb.ErrDying() 776 case change, ok := <-ruw.Changes(): 777 if !ok { 778 return errors.New("relation units watcher closed") 779 } 780 for unit, settings := range change.Changed { 781 relationSnapshot.Members[unit] = settings.Version 782 } 783 } 784 innerRUW, err := newRelationUnitsWatcher(rel.Id(), ruw, w.relationUnitsChanges) 785 if err != nil { 786 return errors.Trace(err) 787 } 788 if err := w.catacomb.Add(innerRUW); err != nil { 789 return errors.Trace(err) 790 } 791 w.current.Relations[rel.Id()] = relationSnapshot 792 w.relations[relationTag] = innerRUW 793 return nil 794 } 795 796 // relationUnitsChanged responds to relation units changes. 797 func (w *RemoteStateWatcher) relationUnitsChanged(change relationUnitsChange) error { 798 w.mu.Lock() 799 defer w.mu.Unlock() 800 snapshot, ok := w.current.Relations[change.relationId] 801 if !ok { 802 return nil 803 } 804 for unit, settings := range change.Changed { 805 snapshot.Members[unit] = settings.Version 806 } 807 for _, unit := range change.Departed { 808 delete(snapshot.Members, unit) 809 } 810 return nil 811 } 812 813 // storageAttachmentChanged responds to storage attachment changes. 814 func (w *RemoteStateWatcher) storageAttachmentChanged(change storageAttachmentChange) { 815 w.mu.Lock() 816 w.current.Storage[change.Tag] = change.Snapshot 817 w.mu.Unlock() 818 } 819 820 func (w *RemoteStateWatcher) actionsChanged(actions []string) { 821 w.mu.Lock() 822 defer w.mu.Unlock() 823 w.current.Actions = append(w.current.Actions, actions...) 824 } 825 826 // storageChanged responds to unit storage changes. 827 func (w *RemoteStateWatcher) storageChanged(keys []string) error { 828 tags := make([]names.StorageTag, len(keys)) 829 for i, key := range keys { 830 tags[i] = names.NewStorageTag(key) 831 } 832 ids := make([]params.StorageAttachmentId, len(keys)) 833 for i, tag := range tags { 834 ids[i] = params.StorageAttachmentId{ 835 StorageTag: tag.String(), 836 UnitTag: w.unit.Tag().String(), 837 } 838 } 839 results, err := w.st.StorageAttachmentLife(ids) 840 if err != nil { 841 return errors.Trace(err) 842 } 843 844 w.mu.Lock() 845 defer w.mu.Unlock() 846 847 for i, result := range results { 848 tag := tags[i] 849 if result.Error == nil { 850 if storageSnapshot, ok := w.current.Storage[tag]; ok { 851 // We've previously started a watcher for this storage 852 // attachment, so all we needed to do was update the 853 // lifecycle state. 854 storageSnapshot.Life = result.Life 855 w.current.Storage[tag] = storageSnapshot 856 continue 857 } 858 // We haven't seen this storage attachment before, so start 859 // a watcher now; add it to our catacomb in case of mishap; 860 // and wait for the initial event. 861 saw, err := w.st.WatchStorageAttachment(tag, w.unit.Tag()) 862 if err != nil { 863 return errors.Annotate(err, "watching storage attachment") 864 } 865 if err := w.catacomb.Add(saw); err != nil { 866 return errors.Trace(err) 867 } 868 if err := w.watchStorageAttachment(tag, result.Life, saw); err != nil { 869 return errors.Trace(err) 870 } 871 } else if params.IsCodeNotFound(result.Error) { 872 if watcher, ok := w.storageAttachmentWatchers[tag]; ok { 873 // already under catacomb management, any error tracked already 874 worker.Stop(watcher) 875 delete(w.storageAttachmentWatchers, tag) 876 } 877 delete(w.current.Storage, tag) 878 } else { 879 return errors.Annotatef( 880 result.Error, "getting life of %s attachment", 881 names.ReadableString(tag), 882 ) 883 } 884 } 885 return nil 886 } 887 888 // watchStorageAttachment starts watching the storage attachment with 889 // the specified storage tag, waits for its first event, and records 890 // the information in the current snapshot. 891 func (w *RemoteStateWatcher) watchStorageAttachment( 892 tag names.StorageTag, 893 life params.Life, 894 saw watcher.NotifyWatcher, 895 ) error { 896 var storageSnapshot StorageSnapshot 897 select { 898 case <-w.catacomb.Dying(): 899 return w.catacomb.ErrDying() 900 case _, ok := <-saw.Changes(): 901 if !ok { 902 return errors.New("storage attachment watcher closed") 903 } 904 var err error 905 storageSnapshot, err = getStorageSnapshot(w.st, tag, w.unit.Tag()) 906 if params.IsCodeNotProvisioned(err) { 907 // If the storage is unprovisioned, we still want to 908 // record the attachment, but we'll mark it as 909 // unattached. This allows the uniter to wait for 910 // pending storage attachments to be provisioned. 911 storageSnapshot = StorageSnapshot{Life: life} 912 } else if err != nil { 913 return errors.Annotatef(err, "processing initial storage attachment change") 914 } 915 } 916 innerSAW, err := newStorageAttachmentWatcher( 917 w.st, saw, w.unit.Tag(), tag, w.storageAttachmentChanges, 918 ) 919 if err != nil { 920 return errors.Trace(err) 921 } 922 w.current.Storage[tag] = storageSnapshot 923 w.storageAttachmentWatchers[tag] = innerSAW 924 return nil 925 }