github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/controller/controller_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller_test 5 6 import ( 7 "encoding/json" 8 "regexp" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/pubsub" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/names.v2" 18 "gopkg.in/macaroon.v2-unstable" 19 20 "github.com/juju/juju/apiserver" 21 "github.com/juju/juju/apiserver/common" 22 "github.com/juju/juju/apiserver/facade/facadetest" 23 "github.com/juju/juju/apiserver/facades/client/controller" 24 "github.com/juju/juju/apiserver/params" 25 apiservertesting "github.com/juju/juju/apiserver/testing" 26 "github.com/juju/juju/cloud" 27 corecontroller "github.com/juju/juju/controller" 28 "github.com/juju/juju/environs" 29 "github.com/juju/juju/environs/config" 30 "github.com/juju/juju/permission" 31 pscontroller "github.com/juju/juju/pubsub/controller" 32 "github.com/juju/juju/state" 33 "github.com/juju/juju/state/multiwatcher" 34 statetesting "github.com/juju/juju/state/testing" 35 "github.com/juju/juju/testing" 36 "github.com/juju/juju/testing/factory" 37 ) 38 39 type controllerSuite struct { 40 statetesting.StateSuite 41 42 controller *controller.ControllerAPI 43 resources *common.Resources 44 authorizer apiservertesting.FakeAuthorizer 45 hub *pubsub.StructuredHub 46 } 47 48 var _ = gc.Suite(&controllerSuite{}) 49 50 func (s *controllerSuite) SetUpTest(c *gc.C) { 51 // Initial config needs to be set before the StateSuite SetUpTest. 52 s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{ 53 "name": "controller", 54 }) 55 56 s.StateSuite.SetUpTest(c) 57 58 s.resources = common.NewResources() 59 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 60 61 s.authorizer = apiservertesting.FakeAuthorizer{ 62 Tag: s.Owner, 63 AdminTag: s.Owner, 64 } 65 s.hub = pubsub.NewStructuredHub(nil) 66 67 controller, err := controller.NewControllerAPIv7( 68 facadetest.Context{ 69 State_: s.State, 70 StatePool_: s.StatePool, 71 Resources_: s.resources, 72 Auth_: s.authorizer, 73 Hub_: s.hub, 74 }) 75 c.Assert(err, jc.ErrorIsNil) 76 s.controller = controller 77 78 loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE) 79 } 80 81 func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) { 82 anAuthoriser := apiservertesting.FakeAuthorizer{ 83 Tag: names.NewUnitTag("mysql/0"), 84 } 85 endPoint, err := controller.NewControllerAPIv4( 86 facadetest.Context{ 87 State_: s.State, 88 Resources_: s.resources, 89 Auth_: anAuthoriser, 90 }) 91 c.Assert(endPoint, gc.IsNil) 92 c.Assert(err, gc.ErrorMatches, "permission denied") 93 } 94 95 func (s *controllerSuite) checkModelMatches(c *gc.C, model params.Model, expected *state.Model) { 96 c.Check(model.Name, gc.Equals, expected.Name()) 97 c.Check(model.UUID, gc.Equals, expected.UUID()) 98 c.Check(model.OwnerTag, gc.Equals, expected.Owner().String()) 99 } 100 101 func (s *controllerSuite) TestAllModels(c *gc.C) { 102 admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"}) 103 104 s.Factory.MakeModel(c, &factory.ModelParams{ 105 Name: "owned", Owner: admin.UserTag()}).Close() 106 remoteUserTag := names.NewUserTag("user@remote") 107 st := s.Factory.MakeModel(c, &factory.ModelParams{ 108 Name: "user", Owner: remoteUserTag}) 109 defer st.Close() 110 model, err := st.Model() 111 c.Assert(err, jc.ErrorIsNil) 112 113 model.AddUser( 114 state.UserAccessSpec{ 115 User: admin.UserTag(), 116 CreatedBy: remoteUserTag, 117 DisplayName: "Foo Bar", 118 Access: permission.WriteAccess}) 119 120 s.Factory.MakeModel(c, &factory.ModelParams{ 121 Name: "no-access", Owner: remoteUserTag}).Close() 122 123 response, err := s.controller.AllModels() 124 c.Assert(err, jc.ErrorIsNil) 125 // The results are sorted. 126 expected := []string{"controller", "no-access", "owned", "user"} 127 var obtained []string 128 for _, userModel := range response.UserModels { 129 c.Assert(userModel.Type, gc.Equals, "iaas") 130 obtained = append(obtained, userModel.Name) 131 stateModel, ph, err := s.StatePool.GetModel(userModel.UUID) 132 c.Assert(err, jc.ErrorIsNil) 133 defer ph.Release() 134 s.checkModelMatches(c, userModel.Model, stateModel) 135 } 136 c.Assert(obtained, jc.DeepEquals, expected) 137 } 138 139 func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) { 140 owner := s.Factory.MakeUser(c, nil) 141 s.Factory.MakeModel(c, &factory.ModelParams{ 142 Name: "first", Owner: owner.UserTag()}).Close() 143 remoteUserTag := names.NewUserTag("user@remote") 144 s.Factory.MakeModel(c, &factory.ModelParams{ 145 Name: "second", Owner: remoteUserTag}).Close() 146 147 results, err := s.controller.HostedModelConfigs() 148 c.Assert(err, jc.ErrorIsNil) 149 c.Assert(len(results.Models), gc.Equals, 2) 150 151 one := results.Models[0] 152 two := results.Models[1] 153 154 c.Assert(one.Name, gc.Equals, "first") 155 c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String()) 156 c.Assert(two.Name, gc.Equals, "second") 157 c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String()) 158 } 159 160 func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environs.CloudSpec { 161 c.Assert(pSpec, gc.NotNil) 162 var credential *cloud.Credential 163 if pSpec.Credential != nil { 164 credentialValue := cloud.NewCredential( 165 cloud.AuthType(pSpec.Credential.AuthType), 166 pSpec.Credential.Attributes, 167 ) 168 credential = &credentialValue 169 } 170 spec := environs.CloudSpec{ 171 Type: pSpec.Type, 172 Name: pSpec.Name, 173 Region: pSpec.Region, 174 Endpoint: pSpec.Endpoint, 175 IdentityEndpoint: pSpec.IdentityEndpoint, 176 StorageEndpoint: pSpec.StorageEndpoint, 177 Credential: credential, 178 } 179 c.Assert(spec.Validate(), jc.ErrorIsNil) 180 return spec 181 } 182 183 func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) { 184 owner := s.Factory.MakeUser(c, nil) 185 s.Factory.MakeModel(c, &factory.ModelParams{ 186 Name: "first", Owner: owner.UserTag()}).Close() 187 remoteUserTag := names.NewUserTag("user@remote") 188 s.Factory.MakeModel(c, &factory.ModelParams{ 189 Name: "second", Owner: remoteUserTag}).Close() 190 191 results, err := s.controller.HostedModelConfigs() 192 c.Assert(err, jc.ErrorIsNil) 193 c.Assert(len(results.Models), gc.Equals, 2) 194 195 for _, model := range results.Models { 196 c.Assert(model.Error, gc.IsNil) 197 198 cfg, err := config.New(config.NoDefaults, model.Config) 199 c.Assert(err, jc.ErrorIsNil) 200 spec := s.makeCloudSpec(c, model.CloudSpec) 201 _, err = environs.New(environs.OpenParams{ 202 Cloud: spec, 203 Config: cfg, 204 }) 205 c.Assert(err, jc.ErrorIsNil) 206 } 207 } 208 209 func (s *controllerSuite) TestListBlockedModels(c *gc.C) { 210 st := s.Factory.MakeModel(c, &factory.ModelParams{ 211 Name: "test"}) 212 defer st.Close() 213 214 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 215 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 216 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 217 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 218 219 list, err := s.controller.ListBlockedModels() 220 c.Assert(err, jc.ErrorIsNil) 221 222 c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{ 223 { 224 Name: "controller", 225 UUID: s.State.ModelUUID(), 226 OwnerTag: s.Owner.String(), 227 Blocks: []string{ 228 "BlockDestroy", 229 "BlockChange", 230 }, 231 }, 232 { 233 Name: "test", 234 UUID: st.ModelUUID(), 235 OwnerTag: s.Owner.String(), 236 Blocks: []string{ 237 "BlockDestroy", 238 "BlockChange", 239 }, 240 }, 241 }) 242 243 } 244 245 func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) { 246 list, err := s.controller.ListBlockedModels() 247 c.Assert(err, jc.ErrorIsNil) 248 c.Assert(list.Models, gc.HasLen, 0) 249 } 250 251 func (s *controllerSuite) TestModelConfig(c *gc.C) { 252 cfg, err := s.controller.ModelConfig() 253 c.Assert(err, jc.ErrorIsNil) 254 c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 255 } 256 257 func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) { 258 st := s.Factory.MakeModel(c, &factory.ModelParams{ 259 Name: "test"}) 260 defer st.Close() 261 262 authorizer := &apiservertesting.FakeAuthorizer{ 263 Tag: s.Owner, 264 AdminTag: s.Owner, 265 } 266 controller, err := controller.NewControllerAPIv4( 267 facadetest.Context{ 268 State_: st, 269 StatePool_: s.StatePool, 270 Resources_: common.NewResources(), 271 Auth_: authorizer, 272 }) 273 274 c.Assert(err, jc.ErrorIsNil) 275 cfg, err := controller.ModelConfig() 276 c.Assert(err, jc.ErrorIsNil) 277 c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 278 } 279 280 func (s *controllerSuite) TestControllerConfig(c *gc.C) { 281 cfg, err := s.controller.ControllerConfig() 282 c.Assert(err, jc.ErrorIsNil) 283 cfgFromDB, err := s.State.ControllerConfig() 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 286 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 287 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 288 } 289 290 func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) { 291 st := s.Factory.MakeModel(c, &factory.ModelParams{ 292 Name: "test"}) 293 defer st.Close() 294 295 authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner} 296 controller, err := controller.NewControllerAPIv4( 297 facadetest.Context{ 298 State_: st, 299 Resources_: common.NewResources(), 300 Auth_: authorizer, 301 }) 302 c.Assert(err, jc.ErrorIsNil) 303 cfg, err := controller.ControllerConfig() 304 c.Assert(err, jc.ErrorIsNil) 305 cfgFromDB, err := s.State.ControllerConfig() 306 c.Assert(err, jc.ErrorIsNil) 307 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 308 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 309 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 310 } 311 312 func (s *controllerSuite) TestRemoveBlocks(c *gc.C) { 313 st := s.Factory.MakeModel(c, &factory.ModelParams{ 314 Name: "test"}) 315 defer st.Close() 316 317 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 318 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 319 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 320 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 321 322 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true}) 323 c.Assert(err, jc.ErrorIsNil) 324 325 blocks, err := s.State.AllBlocksForController() 326 c.Assert(err, jc.ErrorIsNil) 327 c.Assert(blocks, gc.HasLen, 0) 328 } 329 330 func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) { 331 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{}) 332 c.Assert(err, gc.ErrorMatches, "not supported") 333 } 334 335 func (s *controllerSuite) TestWatchAllModels(c *gc.C) { 336 watcherId, err := s.controller.WatchAllModels() 337 c.Assert(err, jc.ErrorIsNil) 338 339 var disposed bool 340 watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{ 341 State_: s.State, 342 Resources_: s.resources, 343 Auth_: s.authorizer, 344 ID_: watcherId.AllWatcherId, 345 Dispose_: func() { disposed = true }, 346 }) 347 c.Assert(err, jc.ErrorIsNil) 348 watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher) 349 defer func() { 350 err := watcherAPI.Stop() 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(disposed, jc.IsTrue) 353 }() 354 355 resultC := make(chan params.AllWatcherNextResults) 356 go func() { 357 result, err := watcherAPI.Next() 358 c.Assert(err, jc.ErrorIsNil) 359 resultC <- result 360 }() 361 362 select { 363 case result := <-resultC: 364 // Expect to see the initial environment be reported. 365 deltas := result.Deltas 366 c.Assert(deltas, gc.HasLen, 1) 367 modelInfo := deltas[0].Entity.(*multiwatcher.ModelInfo) 368 c.Assert(modelInfo.ModelUUID, gc.Equals, s.State.ModelUUID()) 369 c.Assert(modelInfo.IsController, gc.Equals, s.State.IsController()) 370 case <-time.After(testing.LongWait): 371 c.Fatal("timed out") 372 } 373 } 374 375 func (s *controllerSuite) TestInitiateMigration(c *gc.C) { 376 // Create two hosted models to migrate. 377 st1 := s.Factory.MakeModel(c, nil) 378 defer st1.Close() 379 model1, err := st1.Model() 380 c.Assert(err, jc.ErrorIsNil) 381 382 st2 := s.Factory.MakeModel(c, nil) 383 defer st2.Close() 384 model2, err := st2.Model() 385 c.Assert(err, jc.ErrorIsNil) 386 387 mac, err := macaroon.New([]byte("secret"), []byte("id"), "location") 388 c.Assert(err, jc.ErrorIsNil) 389 macsJSON, err := json.Marshal([]macaroon.Slice{{mac}}) 390 c.Assert(err, jc.ErrorIsNil) 391 392 controller.SetPrecheckResult(s, nil) 393 394 // Kick off migrations 395 args := params.InitiateMigrationArgs{ 396 Specs: []params.MigrationSpec{ 397 { 398 ModelTag: model1.ModelTag().String(), 399 TargetInfo: params.MigrationTargetInfo{ 400 ControllerTag: randomControllerTag(), 401 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 402 CACert: "cert1", 403 AuthTag: names.NewUserTag("admin1").String(), 404 Password: "secret1", 405 }, 406 }, { 407 ModelTag: model2.ModelTag().String(), 408 TargetInfo: params.MigrationTargetInfo{ 409 ControllerTag: randomControllerTag(), 410 Addrs: []string{"3.3.3.3:3333"}, 411 CACert: "cert2", 412 AuthTag: names.NewUserTag("admin2").String(), 413 Macaroons: string(macsJSON), 414 Password: "secret2", 415 }, 416 }, 417 }, 418 } 419 out, err := s.controller.InitiateMigration(args) 420 c.Assert(err, jc.ErrorIsNil) 421 c.Assert(out.Results, gc.HasLen, 2) 422 423 states := []*state.State{st1, st2} 424 for i, spec := range args.Specs { 425 c.Log(i) 426 st := states[i] 427 result := out.Results[i] 428 429 c.Assert(result.Error, gc.IsNil) 430 c.Check(result.ModelTag, gc.Equals, spec.ModelTag) 431 expectedId := st.ModelUUID() + ":0" 432 c.Check(result.MigrationId, gc.Equals, expectedId) 433 434 // Ensure the migration made it into the DB correctly. 435 mig, err := st.LatestMigration() 436 c.Assert(err, jc.ErrorIsNil) 437 c.Check(mig.Id(), gc.Equals, expectedId) 438 c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID()) 439 c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id()) 440 441 targetInfo, err := mig.TargetInfo() 442 c.Assert(err, jc.ErrorIsNil) 443 c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag) 444 c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs) 445 c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert) 446 c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag) 447 c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password) 448 449 if spec.TargetInfo.Macaroons != "" { 450 macJSONdb, err := json.Marshal(targetInfo.Macaroons) 451 c.Assert(err, jc.ErrorIsNil) 452 c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons) 453 } 454 } 455 } 456 457 func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) { 458 // Create a hosted model to migrate. 459 st := s.Factory.MakeModel(c, nil) 460 defer st.Close() 461 model, err := st.Model() 462 c.Assert(err, jc.ErrorIsNil) 463 464 // Kick off the migration with missing details. 465 args := params.InitiateMigrationArgs{ 466 Specs: []params.MigrationSpec{{ 467 ModelTag: model.ModelTag().String(), 468 // TargetInfo missing 469 }}, 470 } 471 out, err := s.controller.InitiateMigration(args) 472 c.Assert(err, jc.ErrorIsNil) 473 c.Assert(out.Results, gc.HasLen, 1) 474 result := out.Results[0] 475 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 476 c.Check(result.MigrationId, gc.Equals, "") 477 c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag") 478 } 479 480 func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) { 481 st := s.Factory.MakeModel(c, nil) 482 defer st.Close() 483 controller.SetPrecheckResult(s, nil) 484 485 m, err := st.Model() 486 c.Assert(err, jc.ErrorIsNil) 487 488 args := params.InitiateMigrationArgs{ 489 Specs: []params.MigrationSpec{ 490 { 491 ModelTag: m.ModelTag().String(), 492 TargetInfo: params.MigrationTargetInfo{ 493 ControllerTag: randomControllerTag(), 494 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 495 CACert: "cert", 496 AuthTag: names.NewUserTag("admin").String(), 497 Password: "secret", 498 }, 499 }, { 500 ModelTag: randomModelTag(), // Doesn't exist. 501 }, 502 }, 503 } 504 out, err := s.controller.InitiateMigration(args) 505 c.Assert(err, jc.ErrorIsNil) 506 c.Assert(out.Results, gc.HasLen, 2) 507 508 c.Check(out.Results[0].ModelTag, gc.Equals, m.ModelTag().String()) 509 c.Check(out.Results[0].Error, gc.IsNil) 510 511 c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag) 512 c.Check(out.Results[1].Error, gc.ErrorMatches, "model not found") 513 } 514 515 func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) { 516 st := s.Factory.MakeModel(c, nil) 517 defer st.Close() 518 519 m, err := st.Model() 520 c.Assert(err, jc.ErrorIsNil) 521 522 args := params.InitiateMigrationArgs{ 523 Specs: []params.MigrationSpec{ 524 { 525 ModelTag: m.ModelTag().String(), 526 TargetInfo: params.MigrationTargetInfo{ 527 ControllerTag: randomControllerTag(), 528 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 529 CACert: "cert", 530 AuthTag: names.NewUserTag("admin").String(), 531 Macaroons: "BLAH", 532 }, 533 }, 534 }, 535 } 536 out, err := s.controller.InitiateMigration(args) 537 c.Assert(err, jc.ErrorIsNil) 538 c.Assert(out.Results, gc.HasLen, 1) 539 result := out.Results[0] 540 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 541 c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+") 542 } 543 544 func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) { 545 st := s.Factory.MakeModel(c, nil) 546 defer st.Close() 547 548 controller.SetPrecheckResult(s, errors.New("boom")) 549 550 m, err := st.Model() 551 c.Assert(err, jc.ErrorIsNil) 552 553 args := params.InitiateMigrationArgs{ 554 Specs: []params.MigrationSpec{{ 555 ModelTag: m.ModelTag().String(), 556 TargetInfo: params.MigrationTargetInfo{ 557 ControllerTag: randomControllerTag(), 558 Addrs: []string{"1.1.1.1:1111"}, 559 CACert: "cert1", 560 AuthTag: names.NewUserTag("admin1").String(), 561 Password: "secret1", 562 }, 563 }}, 564 } 565 out, err := s.controller.InitiateMigration(args) 566 c.Assert(out.Results, gc.HasLen, 1) 567 c.Check(out.Results[0].Error, gc.ErrorMatches, "boom") 568 569 active, err := st.IsMigrationActive() 570 c.Assert(err, jc.ErrorIsNil) 571 c.Check(active, jc.IsFalse) 572 } 573 574 func randomControllerTag() string { 575 uuid := utils.MustNewUUID().String() 576 return names.NewControllerTag(uuid).String() 577 } 578 579 func randomModelTag() string { 580 uuid := utils.MustNewUUID().String() 581 return names.NewModelTag(uuid).String() 582 } 583 584 func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error { 585 args := params.ModifyControllerAccessRequest{ 586 Changes: []params.ModifyControllerAccess{{ 587 UserTag: user.String(), 588 Action: action, 589 Access: access, 590 }}} 591 result, err := s.controller.ModifyControllerAccess(args) 592 c.Assert(err, jc.ErrorIsNil) 593 return result.OneError() 594 } 595 596 func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error { 597 return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access) 598 } 599 600 func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error { 601 return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access) 602 } 603 604 func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) { 605 user := names.NewLocalUserTag("foobar") 606 err := s.controllerGrant(c, user, string(permission.SuperuserAccess)) 607 expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found` 608 c.Assert(err, gc.ErrorMatches, expectedErr) 609 } 610 611 func (s *controllerSuite) TestRevokeSuperuserLeavesLoginAccess(c *gc.C) { 612 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 613 614 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 615 c.Assert(err, gc.IsNil) 616 ctag := names.NewControllerTag(s.State.ControllerUUID()) 617 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 618 c.Assert(err, jc.ErrorIsNil) 619 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 620 621 err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess)) 622 c.Assert(err, gc.IsNil) 623 624 controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object) 625 c.Assert(err, jc.ErrorIsNil) 626 c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess) 627 } 628 629 func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) { 630 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 631 err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess)) 632 c.Assert(err, gc.IsNil) 633 634 ctag := names.NewControllerTag(s.State.ControllerUUID()) 635 _, err = s.State.UserAccess(user.UserTag(), ctag) 636 637 c.Assert(errors.IsNotFound(err), jc.IsTrue) 638 } 639 640 func (s *controllerSuite) TestRevokeAddModelBackwardCompatibility(c *gc.C) { 641 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 642 643 controllerInfo, err := s.State.ControllerInfo() 644 c.Assert(err, jc.ErrorIsNil) 645 err = s.State.CreateCloudAccess(controllerInfo.CloudName, user.UserTag(), permission.AddModelAccess) 646 c.Assert(err, jc.ErrorIsNil) 647 648 err = s.controllerRevoke(c, user.UserTag(), string(permission.AddModelAccess)) 649 c.Assert(err, jc.ErrorIsNil) 650 651 _, err = s.State.GetCloudAccess(controllerInfo.CloudName, user.UserTag()) 652 c.Assert(err, jc.Satisfies, errors.IsNotFound) 653 } 654 655 func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) { 656 user := names.NewLocalUserTag("foobar") 657 err := s.controllerRevoke(c, user, string(permission.SuperuserAccess)) 658 expectedErr := `could not look up controller access for user: user "foobar" not found` 659 c.Assert(err, gc.ErrorMatches, expectedErr) 660 } 661 662 func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) { 663 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 664 665 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 666 c.Assert(err, gc.IsNil) 667 ctag := names.NewControllerTag(s.State.ControllerUUID()) 668 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 669 c.Assert(err, jc.ErrorIsNil) 670 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 671 672 err = s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 673 expectedErr := `could not grant controller access: user already has "superuser" access or greater` 674 c.Assert(err, gc.ErrorMatches, expectedErr) 675 } 676 677 func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) { 678 userTag := names.NewUserTag("foobar@ubuntuone") 679 680 err := s.controllerGrant(c, userTag, string(permission.SuperuserAccess)) 681 c.Assert(err, jc.ErrorIsNil) 682 683 ctag := names.NewControllerTag(s.State.ControllerUUID()) 684 controllerUser, err := s.State.UserAccess(userTag, ctag) 685 c.Assert(err, jc.ErrorIsNil) 686 687 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 688 } 689 690 func (s *controllerSuite) TestGrantAddModelBackwardCompatibility(c *gc.C) { 691 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 692 693 err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess)) 694 c.Assert(err, jc.ErrorIsNil) 695 696 controllerInfo, err := s.State.ControllerInfo() 697 c.Assert(err, jc.ErrorIsNil) 698 perm, err := s.State.GetCloudAccess(controllerInfo.CloudName, user.UserTag()) 699 c.Assert(err, jc.ErrorIsNil) 700 c.Assert(perm, gc.Equals, permission.AddModelAccess) 701 } 702 703 func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) { 704 for _, testParam := range []struct { 705 tag string 706 validTag bool 707 }{{ 708 tag: "unit-foo/0", 709 validTag: true, 710 }, { 711 tag: "application-foo", 712 validTag: true, 713 }, { 714 tag: "relation-wordpress:db mysql:db", 715 validTag: true, 716 }, { 717 tag: "machine-0", 718 validTag: true, 719 }, { 720 tag: "user@local", 721 validTag: false, 722 }, { 723 tag: "user-Mua^h^h^h^arh", 724 validTag: true, 725 }, { 726 tag: "user@", 727 validTag: false, 728 }, { 729 tag: "user@ubuntuone", 730 validTag: false, 731 }, { 732 tag: "user@ubuntuone", 733 validTag: false, 734 }, { 735 tag: "@ubuntuone", 736 validTag: false, 737 }, { 738 tag: "in^valid.", 739 validTag: false, 740 }, { 741 tag: "", 742 validTag: false, 743 }, 744 } { 745 var expectedErr string 746 errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid ` 747 748 if testParam.validTag { 749 // The string is a valid tag, but not a user tag. 750 expectedErr = errPart + `user tag` 751 } else { 752 // The string is not a valid tag of any kind. 753 expectedErr = errPart + `tag` 754 } 755 756 args := params.ModifyControllerAccessRequest{ 757 Changes: []params.ModifyControllerAccess{{ 758 UserTag: testParam.tag, 759 Action: params.GrantControllerAccess, 760 Access: string(permission.SuperuserAccess), 761 }}} 762 763 result, err := s.controller.ModifyControllerAccess(args) 764 c.Assert(err, jc.ErrorIsNil) 765 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 766 } 767 } 768 769 func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) { 770 args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}} 771 772 result, err := s.controller.ModifyControllerAccess(args) 773 c.Assert(err, jc.ErrorIsNil) 774 expectedErr := `"" controller access not valid` 775 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 776 } 777 778 func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) { 779 var dance params.ControllerAction = "dance" 780 args := params.ModifyControllerAccessRequest{ 781 Changes: []params.ModifyControllerAccess{{ 782 UserTag: "user-user@local", 783 Action: dance, 784 Access: string(permission.LoginAccess), 785 }}} 786 787 result, err := s.controller.ModifyControllerAccess(args) 788 c.Assert(err, jc.ErrorIsNil) 789 expectedErr := `unknown action "dance"` 790 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 791 } 792 793 func (s *controllerSuite) TestGetControllerAccess(c *gc.C) { 794 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 795 user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 796 797 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 798 c.Assert(err, gc.IsNil) 799 req := params.Entities{ 800 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}}, 801 } 802 results, err := s.controller.GetControllerAccess(req) 803 c.Assert(err, jc.ErrorIsNil) 804 c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{ 805 Result: ¶ms.UserAccess{ 806 Access: "superuser", 807 UserTag: user.Tag().String(), 808 }}, { 809 Result: ¶ms.UserAccess{ 810 Access: "login", 811 UserTag: user2.Tag().String(), 812 }}}) 813 } 814 815 func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) { 816 // Set up the user making the call. 817 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 818 anAuthoriser := apiservertesting.FakeAuthorizer{ 819 Tag: user.Tag(), 820 } 821 endpoint, err := controller.NewControllerAPIv4( 822 facadetest.Context{ 823 State_: s.State, 824 Resources_: s.resources, 825 Auth_: anAuthoriser, 826 }) 827 c.Assert(err, jc.ErrorIsNil) 828 args := params.ModifyControllerAccessRequest{ 829 Changes: []params.ModifyControllerAccess{{ 830 UserTag: user.Tag().String(), 831 Action: params.GrantControllerAccess, 832 Access: "superuser", 833 }}} 834 result, err := s.controller.ModifyControllerAccess(args) 835 c.Assert(err, jc.ErrorIsNil) 836 c.Assert(result.OneError(), jc.ErrorIsNil) 837 838 // We ask for permissions for a different user as well as ourselves. 839 differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 840 req := params.Entities{ 841 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}}, 842 } 843 results, err := endpoint.GetControllerAccess(req) 844 c.Assert(err, jc.ErrorIsNil) 845 c.Assert(results.Results, gc.HasLen, 2) 846 c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{ 847 Access: "superuser", 848 UserTag: user.Tag().String(), 849 }) 850 c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{ 851 Message: "permission denied", Code: "unauthorized access", 852 }) 853 } 854 855 func (s *controllerSuite) TestModelStatusV3(c *gc.C) { 856 api, err := controller.NewControllerAPIv3( 857 facadetest.Context{ 858 State_: s.State, 859 StatePool_: s.StatePool, 860 Resources_: s.resources, 861 Auth_: s.authorizer, 862 }) 863 c.Assert(err, jc.ErrorIsNil) 864 865 // Check that we err out immediately if a model errs. 866 results, err := api.ModelStatus(params.Entities{[]params.Entity{{ 867 Tag: "bad-tag", 868 }, { 869 Tag: s.Model.ModelTag().String(), 870 }}}) 871 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 872 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 873 874 // Check that we err out if a model errs even if some firsts in collection pass. 875 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 876 Tag: s.Model.ModelTag().String(), 877 }, { 878 Tag: "bad-tag", 879 }}}) 880 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 881 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 882 883 // Check that we return successfully if no errors. 884 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 885 Tag: s.Model.ModelTag().String(), 886 }}}) 887 c.Assert(err, jc.ErrorIsNil) 888 c.Assert(results.Results, gc.HasLen, 1) 889 } 890 891 func (s *controllerSuite) TestModelStatus(c *gc.C) { 892 // Check that we don't err out immediately if a model errs. 893 results, err := s.controller.ModelStatus(params.Entities{[]params.Entity{{ 894 Tag: "bad-tag", 895 }, { 896 Tag: s.Model.ModelTag().String(), 897 }}}) 898 c.Assert(err, jc.ErrorIsNil) 899 c.Assert(results.Results, gc.HasLen, 2) 900 c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 901 902 // Check that we don't err out if a model errs even if some firsts in collection pass. 903 results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{ 904 Tag: s.Model.ModelTag().String(), 905 }, { 906 Tag: "bad-tag", 907 }}}) 908 c.Assert(err, jc.ErrorIsNil) 909 c.Assert(results.Results, gc.HasLen, 2) 910 c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 911 912 // Check that we return successfully if no errors. 913 results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{ 914 Tag: s.Model.ModelTag().String(), 915 }}}) 916 c.Assert(err, jc.ErrorIsNil) 917 c.Assert(results.Results, gc.HasLen, 1) 918 } 919 920 func (s *controllerSuite) TestConfigSet(c *gc.C) { 921 config, err := s.State.ControllerConfig() 922 c.Assert(err, jc.ErrorIsNil) 923 // Sanity check. 924 c.Assert(config.AuditingEnabled(), gc.Equals, false) 925 926 err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 927 "auditing-enabled": true, 928 }}) 929 c.Assert(err, jc.ErrorIsNil) 930 931 config, err = s.State.ControllerConfig() 932 c.Assert(err, jc.ErrorIsNil) 933 c.Assert(config.AuditingEnabled(), gc.Equals, true) 934 } 935 936 func (s *controllerSuite) TestConfigSetRequiresSuperUser(c *gc.C) { 937 user := s.Factory.MakeUser(c, &factory.UserParams{ 938 Access: permission.ReadAccess, 939 }) 940 anAuthoriser := apiservertesting.FakeAuthorizer{ 941 Tag: user.Tag(), 942 } 943 endpoint, err := controller.NewControllerAPIv5( 944 facadetest.Context{ 945 State_: s.State, 946 Resources_: s.resources, 947 Auth_: anAuthoriser, 948 }) 949 c.Assert(err, jc.ErrorIsNil) 950 951 err = endpoint.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 952 "something": 23, 953 }}) 954 955 c.Assert(err, gc.ErrorMatches, "permission denied") 956 } 957 958 func (s *controllerSuite) TestConfigSetPublishesEvent(c *gc.C) { 959 done := make(chan struct{}) 960 var config corecontroller.Config 961 s.hub.Subscribe(pscontroller.ConfigChanged, func(topic string, data pscontroller.ConfigChangedMessage, err error) { 962 c.Check(err, jc.ErrorIsNil) 963 config = data.Config 964 close(done) 965 }) 966 967 err := s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{ 968 "features": []string{"foo", "bar"}, 969 }}) 970 c.Assert(err, jc.ErrorIsNil) 971 972 select { 973 case <-done: 974 case <-time.After(testing.LongWait): 975 c.Fatal("no event sent}") 976 } 977 978 c.Assert(config.Features().SortedValues(), jc.DeepEquals, []string{"bar", "foo"}) 979 } 980 981 func (s *controllerSuite) TestMongoVersion(c *gc.C) { 982 result, err := s.controller.MongoVersion() 983 c.Assert(err, jc.ErrorIsNil) 984 985 var resErr *params.Error 986 c.Assert(result.Error, gc.Equals, resErr) 987 // We can't guarantee which version of mongo is running, so let's just 988 // attempt to match it to a very basic version (major.minor.patch) 989 c.Assert(result.Result, gc.Matches, "^([0-9]{1,}).([0-9]{1,}).([0-9]{1,})$") 990 } 991 992 func (s *controllerSuite) TestIdentityProviderURL(c *gc.C) { 993 // Preserve default controller config as we will be mutating it just 994 // for this test 995 defer func(orig map[string]interface{}) { 996 s.ControllerConfig = orig 997 }(s.ControllerConfig) 998 999 // Our default test configuration does not specify an IdentityURL 1000 urlRes, err := s.controller.IdentityProviderURL() 1001 c.Assert(err, jc.ErrorIsNil) 1002 c.Assert(urlRes.Result, gc.Equals, "") 1003 1004 // IdentityURL cannot be changed after bootstrap; we need to spin up 1005 // another controller with IdentityURL pre-configured 1006 s.TearDownTest(c) 1007 expURL := "https://api.jujucharms.com/identity" 1008 s.ControllerConfig = map[string]interface{}{ 1009 corecontroller.IdentityURL: expURL, 1010 } 1011 s.SetUpTest(c) 1012 1013 urlRes, err = s.controller.IdentityProviderURL() 1014 c.Assert(err, jc.ErrorIsNil) 1015 c.Assert(urlRes.Result, gc.Equals, expURL) 1016 }