github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/watcher.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "crypto/sha256" 8 "encoding/json" 9 "fmt" 10 "hash" 11 "reflect" 12 "regexp" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/juju/charm/v12" 19 "github.com/juju/clock" 20 "github.com/juju/collections/set" 21 "github.com/juju/errors" 22 "github.com/juju/loggo" 23 "github.com/juju/mgo/v3" 24 "github.com/juju/mgo/v3/bson" 25 "github.com/juju/names/v5" 26 "github.com/kr/pretty" 27 "gopkg.in/tomb.v2" 28 29 "github.com/juju/juju/core/actions" 30 "github.com/juju/juju/core/instance" 31 "github.com/juju/juju/core/lxdprofile" 32 corenetwork "github.com/juju/juju/core/network" 33 corewatcher "github.com/juju/juju/core/watcher" 34 "github.com/juju/juju/mongo" 35 "github.com/juju/juju/state/watcher" 36 ) 37 38 var watchLogger = loggo.GetLogger("juju.state.watch") 39 40 // Watcher is implemented by all watchers; the actual 41 // changes channel is returned by a watcher-specific 42 // Changes method. 43 type Watcher interface { 44 // Kill asks the watcher to stop without waiting for it do so. 45 Kill() 46 // Wait waits for the watcher to die and returns any 47 // error encountered when it was running. 48 Wait() error 49 // Stop kills the watcher, then waits for it to die. 50 Stop() error 51 // Err returns any error encountered while the watcher 52 // has been running. 53 Err() error 54 } 55 56 // NotifyWatcher generates signals when something changes, but it does not 57 // return any content for those changes 58 type NotifyWatcher interface { 59 Watcher 60 Changes() <-chan struct{} 61 } 62 63 // StringsWatcher generates signals when something changes, returning 64 // the changes as a list of strings. 65 type StringsWatcher interface { 66 Watcher 67 Changes() <-chan []string 68 } 69 70 // RelationUnitsWatcher generates signals when units enter or leave 71 // the scope of a RelationUnit, and changes to the settings of those 72 // units known to have entered. 73 type RelationUnitsWatcher interface { 74 Watcher 75 76 Changes() corewatcher.RelationUnitsChannel 77 } 78 79 // newCommonWatcher exists so that all embedders have a place from which 80 // to get a single TxnLogWatcher that will not be replaced in the lifetime 81 // of the embedder (and also to restrict the width of the interface by 82 // which they can access the rest of State, by storing st as a 83 // modelBackend). 84 func newCommonWatcher(backend modelBackend) commonWatcher { 85 return commonWatcher{ 86 backend: backend, 87 db: backend.db(), 88 watcher: backend.txnLogWatcher(), 89 } 90 } 91 92 // commonWatcher is part of all client watchers. 93 type commonWatcher struct { 94 backend modelBackend 95 db Database 96 watcher watcher.BaseWatcher 97 tomb tomb.Tomb 98 } 99 100 // Stop stops the watcher, and returns any error encountered while running 101 // or shutting down. 102 func (w *commonWatcher) Stop() error { 103 w.Kill() 104 return w.Wait() 105 } 106 107 // Kill kills the watcher without waiting for it to shut down. 108 func (w *commonWatcher) Kill() { 109 w.tomb.Kill(nil) 110 } 111 112 // Wait waits for the watcher to die and returns any 113 // error encountered when it was running. 114 func (w *commonWatcher) Wait() error { 115 return w.tomb.Wait() 116 } 117 118 // Err returns any error encountered while running or shutting down, or 119 // tomb.ErrStillAlive if the watcher is still running. 120 func (w *commonWatcher) Err() error { 121 return w.tomb.Err() 122 } 123 124 // collect combines the effects of the one change, and any further 125 // changes read from more in the next 10ms. The result map describes the 126 // existence, or not, of every id observed to have changed. If a value is read 127 // from the supplied stop chan, collect returns false immediately. 128 func collect(one watcher.Change, more <-chan watcher.Change, stop <-chan struct{}) (map[interface{}]bool, bool) { 129 return collectWhereRevnoGreaterThan(one, more, stop, 0) 130 } 131 132 // collectWhereRevnoGreaterThan combines the effects of the one change, and any 133 // further changes read from more in the next 10ms. The result map describes 134 // the existence, or not, of every id observed to have changed. If a value is 135 // read from the supplied stop chan, collect returns false immediately. 136 // 137 // The implementation will flag result doc IDs as existing iff the doc revno 138 // is greater than the provided revnoThreshold value. 139 func collectWhereRevnoGreaterThan(one watcher.Change, more <-chan watcher.Change, stop <-chan struct{}, revnoThreshold int64) (map[interface{}]bool, bool) { 140 var count int 141 result := map[interface{}]bool{} 142 handle := func(ch watcher.Change) { 143 count++ 144 result[ch.Id] = ch.Revno > revnoThreshold 145 } 146 handle(one) 147 // TODO(fwereade): 2016-03-17 lp:1558657 148 timeout := time.After(10 * time.Millisecond) 149 for done := false; !done; { 150 select { 151 case <-stop: 152 return nil, false 153 case another := <-more: 154 handle(another) 155 case <-timeout: 156 done = true 157 } 158 } 159 watchLogger.Tracef("read %d events for %d documents", count, len(result)) 160 return result, true 161 } 162 163 func hasString(changes []string, name string) bool { 164 for _, v := range changes { 165 if v == name { 166 return true 167 } 168 } 169 return false 170 } 171 172 var _ Watcher = (*lifecycleWatcher)(nil) 173 174 // lifecycleWatcher notifies about lifecycle changes for a set of entities of 175 // the same kind. The first event emitted will contain the ids of all 176 // entities; subsequent events are emitted whenever one or more entities are 177 // added, or change their lifecycle state. After an entity is found to be 178 // Dead, no further event will include it. 179 type lifecycleWatcher struct { 180 commonWatcher 181 out chan []string 182 183 // coll is a function returning the mongo.Collection holding all 184 // interesting entities 185 coll func() (mongo.Collection, func()) 186 collName string 187 188 // members is used to select the initial set of interesting entities. 189 members bson.D 190 // filter is used to exclude events not affecting interesting entities. 191 filter func(interface{}) bool 192 // transform, if non-nil, is used to transform a document ID immediately 193 // prior to emitting to the out channel. 194 transform func(string) string 195 // life holds the most recent known life states of interesting entities. 196 life map[string]Life 197 } 198 199 func collFactory(db Database, collName string) func() (mongo.Collection, func()) { 200 return func() (mongo.Collection, func()) { 201 return db.GetCollection(collName) 202 } 203 } 204 205 // WatchModels returns a StringsWatcher that notifies of changes to 206 // any models. If a model is removed this *won't* signal that the 207 // model has gone away - it's based on a collectionWatcher which omits 208 // these events. 209 func (st *State) WatchModels() StringsWatcher { 210 return newCollectionWatcher(st, colWCfg{ 211 col: modelsC, 212 global: true, 213 }) 214 } 215 216 // WatchModelLives returns a StringsWatcher that notifies of changes 217 // to any model life values. The watcher will not send any more events 218 // for a model after it has been observed to be Dead. 219 func (st *State) WatchModelLives() StringsWatcher { 220 return newLifecycleWatcher(st, modelsC, nil, nil, nil) 221 } 222 223 // WatchModelVolumes returns a StringsWatcher that notifies of changes to 224 // the lifecycles of all model-scoped volumes. 225 func (sb *storageBackend) WatchModelVolumes() StringsWatcher { 226 return sb.watchModelHostStorage(volumesC) 227 } 228 229 // WatchModelFilesystems returns a StringsWatcher that notifies of changes 230 // to the lifecycles of all model-scoped filesystems. 231 func (sb *storageBackend) WatchModelFilesystems() StringsWatcher { 232 return sb.watchModelHostStorage(filesystemsC) 233 } 234 235 var machineOrUnitSnippet = "(" + names.NumberSnippet + "|" + names.UnitSnippet + ")" 236 237 func (sb *storageBackend) watchModelHostStorage(collection string) StringsWatcher { 238 mb := sb.mb 239 pattern := fmt.Sprintf("^%s$", mb.docID(machineOrUnitSnippet)) 240 members := bson.D{{"_id", bson.D{{"$regex", pattern}}}} 241 filter := func(id interface{}) bool { 242 k, err := mb.strictLocalID(id.(string)) 243 if err != nil { 244 return false 245 } 246 return !strings.Contains(k, "/") 247 } 248 return newLifecycleWatcher(mb, collection, members, filter, nil) 249 } 250 251 // WatchMachineVolumes returns a StringsWatcher that notifies of changes to 252 // the lifecycles of all volumes scoped to the specified machine. 253 func (sb *storageBackend) WatchMachineVolumes(m names.MachineTag) StringsWatcher { 254 return sb.watchHostStorage(m, volumesC) 255 } 256 257 // WatchMachineFilesystems returns a StringsWatcher that notifies of changes 258 // to the lifecycles of all filesystems scoped to the specified machine. 259 func (sb *storageBackend) WatchMachineFilesystems(m names.MachineTag) StringsWatcher { 260 return sb.watchHostStorage(m, filesystemsC) 261 } 262 263 // WatchUnitFilesystems returns a StringsWatcher that notifies of changes 264 // to the lifecycles of all filesystems scoped to units of the specified application. 265 func (sb *storageBackend) WatchUnitFilesystems(app names.ApplicationTag) StringsWatcher { 266 return sb.watchHostStorage(app, filesystemsC) 267 } 268 269 func (sb *storageBackend) watchHostStorage(host names.Tag, collection string) StringsWatcher { 270 mb := sb.mb 271 // The regexp patterns below represent either machine or unit attached storage, <hostid>/<number>. 272 // For machines, it can be something like 4/6. 273 // For units the pattern becomes something like mariadb/0/6. 274 // The host parameter passed into this method is the application name, any of whose units we are interested in. 275 pattern := fmt.Sprintf("^%s(/%s)?/%s$", mb.docID(host.Id()), names.NumberSnippet, names.NumberSnippet) 276 members := bson.D{{"_id", bson.D{{"$regex", pattern}}}} 277 prefix := fmt.Sprintf("%s(/%s)?/.*", host.Id(), names.NumberSnippet) 278 matchExp := regexp.MustCompile(prefix) 279 filter := func(id interface{}) bool { 280 k, err := mb.strictLocalID(id.(string)) 281 if err != nil { 282 return false 283 } 284 return matchExp.MatchString(k) 285 } 286 return newLifecycleWatcher(mb, collection, members, filter, nil) 287 } 288 289 // WatchMachineAttachmentsPlans returns a StringsWatcher that notifies machine agents 290 // that a volume has been attached to their instance by the environment provider. 291 // This allows machine agents to do extra initialization to the volume, in cases 292 // such as iSCSI disks, or other disks that have similar requirements 293 func (sb *storageBackend) WatchMachineAttachmentsPlans(m names.MachineTag) StringsWatcher { 294 return sb.watchMachineVolumeAttachmentPlans(m) 295 } 296 297 func (sb *storageBackend) watchMachineVolumeAttachmentPlans(m names.MachineTag) StringsWatcher { 298 mb := sb.mb 299 pattern := fmt.Sprintf("^%s:%s$", mb.docID(m.Id()), names.NumberSnippet) 300 members := bson.D{{"_id", bson.D{{"$regex", pattern}}}} 301 prefix := fmt.Sprintf("%s:", m.Id()) 302 filter := func(id interface{}) bool { 303 k, err := mb.strictLocalID(id.(string)) 304 if err != nil { 305 return false 306 } 307 return strings.HasPrefix(k, prefix) 308 } 309 return newLifecycleWatcher(mb, volumeAttachmentPlanC, members, filter, nil) 310 } 311 312 // WatchModelVolumeAttachments returns a StringsWatcher that notifies of 313 // changes to the lifecycles of all volume attachments related to environ- 314 // scoped volumes. 315 func (sb *storageBackend) WatchModelVolumeAttachments() StringsWatcher { 316 return sb.watchModelHostStorageAttachments(volumeAttachmentsC) 317 } 318 319 // WatchModelFilesystemAttachments returns a StringsWatcher that notifies 320 // of changes to the lifecycles of all filesystem attachments related to 321 // environ-scoped filesystems. 322 func (sb *storageBackend) WatchModelFilesystemAttachments() StringsWatcher { 323 return sb.watchModelHostStorageAttachments(filesystemAttachmentsC) 324 } 325 326 func (sb *storageBackend) watchModelHostStorageAttachments(collection string) StringsWatcher { 327 mb := sb.mb 328 pattern := fmt.Sprintf("^%s.*:%s$", mb.docID(""), machineOrUnitSnippet) 329 members := bson.D{{"_id", bson.D{{"$regex", pattern}}}} 330 filter := func(id interface{}) bool { 331 k, err := mb.strictLocalID(id.(string)) 332 if err != nil { 333 return false 334 } 335 colon := strings.IndexRune(k, ':') 336 if colon == -1 { 337 return false 338 } 339 return !strings.Contains(k[colon+1:], "/") 340 } 341 return newLifecycleWatcher(mb, collection, members, filter, nil) 342 } 343 344 // WatchMachineVolumeAttachments returns a StringsWatcher that notifies of 345 // changes to the lifecycles of all volume attachments related to the specified 346 // machine, for volumes scoped to the machine. 347 func (sb *storageBackend) WatchMachineVolumeAttachments(m names.MachineTag) StringsWatcher { 348 return sb.watchHostStorageAttachments(m, volumeAttachmentsC) 349 } 350 351 // WatchMachineFilesystemAttachments returns a StringsWatcher that notifies of 352 // changes to the lifecycles of all filesystem attachments related to the specified 353 // machine, for filesystems scoped to the machine. 354 func (sb *storageBackend) WatchMachineFilesystemAttachments(m names.MachineTag) StringsWatcher { 355 return sb.watchHostStorageAttachments(m, filesystemAttachmentsC) 356 } 357 358 // WatchUnitVolumeAttachments returns a StringsWatcher that notifies of 359 // changes to the lifecycles of all volume attachments related to the specified 360 // application's units, for volumes scoped to the application's units. 361 // TODO(caas) - currently untested since units don't directly support attached volumes 362 func (sb *storageBackend) WatchUnitVolumeAttachments(app names.ApplicationTag) StringsWatcher { 363 return sb.watchHostStorageAttachments(app, volumeAttachmentsC) 364 } 365 366 // WatchUnitFilesystemAttachments returns a StringsWatcher that notifies of 367 // changes to the lifecycles of all filesystem attachments related to the specified 368 // application's units, for filesystems scoped to the application's units. 369 func (sb *storageBackend) WatchUnitFilesystemAttachments(app names.ApplicationTag) StringsWatcher { 370 return sb.watchHostStorageAttachments(app, filesystemAttachmentsC) 371 } 372 373 func (sb *storageBackend) watchHostStorageAttachments(host names.Tag, collection string) StringsWatcher { 374 mb := sb.mb 375 // Go's regex doesn't support lookbacks so the pattern match is a bit clumsy. 376 // We look for either a machine attachment id, eg 0:0/42 377 // or a unit attachment id, eg mariadb/0:mariadb/0/42 378 // The host parameter passed into this method is the application name, any of whose units we are interested in. 379 pattern := fmt.Sprintf("^%s(/%s)?:%s(/%s)?/.*", mb.docID(host.Id()), names.NumberSnippet, host.Id(), names.NumberSnippet) 380 members := bson.D{{"_id", bson.D{{"$regex", pattern}}}} 381 prefix := fmt.Sprintf("%s(/%s)?:%s(/%s)?/.*", host.Id(), names.NumberSnippet, host.Id(), names.NumberSnippet) 382 matchExp := regexp.MustCompile(prefix) 383 filter := func(id interface{}) bool { 384 k, err := mb.strictLocalID(id.(string)) 385 if err != nil { 386 return false 387 } 388 matches := matchExp.FindStringSubmatch(k) 389 return len(matches) == 3 && matches[1] == matches[2] 390 } 391 return newLifecycleWatcher(mb, collection, members, filter, nil) 392 } 393 394 // WatchApplications returns a StringsWatcher that notifies of changes to 395 // the lifecycles of the applications in the model. 396 func (st *State) WatchApplications() StringsWatcher { 397 return newLifecycleWatcher(st, applicationsC, nil, isLocalID(st), nil) 398 } 399 400 // WatchRemoteApplications returns a StringsWatcher that notifies of changes to 401 // the lifecycles of the remote applications in the model. 402 func (st *State) WatchRemoteApplications() StringsWatcher { 403 return newLifecycleWatcher(st, remoteApplicationsC, nil, isLocalID(st), nil) 404 } 405 406 // WatchApplicationCharms notifies when application charm URLs change. 407 // TODO(wallyworld) - use a filter to only trigger on charm URL changes. 408 func (st *State) WatchApplicationCharms() StringsWatcher { 409 return newCollectionWatcher(st, colWCfg{col: applicationsC}) 410 } 411 412 // WatchUnits notifies when units change. 413 func (st *State) WatchUnits() StringsWatcher { 414 return newCollectionWatcher(st, colWCfg{col: unitsC}) 415 } 416 417 // WatchMachines notifies when machines change. 418 func (st *State) WatchMachines() StringsWatcher { 419 return newLifecycleWatcher(st, machinesC, nil, isLocalID(st), nil) 420 } 421 422 // WatchCharms notifies when charms change. 423 func (st *State) WatchCharms() StringsWatcher { 424 return newCollectionWatcher(st, colWCfg{col: charmsC}) 425 } 426 427 // WatchStorageAttachments returns a StringsWatcher that notifies of 428 // changes to the lifecycles of all storage instances attached to the 429 // specified unit. 430 func (sb *storageBackend) WatchStorageAttachments(unit names.UnitTag) StringsWatcher { 431 members := bson.D{{"unitid", unit.Id()}} 432 prefix := unitGlobalKey(unit.Id()) + "#" 433 filter := func(id interface{}) bool { 434 k, err := sb.mb.strictLocalID(id.(string)) 435 if err != nil { 436 return false 437 } 438 return strings.HasPrefix(k, prefix) 439 } 440 tr := func(id string) string { 441 // Transform storage attachment document ID to storage ID. 442 return id[len(prefix):] 443 } 444 return newLifecycleWatcher(sb.mb, storageAttachmentsC, members, filter, tr) 445 } 446 447 // WatchUnits returns a StringsWatcher that notifies of changes to the 448 // lifecycles of units of a. 449 func (a *Application) WatchUnits() StringsWatcher { 450 members := bson.D{{"application", a.doc.Name}} 451 prefix := a.doc.Name + "/" 452 filter := func(unitDocID interface{}) bool { 453 unitName, err := a.st.strictLocalID(unitDocID.(string)) 454 if err != nil { 455 return false 456 } 457 return strings.HasPrefix(unitName, prefix) 458 } 459 return newLifecycleWatcher(a.st, unitsC, members, filter, nil) 460 } 461 462 // WatchScale returns a new NotifyWatcher watching for 463 // changes to the specified application's scale value. 464 func (a *Application) WatchScale() NotifyWatcher { 465 currentScale := -1 466 filter := func(id interface{}) bool { 467 k, err := a.st.strictLocalID(id.(string)) 468 if err != nil { 469 return false 470 } 471 if k != a.doc.Name { 472 return false 473 } 474 applications, closer := a.st.db().GetCollection(applicationsC) 475 defer closer() 476 477 var scaleField = bson.D{{"scale", 1}} 478 var doc *applicationDoc 479 if err := applications.FindId(k).Select(scaleField).One(&doc); err != nil { 480 return false 481 } 482 match := doc.DesiredScale != currentScale 483 currentScale = doc.DesiredScale 484 return match 485 } 486 return newNotifyCollWatcher(a.st, applicationsC, filter) 487 } 488 489 // WatchRelations returns a StringsWatcher that notifies of changes to the 490 // lifecycles of relations involving a. 491 func (a *Application) WatchRelations() StringsWatcher { 492 return watchApplicationRelations(a.st, a.doc.Name) 493 } 494 495 // WatchRelations returns a StringsWatcher that notifies of changes to the 496 // lifecycles of relations involving a. 497 func (a *RemoteApplication) WatchRelations() StringsWatcher { 498 return watchApplicationRelations(a.st, a.doc.Name) 499 } 500 501 func watchApplicationRelations(backend modelBackend, applicationName string) StringsWatcher { 502 prefix := applicationName + ":" 503 infix := " " + prefix 504 filter := func(id interface{}) bool { 505 k, err := backend.strictLocalID(id.(string)) 506 if err != nil { 507 return false 508 } 509 return strings.HasPrefix(k, prefix) || strings.Contains(k, infix) 510 } 511 members := bson.D{{"endpoints.applicationname", applicationName}} 512 return newRelationLifeSuspendedWatcher(backend, members, filter, nil) 513 } 514 515 // WatchModelMachineStartTimes watches the non-container machines in the model 516 // for changes to the Life or AgentStartTime fields and reports them as a batch 517 // after the specified quiesceInterval time has passed without seeing any new 518 // change events. 519 func (st *State) WatchModelMachineStartTimes(quiesceInterval time.Duration) StringsWatcher { 520 return newModelMachineStartTimeWatcher(st, st.clock(), quiesceInterval) 521 } 522 523 type modelMachineStartTimeFieldDoc struct { 524 Id string `bson:"_id"` 525 Life Life `bson:"life"` 526 AgentStartedAt time.Time `bson:"agent-started-at"` 527 } 528 529 var ( 530 notContainerQuery = bson.D{{"$or", []bson.D{ 531 {{"containertype", ""}}, 532 {{"containertype", bson.D{{"$exists", false}}}}, 533 }}} 534 535 modelMachineStartTimeFields = bson.D{ 536 {"_id", 1}, {"life", 1}, {"agent-started-at", 1}, 537 } 538 ) 539 540 type modelMachineStartTimeWatcher struct { 541 commonWatcher 542 outCh chan []string 543 544 clk clock.Clock 545 quiesceInterval time.Duration 546 seenDocs map[string]modelMachineStartTimeFieldDoc 547 } 548 549 func newModelMachineStartTimeWatcher(backend modelBackend, clk clock.Clock, quiesceInterval time.Duration) StringsWatcher { 550 w := &modelMachineStartTimeWatcher{ 551 commonWatcher: newCommonWatcher(backend), 552 outCh: make(chan []string), 553 clk: clk, 554 quiesceInterval: quiesceInterval, 555 seenDocs: make(map[string]modelMachineStartTimeFieldDoc), 556 } 557 558 w.tomb.Go(func() error { 559 defer close(w.outCh) 560 return w.loop() 561 }) 562 return w 563 } 564 565 // Changes returns the event channel for the watcher. 566 func (w *modelMachineStartTimeWatcher) Changes() <-chan []string { 567 return w.outCh 568 } 569 570 func (w *modelMachineStartTimeWatcher) loop() error { 571 docWatcher := newCollectionWatcher(w.backend, colWCfg{col: machinesC}) 572 defer func() { _ = docWatcher.Stop() }() 573 574 var ( 575 timer = w.clk.NewTimer(w.quiesceInterval) 576 timerArmed = true 577 // unprocessedDocs is a list of document IDs that need to be processed 578 // with a deadline they must be sent by. 579 unprocessedDocs = make(map[string]time.Time) 580 outCh chan []string 581 changeSet []string 582 ) 583 defer func() { _ = timer.Stop() }() 584 585 // Collect and initial set of machine IDs; this makes the worker 586 // compatible with other workers that expect the full state to be 587 // immediately emitted once the worker starts. 588 initialSet, err := w.initialMachineSet() 589 if err != nil { 590 return errors.Trace(err) 591 } 592 changeSet = initialSet.Values() 593 outCh = w.outCh 594 595 for { 596 select { 597 case <-w.tomb.Dying(): 598 return tomb.ErrDying 599 case <-w.watcher.Dead(): 600 return stateWatcherDeadError(w.watcher.Err()) 601 case changes := <-docWatcher.Changes(): 602 if len(changes) == 0 { 603 continue 604 } 605 for _, docID := range changes { 606 // filter out doc IDs that correspond to containers 607 if strings.ContainsRune(docID, '/') { 608 continue 609 } 610 id := w.backend.docID(docID) 611 if _, ok := unprocessedDocs[id]; ok { 612 continue 613 } 614 unprocessedDocs[id] = w.clk.Now().Add(w.quiesceInterval) 615 } 616 617 // Restart the timer if currently stopped. 618 if !timerArmed { 619 _ = timer.Reset(w.quiesceInterval) 620 timerArmed = true 621 } 622 case <-timer.Chan(): 623 timerArmed = false 624 if len(unprocessedDocs) == 0 { 625 continue // nothing to process 626 } 627 628 visible := make(set.Strings) 629 now := w.clk.Now() 630 var next time.Time 631 hasNext := false 632 for k, due := range unprocessedDocs { 633 if due.After(now) { 634 if !hasNext || due.Before(next) { 635 hasNext = true 636 next = due 637 } 638 continue 639 } 640 delete(unprocessedDocs, k) 641 visible.Add(k) 642 } 643 if hasNext { 644 _ = timer.Reset(next.Sub(now)) 645 timerArmed = true 646 } 647 648 changedIDs, err := w.processChanges(visible) 649 if err != nil { 650 return err 651 } else if changedIDs.IsEmpty() { 652 continue // nothing to report 653 } 654 655 if len(changeSet) == 0 { 656 changeSet = changedIDs.Values() 657 outCh = w.outCh 658 } else { 659 // Append new set of changes to the not yet consumed changeset 660 changeSet = append(changeSet, changedIDs.Values()...) 661 } 662 case outCh <- changeSet: 663 changeSet = changeSet[:0] 664 outCh = nil 665 } 666 } 667 } 668 669 func (w *modelMachineStartTimeWatcher) initialMachineSet() (set.Strings, error) { 670 coll, closer := w.db.GetCollection(machinesC) 671 defer closer() 672 673 // Select the fields we need from documents that are not referring to 674 // containers. 675 iter := coll.Find(notContainerQuery).Select(modelMachineStartTimeFields).Iter() 676 677 var ( 678 doc modelMachineStartTimeFieldDoc 679 ids = make(set.Strings) 680 ) 681 for iter.Next(&doc) { 682 id := w.backend.localID(doc.Id) 683 ids.Add(id) 684 if doc.Life != Dead { 685 w.seenDocs[id] = doc 686 } 687 } 688 return ids, iter.Close() 689 } 690 691 func (w *modelMachineStartTimeWatcher) processChanges(pendingDocs set.Strings) (set.Strings, error) { 692 coll, closer := w.db.GetCollection(machinesC) 693 defer closer() 694 695 // Select the fields we need from the changed documents that are not 696 // referring to containers. 697 iter := coll.Find( 698 append( 699 bson.D{{"_id", bson.D{{"$in", pendingDocs.Values()}}}}, 700 notContainerQuery..., 701 ), 702 ).Select(modelMachineStartTimeFields).Iter() 703 704 var ( 705 doc modelMachineStartTimeFieldDoc 706 ids = make(set.Strings) 707 notFoundDocs = set.NewStrings(pendingDocs.Values()...) 708 ) 709 for iter.Next(&doc) { 710 id := w.backend.localID(doc.Id) 711 old, exists := w.seenDocs[id] 712 if !exists || old.Life != doc.Life || old.AgentStartedAt != doc.AgentStartedAt { 713 w.seenDocs[id] = doc 714 ids.Add(id) 715 } 716 717 // If the machine is now dead we won't see a change for it again 718 // and therefore can permanently remove its entry from docHash 719 if doc.Life == Dead { 720 delete(w.seenDocs, id) 721 } 722 723 notFoundDocs.Remove(doc.Id) 724 } 725 726 // Assume that any doc in the notFound list belongs to a dead machine 727 // that has been reaped from the DB. 728 for docId := range notFoundDocs { 729 id := w.backend.localID(docId) 730 ids.Add(id) 731 delete(w.seenDocs, id) 732 } 733 734 return ids, iter.Close() 735 } 736 737 // WatchModelMachines returns a StringsWatcher that notifies of changes to 738 // the lifecycles of the machines (but not containers) in the model. 739 func (st *State) WatchModelMachines() StringsWatcher { 740 filter := func(id interface{}) bool { 741 k, err := st.strictLocalID(id.(string)) 742 if err != nil { 743 return false 744 } 745 return !strings.Contains(k, "/") 746 } 747 return newLifecycleWatcher(st, machinesC, notContainerQuery, filter, nil) 748 } 749 750 // WatchContainers returns a StringsWatcher that notifies of changes to the 751 // lifecycles of containers of the specified type on a machine. 752 func (m *Machine) WatchContainers(ctype instance.ContainerType) StringsWatcher { 753 isChild := fmt.Sprintf("^%s/%s/%s$", m.doc.DocID, ctype, names.NumberSnippet) 754 return m.containersWatcher(isChild) 755 } 756 757 // WatchAllContainers returns a StringsWatcher that notifies of changes to the 758 // lifecycles of all containers on a machine. 759 func (m *Machine) WatchAllContainers() StringsWatcher { 760 isChild := fmt.Sprintf("^%s/%s/%s$", m.doc.DocID, names.ContainerTypeSnippet, names.NumberSnippet) 761 return m.containersWatcher(isChild) 762 } 763 764 func (m *Machine) containersWatcher(isChildRegexp string) StringsWatcher { 765 members := bson.D{{"_id", bson.D{{"$regex", isChildRegexp}}}} 766 compiled := regexp.MustCompile(isChildRegexp) 767 filter := func(key interface{}) bool { 768 k := key.(string) 769 _, err := m.st.strictLocalID(k) 770 if err != nil { 771 return false 772 } 773 return compiled.MatchString(k) 774 } 775 return newLifecycleWatcher(m.st, machinesC, members, filter, nil) 776 } 777 778 func newLifecycleWatcher( 779 backend modelBackend, 780 collName string, 781 members bson.D, 782 filter func(key interface{}) bool, 783 transform func(id string) string, 784 ) StringsWatcher { 785 w := &lifecycleWatcher{ 786 commonWatcher: newCommonWatcher(backend), 787 coll: collFactory(backend.db(), collName), 788 collName: collName, 789 members: members, 790 filter: filter, 791 transform: transform, 792 life: make(map[string]Life), 793 out: make(chan []string), 794 } 795 w.tomb.Go(func() error { 796 defer close(w.out) 797 return w.loop() 798 }) 799 return w 800 } 801 802 type lifeDoc struct { 803 Id string `bson:"_id"` 804 Life Life 805 } 806 807 var lifeFields = bson.D{{"_id", 1}, {"life", 1}} 808 809 // Changes returns the event channel for the LifecycleWatcher. 810 func (w *lifecycleWatcher) Changes() <-chan []string { 811 return w.out 812 } 813 814 func (w *lifecycleWatcher) initial() (set.Strings, error) { 815 coll, closer := w.coll() 816 defer closer() 817 818 ids := make(set.Strings) 819 var doc lifeDoc 820 iter := coll.Find(w.members).Select(lifeFields).Iter() 821 for iter.Next(&doc) { 822 // If no members criteria is specified, use the filter 823 // to reject any unsuitable initial elements. 824 if w.members == nil && w.filter != nil && !w.filter(doc.Id) { 825 continue 826 } 827 id := w.backend.localID(doc.Id) 828 ids.Add(id) 829 if doc.Life != Dead { 830 w.life[id] = doc.Life 831 } 832 } 833 return ids, iter.Close() 834 } 835 836 func (w *lifecycleWatcher) merge(ids set.Strings, updates map[interface{}]bool) error { 837 coll, closer := w.coll() 838 defer closer() 839 840 // Separate ids into those thought to exist and those known to be removed. 841 var changed []string 842 latest := make(map[string]Life) 843 for docID, exists := range updates { 844 switch docID := docID.(type) { 845 case string: 846 if exists { 847 changed = append(changed, docID) 848 } else { 849 latest[w.backend.localID(docID)] = Dead 850 } 851 default: 852 return errors.Errorf("id is not of type string, got %T", docID) 853 } 854 } 855 856 // Collect life states from ids thought to exist. Any that don't actually 857 // exist are ignored (we'll hear about them in the next set of updates -- 858 // all that's actually happened in that situation is that the watcher 859 // events have lagged a little behind reality). 860 iter := coll.Find(bson.D{{"_id", bson.D{{"$in", changed}}}}).Select(lifeFields).Iter() 861 var doc lifeDoc 862 for iter.Next(&doc) { 863 latest[w.backend.localID(doc.Id)] = doc.Life 864 } 865 if err := iter.Close(); err != nil { 866 return err 867 } 868 869 // Add to ids any whose life state is known to have changed. 870 for id, newLife := range latest { 871 gone := newLife == Dead 872 oldLife, known := w.life[id] 873 switch { 874 case known && gone: 875 delete(w.life, id) 876 case !known && !gone: 877 w.life[id] = newLife 878 case known && newLife != oldLife: 879 w.life[id] = newLife 880 default: 881 continue 882 } 883 ids.Add(id) 884 } 885 return nil 886 } 887 888 // ErrStateClosed is returned from watchers if their underlying 889 // state connection has been closed. 890 var ErrStateClosed = fmt.Errorf("state has been closed") 891 892 // stateWatcherDeadError processes the error received when the watcher 893 // inside a state connection dies. If the State has been closed, the 894 // watcher will have been stopped and error will be nil, so we ensure 895 // that higher level watchers return a non-nil error in that case, as 896 // watchers are not expected to die unexpectedly without an error. 897 func stateWatcherDeadError(err error) error { 898 if err != nil { 899 return err 900 } 901 return ErrStateClosed 902 } 903 904 func (w *lifecycleWatcher) loop() error { 905 in := make(chan watcher.Change) 906 w.watcher.WatchCollectionWithFilter(w.collName, in, w.filter) 907 defer w.watcher.UnwatchCollection(w.collName, in) 908 ids, err := w.initial() 909 if err != nil { 910 return err 911 } 912 out := w.out 913 for { 914 values := ids.Values() 915 if w.transform != nil { 916 for i, v := range values { 917 values[i] = w.transform(v) 918 } 919 } 920 select { 921 case <-w.tomb.Dying(): 922 return tomb.ErrDying 923 case <-w.watcher.Dead(): 924 return stateWatcherDeadError(w.watcher.Err()) 925 case ch := <-in: 926 updates, ok := collect(ch, in, w.tomb.Dying()) 927 if !ok { 928 return tomb.ErrDying 929 } 930 if err := w.merge(ids, updates); err != nil { 931 return err 932 } 933 if !ids.IsEmpty() { 934 out = w.out 935 } 936 case out <- values: 937 ids = make(set.Strings) 938 out = nil 939 } 940 } 941 } 942 943 // minUnitsWatcher notifies about MinUnits changes of the applications requiring 944 // a minimum number of units to be alive. The first event returned by the 945 // watcher is the set of application names requiring a minimum number of units. 946 // Subsequent events are generated when an application increases MinUnits, or when 947 // one or more units belonging to an application are destroyed. 948 type minUnitsWatcher struct { 949 commonWatcher 950 known map[string]int 951 out chan []string 952 } 953 954 var _ Watcher = (*minUnitsWatcher)(nil) 955 956 func newMinUnitsWatcher(backend modelBackend) StringsWatcher { 957 w := &minUnitsWatcher{ 958 commonWatcher: newCommonWatcher(backend), 959 known: make(map[string]int), 960 out: make(chan []string), 961 } 962 w.tomb.Go(func() error { 963 defer close(w.out) 964 return w.loop() 965 }) 966 return w 967 } 968 969 // WatchMinUnits returns a StringsWatcher for the minUnits collection 970 func (st *State) WatchMinUnits() StringsWatcher { 971 return newMinUnitsWatcher(st) 972 } 973 974 func (w *minUnitsWatcher) initial() (set.Strings, error) { 975 applicationnames := make(set.Strings) 976 var doc minUnitsDoc 977 newMinUnits, closer := w.db.GetCollection(minUnitsC) 978 defer closer() 979 980 iter := newMinUnits.Find(nil).Iter() 981 for iter.Next(&doc) { 982 w.known[doc.ApplicationName] = doc.Revno 983 applicationnames.Add(doc.ApplicationName) 984 } 985 return applicationnames, iter.Close() 986 } 987 988 func (w *minUnitsWatcher) merge(applicationnames set.Strings, change watcher.Change) error { 989 applicationname := w.backend.localID(change.Id.(string)) 990 if change.Revno < 0 { 991 delete(w.known, applicationname) 992 applicationnames.Remove(applicationname) 993 return nil 994 } 995 doc := minUnitsDoc{} 996 newMinUnits, closer := w.db.GetCollection(minUnitsC) 997 defer closer() 998 if err := newMinUnits.FindId(change.Id).One(&doc); err != nil { 999 return err 1000 } 1001 revno, known := w.known[applicationname] 1002 w.known[applicationname] = doc.Revno 1003 if !known || doc.Revno > revno { 1004 applicationnames.Add(applicationname) 1005 } 1006 return nil 1007 } 1008 1009 func (w *minUnitsWatcher) loop() (err error) { 1010 ch := make(chan watcher.Change) 1011 w.watcher.WatchCollectionWithFilter(minUnitsC, ch, isLocalID(w.backend)) 1012 defer w.watcher.UnwatchCollection(minUnitsC, ch) 1013 applicationnames, err := w.initial() 1014 if err != nil { 1015 return err 1016 } 1017 out := w.out 1018 for { 1019 select { 1020 case <-w.tomb.Dying(): 1021 return tomb.ErrDying 1022 case <-w.watcher.Dead(): 1023 return stateWatcherDeadError(w.watcher.Err()) 1024 case change := <-ch: 1025 if err = w.merge(applicationnames, change); err != nil { 1026 return err 1027 } 1028 if !applicationnames.IsEmpty() { 1029 out = w.out 1030 } 1031 case out <- applicationnames.Values(): 1032 out = nil 1033 applicationnames = set.NewStrings() 1034 } 1035 } 1036 } 1037 1038 func (w *minUnitsWatcher) Changes() <-chan []string { 1039 return w.out 1040 } 1041 1042 // scopeInfo holds a RelationScopeWatcher's last-delivered state, and any 1043 // known but undelivered changes thereto. 1044 type scopeInfo struct { 1045 base map[string]bool 1046 diff map[string]bool 1047 } 1048 1049 func (info *scopeInfo) add(name string) { 1050 if info.base[name] { 1051 delete(info.diff, name) 1052 } else { 1053 info.diff[name] = true 1054 } 1055 } 1056 1057 func (info *scopeInfo) remove(name string) { 1058 if info.base[name] { 1059 info.diff[name] = false 1060 } else { 1061 delete(info.diff, name) 1062 } 1063 } 1064 1065 func (info *scopeInfo) commit() { 1066 for name, change := range info.diff { 1067 if change { 1068 info.base[name] = true 1069 } else { 1070 delete(info.base, name) 1071 } 1072 } 1073 info.diff = map[string]bool{} 1074 } 1075 1076 func (info *scopeInfo) hasChanges() bool { 1077 return len(info.diff) > 0 1078 } 1079 1080 func (info *scopeInfo) changes() *RelationScopeChange { 1081 ch := &RelationScopeChange{} 1082 for name, change := range info.diff { 1083 if change { 1084 ch.Entered = append(ch.Entered, name) 1085 } else { 1086 ch.Left = append(ch.Left, name) 1087 } 1088 } 1089 return ch 1090 } 1091 1092 var _ Watcher = (*RelationScopeWatcher)(nil) 1093 1094 // RelationScopeChange contains information about units that have 1095 // entered or left a particular scope. 1096 type RelationScopeChange struct { 1097 Entered []string 1098 Left []string 1099 } 1100 1101 // RelationScopeWatcher observes changes to the set of units 1102 // in a particular relation scope. 1103 type RelationScopeWatcher struct { 1104 commonWatcher 1105 prefix string 1106 ignore string 1107 out chan *RelationScopeChange 1108 } 1109 1110 func newRelationScopeWatcher(backend modelBackend, scope, ignore string) *RelationScopeWatcher { 1111 w := &RelationScopeWatcher{ 1112 commonWatcher: newCommonWatcher(backend), 1113 prefix: scope + "#", 1114 ignore: ignore, 1115 out: make(chan *RelationScopeChange), 1116 } 1117 w.tomb.Go(func() error { 1118 defer close(w.out) 1119 return w.loop() 1120 }) 1121 return w 1122 } 1123 1124 // Changes returns a channel that will receive changes when units enter and 1125 // leave a relation scope. The Entered field in the first event on the channel 1126 // holds the initial state. 1127 func (w *RelationScopeWatcher) Changes() <-chan *RelationScopeChange { 1128 return w.out 1129 } 1130 1131 // initialInfo returns an uncommitted scopeInfo with the current set of units. 1132 func (w *RelationScopeWatcher) initialInfo() (info *scopeInfo, err error) { 1133 relationScopes, closer := w.db.GetCollection(relationScopesC) 1134 defer closer() 1135 1136 docs := []relationScopeDoc{} 1137 sel := bson.D{ 1138 {"key", bson.D{{"$regex", "^" + w.prefix}}}, 1139 {"departing", bson.D{{"$ne", true}}}, 1140 } 1141 if err = relationScopes.Find(sel).All(&docs); err != nil { 1142 return nil, err 1143 } 1144 info = &scopeInfo{ 1145 base: map[string]bool{}, 1146 diff: map[string]bool{}, 1147 } 1148 for _, doc := range docs { 1149 if name := doc.unitName(); name != w.ignore { 1150 info.add(name) 1151 } 1152 } 1153 logger.Tracef("relationScopeWatcher prefix %q initializing with %# v", 1154 w.prefix, pretty.Formatter(info)) 1155 return info, nil 1156 } 1157 1158 // mergeChanges updates info with the contents of the changes in ids. False 1159 // values are always treated as removed; true values cause the associated 1160 // document to be read, and whether it's treated as added or removed depends 1161 // on the value of the document's Departing field. 1162 func (w *RelationScopeWatcher) mergeChanges(info *scopeInfo, ids map[interface{}]bool) error { 1163 relationScopes, closer := w.db.GetCollection(relationScopesC) 1164 defer closer() 1165 1166 var existIds []string 1167 for id, exists := range ids { 1168 switch id := id.(type) { 1169 case string: 1170 if exists { 1171 existIds = append(existIds, id) 1172 } else { 1173 key, err := w.backend.strictLocalID(id) 1174 if err != nil { 1175 return errors.Trace(err) 1176 } 1177 info.remove(unitNameFromScopeKey(key)) 1178 } 1179 default: 1180 logger.Warningf("ignoring bad relation scope id: %#v", id) 1181 } 1182 } 1183 var docs []relationScopeDoc 1184 sel := bson.D{{"_id", bson.D{{"$in", existIds}}}} 1185 if err := relationScopes.Find(sel).All(&docs); err != nil { 1186 return err 1187 } 1188 for _, doc := range docs { 1189 name := doc.unitName() 1190 if doc.Departing { 1191 info.remove(name) 1192 } else if name != w.ignore { 1193 info.add(name) 1194 } 1195 } 1196 logger.Tracef("RelationScopeWatcher prefix %q merge scope to %# v from ids: %# v", 1197 w.prefix, pretty.Formatter(info), pretty.Formatter(ids)) 1198 return nil 1199 } 1200 1201 func (w *RelationScopeWatcher) loop() error { 1202 in := make(chan watcher.Change) 1203 fullPrefix := w.backend.docID(w.prefix) 1204 filter := func(id interface{}) bool { 1205 return strings.HasPrefix(id.(string), fullPrefix) 1206 } 1207 w.watcher.WatchCollectionWithFilter(relationScopesC, in, filter) 1208 defer w.watcher.UnwatchCollection(relationScopesC, in) 1209 info, err := w.initialInfo() 1210 if err != nil { 1211 return err 1212 } 1213 sent := false 1214 out := w.out 1215 for { 1216 select { 1217 case <-w.watcher.Dead(): 1218 return stateWatcherDeadError(w.watcher.Err()) 1219 case <-w.tomb.Dying(): 1220 return tomb.ErrDying 1221 case ch := <-in: 1222 latest, ok := collect(ch, in, w.tomb.Dying()) 1223 if !ok { 1224 return tomb.ErrDying 1225 } 1226 if err := w.mergeChanges(info, latest); err != nil { 1227 return err 1228 } 1229 if info.hasChanges() { 1230 out = w.out 1231 } else if sent { 1232 out = nil 1233 } 1234 case out <- info.changes(): 1235 info.commit() 1236 sent = true 1237 out = nil 1238 } 1239 } 1240 } 1241 1242 // relationUnitsWatcher sends notifications of units entering and leaving the 1243 // scope of a RelationUnit, and changes to the settings of those units known 1244 // to have entered. 1245 type relationUnitsWatcher struct { 1246 commonWatcher 1247 sw *RelationScopeWatcher 1248 watching set.Strings 1249 updates chan watcher.Change 1250 appSettingsKeys []string 1251 appUpdates chan watcher.Change 1252 out chan corewatcher.RelationUnitsChange 1253 logger loggo.Logger 1254 } 1255 1256 // Watch returns a watcher that notifies of changes to counterpart units in 1257 // the relation. 1258 func (ru *RelationUnit) Watch() RelationUnitsWatcher { 1259 // TODO(jam): 2019-10-21 passing in ru.counterpartApplicationSettingsKeys() feels wrong here. 1260 // we need *some* way to give the relation units watcher an idea of what actual 1261 // relation it is watching, not just the Scope of what units have/haven't entered. 1262 // However, we need the relation ID (which isn't currently passed), and 1263 // the application names to be passed. We could 1264 // a) pass in 'RelationUnit' 1265 // b) pass just the relation id and app names separately 1266 // c) filter on what enters scope to determine what 'apps' are connected, 1267 // but I was hoping to decouple app settings from scope. 1268 return newRelationUnitsWatcher(ru.st, ru.WatchScope(), ru.counterpartApplicationSettingsKeys()) 1269 } 1270 1271 // WatchUnits returns a watcher that notifies of changes to the units of the 1272 // specified application endpoint in the relation. This method will return an error 1273 // if the endpoint is not globally scoped. 1274 func (r *Relation) WatchUnits(appName string) (RelationUnitsWatcher, error) { 1275 ep, err := r.Endpoint(appName) 1276 if err != nil { 1277 return nil, err 1278 } 1279 if ep.Scope != charm.ScopeGlobal { 1280 return nil, errors.Errorf("%q endpoint is not globally scoped", ep.Name) 1281 } 1282 rsw := watchRelationScope(r.st, r.globalScope(), ep.Role, "") 1283 appSettingsKey := relationApplicationSettingsKey(r.Id(), appName) 1284 logger.Child("relationunits").Tracef("Relation.WatchUnits(%q) watching: %q", appName, appSettingsKey) 1285 return newRelationUnitsWatcher(r.st, rsw, []string{appSettingsKey}), nil 1286 } 1287 1288 func newRelationUnitsWatcher(backend modelBackend, sw *RelationScopeWatcher, appSettingsKeys []string) RelationUnitsWatcher { 1289 w := &relationUnitsWatcher{ 1290 commonWatcher: newCommonWatcher(backend), 1291 sw: sw, 1292 appSettingsKeys: appSettingsKeys, 1293 watching: make(set.Strings), 1294 updates: make(chan watcher.Change), 1295 appUpdates: make(chan watcher.Change), 1296 out: make(chan corewatcher.RelationUnitsChange), 1297 logger: logger.Child("relationunits"), 1298 } 1299 w.tomb.Go(func() error { 1300 defer w.finish() 1301 return w.loop() 1302 }) 1303 return w 1304 } 1305 1306 // Changes returns a channel that will receive the changes to 1307 // counterpart units in a relation. The first event on the 1308 // channel holds the initial state of the relation in its 1309 // Changed field. 1310 func (w *relationUnitsWatcher) Changes() corewatcher.RelationUnitsChannel { 1311 return w.out 1312 } 1313 1314 func emptyRelationUnitsChanges(changes *corewatcher.RelationUnitsChange) bool { 1315 return len(changes.Changed)+len(changes.AppChanged)+len(changes.Departed) == 0 1316 } 1317 1318 func setRelationUnitChangeVersion(changes *corewatcher.RelationUnitsChange, key string, version int64) { 1319 name := unitNameFromScopeKey(key) 1320 settings := corewatcher.UnitSettings{Version: version} 1321 if changes.Changed == nil { 1322 changes.Changed = map[string]corewatcher.UnitSettings{} 1323 } 1324 changes.Changed[name] = settings 1325 } 1326 1327 func (w *relationUnitsWatcher) watchRelatedAppSettings(changes *corewatcher.RelationUnitsChange) error { 1328 idsAsInterface := make([]interface{}, len(w.appSettingsKeys)) 1329 for i, key := range w.appSettingsKeys { 1330 idsAsInterface[i] = w.backend.docID(key) 1331 } 1332 w.logger.Tracef("relationUnitsWatcher %q watching app keys: %v", w.sw.prefix, w.appSettingsKeys) 1333 if err := w.watcher.WatchMulti(settingsC, idsAsInterface, w.appUpdates); err != nil { 1334 return errors.Trace(err) 1335 } 1336 // WatchMulti (as a raw DB watcher) does *not* fire an initial event, it just starts the watch, which 1337 // you then use to know you can read the database without missing updates. 1338 for _, key := range w.appSettingsKeys { 1339 if err := w.mergeAppSettings(changes, key); err != nil { 1340 return errors.Trace(err) 1341 } 1342 } 1343 return nil 1344 } 1345 1346 // mergeSettings reads the relation settings node for the unit with the 1347 // supplied key, and sets a value in the Changed field keyed on the unit's 1348 // name. It returns the mgo/txn revision number of the settings node. 1349 func (w *relationUnitsWatcher) mergeSettings(changes *corewatcher.RelationUnitsChange, key string) error { 1350 version, err := readSettingsDocVersion(w.backend.db(), settingsC, key) 1351 if err != nil { 1352 w.logger.Tracef("relationUnitsWatcher %q merging key %q (not found)", w.sw.prefix, key) 1353 return errors.Trace(err) 1354 } 1355 w.logger.Tracef("relationUnitsWatcher %q merging key %q version: %d", w.sw.prefix, key, version) 1356 setRelationUnitChangeVersion(changes, key, version) 1357 return nil 1358 } 1359 1360 func (w *relationUnitsWatcher) mergeAppSettings(changes *corewatcher.RelationUnitsChange, key string) error { 1361 version, err := readSettingsDocVersion(w.backend.db(), settingsC, key) 1362 if err != nil { 1363 w.logger.Tracef("relationUnitsWatcher %q merging app key %q (not found)", w.sw.prefix, key) 1364 return errors.Trace(err) 1365 } 1366 w.logger.Tracef("relationUnitsWatcher %q merging app key %q version: %d", w.sw.prefix, key, version) 1367 if changes.AppChanged == nil { 1368 changes.AppChanged = make(map[string]int64) 1369 } 1370 // This also works for appName 1371 name := unitNameFromScopeKey(key) 1372 changes.AppChanged[name] = version 1373 return nil 1374 } 1375 1376 // mergeScope starts and stops settings watches on the units entering and 1377 // leaving the scope in the supplied RelationScopeChange event, and applies 1378 // the expressed changes to the supplied RelationUnitsChange event. 1379 func (w *relationUnitsWatcher) mergeScope(changes *corewatcher.RelationUnitsChange, c *RelationScopeChange) error { 1380 docIds := make([]interface{}, len(c.Entered)) 1381 for i, name := range c.Entered { 1382 key := w.sw.prefix + name 1383 docID := w.backend.docID(key) 1384 docIds[i] = docID 1385 } 1386 w.logger.Tracef("relationUnitsWatcher %q watching newly entered: %v, and unwatching left %v", w.sw.prefix, c.Entered, c.Left) 1387 if err := w.watcher.WatchMulti(settingsC, docIds, w.updates); err != nil { 1388 return errors.Trace(err) 1389 } 1390 for _, docID := range docIds { 1391 w.watching.Add(docID.(string)) 1392 } 1393 for _, name := range c.Entered { 1394 key := w.sw.prefix + name 1395 if err := w.mergeSettings(changes, key); err != nil { 1396 return errors.Annotatef(err, "while merging settings for %q entering relation scope", name) 1397 } 1398 changes.Departed = remove(changes.Departed, name) 1399 } 1400 for _, name := range c.Left { 1401 key := w.sw.prefix + name 1402 docID := w.backend.docID(key) 1403 changes.Departed = append(changes.Departed, name) 1404 if changes.Changed != nil { 1405 delete(changes.Changed, name) 1406 } 1407 w.watcher.Unwatch(settingsC, docID, w.updates) 1408 w.watching.Remove(docID) 1409 } 1410 w.logger.Tracef("relationUnitsWatcher %q Change updated to: %# v", w.sw.prefix, changes) 1411 return nil 1412 } 1413 1414 // remove removes s from strs and returns the modified slice. 1415 func remove(strs []string, s string) []string { 1416 for i, v := range strs { 1417 if s == v { 1418 strs[i] = strs[len(strs)-1] 1419 return strs[:len(strs)-1] 1420 } 1421 } 1422 return strs 1423 } 1424 1425 func (w *relationUnitsWatcher) finish() { 1426 watcher.Stop(w.sw, &w.tomb) 1427 for _, watchedValue := range w.watching.Values() { 1428 w.watcher.Unwatch(settingsC, watchedValue, w.updates) 1429 } 1430 for _, appKey := range w.appSettingsKeys { 1431 docID := w.backend.docID(appKey) 1432 w.watcher.Unwatch(settingsC, docID, w.appUpdates) 1433 } 1434 close(w.appUpdates) 1435 close(w.updates) 1436 close(w.out) 1437 } 1438 1439 func (w *relationUnitsWatcher) loop() (err error) { 1440 var ( 1441 gotInitialScopeWatcher bool 1442 sentInitial bool 1443 changes corewatcher.RelationUnitsChange 1444 out chan<- corewatcher.RelationUnitsChange 1445 ) 1446 // Note that w.ScopeWatcher *does* trigger an initial event, while 1447 // WatchMulti from raw document watchers does *not*. (raw database watchers 1448 // don't send initial events, logical watchers built on top of them do.) 1449 if err := w.watchRelatedAppSettings(&changes); err != nil { 1450 return errors.Trace(err) 1451 } 1452 for { 1453 select { 1454 case <-w.watcher.Dead(): 1455 return stateWatcherDeadError(w.watcher.Err()) 1456 case <-w.tomb.Dying(): 1457 return tomb.ErrDying 1458 case c, ok := <-w.sw.Changes(): 1459 if !ok { 1460 return watcher.EnsureErr(w.sw) 1461 } 1462 gotInitialScopeWatcher = true 1463 if w.logger.IsTraceEnabled() { 1464 w.logger.Tracef("relationUnitsWatcher %q scope Changes(): %# v", w.sw.prefix, pretty.Formatter(c)) 1465 } 1466 if err = w.mergeScope(&changes, c); err != nil { 1467 return err 1468 } 1469 if !sentInitial || !emptyRelationUnitsChanges(&changes) { 1470 out = w.out 1471 } else { 1472 // If we get a change that negates a previous change, cancel the event 1473 out = nil 1474 } 1475 case c := <-w.updates: 1476 id, ok := c.Id.(string) 1477 if !ok { 1478 w.logger.Warningf("relationUnitsWatcher %q ignoring bad relation scope id: %#v", w.sw.prefix, c.Id) 1479 continue 1480 } 1481 w.logger.Tracef("relationUnitsWatcher %q relation update %q", w.sw.prefix, id) 1482 if err := w.mergeSettings(&changes, id); err != nil { 1483 return errors.Annotatef(err, "relation scope id %q", id) 1484 } 1485 if gotInitialScopeWatcher && !emptyRelationUnitsChanges(&changes) { 1486 out = w.out 1487 } 1488 case c := <-w.appUpdates: 1489 id, ok := c.Id.(string) 1490 if !ok { 1491 w.logger.Warningf("relationUnitsWatcher %q ignoring bad application settings id: %#v", w.sw.prefix, c.Id) 1492 continue 1493 } 1494 w.logger.Tracef("relationUnitsWatcher %q app settings update %q", w.sw.prefix, id) 1495 if err := w.mergeAppSettings(&changes, id); err != nil { 1496 return errors.Annotatef(err, "relation scope id %q", id) 1497 } 1498 if gotInitialScopeWatcher && (!sentInitial || !emptyRelationUnitsChanges(&changes)) { 1499 out = w.out 1500 } 1501 case out <- changes: 1502 if w.logger.IsTraceEnabled() { 1503 w.logger.Tracef("relationUnitsWatcher %q sent changes %# v", w.sw.prefix, pretty.Formatter(changes)) 1504 } 1505 sentInitial = true 1506 changes = corewatcher.RelationUnitsChange{} 1507 out = nil 1508 } 1509 } 1510 } 1511 1512 // WatchLifeSuspendedStatus returns a watcher that notifies of changes to the life 1513 // or suspended status of the relation. 1514 func (r *Relation) WatchLifeSuspendedStatus() StringsWatcher { 1515 filter := func(id interface{}) bool { 1516 k, err := r.st.strictLocalID(id.(string)) 1517 if err != nil { 1518 return false 1519 } 1520 return k == r.Tag().Id() 1521 } 1522 members := bson.D{{"id", r.Id()}} 1523 return newRelationLifeSuspendedWatcher(r.st, members, filter, nil) 1524 } 1525 1526 type relationLifeSuspended struct { 1527 life Life 1528 suspended bool 1529 } 1530 1531 // relationLifeSuspendedWatcher sends notifications of changes to the life or 1532 // suspended status of specific relations. 1533 type relationLifeSuspendedWatcher struct { 1534 commonWatcher 1535 out chan []string 1536 lifeSuspended map[string]relationLifeSuspended 1537 1538 members bson.D 1539 filter func(interface{}) bool 1540 transform func(id string) string 1541 } 1542 1543 // newRelationLifeSuspendedWatcher creates a watcher that sends changes when the 1544 // life or suspended status of specific relations change. 1545 func newRelationLifeSuspendedWatcher( 1546 backend modelBackend, 1547 members bson.D, 1548 filter func(key interface{}) bool, 1549 transform func(id string) string, 1550 ) *relationLifeSuspendedWatcher { 1551 w := &relationLifeSuspendedWatcher{ 1552 commonWatcher: newCommonWatcher(backend), 1553 out: make(chan []string), 1554 members: members, 1555 filter: filter, 1556 transform: transform, 1557 lifeSuspended: make(map[string]relationLifeSuspended), 1558 } 1559 w.tomb.Go(func() error { 1560 defer close(w.out) 1561 return w.loop() 1562 }) 1563 return w 1564 } 1565 1566 func (w *relationLifeSuspendedWatcher) Changes() <-chan []string { 1567 return w.out 1568 } 1569 1570 type relationLifeSuspendedDoc struct { 1571 DocId string `bson:"_id"` 1572 Life Life `bson:"life"` 1573 Suspended bool `bson:"suspended"` 1574 } 1575 1576 var relationLifeSuspendedFields = bson.D{{"_id", 1}, {"life", 1}, {"suspended", 1}} 1577 1578 func (w *relationLifeSuspendedWatcher) initial() (set.Strings, error) { 1579 coll, closer := w.db.GetCollection(relationsC) 1580 defer closer() 1581 1582 ids := make(set.Strings) 1583 var doc relationLifeSuspendedDoc 1584 iter := coll.Find(w.members).Select(relationLifeSuspendedFields).Iter() 1585 for iter.Next(&doc) { 1586 // If no members criteria is specified, use the filter 1587 // to reject any unsuitable initial elements. 1588 if w.members == nil && w.filter != nil && !w.filter(doc.DocId) { 1589 continue 1590 } 1591 id := w.backend.localID(doc.DocId) 1592 ids.Add(id) 1593 if doc.Life != Dead { 1594 w.lifeSuspended[id] = relationLifeSuspended{life: doc.Life, suspended: doc.Suspended} 1595 } 1596 } 1597 return ids, iter.Close() 1598 } 1599 1600 func (w *relationLifeSuspendedWatcher) merge(ids set.Strings, updates map[interface{}]bool) error { 1601 coll, closer := w.db.GetCollection(relationsC) 1602 defer closer() 1603 1604 // Separate ids into those thought to exist and those known to be removed. 1605 var changed []string 1606 latest := make(map[string]relationLifeSuspended) 1607 for docID, exists := range updates { 1608 switch docID := docID.(type) { 1609 case string: 1610 if exists { 1611 changed = append(changed, docID) 1612 } else { 1613 latest[w.backend.localID(docID)] = relationLifeSuspended{life: Dead} 1614 } 1615 default: 1616 return errors.Errorf("id is not of type string, got %T", docID) 1617 } 1618 } 1619 1620 // Collect life states from ids thought to exist. Any that don't actually 1621 // exist are ignored (we'll hear about them in the next set of updates -- 1622 // all that's actually happened in that situation is that the watcher 1623 // events have lagged a little behind reality). 1624 iter := coll.Find(bson.D{{"_id", bson.D{{"$in", changed}}}}).Select(relationLifeSuspendedFields).Iter() 1625 var doc relationLifeSuspendedDoc 1626 for iter.Next(&doc) { 1627 latest[w.backend.localID(doc.DocId)] = relationLifeSuspended{life: doc.Life, suspended: doc.Suspended} 1628 } 1629 if err := iter.Close(); err != nil { 1630 return err 1631 } 1632 1633 // Add to ids any whose life state is known to have changed. 1634 for id, newLifeSuspended := range latest { 1635 gone := newLifeSuspended.life == Dead 1636 oldLifeSuspended, known := w.lifeSuspended[id] 1637 switch { 1638 case known && gone: 1639 delete(w.lifeSuspended, id) 1640 case !known && !gone: 1641 w.lifeSuspended[id] = newLifeSuspended 1642 case known && 1643 (newLifeSuspended.life != oldLifeSuspended.life || newLifeSuspended.suspended != oldLifeSuspended.suspended): 1644 w.lifeSuspended[id] = newLifeSuspended 1645 default: 1646 continue 1647 } 1648 ids.Add(id) 1649 } 1650 return nil 1651 } 1652 1653 func (w *relationLifeSuspendedWatcher) loop() error { 1654 in := make(chan watcher.Change) 1655 w.watcher.WatchCollectionWithFilter(relationsC, in, w.filter) 1656 defer w.watcher.UnwatchCollection(relationsC, in) 1657 ids, err := w.initial() 1658 if err != nil { 1659 return err 1660 } 1661 out := w.out 1662 for { 1663 values := ids.Values() 1664 if w.transform != nil { 1665 for i, v := range values { 1666 values[i] = w.transform(v) 1667 } 1668 } 1669 select { 1670 case <-w.tomb.Dying(): 1671 return tomb.ErrDying 1672 case <-w.watcher.Dead(): 1673 return stateWatcherDeadError(w.watcher.Err()) 1674 case ch := <-in: 1675 updates, ok := collect(ch, in, w.tomb.Dying()) 1676 if !ok { 1677 return tomb.ErrDying 1678 } 1679 if err := w.merge(ids, updates); err != nil { 1680 return err 1681 } 1682 if !ids.IsEmpty() { 1683 out = w.out 1684 } 1685 case out <- values: 1686 ids = make(set.Strings) 1687 out = nil 1688 } 1689 } 1690 } 1691 1692 // unitsWatcher notifies of changes to a set of units. Notifications will be 1693 // sent when units enter or leave the set, and when units in the set change 1694 // their lifecycle status. The initial event contains all units in the set, 1695 // regardless of lifecycle status; once a unit observed to be Dead or removed 1696 // has been reported, it will not be reported again. 1697 type unitsWatcher struct { 1698 commonWatcher 1699 tag string 1700 getUnits func() ([]string, error) 1701 life map[string]Life 1702 in chan watcher.Change 1703 out chan []string 1704 } 1705 1706 var _ Watcher = (*unitsWatcher)(nil) 1707 1708 // WatchSubordinateUnits returns a StringsWatcher tracking the unit's subordinate units. 1709 func (u *Unit) WatchSubordinateUnits() StringsWatcher { 1710 u = &Unit{st: u.st, doc: u.doc} 1711 coll := unitsC 1712 getUnits := func() ([]string, error) { 1713 if err := u.Refresh(); err != nil { 1714 return nil, err 1715 } 1716 return u.doc.Subordinates, nil 1717 } 1718 return newUnitsWatcher(u.st, u.Tag(), getUnits, coll, u.doc.DocID) 1719 } 1720 1721 // WatchPrincipalUnits returns a StringsWatcher tracking the machine's principal 1722 // units. 1723 func (m *Machine) WatchPrincipalUnits() StringsWatcher { 1724 m = &Machine{st: m.st, doc: m.doc} 1725 coll := machinesC 1726 getUnits := func() ([]string, error) { 1727 if err := m.Refresh(); err != nil { 1728 return nil, err 1729 } 1730 return m.doc.Principals, nil 1731 } 1732 return newUnitsWatcher(m.st, m.Tag(), getUnits, coll, m.doc.DocID) 1733 } 1734 1735 func newUnitsWatcher(backend modelBackend, tag names.Tag, getUnits func() ([]string, error), coll, id string) StringsWatcher { 1736 w := &unitsWatcher{ 1737 commonWatcher: newCommonWatcher(backend), 1738 tag: tag.String(), 1739 getUnits: getUnits, 1740 life: map[string]Life{}, 1741 in: make(chan watcher.Change), 1742 out: make(chan []string), 1743 } 1744 w.tomb.Go(func() error { 1745 defer close(w.out) 1746 return w.loop(coll, id) 1747 }) 1748 return w 1749 } 1750 1751 // Tag returns the tag of the entity whose units are being watched. 1752 func (w *unitsWatcher) Tag() string { 1753 return w.tag 1754 } 1755 1756 // Changes returns the UnitsWatcher's output channel. 1757 func (w *unitsWatcher) Changes() <-chan []string { 1758 return w.out 1759 } 1760 1761 // lifeWatchDoc holds the fields used in starting and maintaining a watch 1762 // on a entity's lifecycle. 1763 type lifeWatchDoc struct { 1764 Id string `bson:"_id"` 1765 Life Life 1766 TxnRevno int64 `bson:"txn-revno"` 1767 } 1768 1769 // lifeWatchFields specifies the fields of a lifeWatchDoc. 1770 var lifeWatchFields = bson.D{{"_id", 1}, {"life", 1}, {"txn-revno", 1}} 1771 1772 // initial returns every member of the tracked set. 1773 func (w *unitsWatcher) initial() ([]string, error) { 1774 initialNames, err := w.getUnits() 1775 if err != nil { 1776 return nil, err 1777 } 1778 return w.watchUnits(initialNames, nil) 1779 } 1780 1781 func (w *unitsWatcher) watchUnits(names, changes []string) ([]string, error) { 1782 docs := []lifeWatchDoc{} 1783 ids := make([]interface{}, len(names)) 1784 for i := range names { 1785 ids[i] = w.backend.docID(names[i]) 1786 } 1787 if err := w.watcher.WatchMulti(unitsC, ids, w.in); err != nil { 1788 logger.Tracef("error watching %q in %q: %v", ids, unitsC, err) 1789 return nil, errors.Trace(err) 1790 } 1791 logger.Tracef("watching %q ids: %q", unitsC, ids) 1792 newUnits, closer := w.db.GetCollection(unitsC) 1793 err := newUnits.Find(bson.M{"_id": bson.M{"$in": names}}).Select(lifeWatchFields).All(&docs) 1794 closer() 1795 if err != nil { 1796 return nil, errors.Trace(err) 1797 } 1798 1799 found := set.NewStrings() 1800 for _, doc := range docs { 1801 localId, err := w.backend.strictLocalID(doc.Id) 1802 if err != nil { 1803 return nil, errors.Trace(err) 1804 } 1805 found.Add(localId) 1806 if !hasString(changes, localId) { 1807 logger.Tracef("marking change for %q", localId) 1808 changes = append(changes, localId) 1809 } 1810 if doc.Life != Dead { 1811 logger.Tracef("setting life of %q to %q", localId, doc.Life) 1812 w.life[localId] = doc.Life 1813 } else { 1814 // Note(jam): 2019-01-31 This was done to match existing behavior, it is not guaranteed 1815 // to be the behavior we want. Specifically, if we see a Dead unit we will report that 1816 // it exists in the initial event. However, we stop watching because 1817 // the object is dead, so you don't get an event when the doc is 1818 // removed from the database. It seems better if we either/ 1819 // a) don't tell you about Dead documents 1820 // b) give you an event if a Dead document goes away. 1821 logger.Tracef("unwatching Dead unit: %q", localId) 1822 w.watcher.Unwatch(unitsC, doc.Id, w.in) 1823 delete(w.life, localId) 1824 } 1825 } 1826 // See if there are any entries that we wanted to watch but are actually gone 1827 for _, name := range names { 1828 if !found.Contains(name) { 1829 logger.Tracef("looking for unit %q, found it gone, Unwatching", name) 1830 if _, ok := w.life[name]; ok { 1831 // we see this doc, but it doesn't exist 1832 if !hasString(changes, name) { 1833 changes = append(changes, name) 1834 } 1835 delete(w.life, name) 1836 } 1837 w.watcher.Unwatch(unitsC, w.backend.docID(name), w.in) 1838 } 1839 } 1840 logger.Tracef("changes: %q", changes) 1841 return changes, nil 1842 } 1843 1844 // update adds to and returns changes, such that it contains the names of any 1845 // non-Dead units to have entered or left the tracked set. 1846 func (w *unitsWatcher) update(changes []string) ([]string, error) { 1847 latest, err := w.getUnits() 1848 if err != nil { 1849 return nil, err 1850 } 1851 var unknown []string 1852 for _, name := range latest { 1853 if _, found := w.life[name]; !found { 1854 unknown = append(unknown, name) 1855 } 1856 } 1857 if len(unknown) > 0 { 1858 changes, err = w.watchUnits(unknown, changes) 1859 if err != nil { 1860 return nil, errors.Trace(err) 1861 } 1862 } 1863 for name := range w.life { 1864 if hasString(latest, name) { 1865 continue 1866 } 1867 if !hasString(changes, name) { 1868 changes = append(changes, name) 1869 } 1870 logger.Tracef("unit %q %q no longer in latest, removing watch", unitsC, name) 1871 delete(w.life, name) 1872 w.watcher.Unwatch(unitsC, w.backend.docID(name), w.in) 1873 } 1874 logger.Tracef("update reports changes: %q", changes) 1875 return changes, nil 1876 } 1877 1878 // merge adds to and returns changes, such that it contains the supplied unit 1879 // name if that unit is unknown and non-Dead, or has changed lifecycle status. 1880 func (w *unitsWatcher) merge(changes []string, name string) ([]string, error) { 1881 logger.Tracef("merging change for %q %q", unitsC, name) 1882 var doc lifeWatchDoc 1883 units, closer := w.db.GetCollection(unitsC) 1884 err := units.FindId(name).Select(lifeWatchFields).One(&doc) 1885 closer() 1886 gone := false 1887 if err == mgo.ErrNotFound { 1888 gone = true 1889 } else if err != nil { 1890 return nil, err 1891 } else if doc.Life == Dead { 1892 gone = true 1893 } 1894 life := w.life[name] 1895 switch { 1896 case gone: 1897 delete(w.life, name) 1898 logger.Tracef("document gone, unwatching %q %q", unitsC, name) 1899 w.watcher.Unwatch(unitsC, w.backend.docID(name), w.in) 1900 case life != doc.Life: 1901 logger.Tracef("updating doc life %q %q to %q", unitsC, name, doc.Life) 1902 w.life[name] = doc.Life 1903 default: 1904 return changes, nil 1905 } 1906 if !hasString(changes, name) { 1907 changes = append(changes, name) 1908 } 1909 logger.Tracef("merge reporting changes: %q", changes) 1910 return changes, nil 1911 } 1912 1913 func (w *unitsWatcher) loop(coll, id string) error { 1914 logger.Tracef("watching root channel %q %q", coll, id) 1915 rootCh := make(chan watcher.Change) 1916 w.watcher.Watch(coll, id, rootCh) 1917 defer func() { 1918 w.watcher.Unwatch(coll, id, rootCh) 1919 for name := range w.life { 1920 w.watcher.Unwatch(unitsC, w.backend.docID(name), w.in) 1921 } 1922 }() 1923 changes, err := w.initial() 1924 if err != nil { 1925 return err 1926 } 1927 out := w.out 1928 for { 1929 select { 1930 case <-w.watcher.Dead(): 1931 return stateWatcherDeadError(w.watcher.Err()) 1932 case <-w.tomb.Dying(): 1933 return tomb.ErrDying 1934 case <-rootCh: 1935 changes, err = w.update(changes) 1936 if err != nil { 1937 return err 1938 } 1939 if len(changes) > 0 { 1940 out = w.out 1941 } 1942 case c := <-w.in: 1943 localID := w.backend.localID(c.Id.(string)) 1944 changes, err = w.merge(changes, localID) 1945 if err != nil { 1946 return err 1947 } 1948 if len(changes) > 0 { 1949 out = w.out 1950 } 1951 case out <- changes: 1952 logger.Tracef("watcher reported changes: %q", changes) 1953 out = nil 1954 changes = nil 1955 } 1956 } 1957 } 1958 1959 // WatchInstanceData returns a watcher for observing changes to a machine's instance data. 1960 func (m *Machine) WatchInstanceData() NotifyWatcher { 1961 return newEntityWatcher(m.st, instanceDataC, m.doc.DocID) 1962 } 1963 1964 // WatchControllerInfo returns a StringsWatcher for the controllers collection 1965 func (st *State) WatchControllerInfo() StringsWatcher { 1966 return newCollectionWatcher(st, colWCfg{col: controllerNodesC}) 1967 } 1968 1969 // WatchControllerConfig returns a NotifyWatcher for controller settings. 1970 func (st *State) WatchControllerConfig() NotifyWatcher { 1971 return newEntityWatcher(st, controllersC, ControllerSettingsGlobalKey) 1972 } 1973 1974 // Watch returns a watcher for observing changes to a controller service. 1975 func (c *CloudService) Watch() NotifyWatcher { 1976 return newEntityWatcher(c.st, cloudServicesC, c.doc.DocID) 1977 } 1978 1979 // Watch returns a watcher for observing changes to a machine. 1980 func (m *Machine) Watch() NotifyWatcher { 1981 return newEntityWatcher(m.st, machinesC, m.doc.DocID) 1982 } 1983 1984 // Watch returns a watcher for observing changes to an application. 1985 func (a *Application) Watch() NotifyWatcher { 1986 return newEntityWatcher(a.st, applicationsC, a.doc.DocID) 1987 } 1988 1989 // WatchLeaderSettings returns a watcher for observing changed to an application's 1990 // leader settings. 1991 func (a *Application) WatchLeaderSettings() NotifyWatcher { 1992 docId := a.st.docID(leadershipSettingsKey(a.Name())) 1993 return newEntityWatcher(a.st, settingsC, docId) 1994 } 1995 1996 // Watch returns a watcher for observing changes to a unit. 1997 func (u *Unit) Watch() NotifyWatcher { 1998 return newEntityWatcher(u.st, unitsC, u.doc.DocID) 1999 } 2000 2001 // Watch returns a watcher for observing changes to a model. 2002 func (m *Model) Watch() NotifyWatcher { 2003 return newEntityWatcher(m.st, modelsC, m.doc.UUID) 2004 } 2005 2006 // WatchUpgradeInfo returns a watcher for observing changes to upgrade 2007 // synchronisation state. 2008 func (st *State) WatchUpgradeInfo() NotifyWatcher { 2009 return newEntityWatcher(st, upgradeInfoC, currentUpgradeId) 2010 } 2011 2012 // WatchForModelConfigChanges returns a NotifyWatcher waiting for the Model 2013 // Config to change. 2014 func (model *Model) WatchForModelConfigChanges() NotifyWatcher { 2015 return newEntityWatcher(model.st, settingsC, model.st.docID(modelGlobalKey)) 2016 } 2017 2018 // WatchCloudSpecChanges returns a NotifyWatcher waiting for the cloud 2019 // to change for the model. 2020 func (model *Model) WatchCloudSpecChanges() NotifyWatcher { 2021 return newEntityWatcher(model.st, cloudsC, model.CloudName()) 2022 } 2023 2024 // WatchModelEntityReferences returns a NotifyWatcher waiting for the Model 2025 // Entity references to change for specified model. 2026 func (st *State) WatchModelEntityReferences(mUUID string) NotifyWatcher { 2027 return newEntityWatcher(st, modelEntityRefsC, mUUID) 2028 } 2029 2030 // WatchForUnitAssignment watches for new applications that request units to be 2031 // assigned to machines. 2032 func (st *State) WatchForUnitAssignment() StringsWatcher { 2033 return newCollectionWatcher(st, colWCfg{col: assignUnitC}) 2034 } 2035 2036 // WatchAPIHostPortsForClients returns a NotifyWatcher that notifies 2037 // when the set of API addresses changes. 2038 func (st *State) WatchAPIHostPortsForClients() NotifyWatcher { 2039 return newEntityWatcher(st, controllersC, apiHostPortsKey) 2040 } 2041 2042 // WatchAPIHostPortsForAgents returns a NotifyWatcher that notifies 2043 // when the set of API addresses usable by agents changes. 2044 func (st *State) WatchAPIHostPortsForAgents() NotifyWatcher { 2045 return newEntityWatcher(st, controllersC, apiHostPortsForAgentsKey) 2046 } 2047 2048 // WatchStorageAttachment returns a watcher for observing changes 2049 // to a storage attachment. 2050 func (sb *storageBackend) WatchStorageAttachment(s names.StorageTag, u names.UnitTag) NotifyWatcher { 2051 id := storageAttachmentId(u.Id(), s.Id()) 2052 return newEntityWatcher(sb.mb, storageAttachmentsC, sb.mb.docID(id)) 2053 } 2054 2055 // WatchVolumeAttachment returns a watcher for observing changes 2056 // to a volume attachment. 2057 func (sb *storageBackend) WatchVolumeAttachment(host names.Tag, v names.VolumeTag) NotifyWatcher { 2058 id := volumeAttachmentId(host.Id(), v.Id()) 2059 return newEntityWatcher(sb.mb, volumeAttachmentsC, sb.mb.docID(id)) 2060 } 2061 2062 // WatchFilesystemAttachment returns a watcher for observing changes 2063 // to a filesystem attachment. 2064 func (sb *storageBackend) WatchFilesystemAttachment(host names.Tag, f names.FilesystemTag) NotifyWatcher { 2065 id := filesystemAttachmentId(host.Id(), f.Id()) 2066 return newEntityWatcher(sb.mb, filesystemAttachmentsC, sb.mb.docID(id)) 2067 } 2068 2069 // WatchCharmConfig returns a watcher for observing changes to the 2070 // application's charm configuration settings. The returned watcher will be 2071 // valid only while the application's charm URL is not changed. 2072 func (a *Application) WatchCharmConfig() (NotifyWatcher, error) { 2073 configKey := a.charmConfigKey() 2074 return newEntityWatcher(a.st, settingsC, a.st.docID(configKey)), nil 2075 } 2076 2077 // WatchConfigSettings returns a watcher for observing changes to the 2078 // unit's application configuration settings. The unit must have a charm URL 2079 // set before this method is called, and the returned watcher will be 2080 // valid only while the unit's charm URL is not changed. 2081 // TODO(fwereade): this could be much smarter; if it were, uniter.Filter 2082 // could be somewhat simpler. 2083 func (u *Unit) WatchConfigSettings() (NotifyWatcher, error) { 2084 if u.doc.CharmURL == nil { 2085 return nil, fmt.Errorf("unit's charm URL must be set before watching config") 2086 } 2087 charmConfigKey := applicationCharmConfigKey(u.doc.Application, u.doc.CharmURL) 2088 return newEntityWatcher(u.st, settingsC, u.st.docID(charmConfigKey)), nil 2089 } 2090 2091 // WatchApplicationConfigSettings is the same as WatchConfigSettings but 2092 // notifies on changes to application configuration not charm configuration. 2093 func (u *Unit) WatchApplicationConfigSettings() (NotifyWatcher, error) { 2094 applicationConfigKey := applicationConfigKey(u.ApplicationName()) 2095 return newEntityWatcher(u.st, settingsC, u.st.docID(applicationConfigKey)), nil 2096 } 2097 2098 // WatchConfigSettingsHash returns a watcher that yields a hash of the 2099 // unit's charm config settings whenever they are changed. The 2100 // returned watcher will be valid only while the application's charm 2101 // URL is not changed. 2102 func (u *Unit) WatchConfigSettingsHash() (StringsWatcher, error) { 2103 if u.doc.CharmURL == nil { 2104 return nil, fmt.Errorf("unit's charm URL must be set before watching config") 2105 } 2106 charmConfigKey := applicationCharmConfigKey(u.doc.Application, u.doc.CharmURL) 2107 return newSettingsHashWatcher(u.st, charmConfigKey), nil 2108 } 2109 2110 // WatchApplicationConfigSettingsHash is the same as 2111 // WatchConfigSettingsHash but watches the application's config rather 2112 // than charm configuration. Yields a hash of the application config 2113 // with each change. 2114 func (u *Unit) WatchApplicationConfigSettingsHash() (StringsWatcher, error) { 2115 applicationConfigKey := applicationConfigKey(u.ApplicationName()) 2116 return newSettingsHashWatcher(u.st, applicationConfigKey), nil 2117 } 2118 2119 // WatchMeterStatus returns a watcher observing changes that affect the meter status 2120 // of a unit. 2121 func (u *Unit) WatchMeterStatus() NotifyWatcher { 2122 return newDocWatcher(u.st, []docKey{ 2123 { 2124 meterStatusC, 2125 u.st.docID(u.globalMeterStatusKey()), 2126 }, { 2127 meterStatusC, 2128 metricsManagerKey(u.st), 2129 }, 2130 }) 2131 } 2132 2133 // WatchLXDProfileUpgradeNotifications returns a watcher that observes the status 2134 // of a lxd profile upgrade by monitoring changes on the unit machine's lxd profile 2135 // upgrade completed field that is specific to an application name. Used by 2136 // UniterAPI v9. 2137 func (m *Machine) WatchLXDProfileUpgradeNotifications(applicationName string) (StringsWatcher, error) { 2138 app, err := m.st.Application(applicationName) 2139 if err != nil { 2140 return nil, errors.Trace(err) 2141 } 2142 watchDocId := app.doc.DocID 2143 return watchInstanceCharmProfileCompatibilityData(m.st, watchDocId), nil 2144 } 2145 2146 // WatchLXDProfileUpgradeNotifications returns a watcher that observes the status 2147 // of a lxd profile upgrade by monitoring changes on the unit machine's lxd profile 2148 // upgrade completed field that is specific to itself. 2149 func (u *Unit) WatchLXDProfileUpgradeNotifications() (StringsWatcher, error) { 2150 app, err := u.Application() 2151 if err != nil { 2152 return nil, errors.Trace(err) 2153 } 2154 watchDocId := app.doc.DocID 2155 return watchInstanceCharmProfileCompatibilityData(u.st, watchDocId), nil 2156 } 2157 2158 func watchInstanceCharmProfileCompatibilityData(backend modelBackend, watchDocId string) StringsWatcher { 2159 initial := "" 2160 members := bson.D{{"_id", watchDocId}} 2161 collection := applicationsC 2162 filter := func(id interface{}) bool { 2163 return id.(string) == watchDocId 2164 } 2165 extract := func(query documentFieldWatcherQuery) (string, error) { 2166 var doc applicationDoc 2167 if err := query.One(&doc); err != nil { 2168 return "", err 2169 } 2170 return *doc.CharmURL, nil 2171 } 2172 transform := func(value string) string { 2173 return lxdprofile.NotRequiredStatus 2174 } 2175 return newDocumentFieldWatcher(backend, collection, members, initial, filter, extract, transform) 2176 } 2177 2178 // *Deprecated* Although this watcher seems fairly admirable in terms of what 2179 // it does, it unfortunately does things at the wrong level. With the 2180 // consequence of wiring up complex structures on something that wasn't intended 2181 // from the outset for it to do. 2182 // 2183 // documentFieldWatcher notifies about any changes to a document field 2184 // specifically, the watcher looks for changes to a document field, and records 2185 // the current document field (known value). If the document doesn't exist an 2186 // initialKnown value can be set for the default. 2187 // Events are generated when there are changes to a document field that is 2188 // different from the known value. So setting field multiple times won't 2189 // dispatch an event, on changes that differ will be dispatched. 2190 type documentFieldWatcher struct { 2191 commonWatcher 2192 // docId is used to select the initial interesting entities. 2193 collection string 2194 members bson.D 2195 known *string 2196 initialKnown string 2197 filter func(interface{}) bool 2198 extract func(documentFieldWatcherQuery) (string, error) 2199 transform func(string) string 2200 out chan []string 2201 } 2202 2203 // documentFieldWatcherQuery is a point of use interface, to prevent the leaking 2204 // of query interface out of the core watcher. 2205 type documentFieldWatcherQuery interface { 2206 One(result interface{}) (err error) 2207 } 2208 2209 var _ Watcher = (*documentFieldWatcher)(nil) 2210 2211 func newDocumentFieldWatcher( 2212 backend modelBackend, 2213 collection string, 2214 members bson.D, 2215 initialKnown string, 2216 filter func(interface{}) bool, 2217 extract func(documentFieldWatcherQuery) (string, error), 2218 transform func(string) string, 2219 ) StringsWatcher { 2220 w := &documentFieldWatcher{ 2221 commonWatcher: newCommonWatcher(backend), 2222 collection: collection, 2223 members: members, 2224 initialKnown: initialKnown, 2225 filter: filter, 2226 extract: extract, 2227 transform: transform, 2228 out: make(chan []string), 2229 } 2230 w.tomb.Go(func() error { 2231 defer close(w.out) 2232 return w.loop() 2233 }) 2234 return w 2235 } 2236 2237 func (w *documentFieldWatcher) initial() error { 2238 col, closer := w.db.GetCollection(w.collection) 2239 defer closer() 2240 2241 field := w.initialKnown 2242 2243 if newField, err := w.extract(col.Find(w.members)); err == nil { 2244 field = newField 2245 } 2246 w.known = &field 2247 2248 logger.Tracef("Started watching %s for %v: %q", w.collection, w.members, field) 2249 return nil 2250 } 2251 2252 func (w *documentFieldWatcher) merge(change watcher.Change) (bool, error) { 2253 // we care about change.Revno equalling -1 as we want to know about 2254 // documents being deleted. 2255 if change.Revno == -1 { 2256 // treat this as the document being deleted 2257 if w.known != nil { 2258 w.known = nil 2259 return true, nil 2260 } 2261 return false, nil 2262 } 2263 col, closer := w.db.GetCollection(w.collection) 2264 defer closer() 2265 2266 // check the field before adding it to the known value 2267 currentField, err := w.extract(col.Find(w.members)) 2268 if err != nil { 2269 if err != mgo.ErrNotFound { 2270 logger.Debugf("%s NOT mgo err not found", w.collection) 2271 return false, err 2272 } 2273 // treat this as the document being deleted 2274 if w.known != nil { 2275 w.known = nil 2276 return true, nil 2277 } 2278 return false, nil 2279 } 2280 if w.known == nil || *w.known != currentField { 2281 w.known = ¤tField 2282 2283 logger.Tracef("Changes in watching %s for %v: %q", w.collection, w.members, currentField) 2284 return true, nil 2285 } 2286 return false, nil 2287 } 2288 2289 func (w *documentFieldWatcher) loop() error { 2290 err := w.initial() 2291 if err != nil { 2292 return err 2293 } 2294 2295 ch := make(chan watcher.Change) 2296 w.watcher.WatchCollectionWithFilter(w.collection, ch, w.filter) 2297 defer w.watcher.UnwatchCollection(w.collection, ch) 2298 2299 out := w.out 2300 for { 2301 var value string 2302 if w.known != nil { 2303 value = *w.known 2304 } 2305 if w.transform != nil { 2306 value = w.transform(value) 2307 } 2308 select { 2309 case <-w.watcher.Dead(): 2310 return stateWatcherDeadError(w.watcher.Err()) 2311 case <-w.tomb.Dying(): 2312 return tomb.ErrDying 2313 case change := <-ch: 2314 isChanged, err := w.merge(change) 2315 if err != nil { 2316 return err 2317 } 2318 if isChanged { 2319 out = w.out 2320 } 2321 case out <- []string{value}: 2322 out = nil 2323 } 2324 } 2325 } 2326 2327 func (w *documentFieldWatcher) Changes() <-chan []string { 2328 return w.out 2329 } 2330 2331 // WatchUpgradeSeriesNotifications returns a watcher that observes the status of 2332 // a series upgrade by monitoring changes to its parent machine's upgrade series 2333 // lock. 2334 func (m *Machine) WatchUpgradeSeriesNotifications() (NotifyWatcher, error) { 2335 watch := newEntityWatcher(m.st, machineUpgradeSeriesLocksC, m.doc.DocID) 2336 if _, ok := <-watch.Changes(); ok { 2337 return watch, nil 2338 } 2339 2340 return nil, watcher.EnsureErr(watch) 2341 } 2342 2343 func newEntityWatcher(backend modelBackend, collName string, key interface{}) NotifyWatcher { 2344 return newDocWatcher(backend, []docKey{{collName, key}}) 2345 } 2346 2347 // docWatcher watches for changes in 1 or more mongo documents 2348 // across collections. 2349 type docWatcher struct { 2350 commonWatcher 2351 out chan struct{} 2352 } 2353 2354 var _ Watcher = (*docWatcher)(nil) 2355 2356 // docKey identifies a single item in a single collection. 2357 // It's used as a parameter to newDocWatcher to specify 2358 // which documents should be watched. 2359 type docKey struct { 2360 coll string 2361 docId interface{} 2362 } 2363 2364 // newDocWatcher returns a new docWatcher. 2365 // docKeys identifies the documents that should be watched (their id and which collection they are in) 2366 func newDocWatcher(backend modelBackend, docKeys []docKey) NotifyWatcher { 2367 w := &docWatcher{ 2368 commonWatcher: newCommonWatcher(backend), 2369 out: make(chan struct{}), 2370 } 2371 w.tomb.Go(func() error { 2372 defer close(w.out) 2373 return w.loop(docKeys) 2374 }) 2375 return w 2376 } 2377 2378 // Changes returns the event channel for the docWatcher. 2379 func (w *docWatcher) Changes() <-chan struct{} { 2380 return w.out 2381 } 2382 2383 // getTxnRevno returns the transaction revision number of the 2384 // given document id in the given collection. It is useful to enable 2385 // a watcher.Watcher to be primed with the correct revision 2386 // id. 2387 func getTxnRevno(coll mongo.Collection, id interface{}) (int64, error) { 2388 doc := struct { 2389 TxnRevno int64 `bson:"txn-revno"` 2390 }{} 2391 fields := bson.D{{"txn-revno", 1}} 2392 if err := coll.FindId(id).Select(fields).One(&doc); err == mgo.ErrNotFound { 2393 return -1, nil 2394 } else if err != nil { 2395 return 0, err 2396 } 2397 return doc.TxnRevno, nil 2398 } 2399 2400 func (w *docWatcher) loop(docKeys []docKey) error { 2401 in := make(chan watcher.Change) 2402 logger.Tracef("watching docs: %v", docKeys) 2403 for _, k := range docKeys { 2404 w.watcher.Watch(k.coll, k.docId, in) 2405 defer w.watcher.Unwatch(k.coll, k.docId, in) 2406 } 2407 // Check to see if there is a backing event that should be coalesced with the 2408 // first event 2409 if _, ok := collect(watcher.Change{}, in, w.tomb.Dying()); !ok { 2410 return tomb.ErrDying 2411 } 2412 out := w.out 2413 n := 1 2414 for { 2415 select { 2416 case <-w.tomb.Dying(): 2417 return tomb.ErrDying 2418 case <-w.watcher.Dead(): 2419 return stateWatcherDeadError(w.watcher.Err()) 2420 case ch := <-in: 2421 if _, ok := collect(ch, in, w.tomb.Dying()); !ok { 2422 return tomb.ErrDying 2423 } 2424 // TODO(quiescence): reimplement quiescence 2425 // increment the number of notifications to send. 2426 n++ 2427 out = w.out 2428 case out <- struct{}{}: 2429 n-- 2430 if n == 0 { 2431 out = nil 2432 } 2433 } 2434 } 2435 } 2436 2437 // machineUnitsWatcher notifies about assignments and lifecycle changes 2438 // for all units of a machine. 2439 // 2440 // The first event emitted contains the unit names of all units currently 2441 // assigned to the machine, irrespective of their life state. From then on, 2442 // a new event is emitted whenever a unit is assigned to or unassigned from 2443 // the machine, or the lifecycle of a unit that is currently assigned to 2444 // the machine changes. 2445 // 2446 // After a unit is found to be Dead, no further event will include it. 2447 type machineUnitsWatcher struct { 2448 commonWatcher 2449 machine *Machine 2450 out chan []string 2451 in chan watcher.Change 2452 known map[string]Life 2453 } 2454 2455 var _ Watcher = (*machineUnitsWatcher)(nil) 2456 2457 // WatchUnits returns a new StringsWatcher watching m's units. 2458 func (m *Machine) WatchUnits() StringsWatcher { 2459 return newMachineUnitsWatcher(m) 2460 } 2461 2462 func newMachineUnitsWatcher(m *Machine) StringsWatcher { 2463 w := &machineUnitsWatcher{ 2464 commonWatcher: newCommonWatcher(m.st), 2465 out: make(chan []string), 2466 in: make(chan watcher.Change), 2467 known: make(map[string]Life), 2468 machine: &Machine{st: m.st, doc: m.doc}, // Copy so it may be freely refreshed 2469 } 2470 w.tomb.Go(func() error { 2471 defer close(w.out) 2472 return w.loop() 2473 }) 2474 return w 2475 } 2476 2477 // Changes returns the event channel for w. 2478 func (w *machineUnitsWatcher) Changes() <-chan []string { 2479 return w.out 2480 } 2481 2482 func (w *machineUnitsWatcher) updateMachine(pending []string) (new []string, err error) { 2483 err = w.machine.Refresh() 2484 if err != nil { 2485 return nil, err 2486 } 2487 var unknown []string 2488 for _, unitName := range w.machine.doc.Principals { 2489 if _, ok := w.known[unitName]; !ok { 2490 unknown = append(unknown, unitName) 2491 } 2492 } 2493 if len(unknown) > 0 { 2494 pending, err = w.watchNewUnits(unknown, pending, nil) 2495 if err != nil { 2496 return nil, errors.Trace(err) 2497 } 2498 } 2499 return pending, nil 2500 } 2501 2502 // watchNewUnits sets up a watcher for all of the named units and then updates pending changes. 2503 // There is an assumption that all unitNames being passed are unknown and do not have a watch active for them. 2504 func (w *machineUnitsWatcher) watchNewUnits(unitNames, pending []string, unitColl mongo.Collection) ([]string, error) { 2505 if len(unitNames) == 0 { 2506 return pending, nil 2507 } 2508 ids := make([]interface{}, len(unitNames)) 2509 for i := range unitNames { 2510 ids[i] = w.backend.docID(unitNames[i]) 2511 } 2512 logger.Tracef("for machine %q watching new units %q", w.machine.doc.DocID, unitNames) 2513 err := w.watcher.WatchMulti(unitsC, ids, w.in) 2514 if err != nil { 2515 return nil, errors.Trace(err) 2516 } 2517 2518 if unitColl == nil { 2519 var closer SessionCloser 2520 unitColl, closer = w.db.GetCollection(unitsC) 2521 defer closer() 2522 } 2523 var doc unitDoc 2524 iter := unitColl.Find(bson.M{"_id": bson.M{"$in": unitNames}}).Iter() 2525 unknownSubs := set.NewStrings() 2526 notfound := set.NewStrings(unitNames...) 2527 for iter.Next(&doc) { 2528 notfound.Remove(doc.Name) 2529 w.known[doc.Name] = doc.Life 2530 pending = append(pending, doc.Name) 2531 // now load subordinates 2532 for _, subunitName := range doc.Subordinates { 2533 if _, subknown := w.known[subunitName]; !subknown { 2534 unknownSubs.Add(subunitName) 2535 } 2536 } 2537 } 2538 if err := iter.Close(); err != nil { 2539 return nil, errors.Trace(err) 2540 } 2541 for name := range notfound { 2542 logger.Debugf("unit %q referenced but not found", name) 2543 w.watcher.Unwatch(unitsC, w.backend.docID(name), w.in) 2544 } 2545 if !unknownSubs.IsEmpty() { 2546 pending, err = w.watchNewUnits(unknownSubs.Values(), pending, unitColl) 2547 if err != nil { 2548 return nil, errors.Trace(err) 2549 } 2550 } 2551 return pending, nil 2552 } 2553 2554 // removeWatchedUnit stops watching the unit, and all subordinates for this unit 2555 func (w *machineUnitsWatcher) removeWatchedUnit(unitName string, doc unitDoc, pending []string) ([]string, error) { 2556 logger.Tracef("machineUnitsWatcher removing unit %q for life %q", doc.Name, doc.Life) 2557 life, known := w.known[unitName] 2558 // Unit was removed or unassigned from w.machine 2559 if known { 2560 delete(w.known, unitName) 2561 docID := w.backend.docID(unitName) 2562 w.watcher.Unwatch(unitsC, docID, w.in) 2563 if life != Dead && !hasString(pending, unitName) { 2564 pending = append(pending, unitName) 2565 } 2566 for _, subunitName := range doc.Subordinates { 2567 if sublife, subknown := w.known[subunitName]; subknown { 2568 delete(w.known, subunitName) 2569 w.watcher.Unwatch(unitsC, w.backend.docID(subunitName), w.in) 2570 if sublife != Dead && !hasString(pending, subunitName) { 2571 pending = append(pending, subunitName) 2572 } 2573 } 2574 } 2575 } 2576 return pending, nil 2577 } 2578 2579 // merge checks if this unitName has been modified and if so, updates pending accordingly. 2580 // merge() should only be called for documents that are already being watched and part of known 2581 // use watchNewUnits if you have a new object 2582 func (w *machineUnitsWatcher) merge(pending []string, unitName string) (new []string, err error) { 2583 doc := unitDoc{} 2584 newUnits, closer := w.db.GetCollection(unitsC) 2585 defer closer() 2586 err = newUnits.FindId(unitName).One(&doc) 2587 if err != nil && err != mgo.ErrNotFound { 2588 return nil, errors.Trace(err) 2589 } 2590 if err == mgo.ErrNotFound || doc.Principal == "" && (doc.MachineId == "" || doc.MachineId != w.machine.doc.Id) { 2591 // We always pass the unitName because the document may be deleted, and thus not have a name on the object 2592 pending, err := w.removeWatchedUnit(unitName, doc, pending) 2593 if err != nil { 2594 return nil, errors.Trace(err) 2595 } 2596 return pending, nil 2597 } 2598 life, known := w.known[unitName] 2599 if !known { 2600 return nil, errors.Errorf("merge() called with an unknown document: %q", doc.DocID) 2601 } 2602 if life != doc.Life && !hasString(pending, doc.Name) { 2603 logger.Tracef("machineUnitsWatcher found life changed to %q => %q for %q", life, doc.Life, doc.Name) 2604 pending = append(pending, doc.Name) 2605 } 2606 w.known[doc.Name] = doc.Life 2607 unknownSubordinates := set.NewStrings() 2608 for _, subunitName := range doc.Subordinates { 2609 if _, ok := w.known[subunitName]; !ok { 2610 unknownSubordinates.Add(subunitName) 2611 } 2612 } 2613 if !unknownSubordinates.IsEmpty() { 2614 pending, err = w.watchNewUnits(unknownSubordinates.Values(), pending, nil) 2615 if err != nil { 2616 return nil, errors.Trace(err) 2617 } 2618 } 2619 return pending, nil 2620 } 2621 2622 func (w *machineUnitsWatcher) loop() error { 2623 defer func() { 2624 for unit := range w.known { 2625 w.watcher.Unwatch(unitsC, w.backend.docID(unit), w.in) 2626 } 2627 }() 2628 2629 machineCh := make(chan watcher.Change) 2630 w.watcher.Watch(machinesC, w.machine.doc.DocID, machineCh) 2631 defer w.watcher.Unwatch(machinesC, w.machine.doc.DocID, machineCh) 2632 changes, err := w.updateMachine(nil) 2633 if err != nil { 2634 return errors.Trace(err) 2635 } 2636 out := w.out 2637 for { 2638 select { 2639 case <-w.watcher.Dead(): 2640 return stateWatcherDeadError(w.watcher.Err()) 2641 case <-w.tomb.Dying(): 2642 return tomb.ErrDying 2643 case <-machineCh: 2644 changes, err = w.updateMachine(changes) 2645 if err != nil { 2646 return errors.Trace(err) 2647 } 2648 if len(changes) > 0 { 2649 out = w.out 2650 } 2651 case c := <-w.in: 2652 changes, err = w.merge(changes, w.backend.localID(c.Id.(string))) 2653 if err != nil { 2654 return errors.Trace(err) 2655 } 2656 if len(changes) > 0 { 2657 out = w.out 2658 } 2659 case out <- changes: 2660 out = nil 2661 changes = nil 2662 } 2663 } 2664 } 2665 2666 // machineAddressesWatcher notifies about changes to a machine's addresses. 2667 // 2668 // The first event emitted contains the addresses currently assigned to the 2669 // machine. From then on, a new event is emitted whenever the machine's 2670 // addresses change. 2671 type machineAddressesWatcher struct { 2672 commonWatcher 2673 machine *Machine 2674 out chan struct{} 2675 } 2676 2677 var _ Watcher = (*machineAddressesWatcher)(nil) 2678 2679 // WatchAddresses returns a new NotifyWatcher watching m's addresses. 2680 func (m *Machine) WatchAddresses() NotifyWatcher { 2681 return newMachineAddressesWatcher(m) 2682 } 2683 2684 func newMachineAddressesWatcher(m *Machine) NotifyWatcher { 2685 w := &machineAddressesWatcher{ 2686 commonWatcher: newCommonWatcher(m.st), 2687 out: make(chan struct{}), 2688 machine: &Machine{st: m.st, doc: m.doc}, // Copy so it may be freely refreshed 2689 } 2690 w.tomb.Go(func() error { 2691 defer close(w.out) 2692 return w.loop() 2693 }) 2694 return w 2695 } 2696 2697 // Changes returns the event channel for w. 2698 func (w *machineAddressesWatcher) Changes() <-chan struct{} { 2699 return w.out 2700 } 2701 2702 func (w *machineAddressesWatcher) loop() error { 2703 machineCh := make(chan watcher.Change) 2704 w.watcher.Watch(machinesC, w.machine.doc.DocID, machineCh) 2705 defer w.watcher.Unwatch(machinesC, w.machine.doc.DocID, machineCh) 2706 addresses := w.machine.Addresses() 2707 out := w.out 2708 for { 2709 select { 2710 case <-w.watcher.Dead(): 2711 return stateWatcherDeadError(w.watcher.Err()) 2712 case <-w.tomb.Dying(): 2713 return tomb.ErrDying 2714 case <-machineCh: 2715 if err := w.machine.Refresh(); err != nil { 2716 return err 2717 } 2718 newAddresses := w.machine.Addresses() 2719 if !addressesEqual(newAddresses, addresses) { 2720 addresses = newAddresses 2721 out = w.out 2722 } 2723 case out <- struct{}{}: 2724 out = nil 2725 } 2726 } 2727 } 2728 2729 // WatchCleanups starts and returns a CleanupWatcher. 2730 func (st *State) WatchCleanups() NotifyWatcher { 2731 return newNotifyCollWatcher(st, cleanupsC, isLocalID(st)) 2732 } 2733 2734 // WatchActionLogs starts and returns a StringsWatcher that 2735 // notifies on new log messages for a specified action being added. 2736 // The strings are json encoded action messages. 2737 func (st *State) WatchActionLogs(actionId string) StringsWatcher { 2738 return newActionLogsWatcher(st, actionId) 2739 } 2740 2741 // actionLogsWatcher reports new action progress messages. 2742 type actionLogsWatcher struct { 2743 commonWatcher 2744 coll func() (mongo.Collection, func()) 2745 out chan []string 2746 2747 actionId string 2748 } 2749 2750 var _ Watcher = (*actionLogsWatcher)(nil) 2751 2752 func newActionLogsWatcher(st *State, actionId string) StringsWatcher { 2753 w := &actionLogsWatcher{ 2754 commonWatcher: newCommonWatcher(st), 2755 coll: collFactory(st.db(), actionsC), 2756 out: make(chan []string), 2757 actionId: actionId, 2758 } 2759 w.tomb.Go(func() error { 2760 defer close(w.out) 2761 return w.loop() 2762 }) 2763 return w 2764 } 2765 2766 // Changes returns the event channel for w. 2767 func (w *actionLogsWatcher) Changes() <-chan []string { 2768 return w.out 2769 } 2770 2771 func (w *actionLogsWatcher) messages() ([]string, error) { 2772 // Get the initial logs. 2773 type messagesDoc struct { 2774 Messages []ActionMessage `bson:"messages"` 2775 } 2776 coll, closer := w.coll() 2777 defer closer() 2778 var doc messagesDoc 2779 err := coll.FindId(w.backend.docID(w.actionId)).Select(bson.D{{"messages", 1}}).One(&doc) 2780 if err != nil { 2781 return nil, errors.Trace(err) 2782 } 2783 var changes []string 2784 for _, m := range doc.Messages { 2785 mjson, err := json.Marshal(actions.ActionMessage{ 2786 Message: m.MessageValue, 2787 Timestamp: m.TimestampValue.UTC(), 2788 }) 2789 if err != nil { 2790 return nil, errors.Trace(err) 2791 } 2792 changes = append(changes, string(mjson)) 2793 } 2794 return changes, nil 2795 } 2796 2797 func (w *actionLogsWatcher) loop() error { 2798 in := make(chan watcher.Change) 2799 filter := func(id interface{}) bool { 2800 k, err := w.backend.strictLocalID(id.(string)) 2801 if err != nil { 2802 return false 2803 } 2804 return k == w.actionId 2805 } 2806 2807 w.watcher.WatchCollectionWithFilter(actionsC, in, filter) 2808 defer w.watcher.UnwatchCollection(actionsC, in) 2809 2810 changes, err := w.messages() 2811 if err != nil { 2812 return errors.Trace(err) 2813 } 2814 // Record how many messages already sent so we 2815 // only send new ones. 2816 var reportedCount int 2817 out := w.out 2818 2819 for { 2820 select { 2821 case <-w.watcher.Dead(): 2822 return stateWatcherDeadError(w.watcher.Err()) 2823 case <-w.tomb.Dying(): 2824 return tomb.ErrDying 2825 case <-in: 2826 messages, err := w.messages() 2827 if err != nil { 2828 return errors.Trace(err) 2829 } 2830 if len(messages) > reportedCount { 2831 out = w.out 2832 changes = messages[reportedCount:] 2833 } 2834 case out <- changes: 2835 reportedCount += len(changes) 2836 out = nil 2837 } 2838 } 2839 } 2840 2841 // collectionWatcher is a StringsWatcher that watches for changes on the 2842 // specified collection that match a filter on the id. 2843 type collectionWatcher struct { 2844 commonWatcher 2845 colWCfg 2846 source chan watcher.Change 2847 sink chan []string 2848 } 2849 2850 // colWCfg contains the parameters for watching a collection. 2851 type colWCfg struct { 2852 col string 2853 filter func(interface{}) bool 2854 idconv func(string) string 2855 2856 // If global is true the watcher won't be limited to this model. 2857 global bool 2858 2859 // Only return documents with a revno greater than revnoThreshold. The 2860 // default zero value ensures that only modified (i.e revno > 0) rather 2861 // than just created documents are returned. 2862 revnoThreshold int64 2863 } 2864 2865 // newCollectionWatcher starts and returns a new StringsWatcher configured 2866 // with the given collection and filter function 2867 func newCollectionWatcher(backend modelBackend, cfg colWCfg) StringsWatcher { 2868 if cfg.global { 2869 if cfg.filter == nil { 2870 cfg.filter = func(x interface{}) bool { 2871 return true 2872 } 2873 } 2874 } else { 2875 // Always ensure that there is at least filtering on the 2876 // model in place. 2877 backstop := isLocalID(backend) 2878 if cfg.filter == nil { 2879 cfg.filter = backstop 2880 } else { 2881 innerFilter := cfg.filter 2882 cfg.filter = func(id interface{}) bool { 2883 if !backstop(id) { 2884 return false 2885 } 2886 return innerFilter(id) 2887 } 2888 } 2889 } 2890 2891 w := &collectionWatcher{ 2892 colWCfg: cfg, 2893 commonWatcher: newCommonWatcher(backend), 2894 source: make(chan watcher.Change), 2895 sink: make(chan []string), 2896 } 2897 2898 w.tomb.Go(func() error { 2899 defer close(w.sink) 2900 defer close(w.source) 2901 return w.loop() 2902 }) 2903 2904 return w 2905 } 2906 2907 // Changes returns the event channel for this watcher 2908 func (w *collectionWatcher) Changes() <-chan []string { 2909 return w.sink 2910 } 2911 2912 // loop performs the main event loop cycle, polling for changes and 2913 // responding to Changes requests 2914 func (w *collectionWatcher) loop() error { 2915 var ( 2916 changes []string 2917 in = (<-chan watcher.Change)(w.source) 2918 out = (chan<- []string)(w.sink) 2919 ) 2920 2921 w.watcher.WatchCollectionWithFilter(w.col, w.source, w.filter) 2922 defer w.watcher.UnwatchCollection(w.col, w.source) 2923 2924 changes, err := w.initial() 2925 if err != nil { 2926 return err 2927 } 2928 2929 for { 2930 select { 2931 case <-w.tomb.Dying(): 2932 return tomb.ErrDying 2933 case <-w.watcher.Dead(): 2934 return stateWatcherDeadError(w.watcher.Err()) 2935 case ch := <-in: 2936 updates, ok := collectWhereRevnoGreaterThan(ch, in, w.tomb.Dying(), w.colWCfg.revnoThreshold) 2937 if !ok { 2938 return tomb.ErrDying 2939 } 2940 if err := w.mergeIds(&changes, updates); err != nil { 2941 return err 2942 } 2943 if len(changes) > 0 { 2944 out = w.sink 2945 } else { 2946 out = nil 2947 } 2948 case out <- changes: 2949 changes = []string{} 2950 out = nil 2951 } 2952 } 2953 } 2954 2955 // makeIdFilter constructs a predicate to filter keys that have the 2956 // prefix matching one of the passed in ActionReceivers, or returns nil 2957 // if tags is empty 2958 func makeIdFilter(backend modelBackend, marker string, receivers ...ActionReceiver) func(interface{}) bool { 2959 if len(receivers) == 0 { 2960 return nil 2961 } 2962 ensureMarkerFn := ensureSuffixFn(marker) 2963 prefixes := make([]string, len(receivers)) 2964 for ix, receiver := range receivers { 2965 prefixes[ix] = backend.docID(ensureMarkerFn(receiver.Tag().Id())) 2966 } 2967 2968 return func(key interface{}) bool { 2969 switch key.(type) { 2970 case string: 2971 for _, prefix := range prefixes { 2972 if strings.HasPrefix(key.(string), prefix) { 2973 return true 2974 } 2975 } 2976 default: 2977 watchLogger.Errorf("key is not type string, got %T", key) 2978 } 2979 return false 2980 } 2981 } 2982 2983 // initial pre-loads the id's that have already been added to the 2984 // collection that would otherwise not normally trigger the watcher 2985 func (w *collectionWatcher) initial() ([]string, error) { 2986 var ids []string 2987 var doc struct { 2988 DocId string `bson:"_id"` 2989 } 2990 coll, closer := w.db.GetCollection(w.col) 2991 defer closer() 2992 iter := coll.Find(nil).Iter() 2993 for iter.Next(&doc) { 2994 if w.filter == nil || w.filter(doc.DocId) { 2995 id := doc.DocId 2996 if !w.colWCfg.global { 2997 id = w.backend.localID(id) 2998 } 2999 if w.idconv != nil { 3000 id = w.idconv(id) 3001 } 3002 ids = append(ids, id) 3003 } 3004 } 3005 return ids, iter.Close() 3006 } 3007 3008 // mergeIds is used for merging actionId's and actionResultId's that 3009 // come in via the updates map. It cleans up the pending changes to 3010 // account for id's being removed before the watcher consumes them, 3011 // and to account for the potential overlap between the id's that were 3012 // pending before the watcher started, and the new id's detected by the 3013 // watcher. 3014 // Additionally, mergeIds strips the model UUID prefix from the id 3015 // before emitting it through the watcher. 3016 func (w *collectionWatcher) mergeIds(changes *[]string, updates map[interface{}]bool) error { 3017 return mergeIds(changes, updates, w.convertId) 3018 } 3019 3020 func (w *collectionWatcher) convertId(id string) (string, error) { 3021 if !w.colWCfg.global { 3022 // Strip off the env UUID prefix. 3023 // We only expect ids for a single model. 3024 var err error 3025 id, err = w.backend.strictLocalID(id) 3026 if err != nil { 3027 return "", errors.Trace(err) 3028 } 3029 } 3030 if w.idconv != nil { 3031 id = w.idconv(id) 3032 } 3033 return id, nil 3034 } 3035 3036 func mergeIds(changes *[]string, updates map[interface{}]bool, idconv func(string) (string, error)) error { 3037 for val, idExists := range updates { 3038 id, ok := val.(string) 3039 if !ok { 3040 return errors.Errorf("id is not of type string, got %T", val) 3041 } 3042 3043 id, err := idconv(id) 3044 if err != nil { 3045 return errors.Annotatef(err, "collection watcher") 3046 } 3047 3048 chIx, idAlreadyInChangeset := indexOf(id, *changes) 3049 if idExists { 3050 if !idAlreadyInChangeset { 3051 *changes = append(*changes, id) 3052 } 3053 } else { 3054 if idAlreadyInChangeset { 3055 // remove id from changes 3056 *changes = append((*changes)[:chIx], (*changes)[chIx+1:]...) 3057 } 3058 } 3059 } 3060 return nil 3061 } 3062 3063 func actionNotificationIdToActionId(id string) string { 3064 ix := strings.Index(id, actionMarker) 3065 if ix == -1 { 3066 return id 3067 } 3068 return id[ix+len(actionMarker):] 3069 } 3070 3071 func indexOf(find string, in []string) (int, bool) { 3072 for ix, cur := range in { 3073 if cur == find { 3074 return ix, true 3075 } 3076 } 3077 return -1, false 3078 } 3079 3080 // ensureSuffixFn returns a function that will make sure the passed in 3081 // string has the marker token at the end of it 3082 func ensureSuffixFn(marker string) func(string) string { 3083 return func(p string) string { 3084 if !strings.HasSuffix(p, marker) { 3085 p = p + marker 3086 } 3087 return p 3088 } 3089 } 3090 3091 // watchActionNotificationsFilteredBy starts and returns a StringsWatcher 3092 // that notifies on new Actions being enqueued on the ActionRecevers 3093 // being watched as well as changes to non-completed Actions. 3094 func (st *State) watchActionNotificationsFilteredBy(receivers ...ActionReceiver) StringsWatcher { 3095 return newActionNotificationWatcher(st, false, receivers...) 3096 } 3097 3098 // watchEnqueuedActionsFilteredBy starts and returns a StringsWatcher 3099 // that notifies on new Actions being enqueued on the ActionRecevers 3100 // being watched. 3101 func (st *State) watchEnqueuedActionsFilteredBy(receivers ...ActionReceiver) StringsWatcher { 3102 return newActionNotificationWatcher(st, true, receivers...) 3103 } 3104 3105 // actionNotificationWatcher is a StringsWatcher that watches for changes on the 3106 // action notification collection, but only triggers events once per action. 3107 type actionNotificationWatcher struct { 3108 commonWatcher 3109 source chan watcher.Change 3110 sink chan []string 3111 filter func(interface{}) bool 3112 // notifyPending when true will notify all pending and running actions as 3113 // initial events, but thereafter only notify on pending actions. 3114 notifyPending bool 3115 } 3116 3117 // newActionNotificationWatcher starts and returns a new StringsWatcher configured 3118 // with the given collection and filter function. notifyPending when true will notify all pending and running actions as 3119 // initial events, but thereafter only notify on pending actions. 3120 func newActionNotificationWatcher(backend modelBackend, notifyPending bool, receivers ...ActionReceiver) StringsWatcher { 3121 w := &actionNotificationWatcher{ 3122 commonWatcher: newCommonWatcher(backend), 3123 source: make(chan watcher.Change), 3124 sink: make(chan []string), 3125 filter: makeIdFilter(backend, actionMarker, receivers...), 3126 notifyPending: notifyPending, 3127 } 3128 3129 w.tomb.Go(func() error { 3130 defer close(w.sink) 3131 defer close(w.source) 3132 return w.loop() 3133 }) 3134 3135 return w 3136 } 3137 3138 // Changes returns the event channel for this watcher 3139 func (w *actionNotificationWatcher) Changes() <-chan []string { 3140 return w.sink 3141 } 3142 3143 func (w *actionNotificationWatcher) loop() error { 3144 var ( 3145 changes []string 3146 in = (<-chan watcher.Change)(w.source) 3147 out = (chan<- []string)(w.sink) 3148 ) 3149 3150 w.watcher.WatchCollectionWithFilter(actionNotificationsC, w.source, w.filter) 3151 defer w.watcher.UnwatchCollection(actionNotificationsC, w.source) 3152 3153 changes, err := w.initial() 3154 if err != nil { 3155 return err 3156 } 3157 3158 for { 3159 select { 3160 case <-w.tomb.Dying(): 3161 return tomb.ErrDying 3162 case <-w.watcher.Dead(): 3163 return stateWatcherDeadError(w.watcher.Err()) 3164 case ch := <-in: 3165 updates, ok := collect(ch, in, w.tomb.Dying()) 3166 if !ok { 3167 return tomb.ErrDying 3168 } 3169 if w.notifyPending { 3170 if err := w.filterPendingAndMergeIds(&changes, updates); err != nil { 3171 return err 3172 } 3173 } else { 3174 if err := w.mergeIds(&changes, updates); err != nil { 3175 return err 3176 } 3177 } 3178 if len(changes) > 0 { 3179 out = w.sink 3180 } 3181 case out <- changes: 3182 changes = []string{} 3183 out = nil 3184 } 3185 } 3186 } 3187 3188 func (w *actionNotificationWatcher) initial() ([]string, error) { 3189 var ids []string 3190 var doc actionNotificationDoc 3191 coll, closer := w.db.GetCollection(actionNotificationsC) 3192 defer closer() 3193 iter := coll.Find(nil).Iter() 3194 for iter.Next(&doc) { 3195 if w.filter(doc.DocId) { 3196 ids = append(ids, actionNotificationIdToActionId(doc.DocId)) 3197 } 3198 } 3199 return ids, iter.Close() 3200 } 3201 3202 // filterPendingAndMergeIds reduces the keys published to the first action notification (pending actions). 3203 func (w *actionNotificationWatcher) filterPendingAndMergeIds(changes *[]string, updates map[interface{}]bool) error { 3204 var newIDs []string 3205 for val, idExists := range updates { 3206 docID, ok := val.(string) 3207 if !ok { 3208 return errors.Errorf("id is not of type string, got %T", val) 3209 } 3210 3211 id := actionNotificationIdToActionId(docID) 3212 chIx, idAlreadyInChangeset := indexOf(id, *changes) 3213 if idExists { 3214 if !idAlreadyInChangeset { 3215 // add id to fetch from mongo 3216 newIDs = append(newIDs, w.backend.localID(docID)) 3217 } 3218 } else { 3219 if idAlreadyInChangeset { 3220 // remove id from changes 3221 *changes = append((*changes)[:chIx], (*changes)[chIx+1:]...) 3222 } 3223 } 3224 } 3225 3226 coll, closer := w.db.GetCollection(actionNotificationsC) 3227 defer closer() 3228 3229 // query for all documents that match the ids who 3230 // don't have a changed field. These are new pending actions. 3231 query := bson.D{{"_id", bson.D{{"$in", newIDs}}}} 3232 var doc actionNotificationDoc 3233 iter := coll.Find(query).Iter() 3234 for iter.Next(&doc) { 3235 if doc.Changed.IsZero() { 3236 *changes = append(*changes, actionNotificationIdToActionId(doc.DocId)) 3237 } 3238 } 3239 return iter.Close() 3240 } 3241 3242 func (w *actionNotificationWatcher) mergeIds(changes *[]string, updates map[interface{}]bool) error { 3243 return mergeIds(changes, updates, func(id string) (string, error) { 3244 return actionNotificationIdToActionId(id), nil 3245 }) 3246 } 3247 3248 // WatchControllerStatusChanges starts and returns a StringsWatcher that 3249 // notifies when the status of a controller node changes. 3250 // TODO(cherylj) Add unit tests for this, as per bug 1543408. 3251 func (st *State) WatchControllerStatusChanges() StringsWatcher { 3252 return newCollectionWatcher(st, colWCfg{ 3253 col: statusesC, 3254 filter: makeControllerIdFilter(st), 3255 }) 3256 } 3257 3258 func makeControllerIdFilter(st *State) func(interface{}) bool { 3259 initialNodes, err := st.ControllerNodes() 3260 if err != nil { 3261 logger.Debugf("unable to get controller nodes: %v", err) 3262 return nil 3263 } 3264 3265 filter := controllerIdFilter{ 3266 st: st, 3267 lastNodes: make([]string, len(initialNodes)), 3268 } 3269 for i, n := range initialNodes { 3270 filter.lastNodes[i] = n.Id() 3271 } 3272 return filter.match 3273 } 3274 3275 // controllerIdFilter is a stateful watcher filter function - if it 3276 // can't get the current controller nodes it uses the 3277 // last nodes retrieved. Since this is called from multiple 3278 // goroutines getting/updating lastNodes is protected by a mutex. 3279 type controllerIdFilter struct { 3280 mu sync.Mutex 3281 st *State 3282 lastNodes []string 3283 } 3284 3285 func (f *controllerIdFilter) nodeIds() []string { 3286 var result []string 3287 nodes, err := f.st.ControllerNodes() 3288 f.mu.Lock() 3289 if err != nil { 3290 // Most likely, things will be killed and 3291 // restarted if we hit this error. Just use 3292 // the machine list we knew about last time. 3293 logger.Debugf("unable to get controller info: %v", err) 3294 result = f.lastNodes 3295 } else { 3296 ids := make([]string, len(nodes)) 3297 for i, n := range nodes { 3298 ids[i] = n.Id() 3299 } 3300 f.lastNodes = ids 3301 result = ids 3302 } 3303 f.mu.Unlock() 3304 return result 3305 } 3306 3307 func (f *controllerIdFilter) match(key interface{}) bool { 3308 switch key.(type) { 3309 case string: 3310 nodeIds := f.nodeIds() 3311 for _, id := range nodeIds { 3312 if strings.HasSuffix(key.(string), fmt.Sprintf("m#%s", id)) { 3313 return true 3314 } 3315 // TODO(HA) - add k8s controller filter when we do k8s HA 3316 } 3317 default: 3318 watchLogger.Errorf("key is not type string, got %T", key) 3319 } 3320 return false 3321 } 3322 3323 // openedPortsWatcher notifies of changes in the openedPorts 3324 // collection 3325 type openedPortsWatcher struct { 3326 commonWatcher 3327 known map[string]int64 3328 out chan []string 3329 } 3330 3331 var _ Watcher = (*openedPortsWatcher)(nil) 3332 3333 // WatchOpenedPorts starts and returns a StringsWatcher notifying of changes to 3334 // the openedPorts collection. Reported changes have the following format: 3335 // "<machine-id>:[<subnet-CIDR>]", i.e. "0:10.20.0.0/16" or "1:" (empty subnet 3336 // ID is allowed for backwards-compatibility). 3337 func (st *State) WatchOpenedPorts() StringsWatcher { 3338 return newOpenedPortsWatcher(st) 3339 } 3340 3341 func newOpenedPortsWatcher(backend modelBackend) StringsWatcher { 3342 w := &openedPortsWatcher{ 3343 commonWatcher: newCommonWatcher(backend), 3344 known: make(map[string]int64), 3345 out: make(chan []string), 3346 } 3347 w.tomb.Go(func() error { 3348 defer close(w.out) 3349 return w.loop() 3350 }) 3351 3352 return w 3353 } 3354 3355 // Changes returns the event channel for w 3356 func (w *openedPortsWatcher) Changes() <-chan []string { 3357 return w.out 3358 } 3359 3360 func (w *openedPortsWatcher) initial() (set.Strings, error) { 3361 ports, closer := w.db.GetCollection(openedPortsC) 3362 defer closer() 3363 3364 portDocs := set.NewStrings() 3365 var doc machinePortRangesDoc 3366 iter := ports.Find(nil).Select(bson.D{{"_id", 1}, {"txn-revno", 1}}).Iter() 3367 defer iter.Close() 3368 for iter.Next(&doc) { 3369 id, err := w.backend.strictLocalID(doc.DocID) 3370 if err != nil { 3371 return nil, errors.Trace(err) 3372 } 3373 if doc.TxnRevno != -1 { 3374 w.known[id] = doc.TxnRevno 3375 } 3376 portDocs.Add(id) 3377 } 3378 return portDocs, errors.Trace(iter.Close()) 3379 } 3380 3381 func (w *openedPortsWatcher) loop() error { 3382 in := make(chan watcher.Change) 3383 changes, err := w.initial() 3384 if err != nil { 3385 return errors.Trace(err) 3386 } 3387 w.watcher.WatchCollectionWithFilter(openedPortsC, in, isLocalID(w.backend)) 3388 defer w.watcher.UnwatchCollection(openedPortsC, in) 3389 3390 out := w.out 3391 for { 3392 select { 3393 case <-w.tomb.Dying(): 3394 return tomb.ErrDying 3395 case <-w.watcher.Dead(): 3396 return stateWatcherDeadError(w.watcher.Err()) 3397 case ch := <-in: 3398 if err = w.merge(changes, ch); err != nil { 3399 return errors.Trace(err) 3400 } 3401 if !changes.IsEmpty() { 3402 out = w.out 3403 } 3404 case out <- changes.Values(): 3405 out = nil 3406 changes = set.NewStrings() 3407 } 3408 } 3409 } 3410 3411 func (w *openedPortsWatcher) merge(ids set.Strings, change watcher.Change) error { 3412 id, ok := change.Id.(string) 3413 if !ok { 3414 return errors.Errorf("id %v is not of type string, got %T", id, id) 3415 } 3416 localID, err := w.backend.strictLocalID(id) 3417 if err != nil { 3418 return errors.Trace(err) 3419 } 3420 if change.Revno < 0 { 3421 // Report the removed id. 3422 delete(w.known, localID) 3423 ids.Add(localID) 3424 return nil 3425 } 3426 openedPorts, closer := w.db.GetCollection(openedPortsC) 3427 currentRevno, err := getTxnRevno(openedPorts, id) 3428 closer() 3429 if err != nil { 3430 return err 3431 } 3432 knownRevno, isKnown := w.known[localID] 3433 w.known[localID] = currentRevno 3434 if !isKnown || currentRevno > knownRevno { 3435 // Report the unknown-so-far id. 3436 ids.Add(localID) 3437 } 3438 return nil 3439 } 3440 3441 // WatchForRebootEvent returns a notify watcher that will trigger an event 3442 // when the reboot flag is set on our machine agent, our parent machine agent 3443 // or grandparent machine agent 3444 func (m *Machine) WatchForRebootEvent() NotifyWatcher { 3445 machineIds := m.machinesToCareAboutRebootsFor() 3446 machines := set.NewStrings(machineIds...) 3447 3448 filter := func(key interface{}) bool { 3449 if id, ok := key.(string); ok { 3450 if id, err := m.st.strictLocalID(id); err == nil { 3451 return machines.Contains(id) 3452 } else { 3453 return false 3454 } 3455 } 3456 return false 3457 } 3458 return newNotifyCollWatcher(m.st, rebootC, filter) 3459 } 3460 3461 // blockDevicesWatcher notifies about changes to all block devices 3462 // associated with a machine. 3463 type blockDevicesWatcher struct { 3464 commonWatcher 3465 machineId string 3466 out chan struct{} 3467 } 3468 3469 var _ NotifyWatcher = (*blockDevicesWatcher)(nil) 3470 3471 func newBlockDevicesWatcher(backend modelBackend, machineId string) NotifyWatcher { 3472 w := &blockDevicesWatcher{ 3473 commonWatcher: newCommonWatcher(backend), 3474 machineId: machineId, 3475 out: make(chan struct{}), 3476 } 3477 w.tomb.Go(func() error { 3478 defer close(w.out) 3479 return w.loop() 3480 }) 3481 return w 3482 } 3483 3484 // Changes returns the event channel for w. 3485 func (w *blockDevicesWatcher) Changes() <-chan struct{} { 3486 return w.out 3487 } 3488 3489 func (w *blockDevicesWatcher) loop() error { 3490 docID := w.backend.docID(w.machineId) 3491 changes := make(chan watcher.Change) 3492 w.watcher.Watch(blockDevicesC, docID, changes) 3493 defer w.watcher.Unwatch(blockDevicesC, docID, changes) 3494 blockDevices, err := getBlockDevices(w.db, w.machineId) 3495 if err != nil { 3496 return errors.Trace(err) 3497 } 3498 out := w.out 3499 for { 3500 select { 3501 case <-w.watcher.Dead(): 3502 return stateWatcherDeadError(w.watcher.Err()) 3503 case <-w.tomb.Dying(): 3504 return tomb.ErrDying 3505 case <-changes: 3506 newBlockDevices, err := getBlockDevices(w.db, w.machineId) 3507 if err != nil { 3508 return errors.Trace(err) 3509 } 3510 if !reflect.DeepEqual(newBlockDevices, blockDevices) { 3511 blockDevices = newBlockDevices 3512 out = w.out 3513 } 3514 case out <- struct{}{}: 3515 out = nil 3516 } 3517 } 3518 } 3519 3520 // WatchForMigration returns a notify watcher which reports when 3521 // a migration is in progress for the model associated with the 3522 // State. 3523 func (st *State) WatchForMigration() NotifyWatcher { 3524 return newMigrationActiveWatcher(st) 3525 } 3526 3527 type migrationActiveWatcher struct { 3528 commonWatcher 3529 collName string 3530 id string 3531 sink chan struct{} 3532 } 3533 3534 func newMigrationActiveWatcher(st *State) NotifyWatcher { 3535 w := &migrationActiveWatcher{ 3536 commonWatcher: newCommonWatcher(st), 3537 collName: migrationsActiveC, 3538 id: st.ModelUUID(), 3539 sink: make(chan struct{}), 3540 } 3541 w.tomb.Go(func() error { 3542 defer close(w.sink) 3543 return w.loop() 3544 }) 3545 return w 3546 } 3547 3548 // Changes returns the event channel for this watcher. 3549 func (w *migrationActiveWatcher) Changes() <-chan struct{} { 3550 return w.sink 3551 } 3552 3553 func (w *migrationActiveWatcher) loop() error { 3554 in := make(chan watcher.Change) 3555 w.watcher.Watch(w.collName, w.id, in) 3556 defer w.watcher.Unwatch(w.collName, w.id, in) 3557 3558 // check if there are any pending changes before the first event 3559 if _, ok := collect(watcher.Change{}, in, w.tomb.Dying()); !ok { 3560 return tomb.ErrDying 3561 } 3562 out := w.sink 3563 for { 3564 select { 3565 case <-w.tomb.Dying(): 3566 return tomb.ErrDying 3567 case <-w.watcher.Dead(): 3568 return stateWatcherDeadError(w.watcher.Err()) 3569 case change := <-in: 3570 if _, ok := collect(change, in, w.tomb.Dying()); !ok { 3571 return tomb.ErrDying 3572 } 3573 out = w.sink 3574 case out <- struct{}{}: 3575 out = nil 3576 } 3577 } 3578 } 3579 3580 // WatchMigrationStatus returns a NotifyWatcher which triggers 3581 // whenever the status of latest migration for the State's model 3582 // changes. One instance can be used across migrations. The watcher 3583 // will report changes when one migration finishes and another one 3584 // begins. 3585 // 3586 // Note that this watcher does not produce an initial event if there's 3587 // never been a migration attempt for the model. 3588 func (st *State) WatchMigrationStatus() NotifyWatcher { 3589 // Watch the entire migrationsStatusC collection for migration 3590 // status updates related to the State's model. This is more 3591 // efficient and simpler than tracking the current active 3592 // migration (and changing watchers when one migration finishes 3593 // and another starts. 3594 // 3595 // This approach is safe because there are strong guarantees that 3596 // there will only be one active migration per model. The watcher 3597 // will only see changes for one migration status document at a 3598 // time for the model. 3599 return newNotifyCollWatcher(st, migrationsStatusC, isLocalID(st)) 3600 } 3601 3602 // WatchMachineRemovals returns a NotifyWatcher which triggers 3603 // whenever machine removal records are added or removed. 3604 func (st *State) WatchMachineRemovals() NotifyWatcher { 3605 return newNotifyCollWatcher(st, machineRemovalsC, isLocalID(st)) 3606 } 3607 3608 // notifyCollWatcher implements NotifyWatcher, triggering when a 3609 // change is seen in a specific collection matching the provided 3610 // filter function. 3611 type notifyCollWatcher struct { 3612 commonWatcher 3613 collName string 3614 filter func(interface{}) bool 3615 sink chan struct{} 3616 } 3617 3618 func newNotifyCollWatcher(backend modelBackend, collName string, filter func(interface{}) bool) NotifyWatcher { 3619 w := ¬ifyCollWatcher{ 3620 commonWatcher: newCommonWatcher(backend), 3621 collName: collName, 3622 filter: filter, 3623 sink: make(chan struct{}), 3624 } 3625 w.tomb.Go(func() error { 3626 defer close(w.sink) 3627 return w.loop() 3628 }) 3629 return w 3630 } 3631 3632 // Changes returns the event channel for this watcher. 3633 func (w *notifyCollWatcher) Changes() <-chan struct{} { 3634 return w.sink 3635 } 3636 3637 func (w *notifyCollWatcher) loop() error { 3638 in := make(chan watcher.Change) 3639 3640 w.watcher.WatchCollectionWithFilter(w.collName, in, w.filter) 3641 defer w.watcher.UnwatchCollection(w.collName, in) 3642 3643 // check if there are any pending changes before the first event 3644 if _, ok := collect(watcher.Change{}, in, w.tomb.Dying()); !ok { 3645 return tomb.ErrDying 3646 } 3647 out := w.sink // out set so that initial event is sent. 3648 for { 3649 select { 3650 case <-w.tomb.Dying(): 3651 return tomb.ErrDying 3652 case <-w.watcher.Dead(): 3653 return stateWatcherDeadError(w.watcher.Err()) 3654 case change := <-in: 3655 if _, ok := collect(change, in, w.tomb.Dying()); !ok { 3656 return tomb.ErrDying 3657 } 3658 out = w.sink 3659 case out <- struct{}{}: 3660 out = nil 3661 } 3662 } 3663 } 3664 3665 // WatchRemoteRelations returns a StringsWatcher that notifies of changes to 3666 // the lifecycles of the remote relations in the model. 3667 func (st *State) WatchRemoteRelations() StringsWatcher { 3668 // Use a no-op transform func to record the known ids. 3669 known := make(map[interface{}]bool) 3670 tr := func(id string) string { 3671 known[id] = true 3672 return id 3673 } 3674 3675 filter := func(id interface{}) bool { 3676 id, err := st.strictLocalID(id.(string)) 3677 if err != nil { 3678 return false 3679 } 3680 3681 // Gather the remote app names. 3682 remoteApps, closer := st.db().GetCollection(remoteApplicationsC) 3683 defer closer() 3684 3685 type remoteAppDoc struct { 3686 Name string 3687 } 3688 remoteAppNameField := bson.D{{"name", 1}} 3689 var apps []remoteAppDoc 3690 err = remoteApps.Find(nil).Select(remoteAppNameField).All(&apps) 3691 if err != nil { 3692 watchLogger.Errorf("could not lookup remote application names: %v", err) 3693 return false 3694 } 3695 remoteAppNames := set.NewStrings() 3696 for _, a := range apps { 3697 remoteAppNames.Add(a.Name) 3698 } 3699 3700 // Run a query to pickup any relations to those remote apps. 3701 relations, closer := st.db().GetCollection(relationsC) 3702 defer closer() 3703 3704 query := bson.D{ 3705 {"key", id}, 3706 {"endpoints.applicationname", bson.D{{"$in", remoteAppNames.Values()}}}, 3707 } 3708 num, err := relations.Find(query).Count() 3709 if err != nil { 3710 watchLogger.Errorf("could not lookup remote relations: %v", err) 3711 return false 3712 } 3713 // The relation (or remote app) may have been deleted, but if it has been 3714 // seen previously, return true. 3715 if num == 0 { 3716 _, seen := known[id] 3717 delete(known, id) 3718 return seen 3719 } 3720 return num > 0 3721 } 3722 return newRelationLifeSuspendedWatcher(st, nil, filter, tr) 3723 } 3724 3725 // WatchSubnets returns a StringsWatcher that notifies of changes to 3726 // the subnets in the model. 3727 func (st *State) WatchSubnets(subnetFilter func(id interface{}) bool) StringsWatcher { 3728 filter := func(id interface{}) bool { 3729 subnet, err := st.strictLocalID(id.(string)) 3730 if err != nil { 3731 return false 3732 } 3733 if subnetFilter == nil { 3734 return true 3735 } 3736 return subnetFilter(subnet) 3737 } 3738 3739 return newCollectionWatcher(st, colWCfg{ 3740 col: subnetsC, 3741 filter: filter, 3742 }) 3743 } 3744 3745 // isLocalID returns a watcher filter func that rejects ids not specific 3746 // to the supplied modelBackend. 3747 func isLocalID(st modelBackend) func(interface{}) bool { 3748 return func(id interface{}) bool { 3749 key, ok := id.(string) 3750 if !ok { 3751 return false 3752 } 3753 _, err := st.strictLocalID(key) 3754 return err == nil 3755 } 3756 } 3757 3758 // relationNetworksWatcher notifies of changes in the 3759 // relationNetworks collection, either ingress of egress. 3760 type relationNetworksWatcher struct { 3761 commonWatcher 3762 key string 3763 direction string 3764 filter func(key interface{}) bool 3765 knownTxnRev int64 3766 knownCidrs set.Strings 3767 out chan []string 3768 } 3769 3770 var _ Watcher = (*relationNetworksWatcher)(nil) 3771 3772 // WatchRelationIngressNetworks starts and returns a StringsWatcher notifying 3773 // of ingress changes to the relationNetworks collection for the relation. 3774 func (r *Relation) WatchRelationIngressNetworks() StringsWatcher { 3775 return newrelationNetworksWatcher(r.st, r.Tag().Id(), IngressDirection.String()) 3776 } 3777 3778 // WatchRelationEgressNetworks starts and returns a StringsWatcher notifying 3779 // of egress changes to the relationNetworks collection for the relation. 3780 func (r *Relation) WatchRelationEgressNetworks() StringsWatcher { 3781 return newrelationNetworksWatcher(r.st, r.Tag().Id(), EgressDirection.String()) 3782 } 3783 3784 func newrelationNetworksWatcher(st modelBackend, relationKey, direction string) StringsWatcher { 3785 filter := func(id interface{}) bool { 3786 k, err := st.strictLocalID(id.(string)) 3787 if err != nil { 3788 return false 3789 } 3790 return strings.HasPrefix(k, relationKey+":"+direction+":") 3791 } 3792 w := &relationNetworksWatcher{ 3793 commonWatcher: newCommonWatcher(st), 3794 key: relationKey, 3795 direction: direction, 3796 filter: filter, 3797 knownCidrs: set.NewStrings(), 3798 out: make(chan []string), 3799 } 3800 w.tomb.Go(func() error { 3801 defer close(w.out) 3802 return w.loop() 3803 }) 3804 3805 return w 3806 } 3807 3808 // Changes returns the event channel for watching changes 3809 // to a relation's ingress networks. 3810 func (w *relationNetworksWatcher) Changes() <-chan []string { 3811 return w.out 3812 } 3813 3814 func (w *relationNetworksWatcher) loadCIDRs() (bool, error) { 3815 coll, closer := w.db.GetCollection(relationNetworksC) 3816 defer closer() 3817 3818 var doc struct { 3819 TxnRevno int64 `bson:"txn-revno"` 3820 Id string `bson:"_id"` 3821 CIDRs []string `bson:"cidrs"` 3822 } 3823 err := coll.FindId(relationNetworkDocID(w.key, w.direction, relationNetworkAdmin)).One(&doc) 3824 if err == mgo.ErrNotFound { 3825 err = coll.FindId(relationNetworkDocID(w.key, w.direction, relationNetworkDefault)).One(&doc) 3826 } 3827 if err == mgo.ErrNotFound { 3828 // Record deleted. 3829 changed := w.knownCidrs.Size() > 0 3830 w.knownCidrs = set.NewStrings() 3831 return changed, nil 3832 } 3833 if err != nil { 3834 return false, errors.Trace(err) 3835 3836 } 3837 cidrs := w.knownCidrs 3838 if doc.TxnRevno == -1 { 3839 // Record deleted. 3840 cidrs = set.NewStrings() 3841 } 3842 if doc.TxnRevno > w.knownTxnRev { 3843 cidrs = set.NewStrings(doc.CIDRs...) 3844 } 3845 w.knownTxnRev = doc.TxnRevno 3846 changed := !cidrs.Difference(w.knownCidrs).IsEmpty() || !w.knownCidrs.Difference(cidrs).IsEmpty() 3847 w.knownCidrs = cidrs 3848 return changed, nil 3849 } 3850 3851 func (w *relationNetworksWatcher) loop() error { 3852 in := make(chan watcher.Change) 3853 w.watcher.WatchCollectionWithFilter(relationNetworksC, in, w.filter) 3854 defer w.watcher.UnwatchCollection(relationNetworksC, in) 3855 3856 var ( 3857 sentInitial bool 3858 changed bool 3859 out chan<- []string 3860 err error 3861 ) 3862 if _, err = w.loadCIDRs(); err != nil { 3863 return errors.Trace(err) 3864 } 3865 for { 3866 if !sentInitial || changed { 3867 changed = false 3868 out = w.out 3869 } 3870 select { 3871 case <-w.tomb.Dying(): 3872 return tomb.ErrDying 3873 case <-w.watcher.Dead(): 3874 return stateWatcherDeadError(w.watcher.Err()) 3875 case _, ok := <-in: 3876 if !ok { 3877 return tomb.ErrDying 3878 } 3879 if changed, err = w.loadCIDRs(); err != nil { 3880 return errors.Trace(err) 3881 } 3882 case out <- w.knownCidrs.Values(): 3883 out = nil 3884 sentInitial = true 3885 } 3886 if w.knownTxnRev == -1 { 3887 // Record deleted 3888 return tomb.ErrDying 3889 } 3890 } 3891 } 3892 3893 // externalControllersWatcher notifies about addition and removal of 3894 // external controller references. 3895 type externalControllersWatcher struct { 3896 commonWatcher 3897 coll func() (mongo.Collection, func()) 3898 out chan []string 3899 } 3900 3901 var _ Watcher = (*externalControllersWatcher)(nil) 3902 3903 func newExternalControllersWatcher(st *State) StringsWatcher { 3904 w := &externalControllersWatcher{ 3905 commonWatcher: newCommonWatcher(st), 3906 coll: collFactory(st.db(), externalControllersC), 3907 out: make(chan []string), 3908 } 3909 w.tomb.Go(func() error { 3910 defer close(w.out) 3911 return w.loop() 3912 }) 3913 return w 3914 } 3915 3916 // Changes returns the event channel for w. 3917 func (w *externalControllersWatcher) Changes() <-chan []string { 3918 return w.out 3919 } 3920 3921 func (w *externalControllersWatcher) initial() (set.Strings, error) { 3922 // Get the initial documents in the collection. 3923 type idDoc struct { 3924 Id string `bson:"_id"` 3925 } 3926 coll, closer := w.coll() 3927 defer closer() 3928 changes := make(set.Strings) 3929 iter := coll.Find(nil).Select(bson.D{{"_id", 1}}).Iter() 3930 var doc idDoc 3931 for iter.Next(&doc) { 3932 changes.Add(doc.Id) 3933 } 3934 return changes, iter.Close() 3935 } 3936 3937 func (w *externalControllersWatcher) loop() error { 3938 in := make(chan watcher.Change) 3939 w.watcher.WatchCollection(externalControllersC, in) 3940 defer w.watcher.UnwatchCollection(externalControllersC, in) 3941 3942 changes, err := w.initial() 3943 if err != nil { 3944 return errors.Trace(err) 3945 } 3946 3947 reported := make(set.Strings) 3948 out := w.out 3949 for { 3950 select { 3951 case <-w.watcher.Dead(): 3952 return stateWatcherDeadError(w.watcher.Err()) 3953 case <-w.tomb.Dying(): 3954 return tomb.ErrDying 3955 case ch := <-in: 3956 updates, ok := collect(ch, in, w.tomb.Dying()) 3957 if !ok { 3958 return tomb.ErrDying 3959 } 3960 for id, updated := range updates { 3961 id := id.(string) 3962 if updated != reported.Contains(id) { 3963 // updated and hasn't been reported, or 3964 // removed and has been reported 3965 changes.Add(id) 3966 } 3967 } 3968 if changes.Size() > 0 { 3969 out = w.out 3970 } 3971 case out <- changes.SortedValues(): 3972 out = nil 3973 for _, id := range changes.Values() { 3974 if reported.Contains(id) { 3975 reported.Remove(id) 3976 } else { 3977 reported.Add(id) 3978 } 3979 } 3980 changes = make(set.Strings) 3981 } 3982 } 3983 } 3984 3985 // WatchPodSpec returns a watcher observing changes that affect the 3986 // pod spec for an application or unit. 3987 func (m *CAASModel) WatchPodSpec(appTag names.ApplicationTag) (NotifyWatcher, error) { 3988 docKeys := []docKey{{ 3989 podSpecsC, 3990 m.st.docID(applicationGlobalKey(appTag.Id())), 3991 }} 3992 return newDocWatcher(m.st, docKeys), nil 3993 } 3994 3995 // containerAddressesWatcher notifies about changes to a unit's pod address(es). 3996 type containerAddressesWatcher struct { 3997 commonWatcher 3998 unit *Unit 3999 out chan struct{} 4000 } 4001 4002 var _ Watcher = (*containerAddressesWatcher)(nil) 4003 4004 // WatchContainerAddresses returns a new NotifyWatcher watching the unit's pod address(es). 4005 func (u *Unit) WatchContainerAddresses() NotifyWatcher { 4006 return newContainerAddressesWatcher(u) 4007 } 4008 4009 func newContainerAddressesWatcher(u *Unit) NotifyWatcher { 4010 w := &containerAddressesWatcher{ 4011 commonWatcher: newCommonWatcher(u.st), 4012 out: make(chan struct{}), 4013 unit: u, 4014 } 4015 w.tomb.Go(func() error { 4016 defer close(w.out) 4017 return w.loop() 4018 }) 4019 return w 4020 } 4021 4022 // Changes returns the event channel for w. 4023 func (w *containerAddressesWatcher) Changes() <-chan struct{} { 4024 return w.out 4025 } 4026 4027 func (w *containerAddressesWatcher) loop() error { 4028 id := w.backend.docID(w.unit.globalKey()) 4029 containerCh := make(chan watcher.Change) 4030 w.watcher.Watch(cloudContainersC, id, containerCh) 4031 defer w.watcher.Unwatch(cloudContainersC, id, containerCh) 4032 4033 var currentAddress *address 4034 container, err := w.unit.cloudContainer() 4035 if err != nil && !errors.IsNotFound(err) { 4036 return err 4037 } else if err == nil { 4038 currentAddress = container.Address 4039 } 4040 out := w.out 4041 for { 4042 select { 4043 case <-w.watcher.Dead(): 4044 return stateWatcherDeadError(w.watcher.Err()) 4045 case <-w.tomb.Dying(): 4046 return tomb.ErrDying 4047 case <-containerCh: 4048 container, err := w.unit.cloudContainer() 4049 if err != nil { 4050 return err 4051 } 4052 addressValue := func(addr *address) string { 4053 if addr == nil { 4054 return "" 4055 } 4056 return addr.Value 4057 } 4058 newAddress := container.Address 4059 if addressValue(newAddress) != addressValue(currentAddress) { 4060 currentAddress = newAddress 4061 out = w.out 4062 } 4063 case out <- struct{}{}: 4064 out = nil 4065 } 4066 } 4067 } 4068 4069 func newSettingsHashWatcher(st *State, localID string) StringsWatcher { 4070 docID := st.docID(localID) 4071 w := &hashWatcher{ 4072 commonWatcher: newCommonWatcher(st), 4073 out: make(chan []string), 4074 collection: settingsC, 4075 id: docID, 4076 hash: func() (string, error) { 4077 return hashSettings(st.db(), docID, localID) 4078 }, 4079 } 4080 w.start() 4081 return w 4082 } 4083 4084 func hashSettings(db Database, id string, name string) (string, error) { 4085 settings, closer := db.GetCollection(settingsC) 4086 defer closer() 4087 var doc settingsDoc 4088 if err := settings.FindId(id).One(&doc); err == mgo.ErrNotFound { 4089 return "", nil 4090 } else if err != nil { 4091 return "", errors.Trace(err) 4092 } 4093 // Ensure elements are in a consistent order. If any are maps, 4094 // replace them with the equivalent sorted bson.Ds. 4095 items := toSortedBsonD(doc.Settings) 4096 data, err := bson.Marshal(items) 4097 if err != nil { 4098 return "", errors.Trace(err) 4099 } 4100 hash := sha256.New() 4101 _, _ = hash.Write([]byte(name)) 4102 _, _ = hash.Write(data) 4103 return fmt.Sprintf("%x", hash.Sum(nil)), nil 4104 } 4105 4106 // WatchServiceAddressesHash returns a StringsWatcher that emits a 4107 // hash of the unit's container address whenever it changes. 4108 func (a *Application) WatchServiceAddressesHash() StringsWatcher { 4109 firstCall := true 4110 w := &hashWatcher{ 4111 commonWatcher: newCommonWatcher(a.st), 4112 out: make(chan []string), 4113 collection: cloudServicesC, 4114 id: a.st.docID(a.globalKey()), 4115 hash: func() (string, error) { 4116 result, err := hashServiceAddresses(a, firstCall) 4117 firstCall = false 4118 return result, err 4119 }, 4120 } 4121 w.start() 4122 return w 4123 } 4124 4125 // WatchConfigSettingsHash returns a watcher that yields a hash of the 4126 // application's config settings whenever they are changed. 4127 func (a *Application) WatchConfigSettingsHash() StringsWatcher { 4128 applicationConfigKey := applicationConfigKey(a.Name()) 4129 return newSettingsHashWatcher(a.st, applicationConfigKey) 4130 } 4131 4132 func hashServiceAddresses(a *Application, firstCall bool) (string, error) { 4133 service, err := a.ServiceInfo() 4134 if errors.IsNotFound(err) && firstCall { 4135 // To keep behaviour the same as 4136 // WatchServiceAddresses, we need to ignore NotFound 4137 // errors on the first call but propagate them after 4138 // that. 4139 return "", nil 4140 } 4141 if err != nil { 4142 return "", errors.Trace(err) 4143 } 4144 addresses := service.Addresses() 4145 if len(addresses) == 0 { 4146 return "", nil 4147 } 4148 address := addresses[0] 4149 hash := sha256.New() 4150 _, _ = hash.Write([]byte(address.Value)) 4151 _, _ = hash.Write([]byte(address.Type)) 4152 _, _ = hash.Write([]byte(address.Scope)) 4153 _, _ = hash.Write([]byte(address.SpaceID)) 4154 return fmt.Sprintf("%x", hash.Sum(nil)), nil 4155 } 4156 4157 type hashWatcher struct { 4158 commonWatcher 4159 collection string 4160 id string 4161 hash func() (string, error) 4162 out chan []string 4163 } 4164 4165 func (w *hashWatcher) Changes() <-chan []string { 4166 return w.out 4167 } 4168 4169 func (w *hashWatcher) start() { 4170 w.tomb.Go(func() error { 4171 defer close(w.out) 4172 return w.loop() 4173 }) 4174 } 4175 4176 func (w *hashWatcher) loop() error { 4177 changesCh := make(chan watcher.Change) 4178 w.watcher.Watch(w.collection, w.id, changesCh) 4179 defer w.watcher.Unwatch(w.collection, w.id, changesCh) 4180 4181 lastHash, err := w.hash() 4182 if err != nil { 4183 return errors.Trace(err) 4184 } 4185 out := w.out 4186 for { 4187 select { 4188 case <-w.watcher.Dead(): 4189 return stateWatcherDeadError(w.watcher.Err()) 4190 case <-w.tomb.Dying(): 4191 return tomb.ErrDying 4192 case change := <-changesCh: 4193 if _, ok := collect(change, changesCh, w.tomb.Dying()); !ok { 4194 return tomb.ErrDying 4195 } 4196 newHash, err := w.hash() 4197 if err != nil { 4198 return errors.Trace(err) 4199 } 4200 if lastHash != newHash { 4201 lastHash = newHash 4202 out = w.out 4203 } 4204 case out <- []string{lastHash}: 4205 out = nil 4206 } 4207 } 4208 } 4209 4210 // WatchMachineAndEndpointAddressesHash returns a StringsWatcher that reports changes to 4211 // the hash value of the address assignments to the unit's endpoints. The hash 4212 // is recalculated when any of the following events occurs: 4213 // - the machine addresses for the unit change. 4214 // - the endpoint bindings for the unit's application change. 4215 func (u *Unit) WatchMachineAndEndpointAddressesHash() (StringsWatcher, error) { 4216 app, err := u.Application() 4217 if err != nil { 4218 return nil, errors.Trace(err) 4219 } 4220 endpointsDoc, err := readEndpointBindingsDoc(app.st, app.globalKey()) 4221 if err != nil { 4222 return nil, errors.Trace(err) 4223 } 4224 machineId, err := u.AssignedMachineId() 4225 if err != nil { 4226 return nil, errors.Trace(err) 4227 } 4228 machine, err := u.st.Machine(machineId) 4229 if err != nil { 4230 return nil, errors.Trace(err) 4231 } 4232 mCopy := &Machine{st: machine.st, doc: machine.doc} 4233 4234 // We need to check the hash when either the bindings change or when 4235 // the machine doc changes (e.g. machine has new network addresses). 4236 w := &hashMultiWatcher{ 4237 commonWatcher: newCommonWatcher(app.st), 4238 out: make(chan []string), 4239 collectionToIDMap: map[string]string{ 4240 endpointBindingsC: endpointsDoc.DocID, 4241 machinesC: machine.doc.DocID, 4242 }, 4243 hash: func() (string, error) { 4244 bindings, err := app.EndpointBindings() 4245 if err != nil { 4246 return "", err 4247 } 4248 return hashMachineAddressesForEndpointBindings(mCopy, bindings.Map()) 4249 }, 4250 } 4251 w.start() 4252 return w, nil 4253 } 4254 4255 func hashMachineAddressesForEndpointBindings(m *Machine, bindingsToSpaceIDs map[string]string) (string, error) { 4256 if err := m.Refresh(); err != nil { 4257 return "", errors.Trace(err) 4258 } 4259 hash := sha256.New() 4260 4261 addresses := m.Addresses() 4262 sort.Slice(addresses, func(i, j int) bool { return addresses[i].Value < addresses[j].Value }) 4263 for _, address := range addresses { 4264 hashAddr(hash, address) 4265 } 4266 4267 // Also include binding assignments to the hash. We don't care about 4268 // address assignments at this point; if the machine addresses change 4269 // (e.g. due to a reboot), the above code block would yield a different 4270 // hash. 4271 sortedEndpoints := make([]string, 0, len(bindingsToSpaceIDs)) 4272 for epName := range bindingsToSpaceIDs { 4273 sortedEndpoints = append(sortedEndpoints, epName) 4274 } 4275 sort.Strings(sortedEndpoints) 4276 4277 for _, epName := range sortedEndpoints { 4278 if epName == "" { 4279 continue 4280 } 4281 _, _ = hash.Write([]byte(fmt.Sprintf("%s:%s", epName, bindingsToSpaceIDs[epName]))) 4282 } 4283 return fmt.Sprintf("%x", hash.Sum(nil)), nil 4284 } 4285 4286 func hashAddr(h hash.Hash, addr corenetwork.SpaceAddress) { 4287 _, _ = h.Write([]byte(addr.Value)) 4288 _, _ = h.Write([]byte(addr.Type)) 4289 _, _ = h.Write([]byte(addr.Scope)) 4290 _, _ = h.Write([]byte(addr.SpaceID)) 4291 } 4292 4293 // hashMultiWatcher watches a set of documents for changes, invokes a 4294 // user-defined hash function and emits changes in the hash value. 4295 type hashMultiWatcher struct { 4296 commonWatcher 4297 collectionToIDMap map[string]string 4298 hash func() (string, error) 4299 out chan []string 4300 } 4301 4302 func (w *hashMultiWatcher) Changes() <-chan []string { 4303 return w.out 4304 } 4305 4306 func (w *hashMultiWatcher) start() { 4307 w.tomb.Go(func() error { 4308 defer close(w.out) 4309 return w.loop() 4310 }) 4311 } 4312 4313 func (w *hashMultiWatcher) loop() error { 4314 changesCh := make(chan watcher.Change) 4315 for collection, id := range w.collectionToIDMap { 4316 w.watcher.Watch(collection, id, changesCh) 4317 } 4318 defer func() { 4319 for collection, id := range w.collectionToIDMap { 4320 w.watcher.Unwatch(collection, id, changesCh) 4321 } 4322 }() 4323 4324 lastHash, err := w.hash() 4325 if err != nil { 4326 return errors.Trace(err) 4327 } 4328 out := w.out 4329 for { 4330 select { 4331 case <-w.watcher.Dead(): 4332 return stateWatcherDeadError(w.watcher.Err()) 4333 case <-w.tomb.Dying(): 4334 return tomb.ErrDying 4335 case change := <-changesCh: 4336 if _, ok := collect(change, changesCh, w.tomb.Dying()); !ok { 4337 return tomb.ErrDying 4338 } 4339 newHash, err := w.hash() 4340 if err != nil { 4341 return errors.Trace(err) 4342 } 4343 if lastHash != newHash { 4344 lastHash = newHash 4345 out = w.out 4346 } 4347 case out <- []string{lastHash}: 4348 out = nil 4349 } 4350 } 4351 } 4352 4353 func toSortedBsonD(values map[string]interface{}) bson.D { 4354 var items bson.D 4355 for name, value := range values { 4356 if mapValue, ok := value.(map[string]interface{}); ok { 4357 value = toSortedBsonD(mapValue) 4358 } 4359 items = append(items, bson.DocElem{Name: name, Value: value}) 4360 } 4361 // We know that there aren't any equal names because the source is 4362 // a map. 4363 sort.Slice(items, func(i int, j int) bool { 4364 return items[i].Name < items[j].Name 4365 }) 4366 return items 4367 }