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