github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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 "fmt" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/juju/collections/set" 14 "github.com/juju/errors" 15 "github.com/juju/names/v5" 16 "github.com/juju/worker/v3" 17 "github.com/juju/worker/v3/catacomb" 18 19 "github.com/juju/juju/core/leadership" 20 "github.com/juju/juju/core/life" 21 "github.com/juju/juju/core/model" 22 "github.com/juju/juju/core/secrets" 23 "github.com/juju/juju/core/watcher" 24 "github.com/juju/juju/rpc/params" 25 jworker "github.com/juju/juju/worker" 26 ) 27 28 // Logger is here to stop the desire of creating a package level Logger. 29 // Don't do this, instead use the one in the RemoteStateWatcher. 30 type logger interface{} 31 32 var _ logger = struct{}{} 33 34 // Logger represents the logging methods used in this package. 35 type Logger interface { 36 Warningf(string, ...interface{}) 37 Debugf(string, ...interface{}) 38 Criticalf(string, ...interface{}) 39 } 40 41 // SecretTriggerWatcherFunc is a function returning a secrets trigger watcher. 42 type SecretTriggerWatcherFunc func(names.UnitTag, bool, chan []string) (worker.Worker, error) 43 44 // SecretsClient provides access to the secrets manager facade. 45 type SecretsClient interface { 46 WatchConsumedSecretsChanges(unitName string) (watcher.StringsWatcher, error) 47 GetConsumerSecretsRevisionInfo(string, []string) (map[string]secrets.SecretRevisionInfo, error) 48 WatchObsolete(ownerTags ...names.Tag) (watcher.StringsWatcher, error) 49 } 50 51 // RemoteStateWatcher collects unit, application, and application config information 52 // from separate state watchers, and updates a Snapshot which is sent on a 53 // channel upon change. 54 type RemoteStateWatcher struct { 55 st State 56 unit Unit 57 application Application 58 modelType model.ModelType 59 sidecar bool 60 enforcedCharmModifiedVersion int 61 logger Logger 62 63 relations map[names.RelationTag]*wrappedRelationUnitsWatcher 64 relationUnitsChanges chan relationUnitsChange 65 storageAttachmentWatchers map[names.StorageTag]*storageAttachmentWatcher 66 storageAttachmentChanges chan storageAttachmentChange 67 leadershipTracker leadership.Tracker 68 updateStatusChannel UpdateStatusTimerFunc 69 commandChannel <-chan string 70 retryHookChannel watcher.NotifyChannel 71 containerRunningStatusChannel watcher.NotifyChannel 72 containerRunningStatusFunc ContainerRunningStatusFunc 73 canApplyCharmProfile bool 74 workloadEventChannel <-chan string 75 shutdownChannel <-chan bool 76 77 secretsClient SecretsClient 78 79 secretRotateWatcherFunc SecretTriggerWatcherFunc 80 secretRotateWatcher worker.Worker 81 rotateSecretsChanges chan []string 82 83 secretExpiryWatcherFunc SecretTriggerWatcherFunc 84 secretExpiryWatcher worker.Worker 85 expireSecretsChanges chan []string 86 87 obsoleteRevisionWatcher worker.Worker 88 obsoleteRevisionChanges watcher.StringsChannel 89 90 catacomb catacomb.Catacomb 91 92 out chan struct{} 93 mu sync.Mutex 94 current Snapshot 95 } 96 97 // ContainerRunningStatus is used on CAAS models to upgrade charms/block actions. 98 type ContainerRunningStatus struct { 99 PodName string 100 Initialising bool 101 InitialisingTime time.Time 102 Running bool 103 } 104 105 // ContainerRunningStatusFunc is used by the RemoteStateWatcher in a CAAS 106 // model to determine if the unit is running and ready to execute actions. 107 type ContainerRunningStatusFunc func(providerID string) (*ContainerRunningStatus, error) 108 109 // WatcherConfig holds configuration parameters for the 110 // remote state watcher. 111 type WatcherConfig struct { 112 State State 113 LeadershipTracker leadership.Tracker 114 SecretRotateWatcherFunc SecretTriggerWatcherFunc 115 SecretExpiryWatcherFunc SecretTriggerWatcherFunc 116 SecretsClient SecretsClient 117 UpdateStatusChannel UpdateStatusTimerFunc 118 CommandChannel <-chan string 119 RetryHookChannel watcher.NotifyChannel 120 ContainerRunningStatusChannel watcher.NotifyChannel 121 ContainerRunningStatusFunc ContainerRunningStatusFunc 122 UnitTag names.UnitTag 123 ModelType model.ModelType 124 Sidecar bool 125 EnforcedCharmModifiedVersion int 126 Logger Logger 127 CanApplyCharmProfile bool 128 WorkloadEventChannel <-chan string 129 InitialWorkloadEventIDs []string 130 ShutdownChannel <-chan bool 131 } 132 133 func (w WatcherConfig) validate() error { 134 if w.ModelType == model.IAAS && w.Sidecar { 135 return errors.NewNotValid(nil, fmt.Sprintf("sidecar mode is only for %q model", model.CAAS)) 136 } 137 138 if w.ModelType == model.CAAS && !w.Sidecar { 139 if w.ContainerRunningStatusChannel != nil && 140 w.ContainerRunningStatusFunc == nil { 141 return errors.NotValidf("watcher config for CAAS model with nil container running status func") 142 } 143 } 144 if w.Logger == nil { 145 return errors.NotValidf("nil Logger") 146 } 147 return nil 148 } 149 150 // NewWatcher returns a RemoteStateWatcher that handles state changes pertaining to the 151 // supplied unit. 152 func NewWatcher(config WatcherConfig) (*RemoteStateWatcher, error) { 153 if err := config.validate(); err != nil { 154 return nil, errors.Trace(err) 155 } 156 w := &RemoteStateWatcher{ 157 st: config.State, 158 relations: make(map[names.RelationTag]*wrappedRelationUnitsWatcher), 159 relationUnitsChanges: make(chan relationUnitsChange), 160 storageAttachmentWatchers: make(map[names.StorageTag]*storageAttachmentWatcher), 161 storageAttachmentChanges: make(chan storageAttachmentChange), 162 leadershipTracker: config.LeadershipTracker, 163 secretRotateWatcherFunc: config.SecretRotateWatcherFunc, 164 secretExpiryWatcherFunc: config.SecretExpiryWatcherFunc, 165 secretsClient: config.SecretsClient, 166 updateStatusChannel: config.UpdateStatusChannel, 167 commandChannel: config.CommandChannel, 168 retryHookChannel: config.RetryHookChannel, 169 containerRunningStatusChannel: config.ContainerRunningStatusChannel, 170 containerRunningStatusFunc: config.ContainerRunningStatusFunc, 171 modelType: config.ModelType, 172 logger: config.Logger, 173 canApplyCharmProfile: config.CanApplyCharmProfile, 174 // Note: it is important that the out channel be buffered! 175 // The remote state watcher will perform a non-blocking send 176 // on the channel to wake up the observer. It is non-blocking 177 // so that we coalesce events while the observer is busy. 178 out: make(chan struct{}, 1), 179 current: Snapshot{ 180 Relations: make(map[int]RelationSnapshot), 181 Storage: make(map[names.StorageTag]StorageSnapshot), 182 ActionsBlocked: config.ContainerRunningStatusChannel != nil, 183 ActionChanged: make(map[string]int), 184 UpgradeMachineStatus: model.UpgradeSeriesNotStarted, 185 WorkloadEvents: config.InitialWorkloadEventIDs, 186 ConsumedSecretInfo: make(map[string]secrets.SecretRevisionInfo), 187 ObsoleteSecretRevisions: make(map[string][]int), 188 }, 189 sidecar: config.Sidecar, 190 enforcedCharmModifiedVersion: config.EnforcedCharmModifiedVersion, 191 workloadEventChannel: config.WorkloadEventChannel, 192 shutdownChannel: config.ShutdownChannel, 193 } 194 err := catacomb.Invoke(catacomb.Plan{ 195 Site: &w.catacomb, 196 Work: func() error { 197 return w.loop(config.UnitTag) 198 }, 199 }) 200 if err != nil { 201 return nil, errors.Trace(err) 202 } 203 return w, nil 204 } 205 206 // Kill is part of the worker.Worker interface. 207 func (w *RemoteStateWatcher) Kill() { 208 w.catacomb.Kill(nil) 209 } 210 211 // Wait is part of the worker.Worker interface. 212 func (w *RemoteStateWatcher) Wait() error { 213 return w.catacomb.Wait() 214 } 215 216 func (w *RemoteStateWatcher) RemoteStateChanged() <-chan struct{} { 217 return w.out 218 } 219 220 func (w *RemoteStateWatcher) Snapshot() Snapshot { 221 w.mu.Lock() 222 defer w.mu.Unlock() 223 snapshot := w.current 224 snapshot.Relations = make(map[int]RelationSnapshot) 225 for id, relationSnapshot := range w.current.Relations { 226 relationSnapshotCopy := RelationSnapshot{ 227 Life: relationSnapshot.Life, 228 Suspended: relationSnapshot.Suspended, 229 Members: make(map[string]int64), 230 ApplicationMembers: make(map[string]int64), 231 } 232 for name, version := range relationSnapshot.Members { 233 relationSnapshotCopy.Members[name] = version 234 } 235 for name, version := range relationSnapshot.ApplicationMembers { 236 relationSnapshotCopy.ApplicationMembers[name] = version 237 } 238 snapshot.Relations[id] = relationSnapshotCopy 239 } 240 snapshot.Storage = make(map[names.StorageTag]StorageSnapshot) 241 for tag, storageSnapshot := range w.current.Storage { 242 snapshot.Storage[tag] = storageSnapshot 243 } 244 snapshot.ActionsPending = make([]string, len(w.current.ActionsPending)) 245 copy(snapshot.ActionsPending, w.current.ActionsPending) 246 snapshot.Commands = make([]string, len(w.current.Commands)) 247 copy(snapshot.Commands, w.current.Commands) 248 snapshot.WorkloadEvents = make([]string, len(w.current.WorkloadEvents)) 249 copy(snapshot.WorkloadEvents, w.current.WorkloadEvents) 250 snapshot.ActionChanged = make(map[string]int) 251 for k, v := range w.current.ActionChanged { 252 snapshot.ActionChanged[k] = v 253 } 254 snapshot.SecretRotations = make([]string, len(w.current.SecretRotations)) 255 copy(snapshot.SecretRotations, w.current.SecretRotations) 256 snapshot.ConsumedSecretInfo = make(map[string]secrets.SecretRevisionInfo) 257 for u, r := range w.current.ConsumedSecretInfo { 258 snapshot.ConsumedSecretInfo[u] = r 259 } 260 snapshot.ObsoleteSecretRevisions = make(map[string][]int) 261 for u, r := range w.current.ObsoleteSecretRevisions { 262 rCopy := make([]int, len(r)) 263 copy(rCopy, r) 264 snapshot.ObsoleteSecretRevisions[u] = rCopy 265 } 266 snapshot.DeletedSecrets = make([]string, len(w.current.DeletedSecrets)) 267 copy(snapshot.DeletedSecrets, w.current.DeletedSecrets) 268 return snapshot 269 } 270 271 func (w *RemoteStateWatcher) ClearResolvedMode() { 272 w.mu.Lock() 273 w.current.ResolvedMode = params.ResolvedNone 274 w.mu.Unlock() 275 } 276 277 func (w *RemoteStateWatcher) CommandCompleted(completed string) { 278 w.mu.Lock() 279 defer w.mu.Unlock() 280 for i, id := range w.current.Commands { 281 if id != completed { 282 continue 283 } 284 w.current.Commands = append( 285 w.current.Commands[:i], 286 w.current.Commands[i+1:]..., 287 ) 288 break 289 } 290 } 291 292 func (w *RemoteStateWatcher) WorkloadEventCompleted(workloadEventID string) { 293 w.mu.Lock() 294 defer w.mu.Unlock() 295 for i, id := range w.current.WorkloadEvents { 296 if id != workloadEventID { 297 continue 298 } 299 w.current.WorkloadEvents = append( 300 w.current.WorkloadEvents[:i], 301 w.current.WorkloadEvents[i+1:]..., 302 ) 303 break 304 } 305 } 306 307 // RotateSecretCompleted is called when a secret identified by the URL 308 // has been rotated. 309 func (w *RemoteStateWatcher) RotateSecretCompleted(rotatedURL string) { 310 w.mu.Lock() 311 defer w.mu.Unlock() 312 for i, url := range w.current.SecretRotations { 313 if url != rotatedURL { 314 continue 315 } 316 w.current.SecretRotations = append( 317 w.current.SecretRotations[:i], 318 w.current.SecretRotations[i+1:]..., 319 ) 320 break 321 } 322 } 323 324 // ExpireRevisionCompleted is called when a secret revision 325 // has been expired. 326 func (w *RemoteStateWatcher) ExpireRevisionCompleted(expiredRevision string) { 327 w.mu.Lock() 328 defer w.mu.Unlock() 329 for i, rev := range w.current.ExpiredSecretRevisions { 330 if rev != expiredRevision { 331 continue 332 } 333 w.current.ExpiredSecretRevisions = append( 334 w.current.ExpiredSecretRevisions[:i], 335 w.current.ExpiredSecretRevisions[i+1:]..., 336 ) 337 break 338 } 339 } 340 341 // RemoveSecretsCompleted is called when secrets have been deleted. 342 func (w *RemoteStateWatcher) RemoveSecretsCompleted(uris []string) { 343 w.mu.Lock() 344 defer w.mu.Unlock() 345 deleted := set.NewStrings(uris...) 346 currentDeleted := set.NewStrings(w.current.DeletedSecrets...) 347 w.current.DeletedSecrets = currentDeleted.Difference(deleted).Values() 348 } 349 350 func (w *RemoteStateWatcher) setUp(unitTag names.UnitTag) (err error) { 351 // TODO(axw) move this logic 352 defer func() { 353 cause := errors.Cause(err) 354 if params.IsCodeNotFoundOrCodeUnauthorized(cause) { 355 // We only want to terminate the agent for IAAS models. 356 if w.modelType == model.IAAS { 357 err = jworker.ErrTerminateAgent 358 } 359 } 360 }() 361 if w.unit, err = w.st.Unit(unitTag); err != nil { 362 return errors.Trace(err) 363 } 364 w.application, err = w.unit.Application() 365 if err != nil { 366 return errors.Trace(err) 367 } 368 if w.containerRunningStatusFunc != nil { 369 providerID := w.unit.ProviderID() 370 if providerID != "" { 371 running, err := w.containerRunningStatusFunc(providerID) 372 if err != nil && !errors.IsNotFound(err) { 373 return errors.Trace(err) 374 } 375 if running != nil { 376 w.containerRunningStatus(*running) 377 } 378 } 379 } 380 w.logger.Debugf("starting remote state watcher, actions for %s; blocked=%v", w.unit.Tag(), w.current.ActionsBlocked) 381 return nil 382 } 383 384 func (w *RemoteStateWatcher) loop(unitTag names.UnitTag) (err error) { 385 if err := w.setUp(unitTag); err != nil { 386 return errors.Trace(err) 387 } 388 389 var requiredEvents int 390 391 var seenUnitChange bool 392 unitw, err := w.unit.Watch() 393 if err != nil { 394 return errors.Trace(err) 395 } 396 if err := w.catacomb.Add(unitw); err != nil { 397 return errors.Trace(err) 398 } 399 requiredEvents++ 400 401 var seenConfigChange bool 402 charmConfigw, err := w.unit.WatchConfigSettingsHash() 403 if err != nil { 404 return errors.Trace(err) 405 } 406 407 if err := w.catacomb.Add(charmConfigw); err != nil { 408 return errors.Trace(err) 409 } 410 requiredEvents++ 411 412 var seenTrustConfigChange bool 413 trustConfigw, err := w.unit.WatchTrustConfigSettingsHash() 414 if err != nil { 415 return errors.Trace(err) 416 } 417 if err := w.catacomb.Add(trustConfigw); err != nil { 418 return errors.Trace(err) 419 } 420 requiredEvents++ 421 422 var seenRelationsChange bool 423 relationsw, err := w.unit.WatchRelations() 424 if err != nil { 425 return errors.Trace(err) 426 } 427 if err := w.catacomb.Add(relationsw); err != nil { 428 return errors.Trace(err) 429 } 430 requiredEvents++ 431 432 var seenAddressesChange bool 433 addressesw, err := w.unit.WatchAddressesHash() 434 if err != nil { 435 return errors.Trace(err) 436 } 437 addressesChanges := addressesw.Changes() 438 if err := w.catacomb.Add(addressesw); err != nil { 439 return errors.Trace(err) 440 } 441 requiredEvents++ 442 443 var seenSecretsChange bool 444 secretsw, err := w.secretsClient.WatchConsumedSecretsChanges(w.unit.Tag().Id()) 445 if err != nil { 446 return errors.Trace(err) 447 } 448 secretsChanges := secretsw.Changes() 449 if err := w.catacomb.Add(secretsw); err != nil { 450 return errors.Trace(err) 451 } 452 requiredEvents++ 453 454 var ( 455 seenApplicationChange bool 456 seenInstanceDataChange bool 457 seenUpgradeSeriesChange bool 458 upgradeSeriesChanges watcher.NotifyChannel 459 instanceDataChannel watcher.NotifyChannel 460 ) 461 462 applicationw, err := w.application.Watch() 463 if err != nil { 464 return errors.Trace(err) 465 } 466 if err := w.catacomb.Add(applicationw); err != nil { 467 return errors.Trace(err) 468 } 469 requiredEvents++ 470 471 if w.modelType == model.IAAS { 472 // Only IAAS models support upgrading the machine series. 473 // TODO(externalreality) This pattern should probably be extracted 474 upgradeSeriesw, err := w.unit.WatchUpgradeSeriesNotifications() 475 if err != nil { 476 return errors.Trace(err) 477 } 478 if err := w.catacomb.Add(upgradeSeriesw); err != nil { 479 return errors.Trace(err) 480 } 481 upgradeSeriesChanges = upgradeSeriesw.Changes() 482 requiredEvents++ 483 } 484 485 if w.canApplyCharmProfile { 486 // Note: canApplyCharmProfile will be false for a CAAS model. 487 instanceDataW, err := w.unit.WatchInstanceData() 488 if err != nil { 489 return errors.Trace(err) 490 } 491 if err := w.catacomb.Add(instanceDataW); err != nil { 492 return errors.Trace(err) 493 } 494 instanceDataChannel = instanceDataW.Changes() 495 requiredEvents++ 496 } 497 498 var seenStorageChange bool 499 storagew, err := w.unit.WatchStorage() 500 if err != nil { 501 return errors.Trace(err) 502 } 503 if err := w.catacomb.Add(storagew); err != nil { 504 return errors.Trace(err) 505 } 506 requiredEvents++ 507 508 var seenLeaderSettingsChange bool 509 leaderSettingsw, err := w.application.WatchLeadershipSettings() 510 if err != nil { 511 return errors.Trace(err) 512 } 513 if err := w.catacomb.Add(leaderSettingsw); err != nil { 514 return errors.Trace(err) 515 } 516 requiredEvents++ 517 518 var seenActionsChange bool 519 actionsw, err := w.unit.WatchActionNotifications() 520 if err != nil { 521 return errors.Trace(err) 522 } 523 if err := w.catacomb.Add(actionsw); err != nil { 524 return errors.Trace(err) 525 } 526 requiredEvents++ 527 528 var seenUpdateStatusIntervalChange bool 529 updateStatusIntervalw, err := w.st.WatchUpdateStatusHookInterval() 530 if err != nil { 531 return errors.Trace(err) 532 } 533 if err := w.catacomb.Add(updateStatusIntervalw); err != nil { 534 return errors.Trace(err) 535 } 536 requiredEvents++ 537 538 var seenLeadershipChange bool 539 // There's no watcher for this per se; we wait on a channel 540 // returned by the leadership tracker. 541 requiredEvents++ 542 543 var eventsObserved int 544 observedEvent := func(flag *bool) { 545 if flag == nil || !*flag { 546 *flag = true 547 eventsObserved++ 548 } 549 } 550 551 // fire will, once the first event for each watcher has 552 // been observed, send a signal on the out channel. 553 fire := func() { 554 if eventsObserved != requiredEvents { 555 return 556 } 557 select { 558 case w.out <- struct{}{}: 559 default: 560 } 561 } 562 563 // Check the initial leadership status, and then we can flip-flop 564 // waiting on leader or minion to trigger the changed event. 565 var waitLeader, waitMinion <-chan struct{} 566 claimLeader := w.leadershipTracker.ClaimLeader() 567 select { 568 case <-w.catacomb.Dying(): 569 return w.catacomb.ErrDying() 570 case <-claimLeader.Ready(): 571 isLeader := claimLeader.Wait() 572 if err := w.leadershipChanged(isLeader); err != nil { 573 return errors.Trace(err) 574 } 575 if isLeader { 576 waitMinion = w.leadershipTracker.WaitMinion().Ready() 577 } else { 578 waitLeader = w.leadershipTracker.WaitLeader().Ready() 579 } 580 observedEvent(&seenLeadershipChange) 581 } 582 583 var updateStatusInterval time.Duration 584 var updateStatusTimer <-chan time.Time 585 resetUpdateStatusTimer := func() { 586 updateStatusTimer = w.updateStatusChannel(updateStatusInterval).After() 587 } 588 589 for { 590 select { 591 case <-w.catacomb.Dying(): 592 return w.catacomb.ErrDying() 593 594 case _, ok := <-unitw.Changes(): 595 w.logger.Debugf("got unit change for %s", w.unit.Tag().Id()) 596 if !ok { 597 return errors.New("unit watcher closed") 598 } 599 if err := w.unitChanged(); err != nil { 600 return errors.Trace(err) 601 } 602 observedEvent(&seenUnitChange) 603 604 case _, ok := <-applicationw.Changes(): 605 w.logger.Debugf("got application change for %s", w.unit.Tag().Id()) 606 if !ok { 607 return errors.New("application watcher closed") 608 } 609 if err := w.applicationChanged(); err != nil { 610 return errors.Trace(err) 611 } 612 observedEvent(&seenApplicationChange) 613 614 case secrets, ok := <-secretsChanges: 615 w.logger.Debugf("got secrets change for %s: %s", w.unit.Tag().Id(), secrets) 616 if !ok { 617 return errors.New("secrets watcher closed") 618 } 619 if err := w.secretsChanged(secrets); err != nil { 620 return errors.Trace(err) 621 } 622 observedEvent(&seenSecretsChange) 623 624 case _, ok := <-instanceDataChannel: 625 w.logger.Debugf("got instance data change for %s", w.unit.Tag().Id()) 626 if !ok { 627 return errors.New("instance data watcher closed") 628 } 629 if err := w.instanceDataChanged(); err != nil { 630 return errors.Trace(err) 631 } 632 observedEvent(&seenInstanceDataChange) 633 634 case _, ok := <-w.containerRunningStatusChannel: 635 w.logger.Debugf("got running status change for %s", w.unit.Tag().Id()) 636 if !ok { 637 return errors.New("running status watcher closed") 638 } 639 if w.current.ProviderID == "" { 640 if err := w.unitChanged(); err != nil { 641 return errors.Trace(err) 642 } 643 if w.current.ProviderID == "" { 644 // This shouldn't happen. 645 w.logger.Warningf("we should already be assigned a provider id for %s but got an empty id", w.unit.Tag().Id()) 646 return nil 647 } 648 } 649 runningStatus, err := w.containerRunningStatusFunc(w.current.ProviderID) 650 if err != nil && !errors.IsNotFound(err) { 651 return errors.Annotatef(err, "getting container running status for %q", unitTag.String()) 652 } 653 if runningStatus != nil { 654 w.containerRunningStatus(*runningStatus) 655 } 656 657 case hashes, ok := <-charmConfigw.Changes(): 658 w.logger.Debugf("got config change for %s: ok=%t, hashes=%v", w.unit.Tag().Id(), ok, hashes) 659 if !ok { 660 return errors.New("config watcher closed") 661 } 662 if len(hashes) != 1 { 663 return errors.New("expected one hash in config change") 664 } 665 w.configHashChanged(hashes[0]) 666 observedEvent(&seenConfigChange) 667 668 case hashes, ok := <-trustConfigw.Changes(): 669 w.logger.Debugf("got trust config change for %s: ok=%t, hashes=%v", w.unit.Tag().Id(), ok, hashes) 670 if !ok { 671 return errors.New("trust config watcher closed") 672 } 673 if len(hashes) != 1 { 674 return errors.New("expected one hash in trust config change") 675 } 676 w.trustHashChanged(hashes[0]) 677 observedEvent(&seenTrustConfigChange) 678 679 case _, ok := <-upgradeSeriesChanges: 680 w.logger.Debugf("got upgrade series change") 681 if !ok { 682 return errors.New("upgrades series watcher closed") 683 } 684 if err := w.upgradeSeriesStatusChanged(); err != nil { 685 return errors.Trace(err) 686 } 687 observedEvent(&seenUpgradeSeriesChange) 688 689 case hashes, ok := <-addressesChanges: 690 w.logger.Debugf("got address change for %s: ok=%t, hashes=%v", w.unit.Tag().Id(), ok, hashes) 691 if !ok { 692 return errors.New("addresses watcher closed") 693 } 694 if len(hashes) != 1 { 695 return errors.New("expected one hash in addresses change") 696 } 697 w.addressesHashChanged(hashes[0]) 698 observedEvent(&seenAddressesChange) 699 700 case _, ok := <-leaderSettingsw.Changes(): 701 w.logger.Debugf("got leader settings change for %s: ok=%t", w.unit.Tag().Id(), ok) 702 if !ok { 703 return errors.New("leader settings watcher closed") 704 } 705 if err := w.leaderSettingsChanged(); err != nil { 706 return errors.Trace(err) 707 } 708 observedEvent(&seenLeaderSettingsChange) 709 710 case actions, ok := <-actionsw.Changes(): 711 w.logger.Debugf("got action change for %s: %v ok=%t", w.unit.Tag().Id(), actions, ok) 712 if !ok { 713 return errors.New("actions watcher closed") 714 } 715 w.actionsChanged(actions) 716 observedEvent(&seenActionsChange) 717 718 case keys, ok := <-relationsw.Changes(): 719 w.logger.Debugf("got relations change for %s: ok=%t", w.unit.Tag().Id(), ok) 720 if !ok { 721 return errors.New("relations watcher closed") 722 } 723 if err := w.relationsChanged(keys); err != nil { 724 return errors.Trace(err) 725 } 726 observedEvent(&seenRelationsChange) 727 728 case keys, ok := <-storagew.Changes(): 729 w.logger.Debugf("got storage change for %s: %v ok=%t", w.unit.Tag().Id(), keys, ok) 730 if !ok { 731 return errors.New("storage watcher closed") 732 } 733 if err := w.storageChanged(keys); err != nil { 734 return errors.Trace(err) 735 } 736 observedEvent(&seenStorageChange) 737 738 case _, ok := <-updateStatusIntervalw.Changes(): 739 w.logger.Debugf("got update status interval change for %s: ok=%t", w.unit.Tag().Id(), ok) 740 if !ok { 741 return errors.New("update status interval watcher closed") 742 } 743 observedEvent(&seenUpdateStatusIntervalChange) 744 745 var err error 746 updateStatusInterval, err = w.st.UpdateStatusHookInterval() 747 if err != nil { 748 return errors.Trace(err) 749 } 750 wasActive := updateStatusTimer != nil 751 resetUpdateStatusTimer() 752 if wasActive { 753 // This is not the first time we've seen an update 754 // status interval change, so there's no need to 755 // fall out and fire an initial change event. 756 continue 757 } 758 759 case <-waitMinion: 760 w.logger.Debugf("got leadership change for %v: minion", unitTag.Id()) 761 if err := w.leadershipChanged(false); err != nil { 762 return errors.Trace(err) 763 } 764 waitMinion = nil 765 waitLeader = w.leadershipTracker.WaitLeader().Ready() 766 767 case <-waitLeader: 768 w.logger.Debugf("got leadership change for %v: leader", unitTag.Id()) 769 if err := w.leadershipChanged(true); err != nil { 770 return errors.Trace(err) 771 } 772 waitLeader = nil 773 waitMinion = w.leadershipTracker.WaitMinion().Ready() 774 775 case uris, ok := <-w.rotateSecretsChanges: 776 if !ok || len(uris) == 0 { 777 continue 778 } 779 w.logger.Debugf("got rotate secret URIs: %q", uris) 780 w.rotateSecretURIs(uris) 781 782 case revisions, ok := <-w.expireSecretsChanges: 783 if !ok || len(revisions) == 0 { 784 continue 785 } 786 w.logger.Debugf("got expired secret revisions: %q", revisions) 787 w.expireSecretRevisions(revisions) 788 789 case secretRevisions, ok := <-w.obsoleteRevisionChanges: 790 w.logger.Debugf("got obsolete secret revisions change for %s: %s", w.application.Tag().Id(), secretRevisions) 791 if !ok { 792 return errors.New("secret revisions watcher closed") 793 } 794 if err := w.secretObsoleteRevisionsChanged(secretRevisions); err != nil { 795 return errors.Trace(err) 796 } 797 798 case change := <-w.storageAttachmentChanges: 799 w.logger.Debugf("storage attachment change for %s: %v", w.unit.Tag().Id(), change) 800 w.storageAttachmentChanged(change) 801 802 case change := <-w.relationUnitsChanges: 803 w.logger.Debugf("got a relation units change for %s : %v", w.unit.Tag().Id(), change) 804 if err := w.relationUnitsChanged(change); err != nil { 805 return errors.Trace(err) 806 } 807 808 case <-updateStatusTimer: 809 w.logger.Debugf("update status timer triggered for %s", w.unit.Tag().Id()) 810 w.updateStatusChanged() 811 resetUpdateStatusTimer() 812 813 case id, ok := <-w.commandChannel: 814 if !ok { 815 return errors.New("commandChannel closed") 816 } 817 w.logger.Debugf("command enqueued for %s: %v", w.unit.Tag().Id(), id) 818 w.commandsChanged(id) 819 820 case id, ok := <-w.workloadEventChannel: 821 if !ok { 822 return errors.New("workloadEventChannel closed") 823 } 824 w.logger.Debugf("workloadEvent enqueued for %s: %v", w.unit.Tag().Id(), id) 825 w.workloadEventsChanged(id) 826 827 case _, ok := <-w.retryHookChannel: 828 if !ok { 829 return errors.New("retryHookChannel closed") 830 } 831 w.logger.Debugf("retry hook timer triggered for %s", w.unit.Tag().Id()) 832 w.retryHookTimerTriggered() 833 834 case shutdown, ok := <-w.shutdownChannel: 835 if !ok { 836 return errors.New("shutdownChannel closed") 837 } 838 if shutdown { 839 w.markShutdown() 840 } 841 } 842 843 // Something changed. 844 fire() 845 } 846 } 847 848 // upgradeSeriesStatusChanged is called when the remote status of a series 849 // upgrade changes. 850 func (w *RemoteStateWatcher) upgradeSeriesStatusChanged() error { 851 w.mu.Lock() 852 defer w.mu.Unlock() 853 854 status, target, err := w.upgradeSeriesStatus() 855 if errors.IsNotFound(err) { 856 // There is no remote state so no upgrade is started. 857 w.logger.Debugf("no upgrade series in progress, reinitializing local upgrade series state") 858 w.current.UpgradeMachineStatus = model.UpgradeSeriesNotStarted 859 return nil 860 } 861 if err != nil { 862 return errors.Trace(err) 863 } 864 865 w.current.UpgradeMachineStatus = status 866 w.current.UpgradeMachineTarget = target 867 868 return nil 869 } 870 871 func (w *RemoteStateWatcher) upgradeSeriesStatus() (model.UpgradeSeriesStatus, string, error) { 872 status, target, err := w.unit.UpgradeSeriesStatus() 873 if err != nil { 874 return "", "", errors.Trace(err) 875 } 876 877 graph := model.UpgradeSeriesGraph() 878 if err := graph.Validate(); err != nil { 879 return "", "", errors.Trace(err) 880 } 881 if !graph.ValidState(status) { 882 return "", "", errors.NotValidf("upgrade series %q is", status) 883 } 884 return status, target, nil 885 } 886 887 // updateStatusChanged is called when the update status timer expires. 888 func (w *RemoteStateWatcher) updateStatusChanged() { 889 w.mu.Lock() 890 w.current.UpdateStatusVersion++ 891 w.mu.Unlock() 892 } 893 894 // commandsChanged is called when a command is enqueued. 895 func (w *RemoteStateWatcher) commandsChanged(id string) { 896 w.mu.Lock() 897 w.current.Commands = append(w.current.Commands, id) 898 w.mu.Unlock() 899 } 900 901 // workloadEventsChanged is called when a container event is enqueued. 902 func (w *RemoteStateWatcher) workloadEventsChanged(id string) { 903 w.mu.Lock() 904 defer w.mu.Unlock() 905 // Ensure we don't add the same ID twice. 906 for _, otherId := range w.current.WorkloadEvents { 907 if otherId == id { 908 return 909 } 910 } 911 w.current.WorkloadEvents = append(w.current.WorkloadEvents, id) 912 } 913 914 // retryHookTimerTriggered is called when the retry hook timer expires. 915 func (w *RemoteStateWatcher) retryHookTimerTriggered() { 916 w.mu.Lock() 917 w.current.RetryHookVersion++ 918 w.mu.Unlock() 919 } 920 921 // unitChanged responds to changes in the unit. 922 func (w *RemoteStateWatcher) unitChanged() error { 923 if err := w.unit.Refresh(); err != nil { 924 return errors.Trace(err) 925 } 926 w.mu.Lock() 927 defer w.mu.Unlock() 928 w.current.Life = w.unit.Life() 929 w.current.ResolvedMode = w.unit.Resolved() 930 // It's ok to sync provider ID by watching unit rather than 931 // cloud container because it will not change once pod created. 932 w.current.ProviderID = w.unit.ProviderID() 933 return nil 934 } 935 936 // applicationChanged responds to changes in the application. 937 func (w *RemoteStateWatcher) applicationChanged() error { 938 if err := w.application.Refresh(); err != nil { 939 return errors.Trace(err) 940 } 941 url, force, err := w.application.CharmURL() 942 if err != nil { 943 return errors.Trace(err) 944 } 945 required := false 946 if w.canApplyCharmProfile { 947 ch, err := w.st.Charm(url) 948 if err != nil { 949 return errors.Trace(err) 950 } 951 required, err = ch.LXDProfileRequired() 952 if err != nil { 953 return errors.Trace(err) 954 } 955 } 956 ver, err := w.application.CharmModifiedVersion() 957 if err != nil { 958 return errors.Trace(err) 959 } 960 // CAAS sidecar charms will wait for the provider to restart/recreate 961 // the unit before performing an upgrade. 962 if w.sidecar && ver != w.enforcedCharmModifiedVersion { 963 return nil 964 } 965 w.mu.Lock() 966 w.current.CharmURL = url 967 w.current.ForceCharmUpgrade = force 968 w.current.CharmModifiedVersion = ver 969 w.current.CharmProfileRequired = required 970 w.mu.Unlock() 971 return nil 972 } 973 974 // secretsChanged responds to changes in secrets. 975 func (w *RemoteStateWatcher) secretsChanged(secretURIs []string) error { 976 w.mu.Lock() 977 defer w.mu.Unlock() 978 info, err := w.secretsClient.GetConsumerSecretsRevisionInfo(w.unit.Tag().Id(), secretURIs) 979 if err != nil { 980 return errors.Trace(err) 981 } 982 w.logger.Debugf("got latest secret info: %#v", info) 983 for _, uri := range secretURIs { 984 if latest, ok := info[uri]; ok { 985 w.current.ConsumedSecretInfo[uri] = latest 986 } else { 987 delete(w.current.ConsumedSecretInfo, uri) 988 deleted := set.NewStrings(w.current.DeletedSecrets...) 989 deleted.Add(uri) 990 w.current.DeletedSecrets = deleted.SortedValues() 991 } 992 } 993 w.logger.Debugf("deleted secrets: %v", w.current.DeletedSecrets) 994 w.logger.Debugf("obsolete secrets: %v", w.current.ObsoleteSecretRevisions) 995 return nil 996 } 997 998 func (w *RemoteStateWatcher) secretObsoleteRevisionsChanged(secretRevisions []string) error { 999 w.mu.Lock() 1000 defer w.mu.Unlock() 1001 for _, revInfo := range secretRevisions { 1002 parts := strings.Split(revInfo, "/") 1003 uri := parts[0] 1004 if len(parts) < 2 { 1005 deleted := set.NewStrings(w.current.DeletedSecrets...) 1006 deleted.Add(uri) 1007 w.current.DeletedSecrets = deleted.SortedValues() 1008 continue 1009 } 1010 rev, err := strconv.Atoi(parts[1]) 1011 if err != nil { 1012 return errors.NotValidf("secret revision %q for %q", parts[1], uri) 1013 } 1014 obsolete := set.NewInts(w.current.ObsoleteSecretRevisions[uri]...) 1015 obsolete.Add(rev) 1016 w.current.ObsoleteSecretRevisions[uri] = obsolete.SortedValues() 1017 } 1018 w.logger.Debugf("obsolete secret revisions: %v", w.current.ObsoleteSecretRevisions) 1019 w.logger.Debugf("deleted secrets: %v", w.current.DeletedSecrets) 1020 return nil 1021 } 1022 1023 func (w *RemoteStateWatcher) instanceDataChanged() error { 1024 name, err := w.unit.LXDProfileName() 1025 if err != nil { 1026 return errors.Trace(err) 1027 } 1028 w.mu.Lock() 1029 w.current.LXDProfileName = name 1030 w.mu.Unlock() 1031 w.logger.Debugf("LXDProfileName changed to %q", name) 1032 return nil 1033 } 1034 1035 func (w *RemoteStateWatcher) configHashChanged(value string) { 1036 w.mu.Lock() 1037 w.current.ConfigHash = value 1038 w.mu.Unlock() 1039 } 1040 1041 func (w *RemoteStateWatcher) trustHashChanged(value string) { 1042 w.mu.Lock() 1043 w.current.TrustHash = value 1044 w.mu.Unlock() 1045 } 1046 1047 func (w *RemoteStateWatcher) addressesHashChanged(value string) { 1048 w.mu.Lock() 1049 w.current.AddressesHash = value 1050 w.mu.Unlock() 1051 } 1052 1053 func (w *RemoteStateWatcher) leaderSettingsChanged() error { 1054 w.mu.Lock() 1055 w.current.LeaderSettingsVersion++ 1056 w.mu.Unlock() 1057 return nil 1058 } 1059 1060 func (w *RemoteStateWatcher) leadershipChanged(isLeader bool) error { 1061 w.mu.Lock() 1062 defer w.mu.Unlock() 1063 1064 w.current.Leader = isLeader 1065 if w.secretRotateWatcher != nil { 1066 _ = worker.Stop(w.secretRotateWatcher) 1067 } 1068 w.secretRotateWatcher = nil 1069 w.rotateSecretsChanges = nil 1070 w.current.SecretRotations = nil 1071 1072 if w.secretExpiryWatcher != nil { 1073 _ = worker.Stop(w.secretExpiryWatcher) 1074 } 1075 w.secretExpiryWatcher = nil 1076 w.expireSecretsChanges = nil 1077 w.current.ExpiredSecretRevisions = nil 1078 1079 if w.obsoleteRevisionWatcher != nil { 1080 _ = worker.Stop(w.obsoleteRevisionWatcher) 1081 } 1082 w.obsoleteRevisionWatcher = nil 1083 w.obsoleteRevisionChanges = nil 1084 1085 // Allow a generous buffer so a slow unit agent does not 1086 // block the upstream worker. 1087 w.rotateSecretsChanges = make(chan []string, 100) 1088 w.logger.Debugf("starting secrets rotation watcher") 1089 rotateWatcher, err := w.secretRotateWatcherFunc(w.unit.Tag(), isLeader, w.rotateSecretsChanges) 1090 if err != nil { 1091 return errors.Trace(err) 1092 } 1093 if err := w.catacomb.Add(rotateWatcher); err != nil { 1094 return errors.Trace(err) 1095 } 1096 w.secretRotateWatcher = rotateWatcher 1097 1098 // Allow a generous buffer so a slow unit agent does not 1099 // block the upstream worker. 1100 w.expireSecretsChanges = make(chan []string, 100) 1101 w.logger.Debugf("starting secret revisions expiry watcher") 1102 expiryWatcher, err := w.secretExpiryWatcherFunc(w.unit.Tag(), isLeader, w.expireSecretsChanges) 1103 if err != nil { 1104 return errors.Trace(err) 1105 } 1106 if err := w.catacomb.Add(expiryWatcher); err != nil { 1107 return errors.Trace(err) 1108 } 1109 w.secretExpiryWatcher = expiryWatcher 1110 1111 // Allow a generous buffer so a slow unit agent does not 1112 // block the upstream worker. 1113 w.obsoleteRevisionChanges = make(chan []string, 100) 1114 w.logger.Debugf("starting obsolete secret revisions watcher") 1115 owners := []names.Tag{w.unit.Tag()} 1116 if isLeader { 1117 appName, _ := names.UnitApplication(w.unit.Tag().Id()) 1118 owners = append(owners, names.NewApplicationTag(appName)) 1119 } 1120 obsoleteRevisionsWatcher, err := w.secretsClient.WatchObsolete(owners...) 1121 if err != nil { 1122 return errors.Trace(err) 1123 } 1124 if err := w.catacomb.Add(obsoleteRevisionsWatcher); err != nil { 1125 return errors.Trace(err) 1126 } 1127 w.obsoleteRevisionWatcher = obsoleteRevisionsWatcher 1128 w.obsoleteRevisionChanges = obsoleteRevisionsWatcher.Changes() 1129 1130 return nil 1131 } 1132 1133 // rotateSecretURIs adds the specified URLs to those that need 1134 // to be rotated. 1135 func (w *RemoteStateWatcher) rotateSecretURIs(uris []string) { 1136 w.mu.Lock() 1137 defer w.mu.Unlock() 1138 1139 pending := set.NewStrings(w.current.SecretRotations...) 1140 for _, uri := range uris { 1141 if !pending.Contains(uri) { 1142 pending.Add(uri) 1143 w.current.SecretRotations = append(w.current.SecretRotations, uri) 1144 } 1145 } 1146 } 1147 1148 // expireSecretRevisions adds the specified secret revisions 1149 // to those that need to be expired. 1150 func (w *RemoteStateWatcher) expireSecretRevisions(revisions []string) { 1151 w.mu.Lock() 1152 defer w.mu.Unlock() 1153 1154 pending := set.NewStrings(w.current.ExpiredSecretRevisions...) 1155 for _, rev := range revisions { 1156 if !pending.Contains(rev) { 1157 pending.Add(rev) 1158 w.current.ExpiredSecretRevisions = append(w.current.ExpiredSecretRevisions, rev) 1159 } 1160 } 1161 } 1162 1163 // relationsChanged responds to application relation changes. 1164 func (w *RemoteStateWatcher) relationsChanged(keys []string) error { 1165 w.mu.Lock() 1166 defer w.mu.Unlock() 1167 1168 // NOTE (stickupkid): Any return nil or early exit during the iteration of 1169 // the keys that is non-exhaustive can cause units (subordinates) to 1170 // commit suicide. 1171 1172 for _, key := range keys { 1173 relationTag := names.NewRelationTag(key) 1174 rel, err := w.st.Relation(relationTag) 1175 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 1176 // If it's actually gone, this unit cannot have entered 1177 // scope, and therefore never needs to know about it. 1178 if ruw, ok := w.relations[relationTag]; ok { 1179 _ = worker.Stop(ruw) 1180 delete(w.relations, relationTag) 1181 delete(w.current.Relations, ruw.relationId) 1182 } 1183 } else if err != nil { 1184 return errors.Trace(err) 1185 } else if relErr := w.ensureRelationUnits(rel); relErr != nil { 1186 return errors.Trace(relErr) 1187 } 1188 } 1189 return nil 1190 } 1191 1192 func (w *RemoteStateWatcher) ensureRelationUnits(rel Relation) error { 1193 relationTag := rel.Tag() 1194 if _, ok := w.relations[relationTag]; ok { 1195 // We're already watching this one, so just update life/suspension status 1196 relationSnapshot := w.current.Relations[rel.Id()] 1197 relationSnapshot.Life = rel.Life() 1198 relationSnapshot.Suspended = rel.Suspended() 1199 w.current.Relations[rel.Id()] = relationSnapshot 1200 if rel.Suspended() { 1201 // Relation has been suspended, so stop the listeners here. 1202 // The relation itself is retained in the current relations 1203 // in the suspended state so that departed/broken hooks can run. 1204 if ruw, ok := w.relations[relationTag]; ok { 1205 err := worker.Stop(ruw) 1206 if err != nil { 1207 // This was always silently ignored, so it can't be 1208 // particularly useful, but avoid suppressing errors entirely. 1209 w.logger.Debugf("error stopping relation watcher for %s: %v", w.unit.Tag().Id(), err) 1210 } 1211 delete(w.relations, relationTag) 1212 } 1213 } 1214 return nil 1215 } 1216 // We weren't watching it already, but if the relation is suspended, 1217 // we don't need to start watching it. 1218 if rel.Suspended() { 1219 return nil 1220 } 1221 return errors.Trace(w.watchRelationUnits(rel)) 1222 } 1223 1224 // watchRelationUnits starts watching the relation units for the given 1225 // relation, waits for its first event, and records the information in 1226 // the current snapshot. 1227 func (w *RemoteStateWatcher) watchRelationUnits(rel Relation) error { 1228 ruw, err := w.st.WatchRelationUnits(rel.Tag(), w.unit.Tag()) 1229 // Deal with the race where Relation returned a valid, perhaps dying 1230 // relation, but by the time we ask to watch it, we get unauthorized 1231 // because it is no longer around. 1232 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 1233 return nil 1234 } else if err != nil { 1235 return errors.Trace(err) 1236 } 1237 // Because of the delay before handing off responsibility to 1238 // wrapRelationUnitsWatcher below, add to our own catacomb to 1239 // ensure errors get picked up if they happen. 1240 if err := w.catacomb.Add(ruw); err != nil { 1241 return errors.Trace(err) 1242 } 1243 relationSnapshot := RelationSnapshot{ 1244 Life: rel.Life(), 1245 Suspended: rel.Suspended(), 1246 Members: make(map[string]int64), 1247 ApplicationMembers: make(map[string]int64), 1248 } 1249 // Handle the first change to populate the Members map. 1250 select { 1251 case <-w.catacomb.Dying(): 1252 return w.catacomb.ErrDying() 1253 case change, ok := <-ruw.Changes(): 1254 if !ok { 1255 return errors.New("relation units watcher closed") 1256 } 1257 for unit, settings := range change.Changed { 1258 relationSnapshot.Members[unit] = settings.Version 1259 } 1260 for app, settingsVersion := range change.AppChanged { 1261 relationSnapshot.ApplicationMembers[app] = settingsVersion 1262 } 1263 } 1264 // Wrap the Changes() with the relationId so we can process all changes 1265 // via the same channel. 1266 innerRUW, err := wrapRelationUnitsWatcher(rel.Id(), ruw, w.relationUnitsChanges) 1267 if err != nil { 1268 return errors.Trace(err) 1269 } 1270 if err := w.catacomb.Add(innerRUW); err != nil { 1271 return errors.Trace(err) 1272 } 1273 w.current.Relations[rel.Id()] = relationSnapshot 1274 w.relations[rel.Tag()] = innerRUW 1275 return nil 1276 } 1277 1278 // relationUnitsChanged responds to relation units changes. 1279 func (w *RemoteStateWatcher) relationUnitsChanged(change relationUnitsChange) error { 1280 w.mu.Lock() 1281 defer w.mu.Unlock() 1282 snapshot, ok := w.current.Relations[change.relationId] 1283 if !ok { 1284 return nil 1285 } 1286 for unit, settings := range change.Changed { 1287 snapshot.Members[unit] = settings.Version 1288 } 1289 for app, settingsVersion := range change.AppChanged { 1290 snapshot.ApplicationMembers[app] = settingsVersion 1291 } 1292 for _, unit := range change.Departed { 1293 delete(snapshot.Members, unit) 1294 } 1295 return nil 1296 } 1297 1298 // storageAttachmentChanged responds to storage attachment changes. 1299 func (w *RemoteStateWatcher) storageAttachmentChanged(change storageAttachmentChange) { 1300 w.mu.Lock() 1301 w.current.Storage[change.Tag] = change.Snapshot 1302 w.mu.Unlock() 1303 } 1304 1305 func (w *RemoteStateWatcher) actionsChanged(actions []string) { 1306 w.mu.Lock() 1307 defer w.mu.Unlock() 1308 for _, action := range actions { 1309 // If we already have the action, signal a change. 1310 if r, ok := w.current.ActionChanged[action]; ok { 1311 w.current.ActionChanged[action] = r + 1 1312 } else { 1313 w.current.ActionsPending = append(w.current.ActionsPending, action) 1314 w.current.ActionChanged[action] = 0 1315 } 1316 } 1317 } 1318 1319 func (w *RemoteStateWatcher) containerRunningStatus(runningStatus ContainerRunningStatus) { 1320 w.mu.Lock() 1321 w.logger.Debugf("running status update for %s(provider-id=%s): %+v", w.unit.Tag(), w.current.ProviderID, runningStatus) 1322 w.current.ActionsBlocked = !runningStatus.Running 1323 w.current.ContainerRunningStatus = &runningStatus 1324 w.mu.Unlock() 1325 } 1326 1327 // storageChanged responds to unit storage changes. 1328 func (w *RemoteStateWatcher) storageChanged(keys []string) error { 1329 tags := make([]names.StorageTag, len(keys)) 1330 for i, key := range keys { 1331 tags[i] = names.NewStorageTag(key) 1332 } 1333 ids := make([]params.StorageAttachmentId, len(keys)) 1334 for i, tag := range tags { 1335 ids[i] = params.StorageAttachmentId{ 1336 StorageTag: tag.String(), 1337 UnitTag: w.unit.Tag().String(), 1338 } 1339 } 1340 results, err := w.st.StorageAttachmentLife(ids) 1341 if err != nil { 1342 return errors.Trace(err) 1343 } 1344 1345 w.mu.Lock() 1346 defer w.mu.Unlock() 1347 1348 for i, result := range results { 1349 tag := tags[i] 1350 if result.Error == nil { 1351 if storageSnapshot, ok := w.current.Storage[tag]; ok { 1352 // We've previously started a watcher for this storage 1353 // attachment, so all we needed to do was update the 1354 // lifecycle state. 1355 storageSnapshot.Life = result.Life 1356 w.current.Storage[tag] = storageSnapshot 1357 continue 1358 } 1359 // We haven't seen this storage attachment before, so start 1360 // a watcher now; add it to our catacomb in case of mishap; 1361 // and wait for the initial event. 1362 saw, err := w.st.WatchStorageAttachment(tag, w.unit.Tag()) 1363 if err != nil { 1364 return errors.Annotate(err, "watching storage attachment") 1365 } 1366 if err := w.catacomb.Add(saw); err != nil { 1367 return errors.Trace(err) 1368 } 1369 if err := w.watchStorageAttachment(tag, result.Life, saw); err != nil { 1370 return errors.Trace(err) 1371 } 1372 } else if params.IsCodeNotFound(result.Error) { 1373 if watcher, ok := w.storageAttachmentWatchers[tag]; ok { 1374 // already under catacomb management, any error tracked already 1375 _ = worker.Stop(watcher) 1376 delete(w.storageAttachmentWatchers, tag) 1377 } 1378 delete(w.current.Storage, tag) 1379 } else { 1380 return errors.Annotatef( 1381 result.Error, "getting life of %s attachment for %s", 1382 names.ReadableString(tag), w.unit.Tag().Id(), 1383 ) 1384 } 1385 } 1386 return nil 1387 } 1388 1389 // watchStorageAttachment starts watching the storage attachment with 1390 // the specified storage tag, waits for its first event, and records 1391 // the information in the current snapshot. 1392 func (w *RemoteStateWatcher) watchStorageAttachment( 1393 tag names.StorageTag, 1394 life life.Value, 1395 saw watcher.NotifyWatcher, 1396 ) error { 1397 var storageSnapshot StorageSnapshot 1398 select { 1399 case <-w.catacomb.Dying(): 1400 return w.catacomb.ErrDying() 1401 case _, ok := <-saw.Changes(): 1402 if !ok { 1403 return errors.Errorf("storage attachment watcher closed for %s", w.unit.Tag().Id()) 1404 } 1405 var err error 1406 storageSnapshot, err = getStorageSnapshot(w.st, tag, w.unit.Tag()) 1407 if errors.Is(err, errors.NotProvisioned) { 1408 // If the storage is unprovisioned, we still want to 1409 // record the attachment, but we'll mark it as 1410 // unattached. This allows the uniter to wait for 1411 // pending storage attachments to be provisioned. 1412 storageSnapshot = StorageSnapshot{Life: life} 1413 } else if err != nil { 1414 return errors.Annotatef(err, "processing initial storage attachment change for %s", w.unit.Tag().Id()) 1415 } 1416 } 1417 innerSAW, err := newStorageAttachmentWatcher( 1418 w.st, saw, w.unit.Tag(), tag, w.storageAttachmentChanges, 1419 ) 1420 if err != nil { 1421 return errors.Trace(err) 1422 } 1423 w.current.Storage[tag] = storageSnapshot 1424 w.storageAttachmentWatchers[tag] = innerSAW 1425 return nil 1426 } 1427 1428 // markShutdown is called when Shutdown is called on remote state. 1429 func (w *RemoteStateWatcher) markShutdown() { 1430 w.mu.Lock() 1431 w.current.Shutdown = true 1432 w.mu.Unlock() 1433 }