github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/uniter/uniter_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/charm.v6-unstable" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/apiserver/common" 18 commontesting "github.com/juju/juju/apiserver/common/testing" 19 "github.com/juju/juju/apiserver/params" 20 apiservertesting "github.com/juju/juju/apiserver/testing" 21 "github.com/juju/juju/apiserver/uniter" 22 "github.com/juju/juju/juju/testing" 23 "github.com/juju/juju/network" 24 "github.com/juju/juju/state" 25 "github.com/juju/juju/state/multiwatcher" 26 statetesting "github.com/juju/juju/state/testing" 27 "github.com/juju/juju/status" 28 "github.com/juju/juju/testing/factory" 29 jujuFactory "github.com/juju/juju/testing/factory" 30 ) 31 32 // uniterSuite implements common testing suite for all API 33 // versions. It's not intended to be used directly or registered as a 34 // suite, but embedded. 35 type uniterSuite struct { 36 testing.JujuConnSuite 37 38 authorizer apiservertesting.FakeAuthorizer 39 resources *common.Resources 40 uniter *uniter.UniterAPIV3 41 42 machine0 *state.Machine 43 machine1 *state.Machine 44 wordpress *state.Application 45 wpCharm *state.Charm 46 mysql *state.Application 47 wordpressUnit *state.Unit 48 mysqlUnit *state.Unit 49 50 meteredService *state.Application 51 meteredCharm *state.Charm 52 meteredUnit *state.Unit 53 } 54 55 var _ = gc.Suite(&uniterSuite{}) 56 57 func (s *uniterSuite) SetUpTest(c *gc.C) { 58 s.JujuConnSuite.SetUpTest(c) 59 60 factory := jujuFactory.NewFactory(s.State) 61 // Create two machines, two services and add a unit to each service. 62 s.machine0 = factory.MakeMachine(c, &jujuFactory.MachineParams{ 63 Series: "quantal", 64 Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, 65 }) 66 s.machine1 = factory.MakeMachine(c, &jujuFactory.MachineParams{ 67 Series: "quantal", 68 Jobs: []state.MachineJob{state.JobHostUnits}, 69 }) 70 s.wpCharm = factory.MakeCharm(c, &jujuFactory.CharmParams{ 71 Name: "wordpress", 72 URL: "cs:quantal/wordpress-3", 73 }) 74 s.wordpress = factory.MakeApplication(c, &jujuFactory.ApplicationParams{ 75 Name: "wordpress", 76 Charm: s.wpCharm, 77 }) 78 mysqlCharm := factory.MakeCharm(c, &jujuFactory.CharmParams{ 79 Name: "mysql", 80 }) 81 s.mysql = factory.MakeApplication(c, &jujuFactory.ApplicationParams{ 82 Name: "mysql", 83 Charm: mysqlCharm, 84 }) 85 s.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{ 86 Application: s.wordpress, 87 Machine: s.machine0, 88 }) 89 s.mysqlUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{ 90 Application: s.mysql, 91 Machine: s.machine1, 92 }) 93 94 s.meteredCharm = s.Factory.MakeCharm(c, &jujuFactory.CharmParams{ 95 Name: "metered", 96 URL: "cs:quantal/metered", 97 }) 98 s.meteredService = s.Factory.MakeApplication(c, &jujuFactory.ApplicationParams{ 99 Charm: s.meteredCharm, 100 }) 101 s.meteredUnit = s.Factory.MakeUnit(c, &jujuFactory.UnitParams{ 102 Application: s.meteredService, 103 SetCharmURL: true, 104 }) 105 106 // Create a FakeAuthorizer so we can check permissions, 107 // set up assuming unit 0 has logged in. 108 s.authorizer = apiservertesting.FakeAuthorizer{ 109 Tag: s.wordpressUnit.Tag(), 110 } 111 112 // Create the resource registry separately to track invocations to 113 // Register. 114 s.resources = common.NewResources() 115 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 116 117 uniterAPIV3, err := uniter.NewUniterAPIV4( 118 s.State, 119 s.resources, 120 s.authorizer, 121 ) 122 c.Assert(err, jc.ErrorIsNil) 123 s.uniter = uniterAPIV3 124 } 125 126 func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) { 127 anAuthorizer := s.authorizer 128 anAuthorizer.Tag = names.NewMachineTag("9") 129 _, err := uniter.NewUniterAPIV4(s.State, s.resources, anAuthorizer) 130 c.Assert(err, gc.NotNil) 131 c.Assert(err, gc.ErrorMatches, "permission denied") 132 } 133 134 func (s *uniterSuite) TestSetStatus(c *gc.C) { 135 now := time.Now() 136 sInfo := status.StatusInfo{ 137 Status: status.Executing, 138 Message: "blah", 139 Since: &now, 140 } 141 err := s.wordpressUnit.SetAgentStatus(sInfo) 142 c.Assert(err, jc.ErrorIsNil) 143 sInfo = status.StatusInfo{ 144 Status: status.Executing, 145 Message: "foo", 146 Since: &now, 147 } 148 err = s.mysqlUnit.SetAgentStatus(sInfo) 149 c.Assert(err, jc.ErrorIsNil) 150 151 args := params.SetStatus{ 152 Entities: []params.EntityStatusArgs{ 153 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 154 {Tag: "unit-wordpress-0", Status: status.Rebooting.String(), Info: "foobar"}, 155 {Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"}, 156 }} 157 result, err := s.uniter.SetStatus(args) 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 160 Results: []params.ErrorResult{ 161 {apiservertesting.ErrUnauthorized}, 162 {nil}, 163 {apiservertesting.ErrUnauthorized}, 164 }, 165 }) 166 167 // Verify mysqlUnit - no change. 168 statusInfo, err := s.mysqlUnit.AgentStatus() 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 171 c.Assert(statusInfo.Message, gc.Equals, "foo") 172 // ...wordpressUnit is fine though. 173 statusInfo, err = s.wordpressUnit.AgentStatus() 174 c.Assert(err, jc.ErrorIsNil) 175 c.Assert(statusInfo.Status, gc.Equals, status.Rebooting) 176 c.Assert(statusInfo.Message, gc.Equals, "foobar") 177 } 178 179 func (s *uniterSuite) TestSetAgentStatus(c *gc.C) { 180 now := time.Now() 181 sInfo := status.StatusInfo{ 182 Status: status.Executing, 183 Message: "blah", 184 Since: &now, 185 } 186 err := s.wordpressUnit.SetAgentStatus(sInfo) 187 c.Assert(err, jc.ErrorIsNil) 188 sInfo = status.StatusInfo{ 189 Status: status.Executing, 190 Message: "foo", 191 Since: &now, 192 } 193 err = s.mysqlUnit.SetAgentStatus(sInfo) 194 c.Assert(err, jc.ErrorIsNil) 195 196 args := params.SetStatus{ 197 Entities: []params.EntityStatusArgs{ 198 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 199 {Tag: "unit-wordpress-0", Status: status.Executing.String(), Info: "foobar"}, 200 {Tag: "unit-foo-42", Status: status.Rebooting.String(), Info: "blah"}, 201 }} 202 result, err := s.uniter.SetAgentStatus(args) 203 c.Assert(err, jc.ErrorIsNil) 204 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 205 Results: []params.ErrorResult{ 206 {apiservertesting.ErrUnauthorized}, 207 {nil}, 208 {apiservertesting.ErrUnauthorized}, 209 }, 210 }) 211 212 // Verify mysqlUnit - no change. 213 statusInfo, err := s.mysqlUnit.AgentStatus() 214 c.Assert(err, jc.ErrorIsNil) 215 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 216 c.Assert(statusInfo.Message, gc.Equals, "foo") 217 // ...wordpressUnit is fine though. 218 statusInfo, err = s.wordpressUnit.AgentStatus() 219 c.Assert(err, jc.ErrorIsNil) 220 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 221 c.Assert(statusInfo.Message, gc.Equals, "foobar") 222 } 223 224 func (s *uniterSuite) TestSetUnitStatus(c *gc.C) { 225 now := time.Now() 226 sInfo := status.StatusInfo{ 227 Status: status.Active, 228 Message: "blah", 229 Since: &now, 230 } 231 err := s.wordpressUnit.SetStatus(sInfo) 232 c.Assert(err, jc.ErrorIsNil) 233 sInfo = status.StatusInfo{ 234 Status: status.Terminated, 235 Message: "foo", 236 Since: &now, 237 } 238 err = s.mysqlUnit.SetStatus(sInfo) 239 c.Assert(err, jc.ErrorIsNil) 240 241 args := params.SetStatus{ 242 Entities: []params.EntityStatusArgs{ 243 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 244 {Tag: "unit-wordpress-0", Status: status.Terminated.String(), Info: "foobar"}, 245 {Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"}, 246 }} 247 result, err := s.uniter.SetUnitStatus(args) 248 c.Assert(err, jc.ErrorIsNil) 249 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 250 Results: []params.ErrorResult{ 251 {apiservertesting.ErrUnauthorized}, 252 {nil}, 253 {apiservertesting.ErrUnauthorized}, 254 }, 255 }) 256 257 // Verify mysqlUnit - no change. 258 statusInfo, err := s.mysqlUnit.Status() 259 c.Assert(err, jc.ErrorIsNil) 260 c.Assert(statusInfo.Status, gc.Equals, status.Terminated) 261 c.Assert(statusInfo.Message, gc.Equals, "foo") 262 // ...wordpressUnit is fine though. 263 statusInfo, err = s.wordpressUnit.Status() 264 c.Assert(err, jc.ErrorIsNil) 265 c.Assert(statusInfo.Status, gc.Equals, status.Terminated) 266 c.Assert(statusInfo.Message, gc.Equals, "foobar") 267 } 268 269 func (s *uniterSuite) TestLife(c *gc.C) { 270 // Add a relation wordpress-mysql. 271 rel := s.addRelation(c, "wordpress", "mysql") 272 relUnit, err := rel.Unit(s.wordpressUnit) 273 c.Assert(err, jc.ErrorIsNil) 274 err = relUnit.EnterScope(nil) 275 c.Assert(err, jc.ErrorIsNil) 276 c.Assert(rel.Life(), gc.Equals, state.Alive) 277 278 // Make the wordpressUnit dead. 279 err = s.wordpressUnit.EnsureDead() 280 c.Assert(err, jc.ErrorIsNil) 281 err = s.wordpressUnit.Refresh() 282 c.Assert(err, jc.ErrorIsNil) 283 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 284 285 // Add another unit, so the service will stay dying when we 286 // destroy it later. 287 extraUnit, err := s.wordpress.AddUnit() 288 c.Assert(err, jc.ErrorIsNil) 289 c.Assert(extraUnit, gc.NotNil) 290 291 // Make the wordpress service dying. 292 err = s.wordpress.Destroy() 293 c.Assert(err, jc.ErrorIsNil) 294 err = s.wordpress.Refresh() 295 c.Assert(err, jc.ErrorIsNil) 296 c.Assert(s.wordpress.Life(), gc.Equals, state.Dying) 297 298 args := params.Entities{Entities: []params.Entity{ 299 {Tag: "unit-mysql-0"}, 300 {Tag: "unit-wordpress-0"}, 301 {Tag: "unit-foo-42"}, 302 {Tag: "application-mysql"}, 303 {Tag: "application-wordpress"}, 304 {Tag: "machine-0"}, 305 {Tag: "machine-1"}, 306 {Tag: "machine-42"}, 307 {Tag: "application-foo"}, 308 // TODO(dfc) these aren't valid tags any more 309 // but I hope to restore this test when params.Entity takes 310 // tags, not strings, which is coming soon. 311 // {Tag: "just-foo"}, 312 {Tag: rel.Tag().String()}, 313 {Tag: "relation-svc1.rel1#svc2.rel2"}, 314 // {Tag: "relation-blah"}, 315 }} 316 result, err := s.uniter.Life(args) 317 c.Assert(err, jc.ErrorIsNil) 318 c.Assert(result, gc.DeepEquals, params.LifeResults{ 319 Results: []params.LifeResult{ 320 {Error: apiservertesting.ErrUnauthorized}, 321 {Life: "dead"}, 322 {Error: apiservertesting.ErrUnauthorized}, 323 {Error: apiservertesting.ErrUnauthorized}, 324 {Life: "dying"}, 325 {Error: apiservertesting.ErrUnauthorized}, 326 {Error: apiservertesting.ErrUnauthorized}, 327 {Error: apiservertesting.ErrUnauthorized}, 328 {Error: apiservertesting.ErrUnauthorized}, 329 // TODO(dfc) see above 330 // {Error: apiservertesting.ErrUnauthorized}, 331 {Error: apiservertesting.ErrUnauthorized}, 332 {Error: apiservertesting.ErrUnauthorized}, 333 // {Error: apiservertesting.ErrUnauthorized}, 334 }, 335 }) 336 } 337 338 func (s *uniterSuite) TestEnsureDead(c *gc.C) { 339 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 340 c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) 341 342 args := params.Entities{Entities: []params.Entity{ 343 {Tag: "unit-mysql-0"}, 344 {Tag: "unit-wordpress-0"}, 345 {Tag: "unit-foo-42"}, 346 }} 347 result, err := s.uniter.EnsureDead(args) 348 c.Assert(err, jc.ErrorIsNil) 349 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 350 Results: []params.ErrorResult{ 351 {apiservertesting.ErrUnauthorized}, 352 {nil}, 353 {apiservertesting.ErrUnauthorized}, 354 }, 355 }) 356 357 err = s.wordpressUnit.Refresh() 358 c.Assert(err, jc.ErrorIsNil) 359 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 360 err = s.mysqlUnit.Refresh() 361 c.Assert(err, jc.ErrorIsNil) 362 c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) 363 364 // Try it again on a Dead unit; should work. 365 args = params.Entities{ 366 Entities: []params.Entity{{Tag: "unit-wordpress-0"}}, 367 } 368 result, err = s.uniter.EnsureDead(args) 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 371 Results: []params.ErrorResult{{nil}}, 372 }) 373 374 // Verify Life is unchanged. 375 err = s.wordpressUnit.Refresh() 376 c.Assert(err, jc.ErrorIsNil) 377 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 378 } 379 380 func (s *uniterSuite) TestWatch(c *gc.C) { 381 c.Assert(s.resources.Count(), gc.Equals, 0) 382 383 args := params.Entities{Entities: []params.Entity{ 384 {Tag: "unit-mysql-0"}, 385 {Tag: "unit-wordpress-0"}, 386 {Tag: "unit-foo-42"}, 387 {Tag: "application-mysql"}, 388 {Tag: "application-wordpress"}, 389 {Tag: "application-foo"}, 390 // TODO(dfc) these aren't valid tags any more 391 // but I hope to restore this test when params.Entity takes 392 // tags, not strings, which is coming soon. 393 // {Tag: "just-foo"}, 394 }} 395 result, err := s.uniter.Watch(args) 396 c.Assert(err, jc.ErrorIsNil) 397 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 398 Results: []params.NotifyWatchResult{ 399 {Error: apiservertesting.ErrUnauthorized}, 400 {NotifyWatcherId: "1"}, 401 {Error: apiservertesting.ErrUnauthorized}, 402 {Error: apiservertesting.ErrUnauthorized}, 403 {NotifyWatcherId: "2"}, 404 {Error: apiservertesting.ErrUnauthorized}, 405 // see above 406 // {Error: apiservertesting.ErrUnauthorized}, 407 }, 408 }) 409 410 // Verify the resource was registered and stop when done 411 c.Assert(s.resources.Count(), gc.Equals, 2) 412 resource1 := s.resources.Get("1") 413 defer statetesting.AssertStop(c, resource1) 414 resource2 := s.resources.Get("2") 415 defer statetesting.AssertStop(c, resource2) 416 417 // Check that the Watch has consumed the initial event ("returned" in 418 // the Watch call) 419 wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher)) 420 wc.AssertNoChange() 421 wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher)) 422 wc.AssertNoChange() 423 } 424 425 func (s *uniterSuite) TestPublicAddress(c *gc.C) { 426 // Try first without setting an address. 427 args := params.Entities{Entities: []params.Entity{ 428 {Tag: "unit-mysql-0"}, 429 {Tag: "unit-wordpress-0"}, 430 {Tag: "unit-foo-42"}, 431 }} 432 expectErr := ¶ms.Error{ 433 Code: params.CodeNoAddressSet, 434 Message: `"unit-wordpress-0" has no public address set`, 435 } 436 result, err := s.uniter.PublicAddress(args) 437 c.Assert(err, jc.ErrorIsNil) 438 c.Assert(result, gc.DeepEquals, params.StringResults{ 439 Results: []params.StringResult{ 440 {Error: apiservertesting.ErrUnauthorized}, 441 {Error: expectErr}, 442 {Error: apiservertesting.ErrUnauthorized}, 443 }, 444 }) 445 446 // Now set it an try again. 447 err = s.machine0.SetProviderAddresses( 448 network.NewScopedAddress("1.2.3.4", network.ScopePublic), 449 ) 450 c.Assert(err, jc.ErrorIsNil) 451 address, err := s.wordpressUnit.PublicAddress() 452 c.Assert(address.Value, gc.Equals, "1.2.3.4") 453 c.Assert(err, jc.ErrorIsNil) 454 455 result, err = s.uniter.PublicAddress(args) 456 c.Assert(err, jc.ErrorIsNil) 457 c.Assert(result, gc.DeepEquals, params.StringResults{ 458 Results: []params.StringResult{ 459 {Error: apiservertesting.ErrUnauthorized}, 460 {Result: "1.2.3.4"}, 461 {Error: apiservertesting.ErrUnauthorized}, 462 }, 463 }) 464 } 465 466 func (s *uniterSuite) TestPrivateAddress(c *gc.C) { 467 args := params.Entities{Entities: []params.Entity{ 468 {Tag: "unit-mysql-0"}, 469 {Tag: "unit-wordpress-0"}, 470 {Tag: "unit-foo-42"}, 471 }} 472 expectErr := ¶ms.Error{ 473 Code: params.CodeNoAddressSet, 474 Message: `"unit-wordpress-0" has no private address set`, 475 } 476 result, err := s.uniter.PrivateAddress(args) 477 c.Assert(err, jc.ErrorIsNil) 478 c.Assert(result, gc.DeepEquals, params.StringResults{ 479 Results: []params.StringResult{ 480 {Error: apiservertesting.ErrUnauthorized}, 481 {Error: expectErr}, 482 {Error: apiservertesting.ErrUnauthorized}, 483 }, 484 }) 485 486 // Now set it and try again. 487 err = s.machine0.SetProviderAddresses( 488 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 489 ) 490 c.Assert(err, jc.ErrorIsNil) 491 address, err := s.wordpressUnit.PrivateAddress() 492 c.Assert(address.Value, gc.Equals, "1.2.3.4") 493 c.Assert(err, jc.ErrorIsNil) 494 495 result, err = s.uniter.PrivateAddress(args) 496 c.Assert(err, jc.ErrorIsNil) 497 c.Assert(result, gc.DeepEquals, params.StringResults{ 498 Results: []params.StringResult{ 499 {Error: apiservertesting.ErrUnauthorized}, 500 {Result: "1.2.3.4"}, 501 {Error: apiservertesting.ErrUnauthorized}, 502 }, 503 }) 504 } 505 506 func (s *uniterSuite) TestAvailabilityZone(c *gc.C) { 507 s.PatchValue(uniter.GetZone, func(st *state.State, tag names.Tag) (string, error) { 508 return "a_zone", nil 509 }) 510 511 args := params.Entities{Entities: []params.Entity{ 512 {Tag: "unit-wordpress-0"}, 513 }} 514 result, err := s.uniter.AvailabilityZone(args) 515 c.Assert(err, jc.ErrorIsNil) 516 517 c.Check(result, gc.DeepEquals, params.StringResults{ 518 Results: []params.StringResult{ 519 {Result: "a_zone"}, 520 }, 521 }) 522 } 523 524 func (s *uniterSuite) TestResolved(c *gc.C) { 525 err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) 526 c.Assert(err, jc.ErrorIsNil) 527 mode := s.wordpressUnit.Resolved() 528 c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) 529 530 args := params.Entities{Entities: []params.Entity{ 531 {Tag: "unit-mysql-0"}, 532 {Tag: "unit-wordpress-0"}, 533 {Tag: "unit-foo-42"}, 534 }} 535 result, err := s.uniter.Resolved(args) 536 c.Assert(err, jc.ErrorIsNil) 537 c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{ 538 Results: []params.ResolvedModeResult{ 539 {Error: apiservertesting.ErrUnauthorized}, 540 {Mode: params.ResolvedMode(mode)}, 541 {Error: apiservertesting.ErrUnauthorized}, 542 }, 543 }) 544 } 545 546 func (s *uniterSuite) TestClearResolved(c *gc.C) { 547 err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) 548 c.Assert(err, jc.ErrorIsNil) 549 mode := s.wordpressUnit.Resolved() 550 c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) 551 552 args := params.Entities{Entities: []params.Entity{ 553 {Tag: "unit-mysql-0"}, 554 {Tag: "unit-wordpress-0"}, 555 {Tag: "unit-foo-42"}, 556 }} 557 result, err := s.uniter.ClearResolved(args) 558 c.Assert(err, jc.ErrorIsNil) 559 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 560 Results: []params.ErrorResult{ 561 {apiservertesting.ErrUnauthorized}, 562 {nil}, 563 {apiservertesting.ErrUnauthorized}, 564 }, 565 }) 566 567 // Verify wordpressUnit's resolved mode has changed. 568 err = s.wordpressUnit.Refresh() 569 c.Assert(err, jc.ErrorIsNil) 570 mode = s.wordpressUnit.Resolved() 571 c.Assert(mode, gc.Equals, state.ResolvedNone) 572 } 573 574 func (s *uniterSuite) TestGetPrincipal(c *gc.C) { 575 // Add a subordinate to wordpressUnit. 576 _, _, subordinate := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) 577 578 principal, ok := subordinate.PrincipalName() 579 c.Assert(principal, gc.Equals, s.wordpressUnit.Name()) 580 c.Assert(ok, jc.IsTrue) 581 582 // First try it as wordpressUnit's agent. 583 args := params.Entities{Entities: []params.Entity{ 584 {Tag: "unit-mysql-0"}, 585 {Tag: "unit-wordpress-0"}, 586 {Tag: subordinate.Tag().String()}, 587 {Tag: "unit-foo-42"}, 588 }} 589 result, err := s.uniter.GetPrincipal(args) 590 c.Assert(err, jc.ErrorIsNil) 591 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 592 Results: []params.StringBoolResult{ 593 {Error: apiservertesting.ErrUnauthorized}, 594 {Result: "", Ok: false, Error: nil}, 595 {Error: apiservertesting.ErrUnauthorized}, 596 {Error: apiservertesting.ErrUnauthorized}, 597 }, 598 }) 599 600 // Now try as subordinate's agent. 601 subAuthorizer := s.authorizer 602 subAuthorizer.Tag = subordinate.Tag() 603 subUniter, err := uniter.NewUniterAPIV4(s.State, s.resources, subAuthorizer) 604 c.Assert(err, jc.ErrorIsNil) 605 606 result, err = subUniter.GetPrincipal(args) 607 c.Assert(err, jc.ErrorIsNil) 608 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 609 Results: []params.StringBoolResult{ 610 {Error: apiservertesting.ErrUnauthorized}, 611 {Error: apiservertesting.ErrUnauthorized}, 612 {Result: "unit-wordpress-0", Ok: true, Error: nil}, 613 {Error: apiservertesting.ErrUnauthorized}, 614 }, 615 }) 616 } 617 618 func (s *uniterSuite) TestHasSubordinates(c *gc.C) { 619 // Try first without any subordinates for wordpressUnit. 620 args := params.Entities{Entities: []params.Entity{ 621 {Tag: "unit-mysql-0"}, 622 {Tag: "unit-wordpress-0"}, 623 {Tag: "unit-logging-0"}, 624 {Tag: "unit-foo-42"}, 625 }} 626 result, err := s.uniter.HasSubordinates(args) 627 c.Assert(err, jc.ErrorIsNil) 628 c.Assert(result, gc.DeepEquals, params.BoolResults{ 629 Results: []params.BoolResult{ 630 {Error: apiservertesting.ErrUnauthorized}, 631 {Result: false}, 632 {Error: apiservertesting.ErrUnauthorized}, 633 {Error: apiservertesting.ErrUnauthorized}, 634 }, 635 }) 636 637 // Add two subordinates to wordpressUnit and try again. 638 s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) 639 s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) 640 641 result, err = s.uniter.HasSubordinates(args) 642 c.Assert(err, jc.ErrorIsNil) 643 c.Assert(result, gc.DeepEquals, params.BoolResults{ 644 Results: []params.BoolResult{ 645 {Error: apiservertesting.ErrUnauthorized}, 646 {Result: true}, 647 {Error: apiservertesting.ErrUnauthorized}, 648 {Error: apiservertesting.ErrUnauthorized}, 649 }, 650 }) 651 } 652 653 func (s *uniterSuite) TestDestroy(c *gc.C) { 654 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 655 656 args := params.Entities{Entities: []params.Entity{ 657 {Tag: "unit-mysql-0"}, 658 {Tag: "unit-wordpress-0"}, 659 {Tag: "unit-foo-42"}, 660 }} 661 result, err := s.uniter.Destroy(args) 662 c.Assert(err, jc.ErrorIsNil) 663 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 664 Results: []params.ErrorResult{ 665 {apiservertesting.ErrUnauthorized}, 666 {nil}, 667 {apiservertesting.ErrUnauthorized}, 668 }, 669 }) 670 671 // Verify wordpressUnit is destroyed and removed. 672 err = s.wordpressUnit.Refresh() 673 c.Assert(err, jc.Satisfies, errors.IsNotFound) 674 } 675 676 func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) { 677 // Add two subordinates to wordpressUnit. 678 _, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) 679 _, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) 680 c.Assert(loggingSub.Life(), gc.Equals, state.Alive) 681 c.Assert(monitoringSub.Life(), gc.Equals, state.Alive) 682 683 err := s.wordpressUnit.Refresh() 684 c.Assert(err, jc.ErrorIsNil) 685 subordinates := s.wordpressUnit.SubordinateNames() 686 c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"}) 687 688 args := params.Entities{Entities: []params.Entity{ 689 {Tag: "unit-mysql-0"}, 690 {Tag: "unit-wordpress-0"}, 691 {Tag: "unit-foo-42"}, 692 }} 693 result, err := s.uniter.DestroyAllSubordinates(args) 694 c.Assert(err, jc.ErrorIsNil) 695 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 696 Results: []params.ErrorResult{ 697 {apiservertesting.ErrUnauthorized}, 698 {nil}, 699 {apiservertesting.ErrUnauthorized}, 700 }, 701 }) 702 703 // Verify wordpressUnit's subordinates were destroyed. 704 err = loggingSub.Refresh() 705 c.Assert(err, jc.ErrorIsNil) 706 c.Assert(loggingSub.Life(), gc.Equals, state.Dying) 707 err = monitoringSub.Refresh() 708 c.Assert(err, jc.ErrorIsNil) 709 c.Assert(monitoringSub.Life(), gc.Equals, state.Dying) 710 } 711 712 func (s *uniterSuite) TestCharmURL(c *gc.C) { 713 // Set wordpressUnit's charm URL first. 714 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 715 c.Assert(err, jc.ErrorIsNil) 716 curl, ok := s.wordpressUnit.CharmURL() 717 c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) 718 c.Assert(ok, jc.IsTrue) 719 720 // Make sure wordpress service's charm is what we expect. 721 curl, force := s.wordpress.CharmURL() 722 c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) 723 c.Assert(force, jc.IsFalse) 724 725 args := params.Entities{Entities: []params.Entity{ 726 {Tag: "unit-mysql-0"}, 727 {Tag: "unit-wordpress-0"}, 728 {Tag: "unit-foo-42"}, 729 {Tag: "application-mysql"}, 730 {Tag: "application-wordpress"}, 731 {Tag: "application-foo"}, 732 // TODO(dfc) these aren't valid tags any more 733 // but I hope to restore this test when params.Entity takes 734 // tags, not strings, which is coming soon. 735 // {Tag: "just-foo"}, 736 }} 737 result, err := s.uniter.CharmURL(args) 738 c.Assert(err, jc.ErrorIsNil) 739 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 740 Results: []params.StringBoolResult{ 741 {Error: apiservertesting.ErrUnauthorized}, 742 {Result: s.wpCharm.String(), Ok: ok}, 743 {Error: apiservertesting.ErrUnauthorized}, 744 {Error: apiservertesting.ErrUnauthorized}, 745 {Result: s.wpCharm.String(), Ok: force}, 746 {Error: apiservertesting.ErrUnauthorized}, 747 // see above 748 // {Error: apiservertesting.ErrUnauthorized}, 749 }, 750 }) 751 } 752 753 func (s *uniterSuite) TestSetCharmURL(c *gc.C) { 754 _, ok := s.wordpressUnit.CharmURL() 755 c.Assert(ok, jc.IsFalse) 756 757 args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{ 758 {Tag: "unit-mysql-0", CharmURL: "cs:quantal/application-42"}, 759 {Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()}, 760 {Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"}, 761 }} 762 result, err := s.uniter.SetCharmURL(args) 763 c.Assert(err, jc.ErrorIsNil) 764 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 765 Results: []params.ErrorResult{ 766 {apiservertesting.ErrUnauthorized}, 767 {nil}, 768 {apiservertesting.ErrUnauthorized}, 769 }, 770 }) 771 772 // Verify the charm URL was set. 773 err = s.wordpressUnit.Refresh() 774 c.Assert(err, jc.ErrorIsNil) 775 charmURL, needsUpgrade := s.wordpressUnit.CharmURL() 776 c.Assert(charmURL, gc.NotNil) 777 c.Assert(charmURL.String(), gc.Equals, s.wpCharm.String()) 778 c.Assert(needsUpgrade, jc.IsTrue) 779 } 780 781 func (s *uniterSuite) TestWorkloadVersion(c *gc.C) { 782 // Set wordpressUnit's workload version first. 783 err := s.wordpressUnit.SetWorkloadVersion("capulet") 784 c.Assert(err, jc.ErrorIsNil) 785 version, err := s.wordpressUnit.WorkloadVersion() 786 c.Assert(version, gc.Equals, "capulet") 787 c.Assert(err, jc.ErrorIsNil) 788 789 args := params.Entities{Entities: []params.Entity{ 790 {Tag: "unit-mysql-0"}, 791 {Tag: "unit-wordpress-0"}, 792 {Tag: "unit-foo-42"}, 793 {Tag: "application-wordpress"}, 794 {Tag: "just-foo"}, 795 }} 796 797 result, err := s.uniter.WorkloadVersion(args) 798 c.Assert(err, jc.ErrorIsNil) 799 c.Assert(result, gc.DeepEquals, params.StringResults{ 800 Results: []params.StringResult{ 801 {Error: apiservertesting.ErrUnauthorized}, 802 {Result: "capulet"}, 803 {Error: apiservertesting.ErrUnauthorized}, 804 {Error: common.ServerError(errors.New(`"application-wordpress" is not a valid unit tag`))}, 805 {Error: common.ServerError(errors.New(`"just-foo" is not a valid tag`))}, 806 }, 807 }) 808 } 809 810 func (s *uniterSuite) TestSetWorkloadVersion(c *gc.C) { 811 currentVersion, err := s.wordpressUnit.WorkloadVersion() 812 c.Assert(err, jc.ErrorIsNil) 813 c.Assert(currentVersion, gc.Equals, "") 814 815 args := params.EntityWorkloadVersions{Entities: []params.EntityWorkloadVersion{ 816 {Tag: "unit-mysql-0", WorkloadVersion: "allura"}, 817 {Tag: "unit-wordpress-0", WorkloadVersion: "shiro"}, 818 {Tag: "unit-foo-42", WorkloadVersion: "pidge"}, 819 }} 820 result, err := s.uniter.SetWorkloadVersion(args) 821 c.Assert(err, jc.ErrorIsNil) 822 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 823 Results: []params.ErrorResult{ 824 {apiservertesting.ErrUnauthorized}, 825 {nil}, 826 {apiservertesting.ErrUnauthorized}, 827 }, 828 }) 829 830 // Verify the workload version was set. 831 err = s.wordpressUnit.Refresh() 832 c.Assert(err, jc.ErrorIsNil) 833 newVersion, err := s.wordpressUnit.WorkloadVersion() 834 c.Assert(err, jc.ErrorIsNil) 835 c.Assert(newVersion, gc.Equals, "shiro") 836 } 837 838 func (s *uniterSuite) TestCharmModifiedVersion(c *gc.C) { 839 args := params.Entities{Entities: []params.Entity{ 840 {Tag: "application-mysql"}, 841 {Tag: "application-wordpress"}, 842 {Tag: "unit-wordpress-0"}, 843 {Tag: "application-foo"}, 844 }} 845 result, err := s.uniter.CharmModifiedVersion(args) 846 c.Assert(err, jc.ErrorIsNil) 847 c.Assert(result, gc.DeepEquals, params.IntResults{ 848 Results: []params.IntResult{ 849 {Error: apiservertesting.ErrUnauthorized}, 850 {Result: s.wordpress.CharmModifiedVersion()}, 851 {Result: s.wordpress.CharmModifiedVersion()}, 852 {Error: apiservertesting.ErrUnauthorized}, 853 }, 854 }) 855 } 856 857 func (s *uniterSuite) TestOpenPorts(c *gc.C) { 858 openedPorts, err := s.wordpressUnit.OpenedPorts() 859 c.Assert(err, jc.ErrorIsNil) 860 c.Assert(openedPorts, gc.HasLen, 0) 861 862 args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{ 863 {Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 864 {Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000}, 865 {Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42}, 866 }} 867 result, err := s.uniter.OpenPorts(args) 868 c.Assert(err, jc.ErrorIsNil) 869 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 870 Results: []params.ErrorResult{ 871 {apiservertesting.ErrUnauthorized}, 872 {nil}, 873 {apiservertesting.ErrUnauthorized}, 874 }, 875 }) 876 877 // Verify the wordpressUnit's port is opened. 878 openedPorts, err = s.wordpressUnit.OpenedPorts() 879 c.Assert(err, jc.ErrorIsNil) 880 c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{ 881 {Protocol: "udp", FromPort: 4321, ToPort: 5000}, 882 }) 883 } 884 885 func (s *uniterSuite) TestClosePorts(c *gc.C) { 886 // Open port udp:4321 in advance on wordpressUnit. 887 err := s.wordpressUnit.OpenPorts("udp", 4321, 5000) 888 c.Assert(err, jc.ErrorIsNil) 889 openedPorts, err := s.wordpressUnit.OpenedPorts() 890 c.Assert(err, jc.ErrorIsNil) 891 c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{ 892 {Protocol: "udp", FromPort: 4321, ToPort: 5000}, 893 }) 894 895 args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{ 896 {Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 897 {Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000}, 898 {Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42}, 899 }} 900 result, err := s.uniter.ClosePorts(args) 901 c.Assert(err, jc.ErrorIsNil) 902 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 903 Results: []params.ErrorResult{ 904 {apiservertesting.ErrUnauthorized}, 905 {nil}, 906 {apiservertesting.ErrUnauthorized}, 907 }, 908 }) 909 910 // Verify the wordpressUnit's port is closed. 911 openedPorts, err = s.wordpressUnit.OpenedPorts() 912 c.Assert(err, jc.ErrorIsNil) 913 c.Assert(openedPorts, gc.HasLen, 0) 914 } 915 916 func (s *uniterSuite) TestWatchConfigSettings(c *gc.C) { 917 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 918 c.Assert(err, jc.ErrorIsNil) 919 920 c.Assert(s.resources.Count(), gc.Equals, 0) 921 922 args := params.Entities{Entities: []params.Entity{ 923 {Tag: "unit-mysql-0"}, 924 {Tag: "unit-wordpress-0"}, 925 {Tag: "unit-foo-42"}, 926 }} 927 result, err := s.uniter.WatchConfigSettings(args) 928 c.Assert(err, jc.ErrorIsNil) 929 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 930 Results: []params.NotifyWatchResult{ 931 {Error: apiservertesting.ErrUnauthorized}, 932 {NotifyWatcherId: "1"}, 933 {Error: apiservertesting.ErrUnauthorized}, 934 }, 935 }) 936 937 // Verify the resource was registered and stop when done 938 c.Assert(s.resources.Count(), gc.Equals, 1) 939 resource := s.resources.Get("1") 940 defer statetesting.AssertStop(c, resource) 941 942 // Check that the Watch has consumed the initial event ("returned" in 943 // the Watch call) 944 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 945 wc.AssertNoChange() 946 } 947 948 func (s *uniterSuite) TestWatchActionNotifications(c *gc.C) { 949 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 950 c.Assert(err, jc.ErrorIsNil) 951 952 c.Assert(s.resources.Count(), gc.Equals, 0) 953 954 args := params.Entities{Entities: []params.Entity{ 955 {Tag: "unit-mysql-0"}, 956 {Tag: "unit-wordpress-0"}, 957 {Tag: "unit-foo-42"}, 958 }} 959 result, err := s.uniter.WatchActionNotifications(args) 960 c.Assert(err, jc.ErrorIsNil) 961 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 962 Results: []params.StringsWatchResult{ 963 {Error: apiservertesting.ErrUnauthorized}, 964 {StringsWatcherId: "1"}, 965 {Error: apiservertesting.ErrUnauthorized}, 966 }, 967 }) 968 969 // Verify the resource was registered and stop when done 970 c.Assert(s.resources.Count(), gc.Equals, 1) 971 resource := s.resources.Get("1") 972 defer statetesting.AssertStop(c, resource) 973 974 // Check that the Watch has consumed the initial event ("returned" in 975 // the Watch call) 976 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 977 wc.AssertNoChange() 978 979 addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil) 980 981 wc.AssertChange(addedAction.Id()) 982 wc.AssertNoChange() 983 } 984 985 func (s *uniterSuite) TestWatchPreexistingActions(c *gc.C) { 986 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 987 c.Assert(err, jc.ErrorIsNil) 988 989 c.Assert(s.resources.Count(), gc.Equals, 0) 990 991 action1, err := s.wordpressUnit.AddAction("fakeaction", nil) 992 c.Assert(err, jc.ErrorIsNil) 993 action2, err := s.wordpressUnit.AddAction("fakeaction", nil) 994 c.Assert(err, jc.ErrorIsNil) 995 996 args := params.Entities{Entities: []params.Entity{ 997 {Tag: "unit-wordpress-0"}, 998 }} 999 1000 s.State.StartSync() 1001 results, err := s.uniter.WatchActionNotifications(args) 1002 c.Assert(err, jc.ErrorIsNil) 1003 1004 checkUnorderedActionIdsEqual(c, []string{action1.Id(), action2.Id()}, results) 1005 1006 // Verify the resource was registered and stop when done 1007 c.Assert(s.resources.Count(), gc.Equals, 1) 1008 resource := s.resources.Get("1") 1009 defer statetesting.AssertStop(c, resource) 1010 1011 // Check that the Watch has consumed the initial event ("returned" in 1012 // the Watch call) 1013 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1014 wc.AssertNoChange() 1015 1016 addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil) 1017 c.Assert(err, jc.ErrorIsNil) 1018 wc.AssertChange(addedAction.Id()) 1019 wc.AssertNoChange() 1020 } 1021 1022 func (s *uniterSuite) TestWatchActionNotificationsMalformedTag(c *gc.C) { 1023 args := params.Entities{Entities: []params.Entity{ 1024 {Tag: "ewenit-mysql-0"}, 1025 }} 1026 results, err := s.uniter.WatchActionNotifications(args) 1027 c.Assert(err, jc.ErrorIsNil) 1028 c.Assert(results, gc.NotNil) 1029 c.Assert(len(results.Results), gc.Equals, 1) 1030 result := results.Results[0] 1031 c.Assert(result.Error, gc.NotNil) 1032 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "ewenit-mysql-0"`) 1033 } 1034 1035 func (s *uniterSuite) TestWatchActionNotificationsMalformedUnitName(c *gc.C) { 1036 args := params.Entities{Entities: []params.Entity{ 1037 {Tag: "unit-mysql-01"}, 1038 }} 1039 results, err := s.uniter.WatchActionNotifications(args) 1040 c.Assert(err, jc.ErrorIsNil) 1041 c.Assert(results, gc.NotNil) 1042 c.Assert(len(results.Results), gc.Equals, 1) 1043 result := results.Results[0] 1044 c.Assert(result.Error, gc.NotNil) 1045 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "unit-mysql-01"`) 1046 } 1047 1048 func (s *uniterSuite) TestWatchActionNotificationsNotUnit(c *gc.C) { 1049 action, err := s.mysqlUnit.AddAction("fakeaction", nil) 1050 c.Assert(err, jc.ErrorIsNil) 1051 args := params.Entities{Entities: []params.Entity{ 1052 {Tag: action.Tag().String()}, 1053 }} 1054 results, err := s.uniter.WatchActionNotifications(args) 1055 c.Assert(err, jc.ErrorIsNil) 1056 c.Assert(results, gc.NotNil) 1057 c.Assert(len(results.Results), gc.Equals, 1) 1058 result := results.Results[0] 1059 c.Assert(result.Error, gc.NotNil) 1060 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "action-`+action.Id()+`"`) 1061 } 1062 1063 func (s *uniterSuite) TestWatchActionNotificationsPermissionDenied(c *gc.C) { 1064 args := params.Entities{Entities: []params.Entity{ 1065 {Tag: "unit-nonexistentgarbage-0"}, 1066 }} 1067 results, err := s.uniter.WatchActionNotifications(args) 1068 c.Assert(err, jc.ErrorIsNil) 1069 c.Assert(results, gc.NotNil) 1070 c.Assert(len(results.Results), gc.Equals, 1) 1071 result := results.Results[0] 1072 c.Assert(result.Error, gc.NotNil) 1073 c.Assert(result.Error.Message, gc.Equals, "permission denied") 1074 } 1075 1076 func (s *uniterSuite) TestConfigSettings(c *gc.C) { 1077 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 1078 c.Assert(err, jc.ErrorIsNil) 1079 settings, err := s.wordpressUnit.ConfigSettings() 1080 c.Assert(err, jc.ErrorIsNil) 1081 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 1082 1083 args := params.Entities{Entities: []params.Entity{ 1084 {Tag: "unit-mysql-0"}, 1085 {Tag: "unit-wordpress-0"}, 1086 {Tag: "unit-foo-42"}, 1087 }} 1088 result, err := s.uniter.ConfigSettings(args) 1089 c.Assert(err, jc.ErrorIsNil) 1090 c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{ 1091 Results: []params.ConfigSettingsResult{ 1092 {Error: apiservertesting.ErrUnauthorized}, 1093 {Settings: params.ConfigSettings{"blog-title": "My Title"}}, 1094 {Error: apiservertesting.ErrUnauthorized}, 1095 }, 1096 }) 1097 } 1098 1099 func (s *uniterSuite) TestWatchApplicationRelations(c *gc.C) { 1100 c.Assert(s.resources.Count(), gc.Equals, 0) 1101 1102 args := params.Entities{Entities: []params.Entity{ 1103 {Tag: "application-mysql"}, 1104 {Tag: "application-wordpress"}, 1105 {Tag: "application-foo"}, 1106 }} 1107 result, err := s.uniter.WatchApplicationRelations(args) 1108 s.assertOneStringsWatcher(c, result, err) 1109 } 1110 1111 func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) { 1112 dummyCharm := s.AddTestingCharm(c, "dummy") 1113 1114 args := params.CharmURLs{URLs: []params.CharmURL{ 1115 {URL: "something-invalid"}, 1116 {URL: s.wpCharm.String()}, 1117 {URL: dummyCharm.String()}, 1118 }} 1119 result, err := s.uniter.CharmArchiveSha256(args) 1120 c.Assert(err, jc.ErrorIsNil) 1121 c.Assert(result, gc.DeepEquals, params.StringResults{ 1122 Results: []params.StringResult{ 1123 {Error: apiservertesting.ErrUnauthorized}, 1124 {Result: s.wpCharm.BundleSha256()}, 1125 {Result: dummyCharm.BundleSha256()}, 1126 }, 1127 }) 1128 } 1129 1130 func (s *uniterSuite) TestCurrentModel(c *gc.C) { 1131 env, err := s.State.Model() 1132 c.Assert(err, jc.ErrorIsNil) 1133 1134 result, err := s.uniter.CurrentModel() 1135 c.Assert(err, jc.ErrorIsNil) 1136 expected := params.ModelResult{ 1137 Name: env.Name(), 1138 UUID: env.UUID(), 1139 } 1140 c.Assert(result, gc.DeepEquals, expected) 1141 } 1142 1143 func (s *uniterSuite) TestActions(c *gc.C) { 1144 var actionTests = []struct { 1145 description string 1146 action params.ActionResult 1147 }{{ 1148 description: "A simple action.", 1149 action: params.ActionResult{ 1150 Action: ¶ms.Action{ 1151 Name: "fakeaction", 1152 Parameters: map[string]interface{}{ 1153 "outfile": "foo.txt", 1154 }}, 1155 }, 1156 }, { 1157 description: "An action with nested parameters.", 1158 action: params.ActionResult{ 1159 Action: ¶ms.Action{ 1160 Name: "fakeaction", 1161 Parameters: map[string]interface{}{ 1162 "outfile": "foo.bz2", 1163 "compression": map[string]interface{}{ 1164 "kind": "bzip", 1165 "quality": 5, 1166 }, 1167 }}, 1168 }, 1169 }} 1170 1171 for i, actionTest := range actionTests { 1172 c.Logf("test %d: %s", i, actionTest.description) 1173 1174 a, err := s.wordpressUnit.AddAction( 1175 actionTest.action.Action.Name, 1176 actionTest.action.Action.Parameters) 1177 c.Assert(err, jc.ErrorIsNil) 1178 c.Assert(names.IsValidAction(a.Id()), gc.Equals, true) 1179 actionTag := names.NewActionTag(a.Id()) 1180 c.Assert(a.ActionTag(), gc.Equals, actionTag) 1181 1182 args := params.Entities{ 1183 Entities: []params.Entity{{ 1184 Tag: actionTag.String(), 1185 }}, 1186 } 1187 results, err := s.uniter.Actions(args) 1188 c.Assert(err, jc.ErrorIsNil) 1189 c.Assert(results.Results, gc.HasLen, 1) 1190 1191 actionsQueryResult := results.Results[0] 1192 1193 c.Assert(actionsQueryResult, jc.DeepEquals, actionTest.action) 1194 } 1195 } 1196 1197 func (s *uniterSuite) TestActionsNotPresent(c *gc.C) { 1198 uuid, err := utils.NewUUID() 1199 c.Assert(err, jc.ErrorIsNil) 1200 args := params.Entities{ 1201 Entities: []params.Entity{{ 1202 Tag: names.NewActionTag(uuid.String()).String(), 1203 }}, 1204 } 1205 results, err := s.uniter.Actions(args) 1206 c.Assert(err, jc.ErrorIsNil) 1207 1208 c.Assert(results.Results, gc.HasLen, 1) 1209 actionsQueryResult := results.Results[0] 1210 c.Assert(actionsQueryResult.Error, gc.NotNil) 1211 c.Assert(actionsQueryResult.Error, gc.ErrorMatches, `action "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}" not found`) 1212 } 1213 1214 func (s *uniterSuite) TestActionsWrongUnit(c *gc.C) { 1215 // Action doesn't match unit. 1216 mysqlUnitAuthorizer := apiservertesting.FakeAuthorizer{ 1217 Tag: s.mysqlUnit.Tag(), 1218 } 1219 mysqlUnitFacade, err := uniter.NewUniterAPIV4(s.State, s.resources, mysqlUnitAuthorizer) 1220 c.Assert(err, jc.ErrorIsNil) 1221 1222 action, err := s.wordpressUnit.AddAction("fakeaction", nil) 1223 c.Assert(err, jc.ErrorIsNil) 1224 args := params.Entities{ 1225 Entities: []params.Entity{{ 1226 Tag: action.Tag().String(), 1227 }}, 1228 } 1229 actions, err := mysqlUnitFacade.Actions(args) 1230 c.Assert(err, jc.ErrorIsNil) 1231 c.Assert(len(actions.Results), gc.Equals, 1) 1232 c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized) 1233 } 1234 1235 func (s *uniterSuite) TestActionsPermissionDenied(c *gc.C) { 1236 action, err := s.mysqlUnit.AddAction("fakeaction", nil) 1237 c.Assert(err, jc.ErrorIsNil) 1238 args := params.Entities{ 1239 Entities: []params.Entity{{ 1240 Tag: action.Tag().String(), 1241 }}, 1242 } 1243 actions, err := s.uniter.Actions(args) 1244 c.Assert(err, jc.ErrorIsNil) 1245 c.Assert(len(actions.Results), gc.Equals, 1) 1246 c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized) 1247 } 1248 1249 func (s *uniterSuite) TestFinishActionsSuccess(c *gc.C) { 1250 testName := "fakeaction" 1251 testOutput := map[string]interface{}{"output": "completed fakeaction successfully"} 1252 1253 results, err := s.wordpressUnit.CompletedActions() 1254 c.Assert(err, jc.ErrorIsNil) 1255 c.Assert(results, gc.DeepEquals, ([]state.Action)(nil)) 1256 1257 action, err := s.wordpressUnit.AddAction(testName, nil) 1258 c.Assert(err, jc.ErrorIsNil) 1259 1260 actionResults := params.ActionExecutionResults{ 1261 Results: []params.ActionExecutionResult{{ 1262 ActionTag: action.ActionTag().String(), 1263 Status: params.ActionCompleted, 1264 Results: testOutput, 1265 }}, 1266 } 1267 res, err := s.uniter.FinishActions(actionResults) 1268 c.Assert(err, jc.ErrorIsNil) 1269 c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}}) 1270 1271 results, err = s.wordpressUnit.CompletedActions() 1272 c.Assert(err, jc.ErrorIsNil) 1273 c.Assert(len(results), gc.Equals, 1) 1274 c.Assert(results[0].Status(), gc.Equals, state.ActionCompleted) 1275 res2, errstr := results[0].Results() 1276 c.Assert(errstr, gc.Equals, "") 1277 c.Assert(res2, gc.DeepEquals, testOutput) 1278 c.Assert(results[0].Name(), gc.Equals, testName) 1279 } 1280 1281 func (s *uniterSuite) TestFinishActionsFailure(c *gc.C) { 1282 testName := "fakeaction" 1283 testError := "fakeaction was a dismal failure" 1284 1285 results, err := s.wordpressUnit.CompletedActions() 1286 c.Assert(err, jc.ErrorIsNil) 1287 c.Assert(results, gc.DeepEquals, ([]state.Action)(nil)) 1288 1289 action, err := s.wordpressUnit.AddAction(testName, nil) 1290 c.Assert(err, jc.ErrorIsNil) 1291 1292 actionResults := params.ActionExecutionResults{ 1293 Results: []params.ActionExecutionResult{{ 1294 ActionTag: action.ActionTag().String(), 1295 Status: params.ActionFailed, 1296 Results: nil, 1297 Message: testError, 1298 }}, 1299 } 1300 res, err := s.uniter.FinishActions(actionResults) 1301 c.Assert(err, jc.ErrorIsNil) 1302 c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}}) 1303 1304 results, err = s.wordpressUnit.CompletedActions() 1305 c.Assert(err, jc.ErrorIsNil) 1306 c.Assert(len(results), gc.Equals, 1) 1307 c.Assert(results[0].Status(), gc.Equals, state.ActionFailed) 1308 res2, errstr := results[0].Results() 1309 c.Assert(errstr, gc.Equals, testError) 1310 c.Assert(res2, gc.DeepEquals, map[string]interface{}{}) 1311 c.Assert(results[0].Name(), gc.Equals, testName) 1312 } 1313 1314 func (s *uniterSuite) TestFinishActionsAuthAccess(c *gc.C) { 1315 good, err := s.wordpressUnit.AddAction("fakeaction", nil) 1316 c.Assert(err, jc.ErrorIsNil) 1317 1318 bad, err := s.mysqlUnit.AddAction("fakeaction", nil) 1319 c.Assert(err, jc.ErrorIsNil) 1320 1321 var tests = []struct { 1322 actionTag names.ActionTag 1323 err error 1324 }{ 1325 {actionTag: good.ActionTag(), err: nil}, 1326 {actionTag: bad.ActionTag(), err: common.ErrPerm}, 1327 } 1328 1329 // Queue up actions from tests 1330 actionResults := params.ActionExecutionResults{Results: make([]params.ActionExecutionResult, len(tests))} 1331 for i, test := range tests { 1332 actionResults.Results[i] = params.ActionExecutionResult{ 1333 ActionTag: test.actionTag.String(), 1334 Status: params.ActionCompleted, 1335 Results: map[string]interface{}{}, 1336 } 1337 } 1338 1339 // Invoke FinishActions 1340 res, err := s.uniter.FinishActions(actionResults) 1341 c.Assert(err, jc.ErrorIsNil) 1342 1343 // Verify permissions errors for actions queued on different unit 1344 for i, result := range res.Results { 1345 expected := tests[i].err 1346 if expected != nil { 1347 c.Assert(result.Error, gc.NotNil) 1348 c.Assert(result.Error.Error(), gc.Equals, expected.Error()) 1349 } else { 1350 c.Assert(result.Error, gc.IsNil) 1351 } 1352 } 1353 } 1354 1355 func (s *uniterSuite) TestBeginActions(c *gc.C) { 1356 ten_seconds_ago := time.Now().Add(-10 * time.Second) 1357 good, err := s.wordpressUnit.AddAction("fakeaction", nil) 1358 c.Assert(err, jc.ErrorIsNil) 1359 1360 running, err := s.wordpressUnit.RunningActions() 1361 c.Assert(err, jc.ErrorIsNil) 1362 c.Assert(len(running), gc.Equals, 0, gc.Commentf("expected no running actions, got %d", len(running))) 1363 1364 args := params.Entities{Entities: []params.Entity{{Tag: good.ActionTag().String()}}} 1365 res, err := s.uniter.BeginActions(args) 1366 c.Assert(err, jc.ErrorIsNil) 1367 c.Assert(len(res.Results), gc.Equals, 1) 1368 c.Assert(res.Results[0].Error, gc.IsNil) 1369 1370 running, err = s.wordpressUnit.RunningActions() 1371 c.Assert(err, jc.ErrorIsNil) 1372 c.Assert(len(running), gc.Equals, 1, gc.Commentf("expected one running action, got %d", len(running))) 1373 c.Assert(running[0].ActionTag(), gc.Equals, good.ActionTag()) 1374 enqueued, started := running[0].Enqueued(), running[0].Started() 1375 c.Assert(ten_seconds_ago.Before(enqueued), jc.IsTrue, gc.Commentf("enqueued time should be after 10 seconds ago")) 1376 c.Assert(ten_seconds_ago.Before(started), jc.IsTrue, gc.Commentf("started time should be after 10 seconds ago")) 1377 c.Assert(started.After(enqueued) || started.Equal(enqueued), jc.IsTrue, gc.Commentf("started should be after or equal to enqueued time")) 1378 } 1379 1380 func (s *uniterSuite) TestRelation(c *gc.C) { 1381 rel := s.addRelation(c, "wordpress", "mysql") 1382 wpEp, err := rel.Endpoint("wordpress") 1383 c.Assert(err, jc.ErrorIsNil) 1384 1385 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1386 {Relation: "relation-42", Unit: "unit-foo-0"}, 1387 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1388 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1389 {Relation: rel.Tag().String(), Unit: "unit-foo-0"}, 1390 {Relation: "relation-blah", Unit: "unit-wordpress-0"}, 1391 {Relation: "application-foo", Unit: "user-foo"}, 1392 {Relation: "foo", Unit: "bar"}, 1393 {Relation: "unit-wordpress-0", Unit: rel.Tag().String()}, 1394 }} 1395 result, err := s.uniter.Relation(args) 1396 c.Assert(err, jc.ErrorIsNil) 1397 c.Assert(result, gc.DeepEquals, params.RelationResults{ 1398 Results: []params.RelationResult{ 1399 {Error: apiservertesting.ErrUnauthorized}, 1400 { 1401 Id: rel.Id(), 1402 Key: rel.String(), 1403 Life: params.Life(rel.Life().String()), 1404 Endpoint: multiwatcher.Endpoint{ 1405 ApplicationName: wpEp.ApplicationName, 1406 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 1407 }, 1408 }, 1409 {Error: apiservertesting.ErrUnauthorized}, 1410 {Error: apiservertesting.ErrUnauthorized}, 1411 {Error: apiservertesting.ErrUnauthorized}, 1412 {Error: apiservertesting.ErrUnauthorized}, 1413 {Error: apiservertesting.ErrUnauthorized}, 1414 {Error: apiservertesting.ErrUnauthorized}, 1415 }, 1416 }) 1417 } 1418 1419 func (s *uniterSuite) TestRelationById(c *gc.C) { 1420 rel := s.addRelation(c, "wordpress", "mysql") 1421 c.Assert(rel.Id(), gc.Equals, 0) 1422 wpEp, err := rel.Endpoint("wordpress") 1423 c.Assert(err, jc.ErrorIsNil) 1424 1425 // Add another relation to mysql service, so we can see we can't 1426 // get it. 1427 otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit) 1428 1429 args := params.RelationIds{ 1430 RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234}, 1431 } 1432 result, err := s.uniter.RelationById(args) 1433 c.Assert(err, jc.ErrorIsNil) 1434 c.Assert(result, gc.DeepEquals, params.RelationResults{ 1435 Results: []params.RelationResult{ 1436 {Error: apiservertesting.ErrUnauthorized}, 1437 { 1438 Id: rel.Id(), 1439 Key: rel.String(), 1440 Life: params.Life(rel.Life().String()), 1441 Endpoint: multiwatcher.Endpoint{ 1442 ApplicationName: wpEp.ApplicationName, 1443 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 1444 }, 1445 }, 1446 {Error: apiservertesting.ErrUnauthorized}, 1447 {Error: apiservertesting.ErrUnauthorized}, 1448 {Error: apiservertesting.ErrUnauthorized}, 1449 }, 1450 }) 1451 } 1452 1453 func (s *uniterSuite) TestProviderType(c *gc.C) { 1454 cfg, err := s.State.ModelConfig() 1455 c.Assert(err, jc.ErrorIsNil) 1456 1457 result, err := s.uniter.ProviderType() 1458 c.Assert(err, jc.ErrorIsNil) 1459 c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()}) 1460 } 1461 1462 func (s *uniterSuite) TestEnterScope(c *gc.C) { 1463 // Set wordpressUnit's private address first. 1464 err := s.machine0.SetProviderAddresses( 1465 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 1466 ) 1467 c.Assert(err, jc.ErrorIsNil) 1468 1469 rel := s.addRelation(c, "wordpress", "mysql") 1470 relUnit, err := rel.Unit(s.wordpressUnit) 1471 c.Assert(err, jc.ErrorIsNil) 1472 s.assertInScope(c, relUnit, false) 1473 1474 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1475 {Relation: "relation-42", Unit: "unit-foo-0"}, 1476 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1477 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1478 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 1479 {Relation: "relation-foo", Unit: "unit-wordpress-0"}, 1480 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 1481 {Relation: "foo", Unit: "bar"}, 1482 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1483 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 1484 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 1485 {Relation: rel.Tag().String(), Unit: "user-foo"}, 1486 }} 1487 result, err := s.uniter.EnterScope(args) 1488 c.Assert(err, jc.ErrorIsNil) 1489 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1490 Results: []params.ErrorResult{ 1491 {apiservertesting.ErrUnauthorized}, 1492 {nil}, 1493 {nil}, 1494 {apiservertesting.ErrUnauthorized}, 1495 {apiservertesting.ErrUnauthorized}, 1496 {apiservertesting.ErrUnauthorized}, 1497 {apiservertesting.ErrUnauthorized}, 1498 {apiservertesting.ErrUnauthorized}, 1499 {apiservertesting.ErrUnauthorized}, 1500 {apiservertesting.ErrUnauthorized}, 1501 {apiservertesting.ErrUnauthorized}, 1502 }, 1503 }) 1504 1505 // Verify the scope changes and settings. 1506 s.assertInScope(c, relUnit, true) 1507 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 1508 c.Assert(err, jc.ErrorIsNil) 1509 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 1510 "private-address": "1.2.3.4", 1511 }) 1512 } 1513 1514 func (s *uniterSuite) TestLeaveScope(c *gc.C) { 1515 rel := s.addRelation(c, "wordpress", "mysql") 1516 relUnit, err := rel.Unit(s.wordpressUnit) 1517 c.Assert(err, jc.ErrorIsNil) 1518 settings := map[string]interface{}{ 1519 "some": "settings", 1520 } 1521 err = relUnit.EnterScope(settings) 1522 c.Assert(err, jc.ErrorIsNil) 1523 s.assertInScope(c, relUnit, true) 1524 1525 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1526 {Relation: "relation-42", Unit: "unit-foo-0"}, 1527 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1528 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1529 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 1530 {Relation: "relation-foo", Unit: "unit-wordpress-0"}, 1531 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 1532 {Relation: "foo", Unit: "bar"}, 1533 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1534 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 1535 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 1536 {Relation: rel.Tag().String(), Unit: "user-foo"}, 1537 }} 1538 result, err := s.uniter.LeaveScope(args) 1539 c.Assert(err, jc.ErrorIsNil) 1540 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1541 Results: []params.ErrorResult{ 1542 {apiservertesting.ErrUnauthorized}, 1543 {nil}, 1544 {nil}, 1545 {apiservertesting.ErrUnauthorized}, 1546 {apiservertesting.ErrUnauthorized}, 1547 {apiservertesting.ErrUnauthorized}, 1548 {apiservertesting.ErrUnauthorized}, 1549 {apiservertesting.ErrUnauthorized}, 1550 {apiservertesting.ErrUnauthorized}, 1551 {apiservertesting.ErrUnauthorized}, 1552 {apiservertesting.ErrUnauthorized}, 1553 }, 1554 }) 1555 1556 // Verify the scope changes. 1557 s.assertInScope(c, relUnit, false) 1558 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 1559 c.Assert(err, jc.ErrorIsNil) 1560 c.Assert(readSettings, gc.DeepEquals, settings) 1561 } 1562 1563 func (s *uniterSuite) TestJoinedRelations(c *gc.C) { 1564 rel := s.addRelation(c, "wordpress", "mysql") 1565 relUnit, err := rel.Unit(s.wordpressUnit) 1566 c.Assert(err, jc.ErrorIsNil) 1567 err = relUnit.EnterScope(nil) 1568 c.Assert(err, jc.ErrorIsNil) 1569 1570 args := params.Entities{ 1571 Entities: []params.Entity{ 1572 {s.wordpressUnit.Tag().String()}, 1573 {s.mysqlUnit.Tag().String()}, 1574 {"unit-unknown-1"}, 1575 {"application-wordpress"}, 1576 {"machine-0"}, 1577 {rel.Tag().String()}, 1578 }, 1579 } 1580 expect := params.StringsResults{ 1581 Results: []params.StringsResult{ 1582 {Result: []string{rel.Tag().String()}}, 1583 {Error: apiservertesting.ErrUnauthorized}, 1584 {Error: apiservertesting.ErrUnauthorized}, 1585 {Error: apiservertesting.ErrUnauthorized}, 1586 {Error: apiservertesting.ErrUnauthorized}, 1587 {Error: apiservertesting.ErrUnauthorized}, 1588 }, 1589 } 1590 check := func() { 1591 result, err := s.uniter.JoinedRelations(args) 1592 c.Assert(err, jc.ErrorIsNil) 1593 c.Assert(result, gc.DeepEquals, expect) 1594 } 1595 check() 1596 err = relUnit.PrepareLeaveScope() 1597 c.Assert(err, jc.ErrorIsNil) 1598 check() 1599 } 1600 1601 func (s *uniterSuite) TestReadSettings(c *gc.C) { 1602 rel := s.addRelation(c, "wordpress", "mysql") 1603 relUnit, err := rel.Unit(s.wordpressUnit) 1604 c.Assert(err, jc.ErrorIsNil) 1605 settings := map[string]interface{}{ 1606 "some": "settings", 1607 } 1608 err = relUnit.EnterScope(settings) 1609 c.Assert(err, jc.ErrorIsNil) 1610 s.assertInScope(c, relUnit, true) 1611 1612 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1613 {Relation: "relation-42", Unit: "unit-foo-0"}, 1614 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1615 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1616 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 1617 {Relation: "relation-foo", Unit: ""}, 1618 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 1619 {Relation: "foo", Unit: "bar"}, 1620 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1621 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 1622 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 1623 {Relation: rel.Tag().String(), Unit: "user-foo"}, 1624 }} 1625 result, err := s.uniter.ReadSettings(args) 1626 c.Assert(err, jc.ErrorIsNil) 1627 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 1628 Results: []params.SettingsResult{ 1629 {Error: apiservertesting.ErrUnauthorized}, 1630 {Settings: params.Settings{ 1631 "some": "settings", 1632 }}, 1633 {Error: apiservertesting.ErrUnauthorized}, 1634 {Error: apiservertesting.ErrUnauthorized}, 1635 {Error: apiservertesting.ErrUnauthorized}, 1636 {Error: apiservertesting.ErrUnauthorized}, 1637 {Error: apiservertesting.ErrUnauthorized}, 1638 {Error: apiservertesting.ErrUnauthorized}, 1639 {Error: apiservertesting.ErrUnauthorized}, 1640 {Error: apiservertesting.ErrUnauthorized}, 1641 {Error: apiservertesting.ErrUnauthorized}, 1642 }, 1643 }) 1644 } 1645 1646 func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) { 1647 rel := s.addRelation(c, "wordpress", "mysql") 1648 relUnit, err := rel.Unit(s.wordpressUnit) 1649 c.Assert(err, jc.ErrorIsNil) 1650 settings := map[string]interface{}{ 1651 "other": "things", 1652 "invalid-bool": false, 1653 } 1654 err = relUnit.EnterScope(settings) 1655 c.Assert(err, jc.ErrorIsNil) 1656 s.assertInScope(c, relUnit, true) 1657 1658 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1659 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1660 }} 1661 expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` 1662 result, err := s.uniter.ReadSettings(args) 1663 c.Assert(err, jc.ErrorIsNil) 1664 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 1665 Results: []params.SettingsResult{ 1666 {Error: ¶ms.Error{Message: expectErr}}, 1667 }, 1668 }) 1669 } 1670 1671 func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) { 1672 rel := s.addRelation(c, "wordpress", "mysql") 1673 relUnit, err := rel.Unit(s.wordpressUnit) 1674 c.Assert(err, jc.ErrorIsNil) 1675 settings := map[string]interface{}{ 1676 "some": "settings", 1677 } 1678 err = relUnit.EnterScope(settings) 1679 c.Assert(err, jc.ErrorIsNil) 1680 s.assertInScope(c, relUnit, true) 1681 1682 // First test most of the invalid args tests and try to read the 1683 // (unset) remote unit settings. 1684 args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{ 1685 {Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"}, 1686 {Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"}, 1687 {Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"}, 1688 {Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""}, 1689 {Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""}, 1690 {Relation: "application-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-foo"}, 1691 {Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"}, 1692 {Relation: rel.Tag().String(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"}, 1693 {Relation: rel.Tag().String(), LocalUnit: "application-wordpress", RemoteUnit: "application-mysql"}, 1694 {Relation: rel.Tag().String(), LocalUnit: "application-mysql", RemoteUnit: "foo"}, 1695 {Relation: rel.Tag().String(), LocalUnit: "user-foo", RemoteUnit: "unit-wordpress-0"}, 1696 }} 1697 result, err := s.uniter.ReadRemoteSettings(args) 1698 1699 // We don't set the remote unit settings on purpose to test the error. 1700 expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings` 1701 c.Assert(err, jc.ErrorIsNil) 1702 c.Assert(result, jc.DeepEquals, params.SettingsResults{ 1703 Results: []params.SettingsResult{ 1704 {Error: apiservertesting.ErrUnauthorized}, 1705 {Error: apiservertesting.ErrUnauthorized}, 1706 {Error: apiservertesting.NotFoundError(expectErr)}, 1707 {Error: apiservertesting.ErrUnauthorized}, 1708 {Error: apiservertesting.ErrUnauthorized}, 1709 {Error: apiservertesting.ErrUnauthorized}, 1710 {Error: apiservertesting.ErrUnauthorized}, 1711 {Error: apiservertesting.ErrUnauthorized}, 1712 {Error: apiservertesting.ErrUnauthorized}, 1713 {Error: apiservertesting.ErrUnauthorized}, 1714 {Error: apiservertesting.ErrUnauthorized}, 1715 }, 1716 }) 1717 1718 // Now leave the mysqlUnit and re-enter with new settings. 1719 relUnit, err = rel.Unit(s.mysqlUnit) 1720 c.Assert(err, jc.ErrorIsNil) 1721 settings = map[string]interface{}{ 1722 "other": "things", 1723 } 1724 err = relUnit.LeaveScope() 1725 c.Assert(err, jc.ErrorIsNil) 1726 s.assertInScope(c, relUnit, false) 1727 err = relUnit.EnterScope(settings) 1728 c.Assert(err, jc.ErrorIsNil) 1729 s.assertInScope(c, relUnit, true) 1730 1731 // Test the remote unit settings can be read. 1732 args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ 1733 Relation: rel.Tag().String(), 1734 LocalUnit: "unit-wordpress-0", 1735 RemoteUnit: "unit-mysql-0", 1736 }}} 1737 expect := params.SettingsResults{ 1738 Results: []params.SettingsResult{ 1739 {Settings: params.Settings{ 1740 "other": "things", 1741 }}, 1742 }, 1743 } 1744 result, err = s.uniter.ReadRemoteSettings(args) 1745 c.Assert(err, jc.ErrorIsNil) 1746 c.Assert(result, gc.DeepEquals, expect) 1747 1748 // Now destroy the remote unit, and check its settings can still be read. 1749 err = s.mysqlUnit.Destroy() 1750 c.Assert(err, jc.ErrorIsNil) 1751 err = s.mysqlUnit.EnsureDead() 1752 c.Assert(err, jc.ErrorIsNil) 1753 err = s.mysqlUnit.Remove() 1754 c.Assert(err, jc.ErrorIsNil) 1755 result, err = s.uniter.ReadRemoteSettings(args) 1756 c.Assert(err, jc.ErrorIsNil) 1757 c.Assert(result, gc.DeepEquals, expect) 1758 } 1759 1760 func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) { 1761 rel := s.addRelation(c, "wordpress", "mysql") 1762 relUnit, err := rel.Unit(s.mysqlUnit) 1763 c.Assert(err, jc.ErrorIsNil) 1764 settings := map[string]interface{}{ 1765 "other": "things", 1766 "invalid-bool": false, 1767 } 1768 err = relUnit.EnterScope(settings) 1769 c.Assert(err, jc.ErrorIsNil) 1770 s.assertInScope(c, relUnit, true) 1771 1772 args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ 1773 Relation: rel.Tag().String(), 1774 LocalUnit: "unit-wordpress-0", 1775 RemoteUnit: "unit-mysql-0", 1776 }}} 1777 expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` 1778 result, err := s.uniter.ReadRemoteSettings(args) 1779 c.Assert(err, jc.ErrorIsNil) 1780 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 1781 Results: []params.SettingsResult{ 1782 {Error: ¶ms.Error{Message: expectErr}}, 1783 }, 1784 }) 1785 } 1786 1787 func (s *uniterSuite) TestUpdateSettings(c *gc.C) { 1788 rel := s.addRelation(c, "wordpress", "mysql") 1789 relUnit, err := rel.Unit(s.wordpressUnit) 1790 c.Assert(err, jc.ErrorIsNil) 1791 settings := map[string]interface{}{ 1792 "some": "settings", 1793 "other": "stuff", 1794 } 1795 err = relUnit.EnterScope(settings) 1796 s.assertInScope(c, relUnit, true) 1797 1798 newSettings := params.Settings{ 1799 "some": "different", 1800 "other": "", 1801 } 1802 1803 args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{ 1804 {Relation: "relation-42", Unit: "unit-foo-0", Settings: nil}, 1805 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0", Settings: newSettings}, 1806 {Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil}, 1807 {Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil}, 1808 {Relation: "application-wordpress", Unit: "unit-foo-0", Settings: nil}, 1809 {Relation: "foo", Unit: "bar", Settings: nil}, 1810 {Relation: rel.Tag().String(), Unit: "unit-mysql-0", Settings: nil}, 1811 {Relation: rel.Tag().String(), Unit: "application-wordpress", Settings: nil}, 1812 {Relation: rel.Tag().String(), Unit: "application-mysql", Settings: nil}, 1813 {Relation: rel.Tag().String(), Unit: "user-foo", Settings: nil}, 1814 }} 1815 result, err := s.uniter.UpdateSettings(args) 1816 c.Assert(err, jc.ErrorIsNil) 1817 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1818 Results: []params.ErrorResult{ 1819 {apiservertesting.ErrUnauthorized}, 1820 {nil}, 1821 {apiservertesting.ErrUnauthorized}, 1822 {apiservertesting.ErrUnauthorized}, 1823 {apiservertesting.ErrUnauthorized}, 1824 {apiservertesting.ErrUnauthorized}, 1825 {apiservertesting.ErrUnauthorized}, 1826 {apiservertesting.ErrUnauthorized}, 1827 {apiservertesting.ErrUnauthorized}, 1828 {apiservertesting.ErrUnauthorized}, 1829 }, 1830 }) 1831 1832 // Verify the settings were saved. 1833 s.assertInScope(c, relUnit, true) 1834 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 1835 c.Assert(err, jc.ErrorIsNil) 1836 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 1837 "some": "different", 1838 }) 1839 } 1840 1841 func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) { 1842 // Add a relation between wordpress and mysql and enter scope with 1843 // mysqlUnit. 1844 rel := s.addRelation(c, "wordpress", "mysql") 1845 myRelUnit, err := rel.Unit(s.mysqlUnit) 1846 c.Assert(err, jc.ErrorIsNil) 1847 err = myRelUnit.EnterScope(nil) 1848 c.Assert(err, jc.ErrorIsNil) 1849 s.assertInScope(c, myRelUnit, true) 1850 1851 c.Assert(s.resources.Count(), gc.Equals, 0) 1852 1853 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1854 {Relation: "relation-42", Unit: "unit-foo-0"}, 1855 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1856 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1857 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 1858 {Relation: "relation-foo", Unit: ""}, 1859 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 1860 {Relation: "foo", Unit: "bar"}, 1861 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1862 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 1863 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 1864 {Relation: rel.Tag().String(), Unit: "user-foo"}, 1865 }} 1866 result, err := s.uniter.WatchRelationUnits(args) 1867 c.Assert(err, jc.ErrorIsNil) 1868 // UnitSettings versions are volatile, so we don't check them. 1869 // We just make sure the keys of the Changed field are as 1870 // expected. 1871 c.Assert(result.Results, gc.HasLen, len(args.RelationUnits)) 1872 mysqlChanges := result.Results[1].Changes 1873 c.Assert(mysqlChanges, gc.NotNil) 1874 changed, ok := mysqlChanges.Changed["mysql/0"] 1875 c.Assert(ok, jc.IsTrue) 1876 expectChanges := params.RelationUnitsChange{ 1877 Changed: map[string]params.UnitSettings{ 1878 "mysql/0": params.UnitSettings{changed.Version}, 1879 }, 1880 } 1881 c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{ 1882 Results: []params.RelationUnitsWatchResult{ 1883 {Error: apiservertesting.ErrUnauthorized}, 1884 { 1885 RelationUnitsWatcherId: "1", 1886 Changes: expectChanges, 1887 }, 1888 {Error: apiservertesting.ErrUnauthorized}, 1889 {Error: apiservertesting.ErrUnauthorized}, 1890 {Error: apiservertesting.ErrUnauthorized}, 1891 {Error: apiservertesting.ErrUnauthorized}, 1892 {Error: apiservertesting.ErrUnauthorized}, 1893 {Error: apiservertesting.ErrUnauthorized}, 1894 {Error: apiservertesting.ErrUnauthorized}, 1895 {Error: apiservertesting.ErrUnauthorized}, 1896 {Error: apiservertesting.ErrUnauthorized}, 1897 }, 1898 }) 1899 1900 // Verify the resource was registered and stop when done 1901 c.Assert(s.resources.Count(), gc.Equals, 1) 1902 resource := s.resources.Get("1") 1903 defer statetesting.AssertStop(c, resource) 1904 1905 // Check that the Watch has consumed the initial event ("returned" in 1906 // the Watch call) 1907 wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher)) 1908 wc.AssertNoChange() 1909 1910 // Leave scope with mysqlUnit and check it's detected. 1911 err = myRelUnit.LeaveScope() 1912 c.Assert(err, jc.ErrorIsNil) 1913 s.assertInScope(c, myRelUnit, false) 1914 1915 wc.AssertChange(nil, []string{"mysql/0"}) 1916 } 1917 1918 func (s *uniterSuite) TestAPIAddresses(c *gc.C) { 1919 hostPorts := [][]network.HostPort{ 1920 network.NewHostPorts(1234, "0.1.2.3"), 1921 } 1922 err := s.State.SetAPIHostPorts(hostPorts) 1923 c.Assert(err, jc.ErrorIsNil) 1924 1925 result, err := s.uniter.APIAddresses() 1926 c.Assert(err, jc.ErrorIsNil) 1927 c.Assert(result, gc.DeepEquals, params.StringsResult{ 1928 Result: []string{"0.1.2.3:1234"}, 1929 }) 1930 } 1931 1932 func (s *uniterSuite) TestWatchUnitAddresses(c *gc.C) { 1933 c.Assert(s.resources.Count(), gc.Equals, 0) 1934 1935 args := params.Entities{Entities: []params.Entity{ 1936 {Tag: "unit-mysql-0"}, 1937 {Tag: "unit-wordpress-0"}, 1938 {Tag: "unit-foo-42"}, 1939 {Tag: "machine-0"}, 1940 {Tag: "application-wordpress"}, 1941 }} 1942 result, err := s.uniter.WatchUnitAddresses(args) 1943 c.Assert(err, jc.ErrorIsNil) 1944 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 1945 Results: []params.NotifyWatchResult{ 1946 {Error: apiservertesting.ErrUnauthorized}, 1947 {NotifyWatcherId: "1"}, 1948 {Error: apiservertesting.ErrUnauthorized}, 1949 {Error: apiservertesting.ErrUnauthorized}, 1950 {Error: apiservertesting.ErrUnauthorized}, 1951 }, 1952 }) 1953 1954 // Verify the resource was registered and stop when done 1955 c.Assert(s.resources.Count(), gc.Equals, 1) 1956 resource := s.resources.Get("1") 1957 defer statetesting.AssertStop(c, resource) 1958 1959 // Check that the Watch has consumed the initial event ("returned" in 1960 // the Watch call) 1961 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 1962 wc.AssertNoChange() 1963 } 1964 1965 func (s *uniterSuite) TestGetMeterStatusUnauthenticated(c *gc.C) { 1966 args := params.Entities{Entities: []params.Entity{{s.mysqlUnit.Tag().String()}}} 1967 result, err := s.uniter.GetMeterStatus(args) 1968 c.Assert(err, jc.ErrorIsNil) 1969 c.Assert(result.Results, gc.HasLen, 1) 1970 c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied") 1971 c.Assert(result.Results[0].Code, gc.Equals, "") 1972 c.Assert(result.Results[0].Info, gc.Equals, "") 1973 } 1974 1975 func (s *uniterSuite) TestGetMeterStatusBadTag(c *gc.C) { 1976 tags := []string{ 1977 "user-admin", 1978 "unit-nosuchunit", 1979 "thisisnotatag", 1980 "machine-0", 1981 "model-blah", 1982 } 1983 args := params.Entities{Entities: make([]params.Entity, len(tags))} 1984 for i, tag := range tags { 1985 args.Entities[i] = params.Entity{Tag: tag} 1986 } 1987 result, err := s.uniter.GetMeterStatus(args) 1988 c.Assert(err, jc.ErrorIsNil) 1989 c.Assert(result.Results, gc.HasLen, len(tags)) 1990 for i, result := range result.Results { 1991 c.Logf("checking result %d", i) 1992 c.Assert(result.Code, gc.Equals, "") 1993 c.Assert(result.Info, gc.Equals, "") 1994 c.Assert(result.Error, gc.ErrorMatches, "permission denied") 1995 } 1996 } 1997 1998 func (s *uniterSuite) assertOneStringsWatcher(c *gc.C, result params.StringsWatchResults, err error) { 1999 c.Assert(err, jc.ErrorIsNil) 2000 c.Assert(result.Results, gc.HasLen, 3) 2001 c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 2002 c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1") 2003 c.Assert(result.Results[1].Changes, gc.NotNil) 2004 c.Assert(result.Results[1].Error, gc.IsNil) 2005 c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 2006 2007 // Verify the resource was registered and stop when done 2008 c.Assert(s.resources.Count(), gc.Equals, 1) 2009 resource := s.resources.Get("1") 2010 defer statetesting.AssertStop(c, resource) 2011 2012 // Check that the Watch has consumed the initial event ("returned" in 2013 // the Watch call) 2014 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 2015 wc.AssertNoChange() 2016 } 2017 2018 func (s *uniterSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) { 2019 ok, err := relUnit.InScope() 2020 c.Assert(err, jc.ErrorIsNil) 2021 c.Assert(ok, gc.Equals, inScope) 2022 } 2023 2024 func (s *uniterSuite) addRelation(c *gc.C, first, second string) *state.Relation { 2025 eps, err := s.State.InferEndpoints(first, second) 2026 c.Assert(err, jc.ErrorIsNil) 2027 rel, err := s.State.AddRelation(eps...) 2028 c.Assert(err, jc.ErrorIsNil) 2029 return rel 2030 } 2031 2032 func (s *uniterSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Application, *state.Unit) { 2033 relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc)) 2034 rel := s.addRelation(c, firstSvc, relatedSvc) 2035 relUnit, err := rel.Unit(unit) 2036 c.Assert(err, jc.ErrorIsNil) 2037 err = relUnit.EnterScope(nil) 2038 c.Assert(err, jc.ErrorIsNil) 2039 relatedUnit, err := s.State.Unit(relatedSvc + "/0") 2040 c.Assert(err, jc.ErrorIsNil) 2041 return rel, relatedService, relatedUnit 2042 } 2043 2044 func (s *uniterSuite) TestRequestReboot(c *gc.C) { 2045 args := params.Entities{Entities: []params.Entity{ 2046 {Tag: s.machine0.Tag().String()}, 2047 {Tag: s.machine1.Tag().String()}, 2048 {Tag: "bogus"}, 2049 {Tag: "nasty-tag"}, 2050 }} 2051 errResult, err := s.uniter.RequestReboot(args) 2052 c.Assert(err, jc.ErrorIsNil) 2053 c.Assert(errResult, gc.DeepEquals, params.ErrorResults{ 2054 Results: []params.ErrorResult{ 2055 {Error: nil}, 2056 {Error: apiservertesting.ErrUnauthorized}, 2057 {Error: apiservertesting.ErrUnauthorized}, 2058 {Error: apiservertesting.ErrUnauthorized}, 2059 }}) 2060 2061 rFlag, err := s.machine0.GetRebootFlag() 2062 c.Assert(err, jc.ErrorIsNil) 2063 c.Assert(rFlag, jc.IsTrue) 2064 2065 rFlag, err = s.machine1.GetRebootFlag() 2066 c.Assert(err, jc.ErrorIsNil) 2067 c.Assert(rFlag, jc.IsFalse) 2068 } 2069 2070 func checkUnorderedActionIdsEqual(c *gc.C, ids []string, results params.StringsWatchResults) { 2071 c.Assert(results, gc.NotNil) 2072 content := results.Results 2073 c.Assert(len(content), gc.Equals, 1) 2074 result := content[0] 2075 c.Assert(result.StringsWatcherId, gc.Equals, "1") 2076 obtainedIds := map[string]int{} 2077 expectedIds := map[string]int{} 2078 for _, id := range ids { 2079 expectedIds[id]++ 2080 } 2081 // The count of each ID that has been seen. 2082 for _, change := range result.Changes { 2083 obtainedIds[change]++ 2084 } 2085 c.Check(obtainedIds, jc.DeepEquals, expectedIds) 2086 } 2087 2088 func (s *uniterSuite) TestStorageAttachments(c *gc.C) { 2089 // We need to set up a unit that has storage metadata defined. 2090 ch := s.AddTestingCharm(c, "storage-block") 2091 sCons := map[string]state.StorageConstraints{ 2092 "data": {Pool: "", Size: 1024, Count: 1}, 2093 } 2094 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons) 2095 unit, err := service.AddUnit() 2096 c.Assert(err, jc.ErrorIsNil) 2097 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 2098 c.Assert(err, jc.ErrorIsNil) 2099 assignedMachineId, err := unit.AssignedMachineId() 2100 c.Assert(err, jc.ErrorIsNil) 2101 machine, err := s.State.Machine(assignedMachineId) 2102 c.Assert(err, jc.ErrorIsNil) 2103 2104 volumeAttachments, err := machine.VolumeAttachments() 2105 c.Assert(err, jc.ErrorIsNil) 2106 c.Assert(volumeAttachments, gc.HasLen, 1) 2107 2108 err = machine.SetProvisioned("inst-id", "fake_nonce", nil) 2109 c.Assert(err, jc.ErrorIsNil) 2110 2111 err = s.State.SetVolumeInfo( 2112 volumeAttachments[0].Volume(), 2113 state.VolumeInfo{VolumeId: "vol-123", Size: 456}, 2114 ) 2115 c.Assert(err, jc.ErrorIsNil) 2116 2117 err = s.State.SetVolumeAttachmentInfo( 2118 machine.MachineTag(), 2119 volumeAttachments[0].Volume(), 2120 state.VolumeAttachmentInfo{DeviceName: "xvdf1"}, 2121 ) 2122 c.Assert(err, jc.ErrorIsNil) 2123 2124 password, err := utils.RandomPassword() 2125 err = unit.SetPassword(password) 2126 c.Assert(err, jc.ErrorIsNil) 2127 st := s.OpenAPIAs(c, unit.Tag(), password) 2128 uniter, err := st.Uniter() 2129 c.Assert(err, jc.ErrorIsNil) 2130 2131 attachments, err := uniter.UnitStorageAttachments(unit.UnitTag()) 2132 c.Assert(err, jc.ErrorIsNil) 2133 c.Assert(attachments, gc.DeepEquals, []params.StorageAttachmentId{{ 2134 StorageTag: "storage-data-0", 2135 UnitTag: unit.Tag().String(), 2136 }}) 2137 } 2138 2139 func (s *uniterSuite) TestUnitStatus(c *gc.C) { 2140 now := time.Now() 2141 sInfo := status.StatusInfo{ 2142 Status: status.Maintenance, 2143 Message: "blah", 2144 Since: &now, 2145 } 2146 err := s.wordpressUnit.SetStatus(sInfo) 2147 c.Assert(err, jc.ErrorIsNil) 2148 sInfo = status.StatusInfo{ 2149 Status: status.Terminated, 2150 Message: "foo", 2151 Since: &now, 2152 } 2153 err = s.mysqlUnit.SetStatus(sInfo) 2154 c.Assert(err, jc.ErrorIsNil) 2155 2156 args := params.Entities{ 2157 Entities: []params.Entity{ 2158 {Tag: "unit-mysql-0"}, 2159 {Tag: "unit-wordpress-0"}, 2160 {Tag: "unit-foo-42"}, 2161 {Tag: "machine-1"}, 2162 {Tag: "invalid"}, 2163 }} 2164 result, err := s.uniter.UnitStatus(args) 2165 c.Assert(err, jc.ErrorIsNil) 2166 // Zero out the updated timestamps so we can easily check the results. 2167 for i, statusResult := range result.Results { 2168 r := statusResult 2169 if r.Status != "" { 2170 c.Assert(r.Since, gc.NotNil) 2171 } 2172 r.Since = nil 2173 result.Results[i] = r 2174 } 2175 c.Assert(result, gc.DeepEquals, params.StatusResults{ 2176 Results: []params.StatusResult{ 2177 {Error: apiservertesting.ErrUnauthorized}, 2178 {Status: status.Maintenance.String(), Info: "blah", Data: map[string]interface{}{}}, 2179 {Error: apiservertesting.ErrUnauthorized}, 2180 {Error: apiservertesting.ErrUnauthorized}, 2181 {Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)}, 2182 }, 2183 }) 2184 } 2185 2186 func (s *uniterSuite) TestAssignedMachine(c *gc.C) { 2187 args := params.Entities{Entities: []params.Entity{ 2188 {Tag: "unit-mysql-0"}, 2189 {Tag: "unit-wordpress-0"}, 2190 {Tag: "unit-foo-42"}, 2191 {Tag: "application-mysql"}, 2192 {Tag: "application-wordpress"}, 2193 {Tag: "machine-0"}, 2194 {Tag: "machine-1"}, 2195 {Tag: "machine-42"}, 2196 {Tag: "application-foo"}, 2197 {Tag: "relation-svc1.rel1#svc2.rel2"}, 2198 }} 2199 result, err := s.uniter.AssignedMachine(args) 2200 c.Assert(err, jc.ErrorIsNil) 2201 c.Assert(result, jc.DeepEquals, params.StringResults{ 2202 Results: []params.StringResult{ 2203 {Error: apiservertesting.ErrUnauthorized}, 2204 {Result: "machine-0"}, 2205 {Error: apiservertesting.ErrUnauthorized}, 2206 {Error: apiservertesting.ErrUnauthorized}, 2207 {Error: apiservertesting.ErrUnauthorized}, 2208 {Error: apiservertesting.ErrUnauthorized}, 2209 {Error: apiservertesting.ErrUnauthorized}, 2210 {Error: apiservertesting.ErrUnauthorized}, 2211 {Error: apiservertesting.ErrUnauthorized}, 2212 {Error: apiservertesting.ErrUnauthorized}, 2213 }, 2214 }) 2215 } 2216 2217 func (s *uniterSuite) TestAllMachinePorts(c *gc.C) { 2218 // Verify no ports are opened yet on the machine or unit. 2219 machinePorts, err := s.machine0.AllPorts() 2220 c.Assert(err, jc.ErrorIsNil) 2221 c.Assert(machinePorts, gc.HasLen, 0) 2222 unitPorts, err := s.wordpressUnit.OpenedPorts() 2223 c.Assert(err, jc.ErrorIsNil) 2224 c.Assert(unitPorts, gc.HasLen, 0) 2225 2226 // Add another mysql unit on machine 0. 2227 mysqlUnit1, err := s.mysql.AddUnit() 2228 c.Assert(err, jc.ErrorIsNil) 2229 err = mysqlUnit1.AssignToMachine(s.machine0) 2230 c.Assert(err, jc.ErrorIsNil) 2231 2232 // Open some ports on both units. 2233 err = s.wordpressUnit.OpenPorts("tcp", 100, 200) 2234 c.Assert(err, jc.ErrorIsNil) 2235 err = s.wordpressUnit.OpenPorts("udp", 10, 20) 2236 c.Assert(err, jc.ErrorIsNil) 2237 err = mysqlUnit1.OpenPorts("tcp", 201, 250) 2238 c.Assert(err, jc.ErrorIsNil) 2239 err = mysqlUnit1.OpenPorts("udp", 1, 8) 2240 c.Assert(err, jc.ErrorIsNil) 2241 2242 args := params.Entities{Entities: []params.Entity{ 2243 {Tag: "unit-mysql-0"}, 2244 {Tag: "machine-0"}, 2245 {Tag: "machine-1"}, 2246 {Tag: "unit-foo-42"}, 2247 {Tag: "machine-42"}, 2248 {Tag: "application-wordpress"}, 2249 }} 2250 expectPorts := []params.MachinePortRange{ 2251 {UnitTag: "unit-wordpress-0", PortRange: params.PortRange{100, 200, "tcp"}}, 2252 {UnitTag: "unit-mysql-1", PortRange: params.PortRange{201, 250, "tcp"}}, 2253 {UnitTag: "unit-mysql-1", PortRange: params.PortRange{1, 8, "udp"}}, 2254 {UnitTag: "unit-wordpress-0", PortRange: params.PortRange{10, 20, "udp"}}, 2255 } 2256 result, err := s.uniter.AllMachinePorts(args) 2257 c.Assert(err, jc.ErrorIsNil) 2258 c.Assert(result, gc.DeepEquals, params.MachinePortsResults{ 2259 Results: []params.MachinePortsResult{ 2260 {Error: apiservertesting.ErrUnauthorized}, 2261 {Ports: expectPorts}, 2262 {Error: apiservertesting.ErrUnauthorized}, 2263 {Error: apiservertesting.ErrUnauthorized}, 2264 {Error: apiservertesting.ErrUnauthorized}, 2265 {Error: apiservertesting.ErrUnauthorized}, 2266 }, 2267 }) 2268 } 2269 2270 type unitMetricBatchesSuite struct { 2271 uniterSuite 2272 *commontesting.ModelWatcherTest 2273 uniter *uniter.UniterAPIV3 2274 } 2275 2276 var _ = gc.Suite(&unitMetricBatchesSuite{}) 2277 2278 func (s *unitMetricBatchesSuite) SetUpTest(c *gc.C) { 2279 s.uniterSuite.SetUpTest(c) 2280 2281 meteredAuthorizer := apiservertesting.FakeAuthorizer{ 2282 Tag: s.meteredUnit.Tag(), 2283 } 2284 var err error 2285 s.uniter, err = uniter.NewUniterAPIV4( 2286 s.State, 2287 s.resources, 2288 meteredAuthorizer, 2289 ) 2290 c.Assert(err, jc.ErrorIsNil) 2291 2292 s.ModelWatcherTest = commontesting.NewModelWatcherTest( 2293 s.uniter, 2294 s.State, 2295 s.resources, 2296 ) 2297 } 2298 2299 func (s *unitMetricBatchesSuite) TestAddMetricsBatch(c *gc.C) { 2300 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 2301 uuid := utils.MustNewUUID().String() 2302 2303 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 2304 Batches: []params.MetricBatchParam{{ 2305 Tag: s.meteredUnit.Tag().String(), 2306 Batch: params.MetricBatch{ 2307 UUID: uuid, 2308 CharmURL: s.meteredCharm.URL().String(), 2309 Created: time.Now(), 2310 Metrics: metrics, 2311 }}}}, 2312 ) 2313 2314 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2315 Results: []params.ErrorResult{{nil}}, 2316 }) 2317 c.Assert(err, jc.ErrorIsNil) 2318 2319 batch, err := s.State.MetricBatch(uuid) 2320 c.Assert(err, jc.ErrorIsNil) 2321 c.Assert(batch.UUID(), gc.Equals, uuid) 2322 c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String()) 2323 c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name()) 2324 storedMetrics := batch.Metrics() 2325 c.Assert(storedMetrics, gc.HasLen, 1) 2326 c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key) 2327 c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value) 2328 } 2329 2330 func (s *unitMetricBatchesSuite) TestAddMetricsBatchNoCharmURL(c *gc.C) { 2331 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 2332 uuid := utils.MustNewUUID().String() 2333 2334 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 2335 Batches: []params.MetricBatchParam{{ 2336 Tag: s.meteredUnit.Tag().String(), 2337 Batch: params.MetricBatch{ 2338 UUID: uuid, 2339 CharmURL: s.meteredCharm.URL().String(), 2340 Created: time.Now(), 2341 Metrics: metrics, 2342 }}}}) 2343 2344 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2345 Results: []params.ErrorResult{{nil}}, 2346 }) 2347 c.Assert(err, jc.ErrorIsNil) 2348 2349 batch, err := s.State.MetricBatch(uuid) 2350 c.Assert(err, jc.ErrorIsNil) 2351 c.Assert(batch.UUID(), gc.Equals, uuid) 2352 c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String()) 2353 c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name()) 2354 storedMetrics := batch.Metrics() 2355 c.Assert(storedMetrics, gc.HasLen, 1) 2356 c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key) 2357 c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value) 2358 } 2359 2360 func (s *unitMetricBatchesSuite) TestAddMetricsBatchDiffTag(c *gc.C) { 2361 unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true}) 2362 2363 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 2364 uuid := utils.MustNewUUID().String() 2365 2366 tests := []struct { 2367 about string 2368 tag string 2369 expect string 2370 }{{ 2371 about: "different unit", 2372 tag: unit2.Tag().String(), 2373 expect: "permission denied", 2374 }, { 2375 about: "user tag", 2376 tag: names.NewLocalUserTag("admin").String(), 2377 expect: `"user-admin@local" is not a valid unit tag`, 2378 }, { 2379 about: "machine tag", 2380 tag: names.NewMachineTag("0").String(), 2381 expect: `"machine-0" is not a valid unit tag`, 2382 }} 2383 2384 for i, test := range tests { 2385 c.Logf("test %d: %s", i, test.about) 2386 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 2387 Batches: []params.MetricBatchParam{{ 2388 Tag: test.tag, 2389 Batch: params.MetricBatch{ 2390 UUID: uuid, 2391 CharmURL: "", 2392 Created: time.Now(), 2393 Metrics: metrics, 2394 }}}}) 2395 2396 if test.expect == "" { 2397 c.Assert(result.OneError(), jc.ErrorIsNil) 2398 } else { 2399 c.Assert(result.OneError(), gc.ErrorMatches, test.expect) 2400 } 2401 c.Assert(err, jc.ErrorIsNil) 2402 2403 _, err = s.State.MetricBatch(uuid) 2404 c.Assert(err, jc.Satisfies, errors.IsNotFound) 2405 } 2406 } 2407 2408 type uniterNetworkConfigSuite struct { 2409 base uniterSuite // not embedded so it doesn't run all tests. 2410 } 2411 2412 var _ = gc.Suite(&uniterNetworkConfigSuite{}) 2413 2414 func (s *uniterNetworkConfigSuite) SetUpSuite(c *gc.C) { 2415 s.base.SetUpSuite(c) 2416 } 2417 2418 func (s *uniterNetworkConfigSuite) TearDownSuite(c *gc.C) { 2419 s.base.TearDownSuite(c) 2420 } 2421 2422 func (s *uniterNetworkConfigSuite) SetUpTest(c *gc.C) { 2423 s.base.JujuConnSuite.SetUpTest(c) 2424 2425 // Add the spaces and subnets used by the test. 2426 subnetInfos := []state.SubnetInfo{{ 2427 CIDR: "8.8.0.0/16", 2428 SpaceName: "public", 2429 }, { 2430 CIDR: "10.0.0.0/24", 2431 SpaceName: "internal", 2432 }} 2433 for _, info := range subnetInfos { 2434 _, err := s.base.State.AddSpace(info.SpaceName, "", nil, false) 2435 c.Assert(err, jc.ErrorIsNil) 2436 _, err = s.base.State.AddSubnet(info) 2437 c.Assert(err, jc.ErrorIsNil) 2438 } 2439 2440 s.base.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10) 2441 2442 factory := jujuFactory.NewFactory(s.base.State) 2443 s.base.wpCharm = factory.MakeCharm(c, &jujuFactory.CharmParams{ 2444 Name: "wordpress-extra-bindings", 2445 URL: "cs:quantal/wordpress-extra-bindings-4", 2446 }) 2447 var err error 2448 s.base.wordpress, err = s.base.State.AddApplication(state.AddApplicationArgs{ 2449 Name: "wordpress", 2450 Charm: s.base.wpCharm, 2451 EndpointBindings: map[string]string{ 2452 "db": "internal", // relation name 2453 "admin-api": "public", // extra-binding name 2454 }, 2455 }) 2456 c.Assert(err, jc.ErrorIsNil) 2457 s.base.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{ 2458 Application: s.base.wordpress, 2459 Machine: s.base.machine0, 2460 }) 2461 2462 s.base.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20) 2463 2464 mysqlCharm := factory.MakeCharm(c, &jujuFactory.CharmParams{ 2465 Name: "mysql", 2466 }) 2467 s.base.mysql = factory.MakeApplication(c, &jujuFactory.ApplicationParams{ 2468 Name: "mysql", 2469 Charm: mysqlCharm, 2470 }) 2471 s.base.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{ 2472 Application: s.base.wordpress, 2473 Machine: s.base.machine0, 2474 }) 2475 s.base.mysqlUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{ 2476 Application: s.base.mysql, 2477 Machine: s.base.machine1, 2478 }) 2479 2480 // Create the resource registry separately to track invocations to 2481 // Register. 2482 s.base.resources = common.NewResources() 2483 s.base.AddCleanup(func(_ *gc.C) { s.base.resources.StopAll() }) 2484 2485 s.setupUniterAPIForUnit(c, s.base.wordpressUnit) 2486 } 2487 2488 func (s *uniterNetworkConfigSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine { 2489 machine, err := s.base.State.AddMachine("quantal", state.JobHostUnits) 2490 c.Assert(err, jc.ErrorIsNil) 2491 devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix) 2492 err = machine.SetInstanceInfo("i-am", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil) 2493 c.Assert(err, jc.ErrorIsNil) 2494 2495 machineAddrs, err := machine.AllAddresses() 2496 c.Assert(err, jc.ErrorIsNil) 2497 2498 netAddrs := make([]network.Address, len(machineAddrs)) 2499 for i, addr := range machineAddrs { 2500 netAddrs[i] = network.NewAddress(addr.Value()) 2501 } 2502 err = machine.SetProviderAddresses(netAddrs...) 2503 c.Assert(err, jc.ErrorIsNil) 2504 2505 return machine 2506 } 2507 2508 func (s *uniterNetworkConfigSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) { 2509 return []state.LinkLayerDeviceArgs{{ 2510 Name: "eth0", 2511 Type: state.EthernetDevice, 2512 }, { 2513 Name: "eth0.100", 2514 Type: state.VLAN_8021QDevice, 2515 ParentName: "eth0", 2516 }, { 2517 Name: "eth1", 2518 Type: state.EthernetDevice, 2519 }, { 2520 Name: "eth1.100", 2521 Type: state.VLAN_8021QDevice, 2522 ParentName: "eth1", 2523 }}, 2524 []state.LinkLayerDeviceAddress{{ 2525 DeviceName: "eth0", 2526 ConfigMethod: state.StaticAddress, 2527 CIDRAddress: fmt.Sprintf("8.8.8.%d/16", addrSuffix), 2528 }, { 2529 DeviceName: "eth0.100", 2530 ConfigMethod: state.StaticAddress, 2531 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix), 2532 }, { 2533 DeviceName: "eth1", 2534 ConfigMethod: state.StaticAddress, 2535 CIDRAddress: fmt.Sprintf("8.8.4.%d/16", addrSuffix), 2536 }, { 2537 DeviceName: "eth1.100", 2538 ConfigMethod: state.StaticAddress, 2539 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix+1), 2540 }} 2541 } 2542 2543 func (s *uniterNetworkConfigSuite) TearDownTest(c *gc.C) { 2544 s.base.JujuConnSuite.TearDownTest(c) 2545 } 2546 2547 func (s *uniterNetworkConfigSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) { 2548 // Create a FakeAuthorizer so we can check permissions, set up assuming the 2549 // given unit agent has logged in. 2550 s.base.authorizer = apiservertesting.FakeAuthorizer{ 2551 Tag: givenUnit.Tag(), 2552 } 2553 2554 var err error 2555 s.base.uniter, err = uniter.NewUniterAPIV4( 2556 s.base.State, 2557 s.base.resources, 2558 s.base.authorizer, 2559 ) 2560 c.Assert(err, jc.ErrorIsNil) 2561 } 2562 2563 func (s *uniterNetworkConfigSuite) TestNetworkConfigPermissions(c *gc.C) { 2564 s.addRelationAndAssertInScope(c) 2565 2566 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 2567 {BindingName: "foo", UnitTag: "unit-foo-0"}, 2568 {BindingName: "db-client", UnitTag: "invalid"}, 2569 {BindingName: "juju-info", UnitTag: "unit-mysql-0"}, 2570 {BindingName: "", UnitTag: s.base.wordpressUnit.Tag().String()}, 2571 {BindingName: "unknown", UnitTag: s.base.wordpressUnit.Tag().String()}, 2572 }} 2573 2574 result, err := s.base.uniter.NetworkConfig(args) 2575 c.Assert(err, jc.ErrorIsNil) 2576 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 2577 Results: []params.UnitNetworkConfigResult{ 2578 {Error: apiservertesting.ErrUnauthorized}, 2579 {Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)}, 2580 {Error: apiservertesting.ErrUnauthorized}, 2581 {Error: apiservertesting.ServerError(`binding name cannot be empty`)}, 2582 {Error: apiservertesting.ServerError(`binding name "unknown" not defined by the unit's charm`)}, 2583 }, 2584 }) 2585 } 2586 2587 func (s *uniterNetworkConfigSuite) addRelationAndAssertInScope(c *gc.C) { 2588 // Add a relation between wordpress and mysql and enter scope with 2589 // mysqlUnit. 2590 rel := s.base.addRelation(c, "wordpress", "mysql") 2591 wpRelUnit, err := rel.Unit(s.base.wordpressUnit) 2592 c.Assert(err, jc.ErrorIsNil) 2593 err = wpRelUnit.EnterScope(nil) 2594 c.Assert(err, jc.ErrorIsNil) 2595 s.base.assertInScope(c, wpRelUnit, true) 2596 } 2597 2598 func (s *uniterNetworkConfigSuite) TestNetworkConfigForExplicitlyBoundEndpoint(c *gc.C) { 2599 s.addRelationAndAssertInScope(c) 2600 2601 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 2602 {BindingName: "db", UnitTag: s.base.wordpressUnit.Tag().String()}, 2603 {BindingName: "admin-api", UnitTag: s.base.wordpressUnit.Tag().String()}, 2604 }} 2605 2606 // For the relation "wordpress:db mysql:server" we expect to see only 2607 // addresses bound to the "internal" space, where the "db" endpoint itself 2608 // is bound to. 2609 expectedConfigWithRelationName := []params.NetworkConfig{ 2610 {Address: "10.0.0.10"}, 2611 {Address: "10.0.0.11"}, 2612 } 2613 // For the "admin-api" extra-binding we expect to see only addresses from 2614 // the "public" space. 2615 expectedConfigWithExtraBindingName := []params.NetworkConfig{ 2616 {Address: "8.8.8.10"}, 2617 {Address: "8.8.4.10"}, 2618 } 2619 2620 result, err := s.base.uniter.NetworkConfig(args) 2621 c.Assert(err, jc.ErrorIsNil) 2622 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 2623 Results: []params.UnitNetworkConfigResult{ 2624 {Config: expectedConfigWithRelationName}, 2625 {Config: expectedConfigWithExtraBindingName}, 2626 }, 2627 }) 2628 } 2629 2630 func (s *uniterNetworkConfigSuite) TestNetworkConfigForImplicitlyBoundEndpoint(c *gc.C) { 2631 // Since wordpressUnit has explicit binding for "db", switch the API to 2632 // mysqlUnit and check "mysql:server" uses the machine preferred private 2633 // address. 2634 s.setupUniterAPIForUnit(c, s.base.mysqlUnit) 2635 rel := s.base.addRelation(c, "mysql", "wordpress") 2636 mysqlRelUnit, err := rel.Unit(s.base.mysqlUnit) 2637 c.Assert(err, jc.ErrorIsNil) 2638 err = mysqlRelUnit.EnterScope(nil) 2639 c.Assert(err, jc.ErrorIsNil) 2640 s.base.assertInScope(c, mysqlRelUnit, true) 2641 2642 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 2643 {BindingName: "server", UnitTag: s.base.mysqlUnit.Tag().String()}, 2644 }} 2645 2646 privateAddress, err := s.base.machine1.PrivateAddress() 2647 c.Assert(err, jc.ErrorIsNil) 2648 2649 expectedConfig := []params.NetworkConfig{{Address: privateAddress.Value}} 2650 2651 result, err := s.base.uniter.NetworkConfig(args) 2652 c.Assert(err, jc.ErrorIsNil) 2653 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 2654 Results: []params.UnitNetworkConfigResult{ 2655 {Config: expectedConfig}, 2656 }, 2657 }) 2658 }