github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/relationunit_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "context" 8 "fmt" 9 "strconv" 10 "time" 11 12 "github.com/juju/charm/v12" 13 "github.com/juju/collections/set" 14 "github.com/juju/errors" 15 "github.com/juju/loggo" 16 jc "github.com/juju/testing/checkers" 17 "golang.org/x/sync/errgroup" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/core/network" 21 "github.com/juju/juju/state" 22 stateerrors "github.com/juju/juju/state/errors" 23 "github.com/juju/juju/state/testing" 24 coretesting "github.com/juju/juju/testing" 25 ) 26 27 type RUs []*state.RelationUnit 28 29 type RelationUnitSuite struct { 30 ConnSuite 31 } 32 33 var _ = gc.Suite(&RelationUnitSuite{}) 34 35 func assertInScope(c *gc.C, ru *state.RelationUnit) { 36 ok, err := ru.InScope() 37 c.Assert(err, jc.ErrorIsNil) 38 c.Assert(ok, jc.IsTrue) 39 } 40 41 func assertNotInScope(c *gc.C, ru *state.RelationUnit) { 42 assertNotJoined(c, ru) 43 ok, err := ru.InScope() 44 c.Assert(err, jc.ErrorIsNil) 45 c.Assert(ok, jc.IsFalse) 46 } 47 48 func assertJoined(c *gc.C, ru *state.RelationUnit) { 49 assertInScope(c, ru) 50 ok, err := ru.Joined() 51 c.Assert(err, jc.ErrorIsNil) 52 c.Assert(ok, jc.IsTrue) 53 } 54 55 func assertNotJoined(c *gc.C, ru *state.RelationUnit) { 56 ok, err := ru.Joined() 57 c.Assert(err, jc.ErrorIsNil) 58 c.Assert(ok, jc.IsFalse) 59 } 60 61 func (s *RelationUnitSuite) TestReadSettingsErrors(c *gc.C) { 62 riak := s.AddTestingApplication(c, "riak", s.AddTestingCharm(c, "riak")) 63 u0, err := riak.AddUnit(state.AddUnitParams{}) 64 c.Assert(err, jc.ErrorIsNil) 65 riakEP, err := riak.Endpoint("ring") 66 c.Assert(err, jc.ErrorIsNil) 67 rel, err := s.State.EndpointsRelation(riakEP) 68 c.Assert(err, jc.ErrorIsNil) 69 ru0, err := rel.Unit(u0) 70 c.Assert(err, jc.ErrorIsNil) 71 72 _, err = ru0.ReadSettings("nonsense") 73 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "nonsense" in relation "riak:ring": "nonsense" is not a valid unit name`) 74 _, err = ru0.ReadSettings("unknown/0") 75 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "unknown/0" in relation "riak:ring": application "unknown" is not a member of "riak:ring"`) 76 _, err = ru0.ReadSettings("riak/pressure") 77 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/pressure" in relation "riak:ring": "riak/pressure" is not a valid unit name`) 78 _, err = ru0.ReadSettings("riak/1") 79 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/1" in relation "riak:ring": unit "riak/1": settings not found`) 80 } 81 82 func (s *RelationUnitSuite) TestCounterpartApplications(c *gc.C) { 83 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) 84 85 c.Check(prr.pru0.CounterpartApplications(), jc.DeepEquals, []string{"wordpress"}) 86 c.Check(prr.pru1.CounterpartApplications(), jc.DeepEquals, []string{"wordpress"}) 87 c.Check(prr.rru0.CounterpartApplications(), jc.DeepEquals, []string{"mysql"}) 88 c.Check(prr.rru1.CounterpartApplications(), jc.DeepEquals, []string{"mysql"}) 89 } 90 91 func (s *RelationUnitSuite) TestCounterpartApplicationsContainerScope(c *gc.C) { 92 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 93 94 // The counterpart application for container scope is still the same 95 c.Check(prr.pru0.CounterpartApplications(), jc.DeepEquals, []string{"logging"}) 96 c.Check(prr.pru1.CounterpartApplications(), jc.DeepEquals, []string{"logging"}) 97 c.Check(prr.rru0.CounterpartApplications(), jc.DeepEquals, []string{"mysql"}) 98 c.Check(prr.rru1.CounterpartApplications(), jc.DeepEquals, []string{"mysql"}) 99 } 100 101 func (s *RelationUnitSuite) TestCounterpartApplicationsPeer(c *gc.C) { 102 pr := newPeerRelation(c, s.State) 103 c.Check(pr.ru0.CounterpartApplications(), jc.DeepEquals, []string{"riak"}) 104 c.Check(pr.ru1.CounterpartApplications(), jc.DeepEquals, []string{"riak"}) 105 c.Check(pr.ru2.CounterpartApplications(), jc.DeepEquals, []string{"riak"}) 106 c.Check(pr.ru3.CounterpartApplications(), jc.DeepEquals, []string{"riak"}) 107 } 108 109 func (s *RelationUnitSuite) TestPeerSettings(c *gc.C) { 110 pr := newPeerRelation(c, s.State) 111 rus := RUs{pr.ru0, pr.ru1} 112 113 // Check missing settings cannot be read by any RU. 114 for _, ru := range rus { 115 _, err := ru.ReadSettings("riak/0") 116 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": unit "riak/0": settings not found`) 117 } 118 119 // Add settings for one RU. 120 assertNotInScope(c, pr.ru0) 121 err := pr.ru0.EnterScope(map[string]interface{}{"gene": "kelly"}) 122 c.Assert(err, jc.ErrorIsNil) 123 node, err := pr.ru0.Settings() 124 c.Assert(err, jc.ErrorIsNil) 125 node.Set("meme", "socially-awkward-penguin") 126 _, err = node.Write() 127 c.Assert(err, jc.ErrorIsNil) 128 normal := map[string]interface{}{ 129 "gene": "kelly", 130 "meme": "socially-awkward-penguin", 131 } 132 133 // Check settings can be read by every RU. 134 assertSettings := func(u *state.Unit, expect map[string]interface{}) { 135 for _, ru := range rus { 136 m, err := ru.ReadSettings(u.Name()) 137 c.Assert(err, jc.ErrorIsNil) 138 c.Assert(m, gc.DeepEquals, expect) 139 } 140 } 141 assertSettings(pr.u0, normal) 142 assertJoined(c, pr.ru0) 143 144 // Check that EnterScope when scope already entered does not touch 145 // settings at all. 146 changed := map[string]interface{}{"foo": "bar"} 147 err = pr.ru0.EnterScope(changed) 148 c.Assert(err, jc.ErrorIsNil) 149 assertSettings(pr.u0, normal) 150 assertJoined(c, pr.ru0) 151 152 // Leave scope, check settings are still as accessible as before. 153 err = pr.ru0.LeaveScope() 154 c.Assert(err, jc.ErrorIsNil) 155 assertSettings(pr.u0, normal) 156 assertNotInScope(c, pr.ru0) 157 158 // Re-enter scope with changed settings, and check they completely overwrite 159 // the old ones. 160 err = pr.ru0.EnterScope(changed) 161 c.Assert(err, jc.ErrorIsNil) 162 assertSettings(pr.u0, changed) 163 assertJoined(c, pr.ru0) 164 165 // Leave and re-enter with nil nettings, and check they overwrite to become 166 // an empty map. 167 err = pr.ru0.LeaveScope() 168 c.Assert(err, jc.ErrorIsNil) 169 assertNotInScope(c, pr.ru0) 170 err = pr.ru0.EnterScope(nil) 171 c.Assert(err, jc.ErrorIsNil) 172 assertSettings(pr.u0, map[string]interface{}{}) 173 assertJoined(c, pr.ru0) 174 175 // Check that entering scope for the first time with nil settings works correctly. 176 assertNotInScope(c, pr.ru1) 177 err = pr.ru1.EnterScope(nil) 178 c.Assert(err, jc.ErrorIsNil) 179 assertSettings(pr.u1, map[string]interface{}{}) 180 assertJoined(c, pr.ru1) 181 } 182 183 func (s *RelationUnitSuite) TestRemoteUnitErrors(c *gc.C) { 184 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 185 Name: "mysql", 186 SourceModel: coretesting.ModelTag, 187 Endpoints: []charm.Relation{{ 188 Interface: "mysql", 189 Name: "server", 190 Role: charm.RoleProvider, 191 Scope: charm.ScopeGlobal, 192 }}}) 193 c.Assert(err, jc.ErrorIsNil) 194 195 _, err = s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 196 Name: "mysql1", 197 SourceModel: coretesting.ModelTag, 198 Endpoints: []charm.Relation{{ 199 Interface: "mysql", 200 Name: "server", 201 Role: charm.RoleProvider, 202 Scope: charm.ScopeGlobal, 203 }}}) 204 c.Assert(err, jc.ErrorIsNil) 205 s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 206 207 eps, err := s.State.InferEndpoints("mysql", "wordpress") 208 c.Assert(err, jc.ErrorIsNil) 209 rel, err := s.State.AddRelation(eps...) 210 c.Assert(err, jc.ErrorIsNil) 211 212 _, err = rel.RemoteUnit("mysql") 213 c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`) 214 215 _, err = rel.RemoteUnit("wordpress/0") 216 c.Assert(err, gc.ErrorMatches, `saas application "wordpress" not found`) 217 218 _, err = rel.RemoteUnit("mysql1/0") 219 c.Assert(err, gc.ErrorMatches, `application "mysql1" is not a member of "wordpress:db mysql:server"`) 220 } 221 222 func (s *RelationUnitSuite) TestAllRemoteUnits(c *gc.C) { 223 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 224 Name: "mysql", 225 SourceModel: coretesting.ModelTag, 226 Endpoints: []charm.Relation{{ 227 Interface: "mysql", 228 Name: "server", 229 Role: charm.RoleProvider, 230 Scope: charm.ScopeGlobal, 231 }}}) 232 c.Assert(err, jc.ErrorIsNil) 233 _, err = s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 234 Name: "another", 235 SourceModel: coretesting.ModelTag, 236 Endpoints: []charm.Relation{{ 237 Interface: "mysql", 238 Name: "server", 239 Role: charm.RoleProvider, 240 Scope: charm.ScopeGlobal, 241 }}}) 242 c.Assert(err, jc.ErrorIsNil) 243 s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 244 245 eps, err := s.State.InferEndpoints("mysql", "wordpress") 246 c.Assert(err, jc.ErrorIsNil) 247 rel, err := s.State.AddRelation(eps...) 248 c.Assert(err, jc.ErrorIsNil) 249 250 ru1 := addRemoteRU(c, rel, "mysql/0") 251 err = ru1.EnterScope(nil) 252 c.Assert(err, jc.ErrorIsNil) 253 ru2 := addRemoteRU(c, rel, "mysql/1") 254 err = ru2.EnterScope(nil) 255 c.Assert(err, jc.ErrorIsNil) 256 257 _, err = rel.AllRemoteUnits("wordpress") 258 c.Assert(err, jc.Satisfies, errors.IsNotFound) 259 _, err = rel.AllRemoteUnits("another") 260 c.Assert(err, gc.ErrorMatches, `application "another" is not a member of "wordpress:db mysql:server"`) 261 all, err := rel.AllRemoteUnits("mysql") 262 c.Assert(err, jc.ErrorIsNil) 263 c.Assert(all, gc.HasLen, 2) 264 c.Assert(all, jc.SameContents, []*state.RelationUnit{ru1, ru2}) 265 } 266 267 func (s *RelationUnitSuite) TestProReqSettings(c *gc.C) { 268 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) 269 s.testProReqSettings(c, prr.pru0, prr.pru1, prr.rru0, prr.rru1) 270 } 271 272 func (s *RelationUnitSuite) TestRemoteProReqSettings(c *gc.C) { 273 prr := newRemoteProReqRelation(c, &s.ConnSuite) 274 s.testProReqSettings(c, prr.pru0, prr.pru1, prr.rru0, prr.rru1) 275 } 276 277 func (s *RelationUnitSuite) testProReqSettings(c *gc.C, pru0, pru1, rru0, rru1 *state.RelationUnit) { 278 rus := RUs{pru0, pru1, rru0, rru1} 279 280 // Check missing settings cannot be read by any RU. 281 for _, ru := range rus { 282 _, err := ru.ReadSettings("mysql/0") 283 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": unit "mysql/0": settings not found`) 284 } 285 286 // Add settings for one RU. 287 assertNotInScope(c, pru0) 288 err := pru0.EnterScope(map[string]interface{}{"gene": "simmons"}) 289 c.Assert(err, jc.ErrorIsNil) 290 node, err := pru0.Settings() 291 c.Assert(err, jc.ErrorIsNil) 292 node.Set("meme", "foul-bachelor-frog") 293 _, err = node.Write() 294 c.Assert(err, jc.ErrorIsNil) 295 assertJoined(c, pru0) 296 297 // Check settings can be read by every RU. 298 for _, ru := range rus { 299 m, err := ru.ReadSettings("mysql/0") 300 c.Assert(err, jc.ErrorIsNil) 301 c.Assert(m["gene"], gc.Equals, "simmons") 302 c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog") 303 } 304 } 305 306 func (s *RelationUnitSuite) TestContainerSettings(c *gc.C) { 307 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 308 rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1} 309 310 // Check missing settings cannot be read by any RU. 311 for _, ru := range rus { 312 _, err := ru.ReadSettings("logging/0") 313 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "logging/0" in relation "logging:info mysql:juju-info": unit "logging/0": settings not found`) 314 } 315 316 // Add settings for one RU. 317 assertNotInScope(c, prr.pru0) 318 err := prr.pru0.EnterScope(map[string]interface{}{"gene": "hackman"}) 319 c.Assert(err, jc.ErrorIsNil) 320 node, err := prr.pru0.Settings() 321 c.Assert(err, jc.ErrorIsNil) 322 node.Set("meme", "foul-bachelor-frog") 323 _, err = node.Write() 324 c.Assert(err, jc.ErrorIsNil) 325 assertJoined(c, prr.pru0) 326 327 // Check settings can be read by RUs in the same container. 328 rus0 := RUs{prr.pru0, prr.rru0} 329 for _, ru := range rus0 { 330 m, err := ru.ReadSettings("mysql/0") 331 c.Assert(err, jc.ErrorIsNil) 332 c.Assert(m["gene"], gc.Equals, "hackman") 333 c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog") 334 } 335 336 // Check settings are still inaccessible to RUs outside that container 337 rus1 := RUs{prr.pru1, prr.rru1} 338 for _, ru := range rus1 { 339 _, err := ru.ReadSettings("mysql/0") 340 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "logging:info mysql:juju-info": unit "mysql/0": settings not found`) 341 } 342 } 343 344 func (s *RelationUnitSuite) TestContainerCreateSubordinate(c *gc.C) { 345 papp := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 346 rapp := s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 347 eps, err := s.State.InferEndpoints("mysql", "logging") 348 c.Assert(err, jc.ErrorIsNil) 349 rel, err := s.State.AddRelation(eps...) 350 c.Assert(err, jc.ErrorIsNil) 351 punit, err := papp.AddUnit(state.AddUnitParams{}) 352 c.Assert(err, jc.ErrorIsNil) 353 pru, err := rel.Unit(punit) 354 c.Assert(err, jc.ErrorIsNil) 355 356 // Check that no units of the subordinate application exist. 357 assertSubCount := func(expect int) []*state.Unit { 358 runits, err := rapp.AllUnits() 359 c.Assert(err, jc.ErrorIsNil) 360 c.Assert(runits, gc.HasLen, expect) 361 return runits 362 } 363 assertSubCount(0) 364 365 // Enter principal's scope and check a subordinate was created. 366 assertNotInScope(c, pru) 367 err = pru.EnterScope(nil) 368 c.Assert(err, jc.ErrorIsNil) 369 assertSubCount(1) 370 assertJoined(c, pru) 371 372 // Enter principal scope again and check no more subordinates created. 373 err = pru.EnterScope(nil) 374 c.Assert(err, jc.ErrorIsNil) 375 assertSubCount(1) 376 assertJoined(c, pru) 377 378 // Leave principal scope, then re-enter, and check that still no further 379 // subordinates are created. 380 err = pru.LeaveScope() 381 c.Assert(err, jc.ErrorIsNil) 382 assertNotInScope(c, pru) 383 err = pru.EnterScope(nil) 384 c.Assert(err, jc.ErrorIsNil) 385 runits := assertSubCount(1) 386 assertJoined(c, pru) 387 388 // Set the subordinate to Dying, and enter scope again; because the scope 389 // is already entered, no error is returned. 390 runit := runits[0] 391 err = runit.Destroy() 392 c.Assert(err, jc.ErrorIsNil) 393 err = pru.EnterScope(nil) 394 c.Assert(err, jc.ErrorIsNil) 395 assertJoined(c, pru) 396 397 // Leave scope, then try to enter again with the Dying subordinate. 398 err = pru.LeaveScope() 399 c.Assert(err, jc.ErrorIsNil) 400 assertNotInScope(c, pru) 401 err = pru.EnterScope(nil) 402 c.Assert(err, gc.ErrorMatches, ".*"+stateerrors.ErrCannotEnterScopeYet.Error()) 403 assertNotInScope(c, pru) 404 405 // Remove the subordinate, and enter scope again; this should work, and 406 // create a new subordinate. 407 err = runit.EnsureDead() 408 c.Assert(err, jc.ErrorIsNil) 409 err = runit.Remove() 410 c.Assert(err, jc.ErrorIsNil) 411 assertSubCount(0) 412 assertNotInScope(c, pru) 413 err = pru.EnterScope(nil) 414 c.Assert(err, jc.ErrorIsNil) 415 assertSubCount(1) 416 assertJoined(c, pru) 417 } 418 419 func (s *RelationUnitSuite) TestDestroyRelationWithUnitsInScope(c *gc.C) { 420 pr := newPeerRelation(c, s.State) 421 preventPeerUnitsDestroyRemove(c, pr) 422 rel := pr.ru0.Relation() 423 424 // Enter two units, and check that Destroying the application sets the 425 // relation to Dying (rather than removing it directly). 426 assertNotInScope(c, pr.ru0) 427 err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"}) 428 c.Assert(err, jc.ErrorIsNil) 429 assertJoined(c, pr.ru0) 430 assertNotInScope(c, pr.ru1) 431 err = pr.ru1.EnterScope(nil) 432 c.Assert(err, jc.ErrorIsNil) 433 assertJoined(c, pr.ru1) 434 err = pr.app.Destroy() 435 c.Assert(err, jc.ErrorIsNil) 436 err = rel.Refresh() 437 c.Assert(err, jc.ErrorIsNil) 438 c.Assert(rel.Life(), gc.Equals, state.Dying) 439 440 // Check that we can't add a new unit now. 441 assertNotInScope(c, pr.ru2) 442 err = pr.ru2.EnterScope(nil) 443 c.Assert(err, gc.ErrorMatches, ".*"+stateerrors.ErrCannotEnterScope.Error()) 444 assertNotInScope(c, pr.ru2) 445 446 // Check that we created no settings for the unit we failed to add. 447 _, err = pr.ru0.ReadSettings("riak/2") 448 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/2" in relation "riak:ring": unit "riak/2": settings not found`) 449 450 // ru0 leaves the scope; check that application Destroy is still a no-op. 451 assertJoined(c, pr.ru0) 452 err = pr.ru0.LeaveScope() 453 c.Assert(err, jc.ErrorIsNil) 454 assertNotInScope(c, pr.ru0) 455 err = pr.app.Destroy() 456 c.Assert(err, jc.ErrorIsNil) 457 458 // Check that unit settings for the original unit still exist, and have 459 // not yet been marked for deletion. 460 err = s.State.Cleanup() 461 c.Assert(err, jc.ErrorIsNil) 462 assertSettings := func() { 463 settings, err := pr.ru1.ReadSettings("riak/0") 464 c.Assert(err, jc.ErrorIsNil) 465 c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"}) 466 } 467 assertSettings() 468 469 // The final unit leaves the scope, and cleans up after itself. 470 assertJoined(c, pr.ru1) 471 err = pr.ru1.LeaveScope() 472 c.Assert(err, jc.ErrorIsNil) 473 assertNotInScope(c, pr.ru1) 474 err = rel.Refresh() 475 c.Assert(err, jc.Satisfies, errors.IsNotFound) 476 477 // The settings were not themselves actually deleted yet... 478 assertSettings() 479 480 // ...but they were scheduled for deletion. 481 err = s.State.Cleanup() 482 c.Assert(err, jc.ErrorIsNil) 483 _, err = pr.ru1.ReadSettings("riak/0") 484 c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": unit "riak/0": settings not found`) 485 } 486 487 func (s *RelationUnitSuite) TestAliveRelationScope(c *gc.C) { 488 pr := newPeerRelation(c, s.State) 489 rel := pr.ru0.Relation() 490 491 // Two units enter... 492 assertNotInScope(c, pr.ru0) 493 err := pr.ru0.EnterScope(nil) 494 c.Assert(err, jc.ErrorIsNil) 495 assertJoined(c, pr.ru0) 496 assertNotInScope(c, pr.ru1) 497 err = pr.ru1.EnterScope(nil) 498 c.Assert(err, jc.ErrorIsNil) 499 assertJoined(c, pr.ru1) 500 501 // One unit becomes Dying, then re-enters the scope; this is not an error, 502 // because the state is already as requested. 503 err = pr.u0.Destroy() 504 c.Assert(err, jc.ErrorIsNil) 505 err = pr.ru0.EnterScope(nil) 506 c.Assert(err, jc.ErrorIsNil) 507 assertJoined(c, pr.ru0) 508 509 // Two units leave... 510 err = pr.ru0.LeaveScope() 511 c.Assert(err, jc.ErrorIsNil) 512 assertNotInScope(c, pr.ru0) 513 err = pr.ru1.LeaveScope() 514 c.Assert(err, jc.ErrorIsNil) 515 assertNotInScope(c, pr.ru1) 516 517 // The relation scope is empty, but the relation is still alive... 518 err = rel.Refresh() 519 c.Assert(err, jc.ErrorIsNil) 520 c.Assert(rel.Life(), gc.Equals, state.Alive) 521 522 // ...and new units can still join it... 523 assertNotInScope(c, pr.ru2) 524 err = pr.ru2.EnterScope(nil) 525 c.Assert(err, jc.ErrorIsNil) 526 assertJoined(c, pr.ru2) 527 528 // ...but Dying units cannot. 529 err = pr.u3.Destroy() 530 c.Assert(err, jc.ErrorIsNil) 531 assertNotInScope(c, pr.ru3) 532 err = pr.ru3.EnterScope(nil) 533 c.Assert(err, gc.ErrorMatches, ".*"+stateerrors.ErrCannotEnterScope.Error()) 534 assertNotInScope(c, pr.ru3) 535 } 536 537 func (s *StateSuite) TestWatchScopeDiesOnStateClose(c *gc.C) { 538 testWatcherDiesWhenStateCloses(c, s.Session, s.modelTag, s.State.ControllerTag(), func(c *gc.C, st *state.State) waiter { 539 pr := newPeerRelation(c, st) 540 w := pr.ru0.WatchScope() 541 <-w.Changes() 542 return w 543 }) 544 } 545 546 func (s *RelationUnitSuite) TestPeerWatchScope(c *gc.C) { 547 pr := newPeerRelation(c, s.State) 548 549 // Test empty initial event. 550 w0 := pr.ru0.WatchScope() 551 defer testing.AssertStop(c, w0) 552 s.assertScopeChange(c, w0, nil, nil) 553 s.assertNoScopeChange(c, w0) 554 555 // ru0 enters; check no change, but settings written. 556 assertNotInScope(c, pr.ru0) 557 err := pr.ru0.EnterScope(map[string]interface{}{"foo": "bar"}) 558 c.Assert(err, jc.ErrorIsNil) 559 s.assertNoScopeChange(c, w0) 560 node, err := pr.ru0.Settings() 561 c.Assert(err, jc.ErrorIsNil) 562 c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{"foo": "bar"}) 563 assertJoined(c, pr.ru0) 564 565 // ru1 enters; check change is observed. 566 assertNotInScope(c, pr.ru1) 567 err = pr.ru1.EnterScope(nil) 568 c.Assert(err, jc.ErrorIsNil) 569 s.assertScopeChange(c, w0, []string{"riak/1"}, nil) 570 s.assertNoScopeChange(c, w0) 571 assertJoined(c, pr.ru1) 572 573 // ru1 enters again, check no problems and no changes. 574 err = pr.ru1.EnterScope(nil) 575 c.Assert(err, jc.ErrorIsNil) 576 s.assertNoScopeChange(c, w0) 577 assertJoined(c, pr.ru1) 578 579 // Stop watching; ru2 enters. 580 testing.AssertStop(c, w0) 581 assertNotInScope(c, pr.ru2) 582 err = pr.ru2.EnterScope(nil) 583 c.Assert(err, jc.ErrorIsNil) 584 assertJoined(c, pr.ru2) 585 586 // Start watch again, check initial event. 587 w0 = pr.ru0.WatchScope() 588 defer testing.AssertStop(c, w0) 589 s.assertScopeChange(c, w0, []string{"riak/1", "riak/2"}, nil) 590 s.assertNoScopeChange(c, w0) 591 592 // ru1 leaves; check event. 593 assertJoined(c, pr.ru1) 594 err = pr.ru1.LeaveScope() 595 c.Assert(err, jc.ErrorIsNil) 596 s.assertScopeChange(c, w0, nil, []string{"riak/1"}) 597 s.assertNoScopeChange(c, w0) 598 assertNotInScope(c, pr.ru1) 599 600 // ru1 leaves again; check no problems and no changes. 601 err = pr.ru1.LeaveScope() 602 c.Assert(err, jc.ErrorIsNil) 603 s.assertNoScopeChange(c, w0) 604 assertNotInScope(c, pr.ru1) 605 } 606 607 func (s *RelationUnitSuite) TestProReqWatchScope(c *gc.C) { 608 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) 609 s.testProReqWatchScope(c, prr.pru0, prr.pru1, prr.rru0, prr.rru1, prr.watches) 610 } 611 612 func (s *RelationUnitSuite) TestRemoteProReqWatchScope(c *gc.C) { 613 prr := newRemoteProReqRelation(c, &s.ConnSuite) 614 s.testProReqWatchScope(c, prr.pru0, prr.pru1, prr.rru0, prr.rru1, prr.watches) 615 } 616 617 func stopAllRelationScope(c *gc.C, ws []*state.RelationScopeWatcher) { 618 for _, w := range ws { 619 testing.AssertStop(c, w) 620 } 621 } 622 623 func (s *RelationUnitSuite) testProReqWatchScope( 624 c *gc.C, pru0, pru1, rru0, rru1 *state.RelationUnit, 625 watches func() []*state.RelationScopeWatcher, 626 ) { 627 // Test empty initial events for all RUs. 628 ws := watches() 629 defer stopAllRelationScope(c, ws) 630 for _, w := range ws { 631 s.assertScopeChange(c, w, nil, nil) 632 } 633 s.assertNoScopeChange(c, ws...) 634 635 // pru0 enters; check detected only by req RUs. 636 assertNotInScope(c, pru0) 637 err := pru0.EnterScope(nil) 638 c.Assert(err, jc.ErrorIsNil) 639 rws := func() []*state.RelationScopeWatcher { 640 return []*state.RelationScopeWatcher{ws[2], ws[3]} 641 } 642 for _, w := range rws() { 643 s.assertScopeChange(c, w, []string{"mysql/0"}, nil) 644 } 645 s.assertNoScopeChange(c, ws...) 646 assertJoined(c, pru0) 647 648 // req0 enters; check detected only by pro RUs. 649 assertNotInScope(c, rru0) 650 err = rru0.EnterScope(nil) 651 c.Assert(err, jc.ErrorIsNil) 652 pws := func() []*state.RelationScopeWatcher { 653 return []*state.RelationScopeWatcher{ws[0], ws[1]} 654 } 655 for _, w := range pws() { 656 s.assertScopeChange(c, w, []string{"wordpress/0"}, nil) 657 } 658 s.assertNoScopeChange(c, ws...) 659 assertJoined(c, rru0) 660 661 // Stop watches; remaining RUs enter. 662 for _, w := range ws { 663 testing.AssertStop(c, w) 664 } 665 assertNotInScope(c, pru1) 666 err = pru1.EnterScope(nil) 667 c.Assert(err, jc.ErrorIsNil) 668 assertJoined(c, pru1) 669 assertNotInScope(c, rru1) 670 err = rru1.EnterScope(nil) 671 c.Assert(err, jc.ErrorIsNil) 672 assertJoined(c, rru0) 673 674 // Start new watches, check initial events. 675 ws = watches() 676 defer stopAllRelationScope(c, ws) 677 for _, w := range pws() { 678 s.assertScopeChange(c, w, []string{"wordpress/0", "wordpress/1"}, nil) 679 } 680 for _, w := range rws() { 681 s.assertScopeChange(c, w, []string{"mysql/0", "mysql/1"}, nil) 682 } 683 s.assertNoScopeChange(c, ws...) 684 685 // pru0 leaves; check detected only by req RUs. 686 assertJoined(c, pru0) 687 err = pru0.LeaveScope() 688 c.Assert(err, jc.ErrorIsNil) 689 for _, w := range rws() { 690 s.assertScopeChange(c, w, nil, []string{"mysql/0"}) 691 } 692 s.assertNoScopeChange(c, ws...) 693 assertNotInScope(c, pru0) 694 695 // rru0 leaves; check detected only by pro RUs. 696 assertJoined(c, rru0) 697 err = rru0.LeaveScope() 698 c.Assert(err, jc.ErrorIsNil) 699 for _, w := range pws() { 700 s.assertScopeChange(c, w, nil, []string{"wordpress/0"}) 701 } 702 s.assertNoScopeChange(c, ws...) 703 assertNotInScope(c, rru0) 704 } 705 706 func (s *RelationUnitSuite) TestContainerWatchScope(c *gc.C) { 707 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 708 709 // Test empty initial events for all RUs. 710 ws := prr.watches() 711 for _, w := range ws { 712 defer testing.AssertStop(c, w) 713 } 714 for _, w := range ws { 715 s.assertScopeChange(c, w, nil, nil) 716 } 717 s.assertNoScopeChange(c, ws...) 718 719 // pru0 enters; check detected only by same-container req. 720 assertNotInScope(c, prr.pru0) 721 err := prr.pru0.EnterScope(nil) 722 c.Assert(err, jc.ErrorIsNil) 723 s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil) 724 s.assertNoScopeChange(c, ws...) 725 assertJoined(c, prr.pru0) 726 727 // req1 enters; check detected only by same-container pro. 728 assertNotInScope(c, prr.rru1) 729 err = prr.rru1.EnterScope(nil) 730 c.Assert(err, jc.ErrorIsNil) 731 s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil) 732 s.assertNoScopeChange(c, ws...) 733 assertJoined(c, prr.rru1) 734 735 // Stop watches; remaining RUs enter scope. 736 for _, w := range ws { 737 testing.AssertStop(c, w) 738 } 739 assertNotInScope(c, prr.pru1) 740 err = prr.pru1.EnterScope(nil) 741 c.Assert(err, jc.ErrorIsNil) 742 assertNotInScope(c, prr.rru0) 743 err = prr.rru0.EnterScope(nil) 744 c.Assert(err, jc.ErrorIsNil) 745 746 // Start new watches, check initial events. 747 ws = prr.watches() 748 for _, w := range ws { 749 defer testing.AssertStop(c, w) 750 } 751 s.assertScopeChange(c, ws[0], []string{"logging/0"}, nil) 752 s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil) 753 s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil) 754 s.assertScopeChange(c, ws[3], []string{"mysql/1"}, nil) 755 s.assertNoScopeChange(c, ws...) 756 assertJoined(c, prr.pru1) 757 assertJoined(c, prr.rru0) 758 759 // pru0 leaves; check detected only by same-container req. 760 assertJoined(c, prr.pru0) 761 err = prr.pru0.LeaveScope() 762 c.Assert(err, jc.ErrorIsNil) 763 s.assertScopeChange(c, ws[2], nil, []string{"mysql/0"}) 764 s.assertNoScopeChange(c, ws...) 765 assertNotInScope(c, prr.pru0) 766 767 // rru0 leaves; check detected only by same-container pro. 768 assertJoined(c, prr.rru0) 769 err = prr.rru0.LeaveScope() 770 c.Assert(err, jc.ErrorIsNil) 771 s.assertScopeChange(c, ws[0], nil, []string{"logging/0"}) 772 s.assertNoScopeChange(c, ws...) 773 assertNotInScope(c, prr.rru0) 774 } 775 776 func (s *RelationUnitSuite) TestCoalesceWatchScope(c *gc.C) { 777 // TODO(quiescence): fix this test once the watcher has some reliable coalescence. 778 c.Skip("skip until watcher has better coalescing") 779 pr := newPeerRelation(c, s.State) 780 781 // Test empty initial event. 782 w0 := pr.ru0.WatchScope() 783 defer testing.AssertStop(c, w0) 784 s.assertScopeChange(c, w0, nil, nil) 785 s.assertNoScopeChange(c, w0) 786 787 // ru1 and ru2 enter; check changes observed together. 788 err := pr.ru1.EnterScope(nil) 789 c.Assert(err, jc.ErrorIsNil) 790 err = pr.ru2.EnterScope(nil) 791 c.Assert(err, jc.ErrorIsNil) 792 793 s.assertScopeChange(c, w0, []string{"riak/1", "riak/2"}, nil) 794 s.assertNoScopeChange(c, w0) 795 796 // ru1 leaves and re-enters; check no change observed. 797 err = pr.ru1.LeaveScope() 798 c.Assert(err, jc.ErrorIsNil) 799 err = pr.ru1.EnterScope(nil) 800 c.Assert(err, jc.ErrorIsNil) 801 s.assertNoScopeChange(c, w0) 802 803 // ru1 and ru2 leave; check changes observed together. 804 err = pr.ru1.LeaveScope() 805 c.Assert(err, jc.ErrorIsNil) 806 err = pr.ru2.LeaveScope() 807 c.Assert(err, jc.ErrorIsNil) 808 s.assertScopeChange(c, w0, nil, []string{"riak/1", "riak/2"}) 809 s.assertNoScopeChange(c, w0) 810 } 811 812 func (s *RelationUnitSuite) TestPrepareLeaveScope(c *gc.C) { 813 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) 814 s.testPrepareLeaveScope(c, prr.rel, prr.pru0, prr.pru1, prr.rru0, prr.rru1) 815 } 816 817 func (s *RelationUnitSuite) TestPrepareLeaveScopeRemote(c *gc.C) { 818 prr := newRemoteProReqRelation(c, &s.ConnSuite) 819 s.testPrepareLeaveScope(c, prr.rel, prr.pru0, prr.pru1, prr.rru0, prr.rru1) 820 } 821 822 func (s *RelationUnitSuite) testPrepareLeaveScope(c *gc.C, rel *state.Relation, pru0, pru1, rru0, rru1 *state.RelationUnit) { 823 // Test empty initial event. 824 w0 := pru0.WatchScope() 825 defer testing.AssertStop(c, w0) 826 s.assertScopeChange(c, w0, nil, nil) 827 s.assertNoScopeChange(c, w0) 828 829 // rru0 and rru1 enter; check changes. 830 err := rru0.EnterScope(nil) 831 c.Assert(err, jc.ErrorIsNil) 832 err = rru1.EnterScope(nil) 833 c.Assert(err, jc.ErrorIsNil) 834 s.assertScopeChange(c, w0, []string{"wordpress/0", "wordpress/1"}, nil) 835 s.assertNoScopeChange(c, w0) 836 837 // rru0 notifies that it will leave soon; it's reported as departed by the 838 // watcher, but InScope remains accurate. 839 err = rru0.PrepareLeaveScope() 840 c.Assert(err, jc.ErrorIsNil) 841 s.assertScopeChange(c, w0, nil, []string{"wordpress/0"}) 842 s.assertNoScopeChange(c, w0) 843 assertInScope(c, rru0) 844 assertNotJoined(c, rru0) 845 846 // rru1 leaves, and the relation is destroyed; it's not removed, because 847 // rru0 keeps it alive until it really leaves scope. 848 err = rru1.LeaveScope() 849 c.Assert(err, jc.ErrorIsNil) 850 s.assertScopeChange(c, w0, nil, []string{"wordpress/1"}) 851 s.assertNoScopeChange(c, w0) 852 err = rel.Refresh() 853 c.Assert(err, jc.ErrorIsNil) 854 err = rel.Destroy() 855 c.Assert(err, jc.ErrorIsNil) 856 err = rel.Refresh() 857 c.Assert(err, jc.ErrorIsNil) 858 859 // rru0 really leaves; the relation is cleaned up. 860 err = rru0.LeaveScope() 861 c.Assert(err, jc.ErrorIsNil) 862 err = rel.Destroy() 863 c.Assert(err, jc.ErrorIsNil) 864 s.assertNoScopeChange(c, w0) 865 err = rel.Refresh() 866 c.Assert(err, jc.Satisfies, errors.IsNotFound) 867 } 868 869 func (s *RelationUnitSuite) assertScopeChange(c *gc.C, w *state.RelationScopeWatcher, entered, left []string) { 870 timeout := time.After(coretesting.LongWait) 871 // Initial event special case. 872 if len(entered) == 0 && len(left) == 0 { 873 select { 874 case ch, ok := <-w.Changes(): 875 c.Assert(ok, jc.IsTrue) 876 c.Assert(ch.Entered, gc.HasLen, 0) 877 c.Assert(ch.Left, gc.HasLen, 0) 878 case <-timeout: 879 c.Fatalf("no change") 880 } 881 return 882 } 883 enteredSet := set.NewStrings(entered...) 884 leftSet := set.NewStrings(left...) 885 for enteredSet.Size() > 0 || leftSet.Size() > 0 { 886 select { 887 case ch, ok := <-w.Changes(): 888 c.Assert(ok, jc.IsTrue) 889 for _, v := range ch.Entered { 890 c.Check(enteredSet.Contains(v), jc.IsTrue, gc.Commentf("unexpected enter scope change %s", v)) 891 enteredSet.Remove(v) 892 } 893 for _, v := range ch.Left { 894 c.Check(leftSet.Contains(v), jc.IsTrue, gc.Commentf("unexpected leave scope change %s", v)) 895 leftSet.Remove(v) 896 } 897 case <-timeout: 898 c.Fatalf("missing changes for enter scope %v and leave scope %v", 899 enteredSet.SortedValues(), leftSet.SortedValues()) 900 } 901 } 902 } 903 904 func (s *RelationUnitSuite) assertNoScopeChange(c *gc.C, ws ...*state.RelationScopeWatcher) { 905 for _, w := range ws { 906 select { 907 case ch, ok := <-w.Changes(): 908 c.Fatalf("got unwanted change: %#v, %t", ch, ok) 909 case <-time.After(coretesting.ShortWait): 910 } 911 } 912 } 913 914 func (s *RelationUnitSuite) TestValidYes(c *gc.C) { 915 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 916 rus := []*state.RelationUnit{prr.pru0, prr.pru1, prr.rru0, prr.rru1} 917 for _, ru := range rus { 918 result, err := ru.Valid() 919 c.Assert(err, jc.ErrorIsNil) 920 c.Assert(result, jc.IsTrue) 921 } 922 } 923 924 func (s *RelationUnitSuite) TestValidNo(c *gc.C) { 925 mysqlLogging := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 926 wpApp := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 927 wpLogging := newProReqRelationForApps(c, s.State, wpApp, mysqlLogging.rapp) 928 929 // We have logging related to mysql and to wordpress. We can 930 // create an invalid RU by taking a logging unit from 931 // mysql-logging and getting the wp-logging RU for it. 932 ru, err := wpLogging.rel.Unit(mysqlLogging.ru0) 933 c.Assert(err, jc.ErrorIsNil) 934 935 result, err := ru.Valid() 936 c.Assert(err, jc.ErrorIsNil) 937 c.Assert(result, jc.IsFalse) 938 } 939 940 func (s *RelationUnitSuite) TestValidSubordinateToSubordinate(c *gc.C) { 941 // Relate mysql and logging... 942 mysqlLogging := newProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) 943 monApp := s.AddTestingApplication(c, "monitoring", s.AddTestingCharm(c, "monitoring")) 944 // Relate mysql and monitoring - relation to a non-subordinate 945 // needed to trigger creation of monitoring unit. 946 mysqlMonitoring := newProReqRelationForApps(c, s.State, mysqlLogging.papp, monApp) 947 // Monitor the logging app (newProReqRelationForApps assumes that 948 // the providing app is a principal, so we need to do it by hand). 949 loggingApp := mysqlLogging.rapp 950 951 // Can't infer endpoints because they're ambiguous. 952 ep1, err := loggingApp.Endpoint("juju-info") 953 c.Assert(err, jc.ErrorIsNil) 954 ep2, err := monApp.Endpoint("info") 955 c.Assert(err, jc.ErrorIsNil) 956 957 rel, err := s.State.AddRelation(ep1, ep2) 958 c.Assert(err, jc.ErrorIsNil) 959 prr := &ProReqRelation{rel: rel, papp: loggingApp, rapp: monApp} 960 961 // The units already exist, create the relation units. 962 prr.pu0 = mysqlLogging.ru0 963 prr.pru0, err = rel.Unit(prr.pu0) 964 c.Assert(err, jc.ErrorIsNil) 965 966 prr.ru0 = mysqlMonitoring.ru0 967 prr.rru0, err = rel.Unit(prr.ru0) 968 c.Assert(err, jc.ErrorIsNil) 969 970 // Logging monitoring relation units should be valid. 971 res, err := prr.rru0.Valid() 972 c.Assert(err, jc.ErrorIsNil) 973 c.Assert(res, jc.IsTrue) 974 res, err = prr.pru0.Valid() 975 c.Assert(err, jc.ErrorIsNil) 976 c.Assert(res, jc.IsTrue) 977 } 978 979 type PeerRelation struct { 980 rel *state.Relation 981 app *state.Application 982 u0, u1, u2, u3 *state.Unit 983 ru0, ru1, ru2, ru3 *state.RelationUnit 984 } 985 986 func preventPeerUnitsDestroyRemove(c *gc.C, pr *PeerRelation) { 987 preventUnitDestroyRemove(c, pr.u0) 988 preventUnitDestroyRemove(c, pr.u1) 989 preventUnitDestroyRemove(c, pr.u2) 990 preventUnitDestroyRemove(c, pr.u3) 991 } 992 993 func newPeerRelation(c *gc.C, st *state.State) *PeerRelation { 994 app := state.AddTestingApplication(c, st, "riak", state.AddTestingCharm(c, st, "riak")) 995 ep, err := app.Endpoint("ring") 996 c.Assert(err, jc.ErrorIsNil) 997 rel, err := st.EndpointsRelation(ep) 998 c.Assert(err, jc.ErrorIsNil) 999 pr := &PeerRelation{rel: rel, app: app} 1000 pr.u0, pr.ru0 = addRU(c, app, rel, nil) 1001 pr.u1, pr.ru1 = addRU(c, app, rel, nil) 1002 pr.u2, pr.ru2 = addRU(c, app, rel, nil) 1003 pr.u3, pr.ru3 = addRU(c, app, rel, nil) 1004 return pr 1005 } 1006 1007 type ProReqRelation struct { 1008 rel *state.Relation 1009 papp, rapp *state.Application 1010 pu0, pu1, ru0, ru1 *state.Unit 1011 pru0, pru1, rru0, rru1 *state.RelationUnit 1012 } 1013 1014 func preventProReqUnitsDestroyRemove(c *gc.C, prr *ProReqRelation) { 1015 preventUnitDestroyRemove(c, prr.pu0) 1016 preventUnitDestroyRemove(c, prr.pu1) 1017 preventUnitDestroyRemove(c, prr.ru0) 1018 preventUnitDestroyRemove(c, prr.ru1) 1019 } 1020 1021 func newProReqRelation(c *gc.C, s *ConnSuite, scope charm.RelationScope, assignedMachines ...*state.Machine) *ProReqRelation { 1022 papp := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1023 var rapp *state.Application 1024 if scope == charm.ScopeGlobal { 1025 rapp = s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1026 } else { 1027 rapp = s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 1028 } 1029 return newProReqRelationForApps(c, s.State, papp, rapp, assignedMachines...) 1030 } 1031 1032 func newProReqRelationForApps(c *gc.C, st *state.State, proApp, reqApp *state.Application, assignedMachines ...*state.Machine) *ProReqRelation { 1033 eps, err := st.InferEndpoints(proApp.Name(), reqApp.Name()) 1034 c.Assert(err, jc.ErrorIsNil) 1035 rel, err := st.AddRelation(eps...) 1036 c.Assert(err, jc.ErrorIsNil) 1037 prr := &ProReqRelation{rel: rel, papp: proApp, rapp: reqApp} 1038 assignToMachine := func(unit *state.Unit) { 1039 if len(assignedMachines) == 0 { 1040 return 1041 } 1042 var machine *state.Machine 1043 machine, assignedMachines = assignedMachines[0], assignedMachines[1:] 1044 c.Assert(unit.AssignToMachine(machine), jc.ErrorIsNil) 1045 } 1046 prr.pu0, prr.pru0 = addRU(c, proApp, rel, nil) 1047 assignToMachine(prr.pu0) 1048 prr.pu1, prr.pru1 = addRU(c, proApp, rel, nil) 1049 assignToMachine(prr.pu1) 1050 if eps[0].Scope == charm.ScopeGlobal { 1051 prr.ru0, prr.rru0 = addRU(c, reqApp, rel, nil) 1052 assignToMachine(prr.ru0) 1053 prr.ru1, prr.rru1 = addRU(c, reqApp, rel, nil) 1054 assignToMachine(prr.ru1) 1055 } else { 1056 prr.ru0, prr.rru0 = addRU(c, reqApp, rel, prr.pu0) 1057 prr.ru1, prr.rru1 = addRU(c, reqApp, rel, prr.pu1) 1058 } 1059 return prr 1060 } 1061 1062 func (prr *ProReqRelation) watches() []*state.RelationScopeWatcher { 1063 return []*state.RelationScopeWatcher{ 1064 prr.pru0.WatchScope(), prr.pru1.WatchScope(), 1065 prr.rru0.WatchScope(), prr.rru1.WatchScope(), 1066 } 1067 } 1068 1069 func (prr *ProReqRelation) allEnterScope(c *gc.C) { 1070 g, _ := errgroup.WithContext(context.Background()) 1071 1072 g.Go(func() error { return prr.pru0.EnterScope(nil) }) 1073 g.Go(func() error { return prr.pru1.EnterScope(nil) }) 1074 g.Go(func() error { return prr.rru0.EnterScope(nil) }) 1075 g.Go(func() error { return prr.rru1.EnterScope(nil) }) 1076 1077 err := g.Wait() 1078 c.Assert(err, jc.ErrorIsNil) 1079 } 1080 1081 func addRU(c *gc.C, app *state.Application, rel *state.Relation, principal *state.Unit) (*state.Unit, *state.RelationUnit) { 1082 // Given the application app in the relation rel, add a unit of app and create 1083 // a RelationUnit with rel. If principal is supplied, app is assumed to be 1084 // subordinate and the unit will be created by temporarily entering the 1085 // relation's scope as the principal. 1086 var u *state.Unit 1087 if principal == nil { 1088 unit, err := app.AddUnit(state.AddUnitParams{}) 1089 c.Assert(err, jc.ErrorIsNil) 1090 u = unit 1091 } else { 1092 origUnits, err := app.AllUnits() 1093 c.Assert(err, jc.ErrorIsNil) 1094 pru, err := rel.Unit(principal) 1095 c.Assert(err, jc.ErrorIsNil) 1096 err = pru.EnterScope(nil) // to create the subordinate 1097 c.Assert(err, jc.ErrorIsNil) 1098 err = pru.LeaveScope() // to reset to initial expected state 1099 c.Assert(err, jc.ErrorIsNil) 1100 newUnits, err := app.AllUnits() 1101 c.Assert(err, jc.ErrorIsNil) 1102 for _, unit := range newUnits { 1103 found := false 1104 for _, old := range origUnits { 1105 if unit.Name() == old.Name() { 1106 found = true 1107 break 1108 } 1109 } 1110 if !found { 1111 u = unit 1112 break 1113 } 1114 } 1115 c.Assert(u, gc.NotNil) 1116 } 1117 ru, err := rel.Unit(u) 1118 c.Assert(err, jc.ErrorIsNil) 1119 c.Assert(ru.UnitName(), gc.Equals, u.Name()) 1120 return u, ru 1121 } 1122 1123 type RemoteProReqRelation struct { 1124 rel *state.Relation 1125 papp *state.RemoteApplication 1126 rapp *state.Application 1127 pru0, pru1, rru0, rru1 *state.RelationUnit 1128 ru0, ru1 *state.Unit 1129 } 1130 1131 func newRemoteProReqRelation(c *gc.C, s *ConnSuite) *RemoteProReqRelation { 1132 papp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1133 Name: "mysql", 1134 SourceModel: coretesting.ModelTag, 1135 Endpoints: []charm.Relation{{ 1136 Interface: "mysql", 1137 Name: "server", 1138 Role: charm.RoleProvider, 1139 Scope: charm.ScopeGlobal, 1140 }}}) 1141 c.Assert(err, jc.ErrorIsNil) 1142 rapp := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1143 1144 eps, err := s.State.InferEndpoints("mysql", "wordpress") 1145 c.Assert(err, jc.ErrorIsNil) 1146 rel, err := s.State.AddRelation(eps...) 1147 c.Assert(err, jc.ErrorIsNil) 1148 1149 prr := &RemoteProReqRelation{rel: rel, papp: papp, rapp: rapp} 1150 prr.pru0 = addRemoteRU(c, rel, "mysql/0") 1151 prr.pru1 = addRemoteRU(c, rel, "mysql/1") 1152 prr.ru0, prr.rru0 = addRU(c, rapp, rel, nil) 1153 prr.ru1, prr.rru1 = addRU(c, rapp, rel, nil) 1154 return prr 1155 } 1156 1157 func (prr *RemoteProReqRelation) watches() []*state.RelationScopeWatcher { 1158 return []*state.RelationScopeWatcher{ 1159 prr.pru0.WatchScope(), prr.pru1.WatchScope(), 1160 prr.rru0.WatchScope(), prr.rru1.WatchScope(), 1161 } 1162 } 1163 1164 func addRemoteRU(c *gc.C, rel *state.Relation, unitName string) *state.RelationUnit { 1165 // Add a remote unit with the given name to rel. 1166 ru, err := rel.RemoteUnit(unitName) 1167 c.Assert(err, jc.ErrorIsNil) 1168 return ru 1169 } 1170 1171 type WatchRelationUnitsSuite struct { 1172 ConnSuite 1173 } 1174 1175 var _ = gc.Suite(&WatchRelationUnitsSuite{}) 1176 1177 func (s *WatchRelationUnitsSuite) TestPeer(c *gc.C) { 1178 // Create an application and get a peer relation. 1179 riak := s.AddTestingApplication(c, "riak", s.AddTestingCharm(c, "riak")) 1180 riakEP, err := riak.Endpoint("ring") 1181 c.Assert(err, jc.ErrorIsNil) 1182 rels, err := riak.Relations() 1183 c.Assert(err, jc.ErrorIsNil) 1184 c.Assert(rels, gc.HasLen, 1) 1185 rel := rels[0] 1186 1187 // Add some units to the application and set their private addresses; get 1188 // the relevant RelationUnits. 1189 // (Private addresses should be set by their unit agents on 1190 // startup; this test does not include that, but Join expects 1191 // the information to be available, and uses it to populate the 1192 // relation settings node.) 1193 addUnit := func(i int) *state.RelationUnit { 1194 unit, err := riak.AddUnit(state.AddUnitParams{}) 1195 c.Assert(err, jc.ErrorIsNil) 1196 err = unit.AssignToNewMachine() 1197 c.Assert(err, jc.ErrorIsNil) 1198 mId, err := unit.AssignedMachineId() 1199 c.Assert(err, jc.ErrorIsNil) 1200 machine, err := s.State.Machine(mId) 1201 c.Assert(err, jc.ErrorIsNil) 1202 privateAddr := network.NewSpaceAddress( 1203 fmt.Sprintf("riak%d.example.com", i), 1204 network.WithScope(network.ScopeCloudLocal), 1205 ) 1206 err = machine.SetProviderAddresses(privateAddr) 1207 c.Assert(err, jc.ErrorIsNil) 1208 ru, err := rel.Unit(unit) 1209 c.Assert(err, jc.ErrorIsNil) 1210 c.Assert(ru.Endpoint(), gc.Equals, riakEP) 1211 return ru 1212 } 1213 ru0 := addUnit(0) 1214 ru1 := addUnit(1) 1215 ru2 := addUnit(2) 1216 1217 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1218 1219 // ---------- Single unit ---------- 1220 1221 // Start watching the relation from the perspective of the first unit. 1222 w0 := ru0.Watch() 1223 defer testing.AssertStop(c, w0) 1224 w0c := testing.NewRelationUnitsWatcherC(c, w0) 1225 w0c.AssertChange(nil, []string{"riak"}, nil) 1226 w0c.AssertNoChange() 1227 1228 // Join the first unit to the relation, and change the settings, and 1229 // check that nothing apparently happens. 1230 err = ru0.EnterScope(nil) 1231 c.Assert(err, jc.ErrorIsNil) 1232 changeSettings(c, ru0) 1233 w0c.AssertNoChange() 1234 1235 // Update the application settings, and see that the units notice 1236 updateAppSettings(c, s.State, rel, riak, "riak/0", map[string]interface{}{"foo": "bar"}) 1237 w0c.AssertChange(nil, []string{"riak"}, nil) 1238 w0c.AssertNoChange() 1239 1240 // ---------- Two units ---------- 1241 1242 // Now join another unit to the relation... 1243 err = ru1.EnterScope(nil) 1244 c.Assert(err, jc.ErrorIsNil) 1245 1246 // ...and check that the first relation unit sees the change. 1247 expectChanged := []string{"riak/1"} 1248 w0c.AssertChange(expectChanged, nil, nil) 1249 w0c.AssertNoChange() 1250 1251 // Join again, check it's a no-op. 1252 err = ru1.EnterScope(nil) 1253 c.Assert(err, jc.ErrorIsNil) 1254 w0c.AssertNoChange() 1255 1256 // Start watching the relation from the perspective of the second unit, 1257 // and check that it sees the right state. 1258 w1 := ru1.Watch() 1259 defer testing.AssertStop(c, w1) 1260 w1c := testing.NewRelationUnitsWatcherC(c, w1) 1261 expectChanged = []string{"riak/0"} 1262 w1c.AssertChange(expectChanged, []string{"riak"}, nil) 1263 w1c.AssertNoChange() 1264 1265 // ---------- Three units ---------- 1266 1267 // Whoa, it works. Ok, check the third unit's opinion of the state. 1268 w2 := ru2.Watch() 1269 defer testing.AssertStop(c, w2) 1270 w2c := testing.NewRelationUnitsWatcherC(c, w2) 1271 expectChanged = []string{"riak/0", "riak/1"} 1272 w2c.AssertChange(expectChanged, []string{"riak"}, nil) 1273 w2c.AssertNoChange() 1274 1275 // Join the third unit, and check the first and second units see it. 1276 err = ru2.EnterScope(nil) 1277 c.Assert(err, jc.ErrorIsNil) 1278 expectChanged = []string{"riak/2"} 1279 w0c.AssertChange(expectChanged, nil, nil) 1280 w0c.AssertNoChange() 1281 w1c.AssertChange(expectChanged, nil, nil) 1282 w1c.AssertNoChange() 1283 1284 // Change the second unit's settings, and check that only 1285 // the first and third see changes. 1286 changeSettings(c, ru1) 1287 w1c.AssertNoChange() 1288 expectChanged = []string{"riak/1"} 1289 w0c.AssertChange(expectChanged, nil, nil) 1290 w0c.AssertNoChange() 1291 w2c.AssertChange(expectChanged, nil, nil) 1292 w2c.AssertNoChange() 1293 1294 // Update the application settings, and see that the units notice 1295 updateAppSettings(c, s.State, rel, riak, "riak/0", map[string]interface{}{"foo": "baz"}) 1296 w0c.AssertChange(nil, []string{"riak"}, nil) 1297 w0c.AssertNoChange() 1298 w1c.AssertChange(nil, []string{"riak"}, nil) 1299 w1c.AssertNoChange() 1300 w2c.AssertChange(nil, []string{"riak"}, nil) 1301 w2c.AssertNoChange() 1302 1303 // ---------- Two units again ---------- 1304 1305 // Depart the second unit, and check that the first and third detect it. 1306 err = ru1.LeaveScope() 1307 c.Assert(err, jc.ErrorIsNil) 1308 expectDeparted := []string{"riak/1"} 1309 w0c.AssertChange(nil, nil, expectDeparted) 1310 w0c.AssertNoChange() 1311 w2c.AssertChange(nil, nil, expectDeparted) 1312 w2c.AssertNoChange() 1313 1314 // Change its settings, and check the others don't observe anything. 1315 changeSettings(c, ru1) 1316 w0c.AssertNoChange() 1317 w2c.AssertNoChange() 1318 1319 // Check no spurious events showed up on the second unit's watch, and check 1320 // it closes cleanly. 1321 w1c.AssertNoChange() 1322 testing.AssertStop(c, w1) 1323 1324 // OK, we're done here. Cleanup, and error detection during same, 1325 // will be handled by the deferred kill/stop calls. Phew. 1326 } 1327 1328 func (s *WatchRelationUnitsSuite) TestWatchAppSettings(c *gc.C) { 1329 loggo.GetLogger("juju.state.relationunits").SetLogLevel(loggo.TRACE) 1330 prr := newProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) 1331 prr.allEnterScope(c) 1332 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1333 1334 // Watch from the perspective of the requiring unit, and see that a change to the 1335 // providing application is seen 1336 watcher := prr.rru0.Watch() 1337 defer testing.AssertStop(c, watcher) 1338 w0c := testing.NewRelationUnitsWatcherC(c, watcher) 1339 w0c.AssertChange([]string{"mysql/0", "mysql/1"}, []string{"mysql"}, nil) 1340 w0c.AssertNoChange() 1341 updateAppSettings(c, s.State, prr.rel, prr.papp, prr.pu0.Name(), map[string]interface{}{"foo": "bar"}) 1342 w0c.AssertChange(nil, []string{prr.papp.Name()}, nil) 1343 } 1344 1345 func (s *WatchRelationUnitsSuite) TestProviderRequirerGlobal(c *gc.C) { 1346 // Create a pair of application and a relation between them. 1347 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1348 mysqlEP, err := mysql.Endpoint("server") 1349 c.Assert(err, jc.ErrorIsNil) 1350 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1351 wordpressEP, err := wordpress.Endpoint("db") 1352 c.Assert(err, jc.ErrorIsNil) 1353 rel, err := s.State.AddRelation(mysqlEP, wordpressEP) 1354 c.Assert(err, jc.ErrorIsNil) 1355 1356 // Add some units to the application and set their private addresses. 1357 addUnit := func(srv *state.Application, sub string, ep state.Endpoint) *state.RelationUnit { 1358 unit, err := srv.AddUnit(state.AddUnitParams{}) 1359 c.Assert(err, jc.ErrorIsNil) 1360 ru, err := rel.Unit(unit) 1361 c.Assert(err, jc.ErrorIsNil) 1362 c.Assert(ru.Endpoint(), gc.Equals, ep) 1363 return ru 1364 } 1365 msru0 := addUnit(mysql, "ms0", mysqlEP) 1366 msru1 := addUnit(mysql, "ms1", mysqlEP) 1367 wpru0 := addUnit(wordpress, "wp0", wordpressEP) 1368 wpru1 := addUnit(wordpress, "wp1", wordpressEP) 1369 1370 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1371 1372 // ---------- Single role active ---------- 1373 1374 // Watch the relation from the perspective of the first provider unit and 1375 // check initial event. 1376 msw0 := msru0.Watch() 1377 defer testing.AssertStop(c, msw0) 1378 msw0c := testing.NewRelationUnitsWatcherC(c, msw0) 1379 msw0c.AssertChange(nil, []string{"wordpress"}, nil) 1380 msw0c.AssertNoChange() 1381 1382 // Join the unit to the relation, change its settings, and check that 1383 // nothing apparently happens. 1384 err = msru0.EnterScope(nil) 1385 c.Assert(err, jc.ErrorIsNil) 1386 changeSettings(c, msru0) 1387 msw0c.AssertNoChange() 1388 1389 // Join the second provider unit, start its watch, and check what it thinks the 1390 // state of the relation is. 1391 err = msru1.EnterScope(nil) 1392 c.Assert(err, jc.ErrorIsNil) 1393 msw1 := msru1.Watch() 1394 defer testing.AssertStop(c, msw1) 1395 msw1c := testing.NewRelationUnitsWatcherC(c, msw1) 1396 msw1c.AssertChange(nil, []string{"wordpress"}, nil) 1397 msw1c.AssertNoChange() 1398 1399 // Change the unit's settings, and check that neither provider unit 1400 // observes any change. 1401 changeSettings(c, msru1) 1402 msw1c.AssertNoChange() 1403 msw0c.AssertNoChange() 1404 1405 // ---------- Two roles active ---------- 1406 1407 // Start watches from both requirer units' perspectives, and check that 1408 // they see the provider units. 1409 expectChanged := []string{"mysql/0", "mysql/1"} 1410 wpw0 := wpru0.Watch() 1411 defer testing.AssertStop(c, wpw0) 1412 wpw0c := testing.NewRelationUnitsWatcherC(c, wpw0) 1413 wpw0c.AssertChange(expectChanged, []string{"mysql"}, nil) 1414 wpw0c.AssertNoChange() 1415 wpw1 := wpru1.Watch() 1416 defer testing.AssertStop(c, wpw1) 1417 wpw1c := testing.NewRelationUnitsWatcherC(c, wpw1) 1418 wpw1c.AssertChange(expectChanged, []string{"mysql"}, nil) 1419 wpw1c.AssertNoChange() 1420 1421 // Join the first requirer unit, and check the provider units see it. 1422 err = wpru0.EnterScope(nil) 1423 c.Assert(err, jc.ErrorIsNil) 1424 expectChanged = []string{"wordpress/0"} 1425 msw0c.AssertChange(expectChanged, nil, nil) 1426 msw0c.AssertNoChange() 1427 msw1c.AssertChange(expectChanged, nil, nil) 1428 msw1c.AssertNoChange() 1429 1430 // Join again, check no-op. 1431 err = wpru0.EnterScope(nil) 1432 c.Assert(err, jc.ErrorIsNil) 1433 msw0c.AssertNoChange() 1434 msw1c.AssertNoChange() 1435 1436 // Join the second requirer, and check the provider units see the change. 1437 err = wpru1.EnterScope(nil) 1438 c.Assert(err, jc.ErrorIsNil) 1439 expectChanged = []string{"wordpress/1"} 1440 msw0c.AssertChange(expectChanged, nil, nil) 1441 msw0c.AssertNoChange() 1442 msw1c.AssertChange(expectChanged, nil, nil) 1443 msw1c.AssertNoChange() 1444 1445 // Verify that neither requirer has observed any change to the relation. 1446 wpw0c.AssertNoChange() 1447 wpw1c.AssertNoChange() 1448 1449 // Change settings for the first requirer, check providers see it... 1450 changeSettings(c, wpru0) 1451 expectChanged = []string{"wordpress/0"} 1452 msw0c.AssertChange(expectChanged, nil, nil) 1453 msw0c.AssertNoChange() 1454 msw1c.AssertChange(expectChanged, nil, nil) 1455 msw1c.AssertNoChange() 1456 1457 // ...and requirers don't. 1458 wpw0c.AssertNoChange() 1459 wpw1c.AssertNoChange() 1460 1461 // Change the application settings for the provider, and see that both 1462 // requirers see it, but the providers do not 1463 updateAppSettings(c, s.State, rel, mysql, "mysql/0", map[string]interface{}{"foo": "bar"}) 1464 wpw0c.AssertChange(nil, []string{"mysql"}, nil) 1465 wpw1c.AssertChange(nil, []string{"mysql"}, nil) 1466 msw0c.AssertNoChange() 1467 msw1c.AssertNoChange() 1468 1469 // Depart the second requirer and check the providers see it... 1470 err = wpru1.LeaveScope() 1471 c.Assert(err, jc.ErrorIsNil) 1472 expectDeparted := []string{"wordpress/1"} 1473 msw0c.AssertChange(nil, nil, expectDeparted) 1474 msw0c.AssertNoChange() 1475 msw1c.AssertChange(nil, nil, expectDeparted) 1476 msw1c.AssertNoChange() 1477 1478 // ...and the requirers don't. 1479 wpw0c.AssertNoChange() 1480 wpw1c.AssertNoChange() 1481 1482 // Cleanup handled by defers as before. 1483 } 1484 1485 func (s *WatchRelationUnitsSuite) TestProviderRequirerContainer(c *gc.C) { 1486 // Create a pair of application and a relation between them. 1487 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1488 mysqlEP, err := mysql.Endpoint("juju-info") 1489 c.Assert(err, jc.ErrorIsNil) 1490 logging := s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 1491 loggingEP, err := logging.Endpoint("info") 1492 c.Assert(err, jc.ErrorIsNil) 1493 rel, err := s.State.AddRelation(mysqlEP, loggingEP) 1494 c.Assert(err, jc.ErrorIsNil) 1495 1496 // Change mysqlEP to match the endpoint that will actually be used by the relation. 1497 mysqlEP.Scope = charm.ScopeContainer 1498 1499 // Add some units to the application and set their private addresses. 1500 addUnits := func(i int) (*state.RelationUnit, *state.RelationUnit) { 1501 msu, err := mysql.AddUnit(state.AddUnitParams{}) 1502 c.Assert(err, jc.ErrorIsNil) 1503 msru, err := rel.Unit(msu) 1504 c.Assert(err, jc.ErrorIsNil) 1505 c.Assert(msru.Endpoint(), gc.Equals, mysqlEP) 1506 err = msru.EnterScope(nil) 1507 c.Assert(err, jc.ErrorIsNil) 1508 err = msru.LeaveScope() 1509 c.Assert(err, jc.ErrorIsNil) 1510 lgu, err := s.State.Unit("logging/" + strconv.Itoa(i)) 1511 c.Assert(err, jc.ErrorIsNil) 1512 lgru, err := rel.Unit(lgu) 1513 c.Assert(err, jc.ErrorIsNil) 1514 c.Assert(lgru.Endpoint(), gc.Equals, loggingEP) 1515 return msru, lgru 1516 } 1517 msru0, lgru0 := addUnits(0) 1518 msru1, lgru1 := addUnits(1) 1519 1520 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1521 1522 // ---------- Single role active ---------- 1523 1524 // Start watching the relation from the perspective of the first unit, and 1525 // check the initial event. 1526 msw0 := msru0.Watch() 1527 defer testing.AssertStop(c, msw0) 1528 msw0c := testing.NewRelationUnitsWatcherC(c, msw0) 1529 msw0c.AssertChange(nil, []string{"logging"}, nil) 1530 msw0c.AssertNoChange() 1531 1532 // Join the unit to the relation, change its settings, and check that 1533 // nothing apparently happens. 1534 err = msru0.EnterScope(nil) 1535 c.Assert(err, jc.ErrorIsNil) 1536 changeSettings(c, msru0) 1537 msw0c.AssertNoChange() 1538 1539 // Watch the relation from the perspective of the second provider, and 1540 // check initial event. 1541 msw1 := msru1.Watch() 1542 defer testing.AssertStop(c, msw1) 1543 msw1c := testing.NewRelationUnitsWatcherC(c, msw1) 1544 msw1c.AssertChange(nil, []string{"logging"}, nil) 1545 msw1c.AssertNoChange() 1546 1547 // Join the second provider unit to the relation, and check that neither 1548 // watching unit observes any change. 1549 err = msru1.EnterScope(nil) 1550 c.Assert(err, jc.ErrorIsNil) 1551 msw1c.AssertNoChange() 1552 msw0c.AssertNoChange() 1553 1554 // Change the unit's settings, and check that nothing apparently happens. 1555 changeSettings(c, msru1) 1556 msw1c.AssertNoChange() 1557 msw0c.AssertNoChange() 1558 1559 // ---------- Two roles active ---------- 1560 1561 // Start a watch from the first requirer unit's perspective, and check it 1562 // only sees the first provider (with which it shares a container). 1563 lgw0 := lgru0.Watch() 1564 defer testing.AssertStop(c, lgw0) 1565 lgw0c := testing.NewRelationUnitsWatcherC(c, lgw0) 1566 expectChanged := []string{"mysql/0"} 1567 lgw0c.AssertChange(expectChanged, []string{"mysql"}, nil) 1568 lgw0c.AssertNoChange() 1569 1570 // Join the first requirer unit, and check that only the first provider 1571 // observes the change. 1572 err = lgru0.EnterScope(nil) 1573 c.Assert(err, jc.ErrorIsNil) 1574 expectChanged = []string{"logging/0"} 1575 msw0c.AssertChange(expectChanged, nil, nil) 1576 msw0c.AssertNoChange() 1577 msw1c.AssertNoChange() 1578 lgw0c.AssertNoChange() 1579 1580 // Watch from the second requirer's perspective, and check it only sees the 1581 // second provider. 1582 lgw1 := lgru1.Watch() 1583 defer testing.AssertStop(c, lgw1) 1584 lgw1c := testing.NewRelationUnitsWatcherC(c, lgw1) 1585 expectChanged = []string{"mysql/1"} 1586 lgw1c.AssertChange(expectChanged, []string{"mysql"}, nil) 1587 lgw1c.AssertNoChange() 1588 1589 // Join the second requirer, and check that the first provider observes it... 1590 err = lgru1.EnterScope(nil) 1591 c.Assert(err, jc.ErrorIsNil) 1592 expectChanged = []string{"logging/1"} 1593 msw1c.AssertChange(expectChanged, nil, nil) 1594 msw1c.AssertNoChange() 1595 1596 // ...and that nothing else sees anything. 1597 msw0c.AssertNoChange() 1598 lgw0c.AssertNoChange() 1599 lgw1c.AssertNoChange() 1600 1601 // Change the second provider's settings and check that the second 1602 // requirer notices... 1603 changeSettings(c, msru1) 1604 expectChanged = []string{"mysql/1"} 1605 lgw1c.AssertChange(expectChanged, nil, nil) 1606 lgw1c.AssertNoChange() 1607 1608 // ...but that nothing else does. 1609 msw0c.AssertNoChange() 1610 msw1c.AssertNoChange() 1611 lgw0c.AssertNoChange() 1612 1613 // Change the provider application settings, and see that all requirers notice 1614 updateAppSettings(c, s.State, rel, mysql, "mysql/0", map[string]interface{}{"foo": "bar"}) 1615 lgw0c.AssertChange(nil, []string{"mysql"}, nil) 1616 lgw1c.AssertChange(nil, []string{"mysql"}, nil) 1617 msw0c.AssertNoChange() 1618 msw1c.AssertNoChange() 1619 1620 // Finally, depart the first provider, and check that only the first 1621 // requirer observes any change. 1622 err = msru0.LeaveScope() 1623 c.Assert(err, jc.ErrorIsNil) 1624 expectDeparted := []string{"mysql/0"} 1625 lgw0c.AssertChange(nil, nil, expectDeparted) 1626 lgw0c.AssertNoChange() 1627 lgw1c.AssertNoChange() 1628 msw0c.AssertNoChange() 1629 msw1c.AssertNoChange() 1630 1631 // Again, I think we're done, and can be comfortable that the appropriate 1632 // connections are in place. 1633 } 1634 1635 type WatchUnitsSuite struct { 1636 ConnSuite 1637 } 1638 1639 var _ = gc.Suite(&WatchUnitsSuite{}) 1640 1641 // updateAppSettings will update the application settings in a relation. 1642 // It claims leadership of the application for "unitName" and then sets the 1643 // application settings for that application to the provided settings. 1644 func updateAppSettings(c *gc.C, state *state.State, rel *state.Relation, app *state.Application, unitName string, settings map[string]interface{}) { 1645 c.Assert(rel.UpdateApplicationSettings(app.Name(), &fakeToken{}, settings), jc.ErrorIsNil) 1646 } 1647 1648 func (s *WatchUnitsSuite) TestProviderRequirerGlobal(c *gc.C) { 1649 // Create a pair of applications and a relation between them. 1650 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1651 mysqlEP, err := mysql.Endpoint("server") 1652 c.Assert(err, jc.ErrorIsNil) 1653 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1654 wordpressEP, err := wordpress.Endpoint("db") 1655 c.Assert(err, jc.ErrorIsNil) 1656 rel, err := s.State.AddRelation(mysqlEP, wordpressEP) 1657 c.Assert(err, jc.ErrorIsNil) 1658 1659 // Add some units to the applications and set their private addresses. 1660 addUnit := func(srv *state.Application) *state.RelationUnit { 1661 unit, err := srv.AddUnit(state.AddUnitParams{}) 1662 c.Assert(err, jc.ErrorIsNil) 1663 ru, err := rel.Unit(unit) 1664 c.Assert(err, jc.ErrorIsNil) 1665 return ru 1666 } 1667 mysql0 := addUnit(mysql) 1668 wordpress0 := addUnit(wordpress) 1669 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1670 1671 wordpressWatcher, err := rel.WatchUnits("wordpress") 1672 c.Assert(err, jc.ErrorIsNil) 1673 defer testing.AssertStop(c, wordpressWatcher) 1674 wordpressWatcherC := testing.NewRelationUnitsWatcherC(c, wordpressWatcher) 1675 wordpressWatcherC.AssertChange(nil, []string{"wordpress"}, nil) 1676 wordpressWatcherC.AssertNoChange() 1677 1678 // Join the mysql unit to the relation, change settings, and check 1679 // that only the mysql relation units watcher triggers. 1680 err = mysql0.EnterScope(nil) 1681 c.Assert(err, jc.ErrorIsNil) 1682 changeSettings(c, mysql0) 1683 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 1684 1685 c.Logf("watching just mysql") 1686 mysqlWatcher, err := rel.WatchUnits("mysql") 1687 c.Assert(err, jc.ErrorIsNil) 1688 defer testing.AssertStop(c, mysqlWatcher) 1689 mysqlWatcherC := testing.NewRelationUnitsWatcherC(c, mysqlWatcher) 1690 mysqlWatcherC.AssertChange([]string{"mysql/0"}, []string{"mysql"}, nil) 1691 mysqlWatcherC.AssertNoChange() 1692 wordpressWatcherC.AssertNoChange() 1693 1694 // Now join the wordpress unit to the relation, and check that only 1695 // the wordpress relation units watcher triggers. 1696 err = wordpress0.EnterScope(nil) 1697 c.Assert(err, jc.ErrorIsNil) 1698 wordpressWatcherC.AssertChange([]string{"wordpress/0"}, nil, nil) 1699 wordpressWatcherC.AssertNoChange() 1700 mysqlWatcherC.AssertNoChange() 1701 1702 // Now update the mysql application settings, and see that only the mysql watcher notices 1703 updateAppSettings(c, s.State, rel, mysql, "mysql/0", map[string]interface{}{"foo": "bar"}) 1704 mysqlWatcherC.AssertChange(nil, []string{"mysql"}, nil) 1705 mysqlWatcherC.AssertNoChange() 1706 wordpressWatcherC.AssertNoChange() 1707 } 1708 1709 func (s *WatchUnitsSuite) TestProviderRequirerContainer(c *gc.C) { 1710 // Create a pair of applications and a relation between them. 1711 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1712 mysqlEP, err := mysql.Endpoint("juju-info") 1713 c.Assert(err, jc.ErrorIsNil) 1714 logging := s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 1715 loggingEP, err := logging.Endpoint("info") 1716 c.Assert(err, jc.ErrorIsNil) 1717 rel, err := s.State.AddRelation(mysqlEP, loggingEP) 1718 c.Assert(err, jc.ErrorIsNil) 1719 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1720 1721 _, err = rel.WatchUnits("mysql") 1722 c.Assert(err, gc.ErrorMatches, `"juju-info" endpoint is not globally scoped`) 1723 _, err = rel.WatchUnits("logging") 1724 c.Assert(err, gc.ErrorMatches, `"info" endpoint is not globally scoped`) 1725 } 1726 1727 func changeSettings(c *gc.C, ru *state.RelationUnit) { 1728 node, err := ru.Settings() 1729 c.Assert(err, jc.ErrorIsNil) 1730 value, _ := node.Get("value") 1731 v, _ := value.(int) 1732 node.Set("value", v+1) 1733 _, err = node.Write() 1734 c.Assert(err, jc.ErrorIsNil) 1735 }