github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/remotestate/watcher_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package remotestate_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/names"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v6-unstable"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/state/multiwatcher"
    16  	"github.com/juju/juju/testing"
    17  	"github.com/juju/juju/worker/uniter/remotestate"
    18  )
    19  
    20  type WatcherSuite struct {
    21  	testing.BaseSuite
    22  
    23  	st         mockState
    24  	leadership mockLeadershipTracker
    25  	watcher    *remotestate.RemoteStateWatcher
    26  	clock      *testing.Clock
    27  }
    28  
    29  // Duration is arbitrary, we'll trigger the ticker
    30  // by advancing the clock past the duration.
    31  var statusTickDuration = 10 * time.Second
    32  
    33  var _ = gc.Suite(&WatcherSuite{})
    34  
    35  func (s *WatcherSuite) SetUpTest(c *gc.C) {
    36  	s.BaseSuite.SetUpTest(c)
    37  	s.st = mockState{
    38  		unit: mockUnit{
    39  			tag:  names.NewUnitTag("mysql/0"),
    40  			life: params.Alive,
    41  			service: mockService{
    42  				tag:            names.NewServiceTag("mysql"),
    43  				life:           params.Alive,
    44  				curl:           charm.MustParseURL("cs:trusty/mysql"),
    45  				serviceWatcher: mockNotifyWatcher{changes: make(chan struct{}, 1)},
    46  				leaderSettingsWatcher: mockNotifyWatcher{
    47  					changes: make(chan struct{}, 1),
    48  				},
    49  				relationsWatcher: mockStringsWatcher{
    50  					changes: make(chan []string, 1),
    51  				},
    52  			},
    53  			unitWatcher:           mockNotifyWatcher{changes: make(chan struct{}, 1)},
    54  			addressesWatcher:      mockNotifyWatcher{changes: make(chan struct{}, 1)},
    55  			configSettingsWatcher: mockNotifyWatcher{changes: make(chan struct{}, 1)},
    56  			storageWatcher:        mockStringsWatcher{changes: make(chan []string, 1)},
    57  			actionWatcher:         mockStringsWatcher{changes: make(chan []string, 1)},
    58  		},
    59  		relations:                 make(map[names.RelationTag]*mockRelation),
    60  		storageAttachment:         make(map[params.StorageAttachmentId]params.StorageAttachment),
    61  		relationUnitsWatchers:     make(map[names.RelationTag]*mockRelationUnitsWatcher),
    62  		storageAttachmentWatchers: make(map[names.StorageTag]*mockStorageAttachmentWatcher),
    63  	}
    64  
    65  	s.leadership = mockLeadershipTracker{
    66  		claimTicket:  mockTicket{make(chan struct{}, 1), true},
    67  		leaderTicket: mockTicket{make(chan struct{}, 1), true},
    68  		minionTicket: mockTicket{make(chan struct{}, 1), true},
    69  	}
    70  
    71  	s.clock = testing.NewClock(time.Now())
    72  	statusTicker := func() <-chan time.Time {
    73  		return s.clock.After(statusTickDuration)
    74  	}
    75  
    76  	w, err := remotestate.NewWatcher(remotestate.WatcherConfig{
    77  		State:               &s.st,
    78  		LeadershipTracker:   &s.leadership,
    79  		UnitTag:             s.st.unit.tag,
    80  		UpdateStatusChannel: statusTicker,
    81  	})
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	s.watcher = w
    84  }
    85  
    86  func (s *WatcherSuite) TearDownTest(c *gc.C) {
    87  	err := s.watcher.Stop()
    88  	c.Assert(err, jc.ErrorIsNil)
    89  }
    90  
    91  func (s *WatcherSuite) TestInitialSnapshot(c *gc.C) {
    92  	snap := s.watcher.Snapshot()
    93  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
    94  		Relations: map[int]remotestate.RelationSnapshot{},
    95  		Storage:   map[names.StorageTag]remotestate.StorageSnapshot{},
    96  	})
    97  }
    98  
    99  func (s *WatcherSuite) TestInitialSignal(c *gc.C) {
   100  	// There should not be a remote state change until
   101  	// we've seen all of the top-level notifications.
   102  	s.st.unit.unitWatcher.changes <- struct{}{}
   103  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   104  
   105  	s.st.unit.addressesWatcher.changes <- struct{}{}
   106  	s.st.unit.configSettingsWatcher.changes <- struct{}{}
   107  	s.st.unit.storageWatcher.changes <- []string{}
   108  	s.st.unit.actionWatcher.changes <- []string{}
   109  	s.st.unit.service.serviceWatcher.changes <- struct{}{}
   110  	s.st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   111  	s.st.unit.service.relationsWatcher.changes <- []string{}
   112  	s.leadership.claimTicket.ch <- struct{}{}
   113  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   114  }
   115  
   116  func signalAll(st *mockState, l *mockLeadershipTracker) {
   117  	st.unit.unitWatcher.changes <- struct{}{}
   118  	st.unit.addressesWatcher.changes <- struct{}{}
   119  	st.unit.configSettingsWatcher.changes <- struct{}{}
   120  	st.unit.storageWatcher.changes <- []string{}
   121  	st.unit.actionWatcher.changes <- []string{}
   122  	st.unit.service.serviceWatcher.changes <- struct{}{}
   123  	st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   124  	st.unit.service.relationsWatcher.changes <- []string{}
   125  	l.claimTicket.ch <- struct{}{}
   126  }
   127  
   128  func (s *WatcherSuite) TestSnapshot(c *gc.C) {
   129  	signalAll(&s.st, &s.leadership)
   130  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   131  
   132  	snap := s.watcher.Snapshot()
   133  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
   134  		Life:                  s.st.unit.life,
   135  		Relations:             map[int]remotestate.RelationSnapshot{},
   136  		Storage:               map[names.StorageTag]remotestate.StorageSnapshot{},
   137  		CharmURL:              s.st.unit.service.curl,
   138  		ForceCharmUpgrade:     s.st.unit.service.forceUpgrade,
   139  		ResolvedMode:          s.st.unit.resolved,
   140  		ConfigVersion:         2, // config settings and addresses
   141  		LeaderSettingsVersion: 1,
   142  		Leader:                true,
   143  	})
   144  }
   145  
   146  func (s *WatcherSuite) TestRemoteStateChanged(c *gc.C) {
   147  	assertOneChange := func() {
   148  		assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   149  		assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   150  	}
   151  
   152  	signalAll(&s.st, &s.leadership)
   153  	assertOneChange()
   154  	initial := s.watcher.Snapshot()
   155  
   156  	s.st.unit.life = params.Dying
   157  	s.st.unit.unitWatcher.changes <- struct{}{}
   158  	assertOneChange()
   159  	c.Assert(s.watcher.Snapshot().Life, gc.Equals, params.Dying)
   160  
   161  	s.st.unit.addressesWatcher.changes <- struct{}{}
   162  	assertOneChange()
   163  	c.Assert(s.watcher.Snapshot().ConfigVersion, gc.Equals, initial.ConfigVersion+1)
   164  
   165  	s.st.unit.configSettingsWatcher.changes <- struct{}{}
   166  	assertOneChange()
   167  	c.Assert(s.watcher.Snapshot().ConfigVersion, gc.Equals, initial.ConfigVersion+2)
   168  
   169  	s.st.unit.storageWatcher.changes <- []string{}
   170  	assertOneChange()
   171  
   172  	s.st.unit.service.forceUpgrade = true
   173  	s.st.unit.service.serviceWatcher.changes <- struct{}{}
   174  	assertOneChange()
   175  	c.Assert(s.watcher.Snapshot().ForceCharmUpgrade, jc.IsTrue)
   176  
   177  	s.st.unit.service.leaderSettingsWatcher.changes <- struct{}{}
   178  	assertOneChange()
   179  	c.Assert(s.watcher.Snapshot().LeaderSettingsVersion, gc.Equals, initial.LeaderSettingsVersion+1)
   180  
   181  	s.st.unit.service.relationsWatcher.changes <- []string{}
   182  	assertOneChange()
   183  
   184  	s.clock.Advance(statusTickDuration + 1)
   185  	assertOneChange()
   186  }
   187  
   188  func (s *WatcherSuite) TestActionsReceived(c *gc.C) {
   189  	signalAll(&s.st, &s.leadership)
   190  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   191  
   192  	s.st.unit.actionWatcher.changes <- []string{"an-action"}
   193  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   194  	c.Assert(s.watcher.Snapshot().Actions, gc.DeepEquals, []string{"an-action"})
   195  }
   196  
   197  func (s *WatcherSuite) TestClearResolvedMode(c *gc.C) {
   198  	s.st.unit.resolved = params.ResolvedRetryHooks
   199  	signalAll(&s.st, &s.leadership)
   200  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   201  
   202  	snap := s.watcher.Snapshot()
   203  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedRetryHooks)
   204  
   205  	s.watcher.ClearResolvedMode()
   206  	snap = s.watcher.Snapshot()
   207  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedNone)
   208  }
   209  
   210  func (s *WatcherSuite) TestLeadershipChanged(c *gc.C) {
   211  	s.leadership.claimTicket.result = false
   212  	signalAll(&s.st, &s.leadership)
   213  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   214  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   215  
   216  	s.leadership.leaderTicket.ch <- struct{}{}
   217  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   218  	c.Assert(s.watcher.Snapshot().Leader, jc.IsTrue)
   219  
   220  	s.leadership.minionTicket.ch <- struct{}{}
   221  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   222  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   223  }
   224  
   225  func (s *WatcherSuite) TestLeadershipMinionUnchanged(c *gc.C) {
   226  	s.leadership.claimTicket.result = false
   227  	signalAll(&s.st, &s.leadership)
   228  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   229  
   230  	// Initially minion, so triggering minion should have no effect.
   231  	s.leadership.minionTicket.ch <- struct{}{}
   232  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   233  }
   234  
   235  func (s *WatcherSuite) TestLeadershipLeaderUnchanged(c *gc.C) {
   236  	signalAll(&s.st, &s.leadership)
   237  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   238  
   239  	// Initially leader, so triggering leader should have no effect.
   240  	s.leadership.leaderTicket.ch <- struct{}{}
   241  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   242  }
   243  
   244  func (s *WatcherSuite) TestStorageChanged(c *gc.C) {
   245  	signalAll(&s.st, &s.leadership)
   246  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   247  
   248  	storageTag0 := names.NewStorageTag("blob/0")
   249  	storageAttachmentId0 := params.StorageAttachmentId{
   250  		UnitTag:    s.st.unit.tag.String(),
   251  		StorageTag: storageTag0.String(),
   252  	}
   253  	storageTag0Watcher := &mockStorageAttachmentWatcher{
   254  		changes: make(chan struct{}, 1),
   255  	}
   256  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   257  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   258  		UnitTag:    storageAttachmentId0.UnitTag,
   259  		StorageTag: storageAttachmentId0.StorageTag,
   260  		Life:       params.Alive,
   261  		Kind:       params.StorageKindUnknown, // unprovisioned
   262  		Location:   "nowhere",
   263  	}
   264  
   265  	storageTag1 := names.NewStorageTag("blob/1")
   266  	storageAttachmentId1 := params.StorageAttachmentId{
   267  		UnitTag:    s.st.unit.tag.String(),
   268  		StorageTag: storageTag1.String(),
   269  	}
   270  	storageTag1Watcher := &mockStorageAttachmentWatcher{
   271  		changes: make(chan struct{}, 1),
   272  	}
   273  	s.st.storageAttachmentWatchers[storageTag1] = storageTag1Watcher
   274  	s.st.storageAttachment[storageAttachmentId1] = params.StorageAttachment{
   275  		UnitTag:    storageAttachmentId1.UnitTag,
   276  		StorageTag: storageAttachmentId1.StorageTag,
   277  		Life:       params.Dying,
   278  		Kind:       params.StorageKindBlock,
   279  		Location:   "malta",
   280  	}
   281  
   282  	// We should not see any event until the storage attachment watchers
   283  	// return their initial events.
   284  	s.st.unit.storageWatcher.changes <- []string{"blob/0", "blob/1"}
   285  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   286  	storageTag0Watcher.changes <- struct{}{}
   287  	storageTag1Watcher.changes <- struct{}{}
   288  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   289  
   290  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   291  		storageTag0: remotestate.StorageSnapshot{
   292  			Life: params.Alive,
   293  		},
   294  		storageTag1: remotestate.StorageSnapshot{
   295  			Life:     params.Dying,
   296  			Kind:     params.StorageKindBlock,
   297  			Attached: true,
   298  			Location: "malta",
   299  		},
   300  	})
   301  
   302  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   303  		UnitTag:    storageAttachmentId0.UnitTag,
   304  		StorageTag: storageAttachmentId0.StorageTag,
   305  		Life:       params.Dying,
   306  		Kind:       params.StorageKindFilesystem,
   307  		Location:   "somewhere",
   308  	}
   309  	delete(s.st.storageAttachment, storageAttachmentId1)
   310  	storageTag0Watcher.changes <- struct{}{}
   311  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   312  	s.st.unit.storageWatcher.changes <- []string{"blob/1"}
   313  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   314  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   315  		storageTag0: remotestate.StorageSnapshot{
   316  			Life:     params.Dying,
   317  			Attached: true,
   318  			Kind:     params.StorageKindFilesystem,
   319  			Location: "somewhere",
   320  		},
   321  	})
   322  }
   323  
   324  func (s *WatcherSuite) TestStorageUnattachedChanged(c *gc.C) {
   325  	signalAll(&s.st, &s.leadership)
   326  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   327  
   328  	storageTag0 := names.NewStorageTag("blob/0")
   329  	storageAttachmentId0 := params.StorageAttachmentId{
   330  		UnitTag:    s.st.unit.tag.String(),
   331  		StorageTag: storageTag0.String(),
   332  	}
   333  	storageTag0Watcher := &mockStorageAttachmentWatcher{
   334  		changes: make(chan struct{}, 1),
   335  	}
   336  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   337  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   338  		UnitTag:    storageAttachmentId0.UnitTag,
   339  		StorageTag: storageAttachmentId0.StorageTag,
   340  		Life:       params.Alive,
   341  		Kind:       params.StorageKindUnknown, // unprovisioned
   342  	}
   343  
   344  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   345  	storageTag0Watcher.changes <- struct{}{}
   346  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   347  
   348  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   349  		storageTag0: remotestate.StorageSnapshot{
   350  			Life: params.Alive,
   351  		},
   352  	})
   353  
   354  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   355  		UnitTag:    storageAttachmentId0.UnitTag,
   356  		StorageTag: storageAttachmentId0.StorageTag,
   357  		Life:       params.Dying,
   358  	}
   359  	// The storage is still unattached; triggering the storage-specific
   360  	// watcher should not cause any event to be emitted.
   361  	storageTag0Watcher.changes <- struct{}{}
   362  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   363  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   364  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   365  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   366  		storageTag0: remotestate.StorageSnapshot{
   367  			Life: params.Dying,
   368  		},
   369  	})
   370  }
   371  
   372  func (s *WatcherSuite) TestStorageAttachmentRemoved(c *gc.C) {
   373  	signalAll(&s.st, &s.leadership)
   374  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   375  
   376  	storageTag0 := names.NewStorageTag("blob/0")
   377  	storageAttachmentId0 := params.StorageAttachmentId{
   378  		UnitTag:    s.st.unit.tag.String(),
   379  		StorageTag: storageTag0.String(),
   380  	}
   381  	storageTag0Watcher := &mockStorageAttachmentWatcher{
   382  		changes: make(chan struct{}, 1),
   383  	}
   384  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   385  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   386  		UnitTag:    storageAttachmentId0.UnitTag,
   387  		StorageTag: storageAttachmentId0.StorageTag,
   388  		Life:       params.Dying,
   389  		Kind:       params.StorageKindUnknown, // unprovisioned
   390  	}
   391  
   392  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   393  	storageTag0Watcher.changes <- struct{}{}
   394  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   395  
   396  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   397  		storageTag0: remotestate.StorageSnapshot{
   398  			Life: params.Dying,
   399  		},
   400  	})
   401  
   402  	// Removing the storage attachment and then triggering the storage-
   403  	// specific watcher should not cause an event to be emitted, but it
   404  	// will cause that watcher to stop running. Triggering the top-level
   405  	// storage watcher will remove it and update the snapshot.
   406  	delete(s.st.storageAttachment, storageAttachmentId0)
   407  	storageTag0Watcher.changes <- struct{}{}
   408  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   409  	c.Assert(storageTag0Watcher.stopped, jc.IsTrue)
   410  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   411  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   412  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   413  }
   414  
   415  func (s *WatcherSuite) TestStorageChangedNotFoundInitially(c *gc.C) {
   416  	signalAll(&s.st, &s.leadership)
   417  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   418  
   419  	// blob/0 is initially in state, but is removed between the
   420  	// watcher signal and the uniter querying it. This should
   421  	// not cause the watcher to raise an error.
   422  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   423  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   424  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   425  }
   426  
   427  func (s *WatcherSuite) TestRelationsChanged(c *gc.C) {
   428  	signalAll(&s.st, &s.leadership)
   429  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   430  
   431  	relationTag := names.NewRelationTag("mysql:peer")
   432  	s.st.relations[relationTag] = &mockRelation{
   433  		id: 123, life: params.Alive,
   434  	}
   435  	s.st.relationUnitsWatchers[relationTag] = &mockRelationUnitsWatcher{
   436  		changes: make(chan multiwatcher.RelationUnitsChange, 1),
   437  	}
   438  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   439  
   440  	// There should not be any signal until the relation units watcher has
   441  	// returned its initial event also.
   442  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   443  	s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{
   444  		Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {1}, "mysql/2": {2}},
   445  	}
   446  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   447  	c.Assert(
   448  		s.watcher.Snapshot().Relations,
   449  		jc.DeepEquals,
   450  		map[int]remotestate.RelationSnapshot{
   451  			123: remotestate.RelationSnapshot{
   452  				Life:    params.Alive,
   453  				Members: map[string]int64{"mysql/1": 1, "mysql/2": 2},
   454  			},
   455  		},
   456  	)
   457  
   458  	// If a relation is known, then updating it does not require any input
   459  	// from the relation units watcher.
   460  	s.st.relations[relationTag].life = params.Dying
   461  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   462  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   463  	c.Assert(s.watcher.Snapshot().Relations[123].Life, gc.Equals, params.Dying)
   464  
   465  	// If a relation is not found, then it should be removed from the
   466  	// snapshot and its relation units watcher stopped.
   467  	delete(s.st.relations, relationTag)
   468  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   469  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   470  	c.Assert(s.watcher.Snapshot().Relations, gc.HasLen, 0)
   471  	c.Assert(s.st.relationUnitsWatchers[relationTag].stopped, jc.IsTrue)
   472  }
   473  
   474  func (s *WatcherSuite) TestRelationUnitsChanged(c *gc.C) {
   475  	signalAll(&s.st, &s.leadership)
   476  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   477  
   478  	relationTag := names.NewRelationTag("mysql:peer")
   479  	s.st.relations[relationTag] = &mockRelation{
   480  		id: 123, life: params.Alive,
   481  	}
   482  	s.st.relationUnitsWatchers[relationTag] = &mockRelationUnitsWatcher{
   483  		changes: make(chan multiwatcher.RelationUnitsChange, 1),
   484  	}
   485  
   486  	s.st.unit.service.relationsWatcher.changes <- []string{relationTag.Id()}
   487  	s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{
   488  		Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {1}},
   489  	}
   490  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   491  
   492  	s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{
   493  		Changed: map[string]multiwatcher.UnitSettings{"mysql/1": {2}, "mysql/2": {1}},
   494  	}
   495  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   496  	c.Assert(
   497  		s.watcher.Snapshot().Relations[123].Members,
   498  		jc.DeepEquals,
   499  		map[string]int64{"mysql/1": 2, "mysql/2": 1},
   500  	)
   501  
   502  	s.st.relationUnitsWatchers[relationTag].changes <- multiwatcher.RelationUnitsChange{
   503  		Departed: []string{"mysql/1", "mysql/42"},
   504  	}
   505  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   506  	c.Assert(
   507  		s.watcher.Snapshot().Relations[123].Members,
   508  		jc.DeepEquals,
   509  		map[string]int64{"mysql/2": 1},
   510  	)
   511  }
   512  
   513  func (s *WatcherSuite) TestUpdateStatusTicker(c *gc.C) {
   514  	signalAll(&s.st, &s.leadership)
   515  	initial := s.watcher.Snapshot()
   516  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   517  
   518  	// Advance the clock past the trigger time.
   519  	s.clock.Advance(11 * time.Second)
   520  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   521  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   522  
   523  	// Advance again but not past the trigger time.
   524  	s.clock.Advance(6 * time.Second)
   525  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "unexpected remote state change")
   526  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   527  
   528  	// And we hit the trigger time.
   529  	s.clock.Advance(5 * time.Second)
   530  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   531  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+2)
   532  }