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