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 }