github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/uniter/filter/filter.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package filter 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names" 10 "github.com/juju/utils/set" 11 "gopkg.in/juju/charm.v5" 12 "launchpad.net/tomb" 13 14 "github.com/juju/juju/api/uniter" 15 apiwatcher "github.com/juju/juju/api/watcher" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/state/watcher" 18 "github.com/juju/juju/worker" 19 ) 20 21 var filterLogger = loggo.GetLogger("juju.worker.uniter.filter") 22 23 // filter collects unit, service, and service config information from separate 24 // state watchers, and presents it as events on channels designed specifically 25 // for the convenience of the uniter. 26 type filter struct { 27 st *uniter.State 28 tomb tomb.Tomb 29 30 // outUnitDying is closed when the unit's life becomes Dying. 31 outUnitDying chan struct{} 32 33 // The out*On chans are used to deliver events to clients. 34 // The out* chans, when set to the corresponding out*On chan (rather than 35 // nil) indicate that an event of the appropriate type is ready to send 36 // to the client. 37 outConfig chan struct{} 38 outConfigOn chan struct{} 39 outAction chan string 40 outActionOn chan string 41 outLeaderSettings chan struct{} 42 outLeaderSettingsOn chan struct{} 43 outUpgrade chan *charm.URL 44 outUpgradeOn chan *charm.URL 45 outResolved chan params.ResolvedMode 46 outResolvedOn chan params.ResolvedMode 47 outRelations chan []int 48 outRelationsOn chan []int 49 outMeterStatus chan struct{} 50 outMeterStatusOn chan struct{} 51 outStorage chan []names.StorageTag 52 outStorageOn chan []names.StorageTag 53 // The want* chans are used to indicate that the filter should send 54 // events if it has them available. 55 wantForcedUpgrade chan bool 56 wantResolved chan struct{} 57 wantLeaderSettings chan bool 58 59 // discardConfig is used to indicate that any pending config event 60 // should be discarded. 61 discardConfig chan struct{} 62 63 // discardLeaderSettings is used to indicate any pending Leader 64 // Settings event should be discarded. 65 discardLeaderSettings chan struct{} 66 67 // setCharm is used to request that the unit's charm URL be set to 68 // a new value. This must be done in the filter's goroutine, so 69 // that config watches can be stopped and restarted pointing to 70 // the new charm URL. If we don't stop the watch before the 71 // (potentially) last reference to that settings document is 72 // removed, we'll see spurious errors (and even in the best case, 73 // we risk getting notifications for the wrong settings version). 74 setCharm chan *charm.URL 75 76 // didSetCharm is used to report back after setting a charm URL. 77 didSetCharm chan struct{} 78 79 // clearResolved is used to request that the unit's resolved flag 80 // be cleared. This must be done on the filter's goroutine so that 81 // it can immediately trigger the unit change handler, and thus 82 // ensure that subsquent requests for resolved events -- that land 83 // before the next watcher update for the unit -- do not erroneously 84 // send out stale values. 85 clearResolved chan struct{} 86 87 // didClearResolved is used to report back after clearing the resolved 88 // flag. 89 didClearResolved chan struct{} 90 91 // The following fields hold state that is collected while running, 92 // and used to detect interesting changes to express as events. 93 unit *uniter.Unit 94 life params.Life 95 resolved params.ResolvedMode 96 service *uniter.Service 97 upgradeFrom serviceCharm 98 upgradeAvailable serviceCharm 99 upgrade *charm.URL 100 relations []int 101 storage []names.StorageTag 102 actionsPending []string 103 nextAction string 104 105 // meterStatusCode and meterStatusInfo reflect the meter status values of the unit. 106 meterStatusCode string 107 meterStatusInfo string 108 } 109 110 // NewFilter returns a filter that handles state changes pertaining to the 111 // supplied unit. 112 func NewFilter(st *uniter.State, unitTag names.UnitTag) (Filter, error) { 113 f := &filter{ 114 st: st, 115 outUnitDying: make(chan struct{}), 116 outConfigOn: make(chan struct{}), 117 outActionOn: make(chan string), 118 outLeaderSettingsOn: make(chan struct{}), 119 outUpgradeOn: make(chan *charm.URL), 120 outResolvedOn: make(chan params.ResolvedMode), 121 outRelationsOn: make(chan []int), 122 outMeterStatusOn: make(chan struct{}), 123 outStorageOn: make(chan []names.StorageTag), 124 wantForcedUpgrade: make(chan bool), 125 wantResolved: make(chan struct{}), 126 wantLeaderSettings: make(chan bool), 127 discardConfig: make(chan struct{}), 128 discardLeaderSettings: make(chan struct{}), 129 setCharm: make(chan *charm.URL), 130 didSetCharm: make(chan struct{}), 131 clearResolved: make(chan struct{}), 132 didClearResolved: make(chan struct{}), 133 } 134 go func() { 135 defer f.tomb.Done() 136 err := f.loop(unitTag) 137 filterLogger.Errorf("%v", err) 138 f.tomb.Kill(err) 139 }() 140 return f, nil 141 } 142 143 func (f *filter) Stop() error { 144 f.tomb.Kill(nil) 145 return f.tomb.Wait() 146 } 147 148 func (f *filter) Dead() <-chan struct{} { 149 return f.tomb.Dead() 150 } 151 152 func (f *filter) Wait() error { 153 return f.tomb.Wait() 154 } 155 156 func (f *filter) Kill() { 157 f.tomb.Kill(nil) 158 } 159 160 // UnitDying returns a channel which is closed when the Unit enters a Dying state. 161 func (f *filter) UnitDying() <-chan struct{} { 162 return f.outUnitDying 163 } 164 165 // UpgradeEvents returns a channel that will receive a new charm URL whenever an 166 // upgrade is indicated. Events should not be read until the baseline state 167 // has been specified by calling WantUpgradeEvent. 168 func (f *filter) UpgradeEvents() <-chan *charm.URL { 169 return f.outUpgradeOn 170 } 171 172 // ResolvedEvents returns a channel that may receive a ResolvedMode when the 173 // unit's Resolved value changes, or when an event is explicitly requested. 174 // A ResolvedNone state will never generate events, but ResolvedRetryHooks and 175 // ResolvedNoHooks will always be delivered as described. 176 func (f *filter) ResolvedEvents() <-chan params.ResolvedMode { 177 return f.outResolvedOn 178 } 179 180 // MeterStatusEvents returns a channel that will receive a signal when the unit's 181 // meter status changes. 182 func (f *filter) MeterStatusEvents() <-chan struct{} { 183 return f.outMeterStatusOn 184 } 185 186 // ConfigEvents returns a channel that will receive a signal whenever the service's 187 // configuration changes, or when an event is explicitly requested. 188 func (f *filter) ConfigEvents() <-chan struct{} { 189 return f.outConfigOn 190 } 191 192 // ActionEvents returns a channel that will receive a signal whenever the unit 193 // receives new Actions. 194 func (f *filter) ActionEvents() <-chan string { 195 return f.outActionOn 196 } 197 198 // RelationsEvents returns a channel that will receive the ids of all the service's 199 // relations whose Life status has changed. 200 func (f *filter) RelationsEvents() <-chan []int { 201 return f.outRelationsOn 202 } 203 204 // StorageEvents returns a channel that will receive the tags of all the unit's 205 // associated storage instances. 206 func (f *filter) StorageEvents() <-chan []names.StorageTag { 207 return f.outStorageOn 208 } 209 210 // WantUpgradeEvent controls whether the filter will generate upgrade 211 // events for unforced service charm changes. 212 func (f *filter) WantUpgradeEvent(mustForce bool) { 213 select { 214 case <-f.tomb.Dying(): 215 case f.wantForcedUpgrade <- mustForce: 216 } 217 } 218 219 // SetCharm notifies the filter that the unit is running a new 220 // charm. It causes the unit's charm URL to be set in state, and the 221 // following changes to the filter's behaviour: 222 // 223 // * Upgrade events will only be generated for charms different to 224 // that supplied; 225 // * A fresh relations event will be generated containing every relation 226 // the service is participating in; 227 // * A fresh configuration event will be generated, and subsequent 228 // events will only be sent in response to changes in the version 229 // of the service's settings that is specific to that charm. 230 // 231 // SetCharm blocks until the charm URL is set in state, returning any 232 // error that occurred. 233 func (f *filter) SetCharm(curl *charm.URL) error { 234 select { 235 case <-f.tomb.Dying(): 236 return tomb.ErrDying 237 case f.setCharm <- curl: 238 } 239 select { 240 case <-f.tomb.Dying(): 241 return tomb.ErrDying 242 case <-f.didSetCharm: 243 return nil 244 } 245 } 246 247 // WantResolvedEvent indicates that the filter should send a resolved event 248 // if one is available. 249 func (f *filter) WantResolvedEvent() { 250 select { 251 case <-f.tomb.Dying(): 252 case f.wantResolved <- nothing: 253 } 254 } 255 256 // ClearResolved notifies the filter that a resolved event has been handled 257 // and should not be reported again. 258 func (f *filter) ClearResolved() error { 259 select { 260 case <-f.tomb.Dying(): 261 return tomb.ErrDying 262 case f.clearResolved <- nothing: 263 } 264 select { 265 case <-f.tomb.Dying(): 266 return tomb.ErrDying 267 case <-f.didClearResolved: 268 filterLogger.Debugf("resolved clear completed") 269 return nil 270 } 271 } 272 273 // LeaderSettingsEvents returns a channel that will receive an event whenever 274 // there is a leader settings change. Events can be temporarily suspended by 275 // calling WantLeaderSettingsEvents(false), and then reenabled by calling 276 // WantLeaderSettingsEvents(true) 277 func (f *filter) LeaderSettingsEvents() <-chan struct{} { 278 return f.outLeaderSettingsOn 279 } 280 281 // DiscardLeaderSettingsEvent can be called to discard any pending 282 // LeaderSettingsEvents. This is used by code that saw a LeaderSettings change, 283 // and has been prepping for a response. Just before they request the current 284 // LeaderSettings, they can discard any other pending changes, since they know 285 // they will be handling all changes that have occurred before right now. 286 func (f *filter) DiscardLeaderSettingsEvent() { 287 select { 288 case <-f.tomb.Dying(): 289 case f.discardLeaderSettings <- nothing: 290 } 291 } 292 293 // WantLeaderSettingsEvents can be used to enable/disable events being sent on 294 // the LeaderSettingsEvents() channel. This is used when an agent notices that 295 // it is the leader, it wants to disable getting events for changes that it is 296 // generating. Calling this with sendEvents=false disables getting change 297 // events. Calling this with sendEvents=true will enable future changes, and 298 // queues up an immediate event so that the agent will refresh its information 299 // for any events it might have missed while it thought it was the leader. 300 func (f *filter) WantLeaderSettingsEvents(sendEvents bool) { 301 select { 302 case <-f.tomb.Dying(): 303 case f.wantLeaderSettings <- sendEvents: 304 } 305 } 306 307 // DiscardConfigEvent indicates that the filter should discard any pending 308 // config event. 309 func (f *filter) DiscardConfigEvent() { 310 select { 311 case <-f.tomb.Dying(): 312 case f.discardConfig <- nothing: 313 } 314 } 315 316 func (f *filter) maybeStopWatcher(w watcher.Stopper) { 317 if w != nil { 318 watcher.Stop(w, &f.tomb) 319 } 320 } 321 322 func (f *filter) loop(unitTag names.UnitTag) (err error) { 323 // TODO(dfc) named return value is a time bomb 324 defer func() { 325 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 326 err = worker.ErrTerminateAgent 327 } 328 }() 329 if f.unit, err = f.st.Unit(unitTag); err != nil { 330 return err 331 } 332 if err = f.unitChanged(); err != nil { 333 return err 334 } 335 if err = f.meterStatusChanged(); err != nil { 336 return err 337 } 338 f.service, err = f.unit.Service() 339 if err != nil { 340 return err 341 } 342 if err = f.serviceChanged(); err != nil { 343 return err 344 } 345 unitw, err := f.unit.Watch() 346 if err != nil { 347 return err 348 } 349 defer f.maybeStopWatcher(unitw) 350 servicew, err := f.service.Watch() 351 if err != nil { 352 return err 353 } 354 defer f.maybeStopWatcher(servicew) 355 // configw and relationsw can get restarted, so we need to use 356 // their eventual values in the defer calls. 357 var configw apiwatcher.NotifyWatcher 358 var configChanges <-chan struct{} 359 curl, err := f.unit.CharmURL() 360 if err == nil { 361 configw, err = f.unit.WatchConfigSettings() 362 if err != nil { 363 return err 364 } 365 configChanges = configw.Changes() 366 f.upgradeFrom.url = curl 367 } else if err != uniter.ErrNoCharmURLSet { 368 filterLogger.Errorf("unit charm: %v", err) 369 return err 370 } 371 defer f.maybeStopWatcher(configw) 372 actionsw, err := f.unit.WatchActionNotifications() 373 if err != nil { 374 return err 375 } 376 f.actionsPending = make([]string, 0) 377 defer f.maybeStopWatcher(actionsw) 378 relationsw, err := f.service.WatchRelations() 379 if err != nil { 380 return err 381 } 382 defer f.maybeStopWatcher(relationsw) 383 meterStatusw, err := f.unit.WatchMeterStatus() 384 if err != nil { 385 return err 386 } 387 defer f.maybeStopWatcher(meterStatusw) 388 addressesw, err := f.unit.WatchAddresses() 389 if err != nil { 390 return err 391 } 392 defer watcher.Stop(addressesw, &f.tomb) 393 storagew, err := f.unit.WatchStorage() 394 if err != nil { 395 return err 396 } 397 defer watcher.Stop(storagew, &f.tomb) 398 leaderSettingsw, err := f.st.LeadershipSettings.WatchLeadershipSettings(f.service.Tag().Id()) 399 if err != nil { 400 return err 401 } 402 defer watcher.Stop(leaderSettingsw, &f.tomb) 403 404 // Ignore external requests for leader settings behaviour until we see the first change. 405 var discardLeaderSettings <-chan struct{} 406 var wantLeaderSettings <-chan bool 407 // By default we send all leaderSettings onwards. 408 sendLeaderSettings := true 409 410 // Config events cannot be meaningfully discarded until one is available; 411 // once we receive the initial config and address changes, we unblock 412 // discard requests by setting this channel to its namesake on f. 413 var discardConfig chan struct{} 414 var seenConfigChange bool 415 var seenAddressChange bool 416 maybePrepareConfigEvent := func() { 417 if !seenAddressChange { 418 filterLogger.Debugf("no address change seen yet, skipping config event") 419 return 420 } 421 if !seenConfigChange { 422 filterLogger.Debugf("no config change seen yet, skipping config event") 423 return 424 } 425 filterLogger.Debugf("preparing new config event") 426 f.outConfig = f.outConfigOn 427 discardConfig = f.discardConfig 428 } 429 430 for { 431 var ok bool 432 select { 433 case <-f.tomb.Dying(): 434 return tomb.ErrDying 435 436 // Handle watcher changes. 437 case _, ok = <-unitw.Changes(): 438 filterLogger.Debugf("got unit change") 439 if !ok { 440 return watcher.EnsureErr(unitw) 441 } 442 if err = f.unitChanged(); err != nil { 443 return err 444 } 445 case _, ok = <-servicew.Changes(): 446 filterLogger.Debugf("got service change") 447 if !ok { 448 return watcher.EnsureErr(servicew) 449 } 450 if err = f.serviceChanged(); err != nil { 451 return err 452 } 453 case _, ok = <-configChanges: 454 filterLogger.Debugf("got config change") 455 if !ok { 456 return watcher.EnsureErr(configw) 457 } 458 seenConfigChange = true 459 maybePrepareConfigEvent() 460 case _, ok = <-addressesw.Changes(): 461 filterLogger.Debugf("got address change") 462 if !ok { 463 return watcher.EnsureErr(addressesw) 464 } 465 seenAddressChange = true 466 maybePrepareConfigEvent() 467 case _, ok = <-meterStatusw.Changes(): 468 filterLogger.Debugf("got meter status change") 469 if !ok { 470 return watcher.EnsureErr(meterStatusw) 471 } 472 if err = f.meterStatusChanged(); err != nil { 473 return errors.Trace(err) 474 } 475 case ids, ok := <-actionsw.Changes(): 476 filterLogger.Debugf("got %d actions", len(ids)) 477 if !ok { 478 return watcher.EnsureErr(actionsw) 479 } 480 f.actionsPending = append(f.actionsPending, ids...) 481 f.nextAction = f.getNextAction() 482 case keys, ok := <-relationsw.Changes(): 483 filterLogger.Debugf("got relations change") 484 if !ok { 485 return watcher.EnsureErr(relationsw) 486 } 487 var ids []int 488 for _, key := range keys { 489 relationTag := names.NewRelationTag(key) 490 rel, err := f.st.Relation(relationTag) 491 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 492 // If it's actually gone, this unit cannot have entered 493 // scope, and therefore never needs to know about it. 494 } else if err != nil { 495 return err 496 } else { 497 ids = append(ids, rel.Id()) 498 } 499 } 500 f.relationsChanged(ids) 501 case ids, ok := <-storagew.Changes(): 502 filterLogger.Debugf("got storage change") 503 if !ok { 504 return watcher.EnsureErr(storagew) 505 } 506 tags := make([]names.StorageTag, len(ids)) 507 for i, id := range ids { 508 tag := names.NewStorageTag(id) 509 tags[i] = tag 510 } 511 f.storageChanged(tags) 512 case _, ok = <-leaderSettingsw.Changes(): 513 filterLogger.Debugf("got leader settings change: ok=%t", ok) 514 if !ok { 515 return watcher.EnsureErr(leaderSettingsw) 516 } 517 if sendLeaderSettings { 518 // only send the leader settings changed event 519 // if it hasn't been explicitly disabled 520 f.outLeaderSettings = f.outLeaderSettingsOn 521 } else { 522 filterLogger.Debugf("not sending leader settings change (want=false)") 523 } 524 discardLeaderSettings = f.discardLeaderSettings 525 wantLeaderSettings = f.wantLeaderSettings 526 527 // Send events on active out chans. 528 case f.outUpgrade <- f.upgrade: 529 filterLogger.Debugf("sent upgrade event") 530 f.outUpgrade = nil 531 case f.outResolved <- f.resolved: 532 filterLogger.Debugf("sent resolved event") 533 f.outResolved = nil 534 case f.outConfig <- nothing: 535 filterLogger.Debugf("sent config event") 536 f.outConfig = nil 537 case f.outLeaderSettings <- nothing: 538 filterLogger.Debugf("sent leader settings event") 539 f.outLeaderSettings = nil 540 case f.outAction <- f.nextAction: 541 f.nextAction = f.getNextAction() 542 filterLogger.Debugf("sent action event") 543 case f.outRelations <- f.relations: 544 filterLogger.Debugf("sent relations event") 545 f.outRelations = nil 546 f.relations = nil 547 case f.outMeterStatus <- nothing: 548 filterLogger.Debugf("sent meter status change event") 549 f.outMeterStatus = nil 550 case f.outStorage <- f.storage: 551 filterLogger.Debugf("sent storage event") 552 f.outStorage = nil 553 f.storage = nil 554 555 // Handle explicit requests. 556 case curl := <-f.setCharm: 557 filterLogger.Debugf("changing charm to %q", curl) 558 // We need to restart the config watcher after setting the 559 // charm, because service config settings are distinct for 560 // different service charms. 561 if configw != nil { 562 if err := configw.Stop(); err != nil { 563 return err 564 } 565 } 566 if err := f.unit.SetCharmURL(curl); err != nil { 567 filterLogger.Debugf("failed setting charm url %q: %v", curl, err) 568 return err 569 } 570 select { 571 case <-f.tomb.Dying(): 572 return tomb.ErrDying 573 case f.didSetCharm <- nothing: 574 } 575 configw, err = f.unit.WatchConfigSettings() 576 if err != nil { 577 return err 578 } 579 configChanges = configw.Changes() 580 581 // Restart the relations watcher. 582 if err := relationsw.Stop(); err != nil { 583 return err 584 } 585 relationsw, err = f.service.WatchRelations() 586 if err != nil { 587 return err 588 } 589 590 f.upgradeFrom.url = curl 591 if err = f.upgradeChanged(); err != nil { 592 return err 593 } 594 case force := <-f.wantForcedUpgrade: 595 filterLogger.Debugf("want forced upgrade %v", force) 596 f.upgradeFrom.force = force 597 if err = f.upgradeChanged(); err != nil { 598 return err 599 } 600 case <-f.wantResolved: 601 filterLogger.Debugf("want resolved event") 602 if f.resolved != params.ResolvedNone { 603 f.outResolved = f.outResolvedOn 604 } 605 case sendEvents := <-wantLeaderSettings: 606 filterLogger.Debugf("want leader settings event: %t", sendEvents) 607 sendLeaderSettings = sendEvents 608 if sendEvents { 609 // go ahead and send an event right now, 610 // they're waiting for us 611 f.outLeaderSettings = f.outLeaderSettingsOn 612 } else { 613 // Make sure we don't have a pending event 614 f.outLeaderSettings = nil 615 } 616 case <-f.clearResolved: 617 filterLogger.Debugf("resolved event handled") 618 f.outResolved = nil 619 if err := f.unit.ClearResolved(); err != nil { 620 return err 621 } 622 if err := f.unitChanged(); err != nil { 623 return err 624 } 625 select { 626 case <-f.tomb.Dying(): 627 return tomb.ErrDying 628 case f.didClearResolved <- nothing: 629 } 630 case <-discardConfig: 631 filterLogger.Debugf("discarded config event") 632 f.outConfig = nil 633 case <-discardLeaderSettings: 634 filterLogger.Debugf("discarded leader settings event") 635 f.outLeaderSettings = nil 636 } 637 } 638 } 639 640 // meterStatusChanges respondes to changes in the unit's meter status. 641 func (f *filter) meterStatusChanged() error { 642 code, info, err := f.unit.MeterStatus() 643 if err != nil { 644 return errors.Trace(err) 645 } 646 if f.meterStatusCode != code || f.meterStatusInfo != info { 647 if f.meterStatusCode != "" { 648 f.outMeterStatus = f.outMeterStatusOn 649 } 650 f.meterStatusCode = code 651 f.meterStatusInfo = info 652 } 653 return nil 654 } 655 656 // unitChanged responds to changes in the unit. 657 func (f *filter) unitChanged() error { 658 if err := f.unit.Refresh(); err != nil { 659 return err 660 } 661 if f.life != f.unit.Life() { 662 switch f.life = f.unit.Life(); f.life { 663 case params.Dying: 664 filterLogger.Infof("unit is dying") 665 close(f.outUnitDying) 666 f.outUpgrade = nil 667 case params.Dead: 668 filterLogger.Infof("unit is dead") 669 return worker.ErrTerminateAgent 670 } 671 } 672 resolved, err := f.unit.Resolved() 673 if err != nil { 674 return err 675 } 676 if resolved != f.resolved { 677 f.resolved = resolved 678 if f.resolved != params.ResolvedNone { 679 f.outResolved = f.outResolvedOn 680 } 681 } 682 return nil 683 } 684 685 // serviceChanged responds to changes in the service. 686 func (f *filter) serviceChanged() error { 687 if err := f.service.Refresh(); err != nil { 688 return err 689 } 690 url, force, err := f.service.CharmURL() 691 if err != nil { 692 return err 693 } 694 f.upgradeAvailable = serviceCharm{url, force} 695 switch f.service.Life() { 696 case params.Dying: 697 if err := f.unit.Destroy(); err != nil { 698 return err 699 } 700 case params.Dead: 701 filterLogger.Infof("service is dead") 702 return worker.ErrTerminateAgent 703 } 704 return f.upgradeChanged() 705 } 706 707 // upgradeChanged responds to changes in the service or in the 708 // upgrade requests that defines which charm changes should be 709 // delivered as upgrades. 710 func (f *filter) upgradeChanged() (err error) { 711 if f.life != params.Alive { 712 filterLogger.Debugf("charm check skipped, unit is dying") 713 f.outUpgrade = nil 714 return nil 715 } 716 if f.upgradeFrom.url == nil { 717 filterLogger.Debugf("charm check skipped, not yet installed.") 718 f.outUpgrade = nil 719 return nil 720 } 721 if *f.upgradeAvailable.url != *f.upgradeFrom.url { 722 if f.upgradeAvailable.force || !f.upgradeFrom.force { 723 filterLogger.Debugf("preparing new upgrade event") 724 if f.upgrade == nil || *f.upgrade != *f.upgradeAvailable.url { 725 f.upgrade = f.upgradeAvailable.url 726 } 727 f.outUpgrade = f.outUpgradeOn 728 return nil 729 } 730 } 731 filterLogger.Debugf("no new charm event") 732 f.outUpgrade = nil 733 return nil 734 } 735 736 // relationsChanged responds to service relation changes. 737 func (f *filter) relationsChanged(changed []int) { 738 ids := set.NewInts(f.relations...) 739 for _, id := range changed { 740 ids.Add(id) 741 } 742 if len(f.relations) != len(ids) { 743 f.relations = ids.SortedValues() 744 f.outRelations = f.outRelationsOn 745 } 746 } 747 748 // storageChanged responds to unit storage changes. 749 func (f *filter) storageChanged(changed []names.StorageTag) { 750 tags := set.NewTags() // f.storage is []StorageTag, not []Tag 751 for _, tag := range f.storage { 752 tags.Add(tag) 753 } 754 for _, tag := range changed { 755 tags.Add(tag) 756 } 757 if len(f.storage) != len(tags) { 758 storage := make([]names.StorageTag, len(tags)) 759 for i, tag := range tags.SortedValues() { 760 storage[i] = tag.(names.StorageTag) 761 } 762 f.storage = storage 763 f.outStorage = f.outStorageOn 764 } 765 } 766 767 func (f *filter) getNextAction() string { 768 if len(f.actionsPending) > 0 { 769 actionId := f.actionsPending[0] 770 771 f.outAction = f.outActionOn 772 f.actionsPending = f.actionsPending[1:] 773 774 return actionId 775 } else { 776 f.outAction = nil 777 } 778 779 return "" 780 } 781 782 // serviceCharm holds information about a charm. 783 type serviceCharm struct { 784 url *charm.URL 785 force bool 786 } 787 788 // nothing is marginally more pleasant to read than "struct{}{}". 789 var nothing = struct{}{}