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 }