func (s *commonSuite) TestAuthFuncForTagKind(c *gc.C) { // TODO(dimitern): This list of all supported tags and kinds needs // to live in juju/names. uuid, err := utils.NewUUID() c.Assert(err, jc.ErrorIsNil) allTags := []names.Tag{ nil, // invalid tag names.NewActionTag(uuid.String()), names.NewCharmTag("cs:precise/missing"), names.NewModelTag(uuid.String()), names.NewFilesystemTag("20/20"), names.NewLocalUserTag("user"), names.NewMachineTag("42"), names.NewNetworkTag("public"), names.NewRelationTag("wordpress:mysql mysql:db"), names.NewServiceTag("wordpress"), names.NewSpaceTag("apps"), names.NewStorageTag("foo/42"), names.NewUnitTag("wordpress/5"), names.NewUserTag("joe"), names.NewVolumeTag("80/20"), } for i, allowedTag := range allTags { c.Logf("test #%d: allowedTag: %v", i, allowedTag) var allowedKind string if allowedTag != nil { allowedKind = allowedTag.Kind() } getAuthFunc := common.AuthFuncForTagKind(allowedKind) authFunc, err := getAuthFunc() if allowedKind == "" { c.Check(err, gc.ErrorMatches, "tag kind cannot be empty") c.Check(authFunc, gc.IsNil) continue } else if !c.Check(err, jc.ErrorIsNil) { continue } for j, givenTag := range allTags { c.Logf("test #%d.%d: givenTag: %v", i, j, givenTag) var givenKind string if givenTag != nil { givenKind = givenTag.Kind() } if allowedKind == givenKind { c.Check(authFunc(givenTag), jc.IsTrue) } else { c.Check(authFunc(givenTag), jc.IsFalse) } } } }
func (s *WatcherSuite) TestRelationsChanged(c *gc.C) { signalAll(&s.st, &s.leadership) assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") relationTag := names.NewRelationTag("mysql:peer") s.st.relations[relationTag] = &mockRelation{ id: 123, life: params.Alive, } s.st.relationUnitsWatchers[relationTag] = &mockRelationUnitsWatcher{ changes: make(chan multiwatcher.RelationUnitsChange, 1), } s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()} // There should not be any signal until the relation units watcher has // returned its initial event also. assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change") s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{ Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {1}, "mysql/2": {2}}, } assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") c.Assert( s.watcher.Snapshot().Relations, jc.DeepEquals, map[int]remotestate.RelationSnapshot{ 123: remotestate.RelationSnapshot{ Life: params.Alive, Members: map[string]int64{"mysql/1": 1, "mysql/2": 2}, }, }, ) // If a relation is known, then updating it does not require any input // from the relation units watcher. s.st.relations[relationTag].life = params.Dying s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()} assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") c.Assert(s.watcher.Snapshot().Relations[123].Life, gc.Equals, params.Dying) // If a relation is not found, then it should be removed from the // snapshot and its relation units watcher stopped. delete(s.st.relations, relationTag) s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()} assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") c.Assert(s.watcher.Snapshot().Relations, gc.HasLen, 0) c.Assert(s.st.relationUnitsWatchers[relationTag].stopped, jc.IsTrue) }
func (s *relationsSuite) TestNewRelationsWithExistingRelations(c *gc.C) { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) var numCalls int32 unitEntity := params.Entities{Entities: []params.Entity{params.Entity{Tag: "unit-wordpress-0"}}} relationUnits := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-wordpress.db#mysql.db", Unit: "unit-wordpress-0"}, }} relationResults := params.RelationResults{ Results: []params.RelationResult{ { Id: 1, Key: "wordpress:db mysql:db", Life: params.Alive, Endpoint: multiwatcher.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{Name: "mysql", Role: charm.RoleProvider, Interface: "db"}, }}, }, } apiCaller := mockAPICaller(c, &numCalls, uniterApiCall("Life", unitEntity, params.LifeResults{Results: []params.LifeResult{{Life: params.Alive}}}, nil), uniterApiCall("JoinedRelations", unitEntity, params.StringsResults{Results: []params.StringsResult{{Result: []string{"relation-wordpress:db mysql:db"}}}}, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Watch", unitEntity, params.NotifyWatchResults{Results: []params.NotifyWatchResult{{NotifyWatcherId: "1"}}}, nil), uniterApiCall("EnterScope", relationUnits, params.ErrorResults{Results: []params.ErrorResult{{}}}, nil), ) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, &numCalls, 6) info := r.GetInfo() c.Assert(info, gc.HasLen, 1) oneInfo := info[1] c.Assert(oneInfo.RelationUnit.Relation().Tag(), gc.Equals, names.NewRelationTag("wordpress:db mysql:db")) c.Assert(oneInfo.RelationUnit.Endpoint(), jc.DeepEquals, uniter.Endpoint{ Relation: charm.Relation{Name: "mysql", Role: "provider", Interface: "db", Optional: false, Limit: 0, Scope: ""}, }) c.Assert(oneInfo.MemberNames, gc.HasLen, 0) }
// relationsChanged responds to service relation changes. func (w *RemoteStateWatcher) relationsChanged(keys []string) error { w.mu.Lock() defer w.mu.Unlock() for _, key := range keys { relationTag := names.NewRelationTag(key) rel, err := w.st.Relation(relationTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { // If it's actually gone, this unit cannot have entered // scope, and therefore never needs to know about it. if ruw, ok := w.relations[relationTag]; ok { worker.Stop(ruw) delete(w.relations, relationTag) delete(w.current.Relations, ruw.relationId) } } else if err != nil { return errors.Trace(err) } else { if _, ok := w.relations[relationTag]; ok { relationSnapshot := w.current.Relations[rel.Id()] relationSnapshot.Life = rel.Life() w.current.Relations[rel.Id()] = relationSnapshot continue } ruw, err := w.st.WatchRelationUnits(relationTag, w.unit.Tag()) if err != nil { return errors.Trace(err) } // Because of the delay before handing off responsibility to // newRelationUnitsWatcher below, add to our own catacomb to // ensure errors get picked up if they happen. if err := w.catacomb.Add(ruw); err != nil { return errors.Trace(err) } if err := w.watchRelationUnits(rel, relationTag, ruw); err != nil { return errors.Trace(err) } } } return nil }
func (s *WatcherSuite) TestRelationUnitsChanged(c *gc.C) { signalAll(&s.st, &s.leadership) assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") relationTag := names.NewRelationTag("mysql:peer") s.st.relations[relationTag] = &mockRelation{ id: 123, life: params.Alive, } s.st.relationUnitsWatchers[relationTag] = &mockRelationUnitsWatcher{ changes: make(chan multiwatcher.RelationUnitsChange, 1), } s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()} s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{ Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {1}}, } assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{ Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {2}, "mysql/2": {1}}, } assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") c.Assert( s.watcher.Snapshot().Relations[123].Members, jc.DeepEquals, map[string]int64{"mysql/1": 2, "mysql/2": 1}, ) s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{ Departed: []string{"mysql/1", "mysql/42"}, } assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change") c.Assert( s.watcher.Snapshot().Relations[123].Members, jc.DeepEquals, map[string]int64{"mysql/2": 1}, ) }
// relationsChanged responds to service relation changes. func (w *RemoteStateWatcher) relationsChanged(keys []string) error { w.mu.Lock() defer w.mu.Unlock() for _, key := range keys { relationTag := names.NewRelationTag(key) rel, err := w.st.Relation(relationTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { // If it's actually gone, this unit cannot have entered // scope, and therefore never needs to know about it. if ruw, ok := w.relations[relationTag]; ok { if err := ruw.Stop(); err != nil { return errors.Trace(err) } delete(w.relations, relationTag) delete(w.current.Relations, ruw.relationId) } } else if err != nil { return err } else { if _, ok := w.relations[relationTag]; ok { relationSnapshot := w.current.Relations[rel.Id()] relationSnapshot.Life = rel.Life() w.current.Relations[rel.Id()] = relationSnapshot continue } in, err := w.st.WatchRelationUnits(relationTag, w.unit.Tag()) if err != nil { return errors.Trace(err) } if err := w.watchRelationUnits(rel, relationTag, in); err != nil { watcher.Stop(in, &w.tomb) return errors.Trace(err) } } } return nil }
// RelationById returns the existing relation with the given id. func (st *State) RelationById(id int) (*Relation, error) { var results params.RelationResults args := params.RelationIds{ RelationIds: []int{id}, } err := st.facade.FacadeCall("RelationById", args, &results) if err != nil { return nil, err } if len(results.Results) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, err } relationTag := names.NewRelationTag(result.Key) return &Relation{ id: result.Id, tag: relationTag, life: result.Life, st: st, }, nil }
// Tag returns a name identifying the relation. func (r *Relation) Tag() names.Tag { return names.NewRelationTag(r.doc.Key) }
c.Assert(names.IsValidRelation(key), gc.Equals, isValid) c.Assert(names.IsValidRelation(peerKey), gc.Equals, isValid) } } } var parseRelationTagTests = []struct { tag string expected names.Tag err error }{{ tag: "", err: names.InvalidTagError("", ""), }, { tag: "relation-wordpress:db mysql:db", expected: names.NewRelationTag("wordpress:db mysql:db"), }, { tag: "relation-wordpress:mysql", expected: names.NewRelationTag("wordpress:mysql"), }, { tag: "dave", err: names.InvalidTagError("dave", ""), }, { tag: "service-dave", err: names.InvalidTagError("service-dave", names.RelationTagKind), }} func (s *relationSuite) TestParseRelationTag(c *gc.C) { for i, t := range parseRelationTagTests { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseRelationTag(t.tag)
func (f *filter) loop(unitTag string) (err error) { // TODO(dfc) named return value is a time bomb defer func() { if params.IsCodeNotFoundOrCodeUnauthorized(err) { err = worker.ErrTerminateAgent } }() tag, err := names.ParseUnitTag(unitTag) if err != nil { return err } if f.unit, err = f.st.Unit(tag); err != nil { return err } if err = f.unitChanged(); err != nil { return err } f.service, err = f.unit.Service() if err != nil { return err } if err = f.serviceChanged(); err != nil { return err } unitw, err := f.unit.Watch() if err != nil { return err } defer f.maybeStopWatcher(unitw) servicew, err := f.service.Watch() if err != nil { return err } defer f.maybeStopWatcher(servicew) // configw and relationsw can get restarted, so we need to use // their eventual values in the defer calls. var configw apiwatcher.NotifyWatcher var configChanges <-chan struct{} curl, err := f.unit.CharmURL() if err == nil { configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() f.upgradeFrom.url = curl } else if err != uniter.ErrNoCharmURLSet { filterLogger.Errorf("unit charm: %v", err) return err } defer func() { if configw != nil { watcher.Stop(configw, &f.tomb) } }() actionsw, err := f.unit.WatchActions() if err != nil { return err } f.actionsPending = make([]string, 0) defer func() { if actionsw != nil { watcher.Stop(actionsw, &f.tomb) } }() relationsw, err := f.service.WatchRelations() if err != nil { return err } defer func() { if relationsw != nil { watcher.Stop(relationsw, &f.tomb) } }() var addressChanges <-chan struct{} addressesw, err := f.unit.WatchAddresses() if err != nil { return err } defer watcher.Stop(addressesw, &f.tomb) // Config events cannot be meaningfully discarded until one is available; // once we receive the initial change, we unblock discard requests by // setting this channel to its namesake on f. var discardConfig chan struct{} for { var ok bool select { case <-f.tomb.Dying(): return tomb.ErrDying // Handle watcher changes. case _, ok = <-unitw.Changes(): filterLogger.Debugf("got unit change") if !ok { return watcher.MustErr(unitw) } if err = f.unitChanged(); err != nil { return err } case _, ok = <-servicew.Changes(): filterLogger.Debugf("got service change") if !ok { return watcher.MustErr(servicew) } if err = f.serviceChanged(); err != nil { return err } case _, ok = <-configChanges: filterLogger.Debugf("got config change") if !ok { return watcher.MustErr(configw) } if addressChanges == nil { // We start reacting to address changes after the // first config-changed is processed, ignoring the // initial address changed event. addressChanges = addressesw.Changes() if _, ok := <-addressChanges; !ok { return watcher.MustErr(addressesw) } } filterLogger.Debugf("preparing new config event") f.outConfig = f.outConfigOn discardConfig = f.discardConfig case _, ok = <-addressChanges: filterLogger.Debugf("got address change") if !ok { return watcher.MustErr(addressesw) } // address change causes config-changed event filterLogger.Debugf("preparing new config event") f.outConfig = f.outConfigOn case ids, ok := <-actionsw.Changes(): filterLogger.Debugf("got %d actions", len(ids)) if !ok { return watcher.MustErr(actionsw) } f.actionsPending = append(f.actionsPending, ids...) f.nextAction = f.getNextAction() case keys, ok := <-relationsw.Changes(): filterLogger.Debugf("got relations change") if !ok { return watcher.MustErr(relationsw) } var ids []int for _, key := range keys { relationTag := names.NewRelationTag(key).String() rel, err := f.st.Relation(relationTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { // If it's actually gone, this unit cannot have entered // scope, and therefore never needs to know about it. } else if err != nil { return err } else { ids = append(ids, rel.Id()) } } f.relationsChanged(ids) // Send events on active out chans. case f.outUpgrade <- f.upgrade: filterLogger.Debugf("sent upgrade event") f.outUpgrade = nil case f.outResolved <- f.resolved: filterLogger.Debugf("sent resolved event") f.outResolved = nil case f.outConfig <- nothing: filterLogger.Debugf("sent config event") f.outConfig = nil case f.outAction <- f.nextAction: f.nextAction = f.getNextAction() filterLogger.Debugf("sent action event") case f.outRelations <- f.relations: filterLogger.Debugf("sent relations event") f.outRelations = nil f.relations = nil // Handle explicit requests. case curl := <-f.setCharm: filterLogger.Debugf("changing charm to %q", curl) // We need to restart the config watcher after setting the // charm, because service config settings are distinct for // different service charms. if configw != nil { if err := configw.Stop(); err != nil { return err } } if err := f.unit.SetCharmURL(curl); err != nil { filterLogger.Debugf("failed setting charm url %q: %v", curl, err) return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didSetCharm <- nothing: } configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() // Restart the relations watcher. if err := relationsw.Stop(); err != nil { return err } relationsw, err = f.service.WatchRelations() if err != nil { return err } f.upgradeFrom.url = curl if err = f.upgradeChanged(); err != nil { return err } case force := <-f.wantForcedUpgrade: filterLogger.Debugf("want forced upgrade %v", force) f.upgradeFrom.force = force if err = f.upgradeChanged(); err != nil { return err } case <-f.wantResolved: filterLogger.Debugf("want resolved event") if f.resolved != params.ResolvedNone { f.outResolved = f.outResolvedOn } case <-f.clearResolved: filterLogger.Debugf("resolved event handled") f.outResolved = nil if err := f.unit.ClearResolved(); err != nil { return err } if err := f.unitChanged(); err != nil { return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didClearResolved <- nothing: } case <-discardConfig: filterLogger.Debugf("discarded config event") f.outConfig = nil } } }
// Tag returns a name identifying the relation that is safe to use // as a file name. func (r *Relation) Tag() string { return names.NewRelationTag(r.doc.Key).String() }
resultId: "wordpress" + names.ActionMarker + "333", }, { tag: "action-wordpress/0" + names.ActionMarker + "333", expectKind: names.ActionTagKind, expectType: names.ActionTag{}, resultId: "wordpress/0" + names.ActionMarker + "333", }, { tag: "foo", resultErr: `"foo" is not a valid tag`, }} var makeTag = map[string]func(string) names.Tag{ names.MachineTagKind: func(tag string) names.Tag { return names.NewMachineTag(tag) }, names.UnitTagKind: func(tag string) names.Tag { return names.NewUnitTag(tag) }, names.ServiceTagKind: func(tag string) names.Tag { return names.NewServiceTag(tag) }, names.RelationTagKind: func(tag string) names.Tag { return names.NewRelationTag(tag) }, names.EnvironTagKind: func(tag string) names.Tag { return names.NewEnvironTag(tag) }, names.UserTagKind: func(tag string) names.Tag { return names.NewUserTag(tag) }, names.NetworkTagKind: func(tag string) names.Tag { return names.NewNetworkTag(tag) }, names.ActionTagKind: func(tag string) names.Tag { return names.NewActionTag(tag) }, } func (*tagSuite) TestParseTag(c *gc.C) { for i, test := range parseTagTests { c.Logf("test %d: %q expectKind %q", i, test.tag, test.expectKind) tag, err := names.ParseTag(test.tag) if test.resultErr != "" { c.Assert(err, gc.ErrorMatches, test.resultErr) c.Assert(tag, gc.IsNil) // If the tag has a valid kind which matches the
func (f *filter) loop(unitTag names.UnitTag) (err error) { // TODO(dfc) named return value is a time bomb defer func() { if params.IsCodeNotFoundOrCodeUnauthorized(err) { err = worker.ErrTerminateAgent } }() if f.unit, err = f.st.Unit(unitTag); err != nil { return err } if err = f.unitChanged(); err != nil { return err } if err = f.meterStatusChanged(); err != nil { return err } f.service, err = f.unit.Service() if err != nil { return err } if err = f.serviceChanged(); err != nil { return err } unitw, err := f.unit.Watch() if err != nil { return err } defer f.maybeStopWatcher(unitw) servicew, err := f.service.Watch() if err != nil { return err } defer f.maybeStopWatcher(servicew) // configw and relationsw can get restarted, so we need to use // their eventual values in the defer calls. var configw apiwatcher.NotifyWatcher var configChanges <-chan struct{} curl, err := f.unit.CharmURL() if err == nil { configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() f.upgradeFrom.url = curl } else if err != uniter.ErrNoCharmURLSet { filterLogger.Errorf("unit charm: %v", err) return err } defer f.maybeStopWatcher(configw) actionsw, err := f.unit.WatchActionNotifications() if err != nil { return err } f.actionsPending = make([]string, 0) defer f.maybeStopWatcher(actionsw) relationsw, err := f.service.WatchRelations() if err != nil { return err } defer f.maybeStopWatcher(relationsw) meterStatusw, err := f.unit.WatchMeterStatus() if err != nil { return err } defer f.maybeStopWatcher(meterStatusw) addressesw, err := f.unit.WatchAddresses() if err != nil { return err } defer watcher.Stop(addressesw, &f.tomb) storagew, err := f.unit.WatchStorage() if err != nil { return err } defer watcher.Stop(storagew, &f.tomb) leaderSettingsw, err := f.st.LeadershipSettings.WatchLeadershipSettings(f.service.Tag().Id()) if err != nil { return err } defer watcher.Stop(leaderSettingsw, &f.tomb) // Ignore external requests for leader settings behaviour until we see the first change. var discardLeaderSettings <-chan struct{} var wantLeaderSettings <-chan bool // By default we send all leaderSettings onwards. sendLeaderSettings := true // Config events cannot be meaningfully discarded until one is available; // once we receive the initial config and address changes, we unblock // discard requests by setting this channel to its namesake on f. var discardConfig chan struct{} var seenConfigChange bool var seenAddressChange bool maybePrepareConfigEvent := func() { if !seenAddressChange { filterLogger.Debugf("no address change seen yet, skipping config event") return } if !seenConfigChange { filterLogger.Debugf("no config change seen yet, skipping config event") return } filterLogger.Debugf("preparing new config event") f.outConfig = f.outConfigOn discardConfig = f.discardConfig } for { var ok bool select { case <-f.tomb.Dying(): return tomb.ErrDying // Handle watcher changes. case _, ok = <-unitw.Changes(): filterLogger.Debugf("got unit change") if !ok { return watcher.EnsureErr(unitw) } if err = f.unitChanged(); err != nil { return err } case _, ok = <-servicew.Changes(): filterLogger.Debugf("got service change") if !ok { return watcher.EnsureErr(servicew) } if err = f.serviceChanged(); err != nil { return err } case _, ok = <-configChanges: filterLogger.Debugf("got config change") if !ok { return watcher.EnsureErr(configw) } seenConfigChange = true maybePrepareConfigEvent() case _, ok = <-addressesw.Changes(): filterLogger.Debugf("got address change") if !ok { return watcher.EnsureErr(addressesw) } seenAddressChange = true maybePrepareConfigEvent() case _, ok = <-meterStatusw.Changes(): filterLogger.Debugf("got meter status change") if !ok { return watcher.EnsureErr(meterStatusw) } if err = f.meterStatusChanged(); err != nil { return errors.Trace(err) } case ids, ok := <-actionsw.Changes(): filterLogger.Debugf("got %d actions", len(ids)) if !ok { return watcher.EnsureErr(actionsw) } f.actionsPending = append(f.actionsPending, ids...) f.nextAction = f.getNextAction() case keys, ok := <-relationsw.Changes(): filterLogger.Debugf("got relations change") if !ok { return watcher.EnsureErr(relationsw) } var ids []int for _, key := range keys { relationTag := names.NewRelationTag(key) rel, err := f.st.Relation(relationTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { // If it's actually gone, this unit cannot have entered // scope, and therefore never needs to know about it. } else if err != nil { return err } else { ids = append(ids, rel.Id()) } } f.relationsChanged(ids) case ids, ok := <-storagew.Changes(): filterLogger.Debugf("got storage change") if !ok { return watcher.EnsureErr(storagew) } tags := make([]names.StorageTag, len(ids)) for i, id := range ids { tag := names.NewStorageTag(id) tags[i] = tag } f.storageChanged(tags) case _, ok = <-leaderSettingsw.Changes(): filterLogger.Debugf("got leader settings change: ok=%t", ok) if !ok { return watcher.EnsureErr(leaderSettingsw) } if sendLeaderSettings { // only send the leader settings changed event // if it hasn't been explicitly disabled f.outLeaderSettings = f.outLeaderSettingsOn } else { filterLogger.Debugf("not sending leader settings change (want=false)") } discardLeaderSettings = f.discardLeaderSettings wantLeaderSettings = f.wantLeaderSettings // Send events on active out chans. case f.outUpgrade <- f.upgrade: filterLogger.Debugf("sent upgrade event") f.outUpgrade = nil case f.outResolved <- f.resolved: filterLogger.Debugf("sent resolved event") f.outResolved = nil case f.outConfig <- nothing: filterLogger.Debugf("sent config event") f.outConfig = nil case f.outLeaderSettings <- nothing: filterLogger.Debugf("sent leader settings event") f.outLeaderSettings = nil case f.outAction <- f.nextAction: f.nextAction = f.getNextAction() filterLogger.Debugf("sent action event") case f.outRelations <- f.relations: filterLogger.Debugf("sent relations event") f.outRelations = nil f.relations = nil case f.outMeterStatus <- nothing: filterLogger.Debugf("sent meter status change event") f.outMeterStatus = nil case f.outStorage <- f.storage: filterLogger.Debugf("sent storage event") f.outStorage = nil f.storage = nil // Handle explicit requests. case curl := <-f.setCharm: filterLogger.Debugf("changing charm to %q", curl) // We need to restart the config watcher after setting the // charm, because service config settings are distinct for // different service charms. if configw != nil { if err := configw.Stop(); err != nil { return err } } if err := f.unit.SetCharmURL(curl); err != nil { filterLogger.Debugf("failed setting charm url %q: %v", curl, err) return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didSetCharm <- nothing: } configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() // Restart the relations watcher. if err := relationsw.Stop(); err != nil { return err } relationsw, err = f.service.WatchRelations() if err != nil { return err } f.upgradeFrom.url = curl if err = f.upgradeChanged(); err != nil { return err } case force := <-f.wantForcedUpgrade: filterLogger.Debugf("want forced upgrade %v", force) f.upgradeFrom.force = force if err = f.upgradeChanged(); err != nil { return err } case <-f.wantResolved: filterLogger.Debugf("want resolved event") if f.resolved != params.ResolvedNone { f.outResolved = f.outResolvedOn } case sendEvents := <-wantLeaderSettings: filterLogger.Debugf("want leader settings event: %t", sendEvents) sendLeaderSettings = sendEvents if sendEvents { // go ahead and send an event right now, // they're waiting for us f.outLeaderSettings = f.outLeaderSettingsOn } else { // Make sure we don't have a pending event f.outLeaderSettings = nil } case <-f.clearResolved: filterLogger.Debugf("resolved event handled") f.outResolved = nil if err := f.unit.ClearResolved(); err != nil { return err } if err := f.unitChanged(); err != nil { return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didClearResolved <- nothing: } case <-discardConfig: filterLogger.Debugf("discarded config event") f.outConfig = nil case <-discardLeaderSettings: filterLogger.Debugf("discarded leader settings event") f.outLeaderSettings = nil } } }