github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/uniter/uniter_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/charm.v6" 15 "gopkg.in/juju/environschema.v1" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/mgo.v2/bson" 18 19 "github.com/juju/juju/api" 20 apiuniter "github.com/juju/juju/api/uniter" 21 "github.com/juju/juju/apiserver/common" 22 commontesting "github.com/juju/juju/apiserver/common/testing" 23 "github.com/juju/juju/apiserver/facade/facadetest" 24 "github.com/juju/juju/apiserver/facades/agent/uniter" 25 "github.com/juju/juju/apiserver/facades/client/application" 26 "github.com/juju/juju/apiserver/params" 27 apiservertesting "github.com/juju/juju/apiserver/testing" 28 coreapplication "github.com/juju/juju/core/application" 29 corenetwork "github.com/juju/juju/core/network" 30 "github.com/juju/juju/core/status" 31 "github.com/juju/juju/environs" 32 "github.com/juju/juju/environs/config" 33 "github.com/juju/juju/environs/context" 34 "github.com/juju/juju/juju/testing" 35 "github.com/juju/juju/network" 36 "github.com/juju/juju/state" 37 "github.com/juju/juju/state/multiwatcher" 38 statetesting "github.com/juju/juju/state/testing" 39 coretesting "github.com/juju/juju/testing" 40 "github.com/juju/juju/testing/factory" 41 ) 42 43 // uniterSuiteBase implements common testing suite for all API versions. 44 // It is not intended to be used directly or registered as a suite, 45 // but embedded. 46 type uniterSuiteBase struct { 47 testing.JujuConnSuite 48 49 authorizer apiservertesting.FakeAuthorizer 50 resources *common.Resources 51 uniter *uniter.UniterAPI 52 53 machine0 *state.Machine 54 machine1 *state.Machine 55 wordpress *state.Application 56 wpCharm *state.Charm 57 mysql *state.Application 58 wordpressUnit *state.Unit 59 mysqlUnit *state.Unit 60 61 meteredApplication *state.Application 62 meteredCharm *state.Charm 63 meteredUnit *state.Unit 64 } 65 66 func (s *uniterSuiteBase) SetUpTest(c *gc.C) { 67 s.JujuConnSuite.SetUpTest(c) 68 69 // Create two machines, two services and add a unit to each service. 70 s.machine0 = s.Factory.MakeMachine(c, &factory.MachineParams{ 71 Series: "quantal", 72 Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, 73 }) 74 s.machine1 = s.Factory.MakeMachine(c, &factory.MachineParams{ 75 Series: "quantal", 76 Jobs: []state.MachineJob{state.JobHostUnits}, 77 }) 78 s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{ 79 Name: "wordpress", 80 URL: "cs:quantal/wordpress-3", 81 }) 82 s.wordpress = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 83 Name: "wordpress", 84 Charm: s.wpCharm, 85 }) 86 mysqlCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 87 Name: "mysql", 88 }) 89 s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 90 Name: "mysql", 91 Charm: mysqlCharm, 92 }) 93 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 94 Application: s.wordpress, 95 Machine: s.machine0, 96 }) 97 s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 98 Application: s.mysql, 99 Machine: s.machine1, 100 }) 101 102 s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{ 103 Name: "metered", 104 URL: "cs:quantal/metered", 105 }) 106 s.meteredApplication = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 107 Charm: s.meteredCharm, 108 }) 109 s.meteredUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 110 Application: s.meteredApplication, 111 SetCharmURL: true, 112 }) 113 114 // Create a FakeAuthorizer so we can check permissions, 115 // set up assuming unit 0 has logged in. 116 s.authorizer = apiservertesting.FakeAuthorizer{ 117 Tag: s.wordpressUnit.Tag(), 118 } 119 120 // Create the resource registry separately to track invocations to 121 // Register. 122 s.resources = common.NewResources() 123 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 124 125 uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{ 126 State_: s.State, 127 Resources_: s.resources, 128 Auth_: s.authorizer, 129 LeadershipChecker_: s.State.LeadershipChecker(), 130 }) 131 c.Assert(err, jc.ErrorIsNil) 132 s.uniter = uniterAPI 133 } 134 135 func (s *uniterSuiteBase) addRelation(c *gc.C, first, second string) *state.Relation { 136 eps, err := s.State.InferEndpoints(first, second) 137 c.Assert(err, jc.ErrorIsNil) 138 rel, err := s.State.AddRelation(eps...) 139 c.Assert(err, jc.ErrorIsNil) 140 return rel 141 } 142 143 func (s *uniterSuiteBase) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) { 144 ok, err := relUnit.InScope() 145 c.Assert(err, jc.ErrorIsNil) 146 c.Assert(ok, gc.Equals, inScope) 147 } 148 149 func (s *uniterSuiteBase) setupCAASModel(c *gc.C) (*apiuniter.State, *state.CAASModel, *state.Application, *state.Unit) { 150 st := s.Factory.MakeCAASModel(c, nil) 151 m, err := st.Model() 152 c.Assert(err, jc.ErrorIsNil) 153 s.CleanupSuite.AddCleanup(func(*gc.C) { st.Close() }) 154 cm, err := m.CAASModel() 155 c.Assert(err, jc.ErrorIsNil) 156 157 f := factory.NewFactory(st, s.StatePool) 158 ch := f.MakeCharm(c, &factory.CharmParams{Name: "gitlab", Series: "kubernetes"}) 159 app := f.MakeApplication(c, &factory.ApplicationParams{Name: "gitlab", Charm: ch}) 160 unit := f.MakeUnit(c, &factory.UnitParams{ 161 Application: app, 162 SetCharmURL: true, 163 }) 164 165 password, err := utils.RandomPassword() 166 c.Assert(err, jc.ErrorIsNil) 167 err = unit.SetPassword(password) 168 c.Assert(err, jc.ErrorIsNil) 169 170 apiInfo, err := environs.APIInfo(context.NewCloudCallContext(), s.ControllerConfig.ControllerUUID(), st.ModelUUID(), coretesting.CACert, s.ControllerConfig.APIPort(), s.Environ) 171 c.Assert(err, jc.ErrorIsNil) 172 apiInfo.Tag = unit.Tag() 173 apiInfo.Password = password 174 apiState, err := api.Open(apiInfo, api.DialOpts{}) 175 c.Assert(err, jc.ErrorIsNil) 176 s.CleanupSuite.AddCleanup(func(*gc.C) { apiState.Close() }) 177 178 s.authorizer = apiservertesting.FakeAuthorizer{ 179 Tag: unit.Tag(), 180 } 181 u, err := apiState.Uniter() 182 c.Assert(err, jc.ErrorIsNil) 183 return u, cm, app, unit 184 } 185 186 type uniterSuite struct { 187 uniterSuiteBase 188 } 189 190 var _ = gc.Suite(&uniterSuite{}) 191 192 func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) { 193 anAuthorizer := s.authorizer 194 anAuthorizer.Tag = names.NewMachineTag("9") 195 _, err := uniter.NewUniterAPI(facadetest.Context{ 196 State_: s.State, 197 Resources_: s.resources, 198 Auth_: anAuthorizer, 199 }) 200 c.Assert(err, gc.NotNil) 201 c.Assert(err, gc.ErrorMatches, "permission denied") 202 } 203 204 func (s *uniterSuite) TestSetStatus(c *gc.C) { 205 now := time.Now() 206 sInfo := status.StatusInfo{ 207 Status: status.Executing, 208 Message: "blah", 209 Since: &now, 210 } 211 err := s.wordpressUnit.SetAgentStatus(sInfo) 212 c.Assert(err, jc.ErrorIsNil) 213 sInfo = status.StatusInfo{ 214 Status: status.Executing, 215 Message: "foo", 216 Since: &now, 217 } 218 err = s.mysqlUnit.SetAgentStatus(sInfo) 219 c.Assert(err, jc.ErrorIsNil) 220 221 args := params.SetStatus{ 222 Entities: []params.EntityStatusArgs{ 223 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 224 {Tag: "unit-wordpress-0", Status: status.Rebooting.String(), Info: "foobar"}, 225 {Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"}, 226 }} 227 result, err := s.uniter.SetStatus(args) 228 c.Assert(err, jc.ErrorIsNil) 229 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 230 Results: []params.ErrorResult{ 231 {apiservertesting.ErrUnauthorized}, 232 {nil}, 233 {apiservertesting.ErrUnauthorized}, 234 }, 235 }) 236 237 // Verify mysqlUnit - no change. 238 statusInfo, err := s.mysqlUnit.AgentStatus() 239 c.Assert(err, jc.ErrorIsNil) 240 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 241 c.Assert(statusInfo.Message, gc.Equals, "foo") 242 // ...wordpressUnit is fine though. 243 statusInfo, err = s.wordpressUnit.AgentStatus() 244 c.Assert(err, jc.ErrorIsNil) 245 c.Assert(statusInfo.Status, gc.Equals, status.Rebooting) 246 c.Assert(statusInfo.Message, gc.Equals, "foobar") 247 } 248 249 func (s *uniterSuite) TestSetAgentStatus(c *gc.C) { 250 now := time.Now() 251 sInfo := status.StatusInfo{ 252 Status: status.Executing, 253 Message: "blah", 254 Since: &now, 255 } 256 err := s.wordpressUnit.SetAgentStatus(sInfo) 257 c.Assert(err, jc.ErrorIsNil) 258 sInfo = status.StatusInfo{ 259 Status: status.Executing, 260 Message: "foo", 261 Since: &now, 262 } 263 err = s.mysqlUnit.SetAgentStatus(sInfo) 264 c.Assert(err, jc.ErrorIsNil) 265 266 args := params.SetStatus{ 267 Entities: []params.EntityStatusArgs{ 268 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 269 {Tag: "unit-wordpress-0", Status: status.Executing.String(), Info: "foobar"}, 270 {Tag: "unit-foo-42", Status: status.Rebooting.String(), Info: "blah"}, 271 }} 272 result, err := s.uniter.SetAgentStatus(args) 273 c.Assert(err, jc.ErrorIsNil) 274 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 275 Results: []params.ErrorResult{ 276 {apiservertesting.ErrUnauthorized}, 277 {nil}, 278 {apiservertesting.ErrUnauthorized}, 279 }, 280 }) 281 282 // Verify mysqlUnit - no change. 283 statusInfo, err := s.mysqlUnit.AgentStatus() 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 286 c.Assert(statusInfo.Message, gc.Equals, "foo") 287 // ...wordpressUnit is fine though. 288 statusInfo, err = s.wordpressUnit.AgentStatus() 289 c.Assert(err, jc.ErrorIsNil) 290 c.Assert(statusInfo.Status, gc.Equals, status.Executing) 291 c.Assert(statusInfo.Message, gc.Equals, "foobar") 292 } 293 294 func (s *uniterSuite) TestSetUnitStatus(c *gc.C) { 295 now := time.Now() 296 sInfo := status.StatusInfo{ 297 Status: status.Active, 298 Message: "blah", 299 Since: &now, 300 } 301 err := s.wordpressUnit.SetStatus(sInfo) 302 c.Assert(err, jc.ErrorIsNil) 303 sInfo = status.StatusInfo{ 304 Status: status.Terminated, 305 Message: "foo", 306 Since: &now, 307 } 308 err = s.mysqlUnit.SetStatus(sInfo) 309 c.Assert(err, jc.ErrorIsNil) 310 311 args := params.SetStatus{ 312 Entities: []params.EntityStatusArgs{ 313 {Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"}, 314 {Tag: "unit-wordpress-0", Status: status.Terminated.String(), Info: "foobar"}, 315 {Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"}, 316 }} 317 result, err := s.uniter.SetUnitStatus(args) 318 c.Assert(err, jc.ErrorIsNil) 319 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 320 Results: []params.ErrorResult{ 321 {apiservertesting.ErrUnauthorized}, 322 {nil}, 323 {apiservertesting.ErrUnauthorized}, 324 }, 325 }) 326 327 // Verify mysqlUnit - no change. 328 statusInfo, err := s.mysqlUnit.Status() 329 c.Assert(err, jc.ErrorIsNil) 330 c.Assert(statusInfo.Status, gc.Equals, status.Terminated) 331 c.Assert(statusInfo.Message, gc.Equals, "foo") 332 // ...wordpressUnit is fine though. 333 statusInfo, err = s.wordpressUnit.Status() 334 c.Assert(err, jc.ErrorIsNil) 335 c.Assert(statusInfo.Status, gc.Equals, status.Terminated) 336 c.Assert(statusInfo.Message, gc.Equals, "foobar") 337 } 338 339 func (s *uniterSuite) TestLife(c *gc.C) { 340 // Add a relation wordpress-mysql. 341 rel := s.addRelation(c, "wordpress", "mysql") 342 relUnit, err := rel.Unit(s.wordpressUnit) 343 c.Assert(err, jc.ErrorIsNil) 344 err = relUnit.EnterScope(nil) 345 c.Assert(err, jc.ErrorIsNil) 346 c.Assert(rel.Life(), gc.Equals, state.Alive) 347 relStatus, err := rel.Status() 348 c.Assert(err, jc.ErrorIsNil) 349 c.Assert(relStatus.Status, gc.Equals, status.Joining) 350 351 // Make the wordpressUnit dead. 352 err = s.wordpressUnit.EnsureDead() 353 c.Assert(err, jc.ErrorIsNil) 354 err = s.wordpressUnit.Refresh() 355 c.Assert(err, jc.ErrorIsNil) 356 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 357 358 // Add another unit, so the service will stay dying when we 359 // destroy it later. 360 extraUnit, err := s.wordpress.AddUnit(state.AddUnitParams{}) 361 c.Assert(err, jc.ErrorIsNil) 362 c.Assert(extraUnit, gc.NotNil) 363 364 // Make the wordpress service dying. 365 err = s.wordpress.Destroy() 366 c.Assert(err, jc.ErrorIsNil) 367 err = s.wordpress.Refresh() 368 c.Assert(err, jc.ErrorIsNil) 369 c.Assert(s.wordpress.Life(), gc.Equals, state.Dying) 370 371 args := params.Entities{Entities: []params.Entity{ 372 {Tag: "unit-mysql-0"}, 373 {Tag: "unit-wordpress-0"}, 374 {Tag: "unit-foo-42"}, 375 {Tag: "application-mysql"}, 376 {Tag: "application-wordpress"}, 377 {Tag: "machine-0"}, 378 {Tag: "machine-1"}, 379 {Tag: "machine-42"}, 380 {Tag: "application-foo"}, 381 // TODO(dfc) these aren't valid tags any more 382 // but I hope to restore this test when params.Entity takes 383 // tags, not strings, which is coming soon. 384 // {Tag: "just-foo"}, 385 {Tag: rel.Tag().String()}, 386 {Tag: "relation-svc1.rel1#svc2.rel2"}, 387 // {Tag: "relation-blah"}, 388 }} 389 result, err := s.uniter.Life(args) 390 c.Assert(err, jc.ErrorIsNil) 391 c.Assert(result, gc.DeepEquals, params.LifeResults{ 392 Results: []params.LifeResult{ 393 {Error: apiservertesting.ErrUnauthorized}, 394 {Life: "dead"}, 395 {Error: apiservertesting.ErrUnauthorized}, 396 {Error: apiservertesting.ErrUnauthorized}, 397 {Life: "dying"}, 398 {Error: apiservertesting.ErrUnauthorized}, 399 {Error: apiservertesting.ErrUnauthorized}, 400 {Error: apiservertesting.ErrUnauthorized}, 401 {Error: apiservertesting.ErrUnauthorized}, 402 // TODO(dfc) see above 403 // {Error: apiservertesting.ErrUnauthorized}, 404 {Error: apiservertesting.ErrUnauthorized}, 405 {Error: apiservertesting.ErrUnauthorized}, 406 // {Error: apiservertesting.ErrUnauthorized}, 407 }, 408 }) 409 } 410 411 func (s *uniterSuite) TestEnsureDead(c *gc.C) { 412 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 413 c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) 414 415 args := params.Entities{Entities: []params.Entity{ 416 {Tag: "unit-mysql-0"}, 417 {Tag: "unit-wordpress-0"}, 418 {Tag: "unit-foo-42"}, 419 }} 420 result, err := s.uniter.EnsureDead(args) 421 c.Assert(err, jc.ErrorIsNil) 422 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 423 Results: []params.ErrorResult{ 424 {apiservertesting.ErrUnauthorized}, 425 {nil}, 426 {apiservertesting.ErrUnauthorized}, 427 }, 428 }) 429 430 err = s.wordpressUnit.Refresh() 431 c.Assert(err, jc.ErrorIsNil) 432 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 433 err = s.mysqlUnit.Refresh() 434 c.Assert(err, jc.ErrorIsNil) 435 c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) 436 437 // Try it again on a Dead unit; should work. 438 args = params.Entities{ 439 Entities: []params.Entity{{Tag: "unit-wordpress-0"}}, 440 } 441 result, err = s.uniter.EnsureDead(args) 442 c.Assert(err, jc.ErrorIsNil) 443 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 444 Results: []params.ErrorResult{{nil}}, 445 }) 446 447 // Verify Life is unchanged. 448 err = s.wordpressUnit.Refresh() 449 c.Assert(err, jc.ErrorIsNil) 450 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 451 } 452 453 func (s *uniterSuite) TestWatch(c *gc.C) { 454 c.Assert(s.resources.Count(), gc.Equals, 0) 455 456 args := params.Entities{Entities: []params.Entity{ 457 {Tag: "unit-mysql-0"}, 458 {Tag: "unit-wordpress-0"}, 459 {Tag: "unit-foo-42"}, 460 {Tag: "application-mysql"}, 461 {Tag: "application-wordpress"}, 462 {Tag: "application-foo"}, 463 // TODO(dfc) these aren't valid tags any more 464 // but I hope to restore this test when params.Entity takes 465 // tags, not strings, which is coming soon. 466 // {Tag: "just-foo"}, 467 }} 468 result, err := s.uniter.Watch(args) 469 c.Assert(err, jc.ErrorIsNil) 470 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 471 Results: []params.NotifyWatchResult{ 472 {Error: apiservertesting.ErrUnauthorized}, 473 {NotifyWatcherId: "1"}, 474 {Error: apiservertesting.ErrUnauthorized}, 475 {Error: apiservertesting.ErrUnauthorized}, 476 {NotifyWatcherId: "2"}, 477 {Error: apiservertesting.ErrUnauthorized}, 478 // see above 479 // {Error: apiservertesting.ErrUnauthorized}, 480 }, 481 }) 482 483 // Verify the resource was registered and stop when done 484 c.Assert(s.resources.Count(), gc.Equals, 2) 485 resource1 := s.resources.Get("1") 486 defer statetesting.AssertStop(c, resource1) 487 resource2 := s.resources.Get("2") 488 defer statetesting.AssertStop(c, resource2) 489 490 // Check that the Watch has consumed the initial event ("returned" in 491 // the Watch call) 492 wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher)) 493 wc.AssertNoChange() 494 wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher)) 495 wc.AssertNoChange() 496 } 497 498 func (s *uniterSuite) TestPublicAddress(c *gc.C) { 499 // Try first without setting an address. 500 args := params.Entities{Entities: []params.Entity{ 501 {Tag: "unit-mysql-0"}, 502 {Tag: "unit-wordpress-0"}, 503 {Tag: "unit-foo-42"}, 504 }} 505 expectErr := ¶ms.Error{ 506 Code: params.CodeNoAddressSet, 507 Message: `"unit-wordpress-0" has no public address set`, 508 } 509 result, err := s.uniter.PublicAddress(args) 510 c.Assert(err, jc.ErrorIsNil) 511 c.Assert(result, gc.DeepEquals, params.StringResults{ 512 Results: []params.StringResult{ 513 {Error: apiservertesting.ErrUnauthorized}, 514 {Error: expectErr}, 515 {Error: apiservertesting.ErrUnauthorized}, 516 }, 517 }) 518 519 // Now set it an try again. 520 err = s.machine0.SetProviderAddresses( 521 network.NewScopedAddress("1.2.3.4", network.ScopePublic), 522 ) 523 c.Assert(err, jc.ErrorIsNil) 524 address, err := s.wordpressUnit.PublicAddress() 525 c.Assert(address.Value, gc.Equals, "1.2.3.4") 526 c.Assert(err, jc.ErrorIsNil) 527 528 result, err = s.uniter.PublicAddress(args) 529 c.Assert(err, jc.ErrorIsNil) 530 c.Assert(result, gc.DeepEquals, params.StringResults{ 531 Results: []params.StringResult{ 532 {Error: apiservertesting.ErrUnauthorized}, 533 {Result: "1.2.3.4"}, 534 {Error: apiservertesting.ErrUnauthorized}, 535 }, 536 }) 537 } 538 539 func (s *uniterSuite) TestPrivateAddress(c *gc.C) { 540 args := params.Entities{Entities: []params.Entity{ 541 {Tag: "unit-mysql-0"}, 542 {Tag: "unit-wordpress-0"}, 543 {Tag: "unit-foo-42"}, 544 }} 545 expectErr := ¶ms.Error{ 546 Code: params.CodeNoAddressSet, 547 Message: `"unit-wordpress-0" has no private address set`, 548 } 549 result, err := s.uniter.PrivateAddress(args) 550 c.Assert(err, jc.ErrorIsNil) 551 c.Assert(result, gc.DeepEquals, params.StringResults{ 552 Results: []params.StringResult{ 553 {Error: apiservertesting.ErrUnauthorized}, 554 {Error: expectErr}, 555 {Error: apiservertesting.ErrUnauthorized}, 556 }, 557 }) 558 559 // Now set it and try again. 560 err = s.machine0.SetProviderAddresses( 561 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 562 ) 563 c.Assert(err, jc.ErrorIsNil) 564 address, err := s.wordpressUnit.PrivateAddress() 565 c.Assert(address.Value, gc.Equals, "1.2.3.4") 566 c.Assert(err, jc.ErrorIsNil) 567 568 result, err = s.uniter.PrivateAddress(args) 569 c.Assert(err, jc.ErrorIsNil) 570 c.Assert(result, gc.DeepEquals, params.StringResults{ 571 Results: []params.StringResult{ 572 {Error: apiservertesting.ErrUnauthorized}, 573 {Result: "1.2.3.4"}, 574 {Error: apiservertesting.ErrUnauthorized}, 575 }, 576 }) 577 } 578 579 // TestNetworkInfoSpaceless is in uniterSuite and not uniterNetworkInfoSuite since we don't want 580 // all the spaces set up. 581 func (s *uniterSuite) TestNetworkInfoSpaceless(c *gc.C) { 582 err := s.machine0.SetProviderAddresses( 583 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 584 ) 585 err = s.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil) 586 c.Assert(err, jc.ErrorIsNil) 587 588 args := params.NetworkInfoParams{ 589 Unit: s.wordpressUnit.Tag().String(), 590 Bindings: []string{"db"}, 591 } 592 593 privateAddress, err := s.machine0.PrivateAddress() 594 c.Assert(err, jc.ErrorIsNil) 595 596 expectedInfo := params.NetworkInfoResult{ 597 Info: []params.NetworkInfo{ 598 { 599 Addresses: []params.InterfaceAddress{ 600 {Address: privateAddress.Value}, 601 }, 602 }, 603 }, 604 EgressSubnets: []string{"10.0.0.0/8"}, 605 IngressAddresses: []string{privateAddress.Value}, 606 } 607 608 result, err := s.uniter.NetworkInfo(args) 609 c.Assert(err, jc.ErrorIsNil) 610 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 611 Results: map[string]params.NetworkInfoResult{ 612 "db": expectedInfo, 613 }, 614 }) 615 } 616 617 func (s *uniterSuite) TestAvailabilityZone(c *gc.C) { 618 s.PatchValue(uniter.GetZone, func(st *state.State, tag names.Tag) (string, error) { 619 return "a_zone", nil 620 }) 621 622 args := params.Entities{Entities: []params.Entity{ 623 {Tag: "unit-wordpress-0"}, 624 }} 625 result, err := s.uniter.AvailabilityZone(args) 626 c.Assert(err, jc.ErrorIsNil) 627 628 c.Check(result, gc.DeepEquals, params.StringResults{ 629 Results: []params.StringResult{ 630 {Result: "a_zone"}, 631 }, 632 }) 633 } 634 635 func (s *uniterSuite) TestResolvedAPIV6(c *gc.C) { 636 err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) 637 c.Assert(err, jc.ErrorIsNil) 638 mode := s.wordpressUnit.Resolved() 639 c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) 640 641 args := params.Entities{Entities: []params.Entity{ 642 {Tag: "unit-mysql-0"}, 643 {Tag: "unit-wordpress-0"}, 644 {Tag: "unit-foo-42"}, 645 }} 646 result, err := s.uniter.Resolved(args) 647 c.Assert(err, jc.ErrorIsNil) 648 c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{ 649 Results: []params.ResolvedModeResult{ 650 {Error: apiservertesting.ErrUnauthorized}, 651 {Mode: params.ResolvedMode(mode)}, 652 {Error: apiservertesting.ErrUnauthorized}, 653 }, 654 }) 655 } 656 657 func (s *uniterSuite) TestClearResolved(c *gc.C) { 658 err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) 659 c.Assert(err, jc.ErrorIsNil) 660 mode := s.wordpressUnit.Resolved() 661 c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) 662 663 args := params.Entities{Entities: []params.Entity{ 664 {Tag: "unit-mysql-0"}, 665 {Tag: "unit-wordpress-0"}, 666 {Tag: "unit-foo-42"}, 667 }} 668 result, err := s.uniter.ClearResolved(args) 669 c.Assert(err, jc.ErrorIsNil) 670 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 671 Results: []params.ErrorResult{ 672 {apiservertesting.ErrUnauthorized}, 673 {nil}, 674 {apiservertesting.ErrUnauthorized}, 675 }, 676 }) 677 678 // Verify wordpressUnit's resolved mode has changed. 679 err = s.wordpressUnit.Refresh() 680 c.Assert(err, jc.ErrorIsNil) 681 mode = s.wordpressUnit.Resolved() 682 c.Assert(mode, gc.Equals, state.ResolvedNone) 683 } 684 685 func (s *uniterSuite) TestGetPrincipal(c *gc.C) { 686 // Add a subordinate to wordpressUnit. 687 _, _, subordinate := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 688 689 principal, ok := subordinate.PrincipalName() 690 c.Assert(principal, gc.Equals, s.wordpressUnit.Name()) 691 c.Assert(ok, jc.IsTrue) 692 693 // First try it as wordpressUnit's agent. 694 args := params.Entities{Entities: []params.Entity{ 695 {Tag: "unit-mysql-0"}, 696 {Tag: "unit-wordpress-0"}, 697 {Tag: subordinate.Tag().String()}, 698 {Tag: "unit-foo-42"}, 699 }} 700 result, err := s.uniter.GetPrincipal(args) 701 c.Assert(err, jc.ErrorIsNil) 702 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 703 Results: []params.StringBoolResult{ 704 {Error: apiservertesting.ErrUnauthorized}, 705 {Result: "", Ok: false, Error: nil}, 706 {Error: apiservertesting.ErrUnauthorized}, 707 {Error: apiservertesting.ErrUnauthorized}, 708 }, 709 }) 710 711 // Now try as subordinate's agent. 712 subAuthorizer := s.authorizer 713 subAuthorizer.Tag = subordinate.Tag() 714 subUniter, err := uniter.NewUniterAPI(facadetest.Context{ 715 State_: s.State, 716 Resources_: s.resources, 717 Auth_: subAuthorizer, 718 LeadershipChecker_: s.State.LeadershipChecker(), 719 }) 720 c.Assert(err, jc.ErrorIsNil) 721 722 result, err = subUniter.GetPrincipal(args) 723 c.Assert(err, jc.ErrorIsNil) 724 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 725 Results: []params.StringBoolResult{ 726 {Error: apiservertesting.ErrUnauthorized}, 727 {Error: apiservertesting.ErrUnauthorized}, 728 {Result: "unit-wordpress-0", Ok: true, Error: nil}, 729 {Error: apiservertesting.ErrUnauthorized}, 730 }, 731 }) 732 } 733 734 func (s *uniterSuite) TestHasSubordinates(c *gc.C) { 735 // Try first without any subordinates for wordpressUnit. 736 args := params.Entities{Entities: []params.Entity{ 737 {Tag: "unit-mysql-0"}, 738 {Tag: "unit-wordpress-0"}, 739 {Tag: "unit-logging-0"}, 740 {Tag: "unit-foo-42"}, 741 }} 742 result, err := s.uniter.HasSubordinates(args) 743 c.Assert(err, jc.ErrorIsNil) 744 c.Assert(result, gc.DeepEquals, params.BoolResults{ 745 Results: []params.BoolResult{ 746 {Error: apiservertesting.ErrUnauthorized}, 747 {Result: false}, 748 {Error: apiservertesting.ErrUnauthorized}, 749 {Error: apiservertesting.ErrUnauthorized}, 750 }, 751 }) 752 753 // Add two subordinates to wordpressUnit and try again. 754 s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 755 s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit) 756 757 result, err = s.uniter.HasSubordinates(args) 758 c.Assert(err, jc.ErrorIsNil) 759 c.Assert(result, gc.DeepEquals, params.BoolResults{ 760 Results: []params.BoolResult{ 761 {Error: apiservertesting.ErrUnauthorized}, 762 {Result: true}, 763 {Error: apiservertesting.ErrUnauthorized}, 764 {Error: apiservertesting.ErrUnauthorized}, 765 }, 766 }) 767 } 768 769 func (s *uniterSuite) TestDestroy(c *gc.C) { 770 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 771 772 args := params.Entities{Entities: []params.Entity{ 773 {Tag: "unit-mysql-0"}, 774 {Tag: "unit-wordpress-0"}, 775 {Tag: "unit-foo-42"}, 776 }} 777 result, err := s.uniter.Destroy(args) 778 c.Assert(err, jc.ErrorIsNil) 779 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 780 Results: []params.ErrorResult{ 781 {apiservertesting.ErrUnauthorized}, 782 {nil}, 783 {apiservertesting.ErrUnauthorized}, 784 }, 785 }) 786 787 // Verify wordpressUnit is destroyed and removed. 788 err = s.wordpressUnit.Refresh() 789 c.Assert(err, jc.Satisfies, errors.IsNotFound) 790 } 791 792 func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) { 793 // Add two subordinates to wordpressUnit. 794 _, _, loggingSub := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 795 _, _, monitoringSub := s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit) 796 c.Assert(loggingSub.Life(), gc.Equals, state.Alive) 797 c.Assert(monitoringSub.Life(), gc.Equals, state.Alive) 798 799 err := s.wordpressUnit.Refresh() 800 c.Assert(err, jc.ErrorIsNil) 801 subordinates := s.wordpressUnit.SubordinateNames() 802 c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"}) 803 804 args := params.Entities{Entities: []params.Entity{ 805 {Tag: "unit-mysql-0"}, 806 {Tag: "unit-wordpress-0"}, 807 {Tag: "unit-foo-42"}, 808 }} 809 result, err := s.uniter.DestroyAllSubordinates(args) 810 c.Assert(err, jc.ErrorIsNil) 811 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 812 Results: []params.ErrorResult{ 813 {apiservertesting.ErrUnauthorized}, 814 {nil}, 815 {apiservertesting.ErrUnauthorized}, 816 }, 817 }) 818 819 // Verify wordpressUnit's subordinates were destroyed. 820 err = loggingSub.Refresh() 821 c.Assert(err, jc.ErrorIsNil) 822 c.Assert(loggingSub.Life(), gc.Equals, state.Dying) 823 err = monitoringSub.Refresh() 824 c.Assert(err, jc.ErrorIsNil) 825 c.Assert(monitoringSub.Life(), gc.Equals, state.Dying) 826 } 827 828 func (s *uniterSuite) TestCharmURL(c *gc.C) { 829 // Set wordpressUnit's charm URL first. 830 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 831 c.Assert(err, jc.ErrorIsNil) 832 curl, ok := s.wordpressUnit.CharmURL() 833 c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) 834 c.Assert(ok, jc.IsTrue) 835 836 // Make sure wordpress application's charm is what we expect. 837 curl, force := s.wordpress.CharmURL() 838 c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) 839 c.Assert(force, jc.IsFalse) 840 841 args := params.Entities{Entities: []params.Entity{ 842 {Tag: "unit-mysql-0"}, 843 {Tag: "unit-wordpress-0"}, 844 {Tag: "unit-foo-42"}, 845 {Tag: "application-mysql"}, 846 {Tag: "application-wordpress"}, 847 {Tag: "application-foo"}, 848 // TODO(dfc) these aren't valid tags any more 849 // but I hope to restore this test when params.Entity takes 850 // tags, not strings, which is coming soon. 851 // {Tag: "just-foo"}, 852 }} 853 result, err := s.uniter.CharmURL(args) 854 c.Assert(err, jc.ErrorIsNil) 855 c.Assert(result, gc.DeepEquals, params.StringBoolResults{ 856 Results: []params.StringBoolResult{ 857 {Error: apiservertesting.ErrUnauthorized}, 858 {Result: s.wpCharm.String(), Ok: ok}, 859 {Error: apiservertesting.ErrUnauthorized}, 860 {Error: apiservertesting.ErrUnauthorized}, 861 {Result: s.wpCharm.String(), Ok: force}, 862 {Error: apiservertesting.ErrUnauthorized}, 863 // see above 864 // {Error: apiservertesting.ErrUnauthorized}, 865 }, 866 }) 867 } 868 869 func (s *uniterSuite) TestSetCharmURL(c *gc.C) { 870 _, ok := s.wordpressUnit.CharmURL() 871 c.Assert(ok, jc.IsFalse) 872 873 args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{ 874 {Tag: "unit-mysql-0", CharmURL: "cs:quantal/application-42"}, 875 {Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()}, 876 {Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"}, 877 }} 878 result, err := s.uniter.SetCharmURL(args) 879 c.Assert(err, jc.ErrorIsNil) 880 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 881 Results: []params.ErrorResult{ 882 {apiservertesting.ErrUnauthorized}, 883 {nil}, 884 {apiservertesting.ErrUnauthorized}, 885 }, 886 }) 887 888 // Verify the charm URL was set. 889 err = s.wordpressUnit.Refresh() 890 c.Assert(err, jc.ErrorIsNil) 891 charmURL, needsUpgrade := s.wordpressUnit.CharmURL() 892 c.Assert(charmURL, gc.NotNil) 893 c.Assert(charmURL.String(), gc.Equals, s.wpCharm.String()) 894 c.Assert(needsUpgrade, jc.IsTrue) 895 } 896 897 func (s *uniterSuite) TestWorkloadVersion(c *gc.C) { 898 // Set wordpressUnit's workload version first. 899 err := s.wordpressUnit.SetWorkloadVersion("capulet") 900 c.Assert(err, jc.ErrorIsNil) 901 version, err := s.wordpressUnit.WorkloadVersion() 902 c.Assert(version, gc.Equals, "capulet") 903 c.Assert(err, jc.ErrorIsNil) 904 905 args := params.Entities{Entities: []params.Entity{ 906 {Tag: "unit-mysql-0"}, 907 {Tag: "unit-wordpress-0"}, 908 {Tag: "unit-foo-42"}, 909 {Tag: "application-wordpress"}, 910 {Tag: "just-foo"}, 911 }} 912 913 result, err := s.uniter.WorkloadVersion(args) 914 c.Assert(err, jc.ErrorIsNil) 915 c.Assert(result, gc.DeepEquals, params.StringResults{ 916 Results: []params.StringResult{ 917 {Error: apiservertesting.ErrUnauthorized}, 918 {Result: "capulet"}, 919 {Error: apiservertesting.ErrUnauthorized}, 920 {Error: common.ServerError(errors.New(`"application-wordpress" is not a valid unit tag`))}, 921 {Error: common.ServerError(errors.New(`"just-foo" is not a valid tag`))}, 922 }, 923 }) 924 } 925 926 func (s *uniterSuite) TestSetWorkloadVersion(c *gc.C) { 927 currentVersion, err := s.wordpressUnit.WorkloadVersion() 928 c.Assert(err, jc.ErrorIsNil) 929 c.Assert(currentVersion, gc.Equals, "") 930 931 args := params.EntityWorkloadVersions{Entities: []params.EntityWorkloadVersion{ 932 {Tag: "unit-mysql-0", WorkloadVersion: "allura"}, 933 {Tag: "unit-wordpress-0", WorkloadVersion: "shiro"}, 934 {Tag: "unit-foo-42", WorkloadVersion: "pidge"}, 935 }} 936 result, err := s.uniter.SetWorkloadVersion(args) 937 c.Assert(err, jc.ErrorIsNil) 938 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 939 Results: []params.ErrorResult{ 940 {apiservertesting.ErrUnauthorized}, 941 {nil}, 942 {apiservertesting.ErrUnauthorized}, 943 }, 944 }) 945 946 // Verify the workload version was set. 947 err = s.wordpressUnit.Refresh() 948 c.Assert(err, jc.ErrorIsNil) 949 newVersion, err := s.wordpressUnit.WorkloadVersion() 950 c.Assert(err, jc.ErrorIsNil) 951 c.Assert(newVersion, gc.Equals, "shiro") 952 } 953 954 func (s *uniterSuite) TestCharmModifiedVersion(c *gc.C) { 955 args := params.Entities{Entities: []params.Entity{ 956 {Tag: "application-mysql"}, 957 {Tag: "application-wordpress"}, 958 {Tag: "unit-wordpress-0"}, 959 {Tag: "application-foo"}, 960 }} 961 result, err := s.uniter.CharmModifiedVersion(args) 962 c.Assert(err, jc.ErrorIsNil) 963 c.Assert(result, gc.DeepEquals, params.IntResults{ 964 Results: []params.IntResult{ 965 {Error: apiservertesting.ErrUnauthorized}, 966 {Result: s.wordpress.CharmModifiedVersion()}, 967 {Result: s.wordpress.CharmModifiedVersion()}, 968 {Error: apiservertesting.ErrUnauthorized}, 969 }, 970 }) 971 } 972 973 func (s *uniterSuite) TestOpenPorts(c *gc.C) { 974 openedPorts, err := s.wordpressUnit.OpenedPorts() 975 c.Assert(err, jc.ErrorIsNil) 976 c.Assert(openedPorts, gc.HasLen, 0) 977 978 args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{ 979 {Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 980 {Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000}, 981 {Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42}, 982 }} 983 result, err := s.uniter.OpenPorts(args) 984 c.Assert(err, jc.ErrorIsNil) 985 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 986 Results: []params.ErrorResult{ 987 {apiservertesting.ErrUnauthorized}, 988 {nil}, 989 {apiservertesting.ErrUnauthorized}, 990 }, 991 }) 992 993 // Verify the wordpressUnit's port is opened. 994 openedPorts, err = s.wordpressUnit.OpenedPorts() 995 c.Assert(err, jc.ErrorIsNil) 996 c.Assert(openedPorts, gc.DeepEquals, []corenetwork.PortRange{ 997 {Protocol: "udp", FromPort: 4321, ToPort: 5000}, 998 }) 999 } 1000 1001 func (s *uniterSuite) TestClosePorts(c *gc.C) { 1002 // Open port udp:4321 in advance on wordpressUnit. 1003 err := s.wordpressUnit.OpenPorts("udp", 4321, 5000) 1004 c.Assert(err, jc.ErrorIsNil) 1005 openedPorts, err := s.wordpressUnit.OpenedPorts() 1006 c.Assert(err, jc.ErrorIsNil) 1007 c.Assert(openedPorts, gc.DeepEquals, []corenetwork.PortRange{ 1008 {Protocol: "udp", FromPort: 4321, ToPort: 5000}, 1009 }) 1010 1011 args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{ 1012 {Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 1013 {Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000}, 1014 {Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42}, 1015 }} 1016 result, err := s.uniter.ClosePorts(args) 1017 c.Assert(err, jc.ErrorIsNil) 1018 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1019 Results: []params.ErrorResult{ 1020 {apiservertesting.ErrUnauthorized}, 1021 {nil}, 1022 {apiservertesting.ErrUnauthorized}, 1023 }, 1024 }) 1025 1026 // Verify the wordpressUnit's port is closed. 1027 openedPorts, err = s.wordpressUnit.OpenedPorts() 1028 c.Assert(err, jc.ErrorIsNil) 1029 c.Assert(openedPorts, gc.HasLen, 0) 1030 } 1031 1032 func (s *uniterSuite) TestWatchConfigSettingsHash(c *gc.C) { 1033 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 1034 c.Assert(err, jc.ErrorIsNil) 1035 err = s.wordpress.UpdateCharmConfig(charm.Settings{ 1036 "blog-title": "sauceror central", 1037 }) 1038 c.Assert(err, jc.ErrorIsNil) 1039 1040 c.Assert(s.resources.Count(), gc.Equals, 0) 1041 1042 args := params.Entities{Entities: []params.Entity{ 1043 {Tag: "unit-mysql-0"}, 1044 {Tag: "unit-wordpress-0"}, 1045 {Tag: "unit-foo-42"}, 1046 }} 1047 result, err := s.uniter.WatchConfigSettingsHash(args) 1048 c.Assert(err, jc.ErrorIsNil) 1049 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 1050 Results: []params.StringsWatchResult{ 1051 {Error: apiservertesting.ErrUnauthorized}, 1052 { 1053 StringsWatcherId: "1", 1054 Changes: []string{"af35e298300150f2c357b4a1c40c1109bde305841c6343113b634b9dada22d00"}, 1055 }, 1056 {Error: apiservertesting.ErrUnauthorized}, 1057 }, 1058 }) 1059 1060 // Verify the resource was registered and stop when done 1061 c.Assert(s.resources.Count(), gc.Equals, 1) 1062 resource := s.resources.Get("1") 1063 defer statetesting.AssertStop(c, resource) 1064 1065 // Check that the Watch has consumed the initial event ("returned" in 1066 // the Watch call) 1067 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1068 wc.AssertNoChange() 1069 } 1070 1071 func (s *uniterSuite) TestWatchTrustConfigSettingsHash(c *gc.C) { 1072 schema := environschema.Fields{ 1073 "trust": environschema.Attr{Type: environschema.Tbool}, 1074 } 1075 err := s.wordpress.UpdateApplicationConfig(coreapplication.ConfigAttributes{ 1076 "trust": true, 1077 }, nil, schema, nil) 1078 c.Assert(err, jc.ErrorIsNil) 1079 1080 c.Assert(s.resources.Count(), gc.Equals, 0) 1081 1082 args := params.Entities{Entities: []params.Entity{ 1083 {Tag: "unit-mysql-0"}, 1084 {Tag: "unit-wordpress-0"}, 1085 {Tag: "unit-foo-42"}, 1086 }} 1087 result, err := s.uniter.WatchTrustConfigSettingsHash(args) 1088 c.Assert(err, jc.ErrorIsNil) 1089 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 1090 Results: []params.StringsWatchResult{ 1091 {Error: apiservertesting.ErrUnauthorized}, 1092 { 1093 StringsWatcherId: "1", 1094 Changes: []string{"2f1368bde39be8106dcdca15e35cc3b5f7db5b8e429806369f621a47fb938519"}, 1095 }, 1096 {Error: apiservertesting.ErrUnauthorized}, 1097 }, 1098 }) 1099 1100 // Verify the resource was registered and stop when done 1101 c.Assert(s.resources.Count(), gc.Equals, 1) 1102 resource := s.resources.Get("1") 1103 defer statetesting.AssertStop(c, resource) 1104 1105 // Check that the Watch has consumed the initial event ("returned" in 1106 // the Watch call) 1107 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1108 wc.AssertNoChange() 1109 } 1110 1111 func (s *uniterSuite) TestWatchActionNotifications(c *gc.C) { 1112 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 1113 c.Assert(err, jc.ErrorIsNil) 1114 1115 c.Assert(s.resources.Count(), gc.Equals, 0) 1116 1117 args := params.Entities{Entities: []params.Entity{ 1118 {Tag: "unit-mysql-0"}, 1119 {Tag: "unit-wordpress-0"}, 1120 {Tag: "unit-foo-42"}, 1121 }} 1122 result, err := s.uniter.WatchActionNotifications(args) 1123 c.Assert(err, jc.ErrorIsNil) 1124 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 1125 Results: []params.StringsWatchResult{ 1126 {Error: apiservertesting.ErrUnauthorized}, 1127 {StringsWatcherId: "1"}, 1128 {Error: apiservertesting.ErrUnauthorized}, 1129 }, 1130 }) 1131 1132 // Verify the resource was registered and stop when done 1133 c.Assert(s.resources.Count(), gc.Equals, 1) 1134 resource := s.resources.Get("1") 1135 defer statetesting.AssertStop(c, resource) 1136 1137 // Check that the Watch has consumed the initial event ("returned" in 1138 // the Watch call) 1139 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1140 wc.AssertNoChange() 1141 1142 addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil) 1143 1144 wc.AssertChange(addedAction.Id()) 1145 wc.AssertNoChange() 1146 } 1147 1148 func (s *uniterSuite) TestWatchPreexistingActions(c *gc.C) { 1149 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 1150 c.Assert(err, jc.ErrorIsNil) 1151 1152 c.Assert(s.resources.Count(), gc.Equals, 0) 1153 1154 action1, err := s.wordpressUnit.AddAction("fakeaction", nil) 1155 c.Assert(err, jc.ErrorIsNil) 1156 action2, err := s.wordpressUnit.AddAction("fakeaction", nil) 1157 c.Assert(err, jc.ErrorIsNil) 1158 1159 args := params.Entities{Entities: []params.Entity{ 1160 {Tag: "unit-wordpress-0"}, 1161 }} 1162 1163 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1164 results, err := s.uniter.WatchActionNotifications(args) 1165 c.Assert(err, jc.ErrorIsNil) 1166 1167 checkUnorderedActionIdsEqual(c, []string{action1.Id(), action2.Id()}, results) 1168 1169 // Verify the resource was registered and stop when done 1170 c.Assert(s.resources.Count(), gc.Equals, 1) 1171 resource := s.resources.Get("1") 1172 defer statetesting.AssertStop(c, resource) 1173 1174 // Check that the Watch has consumed the initial event ("returned" in 1175 // the Watch call) 1176 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1177 wc.AssertNoChange() 1178 1179 addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil) 1180 c.Assert(err, jc.ErrorIsNil) 1181 wc.AssertChange(addedAction.Id()) 1182 wc.AssertNoChange() 1183 } 1184 1185 func (s *uniterSuite) TestWatchActionNotificationsMalformedTag(c *gc.C) { 1186 args := params.Entities{Entities: []params.Entity{ 1187 {Tag: "ewenit-mysql-0"}, 1188 }} 1189 results, err := s.uniter.WatchActionNotifications(args) 1190 c.Assert(err, jc.ErrorIsNil) 1191 c.Assert(results, gc.NotNil) 1192 c.Assert(len(results.Results), gc.Equals, 1) 1193 result := results.Results[0] 1194 c.Assert(result.Error, gc.NotNil) 1195 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "ewenit-mysql-0"`) 1196 } 1197 1198 func (s *uniterSuite) TestWatchActionNotificationsMalformedUnitName(c *gc.C) { 1199 args := params.Entities{Entities: []params.Entity{ 1200 {Tag: "unit-mysql-01"}, 1201 }} 1202 results, err := s.uniter.WatchActionNotifications(args) 1203 c.Assert(err, jc.ErrorIsNil) 1204 c.Assert(results, gc.NotNil) 1205 c.Assert(len(results.Results), gc.Equals, 1) 1206 result := results.Results[0] 1207 c.Assert(result.Error, gc.NotNil) 1208 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "unit-mysql-01"`) 1209 } 1210 1211 func (s *uniterSuite) TestWatchActionNotificationsNotUnit(c *gc.C) { 1212 action, err := s.mysqlUnit.AddAction("fakeaction", nil) 1213 c.Assert(err, jc.ErrorIsNil) 1214 args := params.Entities{Entities: []params.Entity{ 1215 {Tag: action.Tag().String()}, 1216 }} 1217 results, err := s.uniter.WatchActionNotifications(args) 1218 c.Assert(err, jc.ErrorIsNil) 1219 c.Assert(results, gc.NotNil) 1220 c.Assert(len(results.Results), gc.Equals, 1) 1221 result := results.Results[0] 1222 c.Assert(result.Error, gc.NotNil) 1223 c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "action-`+action.Id()+`"`) 1224 } 1225 1226 func (s *uniterSuite) TestWatchActionNotificationsPermissionDenied(c *gc.C) { 1227 args := params.Entities{Entities: []params.Entity{ 1228 {Tag: "unit-nonexistentgarbage-0"}, 1229 }} 1230 results, err := s.uniter.WatchActionNotifications(args) 1231 c.Assert(err, jc.ErrorIsNil) 1232 c.Assert(results, gc.NotNil) 1233 c.Assert(len(results.Results), gc.Equals, 1) 1234 result := results.Results[0] 1235 c.Assert(result.Error, gc.NotNil) 1236 c.Assert(result.Error.Message, gc.Equals, "permission denied") 1237 } 1238 1239 func (s *uniterSuite) TestConfigSettings(c *gc.C) { 1240 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 1241 c.Assert(err, jc.ErrorIsNil) 1242 settings, err := s.wordpressUnit.ConfigSettings() 1243 c.Assert(err, jc.ErrorIsNil) 1244 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 1245 1246 args := params.Entities{Entities: []params.Entity{ 1247 {Tag: "unit-mysql-0"}, 1248 {Tag: "unit-wordpress-0"}, 1249 {Tag: "unit-foo-42"}, 1250 }} 1251 result, err := s.uniter.ConfigSettings(args) 1252 c.Assert(err, jc.ErrorIsNil) 1253 c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{ 1254 Results: []params.ConfigSettingsResult{ 1255 {Error: apiservertesting.ErrUnauthorized}, 1256 {Settings: params.ConfigSettings{"blog-title": "My Title"}}, 1257 {Error: apiservertesting.ErrUnauthorized}, 1258 }, 1259 }) 1260 } 1261 1262 func (s *uniterSuite) TestWatchUnitRelations(c *gc.C) { 1263 c.Assert(s.resources.Count(), gc.Equals, 0) 1264 1265 args := params.Entities{Entities: []params.Entity{ 1266 {Tag: "unit-mysql-0"}, 1267 {Tag: "unit-wordpress-0"}, 1268 {Tag: "unit-foo-0"}, 1269 }} 1270 result, err := s.uniter.WatchUnitRelations(args) 1271 c.Assert(err, jc.ErrorIsNil) 1272 c.Assert(result.Results, gc.HasLen, 3) 1273 c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 1274 c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1") 1275 c.Assert(result.Results[1].Changes, gc.NotNil) 1276 c.Assert(result.Results[1].Error, gc.IsNil) 1277 c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 1278 1279 // Verify the resource was registered and stop when done 1280 c.Assert(s.resources.Count(), gc.Equals, 1) 1281 resource := s.resources.Get("1") 1282 defer statetesting.AssertStop(c, resource) 1283 1284 // Check that the Watch has consumed the initial event ("returned" in 1285 // the Watch call) 1286 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1287 wc.AssertNoChange() 1288 } 1289 1290 func (s *uniterSuite) TestWatchSubordinateUnitRelations(c *gc.C) { 1291 // The logging charm is subordinate (and the info endpoint is scope=container). 1292 loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1293 Name: "logging", 1294 URL: "cs:quantal/logging-1", 1295 }) 1296 loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1297 Name: "logging", 1298 Charm: loggingCharm, 1299 }) 1300 1301 mysqlRel := s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit) 1302 wpRel := s.makeSubordinateRelation(c, loggingApp, s.wordpress, s.wordpressUnit) 1303 mysqlLogUnit := findSubordinateUnit(c, loggingApp, s.mysqlUnit) 1304 1305 subAuthorizer := s.authorizer 1306 subAuthorizer.Tag = mysqlLogUnit.Tag() 1307 api, err := uniter.NewUniterAPI(facadetest.Context{ 1308 State_: s.State, 1309 Resources_: s.resources, 1310 Auth_: subAuthorizer, 1311 LeadershipChecker_: s.State.LeadershipChecker(), 1312 }) 1313 c.Assert(err, jc.ErrorIsNil) 1314 1315 result, err := api.WatchUnitRelations(params.Entities{ 1316 Entities: []params.Entity{{Tag: mysqlLogUnit.Tag().String()}}, 1317 }) 1318 c.Assert(err, jc.ErrorIsNil) 1319 c.Assert(result.Results, gc.HasLen, 1) 1320 c.Assert(result.Results[0].Error, gc.IsNil) 1321 c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") 1322 c.Assert(result.Results[0].Changes, gc.NotNil) 1323 1324 c.Assert(s.resources.Count(), gc.Equals, 1) 1325 resource := s.resources.Get("1") 1326 defer statetesting.AssertStop(c, resource) 1327 1328 // Check that the Watch has consumed the initial event ("returned" in 1329 // the Watch call) 1330 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1331 wc.AssertNoChange() 1332 1333 // We get notified about the mysql relation going away but not the 1334 // wordpress one. 1335 err = mysqlRel.Destroy() 1336 c.Assert(err, jc.ErrorIsNil) 1337 1338 wc.AssertChange(mysqlRel.Tag().Id()) 1339 wc.AssertNoChange() 1340 1341 err = wpRel.Destroy() 1342 c.Assert(err, jc.ErrorIsNil) 1343 wc.AssertNoChange() 1344 } 1345 1346 func (s *uniterSuite) TestWatchUnitRelationsSubordinateWithGlobalEndpoint(c *gc.C) { 1347 // A subordinate unit should still be notified about changes to 1348 // relations with applications that aren't the one this unit is 1349 // attached to if they have global scope. 1350 // The logging charm is subordinate (and the info endpoint is scope=container). 1351 loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1352 Name: "logging", 1353 URL: "cs:quantal/logging-1", 1354 }) 1355 loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1356 Name: "logging", 1357 Charm: loggingCharm, 1358 }) 1359 1360 uiCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1361 Name: "logging-frontend", 1362 URL: "cs:quantal/logging-frontend-1", 1363 }) 1364 uiApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1365 Name: "logging-frontend", 1366 Charm: uiCharm, 1367 }) 1368 1369 _ = s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit) 1370 mysqlLogUnit := findSubordinateUnit(c, loggingApp, s.mysqlUnit) 1371 1372 subAuthorizer := s.authorizer 1373 subAuthorizer.Tag = mysqlLogUnit.Tag() 1374 api, err := uniter.NewUniterAPI(facadetest.Context{ 1375 State_: s.State, 1376 Resources_: s.resources, 1377 Auth_: subAuthorizer, 1378 LeadershipChecker_: s.State.LeadershipChecker(), 1379 }) 1380 c.Assert(err, jc.ErrorIsNil) 1381 1382 result, err := api.WatchUnitRelations(params.Entities{ 1383 Entities: []params.Entity{{Tag: mysqlLogUnit.Tag().String()}}, 1384 }) 1385 c.Assert(err, jc.ErrorIsNil) 1386 c.Assert(result.Results, gc.HasLen, 1) 1387 c.Assert(result.Results[0].Error, gc.IsNil) 1388 c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") 1389 c.Assert(result.Results[0].Changes, gc.NotNil) 1390 1391 c.Assert(s.resources.Count(), gc.Equals, 1) 1392 resource := s.resources.Get("1") 1393 defer statetesting.AssertStop(c, resource) 1394 1395 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1396 wc.AssertNoChange() 1397 1398 // Should be notified about the relation to logging frontend, since it's global scope. 1399 subEndpoint, err := loggingApp.Endpoint("logging-client") 1400 c.Assert(err, jc.ErrorIsNil) 1401 uiEndpoint, err := uiApp.Endpoint("logging-client") 1402 c.Assert(err, jc.ErrorIsNil) 1403 rel := s.Factory.MakeRelation(c, &factory.RelationParams{ 1404 Endpoints: []state.Endpoint{subEndpoint, uiEndpoint}, 1405 }) 1406 1407 wc.AssertChange(rel.Tag().Id()) 1408 wc.AssertNoChange() 1409 } 1410 1411 func (s *uniterSuite) TestWatchUnitRelationsWithSubSubRelation(c *gc.C) { 1412 // We should be notified about relations to other subordinates 1413 // (since it's possible that they'll be colocated in the same 1414 // container). 1415 loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1416 Name: "logging", 1417 URL: "cs:quantal/logging-1", 1418 }) 1419 loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1420 Name: "logging", 1421 Charm: loggingCharm, 1422 }) 1423 monitoringCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1424 Name: "monitoring", 1425 URL: "cs:quantal/monitoring-1", 1426 }) 1427 monitoringApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1428 Name: "monitoring", 1429 Charm: monitoringCharm, 1430 }) 1431 1432 s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit) 1433 mysqlMonitoring := s.makeSubordinateRelation(c, monitoringApp, s.mysql, s.mysqlUnit) 1434 1435 monUnit := findSubordinateUnit(c, monitoringApp, s.mysqlUnit) 1436 1437 subAuthorizer := s.authorizer 1438 subAuthorizer.Tag = monUnit.Tag() 1439 api, err := uniter.NewUniterAPI(facadetest.Context{ 1440 State_: s.State, 1441 Resources_: s.resources, 1442 Auth_: subAuthorizer, 1443 LeadershipChecker_: s.State.LeadershipChecker(), 1444 }) 1445 c.Assert(err, jc.ErrorIsNil) 1446 1447 result, err := api.WatchUnitRelations(params.Entities{ 1448 Entities: []params.Entity{{Tag: monUnit.Tag().String()}}, 1449 }) 1450 c.Assert(err, jc.ErrorIsNil) 1451 c.Assert(result.Results, gc.HasLen, 1) 1452 c.Assert(result.Results[0].Error, gc.IsNil) 1453 c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") 1454 c.Assert(result.Results[0].Changes, gc.DeepEquals, []string{mysqlMonitoring.Tag().Id()}) 1455 1456 c.Assert(s.resources.Count(), gc.Equals, 1) 1457 resource := s.resources.Get("1") 1458 defer statetesting.AssertStop(c, resource) 1459 1460 // Check that the Watch has consumed the initial event ("returned" in 1461 // the Watch call) 1462 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 1463 wc.AssertNoChange() 1464 1465 // Now we relate logging and monitoring together. 1466 monEp, err := monitoringApp.Endpoint("info") 1467 c.Assert(err, jc.ErrorIsNil) 1468 1469 logEp, err := loggingApp.Endpoint("juju-info") 1470 c.Assert(err, jc.ErrorIsNil) 1471 rel := s.Factory.MakeRelation(c, &factory.RelationParams{ 1472 Endpoints: []state.Endpoint{monEp, logEp}, 1473 }) 1474 c.Assert(err, jc.ErrorIsNil) 1475 1476 // We should be told about the new logging-monitoring relation. 1477 wc.AssertChange(rel.Tag().Id()) 1478 wc.AssertNoChange() 1479 1480 err = rel.Destroy() 1481 c.Assert(err, jc.ErrorIsNil) 1482 1483 wc.AssertChange(rel.Tag().Id()) 1484 wc.AssertNoChange() 1485 } 1486 1487 func (s *uniterSuite) makeSubordinateRelation(c *gc.C, subApp, principalApp *state.Application, principalUnit *state.Unit) *state.Relation { 1488 subEndpoint, err := subApp.Endpoint("info") 1489 c.Assert(err, jc.ErrorIsNil) 1490 1491 principalEndpoint, err := principalApp.Endpoint("juju-info") 1492 c.Assert(err, jc.ErrorIsNil) 1493 rel := s.Factory.MakeRelation(c, &factory.RelationParams{ 1494 Endpoints: []state.Endpoint{subEndpoint, principalEndpoint}, 1495 }) 1496 c.Assert(err, jc.ErrorIsNil) 1497 // Trigger the creation of the subordinate unit by entering scope 1498 // on the principal unit. 1499 ru, err := rel.Unit(principalUnit) 1500 c.Assert(err, jc.ErrorIsNil) 1501 err = ru.EnterScope(nil) 1502 c.Assert(err, jc.ErrorIsNil) 1503 1504 return rel 1505 } 1506 1507 func findSubordinateUnit(c *gc.C, subApp *state.Application, principalUnit *state.Unit) *state.Unit { 1508 subUnits, err := subApp.AllUnits() 1509 c.Assert(err, jc.ErrorIsNil) 1510 for _, subUnit := range subUnits { 1511 principal, ok := subUnit.PrincipalName() 1512 c.Assert(ok, jc.IsTrue) 1513 if principal == principalUnit.Name() { 1514 return subUnit 1515 } 1516 } 1517 c.Fatalf("couldn't find subordinate unit for %q", principalUnit.Name()) 1518 return nil 1519 } 1520 1521 func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) { 1522 dummyCharm := s.AddTestingCharm(c, "dummy") 1523 1524 args := params.CharmURLs{URLs: []params.CharmURL{ 1525 {URL: "something-invalid"}, 1526 {URL: s.wpCharm.String()}, 1527 {URL: dummyCharm.String()}, 1528 }} 1529 result, err := s.uniter.CharmArchiveSha256(args) 1530 c.Assert(err, jc.ErrorIsNil) 1531 c.Assert(result, gc.DeepEquals, params.StringResults{ 1532 Results: []params.StringResult{ 1533 {Error: apiservertesting.ErrUnauthorized}, 1534 {Result: s.wpCharm.BundleSha256()}, 1535 {Result: dummyCharm.BundleSha256()}, 1536 }, 1537 }) 1538 } 1539 1540 func (s *uniterSuite) TestCurrentModel(c *gc.C) { 1541 model, err := s.State.Model() 1542 c.Assert(err, jc.ErrorIsNil) 1543 1544 result, err := s.uniter.CurrentModel() 1545 c.Assert(err, jc.ErrorIsNil) 1546 expected := params.ModelResult{ 1547 Name: model.Name(), 1548 UUID: model.UUID(), 1549 Type: "iaas", 1550 } 1551 c.Assert(result, gc.DeepEquals, expected) 1552 } 1553 1554 func (s *uniterSuite) TestActions(c *gc.C) { 1555 var actionTests = []struct { 1556 description string 1557 action params.ActionResult 1558 }{{ 1559 description: "A simple action.", 1560 action: params.ActionResult{ 1561 Action: ¶ms.Action{ 1562 Name: "fakeaction", 1563 Parameters: map[string]interface{}{ 1564 "outfile": "foo.txt", 1565 }}, 1566 }, 1567 }, { 1568 description: "An action with nested parameters.", 1569 action: params.ActionResult{ 1570 Action: ¶ms.Action{ 1571 Name: "fakeaction", 1572 Parameters: map[string]interface{}{ 1573 "outfile": "foo.bz2", 1574 "compression": map[string]interface{}{ 1575 "kind": "bzip", 1576 "quality": 5, 1577 }, 1578 }}, 1579 }, 1580 }} 1581 1582 for i, actionTest := range actionTests { 1583 c.Logf("test %d: %s", i, actionTest.description) 1584 1585 a, err := s.wordpressUnit.AddAction( 1586 actionTest.action.Action.Name, 1587 actionTest.action.Action.Parameters) 1588 c.Assert(err, jc.ErrorIsNil) 1589 c.Assert(names.IsValidAction(a.Id()), gc.Equals, true) 1590 actionTag := names.NewActionTag(a.Id()) 1591 c.Assert(a.ActionTag(), gc.Equals, actionTag) 1592 1593 args := params.Entities{ 1594 Entities: []params.Entity{{ 1595 Tag: actionTag.String(), 1596 }}, 1597 } 1598 results, err := s.uniter.Actions(args) 1599 c.Assert(err, jc.ErrorIsNil) 1600 c.Assert(results.Results, gc.HasLen, 1) 1601 1602 actionsQueryResult := results.Results[0] 1603 1604 c.Assert(actionsQueryResult, jc.DeepEquals, actionTest.action) 1605 } 1606 } 1607 1608 func (s *uniterSuite) TestActionsNotPresent(c *gc.C) { 1609 uuid, err := utils.NewUUID() 1610 c.Assert(err, jc.ErrorIsNil) 1611 args := params.Entities{ 1612 Entities: []params.Entity{{ 1613 Tag: names.NewActionTag(uuid.String()).String(), 1614 }}, 1615 } 1616 results, err := s.uniter.Actions(args) 1617 c.Assert(err, jc.ErrorIsNil) 1618 1619 c.Assert(results.Results, gc.HasLen, 1) 1620 actionsQueryResult := results.Results[0] 1621 c.Assert(actionsQueryResult.Error, gc.NotNil) 1622 c.Assert(actionsQueryResult.Error, gc.ErrorMatches, `action "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}" not found`) 1623 } 1624 1625 func (s *uniterSuite) TestActionsWrongUnit(c *gc.C) { 1626 // Action doesn't match unit. 1627 mysqlUnitAuthorizer := apiservertesting.FakeAuthorizer{ 1628 Tag: s.mysqlUnit.Tag(), 1629 } 1630 mysqlUnitFacade, err := uniter.NewUniterAPI(facadetest.Context{ 1631 State_: s.State, 1632 Resources_: s.resources, 1633 Auth_: mysqlUnitAuthorizer, 1634 LeadershipChecker_: s.State.LeadershipChecker(), 1635 }) 1636 c.Assert(err, jc.ErrorIsNil) 1637 1638 action, err := s.wordpressUnit.AddAction("fakeaction", nil) 1639 c.Assert(err, jc.ErrorIsNil) 1640 args := params.Entities{ 1641 Entities: []params.Entity{{ 1642 Tag: action.Tag().String(), 1643 }}, 1644 } 1645 actions, err := mysqlUnitFacade.Actions(args) 1646 c.Assert(err, jc.ErrorIsNil) 1647 c.Assert(len(actions.Results), gc.Equals, 1) 1648 c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized) 1649 } 1650 1651 func (s *uniterSuite) TestActionsPermissionDenied(c *gc.C) { 1652 action, err := s.mysqlUnit.AddAction("fakeaction", nil) 1653 c.Assert(err, jc.ErrorIsNil) 1654 args := params.Entities{ 1655 Entities: []params.Entity{{ 1656 Tag: action.Tag().String(), 1657 }}, 1658 } 1659 actions, err := s.uniter.Actions(args) 1660 c.Assert(err, jc.ErrorIsNil) 1661 c.Assert(len(actions.Results), gc.Equals, 1) 1662 c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized) 1663 } 1664 1665 func (s *uniterSuite) TestFinishActionsSuccess(c *gc.C) { 1666 testName := "fakeaction" 1667 testOutput := map[string]interface{}{"output": "completed fakeaction successfully"} 1668 1669 results, err := s.wordpressUnit.CompletedActions() 1670 c.Assert(err, jc.ErrorIsNil) 1671 c.Assert(results, gc.DeepEquals, ([]state.Action)(nil)) 1672 1673 action, err := s.wordpressUnit.AddAction(testName, nil) 1674 c.Assert(err, jc.ErrorIsNil) 1675 1676 actionResults := params.ActionExecutionResults{ 1677 Results: []params.ActionExecutionResult{{ 1678 ActionTag: action.ActionTag().String(), 1679 Status: params.ActionCompleted, 1680 Results: testOutput, 1681 }}, 1682 } 1683 res, err := s.uniter.FinishActions(actionResults) 1684 c.Assert(err, jc.ErrorIsNil) 1685 c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}}) 1686 1687 results, err = s.wordpressUnit.CompletedActions() 1688 c.Assert(err, jc.ErrorIsNil) 1689 c.Assert(len(results), gc.Equals, 1) 1690 c.Assert(results[0].Status(), gc.Equals, state.ActionCompleted) 1691 res2, errstr := results[0].Results() 1692 c.Assert(errstr, gc.Equals, "") 1693 c.Assert(res2, gc.DeepEquals, testOutput) 1694 c.Assert(results[0].Name(), gc.Equals, testName) 1695 } 1696 1697 func (s *uniterSuite) TestFinishActionsFailure(c *gc.C) { 1698 testName := "fakeaction" 1699 testError := "fakeaction was a dismal failure" 1700 1701 results, err := s.wordpressUnit.CompletedActions() 1702 c.Assert(err, jc.ErrorIsNil) 1703 c.Assert(results, gc.DeepEquals, ([]state.Action)(nil)) 1704 1705 action, err := s.wordpressUnit.AddAction(testName, nil) 1706 c.Assert(err, jc.ErrorIsNil) 1707 1708 actionResults := params.ActionExecutionResults{ 1709 Results: []params.ActionExecutionResult{{ 1710 ActionTag: action.ActionTag().String(), 1711 Status: params.ActionFailed, 1712 Results: nil, 1713 Message: testError, 1714 }}, 1715 } 1716 res, err := s.uniter.FinishActions(actionResults) 1717 c.Assert(err, jc.ErrorIsNil) 1718 c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}}) 1719 1720 results, err = s.wordpressUnit.CompletedActions() 1721 c.Assert(err, jc.ErrorIsNil) 1722 c.Assert(len(results), gc.Equals, 1) 1723 c.Assert(results[0].Status(), gc.Equals, state.ActionFailed) 1724 res2, errstr := results[0].Results() 1725 c.Assert(errstr, gc.Equals, testError) 1726 c.Assert(res2, gc.DeepEquals, map[string]interface{}{}) 1727 c.Assert(results[0].Name(), gc.Equals, testName) 1728 } 1729 1730 func (s *uniterSuite) TestFinishActionsAuthAccess(c *gc.C) { 1731 good, err := s.wordpressUnit.AddAction("fakeaction", nil) 1732 c.Assert(err, jc.ErrorIsNil) 1733 1734 bad, err := s.mysqlUnit.AddAction("fakeaction", nil) 1735 c.Assert(err, jc.ErrorIsNil) 1736 1737 var tests = []struct { 1738 actionTag names.ActionTag 1739 err error 1740 }{ 1741 {actionTag: good.ActionTag(), err: nil}, 1742 {actionTag: bad.ActionTag(), err: common.ErrPerm}, 1743 } 1744 1745 // Queue up actions from tests 1746 actionResults := params.ActionExecutionResults{Results: make([]params.ActionExecutionResult, len(tests))} 1747 for i, test := range tests { 1748 actionResults.Results[i] = params.ActionExecutionResult{ 1749 ActionTag: test.actionTag.String(), 1750 Status: params.ActionCompleted, 1751 Results: map[string]interface{}{}, 1752 } 1753 } 1754 1755 // Invoke FinishActions 1756 res, err := s.uniter.FinishActions(actionResults) 1757 c.Assert(err, jc.ErrorIsNil) 1758 1759 // Verify permissions errors for actions queued on different unit 1760 for i, result := range res.Results { 1761 expected := tests[i].err 1762 if expected != nil { 1763 c.Assert(result.Error, gc.NotNil) 1764 c.Assert(result.Error.Error(), gc.Equals, expected.Error()) 1765 } else { 1766 c.Assert(result.Error, gc.IsNil) 1767 } 1768 } 1769 } 1770 1771 func (s *uniterSuite) TestBeginActions(c *gc.C) { 1772 ten_seconds_ago := time.Now().Add(-10 * time.Second) 1773 good, err := s.wordpressUnit.AddAction("fakeaction", nil) 1774 c.Assert(err, jc.ErrorIsNil) 1775 1776 running, err := s.wordpressUnit.RunningActions() 1777 c.Assert(err, jc.ErrorIsNil) 1778 c.Assert(len(running), gc.Equals, 0, gc.Commentf("expected no running actions, got %d", len(running))) 1779 1780 args := params.Entities{Entities: []params.Entity{{Tag: good.ActionTag().String()}}} 1781 res, err := s.uniter.BeginActions(args) 1782 c.Assert(err, jc.ErrorIsNil) 1783 c.Assert(len(res.Results), gc.Equals, 1) 1784 c.Assert(res.Results[0].Error, gc.IsNil) 1785 1786 running, err = s.wordpressUnit.RunningActions() 1787 c.Assert(err, jc.ErrorIsNil) 1788 c.Assert(len(running), gc.Equals, 1, gc.Commentf("expected one running action, got %d", len(running))) 1789 c.Assert(running[0].ActionTag(), gc.Equals, good.ActionTag()) 1790 enqueued, started := running[0].Enqueued(), running[0].Started() 1791 c.Assert(ten_seconds_ago.Before(enqueued), jc.IsTrue, gc.Commentf("enqueued time should be after 10 seconds ago")) 1792 c.Assert(ten_seconds_ago.Before(started), jc.IsTrue, gc.Commentf("started time should be after 10 seconds ago")) 1793 c.Assert(started.After(enqueued) || started.Equal(enqueued), jc.IsTrue, gc.Commentf("started should be after or equal to enqueued time")) 1794 } 1795 1796 func (s *uniterSuite) TestRelation(c *gc.C) { 1797 rel := s.addRelation(c, "wordpress", "mysql") 1798 wpEp, err := rel.Endpoint("wordpress") 1799 c.Assert(err, jc.ErrorIsNil) 1800 1801 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1802 {Relation: "relation-42", Unit: "unit-foo-0"}, 1803 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1804 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1805 {Relation: rel.Tag().String(), Unit: "unit-foo-0"}, 1806 {Relation: "relation-blah", Unit: "unit-wordpress-0"}, 1807 {Relation: "application-foo", Unit: "user-foo"}, 1808 {Relation: "foo", Unit: "bar"}, 1809 {Relation: "unit-wordpress-0", Unit: rel.Tag().String()}, 1810 }} 1811 result, err := s.uniter.Relation(args) 1812 c.Assert(err, jc.ErrorIsNil) 1813 c.Assert(result, gc.DeepEquals, params.RelationResults{ 1814 Results: []params.RelationResult{ 1815 {Error: apiservertesting.ErrUnauthorized}, 1816 { 1817 Id: rel.Id(), 1818 Key: rel.String(), 1819 Life: params.Life(rel.Life().String()), 1820 Suspended: rel.Suspended(), 1821 Endpoint: multiwatcher.Endpoint{ 1822 ApplicationName: wpEp.ApplicationName, 1823 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 1824 }, 1825 OtherApplication: s.mysql.Name(), 1826 }, 1827 {Error: apiservertesting.ErrUnauthorized}, 1828 {Error: apiservertesting.ErrUnauthorized}, 1829 {Error: apiservertesting.ErrUnauthorized}, 1830 {Error: apiservertesting.ErrUnauthorized}, 1831 {Error: apiservertesting.ErrUnauthorized}, 1832 {Error: apiservertesting.ErrUnauthorized}, 1833 }, 1834 }) 1835 } 1836 1837 func (s *uniterSuite) TestRelationById(c *gc.C) { 1838 rel := s.addRelation(c, "wordpress", "mysql") 1839 c.Assert(rel.Id(), gc.Equals, 0) 1840 wpEp, err := rel.Endpoint("wordpress") 1841 c.Assert(err, jc.ErrorIsNil) 1842 1843 // Add another relation to mysql application, so we can see we can't 1844 // get it. 1845 otherRel, _, _ := s.addRelatedApplication(c, "mysql", "logging", s.mysqlUnit) 1846 1847 args := params.RelationIds{ 1848 RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234}, 1849 } 1850 result, err := s.uniter.RelationById(args) 1851 c.Assert(err, jc.ErrorIsNil) 1852 c.Assert(result, gc.DeepEquals, params.RelationResults{ 1853 Results: []params.RelationResult{ 1854 {Error: apiservertesting.ErrUnauthorized}, 1855 { 1856 Id: rel.Id(), 1857 Key: rel.String(), 1858 Life: params.Life(rel.Life().String()), 1859 Suspended: rel.Suspended(), 1860 Endpoint: multiwatcher.Endpoint{ 1861 ApplicationName: wpEp.ApplicationName, 1862 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 1863 }, 1864 OtherApplication: s.mysql.Name(), 1865 }, 1866 {Error: apiservertesting.ErrUnauthorized}, 1867 {Error: apiservertesting.ErrUnauthorized}, 1868 {Error: apiservertesting.ErrUnauthorized}, 1869 }, 1870 }) 1871 } 1872 1873 func (s *uniterSuite) TestProviderType(c *gc.C) { 1874 cfg, err := s.Model.ModelConfig() 1875 c.Assert(err, jc.ErrorIsNil) 1876 1877 result, err := s.uniter.ProviderType() 1878 c.Assert(err, jc.ErrorIsNil) 1879 c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()}) 1880 } 1881 1882 func (s *uniterSuite) TestEnterScope(c *gc.C) { 1883 // Set wordpressUnit's private address first. 1884 err := s.machine0.SetProviderAddresses( 1885 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 1886 ) 1887 c.Assert(err, jc.ErrorIsNil) 1888 1889 rel := s.addRelation(c, "wordpress", "mysql") 1890 relUnit, err := rel.Unit(s.wordpressUnit) 1891 c.Assert(err, jc.ErrorIsNil) 1892 s.assertInScope(c, relUnit, false) 1893 1894 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 1895 {Relation: "relation-42", Unit: "unit-foo-0"}, 1896 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1897 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 1898 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 1899 {Relation: "relation-foo", Unit: "unit-wordpress-0"}, 1900 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 1901 {Relation: "foo", Unit: "bar"}, 1902 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 1903 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 1904 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 1905 {Relation: rel.Tag().String(), Unit: "user-foo"}, 1906 }} 1907 result, err := s.uniter.EnterScope(args) 1908 c.Assert(err, jc.ErrorIsNil) 1909 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 1910 Results: []params.ErrorResult{ 1911 {apiservertesting.ErrUnauthorized}, 1912 {nil}, 1913 {nil}, 1914 {apiservertesting.ErrUnauthorized}, 1915 {apiservertesting.ErrUnauthorized}, 1916 {apiservertesting.ErrUnauthorized}, 1917 {apiservertesting.ErrUnauthorized}, 1918 {apiservertesting.ErrUnauthorized}, 1919 {apiservertesting.ErrUnauthorized}, 1920 {apiservertesting.ErrUnauthorized}, 1921 {apiservertesting.ErrUnauthorized}, 1922 }, 1923 }) 1924 1925 // Verify the scope changes and settings. 1926 s.assertInScope(c, relUnit, true) 1927 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 1928 c.Assert(err, jc.ErrorIsNil) 1929 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 1930 "private-address": "1.2.3.4", 1931 "ingress-address": "1.2.3.4", 1932 "egress-subnets": "1.2.3.4/32", 1933 }) 1934 } 1935 1936 func (s *uniterSuite) TestEnterScopeIgnoredForInvalidPrincipals(c *gc.C) { 1937 loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 1938 Name: "logging", 1939 URL: "cs:quantal/logging-1", 1940 }) 1941 logging := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1942 Name: "logging", 1943 Charm: loggingCharm, 1944 }) 1945 mysqlRel := s.addRelation(c, "logging", "mysql") 1946 wpRel := s.addRelation(c, "logging", "wordpress") 1947 1948 // Create logging units for each of the mysql and wp units. 1949 mysqlRU, err := mysqlRel.Unit(s.mysqlUnit) 1950 c.Assert(err, jc.ErrorIsNil) 1951 err = mysqlRU.EnterScope(nil) 1952 c.Assert(err, jc.ErrorIsNil) 1953 mysqlLoggingU := findSubordinateUnit(c, logging, s.mysqlUnit) 1954 mysqlLoggingRU, err := mysqlRel.Unit(mysqlLoggingU) 1955 c.Assert(err, jc.ErrorIsNil) 1956 err = mysqlLoggingRU.EnterScope(nil) 1957 c.Assert(err, jc.ErrorIsNil) 1958 1959 wpRU, err := wpRel.Unit(s.wordpressUnit) 1960 c.Assert(err, jc.ErrorIsNil) 1961 err = wpRU.EnterScope(nil) 1962 c.Assert(err, jc.ErrorIsNil) 1963 wpLoggingU := findSubordinateUnit(c, logging, s.wordpressUnit) 1964 _, err = wpRel.Unit(wpLoggingU) 1965 c.Assert(err, jc.ErrorIsNil) 1966 1967 // Sanity check - a mysqlRel RU for wpLoggingU is invalid. 1968 ru, err := mysqlRel.Unit(wpLoggingU) 1969 c.Assert(err, jc.ErrorIsNil) 1970 valid, err := ru.Valid() 1971 c.Assert(err, jc.ErrorIsNil) 1972 c.Assert(valid, jc.IsFalse) 1973 1974 subAuthorizer := s.authorizer 1975 subAuthorizer.Tag = wpLoggingU.Tag() 1976 api, err := uniter.NewUniterAPI(facadetest.Context{ 1977 State_: s.State, 1978 Resources_: s.resources, 1979 Auth_: subAuthorizer, 1980 LeadershipChecker_: s.State.LeadershipChecker(), 1981 }) 1982 c.Assert(err, jc.ErrorIsNil) 1983 1984 // Count how many relationscopes records there are beforehand. 1985 scopesBefore := countRelationScopes(c, s.State, mysqlRel) 1986 // One for each unit of mysql and the logging subordinate. 1987 c.Assert(scopesBefore, gc.Equals, 2) 1988 1989 // Asking the API to add wpLoggingU to mysqlRel silently 1990 // fails. This means that we'll drop incorrect requests from 1991 // uniters to re-enter the relation scope after the upgrade step 1992 // has cleaned them up. 1993 // See https://bugs.launchpad.net/juju/+bug/1699050 1994 args := params.RelationUnits{RelationUnits: []params.RelationUnit{{ 1995 Relation: mysqlRel.Tag().String(), 1996 Unit: wpLoggingU.Tag().String(), 1997 }}} 1998 result, err := api.EnterScope(args) 1999 c.Assert(err, jc.ErrorIsNil) 2000 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2001 Results: []params.ErrorResult{{Error: nil}}, 2002 }) 2003 2004 scopesAfter := countRelationScopes(c, s.State, mysqlRel) 2005 c.Assert(scopesAfter, gc.Equals, scopesBefore) 2006 } 2007 2008 func countRelationScopes(c *gc.C, st *state.State, rel *state.Relation) int { 2009 coll := st.MongoSession().DB("juju").C("relationscopes") 2010 count, err := coll.Find(bson.M{"key": bson.M{ 2011 "$regex": fmt.Sprintf(`^r#%d#`, rel.Id()), 2012 }}).Count() 2013 c.Assert(err, jc.ErrorIsNil) 2014 return count 2015 } 2016 2017 func (s *uniterSuite) TestLeaveScope(c *gc.C) { 2018 rel := s.addRelation(c, "wordpress", "mysql") 2019 relUnit, err := rel.Unit(s.wordpressUnit) 2020 c.Assert(err, jc.ErrorIsNil) 2021 settings := map[string]interface{}{ 2022 "some": "settings", 2023 } 2024 err = relUnit.EnterScope(settings) 2025 c.Assert(err, jc.ErrorIsNil) 2026 s.assertInScope(c, relUnit, true) 2027 2028 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2029 {Relation: "relation-42", Unit: "unit-foo-0"}, 2030 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 2031 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 2032 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 2033 {Relation: "relation-foo", Unit: "unit-wordpress-0"}, 2034 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 2035 {Relation: "foo", Unit: "bar"}, 2036 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 2037 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 2038 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 2039 {Relation: rel.Tag().String(), Unit: "user-foo"}, 2040 }} 2041 result, err := s.uniter.LeaveScope(args) 2042 c.Assert(err, jc.ErrorIsNil) 2043 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2044 Results: []params.ErrorResult{ 2045 {apiservertesting.ErrUnauthorized}, 2046 {nil}, 2047 {nil}, 2048 {apiservertesting.ErrUnauthorized}, 2049 {apiservertesting.ErrUnauthorized}, 2050 {apiservertesting.ErrUnauthorized}, 2051 {apiservertesting.ErrUnauthorized}, 2052 {apiservertesting.ErrUnauthorized}, 2053 {apiservertesting.ErrUnauthorized}, 2054 {apiservertesting.ErrUnauthorized}, 2055 {apiservertesting.ErrUnauthorized}, 2056 }, 2057 }) 2058 2059 // Verify the scope changes. 2060 s.assertInScope(c, relUnit, false) 2061 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 2062 c.Assert(err, jc.ErrorIsNil) 2063 c.Assert(readSettings, gc.DeepEquals, settings) 2064 } 2065 2066 func (s *uniterSuite) TestRelationsSuspended(c *gc.C) { 2067 rel := s.addRelation(c, "wordpress", "mysql") 2068 relUnit, err := rel.Unit(s.wordpressUnit) 2069 c.Assert(err, jc.ErrorIsNil) 2070 err = relUnit.EnterScope(nil) 2071 c.Assert(err, jc.ErrorIsNil) 2072 2073 s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 2074 rel2 := s.addRelation(c, "wordpress", "logging") 2075 err = rel2.SetSuspended(true, "") 2076 c.Assert(err, jc.ErrorIsNil) 2077 2078 args := params.Entities{ 2079 Entities: []params.Entity{ 2080 {s.wordpressUnit.Tag().String()}, 2081 {s.mysqlUnit.Tag().String()}, 2082 {"unit-unknown-1"}, 2083 {"application-wordpress"}, 2084 {"machine-0"}, 2085 {rel.Tag().String()}, 2086 }, 2087 } 2088 expect := params.RelationUnitStatusResults{ 2089 Results: []params.RelationUnitStatusResult{ 2090 {RelationResults: []params.RelationUnitStatus{{ 2091 RelationTag: rel.Tag().String(), 2092 InScope: true, 2093 Suspended: false, 2094 }, { 2095 RelationTag: rel2.Tag().String(), 2096 InScope: false, 2097 Suspended: true, 2098 }}, 2099 }, 2100 {Error: apiservertesting.ErrUnauthorized}, 2101 {Error: apiservertesting.ErrUnauthorized}, 2102 {Error: apiservertesting.ErrUnauthorized}, 2103 {Error: apiservertesting.ErrUnauthorized}, 2104 {Error: apiservertesting.ErrUnauthorized}, 2105 }, 2106 } 2107 check := func() { 2108 result, err := s.uniter.RelationsStatus(args) 2109 c.Assert(err, jc.ErrorIsNil) 2110 c.Assert(result, gc.DeepEquals, expect) 2111 } 2112 check() 2113 err = relUnit.PrepareLeaveScope() 2114 c.Assert(err, jc.ErrorIsNil) 2115 check() 2116 } 2117 2118 func (s *uniterSuite) TestSetRelationsStatusNotLeader(c *gc.C) { 2119 rel := s.addRelation(c, "wordpress", "mysql") 2120 relUnit, err := rel.Unit(s.wordpressUnit) 2121 c.Assert(err, jc.ErrorIsNil) 2122 err = relUnit.EnterScope(nil) 2123 c.Assert(err, jc.ErrorIsNil) 2124 2125 args := params.RelationStatusArgs{ 2126 Args: []params.RelationStatusArg{ 2127 {s.wordpressUnit.Tag().String(), rel.Id(), params.Suspended, "message"}, 2128 }, 2129 } 2130 result, err := s.uniter.SetRelationStatus(args) 2131 c.Assert(err, jc.ErrorIsNil) 2132 c.Assert(result.OneError(), gc.ErrorMatches, `"wordpress/0" is not leader of "wordpress"`) 2133 } 2134 2135 func (s *uniterSuite) TestSetRelationsStatusLeader(c *gc.C) { 2136 rel := s.addRelation(c, "wordpress", "mysql") 2137 err := rel.SetStatus(status.StatusInfo{Status: status.Suspending, Message: "going, going"}) 2138 c.Assert(err, jc.ErrorIsNil) 2139 relUnit, err := rel.Unit(s.wordpressUnit) 2140 c.Assert(err, jc.ErrorIsNil) 2141 err = relUnit.EnterScope(nil) 2142 c.Assert(err, jc.ErrorIsNil) 2143 2144 s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 2145 rel2 := s.addRelation(c, "wordpress", "logging") 2146 err = rel2.SetSuspended(true, "") 2147 c.Assert(err, jc.ErrorIsNil) 2148 err = rel.SetStatus(status.StatusInfo{Status: status.Suspending, Message: ""}) 2149 c.Assert(err, jc.ErrorIsNil) 2150 2151 s.AddTestingApplication(c, "wp2", s.wpCharm) 2152 rel3 := s.addRelation(c, "wp2", "logging") 2153 c.Assert(err, jc.ErrorIsNil) 2154 2155 args := params.RelationStatusArgs{ 2156 Args: []params.RelationStatusArg{ 2157 {s.wordpressUnit.Tag().String(), rel.Id(), params.Suspended, "message"}, 2158 // This arg omits the explicit unit tag to test older servers. 2159 {RelationId: rel2.Id(), Status: params.Suspended, Message: "gone"}, 2160 {s.wordpressUnit.Tag().String(), rel3.Id(), params.Broken, ""}, 2161 {RelationId: 4}, 2162 }, 2163 } 2164 expect := params.ErrorResults{ 2165 Results: []params.ErrorResult{ 2166 {}, 2167 {}, 2168 {Error: apiservertesting.ErrUnauthorized}, 2169 {Error: apiservertesting.ErrUnauthorized}, 2170 }, 2171 } 2172 check := func(rel *state.Relation, expectedStatus status.Status, expectedMessage string) { 2173 err = rel.Refresh() 2174 c.Assert(err, jc.ErrorIsNil) 2175 relStatus, err := rel.Status() 2176 c.Assert(err, jc.ErrorIsNil) 2177 c.Assert(relStatus.Status, gc.Equals, expectedStatus) 2178 c.Assert(relStatus.Message, gc.Equals, expectedMessage) 2179 } 2180 2181 err = s.State.LeadershipClaimer().ClaimLeadership("wordpress", "wordpress/0", time.Minute) 2182 c.Assert(err, jc.ErrorIsNil) 2183 2184 result, err := s.uniter.SetRelationStatus(args) 2185 c.Assert(err, jc.ErrorIsNil) 2186 c.Assert(result, gc.DeepEquals, expect) 2187 check(rel, status.Suspended, "message") 2188 check(rel2, status.Suspended, "gone") 2189 } 2190 2191 func (s *uniterSuite) TestReadSettings(c *gc.C) { 2192 rel := s.addRelation(c, "wordpress", "mysql") 2193 relUnit, err := rel.Unit(s.wordpressUnit) 2194 c.Assert(err, jc.ErrorIsNil) 2195 settings := map[string]interface{}{ 2196 "some": "settings", 2197 } 2198 err = relUnit.EnterScope(settings) 2199 c.Assert(err, jc.ErrorIsNil) 2200 s.assertInScope(c, relUnit, true) 2201 2202 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2203 {Relation: "relation-42", Unit: "unit-foo-0"}, 2204 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 2205 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 2206 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 2207 {Relation: "relation-foo", Unit: ""}, 2208 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 2209 {Relation: "foo", Unit: "bar"}, 2210 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 2211 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 2212 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 2213 {Relation: rel.Tag().String(), Unit: "user-foo"}, 2214 }} 2215 result, err := s.uniter.ReadSettings(args) 2216 c.Assert(err, jc.ErrorIsNil) 2217 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 2218 Results: []params.SettingsResult{ 2219 {Error: apiservertesting.ErrUnauthorized}, 2220 {Settings: params.Settings{ 2221 "some": "settings", 2222 }}, 2223 {Error: apiservertesting.ErrUnauthorized}, 2224 {Error: apiservertesting.ErrUnauthorized}, 2225 {Error: apiservertesting.ErrUnauthorized}, 2226 {Error: apiservertesting.ErrUnauthorized}, 2227 {Error: apiservertesting.ErrUnauthorized}, 2228 {Error: apiservertesting.ErrUnauthorized}, 2229 {Error: apiservertesting.ErrUnauthorized}, 2230 {Error: apiservertesting.ErrUnauthorized}, 2231 {Error: apiservertesting.ErrUnauthorized}, 2232 }, 2233 }) 2234 } 2235 2236 func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) { 2237 rel := s.addRelation(c, "wordpress", "mysql") 2238 relUnit, err := rel.Unit(s.wordpressUnit) 2239 c.Assert(err, jc.ErrorIsNil) 2240 settings := map[string]interface{}{ 2241 "other": "things", 2242 "invalid-bool": false, 2243 } 2244 err = relUnit.EnterScope(settings) 2245 c.Assert(err, jc.ErrorIsNil) 2246 s.assertInScope(c, relUnit, true) 2247 2248 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2249 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 2250 }} 2251 expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` 2252 result, err := s.uniter.ReadSettings(args) 2253 c.Assert(err, jc.ErrorIsNil) 2254 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 2255 Results: []params.SettingsResult{ 2256 {Error: ¶ms.Error{Message: expectErr}}, 2257 }, 2258 }) 2259 } 2260 2261 func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) { 2262 rel := s.addRelation(c, "wordpress", "mysql") 2263 relUnit, err := rel.Unit(s.wordpressUnit) 2264 c.Assert(err, jc.ErrorIsNil) 2265 settings := map[string]interface{}{ 2266 "some": "settings", 2267 } 2268 err = relUnit.EnterScope(settings) 2269 c.Assert(err, jc.ErrorIsNil) 2270 s.assertInScope(c, relUnit, true) 2271 2272 // First test most of the invalid args tests and try to read the 2273 // (unset) remote unit settings. 2274 args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{ 2275 {Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"}, 2276 {Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"}, 2277 {Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"}, 2278 {Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""}, 2279 {Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""}, 2280 {Relation: "application-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-foo"}, 2281 {Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"}, 2282 {Relation: rel.Tag().String(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"}, 2283 {Relation: rel.Tag().String(), LocalUnit: "application-wordpress", RemoteUnit: "application-mysql"}, 2284 {Relation: rel.Tag().String(), LocalUnit: "application-mysql", RemoteUnit: "foo"}, 2285 {Relation: rel.Tag().String(), LocalUnit: "user-foo", RemoteUnit: "unit-wordpress-0"}, 2286 }} 2287 result, err := s.uniter.ReadRemoteSettings(args) 2288 2289 // We don't set the remote unit settings on purpose to test the error. 2290 expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": unit "mysql/0": settings` 2291 c.Assert(err, jc.ErrorIsNil) 2292 c.Assert(result, jc.DeepEquals, params.SettingsResults{ 2293 Results: []params.SettingsResult{ 2294 {Error: apiservertesting.ErrUnauthorized}, 2295 {Error: apiservertesting.ErrUnauthorized}, 2296 {Error: apiservertesting.NotFoundError(expectErr)}, 2297 {Error: apiservertesting.ErrUnauthorized}, 2298 {Error: apiservertesting.ErrUnauthorized}, 2299 {Error: apiservertesting.ErrUnauthorized}, 2300 {Error: apiservertesting.ErrUnauthorized}, 2301 {Error: apiservertesting.ErrUnauthorized}, 2302 {Error: apiservertesting.ErrUnauthorized}, 2303 {Error: apiservertesting.ErrUnauthorized}, 2304 {Error: apiservertesting.ErrUnauthorized}, 2305 }, 2306 }) 2307 2308 // Now leave the mysqlUnit and re-enter with new settings. 2309 relUnit, err = rel.Unit(s.mysqlUnit) 2310 c.Assert(err, jc.ErrorIsNil) 2311 settings = map[string]interface{}{ 2312 "other": "things", 2313 } 2314 err = relUnit.LeaveScope() 2315 c.Assert(err, jc.ErrorIsNil) 2316 s.assertInScope(c, relUnit, false) 2317 err = relUnit.EnterScope(settings) 2318 c.Assert(err, jc.ErrorIsNil) 2319 s.assertInScope(c, relUnit, true) 2320 2321 // Test the remote unit settings can be read. 2322 args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ 2323 Relation: rel.Tag().String(), 2324 LocalUnit: "unit-wordpress-0", 2325 RemoteUnit: "unit-mysql-0", 2326 }}} 2327 expect := params.SettingsResults{ 2328 Results: []params.SettingsResult{ 2329 {Settings: params.Settings{ 2330 "other": "things", 2331 }}, 2332 }, 2333 } 2334 result, err = s.uniter.ReadRemoteSettings(args) 2335 c.Assert(err, jc.ErrorIsNil) 2336 c.Assert(result, gc.DeepEquals, expect) 2337 2338 // Now destroy the remote unit, and check its settings can still be read. 2339 err = s.mysqlUnit.Destroy() 2340 c.Assert(err, jc.ErrorIsNil) 2341 err = s.mysqlUnit.EnsureDead() 2342 c.Assert(err, jc.ErrorIsNil) 2343 err = s.mysqlUnit.Remove() 2344 c.Assert(err, jc.ErrorIsNil) 2345 result, err = s.uniter.ReadRemoteSettings(args) 2346 c.Assert(err, jc.ErrorIsNil) 2347 c.Assert(result, gc.DeepEquals, expect) 2348 } 2349 2350 func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) { 2351 rel := s.addRelation(c, "wordpress", "mysql") 2352 relUnit, err := rel.Unit(s.mysqlUnit) 2353 c.Assert(err, jc.ErrorIsNil) 2354 settings := map[string]interface{}{ 2355 "other": "things", 2356 "invalid-bool": false, 2357 } 2358 err = relUnit.EnterScope(settings) 2359 c.Assert(err, jc.ErrorIsNil) 2360 s.assertInScope(c, relUnit, true) 2361 2362 args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ 2363 Relation: rel.Tag().String(), 2364 LocalUnit: "unit-wordpress-0", 2365 RemoteUnit: "unit-mysql-0", 2366 }}} 2367 expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` 2368 result, err := s.uniter.ReadRemoteSettings(args) 2369 c.Assert(err, jc.ErrorIsNil) 2370 c.Assert(result, gc.DeepEquals, params.SettingsResults{ 2371 Results: []params.SettingsResult{ 2372 {Error: ¶ms.Error{Message: expectErr}}, 2373 }, 2374 }) 2375 } 2376 2377 func (s *uniterSuite) TestUpdateSettings(c *gc.C) { 2378 rel := s.addRelation(c, "wordpress", "mysql") 2379 relUnit, err := rel.Unit(s.wordpressUnit) 2380 c.Assert(err, jc.ErrorIsNil) 2381 settings := map[string]interface{}{ 2382 "some": "settings", 2383 "other": "stuff", 2384 } 2385 err = relUnit.EnterScope(settings) 2386 s.assertInScope(c, relUnit, true) 2387 2388 newSettings := params.Settings{ 2389 "some": "different", 2390 "other": "", 2391 } 2392 2393 args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{ 2394 {Relation: "relation-42", Unit: "unit-foo-0", Settings: nil}, 2395 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0", Settings: newSettings}, 2396 {Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil}, 2397 {Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil}, 2398 {Relation: "application-wordpress", Unit: "unit-foo-0", Settings: nil}, 2399 {Relation: "foo", Unit: "bar", Settings: nil}, 2400 {Relation: rel.Tag().String(), Unit: "unit-mysql-0", Settings: nil}, 2401 {Relation: rel.Tag().String(), Unit: "application-wordpress", Settings: nil}, 2402 {Relation: rel.Tag().String(), Unit: "application-mysql", Settings: nil}, 2403 {Relation: rel.Tag().String(), Unit: "user-foo", Settings: nil}, 2404 }} 2405 result, err := s.uniter.UpdateSettings(args) 2406 c.Assert(err, jc.ErrorIsNil) 2407 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2408 Results: []params.ErrorResult{ 2409 {apiservertesting.ErrUnauthorized}, 2410 {nil}, 2411 {apiservertesting.ErrUnauthorized}, 2412 {apiservertesting.ErrUnauthorized}, 2413 {apiservertesting.ErrUnauthorized}, 2414 {apiservertesting.ErrUnauthorized}, 2415 {apiservertesting.ErrUnauthorized}, 2416 {apiservertesting.ErrUnauthorized}, 2417 {apiservertesting.ErrUnauthorized}, 2418 {apiservertesting.ErrUnauthorized}, 2419 }, 2420 }) 2421 2422 // Verify the settings were saved. 2423 s.assertInScope(c, relUnit, true) 2424 readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) 2425 c.Assert(err, jc.ErrorIsNil) 2426 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 2427 "some": "different", 2428 }) 2429 } 2430 2431 func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) { 2432 // Add a relation between wordpress and mysql and enter scope with 2433 // mysqlUnit. 2434 rel := s.addRelation(c, "wordpress", "mysql") 2435 myRelUnit, err := rel.Unit(s.mysqlUnit) 2436 c.Assert(err, jc.ErrorIsNil) 2437 err = myRelUnit.EnterScope(nil) 2438 c.Assert(err, jc.ErrorIsNil) 2439 s.assertInScope(c, myRelUnit, true) 2440 2441 c.Assert(s.resources.Count(), gc.Equals, 0) 2442 2443 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 2444 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2445 {Relation: "relation-42", Unit: "unit-foo-0"}, 2446 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 2447 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 2448 {Relation: "relation-42", Unit: "unit-wordpress-0"}, 2449 {Relation: "relation-foo", Unit: ""}, 2450 {Relation: "application-wordpress", Unit: "unit-foo-0"}, 2451 {Relation: "foo", Unit: "bar"}, 2452 {Relation: rel.Tag().String(), Unit: "unit-mysql-0"}, 2453 {Relation: rel.Tag().String(), Unit: "application-wordpress"}, 2454 {Relation: rel.Tag().String(), Unit: "application-mysql"}, 2455 {Relation: rel.Tag().String(), Unit: "user-foo"}, 2456 }} 2457 result, err := s.uniter.WatchRelationUnits(args) 2458 c.Assert(err, jc.ErrorIsNil) 2459 // UnitSettings versions are volatile, so we don't check them. 2460 // We just make sure the keys of the Changed field are as 2461 // expected. 2462 c.Assert(result.Results, gc.HasLen, len(args.RelationUnits)) 2463 mysqlChanges := result.Results[1].Changes 2464 c.Assert(mysqlChanges, gc.NotNil) 2465 changed, ok := mysqlChanges.Changed["mysql/0"] 2466 c.Assert(ok, jc.IsTrue) 2467 expectChanges := params.RelationUnitsChange{ 2468 Changed: map[string]params.UnitSettings{ 2469 "mysql/0": {changed.Version}, 2470 }, 2471 } 2472 c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{ 2473 Results: []params.RelationUnitsWatchResult{ 2474 {Error: apiservertesting.ErrUnauthorized}, 2475 { 2476 RelationUnitsWatcherId: "1", 2477 Changes: expectChanges, 2478 }, 2479 {Error: apiservertesting.ErrUnauthorized}, 2480 {Error: apiservertesting.ErrUnauthorized}, 2481 {Error: apiservertesting.ErrUnauthorized}, 2482 {Error: apiservertesting.ErrUnauthorized}, 2483 {Error: apiservertesting.ErrUnauthorized}, 2484 {Error: apiservertesting.ErrUnauthorized}, 2485 {Error: apiservertesting.ErrUnauthorized}, 2486 {Error: apiservertesting.ErrUnauthorized}, 2487 {Error: apiservertesting.ErrUnauthorized}, 2488 }, 2489 }) 2490 2491 // Verify the resource was registered and stop when done 2492 c.Assert(s.resources.Count(), gc.Equals, 1) 2493 resource := s.resources.Get("1") 2494 defer statetesting.AssertStop(c, resource) 2495 2496 // Check that the Watch has consumed the initial event ("returned" in 2497 // the Watch call) 2498 wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher)) 2499 wc.AssertNoChange() 2500 2501 // Leave scope with mysqlUnit and check it's detected. 2502 err = myRelUnit.LeaveScope() 2503 c.Assert(err, jc.ErrorIsNil) 2504 s.assertInScope(c, myRelUnit, false) 2505 2506 wc.AssertChange(nil, []string{"mysql/0"}) 2507 } 2508 2509 func (s *uniterSuite) TestAPIAddresses(c *gc.C) { 2510 hostPorts := [][]network.HostPort{ 2511 network.NewHostPorts(1234, "0.1.2.3"), 2512 } 2513 err := s.State.SetAPIHostPorts(hostPorts) 2514 c.Assert(err, jc.ErrorIsNil) 2515 2516 result, err := s.uniter.APIAddresses() 2517 c.Assert(err, jc.ErrorIsNil) 2518 c.Assert(result, gc.DeepEquals, params.StringsResult{ 2519 Result: []string{"0.1.2.3:1234"}, 2520 }) 2521 } 2522 2523 func (s *uniterSuite) TestWatchUnitAddressesHash(c *gc.C) { 2524 c.Assert(s.resources.Count(), gc.Equals, 0) 2525 2526 args := params.Entities{Entities: []params.Entity{ 2527 {Tag: "unit-mysql-0"}, 2528 {Tag: "unit-wordpress-0"}, 2529 {Tag: "unit-foo-42"}, 2530 {Tag: "machine-0"}, 2531 {Tag: "application-wordpress"}, 2532 }} 2533 result, err := s.uniter.WatchUnitAddressesHash(args) 2534 c.Assert(err, jc.ErrorIsNil) 2535 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 2536 Results: []params.StringsWatchResult{ 2537 {Error: apiservertesting.ErrUnauthorized}, 2538 { 2539 StringsWatcherId: "1", 2540 // This is an empty sha256 hash. 2541 Changes: []string{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, 2542 }, 2543 {Error: apiservertesting.ErrUnauthorized}, 2544 {Error: apiservertesting.ErrUnauthorized}, 2545 {Error: apiservertesting.ErrUnauthorized}, 2546 }, 2547 }) 2548 2549 // Verify the resource was registered and stop when done 2550 c.Assert(s.resources.Count(), gc.Equals, 1) 2551 resource := s.resources.Get("1") 2552 defer statetesting.AssertStop(c, resource) 2553 2554 // Check that the Watch has consumed the initial event ("returned" in 2555 // the Watch call) 2556 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 2557 wc.AssertNoChange() 2558 } 2559 2560 func (s *uniterSuite) TestWatchCAASUnitAddressesHash(c *gc.C) { 2561 _, cm, _, _ := s.setupCAASModel(c) 2562 c.Assert(s.resources.Count(), gc.Equals, 0) 2563 2564 args := params.Entities{Entities: []params.Entity{ 2565 {Tag: "unit-mysql-0"}, 2566 {Tag: "unit-gitlab-0"}, 2567 {Tag: "unit-foo-42"}, 2568 {Tag: "machine-0"}, 2569 {Tag: "application-gitlab"}, 2570 }} 2571 2572 uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{ 2573 State_: cm.State(), 2574 Resources_: s.resources, 2575 Auth_: s.authorizer, 2576 LeadershipChecker_: s.State.LeadershipChecker(), 2577 }) 2578 c.Assert(err, jc.ErrorIsNil) 2579 2580 result, err := uniterAPI.WatchUnitAddressesHash(args) 2581 c.Assert(err, jc.ErrorIsNil) 2582 c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ 2583 Results: []params.StringsWatchResult{ 2584 {Error: apiservertesting.ErrUnauthorized}, 2585 { 2586 StringsWatcherId: "1", 2587 // The container doesn't have an address. 2588 Changes: []string{""}, 2589 }, 2590 {Error: apiservertesting.ErrUnauthorized}, 2591 {Error: apiservertesting.ErrUnauthorized}, 2592 {Error: apiservertesting.ErrUnauthorized}, 2593 }, 2594 }) 2595 2596 // Verify the resource was registered and stop when done 2597 c.Assert(s.resources.Count(), gc.Equals, 1) 2598 resource := s.resources.Get("1") 2599 defer statetesting.AssertStop(c, resource) 2600 2601 // Check that the Watch has consumed the initial event ("returned" in 2602 // the Watch call) 2603 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 2604 wc.AssertNoChange() 2605 } 2606 2607 func (s *uniterSuite) TestGetMeterStatusUnauthenticated(c *gc.C) { 2608 args := params.Entities{Entities: []params.Entity{{s.mysqlUnit.Tag().String()}}} 2609 result, err := s.uniter.GetMeterStatus(args) 2610 c.Assert(err, jc.ErrorIsNil) 2611 c.Assert(result.Results, gc.HasLen, 1) 2612 c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied") 2613 c.Assert(result.Results[0].Code, gc.Equals, "") 2614 c.Assert(result.Results[0].Info, gc.Equals, "") 2615 } 2616 2617 func (s *uniterSuite) TestGetMeterStatusBadTag(c *gc.C) { 2618 tags := []string{ 2619 "user-admin", 2620 "unit-nosuchunit", 2621 "thisisnotatag", 2622 "machine-0", 2623 "model-blah", 2624 } 2625 args := params.Entities{Entities: make([]params.Entity, len(tags))} 2626 for i, tag := range tags { 2627 args.Entities[i] = params.Entity{Tag: tag} 2628 } 2629 result, err := s.uniter.GetMeterStatus(args) 2630 c.Assert(err, jc.ErrorIsNil) 2631 c.Assert(result.Results, gc.HasLen, len(tags)) 2632 for i, result := range result.Results { 2633 c.Logf("checking result %d", i) 2634 c.Assert(result.Code, gc.Equals, "") 2635 c.Assert(result.Info, gc.Equals, "") 2636 c.Assert(result.Error, gc.ErrorMatches, "permission denied") 2637 } 2638 } 2639 2640 func (s *uniterSuite) addRelatedApplication(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Application, *state.Unit) { 2641 relatedApplication := s.AddTestingApplication(c, relatedSvc, s.AddTestingCharm(c, relatedSvc)) 2642 rel := s.addRelation(c, firstSvc, relatedSvc) 2643 relUnit, err := rel.Unit(unit) 2644 c.Assert(err, jc.ErrorIsNil) 2645 err = relUnit.EnterScope(nil) 2646 c.Assert(err, jc.ErrorIsNil) 2647 relatedUnit, err := s.State.Unit(relatedSvc + "/0") 2648 c.Assert(err, jc.ErrorIsNil) 2649 return rel, relatedApplication, relatedUnit 2650 } 2651 2652 func (s *uniterSuite) TestRequestReboot(c *gc.C) { 2653 args := params.Entities{Entities: []params.Entity{ 2654 {Tag: s.machine0.Tag().String()}, 2655 {Tag: s.machine1.Tag().String()}, 2656 {Tag: "bogus"}, 2657 {Tag: "nasty-tag"}, 2658 }} 2659 errResult, err := s.uniter.RequestReboot(args) 2660 c.Assert(err, jc.ErrorIsNil) 2661 c.Assert(errResult, gc.DeepEquals, params.ErrorResults{ 2662 Results: []params.ErrorResult{ 2663 {Error: nil}, 2664 {Error: apiservertesting.ErrUnauthorized}, 2665 {Error: apiservertesting.ErrUnauthorized}, 2666 {Error: apiservertesting.ErrUnauthorized}, 2667 }}) 2668 2669 rFlag, err := s.machine0.GetRebootFlag() 2670 c.Assert(err, jc.ErrorIsNil) 2671 c.Assert(rFlag, jc.IsTrue) 2672 2673 rFlag, err = s.machine1.GetRebootFlag() 2674 c.Assert(err, jc.ErrorIsNil) 2675 c.Assert(rFlag, jc.IsFalse) 2676 } 2677 2678 func checkUnorderedActionIdsEqual(c *gc.C, ids []string, results params.StringsWatchResults) { 2679 c.Assert(results, gc.NotNil) 2680 content := results.Results 2681 c.Assert(len(content), gc.Equals, 1) 2682 result := content[0] 2683 c.Assert(result.StringsWatcherId, gc.Equals, "1") 2684 obtainedIds := map[string]int{} 2685 expectedIds := map[string]int{} 2686 for _, id := range ids { 2687 expectedIds[id]++ 2688 } 2689 // The count of each ID that has been seen. 2690 for _, change := range result.Changes { 2691 obtainedIds[change]++ 2692 } 2693 c.Check(obtainedIds, jc.DeepEquals, expectedIds) 2694 } 2695 2696 func (s *uniterSuite) TestStorageAttachments(c *gc.C) { 2697 // We need to set up a unit that has storage metadata defined. 2698 ch := s.AddTestingCharm(c, "storage-block") 2699 sCons := map[string]state.StorageConstraints{ 2700 "data": {Pool: "", Size: 1024, Count: 1}, 2701 } 2702 application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, sCons) 2703 unit, err := application.AddUnit(state.AddUnitParams{}) 2704 c.Assert(err, jc.ErrorIsNil) 2705 err = s.State.AssignUnit(unit, state.AssignCleanEmpty) 2706 c.Assert(err, jc.ErrorIsNil) 2707 assignedMachineId, err := unit.AssignedMachineId() 2708 c.Assert(err, jc.ErrorIsNil) 2709 machine, err := s.State.Machine(assignedMachineId) 2710 c.Assert(err, jc.ErrorIsNil) 2711 2712 volumeAttachments, err := machine.VolumeAttachments() 2713 c.Assert(err, jc.ErrorIsNil) 2714 c.Assert(volumeAttachments, gc.HasLen, 1) 2715 2716 err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil) 2717 c.Assert(err, jc.ErrorIsNil) 2718 2719 sb, err := state.NewStorageBackend(s.State) 2720 c.Assert(err, jc.ErrorIsNil) 2721 err = sb.SetVolumeInfo( 2722 volumeAttachments[0].Volume(), 2723 state.VolumeInfo{VolumeId: "vol-123", Size: 456}, 2724 ) 2725 c.Assert(err, jc.ErrorIsNil) 2726 2727 err = sb.SetVolumeAttachmentInfo( 2728 machine.MachineTag(), 2729 volumeAttachments[0].Volume(), 2730 state.VolumeAttachmentInfo{DeviceName: "xvdf1"}, 2731 ) 2732 c.Assert(err, jc.ErrorIsNil) 2733 2734 password, err := utils.RandomPassword() 2735 err = unit.SetPassword(password) 2736 c.Assert(err, jc.ErrorIsNil) 2737 st := s.OpenAPIAs(c, unit.Tag(), password) 2738 uniter, err := st.Uniter() 2739 c.Assert(err, jc.ErrorIsNil) 2740 2741 attachments, err := uniter.UnitStorageAttachments(unit.UnitTag()) 2742 c.Assert(err, jc.ErrorIsNil) 2743 c.Assert(attachments, gc.DeepEquals, []params.StorageAttachmentId{{ 2744 StorageTag: "storage-data-0", 2745 UnitTag: unit.Tag().String(), 2746 }}) 2747 } 2748 2749 func (s *uniterSuite) TestUnitStatus(c *gc.C) { 2750 now := time.Now() 2751 sInfo := status.StatusInfo{ 2752 Status: status.Maintenance, 2753 Message: "blah", 2754 Since: &now, 2755 } 2756 err := s.wordpressUnit.SetStatus(sInfo) 2757 c.Assert(err, jc.ErrorIsNil) 2758 sInfo = status.StatusInfo{ 2759 Status: status.Terminated, 2760 Message: "foo", 2761 Since: &now, 2762 } 2763 err = s.mysqlUnit.SetStatus(sInfo) 2764 c.Assert(err, jc.ErrorIsNil) 2765 2766 args := params.Entities{ 2767 Entities: []params.Entity{ 2768 {Tag: "unit-mysql-0"}, 2769 {Tag: "unit-wordpress-0"}, 2770 {Tag: "unit-foo-42"}, 2771 {Tag: "machine-1"}, 2772 {Tag: "invalid"}, 2773 }} 2774 result, err := s.uniter.UnitStatus(args) 2775 c.Assert(err, jc.ErrorIsNil) 2776 // Zero out the updated timestamps so we can easily check the results. 2777 for i, statusResult := range result.Results { 2778 r := statusResult 2779 if r.Status != "" { 2780 c.Assert(r.Since, gc.NotNil) 2781 } 2782 r.Since = nil 2783 result.Results[i] = r 2784 } 2785 c.Assert(result, gc.DeepEquals, params.StatusResults{ 2786 Results: []params.StatusResult{ 2787 {Error: apiservertesting.ErrUnauthorized}, 2788 {Status: status.Maintenance.String(), Info: "blah", Data: map[string]interface{}{}}, 2789 {Error: apiservertesting.ErrUnauthorized}, 2790 {Error: apiservertesting.ErrUnauthorized}, 2791 {Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)}, 2792 }, 2793 }) 2794 } 2795 2796 func (s *uniterSuite) TestAssignedMachine(c *gc.C) { 2797 args := params.Entities{Entities: []params.Entity{ 2798 {Tag: "unit-mysql-0"}, 2799 {Tag: "unit-wordpress-0"}, 2800 {Tag: "unit-foo-42"}, 2801 {Tag: "application-mysql"}, 2802 {Tag: "application-wordpress"}, 2803 {Tag: "machine-0"}, 2804 {Tag: "machine-1"}, 2805 {Tag: "machine-42"}, 2806 {Tag: "application-foo"}, 2807 {Tag: "relation-svc1.rel1#svc2.rel2"}, 2808 }} 2809 result, err := s.uniter.AssignedMachine(args) 2810 c.Assert(err, jc.ErrorIsNil) 2811 c.Assert(result, jc.DeepEquals, params.StringResults{ 2812 Results: []params.StringResult{ 2813 {Error: apiservertesting.ErrUnauthorized}, 2814 {Result: "machine-0"}, 2815 {Error: apiservertesting.ErrUnauthorized}, 2816 {Error: apiservertesting.ErrUnauthorized}, 2817 {Error: apiservertesting.ErrUnauthorized}, 2818 {Error: apiservertesting.ErrUnauthorized}, 2819 {Error: apiservertesting.ErrUnauthorized}, 2820 {Error: apiservertesting.ErrUnauthorized}, 2821 {Error: apiservertesting.ErrUnauthorized}, 2822 {Error: apiservertesting.ErrUnauthorized}, 2823 }, 2824 }) 2825 } 2826 2827 func (s *uniterSuite) TestAllMachinePorts(c *gc.C) { 2828 // Verify no ports are opened yet on the machine or unit. 2829 machinePorts, err := s.machine0.AllPorts() 2830 c.Assert(err, jc.ErrorIsNil) 2831 c.Assert(machinePorts, gc.HasLen, 0) 2832 unitPorts, err := s.wordpressUnit.OpenedPorts() 2833 c.Assert(err, jc.ErrorIsNil) 2834 c.Assert(unitPorts, gc.HasLen, 0) 2835 2836 // Add another mysql unit on machine 0. 2837 mysqlUnit1, err := s.mysql.AddUnit(state.AddUnitParams{}) 2838 c.Assert(err, jc.ErrorIsNil) 2839 err = mysqlUnit1.AssignToMachine(s.machine0) 2840 c.Assert(err, jc.ErrorIsNil) 2841 2842 // Open some ports on both units. 2843 err = s.wordpressUnit.OpenPorts("tcp", 100, 200) 2844 c.Assert(err, jc.ErrorIsNil) 2845 err = s.wordpressUnit.OpenPorts("udp", 10, 20) 2846 c.Assert(err, jc.ErrorIsNil) 2847 err = mysqlUnit1.OpenPorts("tcp", 201, 250) 2848 c.Assert(err, jc.ErrorIsNil) 2849 err = mysqlUnit1.OpenPorts("udp", 1, 8) 2850 c.Assert(err, jc.ErrorIsNil) 2851 2852 args := params.Entities{Entities: []params.Entity{ 2853 {Tag: "unit-mysql-0"}, 2854 {Tag: "machine-0"}, 2855 {Tag: "machine-1"}, 2856 {Tag: "unit-foo-42"}, 2857 {Tag: "machine-42"}, 2858 {Tag: "application-wordpress"}, 2859 }} 2860 expectPorts := []params.MachinePortRange{ 2861 {UnitTag: "unit-wordpress-0", PortRange: params.PortRange{100, 200, "tcp"}}, 2862 {UnitTag: "unit-mysql-1", PortRange: params.PortRange{201, 250, "tcp"}}, 2863 {UnitTag: "unit-mysql-1", PortRange: params.PortRange{1, 8, "udp"}}, 2864 {UnitTag: "unit-wordpress-0", PortRange: params.PortRange{10, 20, "udp"}}, 2865 } 2866 result, err := s.uniter.AllMachinePorts(args) 2867 c.Assert(err, jc.ErrorIsNil) 2868 c.Assert(result, gc.DeepEquals, params.MachinePortsResults{ 2869 Results: []params.MachinePortsResult{ 2870 {Error: apiservertesting.ErrUnauthorized}, 2871 {Ports: expectPorts}, 2872 {Error: apiservertesting.ErrUnauthorized}, 2873 {Error: apiservertesting.ErrUnauthorized}, 2874 {Error: apiservertesting.ErrUnauthorized}, 2875 {Error: apiservertesting.ErrUnauthorized}, 2876 }, 2877 }) 2878 } 2879 2880 func (s *uniterSuite) TestSLALevel(c *gc.C) { 2881 err := s.State.SetSLA("essential", "bob", []byte("creds")) 2882 c.Assert(err, jc.ErrorIsNil) 2883 2884 result, err := s.uniter.SLALevel() 2885 c.Assert(err, jc.ErrorIsNil) 2886 c.Assert(result, jc.DeepEquals, params.StringResult{Result: "essential"}) 2887 } 2888 2889 func (s *uniterSuite) setupRemoteRelationScenario(c *gc.C) (names.Tag, *state.RelationUnit) { 2890 s.makeRemoteWordpress(c) 2891 2892 // Set mysql's addresses first. 2893 err := s.machine1.SetProviderAddresses( 2894 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 2895 network.NewScopedAddress("4.3.2.1", network.ScopePublic), 2896 ) 2897 c.Assert(err, jc.ErrorIsNil) 2898 2899 eps, err := s.State.InferEndpoints("mysql", "remote-wordpress") 2900 c.Assert(err, jc.ErrorIsNil) 2901 rel, err := s.State.AddRelation(eps...) 2902 c.Assert(err, jc.ErrorIsNil) 2903 2904 relUnit, err := rel.Unit(s.mysqlUnit) 2905 c.Assert(err, jc.ErrorIsNil) 2906 s.assertInScope(c, relUnit, false) 2907 return rel.Tag(), relUnit 2908 } 2909 2910 func (s *uniterSuite) TestPrivateAddressWithRemoteRelation(c *gc.C) { 2911 relTag, relUnit := s.setupRemoteRelationScenario(c) 2912 2913 thisUniter := s.makeMysqlUniter(c) 2914 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2915 {Relation: relTag.String(), Unit: "unit-mysql-0"}, 2916 }} 2917 result, err := thisUniter.EnterScope(args) 2918 c.Assert(err, jc.ErrorIsNil) 2919 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2920 Results: []params.ErrorResult{{Error: nil}}, 2921 }) 2922 2923 // Verify the scope changes and settings. 2924 s.assertInScope(c, relUnit, true) 2925 readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name()) 2926 c.Assert(err, jc.ErrorIsNil) 2927 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 2928 "private-address": "4.3.2.1", 2929 "ingress-address": "4.3.2.1", 2930 "egress-subnets": "4.3.2.1/32", 2931 }) 2932 } 2933 2934 func (s *uniterSuite) TestPrivateAddressWithRemoteRelationNoPublic(c *gc.C) { 2935 relTag, relUnit := s.setupRemoteRelationScenario(c) 2936 2937 thisUniter := s.makeMysqlUniter(c) 2938 // Set mysql's addresses - no public address. 2939 err := s.machine1.SetProviderAddresses( 2940 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 2941 ) 2942 c.Assert(err, jc.ErrorIsNil) 2943 2944 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2945 {Relation: relTag.String(), Unit: "unit-mysql-0"}, 2946 }} 2947 result, err := thisUniter.EnterScope(args) 2948 c.Assert(err, jc.ErrorIsNil) 2949 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2950 Results: []params.ErrorResult{{Error: nil}}, 2951 }) 2952 2953 // Verify that we fell back to the private address. 2954 s.assertInScope(c, relUnit, true) 2955 readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name()) 2956 c.Assert(err, jc.ErrorIsNil) 2957 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 2958 "private-address": "1.2.3.4", 2959 "ingress-address": "1.2.3.4", 2960 "egress-subnets": "1.2.3.4/32", 2961 }) 2962 } 2963 2964 func (s *uniterSuite) TestRelationEgressSubnets(c *gc.C) { 2965 relTag, relUnit := s.setupRemoteRelationScenario(c) 2966 2967 // Check model attributes are overridden by setting up a value. 2968 err := s.Model.UpdateModelConfig(map[string]interface{}{"egress-subnets": "192.168.0.0/16"}, nil) 2969 c.Assert(err, jc.ErrorIsNil) 2970 egress := state.NewRelationEgressNetworks(s.State) 2971 _, err = egress.Save(relTag.Id(), false, []string{"10.0.0.0/16", "10.1.2.0/8"}) 2972 c.Assert(err, jc.ErrorIsNil) 2973 2974 thisUniter := s.makeMysqlUniter(c) 2975 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 2976 {Relation: relTag.String(), Unit: "unit-mysql-0"}, 2977 }} 2978 result, err := thisUniter.EnterScope(args) 2979 c.Assert(err, jc.ErrorIsNil) 2980 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 2981 Results: []params.ErrorResult{{Error: nil}}, 2982 }) 2983 2984 // Verify the scope changes and settings. 2985 s.assertInScope(c, relUnit, true) 2986 readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name()) 2987 c.Assert(err, jc.ErrorIsNil) 2988 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 2989 "private-address": "4.3.2.1", 2990 "ingress-address": "4.3.2.1", 2991 "egress-subnets": "10.0.0.0/16,10.1.2.0/8", 2992 }) 2993 } 2994 2995 func (s *uniterSuite) TestModelEgressSubnets(c *gc.C) { 2996 relTag, relUnit := s.setupRemoteRelationScenario(c) 2997 2998 err := s.Model.UpdateModelConfig(map[string]interface{}{"egress-subnets": "192.168.0.0/16"}, nil) 2999 c.Assert(err, jc.ErrorIsNil) 3000 3001 thisUniter := s.makeMysqlUniter(c) 3002 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 3003 {Relation: relTag.String(), Unit: "unit-mysql-0"}, 3004 }} 3005 result, err := thisUniter.EnterScope(args) 3006 c.Assert(err, jc.ErrorIsNil) 3007 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 3008 Results: []params.ErrorResult{{Error: nil}}, 3009 }) 3010 3011 // Verify the scope changes and settings. 3012 s.assertInScope(c, relUnit, true) 3013 readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name()) 3014 c.Assert(err, jc.ErrorIsNil) 3015 c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ 3016 "private-address": "4.3.2.1", 3017 "ingress-address": "4.3.2.1", 3018 "egress-subnets": "192.168.0.0/16", 3019 }) 3020 } 3021 3022 func (s *uniterSuite) makeMysqlUniter(c *gc.C) *uniter.UniterAPI { 3023 authorizer := s.authorizer 3024 authorizer.Tag = s.mysqlUnit.Tag() 3025 result, err := uniter.NewUniterAPI(facadetest.Context{ 3026 State_: s.State, 3027 Resources_: s.resources, 3028 Auth_: authorizer, 3029 LeadershipChecker_: s.State.LeadershipChecker(), 3030 }) 3031 c.Assert(err, jc.ErrorIsNil) 3032 return result 3033 } 3034 3035 func (s *uniterSuite) makeRemoteWordpress(c *gc.C) { 3036 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 3037 Name: "remote-wordpress", 3038 SourceModel: names.NewModelTag("source-model"), 3039 IsConsumerProxy: true, 3040 OfferUUID: "offer-uuid", 3041 Endpoints: []charm.Relation{{ 3042 Interface: "mysql", 3043 Limit: 1, 3044 Name: "db", 3045 Role: charm.RoleRequirer, 3046 Scope: charm.ScopeGlobal, 3047 }}, 3048 }) 3049 c.Assert(err, jc.ErrorIsNil) 3050 } 3051 3052 func (s *uniterSuite) TestV4WatchApplicationRelations(c *gc.C) { 3053 c.Assert(s.resources.Count(), gc.Equals, 0) 3054 3055 args := params.Entities{Entities: []params.Entity{ 3056 {Tag: "application-mysql"}, 3057 {Tag: "application-wordpress"}, 3058 {Tag: "application-foo"}, 3059 }} 3060 apiV4, err := uniter.NewUniterAPIV4(facadetest.Context{ 3061 State_: s.State, 3062 Resources_: s.resources, 3063 Auth_: s.authorizer, 3064 LeadershipChecker_: s.State.LeadershipChecker(), 3065 }) 3066 c.Assert(err, jc.ErrorIsNil) 3067 result, err := apiV4.WatchApplicationRelations(args) 3068 c.Assert(err, jc.ErrorIsNil) 3069 c.Assert(result.Results, gc.HasLen, 3) 3070 c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 3071 c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1") 3072 c.Assert(result.Results[1].Changes, gc.NotNil) 3073 c.Assert(result.Results[1].Error, gc.IsNil) 3074 c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) 3075 3076 // Verify the resource was registered and stop when done 3077 c.Assert(s.resources.Count(), gc.Equals, 1) 3078 resource := s.resources.Get("1") 3079 defer statetesting.AssertStop(c, resource) 3080 3081 // Check that the Watch has consumed the initial event ("returned" in 3082 // the Watch call) 3083 wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) 3084 wc.AssertNoChange() 3085 } 3086 3087 func (s *uniterSuite) TestV5Relation(c *gc.C) { 3088 rel := s.addRelation(c, "wordpress", "mysql") 3089 wpEp, err := rel.Endpoint("wordpress") 3090 c.Assert(err, jc.ErrorIsNil) 3091 3092 args := params.RelationUnits{RelationUnits: []params.RelationUnit{ 3093 {Relation: rel.Tag().String(), Unit: "unit-wordpress-0"}, 3094 }} 3095 apiV5, err := uniter.NewUniterAPIV5(facadetest.Context{ 3096 State_: s.State, 3097 Resources_: s.resources, 3098 Auth_: s.authorizer, 3099 LeadershipChecker_: s.State.LeadershipChecker(), 3100 }) 3101 c.Assert(err, jc.ErrorIsNil) 3102 result, err := apiV5.Relation(args) 3103 c.Assert(err, jc.ErrorIsNil) 3104 c.Assert(result, gc.DeepEquals, params.RelationResultsV5{ 3105 Results: []params.RelationResultV5{ 3106 { 3107 Id: rel.Id(), 3108 Key: rel.String(), 3109 Life: params.Life(rel.Life().String()), 3110 Endpoint: multiwatcher.Endpoint{ 3111 ApplicationName: wpEp.ApplicationName, 3112 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 3113 }, 3114 }, 3115 }, 3116 }) 3117 } 3118 3119 func (s *uniterSuite) TestV5RelationById(c *gc.C) { 3120 rel := s.addRelation(c, "wordpress", "mysql") 3121 wpEp, err := rel.Endpoint("wordpress") 3122 c.Assert(err, jc.ErrorIsNil) 3123 3124 args := params.RelationIds{RelationIds: []int{rel.Id()}} 3125 apiV5, err := uniter.NewUniterAPIV5(facadetest.Context{ 3126 State_: s.State, 3127 Resources_: s.resources, 3128 Auth_: s.authorizer, 3129 LeadershipChecker_: s.State.LeadershipChecker(), 3130 }) 3131 c.Assert(err, jc.ErrorIsNil) 3132 result, err := apiV5.RelationById(args) 3133 c.Assert(err, jc.ErrorIsNil) 3134 c.Assert(result, gc.DeepEquals, params.RelationResultsV5{ 3135 Results: []params.RelationResultV5{ 3136 { 3137 Id: rel.Id(), 3138 Key: rel.String(), 3139 Life: params.Life(rel.Life().String()), 3140 Endpoint: multiwatcher.Endpoint{ 3141 ApplicationName: wpEp.ApplicationName, 3142 Relation: multiwatcher.NewCharmRelation(wpEp.Relation), 3143 }, 3144 }, 3145 }, 3146 }) 3147 } 3148 3149 func (s *uniterSuite) TestRefresh(c *gc.C) { 3150 args := params.Entities{ 3151 Entities: []params.Entity{ 3152 {s.wordpressUnit.Tag().String()}, 3153 {s.mysqlUnit.Tag().String()}, 3154 {s.mysql.Tag().String()}, 3155 {s.machine0.Tag().String()}, 3156 {"some-word"}, 3157 }, 3158 } 3159 expect := params.UnitRefreshResults{ 3160 Results: []params.UnitRefreshResult{ 3161 {Life: params.Alive, Resolved: params.ResolvedNone}, 3162 {Error: apiservertesting.ErrUnauthorized}, 3163 {Error: apiservertesting.ErrUnauthorized}, 3164 {Error: apiservertesting.ErrUnauthorized}, 3165 {Error: apiservertesting.ErrUnauthorized}, 3166 }, 3167 } 3168 results, err := s.uniter.Refresh(args) 3169 c.Assert(err, jc.ErrorIsNil) 3170 c.Assert(results, gc.DeepEquals, expect) 3171 } 3172 3173 func (s *uniterSuite) TestRefreshNoArgs(c *gc.C) { 3174 results, err := s.uniter.Refresh(params.Entities{Entities: []params.Entity{}}) 3175 c.Assert(err, jc.ErrorIsNil) 3176 c.Assert(results, gc.DeepEquals, params.UnitRefreshResults{Results: []params.UnitRefreshResult{}}) 3177 } 3178 3179 var podSpec = ` 3180 containers: 3181 - name: gitlab 3182 image: gitlab/latest 3183 ports: 3184 - containerPort: 80 3185 protocol: TCP 3186 - containerPort: 443 3187 config: 3188 attr: foo=bar; fred=blogs 3189 foo: bar 3190 `[1:] 3191 3192 func (s *uniterSuite) TestSetPodSpec(c *gc.C) { 3193 u, cm, app, _ := s.setupCAASModel(c) 3194 err := u.SetPodSpec(app.Name(), podSpec) 3195 c.Assert(err, jc.ErrorIsNil) 3196 spec, err := cm.PodSpec(app.ApplicationTag()) 3197 c.Assert(err, jc.ErrorIsNil) 3198 c.Assert(spec, gc.Equals, podSpec) 3199 } 3200 3201 type unitMetricBatchesSuite struct { 3202 uniterSuiteBase 3203 *commontesting.ModelWatcherTest 3204 uniter *uniter.UniterAPI 3205 } 3206 3207 var _ = gc.Suite(&unitMetricBatchesSuite{}) 3208 3209 func (s *unitMetricBatchesSuite) SetUpTest(c *gc.C) { 3210 s.uniterSuiteBase.SetUpTest(c) 3211 3212 meteredAuthorizer := apiservertesting.FakeAuthorizer{ 3213 Tag: s.meteredUnit.Tag(), 3214 } 3215 var err error 3216 s.uniter, err = uniter.NewUniterAPI(facadetest.Context{ 3217 State_: s.State, 3218 Resources_: s.resources, 3219 Auth_: meteredAuthorizer, 3220 LeadershipChecker_: s.State.LeadershipChecker(), 3221 }) 3222 c.Assert(err, jc.ErrorIsNil) 3223 3224 s.ModelWatcherTest = commontesting.NewModelWatcherTest( 3225 s.uniter, 3226 s.State, 3227 s.resources, 3228 ) 3229 } 3230 3231 func (s *unitMetricBatchesSuite) TestAddMetricsBatch(c *gc.C) { 3232 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 3233 uuid := utils.MustNewUUID().String() 3234 3235 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 3236 Batches: []params.MetricBatchParam{{ 3237 Tag: s.meteredUnit.Tag().String(), 3238 Batch: params.MetricBatch{ 3239 UUID: uuid, 3240 CharmURL: s.meteredCharm.URL().String(), 3241 Created: time.Now(), 3242 Metrics: metrics, 3243 }}}}, 3244 ) 3245 3246 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 3247 Results: []params.ErrorResult{{nil}}, 3248 }) 3249 c.Assert(err, jc.ErrorIsNil) 3250 3251 batch, err := s.State.MetricBatch(uuid) 3252 c.Assert(err, jc.ErrorIsNil) 3253 c.Assert(batch.UUID(), gc.Equals, uuid) 3254 c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String()) 3255 c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name()) 3256 storedMetrics := batch.Metrics() 3257 c.Assert(storedMetrics, gc.HasLen, 1) 3258 c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key) 3259 c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value) 3260 } 3261 3262 func (s *unitMetricBatchesSuite) TestAddMetricsBatchNoCharmURL(c *gc.C) { 3263 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 3264 uuid := utils.MustNewUUID().String() 3265 3266 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 3267 Batches: []params.MetricBatchParam{{ 3268 Tag: s.meteredUnit.Tag().String(), 3269 Batch: params.MetricBatch{ 3270 UUID: uuid, 3271 CharmURL: s.meteredCharm.URL().String(), 3272 Created: time.Now(), 3273 Metrics: metrics, 3274 }}}}) 3275 3276 c.Assert(result, gc.DeepEquals, params.ErrorResults{ 3277 Results: []params.ErrorResult{{nil}}, 3278 }) 3279 c.Assert(err, jc.ErrorIsNil) 3280 3281 batch, err := s.State.MetricBatch(uuid) 3282 c.Assert(err, jc.ErrorIsNil) 3283 c.Assert(batch.UUID(), gc.Equals, uuid) 3284 c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String()) 3285 c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name()) 3286 storedMetrics := batch.Metrics() 3287 c.Assert(storedMetrics, gc.HasLen, 1) 3288 c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key) 3289 c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value) 3290 } 3291 3292 func (s *unitMetricBatchesSuite) TestAddMetricsBatchDiffTag(c *gc.C) { 3293 unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredApplication, SetCharmURL: true}) 3294 3295 metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}} 3296 uuid := utils.MustNewUUID().String() 3297 3298 tests := []struct { 3299 about string 3300 tag string 3301 expect string 3302 }{{ 3303 about: "different unit", 3304 tag: unit2.Tag().String(), 3305 expect: "permission denied", 3306 }, { 3307 about: "user tag", 3308 tag: names.NewLocalUserTag("admin").String(), 3309 expect: `"user-admin" is not a valid unit tag`, 3310 }, { 3311 about: "machine tag", 3312 tag: names.NewMachineTag("0").String(), 3313 expect: `"machine-0" is not a valid unit tag`, 3314 }} 3315 3316 for i, test := range tests { 3317 c.Logf("test %d: %s", i, test.about) 3318 result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{ 3319 Batches: []params.MetricBatchParam{{ 3320 Tag: test.tag, 3321 Batch: params.MetricBatch{ 3322 UUID: uuid, 3323 CharmURL: "", 3324 Created: time.Now(), 3325 Metrics: metrics, 3326 }}}}) 3327 3328 if test.expect == "" { 3329 c.Assert(result.OneError(), jc.ErrorIsNil) 3330 } else { 3331 c.Assert(result.OneError(), gc.ErrorMatches, test.expect) 3332 } 3333 c.Assert(err, jc.ErrorIsNil) 3334 3335 _, err = s.State.MetricBatch(uuid) 3336 c.Assert(err, jc.Satisfies, errors.IsNotFound) 3337 } 3338 } 3339 3340 type uniterNetworkConfigSuite struct { 3341 uniterSuiteBase 3342 uniterv4 *uniter.UniterAPIV4 3343 } 3344 3345 var _ = gc.Suite(&uniterNetworkConfigSuite{}) 3346 3347 func (s *uniterNetworkConfigSuite) SetUpTest(c *gc.C) { 3348 s.uniterSuiteBase.JujuConnSuite.SetUpTest(c) 3349 3350 // Add the spaces and subnets used by the test. 3351 subnetInfos := []state.SubnetInfo{{ 3352 CIDR: "8.8.0.0/16", 3353 SpaceName: "public", 3354 }, { 3355 CIDR: "10.0.0.0/24", 3356 SpaceName: "internal", 3357 }} 3358 for _, info := range subnetInfos { 3359 _, err := s.State.AddSpace(info.SpaceName, "", nil, false) 3360 c.Assert(err, jc.ErrorIsNil) 3361 _, err = s.State.AddSubnet(info) 3362 c.Assert(err, jc.ErrorIsNil) 3363 } 3364 3365 s.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10) 3366 3367 s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{ 3368 Name: "wordpress-extra-bindings", 3369 URL: "cs:quantal/wordpress-extra-bindings-4", 3370 }) 3371 var err error 3372 s.wordpress, err = s.State.AddApplication(state.AddApplicationArgs{ 3373 Name: "wordpress", 3374 Charm: s.wpCharm, 3375 EndpointBindings: map[string]string{ 3376 "db": "internal", // relation name 3377 "admin-api": "public", // extra-binding name 3378 }, 3379 }) 3380 c.Assert(err, jc.ErrorIsNil) 3381 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3382 Application: s.wordpress, 3383 Machine: s.machine0, 3384 }) 3385 3386 s.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20) 3387 3388 mysqlCharm := s.Factory.MakeCharm(c, &factory.CharmParams{ 3389 Name: "mysql", 3390 }) 3391 s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 3392 Name: "mysql", 3393 Charm: mysqlCharm, 3394 }) 3395 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3396 Application: s.wordpress, 3397 Machine: s.machine0, 3398 }) 3399 s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3400 Application: s.mysql, 3401 Machine: s.machine1, 3402 }) 3403 3404 // Create the resource registry separately to track invocations to register. 3405 s.resources = common.NewResources() 3406 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 3407 3408 s.setupUniterAPIForUnit(c, s.wordpressUnit) 3409 } 3410 3411 func (s *uniterNetworkConfigSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine { 3412 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 3413 c.Assert(err, jc.ErrorIsNil) 3414 devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix) 3415 err = machine.SetInstanceInfo("i-am", "", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil, nil) 3416 c.Assert(err, jc.ErrorIsNil) 3417 3418 machineAddrs, err := machine.AllAddresses() 3419 c.Assert(err, jc.ErrorIsNil) 3420 3421 netAddrs := make([]network.Address, len(machineAddrs)) 3422 for i, addr := range machineAddrs { 3423 netAddrs[i] = network.NewAddress(addr.Value()) 3424 } 3425 err = machine.SetProviderAddresses(netAddrs...) 3426 c.Assert(err, jc.ErrorIsNil) 3427 3428 return machine 3429 } 3430 3431 func (s *uniterNetworkConfigSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) { 3432 return []state.LinkLayerDeviceArgs{{ 3433 Name: "eth0", 3434 Type: state.EthernetDevice, 3435 }, { 3436 Name: "eth0.100", 3437 Type: state.VLAN_8021QDevice, 3438 ParentName: "eth0", 3439 }, { 3440 Name: "eth1", 3441 Type: state.EthernetDevice, 3442 }, { 3443 Name: "eth1.100", 3444 Type: state.VLAN_8021QDevice, 3445 ParentName: "eth1", 3446 }}, 3447 []state.LinkLayerDeviceAddress{{ 3448 DeviceName: "eth0", 3449 ConfigMethod: state.StaticAddress, 3450 CIDRAddress: fmt.Sprintf("8.8.8.%d/16", addrSuffix), 3451 }, { 3452 DeviceName: "eth0.100", 3453 ConfigMethod: state.StaticAddress, 3454 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix), 3455 }, { 3456 DeviceName: "eth1", 3457 ConfigMethod: state.StaticAddress, 3458 CIDRAddress: fmt.Sprintf("8.8.4.%d/16", addrSuffix), 3459 }, { 3460 DeviceName: "eth1.100", 3461 ConfigMethod: state.StaticAddress, 3462 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix+1), 3463 }} 3464 } 3465 3466 func (s *uniterNetworkConfigSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) { 3467 // Create a FakeAuthorizer so we can check permissions, set up assuming the 3468 // given unit agent has logged in. 3469 s.authorizer = apiservertesting.FakeAuthorizer{ 3470 Tag: givenUnit.Tag(), 3471 } 3472 3473 var err error 3474 s.uniterv4, err = uniter.NewUniterAPIV4(facadetest.Context{ 3475 State_: s.State, 3476 Resources_: s.resources, 3477 Auth_: s.authorizer, 3478 LeadershipChecker_: s.State.LeadershipChecker(), 3479 }) 3480 c.Assert(err, jc.ErrorIsNil) 3481 } 3482 3483 func (s *uniterNetworkConfigSuite) TestNetworkConfigPermissions(c *gc.C) { 3484 s.addRelationAndAssertInScope(c) 3485 3486 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 3487 {BindingName: "foo", UnitTag: "unit-foo-0"}, 3488 {BindingName: "db-client", UnitTag: "invalid"}, 3489 {BindingName: "juju-info", UnitTag: "unit-mysql-0"}, 3490 {BindingName: "", UnitTag: s.wordpressUnit.Tag().String()}, 3491 {BindingName: "unknown", UnitTag: s.wordpressUnit.Tag().String()}, 3492 }} 3493 3494 result, err := s.uniterv4.NetworkConfig(args) 3495 c.Assert(err, jc.ErrorIsNil) 3496 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 3497 Results: []params.UnitNetworkConfigResult{ 3498 {Error: apiservertesting.ErrUnauthorized}, 3499 {Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)}, 3500 {Error: apiservertesting.ErrUnauthorized}, 3501 {Error: apiservertesting.ServerError(`binding name cannot be empty`)}, 3502 {Error: apiservertesting.ServerError(`binding name "unknown" not defined by the unit's charm`)}, 3503 }, 3504 }) 3505 } 3506 3507 func (s *uniterNetworkConfigSuite) addRelationAndAssertInScope(c *gc.C) { 3508 // Add a relation between wordpress and mysql and enter scope with 3509 // mysqlUnit. 3510 rel := s.addRelation(c, "wordpress", "mysql") 3511 wpRelUnit, err := rel.Unit(s.wordpressUnit) 3512 c.Assert(err, jc.ErrorIsNil) 3513 err = wpRelUnit.EnterScope(nil) 3514 c.Assert(err, jc.ErrorIsNil) 3515 s.assertInScope(c, wpRelUnit, true) 3516 } 3517 3518 func (s *uniterNetworkConfigSuite) TestNetworkConfigForExplicitlyBoundEndpoint(c *gc.C) { 3519 s.addRelationAndAssertInScope(c) 3520 3521 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 3522 {BindingName: "db", UnitTag: s.wordpressUnit.Tag().String()}, 3523 {BindingName: "admin-api", UnitTag: s.wordpressUnit.Tag().String()}, 3524 }} 3525 3526 // For the relation "wordpress:db mysql:server" we expect to see only 3527 // addresses bound to the "internal" space, where the "db" endpoint itself 3528 // is bound to. 3529 expectedConfigWithRelationName := []params.NetworkConfig{ 3530 {Address: "10.0.0.10"}, 3531 {Address: "10.0.0.11"}, 3532 } 3533 // For the "admin-api" extra-binding we expect to see only addresses from 3534 // the "public" space. 3535 expectedConfigWithExtraBindingName := []params.NetworkConfig{ 3536 {Address: "8.8.8.10"}, 3537 {Address: "8.8.4.10"}, 3538 } 3539 3540 result, err := s.uniterv4.NetworkConfig(args) 3541 c.Assert(err, jc.ErrorIsNil) 3542 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 3543 Results: []params.UnitNetworkConfigResult{ 3544 {Config: expectedConfigWithRelationName}, 3545 {Config: expectedConfigWithExtraBindingName}, 3546 }, 3547 }) 3548 } 3549 3550 func (s *uniterNetworkConfigSuite) TestNetworkConfigForImplicitlyBoundEndpoint(c *gc.C) { 3551 // Since wordpressUnit has explicit binding for "db", switch the API to 3552 // mysqlUnit and check "mysql:server" uses the machine preferred private 3553 // address. 3554 s.setupUniterAPIForUnit(c, s.mysqlUnit) 3555 rel := s.addRelation(c, "mysql", "wordpress") 3556 mysqlRelUnit, err := rel.Unit(s.mysqlUnit) 3557 c.Assert(err, jc.ErrorIsNil) 3558 err = mysqlRelUnit.EnterScope(nil) 3559 c.Assert(err, jc.ErrorIsNil) 3560 s.assertInScope(c, mysqlRelUnit, true) 3561 3562 args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{ 3563 {BindingName: "server", UnitTag: s.mysqlUnit.Tag().String()}, 3564 }} 3565 3566 privateAddress, err := s.machine1.PrivateAddress() 3567 c.Assert(err, jc.ErrorIsNil) 3568 3569 expectedConfig := []params.NetworkConfig{{Address: privateAddress.Value}} 3570 3571 result, err := s.uniterv4.NetworkConfig(args) 3572 c.Assert(err, jc.ErrorIsNil) 3573 c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{ 3574 Results: []params.UnitNetworkConfigResult{ 3575 {Config: expectedConfig}, 3576 }, 3577 }) 3578 } 3579 3580 type uniterNetworkInfoSuite struct { 3581 uniterSuiteBase 3582 mysqlCharm *state.Charm 3583 } 3584 3585 var _ = gc.Suite(&uniterNetworkInfoSuite{}) 3586 3587 func (s *uniterNetworkInfoSuite) SetUpTest(c *gc.C) { 3588 s.uniterSuiteBase.JujuConnSuite.SetUpTest(c) 3589 3590 // Add the spaces and subnets used by the test. 3591 subnetInfos := []state.SubnetInfo{{ 3592 CIDR: "8.8.0.0/16", 3593 SpaceName: "public", 3594 }, { 3595 CIDR: "10.0.0.0/24", 3596 SpaceName: "internal", 3597 }, { 3598 CIDR: "100.64.0.0/16", 3599 SpaceName: "wp-default", 3600 }, { 3601 CIDR: "192.168.1.0/24", 3602 SpaceName: "database", 3603 }, { 3604 SpaceName: "layertwo", 3605 }} 3606 for _, info := range subnetInfos { 3607 _, err := s.State.AddSpace(info.SpaceName, "", nil, false) 3608 c.Assert(err, jc.ErrorIsNil) 3609 if info.CIDR != "" { 3610 _, err = s.State.AddSubnet(info) 3611 c.Assert(err, jc.ErrorIsNil) 3612 } 3613 } 3614 3615 s.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10) 3616 3617 s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{ 3618 Name: "wordpress-extra-bindings", 3619 URL: "cs:quantal/wordpress-extra-bindings-4", 3620 }) 3621 var err error 3622 s.wordpress, err = s.State.AddApplication(state.AddApplicationArgs{ 3623 Name: "wordpress", 3624 Charm: s.wpCharm, 3625 EndpointBindings: map[string]string{ 3626 "db": "internal", // relation name 3627 "admin-api": "public", // extra-binding name 3628 "foo-bar": "layertwo", // extra-binding to L2 3629 "": "wp-default", // explicitly specified default space 3630 }, 3631 }) 3632 c.Assert(err, jc.ErrorIsNil) 3633 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3634 Application: s.wordpress, 3635 Machine: s.machine0, 3636 }) 3637 3638 s.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20) 3639 3640 s.mysqlCharm = s.Factory.MakeCharm(c, &factory.CharmParams{ 3641 Name: "mysql", 3642 }) 3643 s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 3644 Name: "mysql", 3645 Charm: s.mysqlCharm, 3646 EndpointBindings: map[string]string{ 3647 "server": "database", 3648 }, 3649 }) 3650 s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3651 Application: s.wordpress, 3652 Machine: s.machine0, 3653 }) 3654 s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 3655 Application: s.mysql, 3656 Machine: s.machine1, 3657 }) 3658 3659 // Create the resource registry separately to track invocations to 3660 // Register. 3661 s.resources = common.NewResources() 3662 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 3663 3664 s.setupUniterAPIForUnit(c, s.wordpressUnit) 3665 } 3666 3667 func (s *uniterNetworkInfoSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine { 3668 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 3669 c.Assert(err, jc.ErrorIsNil) 3670 devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix) 3671 err = machine.SetInstanceInfo("i-am", "", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil, nil) 3672 c.Assert(err, jc.ErrorIsNil) 3673 3674 machineAddrs, err := machine.AllAddresses() 3675 c.Assert(err, jc.ErrorIsNil) 3676 3677 netAddrs := make([]network.Address, len(machineAddrs)) 3678 for i, addr := range machineAddrs { 3679 netAddrs[i] = network.NewAddress(addr.Value()) 3680 } 3681 err = machine.SetProviderAddresses(netAddrs...) 3682 c.Assert(err, jc.ErrorIsNil) 3683 3684 return machine 3685 } 3686 3687 func (s *uniterNetworkInfoSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) { 3688 return []state.LinkLayerDeviceArgs{{ 3689 Name: "eth0", 3690 Type: state.EthernetDevice, 3691 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:50", addrSuffix), 3692 }, { 3693 Name: "eth0.100", 3694 Type: state.VLAN_8021QDevice, 3695 ParentName: "eth0", 3696 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:50", addrSuffix), 3697 }, { 3698 Name: "eth1", 3699 Type: state.EthernetDevice, 3700 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:51", addrSuffix), 3701 }, { 3702 Name: "eth1.100", 3703 Type: state.VLAN_8021QDevice, 3704 ParentName: "eth1", 3705 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:51", addrSuffix), 3706 }, { 3707 Name: "eth2", 3708 Type: state.EthernetDevice, 3709 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:52", addrSuffix), 3710 }, { 3711 Name: "eth3", 3712 Type: state.EthernetDevice, 3713 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:53", addrSuffix), 3714 }, { 3715 Name: "eth4", 3716 Type: state.EthernetDevice, 3717 MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:54", addrSuffix), 3718 }}, 3719 []state.LinkLayerDeviceAddress{{ 3720 DeviceName: "eth0", 3721 ConfigMethod: state.StaticAddress, 3722 CIDRAddress: fmt.Sprintf("8.8.8.%d/16", addrSuffix), 3723 }, { 3724 DeviceName: "eth0.100", 3725 ConfigMethod: state.StaticAddress, 3726 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix), 3727 }, { 3728 DeviceName: "eth1", 3729 ConfigMethod: state.StaticAddress, 3730 CIDRAddress: fmt.Sprintf("8.8.4.%d/16", addrSuffix), 3731 }, { 3732 DeviceName: "eth1", 3733 ConfigMethod: state.StaticAddress, 3734 CIDRAddress: fmt.Sprintf("8.8.4.%d/16", addrSuffix+1), 3735 }, { 3736 DeviceName: "eth1.100", 3737 ConfigMethod: state.StaticAddress, 3738 CIDRAddress: fmt.Sprintf("10.0.0.%d/24", addrSuffix+1), 3739 }, { 3740 DeviceName: "eth2", 3741 ConfigMethod: state.StaticAddress, 3742 CIDRAddress: fmt.Sprintf("100.64.0.%d/16", addrSuffix), 3743 }, { 3744 DeviceName: "eth4", 3745 ConfigMethod: state.StaticAddress, 3746 CIDRAddress: fmt.Sprintf("192.168.1.%d/24", addrSuffix), 3747 }} 3748 } 3749 3750 func (s *uniterNetworkInfoSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) { 3751 // Create a FakeAuthorizer so we can check permissions, set up assuming the 3752 // given unit agent has logged in. 3753 s.authorizer = apiservertesting.FakeAuthorizer{ 3754 Tag: givenUnit.Tag(), 3755 } 3756 3757 var err error 3758 s.uniter, err = uniter.NewUniterAPI(facadetest.Context{ 3759 State_: s.State, 3760 Resources_: s.resources, 3761 Auth_: s.authorizer, 3762 LeadershipChecker_: s.State.LeadershipChecker(), 3763 }) 3764 c.Assert(err, jc.ErrorIsNil) 3765 } 3766 3767 func (s *uniterNetworkInfoSuite) addRelationAndAssertInScope(c *gc.C) { 3768 // Add a relation between wordpress and mysql and enter scope with 3769 // mysqlUnit. 3770 rel := s.addRelation(c, "wordpress", "mysql") 3771 wpRelUnit, err := rel.Unit(s.wordpressUnit) 3772 c.Assert(err, jc.ErrorIsNil) 3773 err = wpRelUnit.EnterScope(nil) 3774 c.Assert(err, jc.ErrorIsNil) 3775 s.assertInScope(c, wpRelUnit, true) 3776 } 3777 3778 func (s *uniterNetworkInfoSuite) TestNetworkInfoPermissions(c *gc.C) { 3779 s.addRelationAndAssertInScope(c) 3780 var tests = []struct { 3781 Name string 3782 Arg params.NetworkInfoParams 3783 Result params.NetworkInfoResults 3784 Error string 3785 }{ 3786 { 3787 "Wrong unit name", 3788 params.NetworkInfoParams{Unit: "unit-foo-0", Bindings: []string{"foo"}}, 3789 params.NetworkInfoResults{}, 3790 "permission denied", 3791 }, 3792 { 3793 "Invalid tag", 3794 params.NetworkInfoParams{Unit: "invalid", Bindings: []string{"db-client"}}, 3795 params.NetworkInfoResults{}, 3796 `"invalid" is not a valid tag`, 3797 }, 3798 { 3799 "No access to unit", 3800 params.NetworkInfoParams{Unit: "unit-mysql-0", Bindings: []string{"juju-info"}}, 3801 params.NetworkInfoResults{}, 3802 "permission denied", 3803 }, 3804 { 3805 "Unknown binding name", 3806 params.NetworkInfoParams{Unit: s.wordpressUnit.Tag().String(), Bindings: []string{"unknown"}}, 3807 params.NetworkInfoResults{ 3808 Results: map[string]params.NetworkInfoResult{ 3809 "unknown": { 3810 Error: ¶ms.Error{ 3811 Message: `binding name "unknown" not defined by the unit's charm`, 3812 }, 3813 }, 3814 }, 3815 }, 3816 "", 3817 }, 3818 } 3819 3820 for _, test := range tests { 3821 c.Logf("Testing %s", test.Name) 3822 result, err := s.uniter.NetworkInfo(test.Arg) 3823 if test.Error != "" { 3824 c.Check(err, gc.ErrorMatches, test.Error) 3825 } else { 3826 c.Assert(err, jc.ErrorIsNil) 3827 c.Check(result, jc.DeepEquals, test.Result) 3828 } 3829 } 3830 } 3831 3832 func (s *uniterNetworkInfoSuite) TestNetworkInfoForExplicitlyBoundEndpointAndDefaultSpace(c *gc.C) { 3833 s.addRelationAndAssertInScope(c) 3834 3835 args := params.NetworkInfoParams{ 3836 Unit: s.wordpressUnit.Tag().String(), 3837 Bindings: []string{"db", "admin-api", "db-client"}, 3838 } 3839 // For the relation "wordpress:db mysql:server" we expect to see only 3840 // ifaces in the "internal" space, where the "db" endpoint itself 3841 // is bound to. 3842 expectedConfigWithRelationName := params.NetworkInfoResult{ 3843 Info: []params.NetworkInfo{ 3844 { 3845 MACAddress: "00:11:22:33:10:50", 3846 InterfaceName: "eth0.100", 3847 Addresses: []params.InterfaceAddress{ 3848 {Address: "10.0.0.10", CIDR: "10.0.0.0/24"}, 3849 }, 3850 }, 3851 { 3852 MACAddress: "00:11:22:33:10:51", 3853 InterfaceName: "eth1.100", 3854 Addresses: []params.InterfaceAddress{ 3855 {Address: "10.0.0.11", CIDR: "10.0.0.0/24"}, 3856 }, 3857 }, 3858 }, 3859 EgressSubnets: []string{"10.0.0.10/32"}, 3860 IngressAddresses: []string{"10.0.0.10", "10.0.0.11"}, 3861 } 3862 // For the "admin-api" extra-binding we expect to see only interfaces from 3863 // the "public" space. 3864 expectedConfigWithExtraBindingName := params.NetworkInfoResult{ 3865 Info: []params.NetworkInfo{ 3866 { 3867 MACAddress: "00:11:22:33:10:50", 3868 InterfaceName: "eth0", 3869 Addresses: []params.InterfaceAddress{ 3870 {Address: "8.8.8.10", CIDR: "8.8.0.0/16"}, 3871 }, 3872 }, 3873 { 3874 MACAddress: "00:11:22:33:10:51", 3875 InterfaceName: "eth1", 3876 Addresses: []params.InterfaceAddress{ 3877 {Address: "8.8.4.10", CIDR: "8.8.0.0/16"}, 3878 {Address: "8.8.4.11", CIDR: "8.8.0.0/16"}, 3879 }, 3880 }, 3881 }, 3882 EgressSubnets: []string{"8.8.8.10/32"}, 3883 IngressAddresses: []string{"8.8.8.10", "8.8.4.10", "8.8.4.11"}, 3884 } 3885 3886 // For the "db-client" extra-binding we expect to see interfaces from default 3887 // "wp-default" space 3888 expectedConfigWithDefaultSpace := params.NetworkInfoResult{ 3889 Info: []params.NetworkInfo{ 3890 { 3891 MACAddress: "00:11:22:33:10:52", 3892 InterfaceName: "eth2", 3893 Addresses: []params.InterfaceAddress{ 3894 {Address: "100.64.0.10", CIDR: "100.64.0.0/16"}, 3895 }, 3896 }, 3897 }, 3898 EgressSubnets: []string{"100.64.0.10/32"}, 3899 IngressAddresses: []string{"100.64.0.10"}, 3900 } 3901 3902 result, err := s.uniter.NetworkInfo(args) 3903 c.Assert(err, jc.ErrorIsNil) 3904 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 3905 Results: map[string]params.NetworkInfoResult{ 3906 "db": expectedConfigWithRelationName, 3907 "admin-api": expectedConfigWithExtraBindingName, 3908 "db-client": expectedConfigWithDefaultSpace, 3909 }, 3910 }) 3911 } 3912 3913 func (s *uniterNetworkInfoSuite) TestNetworkInfoL2Binding(c *gc.C) { 3914 c.Skip("L2 not supported yet") 3915 s.addRelationAndAssertInScope(c) 3916 3917 args := params.NetworkInfoParams{ 3918 Unit: s.wordpressUnit.Tag().String(), 3919 Bindings: []string{"foo-bar"}, 3920 } 3921 3922 expectedInfo := params.NetworkInfoResult{ 3923 Info: []params.NetworkInfo{ 3924 { 3925 MACAddress: "00:11:22:33:10:50", 3926 InterfaceName: "eth2", 3927 }, 3928 }, 3929 } 3930 3931 result, err := s.uniter.NetworkInfo(args) 3932 c.Assert(err, jc.ErrorIsNil) 3933 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 3934 Results: map[string]params.NetworkInfoResult{ 3935 "foo-bar": expectedInfo, 3936 }, 3937 }) 3938 } 3939 3940 func (s *uniterNetworkInfoSuite) TestNetworkInfoForImplicitlyBoundEndpoint(c *gc.C) { 3941 // Since wordpressUnit has explicit binding for "db", switch the API to 3942 // mysqlUnit and check "mysql:server" uses the machine preferred private 3943 // address. 3944 s.setupUniterAPIForUnit(c, s.mysqlUnit) 3945 rel := s.addRelation(c, "mysql", "wordpress") 3946 mysqlRelUnit, err := rel.Unit(s.mysqlUnit) 3947 c.Assert(err, jc.ErrorIsNil) 3948 err = mysqlRelUnit.EnterScope(nil) 3949 c.Assert(err, jc.ErrorIsNil) 3950 s.assertInScope(c, mysqlRelUnit, true) 3951 3952 args := params.NetworkInfoParams{ 3953 Unit: s.mysqlUnit.Tag().String(), 3954 Bindings: []string{"server"}, 3955 } 3956 3957 expectedInfo := params.NetworkInfoResult{ 3958 Info: []params.NetworkInfo{ 3959 { 3960 MACAddress: "00:11:22:33:20:54", 3961 InterfaceName: "eth4", 3962 Addresses: []params.InterfaceAddress{ 3963 {Address: "192.168.1.20", CIDR: "192.168.1.0/24"}, 3964 }, 3965 }, 3966 }, 3967 EgressSubnets: []string{"192.168.1.20/32"}, 3968 IngressAddresses: []string{"192.168.1.20"}, 3969 } 3970 3971 result, err := s.uniter.NetworkInfo(args) 3972 c.Assert(err, jc.ErrorIsNil) 3973 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 3974 Results: map[string]params.NetworkInfoResult{ 3975 "server": expectedInfo, 3976 }, 3977 }) 3978 } 3979 3980 func (s *uniterNetworkInfoSuite) TestNetworkInfoUsesRelationAddressNonDefaultBinding(c *gc.C) { 3981 // If a network info call is made in the context of a relation, and the 3982 // endpoint of that relation is bound to the non default space, we 3983 // provide the ingress addresses as those belonging to the space. 3984 s.setupUniterAPIForUnit(c, s.mysqlUnit) 3985 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 3986 SourceModel: coretesting.ModelTag, 3987 Name: "wordpress-remote", 3988 Endpoints: []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer"}}, 3989 }) 3990 c.Assert(err, jc.ErrorIsNil) 3991 rel := s.addRelation(c, "mysql", "wordpress-remote") 3992 mysqlRelUnit, err := rel.Unit(s.mysqlUnit) 3993 c.Assert(err, jc.ErrorIsNil) 3994 err = mysqlRelUnit.EnterScope(nil) 3995 c.Assert(err, jc.ErrorIsNil) 3996 s.assertInScope(c, mysqlRelUnit, true) 3997 3998 // Relation specific egress subnets override model config. 3999 err = s.JujuConnSuite.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil) 4000 c.Assert(err, jc.ErrorIsNil) 4001 relEgress := state.NewRelationEgressNetworks(s.State) 4002 _, err = relEgress.Save(rel.Tag().Id(), false, []string{"192.168.1.0/24"}) 4003 c.Assert(err, jc.ErrorIsNil) 4004 4005 relId := rel.Id() 4006 args := params.NetworkInfoParams{ 4007 Unit: s.mysqlUnit.Tag().String(), 4008 Bindings: []string{"server"}, 4009 RelationId: &relId, 4010 } 4011 4012 expectedInfo := params.NetworkInfoResult{ 4013 Info: []params.NetworkInfo{ 4014 { 4015 MACAddress: "00:11:22:33:20:54", 4016 InterfaceName: "eth4", 4017 Addresses: []params.InterfaceAddress{ 4018 {Address: "192.168.1.20", CIDR: "192.168.1.0/24"}, 4019 }, 4020 }, 4021 }, 4022 EgressSubnets: []string{"192.168.1.0/24"}, 4023 IngressAddresses: []string{"192.168.1.20"}, 4024 } 4025 4026 result, err := s.uniter.NetworkInfo(args) 4027 c.Assert(err, jc.ErrorIsNil) 4028 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 4029 Results: map[string]params.NetworkInfoResult{ 4030 "server": expectedInfo, 4031 }, 4032 }) 4033 } 4034 4035 func (s *uniterNetworkInfoSuite) TestNetworkInfoUsesRelationAddressDefaultBinding(c *gc.C) { 4036 // If a network info call is made in the context of a relation, and the 4037 // endpoint of that relation is not bound, or bound to the default space, we 4038 // provide the ingress address relevant to the relation: public for CMR. 4039 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 4040 SourceModel: coretesting.ModelTag, 4041 Name: "wordpress-remote", 4042 Endpoints: []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer"}}, 4043 }) 4044 c.Assert(err, jc.ErrorIsNil) 4045 4046 // Recreate mysql app without endpoint binding. 4047 s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{ 4048 Name: "mysql-default", 4049 Charm: s.mysqlCharm, 4050 }) 4051 s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{ 4052 Application: s.mysql, 4053 Machine: s.machine1, 4054 }) 4055 s.setupUniterAPIForUnit(c, s.mysqlUnit) 4056 4057 rel := s.addRelation(c, "mysql-default", "wordpress-remote") 4058 mysqlRelUnit, err := rel.Unit(s.mysqlUnit) 4059 c.Assert(err, jc.ErrorIsNil) 4060 err = mysqlRelUnit.EnterScope(nil) 4061 c.Assert(err, jc.ErrorIsNil) 4062 s.assertInScope(c, mysqlRelUnit, true) 4063 4064 // Relation specific egress subnets override model config. 4065 err = s.JujuConnSuite.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil) 4066 c.Assert(err, jc.ErrorIsNil) 4067 relEgress := state.NewRelationEgressNetworks(s.State) 4068 _, err = relEgress.Save(rel.Tag().Id(), false, []string{"192.168.1.0/24"}) 4069 c.Assert(err, jc.ErrorIsNil) 4070 4071 relId := rel.Id() 4072 args := params.NetworkInfoParams{ 4073 Unit: s.mysqlUnit.Tag().String(), 4074 Bindings: []string{"server"}, 4075 RelationId: &relId, 4076 } 4077 4078 // Since it is a remote relation, the expected ingress address is set to the 4079 // machine's public address. 4080 expectedIngressAddress, err := s.machine1.PublicAddress() 4081 c.Assert(err, jc.ErrorIsNil) 4082 4083 expectedInfo := params.NetworkInfoResult{ 4084 Info: []params.NetworkInfo{ 4085 { 4086 MACAddress: "00:11:22:33:20:50", 4087 InterfaceName: "eth0.100", 4088 Addresses: []params.InterfaceAddress{ 4089 {Address: "10.0.0.20", CIDR: "10.0.0.0/24"}, 4090 }, 4091 }, 4092 }, 4093 EgressSubnets: []string{"192.168.1.0/24"}, 4094 IngressAddresses: []string{expectedIngressAddress.Value}, 4095 } 4096 4097 result, err := s.uniter.NetworkInfo(args) 4098 c.Assert(err, jc.ErrorIsNil) 4099 c.Check(result, jc.DeepEquals, params.NetworkInfoResults{ 4100 Results: map[string]params.NetworkInfoResult{ 4101 "server": expectedInfo, 4102 }, 4103 }) 4104 } 4105 4106 func (s *uniterNetworkInfoSuite) TestNetworkInfoV6Results(c *gc.C) { 4107 s.addRelationAndAssertInScope(c) 4108 4109 args := params.NetworkInfoParams{ 4110 Unit: s.wordpressUnit.Tag().String(), 4111 Bindings: []string{"db"}, 4112 } 4113 4114 expectedResult := params.NetworkInfoResultsV6{ 4115 Results: map[string]params.NetworkInfoResultV6{ 4116 "db": { 4117 Info: []params.NetworkInfo{ 4118 { 4119 MACAddress: "00:11:22:33:10:50", 4120 InterfaceName: "eth0.100", 4121 Addresses: []params.InterfaceAddress{{Address: "10.0.0.10", CIDR: "10.0.0.0/24"}}, 4122 }, { 4123 MACAddress: "00:11:22:33:10:51", 4124 InterfaceName: "eth1.100", 4125 Addresses: []params.InterfaceAddress{{Address: "10.0.0.11", CIDR: "10.0.0.0/24"}}, 4126 }, 4127 }, 4128 }, 4129 }, 4130 } 4131 4132 apiV6, err := uniter.NewUniterAPIV6(facadetest.Context{ 4133 State_: s.State, 4134 Resources_: s.resources, 4135 Auth_: s.authorizer, 4136 LeadershipChecker_: s.State.LeadershipChecker(), 4137 }) 4138 c.Assert(err, jc.ErrorIsNil) 4139 4140 result, err := apiV6.NetworkInfo(args) 4141 c.Assert(err, jc.ErrorIsNil) 4142 4143 c.Check(result, jc.DeepEquals, expectedResult) 4144 } 4145 4146 func (s *uniterSuite) TestNetworkInfoCAASModelRelation(c *gc.C) { 4147 _, cm, gitlab, gitlabUnit := s.setupCAASModel(c) 4148 4149 st := cm.State() 4150 f := factory.NewFactory(st, s.StatePool) 4151 ch := f.MakeCharm(c, &factory.CharmParams{Name: "mariadb", Series: "kubernetes"}) 4152 f.MakeApplication(c, &factory.ApplicationParams{Name: "mariadb", Charm: ch}) 4153 eps, err := st.InferEndpoints("gitlab", "mariadb") 4154 c.Assert(err, jc.ErrorIsNil) 4155 rel, err := st.AddRelation(eps...) 4156 c.Assert(err, jc.ErrorIsNil) 4157 wpRelUnit, err := rel.Unit(gitlabUnit) 4158 c.Assert(err, jc.ErrorIsNil) 4159 err = wpRelUnit.EnterScope(nil) 4160 c.Assert(err, jc.ErrorIsNil) 4161 4162 var updateUnits state.UpdateUnitsOperation 4163 addr := "10.0.0.1" 4164 updateUnits.Updates = []*state.UpdateUnitOperation{gitlabUnit.UpdateOperation(state.UnitUpdateProperties{ 4165 Address: &addr, 4166 Ports: &[]string{"443"}, 4167 })} 4168 err = gitlab.UpdateUnits(&updateUnits) 4169 c.Assert(err, jc.ErrorIsNil) 4170 4171 err = gitlab.UpdateCloudService("", []network.Address{ 4172 {Value: "192.168.1.2", Scope: network.ScopeCloudLocal}, 4173 {Value: "54.32.1.2", Scope: network.ScopePublic}, 4174 }) 4175 c.Assert(err, jc.ErrorIsNil) 4176 4177 relId := rel.Id() 4178 args := params.NetworkInfoParams{ 4179 Unit: gitlabUnit.Tag().String(), 4180 Bindings: []string{"db"}, 4181 RelationId: &relId, 4182 } 4183 4184 expectedResult := params.NetworkInfoResult{ 4185 Info: []params.NetworkInfo{ 4186 { 4187 Addresses: []params.InterfaceAddress{ 4188 {Address: "10.0.0.1"}, 4189 }, 4190 }, 4191 }, 4192 EgressSubnets: []string{"54.32.1.2/32"}, 4193 IngressAddresses: []string{"54.32.1.2", "192.168.1.2", "10.0.0.1"}, 4194 } 4195 4196 uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{ 4197 State_: st, 4198 Resources_: s.resources, 4199 Auth_: s.authorizer, 4200 LeadershipChecker_: s.State.LeadershipChecker(), 4201 }) 4202 c.Assert(err, jc.ErrorIsNil) 4203 4204 result, err := uniterAPI.NetworkInfo(args) 4205 c.Assert(err, jc.ErrorIsNil) 4206 c.Check(result.Results["db"], jc.DeepEquals, expectedResult) 4207 } 4208 4209 func (s *uniterSuite) TestNetworkInfoCAASModelNoRelation(c *gc.C) { 4210 _, cm, wp, wpUnit := s.setupCAASModel(c) 4211 4212 st := cm.State() 4213 f := factory.NewFactory(st, s.StatePool) 4214 ch := f.MakeCharm(c, &factory.CharmParams{Name: "mariadb", Series: "kubernetes"}) 4215 f.MakeApplication(c, &factory.ApplicationParams{Name: "mariadb", Charm: ch}) 4216 4217 var updateUnits state.UpdateUnitsOperation 4218 addr := "10.0.0.1" 4219 updateUnits.Updates = []*state.UpdateUnitOperation{wpUnit.UpdateOperation(state.UnitUpdateProperties{ 4220 Address: &addr, 4221 Ports: &[]string{"443"}, 4222 })} 4223 err := wp.UpdateUnits(&updateUnits) 4224 c.Assert(err, jc.ErrorIsNil) 4225 4226 err = wp.UpdateCloudService("", []network.Address{ 4227 {Value: "192.168.1.2", Scope: network.ScopeCloudLocal}, 4228 {Value: "54.32.1.2", Scope: network.ScopePublic}, 4229 }) 4230 c.Assert(err, jc.ErrorIsNil) 4231 4232 args := params.NetworkInfoParams{ 4233 Unit: wpUnit.Tag().String(), 4234 Bindings: []string{"db"}, 4235 } 4236 4237 expectedResult := params.NetworkInfoResult{ 4238 Info: []params.NetworkInfo{ 4239 { 4240 Addresses: []params.InterfaceAddress{ 4241 {Address: "10.0.0.1"}, 4242 }, 4243 }, 4244 }, 4245 EgressSubnets: []string{"54.32.1.2/32"}, 4246 IngressAddresses: []string{"54.32.1.2", "192.168.1.2", "10.0.0.1"}, 4247 } 4248 4249 uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{ 4250 State_: st, 4251 Resources_: s.resources, 4252 Auth_: s.authorizer, 4253 LeadershipChecker_: s.State.LeadershipChecker(), 4254 }) 4255 c.Assert(err, jc.ErrorIsNil) 4256 4257 result, err := uniterAPI.NetworkInfo(args) 4258 c.Assert(err, jc.ErrorIsNil) 4259 c.Check(result.Results["db"], jc.DeepEquals, expectedResult) 4260 } 4261 4262 func (s *uniterSuite) TestGetCloudSpecDeniesAccessWhenNotTrusted(c *gc.C) { 4263 result, err := s.uniter.CloudSpec() 4264 c.Assert(err, jc.ErrorIsNil) 4265 c.Assert(result, gc.DeepEquals, params.CloudSpecResult{Error: apiservertesting.ErrUnauthorized}) 4266 } 4267 4268 type cloudSpecUniterSuite struct { 4269 uniterSuiteBase 4270 } 4271 4272 var _ = gc.Suite(&cloudSpecUniterSuite{}) 4273 4274 func (s *cloudSpecUniterSuite) SetUpTest(c *gc.C) { 4275 s.uniterSuiteBase.SetUpTest(c) 4276 4277 // Update the application config for wordpress so that it is authorised to 4278 // retrieve its cloud spec. 4279 conf := map[string]interface{}{application.TrustConfigOptionName: true} 4280 fields := map[string]environschema.Attr{application.TrustConfigOptionName: {Type: environschema.Tbool}} 4281 defaults := map[string]interface{}{application.TrustConfigOptionName: false} 4282 err := s.wordpress.UpdateApplicationConfig(conf, nil, fields, defaults) 4283 c.Assert(err, jc.ErrorIsNil) 4284 } 4285 4286 func (s *cloudSpecUniterSuite) TestGetCloudSpecReturnsSpecWhenTrusted(c *gc.C) { 4287 result, err := s.uniter.CloudSpec() 4288 c.Assert(err, jc.ErrorIsNil) 4289 c.Assert(result.Error, gc.IsNil) 4290 c.Assert(result.Result.Name, gc.Equals, "dummy") 4291 4292 exp := map[string]string{ 4293 "username": "dummy", 4294 "password": "secret", 4295 } 4296 c.Assert(result.Result.Credential.Attributes, gc.DeepEquals, exp) 4297 } 4298 4299 type uniterV8Suite struct { 4300 uniterSuiteBase 4301 uniterV8 *uniter.UniterAPIV8 4302 } 4303 4304 var _ = gc.Suite(&uniterV8Suite{}) 4305 4306 func (s *uniterV8Suite) SetUpTest(c *gc.C) { 4307 s.uniterSuiteBase.SetUpTest(c) 4308 4309 uniterV8, err := uniter.NewUniterAPIV8(facadetest.Context{ 4310 State_: s.State, 4311 Resources_: s.resources, 4312 Auth_: s.authorizer, 4313 LeadershipChecker_: s.State.LeadershipChecker(), 4314 }) 4315 c.Assert(err, jc.ErrorIsNil) 4316 s.uniterV8 = uniterV8 4317 } 4318 4319 func (s *uniterV8Suite) TestWatchConfigSettings(c *gc.C) { 4320 err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) 4321 c.Assert(err, jc.ErrorIsNil) 4322 4323 c.Assert(s.resources.Count(), gc.Equals, 0) 4324 4325 args := params.Entities{Entities: []params.Entity{ 4326 {Tag: "unit-mysql-0"}, 4327 {Tag: "unit-wordpress-0"}, 4328 {Tag: "unit-foo-42"}, 4329 }} 4330 result, err := s.uniterV8.WatchConfigSettings(args) 4331 c.Assert(err, jc.ErrorIsNil) 4332 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 4333 Results: []params.NotifyWatchResult{ 4334 {Error: apiservertesting.ErrUnauthorized}, 4335 {NotifyWatcherId: "1"}, 4336 {Error: apiservertesting.ErrUnauthorized}, 4337 }, 4338 }) 4339 4340 // Verify the resource was registered and stop when done 4341 c.Assert(s.resources.Count(), gc.Equals, 1) 4342 resource := s.resources.Get("1") 4343 defer statetesting.AssertStop(c, resource) 4344 4345 // Check that the Watch has consumed the initial event ("returned" in 4346 // the Watch call) 4347 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 4348 wc.AssertNoChange() 4349 } 4350 4351 func (s *uniterV8Suite) TestWatchUnitAddresses(c *gc.C) { 4352 c.Assert(s.resources.Count(), gc.Equals, 0) 4353 4354 args := params.Entities{Entities: []params.Entity{ 4355 {Tag: "unit-mysql-0"}, 4356 {Tag: "unit-wordpress-0"}, 4357 {Tag: "unit-foo-42"}, 4358 {Tag: "machine-0"}, 4359 {Tag: "application-wordpress"}, 4360 }} 4361 result, err := s.uniterV8.WatchUnitAddresses(args) 4362 c.Assert(err, jc.ErrorIsNil) 4363 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 4364 Results: []params.NotifyWatchResult{ 4365 {Error: apiservertesting.ErrUnauthorized}, 4366 {NotifyWatcherId: "1"}, 4367 {Error: apiservertesting.ErrUnauthorized}, 4368 {Error: apiservertesting.ErrUnauthorized}, 4369 {Error: apiservertesting.ErrUnauthorized}, 4370 }, 4371 }) 4372 4373 // Verify the resource was registered and stop when done 4374 c.Assert(s.resources.Count(), gc.Equals, 1) 4375 resource := s.resources.Get("1") 4376 defer statetesting.AssertStop(c, resource) 4377 4378 // Check that the Watch has consumed the initial event ("returned" in 4379 // the Watch call) 4380 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 4381 wc.AssertNoChange() 4382 } 4383 4384 func (s *uniterV8Suite) TestWatchCAASUnitAddresses(c *gc.C) { 4385 _, cm, _, _ := s.setupCAASModel(c) 4386 c.Assert(s.resources.Count(), gc.Equals, 0) 4387 4388 args := params.Entities{Entities: []params.Entity{ 4389 {Tag: "unit-mysql-0"}, 4390 {Tag: "unit-gitlab-0"}, 4391 {Tag: "unit-foo-42"}, 4392 {Tag: "machine-0"}, 4393 {Tag: "application-gitlab"}, 4394 }} 4395 4396 uniterAPI, err := uniter.NewUniterAPIV8(facadetest.Context{ 4397 State_: cm.State(), 4398 Resources_: s.resources, 4399 Auth_: s.authorizer, 4400 LeadershipChecker_: s.State.LeadershipChecker(), 4401 }) 4402 c.Assert(err, jc.ErrorIsNil) 4403 4404 result, err := uniterAPI.WatchUnitAddresses(args) 4405 c.Assert(err, jc.ErrorIsNil) 4406 c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ 4407 Results: []params.NotifyWatchResult{ 4408 {Error: apiservertesting.ErrUnauthorized}, 4409 {NotifyWatcherId: "1"}, 4410 {Error: apiservertesting.ErrUnauthorized}, 4411 {Error: apiservertesting.ErrUnauthorized}, 4412 {Error: apiservertesting.ErrUnauthorized}, 4413 }, 4414 }) 4415 4416 // Verify the resource was registered and stop when done 4417 c.Assert(s.resources.Count(), gc.Equals, 1) 4418 resource := s.resources.Get("1") 4419 defer statetesting.AssertStop(c, resource) 4420 4421 // Check that the Watch has consumed the initial event ("returned" in 4422 // the Watch call) 4423 wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) 4424 wc.AssertNoChange() 4425 }