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 }