func (s *FilterSuite) TestConfigEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) err = s.machine.SetProviderAddresses(network.NewAddress("0.1.2.3")) c.Assert(err, jc.ErrorIsNil) // Test no changes before the charm URL is set. configC := s.notifyAsserterC(c, f.ConfigEvents()) configC.AssertNoReceive() // Set the charm URL to trigger config events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, jc.ErrorIsNil) s.EvilSync() configC.AssertOneReceive() // Change the config; new event received. changeConfig := func(title interface{}) { err := s.wordpress.UpdateConfigSettings(charm.Settings{ "blog-title": title, }) c.Assert(err, jc.ErrorIsNil) } changeConfig("20,000 leagues in the cloud") configC.AssertOneReceive() // Change the config a few more times, then reset the events. We sync to // make sure the events have arrived in the watcher -- and then wait a // little longer, to allow for the delay while the events are coalesced // -- before we tell it to discard all received events. This would be // much better tested by controlling a mocked-out watcher directly, but // that's a bit inconvenient for this change. changeConfig(nil) changeConfig("the curious incident of the dog in the cloud") s.EvilSync() f.DiscardConfigEvent() configC.AssertNoReceive() // Change the addresses of the unit's assigned machine; new event received. err = s.machine.SetProviderAddresses(network.NewAddress("0.1.2.4")) c.Assert(err, jc.ErrorIsNil) s.BackingState.StartSync() configC.AssertOneReceive() // Check that a filter's initial event works with DiscardConfigEvent // as expected. f, err = filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) s.BackingState.StartSync() f.DiscardConfigEvent() configC.AssertNoReceive() // Further changes are still collapsed as appropriate. changeConfig("forsooth") changeConfig("imagination failure") configC.AssertOneReceive() }
func (s *FilterSuite) TestWantLeaderSettingsEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) leaderSettingsC := s.notifyAsserterC(c, f.LeaderSettingsEvents()) // Supress the initial event f.WantLeaderSettingsEvents(false) leaderSettingsC.AssertNoReceive() // Also suppresses actual changes s.setLeaderSetting(c, "foo", "baz-1") s.EvilSync() leaderSettingsC.AssertNoReceive() // Reenabling the settings gives us an immediate change f.WantLeaderSettingsEvents(true) leaderSettingsC.AssertOneReceive() // And also gives changes when actual changes are made s.setLeaderSetting(c, "foo", "baz-2") s.EvilSync() leaderSettingsC.AssertOneReceive() // Setting a value to the same thing doesn't trigger a change s.setLeaderSetting(c, "foo", "baz-2") s.EvilSync() leaderSettingsC.AssertNoReceive() }
func (s *FilterSuite) TestServiceDeath(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) dyingC := s.notifyAsserterC(c, f.UnitDying()) dyingC.AssertNoReceive() err = s.unit.SetAgentStatus(state.StatusIdle, "", nil) c.Assert(err, jc.ErrorIsNil) err = s.wordpress.Destroy() c.Assert(err, jc.ErrorIsNil) timeout := time.After(coretesting.LongWait) loop: for { select { case <-f.UnitDying(): break loop case <-time.After(coretesting.ShortWait): s.BackingState.StartSync() case <-timeout: c.Fatalf("dead not detected") } } err = s.unit.Refresh() c.Assert(err, jc.ErrorIsNil) c.Assert(s.unit.Life(), gc.Equals, state.Dying) // Can't set s.wordpress to Dead while it still has units. }
func (s *FilterSuite) TestUnitDeath(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer f.Stop() // no AssertStop, we test for an error below dyingC := s.notifyAsserterC(c, f.UnitDying()) dyingC.AssertNoReceive() // Irrelevant change. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, jc.ErrorIsNil) dyingC.AssertNoReceive() // Set dying. err = s.unit.SetAgentStatus(state.StatusIdle, "", nil) c.Assert(err, jc.ErrorIsNil) err = s.unit.Destroy() c.Assert(err, jc.ErrorIsNil) dyingC.AssertClosed() // Another irrelevant change. err = s.unit.ClearResolved() c.Assert(err, jc.ErrorIsNil) dyingC.AssertClosed() // Set dead. err = s.unit.EnsureDead() c.Assert(err, jc.ErrorIsNil) s.assertAgentTerminates(c, f) }
func (s *FilterSuite) TestStorageEvents(c *gc.C) { storageCharm := s.AddTestingCharm(c, "storage-block2") svc := s.AddTestingServiceWithStorage(c, "storage-block2", storageCharm, map[string]state.StorageConstraints{ "multi1to10": state.StorageConstraints{Pool: "loop", Size: 1024, Count: 1}, "multi2up": state.StorageConstraints{Pool: "loop", Size: 2048, Count: 2}, }) unit, err := svc.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToNewMachine() c.Assert(err, jc.ErrorIsNil) s.APILogin(c, unit) f, err := filter.NewFilter(s.uniter, unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) storageC := s.contentAsserterC(c, f.StorageEvents()) c.Assert(storageC.AssertOneReceive(), gc.DeepEquals, []names.StorageTag{ names.NewStorageTag("multi1to10/0"), names.NewStorageTag("multi2up/1"), names.NewStorageTag("multi2up/2"), }) err = s.State.DestroyStorageInstance(names.NewStorageTag("multi2up/1")) c.Assert(err, jc.ErrorIsNil) err = s.State.Cleanup() c.Assert(err, jc.ErrorIsNil) c.Assert(storageC.AssertOneReceive(), gc.DeepEquals, []names.StorageTag{ names.NewStorageTag("multi2up/1"), }) }
func (s *FilterSuite) TestRelationsEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) relationsC := s.contentAsserterC(c, f.RelationsEvents()) relationsC.AssertNoReceive() // Add a couple of relations; check the event. rel0 := s.addRelation(c) rel1 := s.addRelation(c) c.Assert(relationsC.AssertOneReceive(), gc.DeepEquals, []int{0, 1}) // Add another relation, and change another's Life (by entering scope before // Destroy, thereby setting the relation to Dying); check event. s.addRelation(c) ru0, err := rel0.Unit(s.unit) c.Assert(err, jc.ErrorIsNil) err = ru0.EnterScope(nil) c.Assert(err, jc.ErrorIsNil) err = rel0.Destroy() c.Assert(err, jc.ErrorIsNil) c.Assert(relationsC.AssertOneReceive(), gc.DeepEquals, []int{0, 2}) // Remove a relation completely; check no event, because the relation // could not have been removed if the unit was in scope, and therefore // the uniter never needs to hear about it. err = rel1.Destroy() c.Assert(err, jc.ErrorIsNil) relationsC.AssertNoReceive() err = f.Stop() c.Assert(err, jc.ErrorIsNil) // Start a new filter, check initial event. f, err = filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) relationsC = s.contentAsserterC(c, f.RelationsEvents()) c.Assert(relationsC.AssertOneReceive(), gc.DeepEquals, []int{0, 2}) // Check setting the charm URL generates all new relation events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, jc.ErrorIsNil) c.Assert(relationsC.AssertOneReceive(), gc.DeepEquals, []int{0, 2}) }
func (s *FilterSuite) TestCharmUpgradeEvents(c *gc.C) { oldCharm := s.AddTestingCharm(c, "upgrade1") svc := s.AddTestingService(c, "upgradetest", oldCharm) unit, err := svc.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToNewMachine() c.Assert(err, jc.ErrorIsNil) s.APILogin(c, unit) f, err := filter.NewFilter(s.uniter, unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) // No initial event is sent. upgradeC := s.contentAsserterC(c, f.UpgradeEvents()) upgradeC.AssertNoReceive() // Setting a charm generates no new events if it already matches. err = f.SetCharm(oldCharm.URL()) c.Assert(err, jc.ErrorIsNil) upgradeC.AssertNoReceive() // Explicitly request an event relative to the existing state; nothing. f.WantUpgradeEvent(false) upgradeC.AssertNoReceive() // Change the service in an irrelevant way; no events. err = svc.SetExposed() c.Assert(err, jc.ErrorIsNil) upgradeC.AssertNoReceive() // Change the service's charm; new event received. newCharm := s.AddTestingCharm(c, "upgrade2") err = svc.SetCharm(newCharm, false) c.Assert(err, jc.ErrorIsNil) upgradeC.AssertOneValue(newCharm.URL()) // Request a new upgrade *unforced* upgrade event, we should see one. f.WantUpgradeEvent(false) upgradeC.AssertOneValue(newCharm.URL()) // Request only *forced* upgrade events; nothing. f.WantUpgradeEvent(true) upgradeC.AssertNoReceive() // But when we have a forced upgrade to the same URL, no new event. err = svc.SetCharm(oldCharm, true) c.Assert(err, jc.ErrorIsNil) upgradeC.AssertNoReceive() // ...but a *forced* change to a different URL should generate an event. err = svc.SetCharm(newCharm, true) c.Assert(err, jc.ErrorIsNil) upgradeC.AssertOneValue(newCharm.URL()) }
func (s *FilterSuite) TestUnitRemoval(c *gc.C) { coretesting.SkipIfI386(c, "lp:1425569") f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer f.Stop() // no AssertStop, we test for an error below // short-circuit to remove because no status set. err = s.unit.Destroy() c.Assert(err, jc.ErrorIsNil) s.assertAgentTerminates(c, f) }
func (u *Uniter) loop(unitTag names.UnitTag) (err error) { if err := u.init(unitTag); err != nil { if err == worker.ErrTerminateAgent { return err } return fmt.Errorf("failed to initialize uniter for %q: %v", unitTag, err) } logger.Infof("unit %q started", u.unit) // Start filtering state change events for consumption by modes. u.f, err = filter.NewFilter(u.st, unitTag) if err != nil { return err } u.addCleanup(u.f.Stop) // Stop the uniter if the filter fails. go func() { u.tomb.Kill(u.f.Wait()) }() // Start handling leader settings events, or not, as appropriate. u.f.WantLeaderSettingsEvents(!u.operationState().Leader) // Run modes until we encounter an error. mode := ModeContinue for err == nil { select { case <-u.tomb.Dying(): err = tomb.ErrDying default: mode, err = mode(u) switch cause := errors.Cause(err); cause { case operation.ErrNeedsReboot: err = worker.ErrRebootMachine case tomb.ErrDying, worker.ErrTerminateAgent: err = cause case operation.ErrHookFailed: mode, err = ModeHookError, nil default: charmURL, ok := operation.DeployConflictCharmURL(cause) if ok { mode, err = ModeConflicted(charmURL), nil } } } } logger.Infof("unit %q shutting down: %s", u.unit, err) return err }
func (s *FilterSuite) TestCharmErrorEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer f.Stop() // no AssertStop, we test for an error below configC := s.notifyAsserterC(c, f.ConfigEvents()) // Check setting an invalid charm URL does not send events. err = f.SetCharm(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, gc.Equals, tomb.ErrDying) configC.AssertNoReceive() s.assertFilterDies(c, f) // Filter died after the error, so restart it. f, err = filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer f.Stop() // no AssertStop, we test for an error below // Check with a nil charm URL, again no changes. err = f.SetCharm(nil) c.Assert(err, gc.Equals, tomb.ErrDying) configC.AssertNoReceive() s.assertFilterDies(c, f) }
func (s *FilterSuite) TestResolvedEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) resolvedC := s.contentAsserterC(c, f.ResolvedEvents()) resolvedC.AssertNoReceive() // Request an event; no interesting event is available. f.WantResolvedEvent() resolvedC.AssertNoReceive() // Change the unit in an irrelevant way; no events. err = s.unit.SetAgentStatus(state.StatusError, "blarg", nil) c.Assert(err, jc.ErrorIsNil) resolvedC.AssertNoReceive() // Change the unit's resolved to an interesting value; new event received. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, jc.ErrorIsNil) resolvedC.AssertOneValue(params.ResolvedRetryHooks) // Ask for the event again, and check it's resent. f.WantResolvedEvent() resolvedC.AssertOneValue(params.ResolvedRetryHooks) // Clear the resolved status *via the filter*; check not resent... err = f.ClearResolved() c.Assert(err, jc.ErrorIsNil) resolvedC.AssertNoReceive() // ...even when requested. f.WantResolvedEvent() resolvedC.AssertNoReceive() // Induce several events; only latest state is reported. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, jc.ErrorIsNil) err = f.ClearResolved() c.Assert(err, jc.ErrorIsNil) err = s.unit.SetResolved(state.ResolvedNoHooks) c.Assert(err, jc.ErrorIsNil) resolvedC.AssertOneValue(params.ResolvedNoHooks) }
func (s *FilterSuite) TestMeterStatusEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) meterC := s.notifyAsserterC(c, f.MeterStatusEvents()) // Initial meter status does not trigger event. meterC.AssertNoReceive() // Set unit meter status to trigger event. err = s.unit.SetMeterStatus("GREEN", "Operating normally.") c.Assert(err, jc.ErrorIsNil) meterC.AssertOneReceive() // Make sure bundled events arrive properly. for i := 0; i < 5; i++ { err = s.unit.SetMeterStatus("RED", fmt.Sprintf("Update %d.", i)) c.Assert(err, jc.ErrorIsNil) } meterC.AssertOneReceive() }
func (s *FilterSuite) TestPreexistingActions(c *gc.C) { addAction := getAddAction(s, c) // Add an Action before the Filter has been created and see if // it arrives properly. testId := addAction("snapshot") // Now create the Filter and see whether the Action comes in as expected. f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) actionC := s.contentAsserterC(c, f.ActionEvents()) assertChange := getAssertActionChange(actionC) assertChange([]string{testId}) // Let's make sure there were no duplicates. actionC.AssertNoReceive() }
func (s *FilterSuite) TestLeaderSettingsEventsSendsChanges(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) leaderSettingsC := s.notifyAsserterC(c, f.LeaderSettingsEvents()) // Assert that we get the initial event leaderSettingsC.AssertOneReceive() // And any time we make changes to the leader settings, we get an event s.setLeaderSetting(c, "foo", "bar-1") leaderSettingsC.AssertOneReceive() // And multiple changes to settings still get collapsed into a single event s.setLeaderSetting(c, "foo", "bar-2") s.setLeaderSetting(c, "foo", "bar-3") s.setLeaderSetting(c, "foo", "bar-4") s.EvilSync() leaderSettingsC.AssertOneReceive() }
func (s *FilterSuite) TestInitialAddressEventIgnored(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) err = s.machine.SetProviderAddresses(network.NewAddress("0.1.2.3")) c.Assert(err, jc.ErrorIsNil) // We should not get any config-change events until // setting the charm URL. configC := s.notifyAsserterC(c, f.ConfigEvents()) configC.AssertNoReceive() // Set the charm URL to trigger config events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, jc.ErrorIsNil) // We should get one config-change event only. s.EvilSync() configC.AssertOneReceive() }
func (s *FilterSuite) TestConfigAndAddressEventsDiscarded(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) // There should be no pending changes yet configC := s.notifyAsserterC(c, f.ConfigEvents()) configC.AssertNoReceive() // Change the machine addresses. err = s.machine.SetProviderAddresses(network.NewAddress("0.1.2.3")) c.Assert(err, jc.ErrorIsNil) // Set the charm URL to trigger config events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, jc.ErrorIsNil) // We should not receive any config-change events. s.EvilSync() f.DiscardConfigEvent() configC.AssertNoReceive() }
func (s *FilterSuite) TestDiscardLeaderSettingsEvent(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) leaderSettingsC := s.notifyAsserterC(c, f.LeaderSettingsEvents()) // Discard the initial event f.DiscardLeaderSettingsEvent() leaderSettingsC.AssertNoReceive() // However, it has not permanently disabled change events, another // change still shows up s.setLeaderSetting(c, "foo", "bing-1") s.EvilSync() leaderSettingsC.AssertOneReceive() // But at any point we can discard them s.setLeaderSetting(c, "foo", "bing-2") s.EvilSync() f.DiscardLeaderSettingsEvent() leaderSettingsC.AssertNoReceive() }
func (s *FilterSuite) TestActionEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) actionC := s.contentAsserterC(c, f.ActionEvents()) addAction := getAddAction(s, c) assertChange := getAssertActionChange(actionC) // Test no changes before Actions are added for the Unit. actionC.AssertNoReceive() // Add a new action; event occurs testId := addAction("fakeaction") assertChange([]string{testId}) // Make sure bundled events arrive properly. testIds := make([]string, 5) for i := 0; i < 5; i++ { testIds[i] = addAction("fakeaction") } assertChange(testIds) }
func (s *FilterSuite) TestConfigAndAddressEvents(c *gc.C) { f, err := filter.NewFilter(s.uniter, s.unit.Tag().(names.UnitTag)) c.Assert(err, jc.ErrorIsNil) defer statetesting.AssertStop(c, f) // Set the charm URL to trigger config events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, jc.ErrorIsNil) // Changing the machine addresses should also result in // a config-change event. err = s.machine.SetProviderAddresses( network.NewAddress("0.1.2.3"), ) c.Assert(err, jc.ErrorIsNil) configC := s.notifyAsserterC(c, f.ConfigEvents()) // Config and address events should be coalesced. Start // the synchronisation and sleep a bit to give the filter // a chance to pick them both up. s.EvilSync() configC.AssertOneReceive() }
func (u *Uniter) loop(unitTag names.UnitTag) (err error) { // Start tracking leadership state. // TODO(fwereade): ideally, this wouldn't be created here; as a worker it's // clearly better off being managed by a Runner. However, we haven't come up // with a clean way to reference one (lineage of a...) worker from another, // so for now the tracker is accessible only to its unit. leadershipTracker := leadership.NewTrackerWorker( unitTag, u.leadershipClaimer, leadershipGuarantee, ) u.addCleanup(func() error { return worker.Stop(leadershipTracker) }) u.leadershipTracker = leadershipTracker if err := u.init(unitTag); err != nil { if err == worker.ErrTerminateAgent { return err } return fmt.Errorf("failed to initialize uniter for %q: %v", unitTag, err) } logger.Infof("unit %q started", u.unit) // Start filtering state change events for consumption by modes. u.f, err = filter.NewFilter(u.st, unitTag) if err != nil { return err } u.addCleanup(u.f.Stop) // Stop the uniter if either of these components fails. go func() { u.tomb.Kill(leadershipTracker.Wait()) }() go func() { u.tomb.Kill(u.f.Wait()) }() // Start handling leader settings events, or not, as appropriate. u.f.WantLeaderSettingsEvents(!u.operationState().Leader) // Run modes until we encounter an error. mode := ModeContinue for err == nil { select { case <-u.tomb.Dying(): err = tomb.ErrDying default: mode, err = mode(u) switch cause := errors.Cause(err); cause { case operation.ErrNeedsReboot: err = worker.ErrRebootMachine case tomb.ErrDying, worker.ErrTerminateAgent: err = cause case operation.ErrHookFailed: mode, err = ModeHookError, nil default: charmURL, ok := operation.DeployConflictCharmURL(cause) if ok { mode, err = ModeConflicted(charmURL), nil } } } } logger.Infof("unit %q shutting down: %s", u.unit, err) return err }