github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/clock/testclock"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/charm.v6"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/lxdprofile"
    17  	"github.com/juju/juju/core/model"
    18  	"github.com/juju/juju/core/watcher"
    19  	coretesting "github.com/juju/juju/testing"
    20  	"github.com/juju/juju/worker/uniter/remotestate"
    21  )
    22  
    23  type WatcherSuite struct {
    24  	coretesting.BaseSuite
    25  
    26  	modelType  model.ModelType
    27  	st         *mockState
    28  	leadership *mockLeadershipTracker
    29  	watcher    *remotestate.RemoteStateWatcher
    30  	clock      *testclock.Clock
    31  
    32  	applicationWatcher *mockNotifyWatcher
    33  }
    34  
    35  type WatcherSuiteIAAS struct {
    36  	WatcherSuite
    37  }
    38  
    39  type WatcherSuiteCAAS struct {
    40  	WatcherSuite
    41  }
    42  
    43  var _ = gc.Suite(&WatcherSuiteIAAS{WatcherSuite{modelType: model.IAAS}})
    44  var _ = gc.Suite(&WatcherSuiteCAAS{WatcherSuite{modelType: model.CAAS}})
    45  
    46  func (s *WatcherSuite) SetUpTest(c *gc.C) {
    47  	s.BaseSuite.SetUpTest(c)
    48  	s.st = &mockState{
    49  		modelType: s.modelType,
    50  		unit: mockUnit{
    51  			tag:  names.NewUnitTag("mysql/0"),
    52  			life: params.Alive,
    53  			application: mockApplication{
    54  				tag:                   names.NewApplicationTag("mysql"),
    55  				life:                  params.Alive,
    56  				curl:                  charm.MustParseURL("cs:trusty/mysql"),
    57  				charmModifiedVersion:  5,
    58  				leaderSettingsWatcher: newMockNotifyWatcher(),
    59  			},
    60  			unitWatcher:                      newMockNotifyWatcher(),
    61  			addressesWatcher:                 newMockStringsWatcher(),
    62  			configSettingsWatcher:            newMockStringsWatcher(),
    63  			applicationConfigSettingsWatcher: newMockStringsWatcher(),
    64  			storageWatcher:                   newMockStringsWatcher(),
    65  			actionWatcher:                    newMockStringsWatcher(),
    66  			relationsWatcher:                 newMockStringsWatcher(),
    67  			upgradeLXDProfileUpgradeWatcher:  newMockStringsWatcher(),
    68  		},
    69  		relations:                   make(map[names.RelationTag]*mockRelation),
    70  		storageAttachment:           make(map[params.StorageAttachmentId]params.StorageAttachment),
    71  		relationUnitsWatchers:       make(map[names.RelationTag]*mockRelationUnitsWatcher),
    72  		storageAttachmentWatchers:   make(map[names.StorageTag]*mockNotifyWatcher),
    73  		updateStatusInterval:        5 * time.Minute,
    74  		updateStatusIntervalWatcher: newMockNotifyWatcher(),
    75  	}
    76  
    77  	s.leadership = &mockLeadershipTracker{
    78  		claimTicket:  mockTicket{make(chan struct{}, 1), true},
    79  		leaderTicket: mockTicket{make(chan struct{}, 1), true},
    80  		minionTicket: mockTicket{make(chan struct{}, 1), true},
    81  	}
    82  
    83  	s.clock = testclock.NewClock(time.Now())
    84  }
    85  
    86  func (s *WatcherSuiteIAAS) SetUpTest(c *gc.C) {
    87  	s.WatcherSuite.SetUpTest(c)
    88  	statusTicker := func(wait time.Duration) remotestate.Waiter {
    89  		return dummyWaiter{s.clock.After(wait)}
    90  	}
    91  
    92  	s.st.unit.application.applicationWatcher = newMockNotifyWatcher()
    93  	s.applicationWatcher = s.st.unit.application.applicationWatcher
    94  	s.st.unit.upgradeSeriesWatcher = newMockNotifyWatcher()
    95  	s.st.unit.upgradeLXDProfileUpgradeWatcher = newMockStringsWatcher()
    96  	w, err := remotestate.NewWatcher(remotestate.WatcherConfig{
    97  		State:               s.st,
    98  		ModelType:           s.modelType,
    99  		LeadershipTracker:   s.leadership,
   100  		UnitTag:             s.st.unit.tag,
   101  		UpdateStatusChannel: statusTicker,
   102  	})
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	s.watcher = w
   105  }
   106  
   107  func (s *WatcherSuiteCAAS) SetUpTest(c *gc.C) {
   108  	s.WatcherSuite.SetUpTest(c)
   109  	statusTicker := func(wait time.Duration) remotestate.Waiter {
   110  		return dummyWaiter{s.clock.After(wait)}
   111  	}
   112  
   113  	s.applicationWatcher = newMockNotifyWatcher()
   114  	w, err := remotestate.NewWatcher(remotestate.WatcherConfig{
   115  		State:               s.st,
   116  		ModelType:           s.modelType,
   117  		LeadershipTracker:   s.leadership,
   118  		UnitTag:             s.st.unit.tag,
   119  		UpdateStatusChannel: statusTicker,
   120  		ApplicationChannel:  s.applicationWatcher.Changes(),
   121  	})
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	s.watcher = w
   124  }
   125  
   126  type dummyWaiter struct {
   127  	c <-chan time.Time
   128  }
   129  
   130  func (w dummyWaiter) After() <-chan time.Time {
   131  	return w.c
   132  }
   133  
   134  func (s *WatcherSuite) TearDownTest(c *gc.C) {
   135  	if s.watcher != nil {
   136  		s.watcher.Kill()
   137  		err := s.watcher.Wait()
   138  		c.Assert(err, jc.ErrorIsNil)
   139  	}
   140  }
   141  
   142  func (s *WatcherSuite) TestInitialSnapshot(c *gc.C) {
   143  	snap := s.watcher.Snapshot()
   144  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
   145  		Relations: map[int]remotestate.RelationSnapshot{},
   146  		Storage:   map[names.StorageTag]remotestate.StorageSnapshot{},
   147  	})
   148  }
   149  
   150  func (s *WatcherSuite) TestInitialSignal(c *gc.C) {
   151  	// There should not be a remote state change until
   152  	// we've seen all of the top-level notifications.
   153  	s.st.unit.unitWatcher.changes <- struct{}{}
   154  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   155  	s.st.unit.addressesWatcher.changes <- []string{"addresseshash"}
   156  	s.st.unit.configSettingsWatcher.changes <- []string{"confighash"}
   157  	s.st.unit.applicationConfigSettingsWatcher.changes <- []string{"trusthash"}
   158  	if s.st.unit.upgradeSeriesWatcher != nil {
   159  		s.st.unit.upgradeSeriesWatcher.changes <- struct{}{}
   160  	}
   161  	if s.st.unit.upgradeLXDProfileUpgradeWatcher != nil {
   162  		s.st.unit.upgradeLXDProfileUpgradeWatcher.changes <- []string{lxdprofile.SuccessStatus}
   163  	}
   164  	s.st.unit.storageWatcher.changes <- []string{}
   165  	s.st.unit.actionWatcher.changes <- []string{}
   166  	if s.st.unit.application.applicationWatcher != nil {
   167  		s.st.unit.application.applicationWatcher.changes <- struct{}{}
   168  	}
   169  	s.st.unit.application.leaderSettingsWatcher.changes <- struct{}{}
   170  	s.st.unit.relationsWatcher.changes <- []string{}
   171  	s.st.updateStatusIntervalWatcher.changes <- struct{}{}
   172  	s.leadership.claimTicket.ch <- struct{}{}
   173  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   174  }
   175  
   176  func (s *WatcherSuite) signalAll() {
   177  	s.st.unit.unitWatcher.changes <- struct{}{}
   178  	s.st.unit.configSettingsWatcher.changes <- []string{"confighash"}
   179  	s.st.unit.applicationConfigSettingsWatcher.changes <- []string{"trusthash"}
   180  	s.st.unit.actionWatcher.changes <- []string{}
   181  	s.st.unit.application.leaderSettingsWatcher.changes <- struct{}{}
   182  	s.st.unit.relationsWatcher.changes <- []string{}
   183  	s.st.unit.addressesWatcher.changes <- []string{"addresseshash"}
   184  	s.st.updateStatusIntervalWatcher.changes <- struct{}{}
   185  	s.leadership.claimTicket.ch <- struct{}{}
   186  	s.st.unit.storageWatcher.changes <- []string{}
   187  	if s.st.modelType == model.IAAS {
   188  		s.applicationWatcher.changes <- struct{}{}
   189  		s.st.unit.upgradeSeriesWatcher.changes <- struct{}{}
   190  		s.st.unit.upgradeLXDProfileUpgradeWatcher.changes <- []string{lxdprofile.NotRequiredStatus}
   191  	}
   192  }
   193  
   194  func (s *WatcherSuiteIAAS) TestSnapshot(c *gc.C) {
   195  	s.signalAll()
   196  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   197  
   198  	snap := s.watcher.Snapshot()
   199  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
   200  		Life:                      s.st.unit.life,
   201  		Relations:                 map[int]remotestate.RelationSnapshot{},
   202  		Storage:                   map[names.StorageTag]remotestate.StorageSnapshot{},
   203  		CharmModifiedVersion:      s.st.unit.application.charmModifiedVersion,
   204  		CharmURL:                  s.st.unit.application.curl,
   205  		ForceCharmUpgrade:         s.st.unit.application.forceUpgrade,
   206  		ResolvedMode:              s.st.unit.resolved,
   207  		ConfigHash:                "confighash",
   208  		TrustHash:                 "trusthash",
   209  		AddressesHash:             "addresseshash",
   210  		LeaderSettingsVersion:     1,
   211  		Leader:                    true,
   212  		UpgradeSeriesStatus:       model.UpgradeSeriesPrepareStarted,
   213  		UpgradeCharmProfileStatus: lxdprofile.NotRequiredStatus,
   214  	})
   215  }
   216  
   217  func (s *WatcherSuiteCAAS) TestSnapshot(c *gc.C) {
   218  	s.signalAll()
   219  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   220  
   221  	snap := s.watcher.Snapshot()
   222  	c.Assert(snap, jc.DeepEquals, remotestate.Snapshot{
   223  		Life:                      s.st.unit.life,
   224  		Relations:                 map[int]remotestate.RelationSnapshot{},
   225  		Storage:                   map[names.StorageTag]remotestate.StorageSnapshot{},
   226  		CharmModifiedVersion:      0,
   227  		CharmURL:                  nil,
   228  		ForceCharmUpgrade:         s.st.unit.application.forceUpgrade,
   229  		ResolvedMode:              s.st.unit.resolved,
   230  		ConfigHash:                "confighash",
   231  		TrustHash:                 "trusthash",
   232  		AddressesHash:             "addresseshash",
   233  		LeaderSettingsVersion:     1,
   234  		Leader:                    true,
   235  		UpgradeSeriesStatus:       "",
   236  		UpgradeCharmProfileStatus: "",
   237  	})
   238  }
   239  
   240  func (s *WatcherSuite) TestRemoteStateChanged(c *gc.C) {
   241  	assertOneChange := func() {
   242  		assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   243  		assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   244  	}
   245  
   246  	s.signalAll()
   247  	assertOneChange()
   248  	initial := s.watcher.Snapshot()
   249  
   250  	s.st.unit.life = params.Dying
   251  	s.st.unit.unitWatcher.changes <- struct{}{}
   252  	assertOneChange()
   253  	c.Assert(s.watcher.Snapshot().Life, gc.Equals, params.Dying)
   254  
   255  	s.st.unit.resolved = params.ResolvedRetryHooks
   256  	s.st.unit.unitWatcher.changes <- struct{}{}
   257  	assertOneChange()
   258  	c.Assert(s.watcher.Snapshot().ResolvedMode, gc.Equals, params.ResolvedRetryHooks)
   259  
   260  	s.st.unit.addressesWatcher.changes <- []string{"addresseshash2"}
   261  	assertOneChange()
   262  	c.Assert(s.watcher.Snapshot().AddressesHash, gc.Equals, "addresseshash2")
   263  
   264  	s.st.unit.storageWatcher.changes <- []string{}
   265  	assertOneChange()
   266  
   267  	s.st.unit.configSettingsWatcher.changes <- []string{"confighash2"}
   268  	assertOneChange()
   269  	c.Assert(s.watcher.Snapshot().ConfigHash, gc.Equals, "confighash2")
   270  
   271  	s.st.unit.applicationConfigSettingsWatcher.changes <- []string{"trusthash2"}
   272  	assertOneChange()
   273  	c.Assert(s.watcher.Snapshot().TrustHash, gc.Equals, "trusthash2")
   274  
   275  	s.st.unit.application.leaderSettingsWatcher.changes <- struct{}{}
   276  	assertOneChange()
   277  	c.Assert(s.watcher.Snapshot().LeaderSettingsVersion, gc.Equals, initial.LeaderSettingsVersion+1)
   278  
   279  	s.st.unit.relationsWatcher.changes <- []string{}
   280  	assertOneChange()
   281  
   282  	if s.modelType == model.IAAS {
   283  		s.st.unit.upgradeSeriesWatcher.changes <- struct{}{}
   284  		assertOneChange()
   285  		s.st.unit.upgradeLXDProfileUpgradeWatcher.changes <- []string{lxdprofile.SuccessStatus}
   286  		assertOneChange()
   287  	}
   288  	s.st.unit.application.forceUpgrade = true
   289  	s.applicationWatcher.changes <- struct{}{}
   290  	assertOneChange()
   291  	c.Assert(s.watcher.Snapshot().ForceCharmUpgrade, jc.IsTrue)
   292  
   293  	s.clock.Advance(5 * time.Minute)
   294  	assertOneChange()
   295  }
   296  
   297  func (s *WatcherSuite) TestActionsReceived(c *gc.C) {
   298  	s.signalAll()
   299  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   300  
   301  	s.st.unit.actionWatcher.changes <- []string{"an-action"}
   302  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   303  	c.Assert(s.watcher.Snapshot().Actions, gc.DeepEquals, []string{"an-action"})
   304  }
   305  
   306  func (s *WatcherSuite) TestClearResolvedMode(c *gc.C) {
   307  	s.st.unit.resolved = params.ResolvedRetryHooks
   308  	s.signalAll()
   309  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   310  
   311  	snap := s.watcher.Snapshot()
   312  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedRetryHooks)
   313  
   314  	s.watcher.ClearResolvedMode()
   315  	snap = s.watcher.Snapshot()
   316  	c.Assert(snap.ResolvedMode, gc.Equals, params.ResolvedNone)
   317  }
   318  
   319  func (s *WatcherSuite) TestLeadershipChanged(c *gc.C) {
   320  	s.leadership.claimTicket.result = false
   321  	s.signalAll()
   322  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   323  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   324  
   325  	s.leadership.leaderTicket.ch <- struct{}{}
   326  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   327  	c.Assert(s.watcher.Snapshot().Leader, jc.IsTrue)
   328  
   329  	s.leadership.minionTicket.ch <- struct{}{}
   330  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   331  	c.Assert(s.watcher.Snapshot().Leader, jc.IsFalse)
   332  }
   333  
   334  func (s *WatcherSuite) TestLeadershipMinionUnchanged(c *gc.C) {
   335  	s.leadership.claimTicket.result = false
   336  	s.signalAll()
   337  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   338  
   339  	// Initially minion, so triggering minion should have no effect.
   340  	s.leadership.minionTicket.ch <- struct{}{}
   341  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   342  }
   343  
   344  func (s *WatcherSuite) TestLeadershipLeaderUnchanged(c *gc.C) {
   345  	s.signalAll()
   346  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   347  
   348  	// Initially leader, so triggering leader should have no effect.
   349  	s.leadership.leaderTicket.ch <- struct{}{}
   350  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   351  }
   352  
   353  func (s *WatcherSuite) TestStorageChanged(c *gc.C) {
   354  	s.signalAll()
   355  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   356  
   357  	storageTag0 := names.NewStorageTag("blob/0")
   358  	storageAttachmentId0 := params.StorageAttachmentId{
   359  		UnitTag:    s.st.unit.tag.String(),
   360  		StorageTag: storageTag0.String(),
   361  	}
   362  	storageTag0Watcher := newMockNotifyWatcher()
   363  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   364  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   365  		UnitTag:    storageAttachmentId0.UnitTag,
   366  		StorageTag: storageAttachmentId0.StorageTag,
   367  		Life:       params.Alive,
   368  		Kind:       params.StorageKindUnknown, // unprovisioned
   369  		Location:   "nowhere",
   370  	}
   371  
   372  	storageTag1 := names.NewStorageTag("blob/1")
   373  	storageAttachmentId1 := params.StorageAttachmentId{
   374  		UnitTag:    s.st.unit.tag.String(),
   375  		StorageTag: storageTag1.String(),
   376  	}
   377  	storageTag1Watcher := newMockNotifyWatcher()
   378  	s.st.storageAttachmentWatchers[storageTag1] = storageTag1Watcher
   379  	s.st.storageAttachment[storageAttachmentId1] = params.StorageAttachment{
   380  		UnitTag:    storageAttachmentId1.UnitTag,
   381  		StorageTag: storageAttachmentId1.StorageTag,
   382  		Life:       params.Dying,
   383  		Kind:       params.StorageKindBlock,
   384  		Location:   "malta",
   385  	}
   386  
   387  	// We should not see any event until the storage attachment watchers
   388  	// return their initial events.
   389  	s.st.unit.storageWatcher.changes <- []string{"blob/0", "blob/1"}
   390  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   391  	storageTag0Watcher.changes <- struct{}{}
   392  	storageTag1Watcher.changes <- struct{}{}
   393  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   394  
   395  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   396  		storageTag0: {
   397  			Life: params.Alive,
   398  		},
   399  		storageTag1: {
   400  			Life:     params.Dying,
   401  			Kind:     params.StorageKindBlock,
   402  			Attached: true,
   403  			Location: "malta",
   404  		},
   405  	})
   406  
   407  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   408  		UnitTag:    storageAttachmentId0.UnitTag,
   409  		StorageTag: storageAttachmentId0.StorageTag,
   410  		Life:       params.Dying,
   411  		Kind:       params.StorageKindFilesystem,
   412  		Location:   "somewhere",
   413  	}
   414  	delete(s.st.storageAttachment, storageAttachmentId1)
   415  	storageTag0Watcher.changes <- struct{}{}
   416  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   417  	s.st.unit.storageWatcher.changes <- []string{"blob/1"}
   418  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   419  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   420  		storageTag0: {
   421  			Life:     params.Dying,
   422  			Attached: true,
   423  			Kind:     params.StorageKindFilesystem,
   424  			Location: "somewhere",
   425  		},
   426  	})
   427  }
   428  
   429  func (s *WatcherSuite) TestStorageUnattachedChanged(c *gc.C) {
   430  	s.signalAll()
   431  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   432  
   433  	storageTag0 := names.NewStorageTag("blob/0")
   434  	storageAttachmentId0 := params.StorageAttachmentId{
   435  		UnitTag:    s.st.unit.tag.String(),
   436  		StorageTag: storageTag0.String(),
   437  	}
   438  	storageTag0Watcher := newMockNotifyWatcher()
   439  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   440  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   441  		UnitTag:    storageAttachmentId0.UnitTag,
   442  		StorageTag: storageAttachmentId0.StorageTag,
   443  		Life:       params.Alive,
   444  		Kind:       params.StorageKindUnknown, // unprovisioned
   445  	}
   446  
   447  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   448  	storageTag0Watcher.changes <- struct{}{}
   449  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   450  
   451  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   452  		storageTag0: {
   453  			Life: params.Alive,
   454  		},
   455  	})
   456  
   457  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   458  		UnitTag:    storageAttachmentId0.UnitTag,
   459  		StorageTag: storageAttachmentId0.StorageTag,
   460  		Life:       params.Dying,
   461  	}
   462  	// The storage is still unattached; triggering the storage-specific
   463  	// watcher should not cause any event to be emitted.
   464  	storageTag0Watcher.changes <- struct{}{}
   465  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   466  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   467  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   468  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   469  		storageTag0: {
   470  			Life: params.Dying,
   471  		},
   472  	})
   473  }
   474  
   475  func (s *WatcherSuite) TestStorageAttachmentRemoved(c *gc.C) {
   476  	s.signalAll()
   477  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   478  
   479  	storageTag0 := names.NewStorageTag("blob/0")
   480  	storageAttachmentId0 := params.StorageAttachmentId{
   481  		UnitTag:    s.st.unit.tag.String(),
   482  		StorageTag: storageTag0.String(),
   483  	}
   484  	storageTag0Watcher := newMockNotifyWatcher()
   485  	s.st.storageAttachmentWatchers[storageTag0] = storageTag0Watcher
   486  	s.st.storageAttachment[storageAttachmentId0] = params.StorageAttachment{
   487  		UnitTag:    storageAttachmentId0.UnitTag,
   488  		StorageTag: storageAttachmentId0.StorageTag,
   489  		Life:       params.Dying,
   490  		Kind:       params.StorageKindUnknown, // unprovisioned
   491  	}
   492  
   493  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   494  	storageTag0Watcher.changes <- struct{}{}
   495  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   496  
   497  	c.Assert(s.watcher.Snapshot().Storage, jc.DeepEquals, map[names.StorageTag]remotestate.StorageSnapshot{
   498  		storageTag0: {
   499  			Life: params.Dying,
   500  		},
   501  	})
   502  
   503  	// Removing the storage attachment and then triggering the storage-
   504  	// specific watcher should not cause an event to be emitted, but it
   505  	// will cause that watcher to stop running. Triggering the top-level
   506  	// storage watcher will remove it and update the snapshot.
   507  	delete(s.st.storageAttachment, storageAttachmentId0)
   508  	storageTag0Watcher.changes <- struct{}{}
   509  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   510  	c.Assert(storageTag0Watcher.Stopped(), jc.IsTrue)
   511  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   512  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   513  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   514  }
   515  
   516  func (s *WatcherSuite) TestStorageChangedNotFoundInitially(c *gc.C) {
   517  	s.signalAll()
   518  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   519  
   520  	// blob/0 is initially in state, but is removed between the
   521  	// watcher signal and the uniter querying it. This should
   522  	// not cause the watcher to raise an error.
   523  	s.st.unit.storageWatcher.changes <- []string{"blob/0"}
   524  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   525  	c.Assert(s.watcher.Snapshot().Storage, gc.HasLen, 0)
   526  }
   527  
   528  func (s *WatcherSuite) TestRelationsChanged(c *gc.C) {
   529  	s.signalAll()
   530  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   531  
   532  	relationTag := names.NewRelationTag("mysql:peer")
   533  	s.st.relations[relationTag] = &mockRelation{
   534  		id: 123, life: params.Alive, suspended: false,
   535  	}
   536  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   537  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   538  
   539  	// There should not be any signal until the relation units watcher has
   540  	// returned its initial event also.
   541  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   542  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   543  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}, "mysql/2": {2}},
   544  	}
   545  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   546  	c.Assert(
   547  		s.watcher.Snapshot().Relations,
   548  		jc.DeepEquals,
   549  		map[int]remotestate.RelationSnapshot{
   550  			123: {
   551  				Life:      params.Alive,
   552  				Suspended: false,
   553  				Members:   map[string]int64{"mysql/1": 1, "mysql/2": 2},
   554  			},
   555  		},
   556  	)
   557  
   558  	// If a relation is known, then updating it does not require any input
   559  	// from the relation units watcher.
   560  	s.st.relations[relationTag].life = params.Dying
   561  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   562  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   563  	c.Assert(s.watcher.Snapshot().Relations[123].Life, gc.Equals, params.Dying)
   564  
   565  	// If a relation is not found, then it should be removed from the
   566  	// snapshot and its relation units watcher stopped.
   567  	delete(s.st.relations, relationTag)
   568  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   569  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   570  	c.Assert(s.watcher.Snapshot().Relations, gc.HasLen, 0)
   571  	c.Assert(s.st.relationUnitsWatchers[relationTag].Stopped(), jc.IsTrue)
   572  }
   573  
   574  func (s *WatcherSuite) TestRelationsSuspended(c *gc.C) {
   575  	s.signalAll()
   576  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   577  
   578  	relationTag := names.NewRelationTag("mysql:db wordpress:db")
   579  	s.st.relations[relationTag] = &mockRelation{
   580  		id: 123, life: params.Alive, suspended: false,
   581  	}
   582  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   583  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   584  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "remote state change")
   585  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   586  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}, "mysql/2": {2}},
   587  	}
   588  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   589  
   590  	s.st.relations[relationTag].suspended = true
   591  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   592  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   593  	c.Assert(s.watcher.Snapshot().Relations[123].Suspended, jc.IsTrue)
   594  	c.Assert(s.st.relationUnitsWatchers[relationTag].Stopped(), jc.IsTrue)
   595  }
   596  
   597  func (s *WatcherSuite) TestRelationUnitsChanged(c *gc.C) {
   598  	s.signalAll()
   599  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   600  
   601  	relationTag := names.NewRelationTag("mysql:peer")
   602  	s.st.relations[relationTag] = &mockRelation{
   603  		id: 123, life: params.Alive,
   604  	}
   605  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   606  
   607  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   608  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   609  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}},
   610  	}
   611  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   612  
   613  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   614  		Changed: map[string]watcher.UnitSettings{"mysql/1": {2}, "mysql/2": {1}},
   615  	}
   616  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   617  	c.Assert(
   618  		s.watcher.Snapshot().Relations[123].Members,
   619  		jc.DeepEquals,
   620  		map[string]int64{"mysql/1": 2, "mysql/2": 1},
   621  	)
   622  
   623  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   624  		Departed: []string{"mysql/1", "mysql/42"},
   625  	}
   626  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   627  	c.Assert(
   628  		s.watcher.Snapshot().Relations[123].Members,
   629  		jc.DeepEquals,
   630  		map[string]int64{"mysql/2": 1},
   631  	)
   632  }
   633  
   634  func (s *WatcherSuite) TestRelationUnitsDontLeakReferences(c *gc.C) {
   635  	s.signalAll()
   636  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   637  
   638  	relationTag := names.NewRelationTag("mysql:peer")
   639  	s.st.relations[relationTag] = &mockRelation{
   640  		id: 123, life: params.Alive,
   641  	}
   642  	s.st.relationUnitsWatchers[relationTag] = newMockRelationUnitsWatcher()
   643  
   644  	s.st.unit.relationsWatcher.changes <- []string{relationTag.Id()}
   645  	s.st.relationUnitsWatchers[relationTag].changes <- watcher.RelationUnitsChange{
   646  		Changed: map[string]watcher.UnitSettings{"mysql/1": {1}},
   647  	}
   648  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   649  
   650  	snapshot := s.watcher.Snapshot()
   651  	snapshot.Relations[123].Members["pwned"] = 2600
   652  	c.Assert(
   653  		s.watcher.Snapshot().Relations[123].Members,
   654  		jc.DeepEquals,
   655  		map[string]int64{"mysql/1": 1},
   656  	)
   657  }
   658  
   659  func (s *WatcherSuite) TestUpdateStatusTicker(c *gc.C) {
   660  	s.signalAll()
   661  	initial := s.watcher.Snapshot()
   662  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   663  
   664  	// Advance the clock past the trigger time.
   665  	s.waitAlarmsStable(c)
   666  	s.clock.Advance(5 * time.Minute)
   667  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   668  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   669  
   670  	// Advance again but not past the trigger time.
   671  	s.waitAlarmsStable(c)
   672  	s.clock.Advance(4 * time.Minute)
   673  	assertNoNotifyEvent(c, s.watcher.RemoteStateChanged(), "unexpected remote state change")
   674  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   675  
   676  	// And we hit the trigger time.
   677  	s.clock.Advance(1 * time.Minute)
   678  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   679  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+2)
   680  }
   681  
   682  func (s *WatcherSuite) TestUpdateStatusIntervalChanges(c *gc.C) {
   683  	s.signalAll()
   684  	initial := s.watcher.Snapshot()
   685  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   686  
   687  	// Advance the clock past the trigger time.
   688  	s.waitAlarmsStable(c)
   689  	s.clock.Advance(5 * time.Minute)
   690  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   691  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+1)
   692  
   693  	// Change the update status interval to 10 seconds.
   694  	s.st.updateStatusInterval = 10 * time.Second
   695  	s.st.updateStatusIntervalWatcher.changes <- struct{}{}
   696  
   697  	// Advance 10 seconds; the timer should be triggered.
   698  	s.waitAlarmsStable(c)
   699  	s.clock.Advance(10 * time.Second)
   700  	assertNotifyEvent(c, s.watcher.RemoteStateChanged(), "waiting for remote state change")
   701  	c.Assert(s.watcher.Snapshot().UpdateStatusVersion, gc.Equals, initial.UpdateStatusVersion+2)
   702  }
   703  
   704  // waitAlarmsStable is used to wait until the remote watcher's loop has
   705  // stopped churning (at least for testing.ShortWait), so that we can
   706  // then Advance the clock with some confidence that the SUT really is
   707  // waiting for it. This seems likely to be more stable than waiting for
   708  // a specific number of loop iterations; it's currently 9, but waiting
   709  // for a specific number is very likely to start failing intermittently
   710  // again, as in lp:1604955, if the SUT undergoes even subtle changes.
   711  func (s *WatcherSuite) waitAlarmsStable(c *gc.C) {
   712  	timeout := time.After(coretesting.LongWait)
   713  	for i := 0; ; i++ {
   714  		c.Logf("waiting for alarm %d", i)
   715  		select {
   716  		case <-s.clock.Alarms():
   717  		case <-time.After(coretesting.ShortWait):
   718  			return
   719  		case <-timeout:
   720  			c.Fatalf("never stopped setting alarms")
   721  		}
   722  	}
   723  }
   724  
   725  func (s *WatcherSuiteCAAS) TestWatcherConfig(c *gc.C) {
   726  	_, err := remotestate.NewWatcher(remotestate.WatcherConfig{
   727  		ModelType: model.CAAS,
   728  	})
   729  	c.Assert(err, gc.ErrorMatches, "watcher config for CAAS model with nil application channel not valid")
   730  }