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