github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/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 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/names.v2" 17 "gopkg.in/macaroon.v1" 18 19 "github.com/juju/juju/apiserver" 20 "github.com/juju/juju/apiserver/common" 21 "github.com/juju/juju/apiserver/controller" 22 "github.com/juju/juju/apiserver/facade/facadetest" 23 "github.com/juju/juju/apiserver/params" 24 apiservertesting "github.com/juju/juju/apiserver/testing" 25 "github.com/juju/juju/cloud" 26 "github.com/juju/juju/environs" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/permission" 29 "github.com/juju/juju/state" 30 "github.com/juju/juju/state/multiwatcher" 31 statetesting "github.com/juju/juju/state/testing" 32 "github.com/juju/juju/testing" 33 "github.com/juju/juju/testing/factory" 34 ) 35 36 type controllerSuite struct { 37 statetesting.StateSuite 38 39 controller *controller.ControllerAPI 40 resources *common.Resources 41 authorizer apiservertesting.FakeAuthorizer 42 } 43 44 var _ = gc.Suite(&controllerSuite{}) 45 46 func (s *controllerSuite) SetUpTest(c *gc.C) { 47 // Initial config needs to be set before the StateSuite SetUpTest. 48 s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{ 49 "name": "controller", 50 }) 51 52 s.StateSuite.SetUpTest(c) 53 s.resources = common.NewResources() 54 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 55 56 s.authorizer = apiservertesting.FakeAuthorizer{ 57 Tag: s.Owner, 58 AdminTag: s.Owner, 59 } 60 61 controller, err := controller.NewControllerAPI(s.State, s.resources, s.authorizer) 62 c.Assert(err, jc.ErrorIsNil) 63 s.controller = controller 64 65 loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE) 66 } 67 68 func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) { 69 anAuthoriser := apiservertesting.FakeAuthorizer{ 70 Tag: names.NewUnitTag("mysql/0"), 71 } 72 endPoint, err := controller.NewControllerAPI(s.State, s.resources, anAuthoriser) 73 c.Assert(endPoint, gc.IsNil) 74 c.Assert(err, gc.ErrorMatches, "permission denied") 75 } 76 77 func (s *controllerSuite) checkEnvironmentMatches(c *gc.C, env params.Model, expected *state.Model) { 78 c.Check(env.Name, gc.Equals, expected.Name()) 79 c.Check(env.UUID, gc.Equals, expected.UUID()) 80 c.Check(env.OwnerTag, gc.Equals, expected.Owner().String()) 81 } 82 83 func (s *controllerSuite) TestAllModels(c *gc.C) { 84 admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"}) 85 86 s.Factory.MakeModel(c, &factory.ModelParams{ 87 Name: "owned", Owner: admin.UserTag()}).Close() 88 remoteUserTag := names.NewUserTag("user@remote") 89 st := s.Factory.MakeModel(c, &factory.ModelParams{ 90 Name: "user", Owner: remoteUserTag}) 91 defer st.Close() 92 st.AddModelUser(st.ModelUUID(), 93 state.UserAccessSpec{ 94 User: admin.UserTag(), 95 CreatedBy: remoteUserTag, 96 DisplayName: "Foo Bar", 97 Access: permission.ReadAccess}) 98 99 s.Factory.MakeModel(c, &factory.ModelParams{ 100 Name: "no-access", Owner: remoteUserTag}).Close() 101 102 response, err := s.controller.AllModels() 103 c.Assert(err, jc.ErrorIsNil) 104 // The results are sorted. 105 expected := []string{"controller", "no-access", "owned", "user"} 106 var obtained []string 107 for _, env := range response.UserModels { 108 obtained = append(obtained, env.Name) 109 stateEnv, err := s.State.GetModel(names.NewModelTag(env.UUID)) 110 c.Assert(err, jc.ErrorIsNil) 111 s.checkEnvironmentMatches(c, env.Model, stateEnv) 112 } 113 c.Assert(obtained, jc.DeepEquals, expected) 114 } 115 116 func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) { 117 owner := s.Factory.MakeUser(c, nil) 118 s.Factory.MakeModel(c, &factory.ModelParams{ 119 Name: "first", Owner: owner.UserTag()}).Close() 120 remoteUserTag := names.NewUserTag("user@remote") 121 s.Factory.MakeModel(c, &factory.ModelParams{ 122 Name: "second", Owner: remoteUserTag}).Close() 123 124 results, err := s.controller.HostedModelConfigs() 125 c.Assert(err, jc.ErrorIsNil) 126 c.Assert(len(results.Models), gc.Equals, 2) 127 128 one := results.Models[0] 129 two := results.Models[1] 130 131 c.Assert(one.Name, gc.Equals, "first") 132 c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String()) 133 c.Assert(two.Name, gc.Equals, "second") 134 c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String()) 135 } 136 137 func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environs.CloudSpec { 138 c.Assert(pSpec, gc.NotNil) 139 var credential *cloud.Credential 140 if pSpec.Credential != nil { 141 credentialValue := cloud.NewCredential( 142 cloud.AuthType(pSpec.Credential.AuthType), 143 pSpec.Credential.Attributes, 144 ) 145 credential = &credentialValue 146 } 147 spec := environs.CloudSpec{ 148 Type: pSpec.Type, 149 Name: pSpec.Name, 150 Region: pSpec.Region, 151 Endpoint: pSpec.Endpoint, 152 IdentityEndpoint: pSpec.IdentityEndpoint, 153 StorageEndpoint: pSpec.StorageEndpoint, 154 Credential: credential, 155 } 156 c.Assert(spec.Validate(), jc.ErrorIsNil) 157 return spec 158 } 159 160 func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) { 161 owner := s.Factory.MakeUser(c, nil) 162 s.Factory.MakeModel(c, &factory.ModelParams{ 163 Name: "first", Owner: owner.UserTag()}).Close() 164 remoteUserTag := names.NewUserTag("user@remote") 165 s.Factory.MakeModel(c, &factory.ModelParams{ 166 Name: "second", Owner: remoteUserTag}).Close() 167 168 results, err := s.controller.HostedModelConfigs() 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(len(results.Models), gc.Equals, 2) 171 172 for _, model := range results.Models { 173 c.Assert(model.Error, gc.IsNil) 174 175 cfg, err := config.New(config.NoDefaults, model.Config) 176 c.Assert(err, jc.ErrorIsNil) 177 spec := s.makeCloudSpec(c, model.CloudSpec) 178 _, err = environs.New(environs.OpenParams{ 179 Cloud: spec, 180 Config: cfg, 181 }) 182 c.Assert(err, jc.ErrorIsNil) 183 } 184 } 185 186 func (s *controllerSuite) TestListBlockedModels(c *gc.C) { 187 st := s.Factory.MakeModel(c, &factory.ModelParams{ 188 Name: "test"}) 189 defer st.Close() 190 191 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 192 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 193 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 194 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 195 196 list, err := s.controller.ListBlockedModels() 197 c.Assert(err, jc.ErrorIsNil) 198 199 c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{ 200 params.ModelBlockInfo{ 201 Name: "controller", 202 UUID: s.State.ModelUUID(), 203 OwnerTag: s.Owner.String(), 204 Blocks: []string{ 205 "BlockDestroy", 206 "BlockChange", 207 }, 208 }, 209 params.ModelBlockInfo{ 210 Name: "test", 211 UUID: st.ModelUUID(), 212 OwnerTag: s.Owner.String(), 213 Blocks: []string{ 214 "BlockDestroy", 215 "BlockChange", 216 }, 217 }, 218 }) 219 220 } 221 222 func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) { 223 list, err := s.controller.ListBlockedModels() 224 c.Assert(err, jc.ErrorIsNil) 225 c.Assert(list.Models, gc.HasLen, 0) 226 } 227 228 func (s *controllerSuite) TestModelConfig(c *gc.C) { 229 env, err := s.controller.ModelConfig() 230 c.Assert(err, jc.ErrorIsNil) 231 c.Assert(env.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 232 } 233 234 func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) { 235 st := s.Factory.MakeModel(c, &factory.ModelParams{ 236 Name: "test"}) 237 defer st.Close() 238 239 authorizer := &apiservertesting.FakeAuthorizer{ 240 Tag: s.Owner, 241 AdminTag: s.Owner, 242 } 243 controller, err := controller.NewControllerAPI(st, common.NewResources(), authorizer) 244 c.Assert(err, jc.ErrorIsNil) 245 cfg, err := controller.ModelConfig() 246 c.Assert(err, jc.ErrorIsNil) 247 c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"}) 248 } 249 250 func (s *controllerSuite) TestControllerConfig(c *gc.C) { 251 cfg, err := s.controller.ControllerConfig() 252 c.Assert(err, jc.ErrorIsNil) 253 cfgFromDB, err := s.State.ControllerConfig() 254 c.Assert(err, jc.ErrorIsNil) 255 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 256 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 257 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 258 } 259 260 func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) { 261 st := s.Factory.MakeModel(c, &factory.ModelParams{ 262 Name: "test"}) 263 defer st.Close() 264 265 authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner} 266 controller, err := controller.NewControllerAPI(st, common.NewResources(), authorizer) 267 c.Assert(err, jc.ErrorIsNil) 268 cfg, err := controller.ControllerConfig() 269 c.Assert(err, jc.ErrorIsNil) 270 cfgFromDB, err := s.State.ControllerConfig() 271 c.Assert(err, jc.ErrorIsNil) 272 c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID()) 273 c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort()) 274 c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort()) 275 } 276 277 func (s *controllerSuite) TestRemoveBlocks(c *gc.C) { 278 st := s.Factory.MakeModel(c, &factory.ModelParams{ 279 Name: "test"}) 280 defer st.Close() 281 282 s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 283 s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 284 st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel") 285 st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock") 286 287 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true}) 288 c.Assert(err, jc.ErrorIsNil) 289 290 blocks, err := s.State.AllBlocksForController() 291 c.Assert(err, jc.ErrorIsNil) 292 c.Assert(blocks, gc.HasLen, 0) 293 } 294 295 func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) { 296 err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{}) 297 c.Assert(err, gc.ErrorMatches, "not supported") 298 } 299 300 func (s *controllerSuite) TestWatchAllModels(c *gc.C) { 301 watcherId, err := s.controller.WatchAllModels() 302 c.Assert(err, jc.ErrorIsNil) 303 304 watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{ 305 State_: s.State, 306 Resources_: s.resources, 307 Auth_: s.authorizer, 308 ID_: watcherId.AllWatcherId, 309 }) 310 c.Assert(err, jc.ErrorIsNil) 311 watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher) 312 defer func() { 313 err := watcherAPI.Stop() 314 c.Assert(err, jc.ErrorIsNil) 315 }() 316 317 resultC := make(chan params.AllWatcherNextResults) 318 go func() { 319 result, err := watcherAPI.Next() 320 c.Assert(err, jc.ErrorIsNil) 321 resultC <- result 322 }() 323 324 select { 325 case result := <-resultC: 326 // Expect to see the initial environment be reported. 327 deltas := result.Deltas 328 c.Assert(deltas, gc.HasLen, 1) 329 envInfo := deltas[0].Entity.(*multiwatcher.ModelInfo) 330 c.Assert(envInfo.ModelUUID, gc.Equals, s.State.ModelUUID()) 331 case <-time.After(testing.LongWait): 332 c.Fatal("timed out") 333 } 334 } 335 336 func (s *controllerSuite) TestInitiateMigration(c *gc.C) { 337 // Create two hosted models to migrate. 338 st1 := s.Factory.MakeModel(c, nil) 339 defer st1.Close() 340 341 st2 := s.Factory.MakeModel(c, nil) 342 defer st2.Close() 343 344 mac, err := macaroon.New([]byte("secret"), "id", "location") 345 c.Assert(err, jc.ErrorIsNil) 346 macsJSON, err := json.Marshal([]macaroon.Slice{{mac}}) 347 c.Assert(err, jc.ErrorIsNil) 348 349 controller.SetPrecheckResult(s, nil) 350 351 // Kick off migrations 352 args := params.InitiateMigrationArgs{ 353 Specs: []params.MigrationSpec{ 354 { 355 ModelTag: st1.ModelTag().String(), 356 TargetInfo: params.MigrationTargetInfo{ 357 ControllerTag: randomControllerTag(), 358 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 359 CACert: "cert1", 360 AuthTag: names.NewUserTag("admin1").String(), 361 Password: "secret1", 362 }, 363 }, { 364 ModelTag: st2.ModelTag().String(), 365 TargetInfo: params.MigrationTargetInfo{ 366 ControllerTag: randomControllerTag(), 367 Addrs: []string{"3.3.3.3:3333"}, 368 CACert: "cert2", 369 AuthTag: names.NewUserTag("admin2").String(), 370 Macaroons: string(macsJSON), 371 Password: "secret2", 372 }, 373 ExternalControl: true, 374 }, 375 }, 376 } 377 out, err := s.controller.InitiateMigration(args) 378 c.Assert(err, jc.ErrorIsNil) 379 c.Assert(out.Results, gc.HasLen, 2) 380 381 states := []*state.State{st1, st2} 382 for i, spec := range args.Specs { 383 c.Log(i) 384 st := states[i] 385 result := out.Results[i] 386 387 c.Assert(result.Error, gc.IsNil) 388 c.Check(result.ModelTag, gc.Equals, spec.ModelTag) 389 expectedId := st.ModelUUID() + ":0" 390 c.Check(result.MigrationId, gc.Equals, expectedId) 391 392 // Ensure the migration made it into the DB correctly. 393 mig, err := st.LatestMigration() 394 c.Assert(err, jc.ErrorIsNil) 395 c.Check(mig.Id(), gc.Equals, expectedId) 396 c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID()) 397 c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id()) 398 c.Check(mig.ExternalControl(), gc.Equals, args.Specs[i].ExternalControl) 399 400 targetInfo, err := mig.TargetInfo() 401 c.Assert(err, jc.ErrorIsNil) 402 c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag) 403 c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs) 404 c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert) 405 c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag) 406 c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password) 407 408 if spec.TargetInfo.Macaroons != "" { 409 macJSONdb, err := json.Marshal(targetInfo.Macaroons) 410 c.Assert(err, jc.ErrorIsNil) 411 c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons) 412 } 413 } 414 } 415 416 func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) { 417 // Create a hosted model to migrate. 418 st := s.Factory.MakeModel(c, nil) 419 defer st.Close() 420 421 // Kick off the migration with missing details. 422 args := params.InitiateMigrationArgs{ 423 Specs: []params.MigrationSpec{{ 424 ModelTag: st.ModelTag().String(), 425 // TargetInfo missing 426 }}, 427 } 428 out, err := s.controller.InitiateMigration(args) 429 c.Assert(err, jc.ErrorIsNil) 430 c.Assert(out.Results, gc.HasLen, 1) 431 result := out.Results[0] 432 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 433 c.Check(result.MigrationId, gc.Equals, "") 434 c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag") 435 } 436 437 func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) { 438 st := s.Factory.MakeModel(c, nil) 439 defer st.Close() 440 controller.SetPrecheckResult(s, nil) 441 442 args := params.InitiateMigrationArgs{ 443 Specs: []params.MigrationSpec{ 444 { 445 ModelTag: st.ModelTag().String(), 446 TargetInfo: params.MigrationTargetInfo{ 447 ControllerTag: randomControllerTag(), 448 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 449 CACert: "cert", 450 AuthTag: names.NewUserTag("admin").String(), 451 Password: "secret", 452 }, 453 }, { 454 ModelTag: randomModelTag(), // Doesn't exist. 455 }, 456 }, 457 } 458 out, err := s.controller.InitiateMigration(args) 459 c.Assert(err, jc.ErrorIsNil) 460 c.Assert(out.Results, gc.HasLen, 2) 461 462 c.Check(out.Results[0].ModelTag, gc.Equals, st.ModelTag().String()) 463 c.Check(out.Results[0].Error, gc.IsNil) 464 465 c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag) 466 c.Check(out.Results[1].Error, gc.ErrorMatches, "unable to read model: .+") 467 } 468 469 func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) { 470 st := s.Factory.MakeModel(c, nil) 471 defer st.Close() 472 473 args := params.InitiateMigrationArgs{ 474 Specs: []params.MigrationSpec{ 475 { 476 ModelTag: st.ModelTag().String(), 477 TargetInfo: params.MigrationTargetInfo{ 478 ControllerTag: randomControllerTag(), 479 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 480 CACert: "cert", 481 AuthTag: names.NewUserTag("admin").String(), 482 Macaroons: "BLAH", 483 }, 484 }, 485 }, 486 } 487 out, err := s.controller.InitiateMigration(args) 488 c.Assert(err, jc.ErrorIsNil) 489 c.Assert(out.Results, gc.HasLen, 1) 490 result := out.Results[0] 491 c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag) 492 c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+") 493 } 494 495 func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) { 496 st := s.Factory.MakeModel(c, nil) 497 defer st.Close() 498 499 controller.SetPrecheckResult(s, errors.New("boom")) 500 501 args := params.InitiateMigrationArgs{ 502 Specs: []params.MigrationSpec{{ 503 ModelTag: st.ModelTag().String(), 504 TargetInfo: params.MigrationTargetInfo{ 505 ControllerTag: randomControllerTag(), 506 Addrs: []string{"1.1.1.1:1111"}, 507 CACert: "cert1", 508 AuthTag: names.NewUserTag("admin1").String(), 509 Password: "secret1", 510 }, 511 }}, 512 } 513 out, err := s.controller.InitiateMigration(args) 514 c.Assert(out.Results, gc.HasLen, 1) 515 c.Check(out.Results[0].Error, gc.ErrorMatches, "boom") 516 517 active, err := st.IsMigrationActive() 518 c.Assert(err, jc.ErrorIsNil) 519 c.Check(active, jc.IsFalse) 520 } 521 522 func (s *controllerSuite) TestInitiateMigrationSkipPrechecks(c *gc.C) { 523 st := s.Factory.MakeModel(c, nil) 524 defer st.Close() 525 controller.SetPrecheckResult(s, errors.New("should not happen")) 526 527 args := params.InitiateMigrationArgs{ 528 Specs: []params.MigrationSpec{ 529 { 530 ModelTag: st.ModelTag().String(), 531 TargetInfo: params.MigrationTargetInfo{ 532 ControllerTag: randomControllerTag(), 533 Addrs: []string{"1.1.1.1:1111", "2.2.2.2:2222"}, 534 CACert: "cert", 535 AuthTag: names.NewUserTag("admin").String(), 536 Password: "secret", 537 }, 538 ExternalControl: true, 539 SkipInitialPrechecks: true, 540 }, 541 }, 542 } 543 out, err := s.controller.InitiateMigration(args) 544 c.Assert(err, jc.ErrorIsNil) 545 c.Assert(out.Results, gc.HasLen, 1) 546 c.Check(out.Results[0].ModelTag, gc.Equals, st.ModelTag().String()) 547 c.Check(out.Results[0].Error, gc.IsNil) 548 } 549 550 func randomControllerTag() string { 551 uuid := utils.MustNewUUID().String() 552 return names.NewControllerTag(uuid).String() 553 } 554 555 func randomModelTag() string { 556 uuid := utils.MustNewUUID().String() 557 return names.NewModelTag(uuid).String() 558 } 559 560 func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error { 561 args := params.ModifyControllerAccessRequest{ 562 Changes: []params.ModifyControllerAccess{{ 563 UserTag: user.String(), 564 Action: action, 565 Access: access, 566 }}} 567 result, err := s.controller.ModifyControllerAccess(args) 568 c.Assert(err, jc.ErrorIsNil) 569 return result.OneError() 570 } 571 572 func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error { 573 return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access) 574 } 575 576 func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error { 577 return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access) 578 } 579 580 func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) { 581 user := names.NewLocalUserTag("foobar") 582 err := s.controllerGrant(c, user, string(permission.AddModelAccess)) 583 expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found` 584 c.Assert(err, gc.ErrorMatches, expectedErr) 585 } 586 587 func (s *controllerSuite) TestRevokeSuperuserLeavesAddModelAccess(c *gc.C) { 588 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 589 590 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 591 c.Assert(err, gc.IsNil) 592 ctag := names.NewControllerTag(s.State.ControllerUUID()) 593 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 594 c.Assert(err, jc.ErrorIsNil) 595 c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess) 596 597 err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess)) 598 c.Assert(err, gc.IsNil) 599 600 controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object) 601 c.Assert(err, jc.ErrorIsNil) 602 c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess) 603 } 604 605 func (s *controllerSuite) TestRevokeAddModelLeavesLoginAccess(c *gc.C) { 606 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 607 608 err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess)) 609 c.Assert(err, gc.IsNil) 610 ctag := names.NewControllerTag(s.State.ControllerUUID()) 611 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 612 c.Assert(err, jc.ErrorIsNil) 613 c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess) 614 615 err = s.controllerRevoke(c, user.UserTag(), string(permission.AddModelAccess)) 616 c.Assert(err, gc.IsNil) 617 618 controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object) 619 c.Assert(err, jc.ErrorIsNil) 620 c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess) 621 } 622 623 func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) { 624 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 625 err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess)) 626 c.Assert(err, gc.IsNil) 627 628 ctag := names.NewControllerTag(s.State.ControllerUUID()) 629 _, err = s.State.UserAccess(user.UserTag(), ctag) 630 631 c.Assert(errors.IsNotFound(err), jc.IsTrue) 632 } 633 634 func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) { 635 user := names.NewLocalUserTag("foobar") 636 err := s.controllerRevoke(c, user, string(permission.AddModelAccess)) 637 expectedErr := `could not look up controller access for user: user "foobar" not found` 638 c.Assert(err, gc.ErrorMatches, expectedErr) 639 } 640 641 func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) { 642 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 643 644 err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess)) 645 c.Assert(err, gc.IsNil) 646 ctag := names.NewControllerTag(s.State.ControllerUUID()) 647 controllerUser, err := s.State.UserAccess(user.UserTag(), ctag) 648 c.Assert(err, jc.ErrorIsNil) 649 c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess) 650 651 err = s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess)) 652 expectedErr := `could not grant controller access: user already has "add-model" access or greater` 653 c.Assert(err, gc.ErrorMatches, expectedErr) 654 } 655 656 func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) { 657 userTag := names.NewUserTag("foobar@ubuntuone") 658 659 err := s.controllerGrant(c, userTag, string(permission.AddModelAccess)) 660 c.Assert(err, jc.ErrorIsNil) 661 662 ctag := names.NewControllerTag(s.State.ControllerUUID()) 663 controllerUser, err := s.State.UserAccess(userTag, ctag) 664 c.Assert(err, jc.ErrorIsNil) 665 666 c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess) 667 } 668 669 func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) { 670 for _, testParam := range []struct { 671 tag string 672 validTag bool 673 }{{ 674 tag: "unit-foo/0", 675 validTag: true, 676 }, { 677 tag: "application-foo", 678 validTag: true, 679 }, { 680 tag: "relation-wordpress:db mysql:db", 681 validTag: true, 682 }, { 683 tag: "machine-0", 684 validTag: true, 685 }, { 686 tag: "user@local", 687 validTag: false, 688 }, { 689 tag: "user-Mua^h^h^h^arh", 690 validTag: true, 691 }, { 692 tag: "user@", 693 validTag: false, 694 }, { 695 tag: "user@ubuntuone", 696 validTag: false, 697 }, { 698 tag: "user@ubuntuone", 699 validTag: false, 700 }, { 701 tag: "@ubuntuone", 702 validTag: false, 703 }, { 704 tag: "in^valid.", 705 validTag: false, 706 }, { 707 tag: "", 708 validTag: false, 709 }, 710 } { 711 var expectedErr string 712 errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid ` 713 714 if testParam.validTag { 715 // The string is a valid tag, but not a user tag. 716 expectedErr = errPart + `user tag` 717 } else { 718 // The string is not a valid tag of any kind. 719 expectedErr = errPart + `tag` 720 } 721 722 args := params.ModifyControllerAccessRequest{ 723 Changes: []params.ModifyControllerAccess{{ 724 UserTag: testParam.tag, 725 Action: params.GrantControllerAccess, 726 Access: string(permission.SuperuserAccess), 727 }}} 728 729 result, err := s.controller.ModifyControllerAccess(args) 730 c.Assert(err, jc.ErrorIsNil) 731 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 732 } 733 } 734 735 func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) { 736 args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}} 737 738 result, err := s.controller.ModifyControllerAccess(args) 739 c.Assert(err, jc.ErrorIsNil) 740 expectedErr := `"" controller access not valid` 741 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 742 } 743 744 func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) { 745 var dance params.ControllerAction = "dance" 746 args := params.ModifyControllerAccessRequest{ 747 Changes: []params.ModifyControllerAccess{{ 748 UserTag: "user-user@local", 749 Action: dance, 750 Access: string(permission.LoginAccess), 751 }}} 752 753 result, err := s.controller.ModifyControllerAccess(args) 754 c.Assert(err, jc.ErrorIsNil) 755 expectedErr := `unknown action "dance"` 756 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 757 } 758 759 func (s *controllerSuite) TestGetControllerAccess(c *gc.C) { 760 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 761 user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 762 763 err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess)) 764 c.Assert(err, gc.IsNil) 765 err = s.controllerGrant(c, user2.UserTag(), string(permission.AddModelAccess)) 766 c.Assert(err, gc.IsNil) 767 req := params.Entities{ 768 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}}, 769 } 770 results, err := s.controller.GetControllerAccess(req) 771 c.Assert(err, jc.ErrorIsNil) 772 c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{ 773 Result: ¶ms.UserAccess{ 774 Access: "superuser", 775 UserTag: user.Tag().String(), 776 }}, { 777 Result: ¶ms.UserAccess{ 778 Access: "add-model", 779 UserTag: user2.Tag().String(), 780 }}}) 781 } 782 783 func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) { 784 // Set up the user making the call. 785 user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 786 anAuthoriser := apiservertesting.FakeAuthorizer{ 787 Tag: user.Tag(), 788 } 789 endpoint, err := controller.NewControllerAPI(s.State, s.resources, anAuthoriser) 790 c.Assert(err, jc.ErrorIsNil) 791 args := params.ModifyControllerAccessRequest{ 792 Changes: []params.ModifyControllerAccess{{ 793 UserTag: user.Tag().String(), 794 Action: params.GrantControllerAccess, 795 Access: "superuser", 796 }}} 797 result, err := s.controller.ModifyControllerAccess(args) 798 c.Assert(err, jc.ErrorIsNil) 799 c.Assert(result.OneError(), jc.ErrorIsNil) 800 801 // We ask for permissions for a different user as well as ourselves. 802 differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true}) 803 req := params.Entities{ 804 Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}}, 805 } 806 results, err := endpoint.GetControllerAccess(req) 807 c.Assert(err, jc.ErrorIsNil) 808 c.Assert(results.Results, gc.HasLen, 2) 809 c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{ 810 Access: "superuser", 811 UserTag: user.Tag().String(), 812 }) 813 c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{ 814 Message: "permission denied", Code: "unauthorized access", 815 }) 816 }