github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelmanager/modelmanager_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelmanager_test 5 6 import ( 7 "regexp" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 gitjujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/names.v2" 16 17 // Register the providers for the field check test 18 "github.com/juju/juju/apiserver/common" 19 "github.com/juju/juju/apiserver/facades/client/modelmanager" 20 "github.com/juju/juju/apiserver/params" 21 apiservertesting "github.com/juju/juju/apiserver/testing" 22 "github.com/juju/juju/caas" 23 "github.com/juju/juju/cloud" 24 "github.com/juju/juju/core/status" 25 "github.com/juju/juju/environs" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/context" 28 jujutesting "github.com/juju/juju/juju/testing" 29 "github.com/juju/juju/permission" 30 _ "github.com/juju/juju/provider/azure" 31 "github.com/juju/juju/provider/dummy" 32 _ "github.com/juju/juju/provider/ec2" 33 _ "github.com/juju/juju/provider/joyent" 34 _ "github.com/juju/juju/provider/maas" 35 _ "github.com/juju/juju/provider/openstack" 36 "github.com/juju/juju/state" 37 "github.com/juju/juju/state/stateenvirons" 38 statetesting "github.com/juju/juju/state/testing" 39 coretesting "github.com/juju/juju/testing" 40 "github.com/juju/juju/testing/factory" 41 jujuversion "github.com/juju/juju/version" 42 ) 43 44 func createArgs(owner names.UserTag) params.ModelCreateArgs { 45 return params.ModelCreateArgs{ 46 Name: "test-model", 47 OwnerTag: owner.String(), 48 Config: map[string]interface{}{ 49 "authorized-keys": "ssh-key", 50 // And to make it a valid dummy config 51 "controller": false, 52 }, 53 } 54 } 55 56 type modelManagerSuite struct { 57 gitjujutesting.IsolationSuite 58 st *mockState 59 ctlrSt *mockState 60 caasSt *mockState 61 caasBroker *mockCaasBroker 62 authoriser apiservertesting.FakeAuthorizer 63 api *modelmanager.ModelManagerAPI 64 caasApi *modelmanager.ModelManagerAPI 65 66 callContext context.ProviderCallContext 67 } 68 69 var _ = gc.Suite(&modelManagerSuite{}) 70 71 func (s *modelManagerSuite) SetUpTest(c *gc.C) { 72 s.IsolationSuite.SetUpTest(c) 73 74 attrs := dummy.SampleConfig() 75 attrs["agent-version"] = jujuversion.Current.String() 76 cfg, err := config.New(config.UseDefaults, attrs) 77 c.Assert(err, jc.ErrorIsNil) 78 79 dummyCloud := cloud.Cloud{ 80 Name: "dummy", 81 Type: "dummy", 82 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 83 Regions: []cloud.Region{ 84 {Name: "some-region"}, 85 {Name: "qux"}, 86 }, 87 } 88 89 mockK8sCloud := cloud.Cloud{ 90 Name: "k8s-cloud", 91 Type: "kubernetes", 92 AuthTypes: []cloud.AuthType{cloud.UserPassAuthType}, 93 } 94 95 controllerModel := &mockModel{ 96 owner: names.NewUserTag("admin"), 97 life: state.Alive, 98 cfg: cfg, 99 status: status.StatusInfo{ 100 Status: status.Available, 101 Since: &time.Time{}, 102 }, 103 users: []*mockModelUser{{ 104 userName: "admin", 105 access: permission.AdminAccess, 106 }, { 107 userName: "add-model", 108 access: permission.AdminAccess, 109 }, { 110 111 userName: "otheruser", 112 access: permission.WriteAccess, 113 }}, 114 } 115 116 s.st = &mockState{ 117 block: -1, 118 cloud: dummyCloud, 119 clouds: map[names.CloudTag]cloud.Cloud{ 120 names.NewCloudTag("some-cloud"): dummyCloud, 121 }, 122 controllerModel: controllerModel, 123 model: &mockModel{ 124 owner: names.NewUserTag("admin"), 125 life: state.Alive, 126 tag: coretesting.ModelTag, 127 cfg: cfg, 128 status: status.StatusInfo{ 129 Status: status.Available, 130 Since: &time.Time{}, 131 }, 132 users: []*mockModelUser{{ 133 userName: "admin", 134 access: permission.AdminAccess, 135 }, { 136 userName: "add-model", 137 access: permission.AdminAccess, 138 }, { 139 140 userName: "otheruser", 141 access: permission.WriteAccess, 142 }}, 143 cfgDefaults: config.ModelDefaultAttributes{ 144 "attr": config.AttributeDefaultValues{ 145 Default: "", 146 Controller: "val", 147 Regions: []config.RegionDefaultValue{{ 148 Name: "dummy", 149 Value: "val++"}}}, 150 "attr2": config.AttributeDefaultValues{ 151 Controller: "val3", 152 Default: "val2", 153 Regions: []config.RegionDefaultValue{{ 154 Name: "left", 155 Value: "spam"}}}, 156 }, 157 }, 158 cred: statetesting.NewEmptyCredential(), 159 cfgDefaults: config.ModelDefaultAttributes{ 160 "attr": config.AttributeDefaultValues{ 161 Default: "", 162 Controller: "val", 163 Regions: []config.RegionDefaultValue{{ 164 Name: "dummy", 165 Value: "val++"}}}, 166 "attr2": config.AttributeDefaultValues{ 167 Controller: "val3", 168 Default: "val2", 169 Regions: []config.RegionDefaultValue{{ 170 Name: "left", 171 Value: "spam"}}}, 172 }, 173 modelConfig: coretesting.ModelConfig(c), 174 } 175 s.ctlrSt = &mockState{ 176 model: controllerModel, 177 controllerModel: controllerModel, 178 cred: statetesting.NewEmptyCredential(), 179 cloud: dummyCloud, 180 clouds: map[names.CloudTag]cloud.Cloud{ 181 names.NewCloudTag("some-cloud"): dummyCloud, 182 }, 183 cloudUsers: map[string]permission.Access{}, 184 } 185 186 caasCred := state.Credential{} 187 caasCred.AuthType = string(cloud.UserPassAuthType) 188 s.caasSt = &mockState{ 189 cloud: mockK8sCloud, 190 clouds: map[names.CloudTag]cloud.Cloud{ 191 names.NewCloudTag("k8s-cloud"): mockK8sCloud, 192 }, 193 controllerModel: controllerModel, 194 model: &mockModel{ 195 owner: names.NewUserTag("admin"), 196 life: state.Alive, 197 tag: coretesting.ModelTag, 198 cfg: cfg, 199 status: status.StatusInfo{ 200 Status: status.Available, 201 Since: &time.Time{}, 202 }, 203 users: []*mockModelUser{{ 204 userName: "admin", 205 access: permission.AdminAccess, 206 }, { 207 userName: "add-model", 208 access: permission.AdminAccess, 209 }}, 210 }, 211 cred: caasCred, 212 modelConfig: coretesting.ModelConfig(c), 213 } 214 215 s.authoriser = apiservertesting.FakeAuthorizer{ 216 Tag: names.NewUserTag("admin"), 217 } 218 219 s.callContext = context.NewCloudCallContext() 220 221 s.caasBroker = &mockCaasBroker{} 222 newBroker := func(args environs.OpenParams) (caas.Broker, error) { 223 return s.caasBroker, nil 224 } 225 226 api, err := modelmanager.NewModelManagerAPI(s.st, s.ctlrSt, nil, newBroker, s.authoriser, s.st.model, s.callContext) 227 c.Assert(err, jc.ErrorIsNil) 228 s.api = api 229 caasApi, err := modelmanager.NewModelManagerAPI(s.caasSt, s.ctlrSt, nil, newBroker, s.authoriser, s.st.model, s.callContext) 230 c.Assert(err, jc.ErrorIsNil) 231 s.caasApi = caasApi 232 } 233 234 func (s *modelManagerSuite) setAPIUser(c *gc.C, user names.UserTag) { 235 s.authoriser.Tag = user 236 newBroker := func(args environs.OpenParams) (caas.Broker, error) { 237 return s.caasBroker, nil 238 } 239 mm, err := modelmanager.NewModelManagerAPI(s.st, s.ctlrSt, nil, newBroker, s.authoriser, s.st.model, s.callContext) 240 c.Assert(err, jc.ErrorIsNil) 241 s.api = mm 242 } 243 244 func (s *modelManagerSuite) getModelArgs(c *gc.C) state.ModelArgs { 245 return getModelArgsFor(c, s.st) 246 } 247 248 func getModelArgsFor(c *gc.C, mockState *mockState) state.ModelArgs { 249 for _, v := range mockState.Calls() { 250 if v.Args == nil { 251 continue 252 } 253 if newModelArgs, ok := v.Args[0].(state.ModelArgs); ok { 254 return newModelArgs 255 } 256 } 257 c.Fatal("failed to find state.ModelArgs") 258 panic("unreachable") 259 } 260 261 func (s *modelManagerSuite) TestCreateModelArgs(c *gc.C) { 262 args := params.ModelCreateArgs{ 263 Name: "foo", 264 OwnerTag: "user-admin", 265 Config: map[string]interface{}{ 266 "bar": "baz", 267 }, 268 CloudRegion: "qux", 269 CloudCredentialTag: "cloudcred-some-cloud_admin_some-credential", 270 } 271 _, err := s.api.CreateModel(args) 272 c.Assert(err, jc.ErrorIsNil) 273 s.st.CheckCallNames(c, 274 "ControllerTag", 275 "ModelUUID", 276 "ControllerTag", 277 "Cloud", 278 "CloudCredential", 279 "ComposeNewModelConfig", 280 "ControllerConfig", 281 "NewModel", 282 "ReloadSpaces", 283 "Close", 284 "GetBackend", 285 "Model", 286 "IsController", 287 "AllMachines", 288 "LatestMigration", 289 ) 290 291 // Check that Model.LastModelConnection is called three times 292 // without making the test depend on other calls to Model 293 n := 0 294 for _, call := range s.st.model.Calls() { 295 if call.FuncName == "LastModelConnection" { 296 n = n + 1 297 } 298 } 299 c.Assert(n, gc.Equals, 3) 300 301 // We cannot predict the UUID, because it's generated, 302 // so we just extract it and ensure that it's not the 303 // same as the controller UUID. 304 newModelArgs := s.getModelArgs(c) 305 uuid := newModelArgs.Config.UUID() 306 c.Assert(uuid, gc.Not(gc.Equals), s.st.controllerModel.cfg.UUID()) 307 308 cfg, err := config.New(config.UseDefaults, map[string]interface{}{ 309 "name": "foo", 310 "type": "dummy", 311 "uuid": uuid, 312 "agent-version": jujuversion.Current.String(), 313 "bar": "baz", 314 "controller": false, 315 "broken": "", 316 "secret": "pork", 317 "something": "value", 318 }) 319 c.Assert(err, jc.ErrorIsNil) 320 321 c.Assert(newModelArgs.StorageProviderRegistry, gc.NotNil) 322 newModelArgs.StorageProviderRegistry = nil 323 324 c.Assert(newModelArgs, jc.DeepEquals, state.ModelArgs{ 325 Type: state.ModelTypeIAAS, 326 Owner: names.NewUserTag("admin"), 327 CloudName: "some-cloud", 328 CloudRegion: "qux", 329 CloudCredential: names.NewCloudCredentialTag( 330 "some-cloud/admin/some-credential", 331 ), 332 Config: cfg, 333 }) 334 } 335 336 func (s *modelManagerSuite) TestCreateModelArgsWithCloud(c *gc.C) { 337 args := params.ModelCreateArgs{ 338 Name: "foo", 339 OwnerTag: "user-admin", 340 Config: map[string]interface{}{ 341 "bar": "baz", 342 }, 343 CloudTag: "cloud-some-cloud", 344 CloudRegion: "qux", 345 CloudCredentialTag: "cloudcred-some-cloud_admin_some-credential", 346 } 347 _, err := s.api.CreateModel(args) 348 c.Assert(err, jc.ErrorIsNil) 349 350 newModelArgs := s.getModelArgs(c) 351 c.Assert(newModelArgs.CloudName, gc.Equals, "some-cloud") 352 } 353 354 func (s *modelManagerSuite) TestCreateModelArgsWithCloudNotFound(c *gc.C) { 355 s.st.SetErrors(errors.NotFoundf("cloud")) 356 args := params.ModelCreateArgs{ 357 Name: "foo", 358 OwnerTag: "user-admin", 359 CloudTag: "cloud-some-unknown-cloud", 360 } 361 _, err := s.api.CreateModel(args) 362 c.Assert(err, gc.ErrorMatches, `cloud "some-unknown-cloud" not found, expected one of \["some-cloud"\]`) 363 } 364 365 func (s *modelManagerSuite) TestCreateModelDefaultRegion(c *gc.C) { 366 args := params.ModelCreateArgs{ 367 Name: "foo", 368 OwnerTag: "user-admin", 369 } 370 _, err := s.api.CreateModel(args) 371 c.Assert(err, jc.ErrorIsNil) 372 373 newModelArgs := s.getModelArgs(c) 374 c.Assert(newModelArgs.CloudRegion, gc.Equals, "some-region") 375 } 376 377 func (s *modelManagerSuite) TestCreateModelDefaultCredentialAdmin(c *gc.C) { 378 s.testCreateModelDefaultCredentialAdmin(c, "user-admin") 379 } 380 381 func (s *modelManagerSuite) TestCreateModelDefaultCredentialAdminNoDomain(c *gc.C) { 382 s.testCreateModelDefaultCredentialAdmin(c, "user-admin") 383 } 384 385 func (s *modelManagerSuite) testCreateModelDefaultCredentialAdmin(c *gc.C, ownerTag string) { 386 s.st.cloud.AuthTypes = []cloud.AuthType{"userpass"} 387 args := params.ModelCreateArgs{ 388 Name: "foo", 389 OwnerTag: ownerTag, 390 } 391 _, err := s.api.CreateModel(args) 392 c.Assert(err, jc.ErrorIsNil) 393 394 newModelArgs := s.getModelArgs(c) 395 c.Assert(newModelArgs.CloudCredential, gc.Equals, names.NewCloudCredentialTag( 396 "some-cloud/bob/some-credential", 397 )) 398 } 399 400 func (s *modelManagerSuite) TestCreateModelEmptyCredentialNonAdmin(c *gc.C) { 401 args := params.ModelCreateArgs{ 402 Name: "foo", 403 OwnerTag: "user-bob", 404 } 405 _, err := s.api.CreateModel(args) 406 c.Assert(err, jc.ErrorIsNil) 407 408 newModelArgs := s.getModelArgs(c) 409 c.Assert(newModelArgs.CloudCredential, gc.Equals, names.CloudCredentialTag{}) 410 } 411 412 func (s *modelManagerSuite) TestCreateModelNoDefaultCredentialNonAdmin(c *gc.C) { 413 s.st.cloud.AuthTypes = nil 414 args := params.ModelCreateArgs{ 415 Name: "foo", 416 OwnerTag: "user-bob", 417 } 418 _, err := s.api.CreateModel(args) 419 c.Assert(err, gc.ErrorMatches, "no credential specified") 420 } 421 422 func (s *modelManagerSuite) TestCreateModelUnknownCredential(c *gc.C) { 423 s.st.SetErrors(nil, errors.NotFoundf("credential")) 424 args := params.ModelCreateArgs{ 425 Name: "foo", 426 OwnerTag: "user-admin", 427 CloudCredentialTag: "cloudcred-some-cloud_admin_bar", 428 } 429 _, err := s.api.CreateModel(args) 430 c.Assert(err, gc.ErrorMatches, `getting credential: credential not found`) 431 } 432 433 func (s *modelManagerSuite) TestCreateCAASModelArgs(c *gc.C) { 434 args := params.ModelCreateArgs{ 435 Name: "foo", 436 OwnerTag: "user-admin", 437 Config: map[string]interface{}{}, 438 CloudTag: "cloud-k8s-cloud", 439 CloudCredentialTag: "cloudcred-k8s-cloud_admin_some-credential", 440 } 441 _, err := s.caasApi.CreateModel(args) 442 c.Assert(err, jc.ErrorIsNil) 443 s.caasSt.CheckCallNames(c, 444 "ControllerTag", 445 "ModelUUID", 446 "ControllerTag", 447 "Cloud", 448 "CloudCredential", 449 "NewModel", 450 "Close", 451 "GetBackend", 452 "Model", 453 "IsController", 454 "AllMachines", 455 "LatestMigration", 456 ) 457 458 // Check that Model.LastModelConnection is called just twice 459 // without making the test depend on other calls to Model 460 n := 0 461 for _, call := range s.caasSt.model.Calls() { 462 if call.FuncName == "LastModelConnection" { 463 n = n + 1 464 } 465 } 466 c.Assert(n, gc.Equals, 2) 467 468 // We cannot predict the UUID, because it's generated, 469 // so we just extract it and ensure that it's not the 470 // same as the controller UUID. 471 newModelArgs := getModelArgsFor(c, s.caasSt) 472 uuid := newModelArgs.Config.UUID() 473 c.Assert(uuid, gc.Not(gc.Equals), s.caasSt.controllerModel.cfg.UUID()) 474 475 cfg, err := config.New(config.UseDefaults, map[string]interface{}{ 476 "name": "foo", 477 "type": "kubernetes", 478 "uuid": uuid, 479 "agent-version": jujuversion.Current.String(), 480 }) 481 c.Assert(err, jc.ErrorIsNil) 482 483 c.Assert(newModelArgs.StorageProviderRegistry, gc.NotNil) 484 newModelArgs.StorageProviderRegistry = nil 485 486 c.Assert(newModelArgs, jc.DeepEquals, state.ModelArgs{ 487 Type: state.ModelTypeCAAS, 488 Owner: names.NewUserTag("admin"), 489 CloudName: "k8s-cloud", 490 CloudCredential: names.NewCloudCredentialTag( 491 "k8s-cloud/admin/some-credential", 492 ), 493 Config: cfg, 494 }) 495 } 496 497 func (s *modelManagerSuite) TestCreateCAASModelNamespaceClash(c *gc.C) { 498 s.caasBroker.namespaces = []string{"foo"} 499 args := params.ModelCreateArgs{ 500 Name: "foo", 501 OwnerTag: "user-admin", 502 Config: map[string]interface{}{}, 503 CloudTag: "cloud-k8s-cloud", 504 CloudCredentialTag: "cloudcred-k8s-cloud_admin_some-credential", 505 } 506 _, err := s.caasApi.CreateModel(args) 507 c.Assert(err, gc.ErrorMatches, `namespace called "foo" already exists, would clash with model name`) 508 } 509 510 func (s *modelManagerSuite) TestModelDefaults(c *gc.C) { 511 result, err := s.api.ModelDefaults() 512 c.Assert(err, jc.ErrorIsNil) 513 expectedValues := map[string]params.ModelDefaults{ 514 "attr": { 515 Controller: "val", 516 Default: "", 517 Regions: []params.RegionDefaults{{ 518 RegionName: "dummy", 519 Value: "val++"}}}, 520 "attr2": { 521 Controller: "val3", 522 Default: "val2", 523 Regions: []params.RegionDefaults{{ 524 RegionName: "left", 525 Value: "spam"}}}, 526 } 527 c.Assert(result.Config, jc.DeepEquals, expectedValues) 528 } 529 530 func (s *modelManagerSuite) TestSetModelDefaults(c *gc.C) { 531 params := params.SetModelDefaults{ 532 Config: []params.ModelDefaultValues{{ 533 Config: map[string]interface{}{ 534 "attr3": "val3", 535 "attr4": "val4"}, 536 }}} 537 result, err := s.api.SetModelDefaults(params) 538 c.Assert(err, jc.ErrorIsNil) 539 c.Assert(result.OneError(), jc.ErrorIsNil) 540 c.Assert(s.st.cfgDefaults, jc.DeepEquals, config.ModelDefaultAttributes{ 541 "attr": { 542 Controller: "val", 543 Default: "", 544 Regions: []config.RegionDefaultValue{{ 545 Name: "dummy", 546 Value: "val++"}}}, 547 "attr2": { 548 Controller: "val3", 549 Default: "val2", 550 Regions: []config.RegionDefaultValue{{ 551 Name: "left", 552 Value: "spam"}}}, 553 "attr3": {Controller: "val3"}, 554 "attr4": {Controller: "val4"}, 555 }) 556 } 557 558 func (s *modelManagerSuite) blockAllChanges(c *gc.C, msg string) { 559 s.st.blockMsg = msg 560 s.st.block = state.ChangeBlock 561 } 562 563 func (s *modelManagerSuite) assertBlocked(c *gc.C, err error, msg string) { 564 c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err)) 565 c.Assert(errors.Cause(err), jc.DeepEquals, ¶ms.Error{ 566 Message: msg, 567 Code: "operation is blocked", 568 }) 569 } 570 571 func (s *modelManagerSuite) TestBlockChangesSetModelDefaults(c *gc.C) { 572 s.blockAllChanges(c, "TestBlockChangesSetModelDefaults") 573 _, err := s.api.SetModelDefaults(params.SetModelDefaults{}) 574 s.assertBlocked(c, err, "TestBlockChangesSetModelDefaults") 575 } 576 577 func (s *modelManagerSuite) TestUnsetModelDefaults(c *gc.C) { 578 args := params.UnsetModelDefaults{ 579 Keys: []params.ModelUnsetKeys{{ 580 Keys: []string{"attr"}, 581 }}} 582 result, err := s.api.UnsetModelDefaults(args) 583 c.Assert(err, jc.ErrorIsNil) 584 c.Assert(result.OneError(), jc.ErrorIsNil) 585 want := config.ModelDefaultAttributes{ 586 "attr": config.AttributeDefaultValues{ 587 Regions: []config.RegionDefaultValue{ 588 {Name: "dummy", Value: "val++"}, 589 }, 590 }, 591 "attr2": config.AttributeDefaultValues{ 592 Default: "val2", 593 Controller: "val3", 594 Regions: []config.RegionDefaultValue{ 595 {Name: "left", Value: "spam"}, 596 }, 597 }, 598 } 599 c.Assert(s.st.cfgDefaults, jc.DeepEquals, want) 600 } 601 602 func (s *modelManagerSuite) TestBlockUnsetModelDefaults(c *gc.C) { 603 s.blockAllChanges(c, "TestBlockUnsetModelDefaults") 604 args := params.UnsetModelDefaults{ 605 Keys: []params.ModelUnsetKeys{{ 606 Keys: []string{"abc"}, 607 }}} 608 _, err := s.api.UnsetModelDefaults(args) 609 s.assertBlocked(c, err, "TestBlockUnsetModelDefaults") 610 } 611 612 func (s *modelManagerSuite) TestUnsetModelDefaultsMissing(c *gc.C) { 613 // It's okay to unset a non-existent attribute. 614 args := params.UnsetModelDefaults{ 615 Keys: []params.ModelUnsetKeys{{ 616 Keys: []string{"not there"}, 617 }}} 618 result, err := s.api.UnsetModelDefaults(args) 619 c.Assert(err, jc.ErrorIsNil) 620 c.Assert(result.OneError(), jc.ErrorIsNil) 621 } 622 623 func (s *modelManagerSuite) TestModelDefaultsAsNormalUser(c *gc.C) { 624 s.setAPIUser(c, names.NewUserTag("charlie")) 625 got, err := s.api.ModelDefaults() 626 c.Assert(err, gc.ErrorMatches, "permission denied") 627 c.Assert(got, gc.DeepEquals, params.ModelDefaultsResult{}) 628 } 629 630 func (s *modelManagerSuite) TestSetModelDefaultsAsNormalUser(c *gc.C) { 631 s.setAPIUser(c, names.NewUserTag("charlie")) 632 got, err := s.api.SetModelDefaults(params.SetModelDefaults{ 633 Config: []params.ModelDefaultValues{{ 634 Config: map[string]interface{}{ 635 "ftp-proxy": "http://charlie", 636 }}}}) 637 c.Assert(err, jc.ErrorIsNil) 638 c.Assert(got, jc.DeepEquals, params.ErrorResults{ 639 Results: []params.ErrorResult{ 640 { 641 Error: ¶ms.Error{ 642 Message: "permission denied", 643 Code: "unauthorized access"}}}}) 644 645 // Make sure it didn't change. 646 s.setAPIUser(c, names.NewUserTag("admin")) 647 cfg, err := s.api.ModelDefaults() 648 c.Assert(err, jc.ErrorIsNil) 649 c.Assert(cfg.Config["ftp-proxy"].Controller, gc.IsNil) 650 } 651 652 func (s *modelManagerSuite) TestUnsetModelDefaultsAsNormalUser(c *gc.C) { 653 s.setAPIUser(c, names.NewUserTag("charlie")) 654 got, err := s.api.UnsetModelDefaults(params.UnsetModelDefaults{ 655 Keys: []params.ModelUnsetKeys{{ 656 Keys: []string{"attr2"}}}}) 657 c.Assert(err, gc.ErrorMatches, "permission denied") 658 c.Assert(got, gc.DeepEquals, params.ErrorResults{ 659 Results: []params.ErrorResult{{Error: nil}}, 660 }) 661 662 // Make sure it didn't change. 663 s.setAPIUser(c, names.NewUserTag("admin")) 664 cfg, err := s.api.ModelDefaults() 665 c.Assert(err, jc.ErrorIsNil) 666 c.Assert(cfg.Config["attr2"].Controller.(string), gc.Equals, "val3") 667 } 668 669 func (s *modelManagerSuite) TestDumpModelV2(c *gc.C) { 670 api := &modelmanager.ModelManagerAPIV2{ 671 &modelmanager.ModelManagerAPIV3{ 672 &modelmanager.ModelManagerAPIV4{ 673 s.api, 674 }, 675 }, 676 } 677 678 results := api.DumpModels(params.Entities{[]params.Entity{{ 679 Tag: "bad-tag", 680 }, { 681 Tag: "application-foo", 682 }, { 683 Tag: s.st.ModelTag().String(), 684 }}}) 685 686 c.Assert(results.Results, gc.HasLen, 3) 687 bad, notApp, good := results.Results[0], results.Results[1], results.Results[2] 688 c.Check(bad.Result, gc.IsNil) 689 c.Check(bad.Error.Message, gc.Equals, `"bad-tag" is not a valid tag`) 690 691 c.Check(notApp.Result, gc.IsNil) 692 c.Check(notApp.Error.Message, gc.Equals, `"application-foo" is not a valid model tag`) 693 694 c.Check(good.Error, gc.IsNil) 695 c.Check(good.Result, jc.DeepEquals, map[string]interface{}{ 696 "model-uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d", 697 }) 698 } 699 700 func (s *modelManagerSuite) TestDumpModel(c *gc.C) { 701 results := s.api.DumpModels(params.DumpModelRequest{ 702 Entities: []params.Entity{{ 703 Tag: "bad-tag", 704 }, { 705 Tag: "application-foo", 706 }, { 707 Tag: s.st.ModelTag().String(), 708 }}}) 709 710 c.Assert(results.Results, gc.HasLen, 3) 711 bad, notApp, good := results.Results[0], results.Results[1], results.Results[2] 712 c.Check(bad.Result, gc.Equals, "") 713 c.Check(bad.Error.Message, gc.Equals, `"bad-tag" is not a valid tag`) 714 715 c.Check(notApp.Result, gc.Equals, "") 716 c.Check(notApp.Error.Message, gc.Equals, `"application-foo" is not a valid model tag`) 717 718 c.Check(good.Error, gc.IsNil) 719 c.Check(good.Result, jc.DeepEquals, "model-uuid: deadbeef-0bad-400d-8000-4b1d0d06f00d\n") 720 } 721 722 func (s *modelManagerSuite) TestDumpModelMissingModel(c *gc.C) { 723 s.st.SetErrors(errors.NotFoundf("boom")) 724 tag := names.NewModelTag("deadbeef-0bad-400d-8000-4b1d0d06f000") 725 models := params.DumpModelRequest{Entities: []params.Entity{{Tag: tag.String()}}} 726 results := s.api.DumpModels(models) 727 s.st.CheckCalls(c, []gitjujutesting.StubCall{ 728 {"ControllerTag", nil}, 729 {"ModelUUID", nil}, 730 {"GetBackend", []interface{}{tag.Id()}}, 731 }) 732 c.Assert(results.Results, gc.HasLen, 1) 733 result := results.Results[0] 734 c.Assert(result.Result, gc.Equals, "") 735 c.Assert(result.Error, gc.NotNil) 736 c.Check(result.Error.Code, gc.Equals, `not found`) 737 c.Check(result.Error.Message, gc.Equals, `id not found`) 738 } 739 740 func (s *modelManagerSuite) TestDumpModelUsers(c *gc.C) { 741 models := params.DumpModelRequest{Entities: []params.Entity{{Tag: s.st.ModelTag().String()}}} 742 for _, user := range []names.UserTag{ 743 names.NewUserTag("otheruser"), 744 names.NewUserTag("unknown"), 745 } { 746 s.setAPIUser(c, user) 747 results := s.api.DumpModels(models) 748 c.Assert(results.Results, gc.HasLen, 1) 749 result := results.Results[0] 750 c.Assert(result.Result, gc.Equals, "") 751 c.Assert(result.Error, gc.NotNil) 752 c.Check(result.Error.Message, gc.Equals, `permission denied`) 753 } 754 } 755 756 func (s *modelManagerSuite) TestDumpModelsDB(c *gc.C) { 757 results := s.api.DumpModelsDB(params.Entities{[]params.Entity{{ 758 Tag: "bad-tag", 759 }, { 760 Tag: "application-foo", 761 }, { 762 Tag: s.st.ModelTag().String(), 763 }}}) 764 765 c.Assert(results.Results, gc.HasLen, 3) 766 bad, notApp, good := results.Results[0], results.Results[1], results.Results[2] 767 c.Check(bad.Result, gc.IsNil) 768 c.Check(bad.Error.Message, gc.Equals, `"bad-tag" is not a valid tag`) 769 770 c.Check(notApp.Result, gc.IsNil) 771 c.Check(notApp.Error.Message, gc.Equals, `"application-foo" is not a valid model tag`) 772 773 c.Check(good.Error, gc.IsNil) 774 c.Check(good.Result, jc.DeepEquals, map[string]interface{}{ 775 "models": "lots of data", 776 }) 777 } 778 779 func (s *modelManagerSuite) TestDumpModelsDBMissingModel(c *gc.C) { 780 s.st.SetErrors(errors.NotFoundf("boom")) 781 tag := names.NewModelTag("deadbeef-0bad-400d-8000-4b1d0d06f000") 782 models := params.Entities{[]params.Entity{{Tag: tag.String()}}} 783 results := s.api.DumpModelsDB(models) 784 785 s.st.CheckCalls(c, []gitjujutesting.StubCall{ 786 {"ControllerTag", nil}, 787 {"ModelUUID", nil}, 788 {"ModelTag", nil}, 789 {"GetBackend", []interface{}{tag.Id()}}, 790 }) 791 c.Assert(results.Results, gc.HasLen, 1) 792 result := results.Results[0] 793 c.Assert(result.Result, gc.IsNil) 794 c.Assert(result.Error, gc.NotNil) 795 c.Check(result.Error.Code, gc.Equals, `not found`) 796 c.Check(result.Error.Message, gc.Equals, `id not found`) 797 } 798 799 func (s *modelManagerSuite) TestDumpModelsDBUsers(c *gc.C) { 800 models := params.Entities{[]params.Entity{{Tag: s.st.ModelTag().String()}}} 801 for _, user := range []names.UserTag{ 802 names.NewUserTag("otheruser"), 803 names.NewUserTag("unknown"), 804 } { 805 s.setAPIUser(c, user) 806 results := s.api.DumpModelsDB(models) 807 c.Assert(results.Results, gc.HasLen, 1) 808 result := results.Results[0] 809 c.Assert(result.Result, gc.IsNil) 810 c.Assert(result.Error, gc.NotNil) 811 c.Check(result.Error.Message, gc.Equals, `permission denied`) 812 } 813 } 814 815 func (s *modelManagerSuite) TestAddModelCanCreateModel(c *gc.C) { 816 addModelUser := names.NewUserTag("add-model") 817 s.ctlrSt.cloudUsers[addModelUser.Id()] = permission.AddModelAccess 818 s.setAPIUser(c, addModelUser) 819 _, err := s.api.CreateModel(createArgs(addModelUser)) 820 c.Assert(err, jc.ErrorIsNil) 821 } 822 823 func (s *modelManagerSuite) TestAddModelCantCreateModelForSomeoneElse(c *gc.C) { 824 addModelUser := names.NewUserTag("add-model") 825 s.ctlrSt.cloudUsers[addModelUser.Id()] = permission.AddModelAccess 826 s.setAPIUser(c, addModelUser) 827 nonAdminUser := names.NewUserTag("non-admin") 828 _, err := s.api.CreateModel(createArgs(nonAdminUser)) 829 c.Assert(err, gc.ErrorMatches, "\"add-model\" permission does not permit creation of models for different owners: permission denied") 830 } 831 832 func (s *modelManagerSuite) TestDestroyModelsV3(c *gc.C) { 833 api := &modelmanager.ModelManagerAPIV3{ 834 &modelmanager.ModelManagerAPIV4{ 835 s.api, 836 }, 837 } 838 results, err := api.DestroyModels(params.Entities{ 839 Entities: []params.Entity{{coretesting.ModelTag.String()}}, 840 }) 841 c.Assert(err, jc.ErrorIsNil) 842 c.Assert(results, jc.DeepEquals, params.ErrorResults{[]params.ErrorResult{{}}}) 843 s.st.CheckCallNames(c, 844 "ControllerTag", 845 "ModelUUID", 846 "GetBackend", 847 "Model", 848 "GetBlockForType", 849 "GetBlockForType", 850 "GetBlockForType", 851 "Model", 852 "ControllerConfig", 853 "ModelConfig", 854 "MetricsManager", 855 ) 856 destroyStorage := true 857 s.st.model.CheckCalls(c, []gitjujutesting.StubCall{ 858 {"UUID", nil}, 859 {"Destroy", []interface{}{state.DestroyModelParams{ 860 DestroyStorage: &destroyStorage, 861 }}}, 862 }) 863 } 864 865 // modelManagerStateSuite contains end-to-end tests. 866 // Prefer adding tests to modelManagerSuite above. 867 type modelManagerStateSuite struct { 868 jujutesting.JujuConnSuite 869 modelmanager *modelmanager.ModelManagerAPI 870 authoriser apiservertesting.FakeAuthorizer 871 872 callContext context.ProviderCallContext 873 } 874 875 var _ = gc.Suite(&modelManagerStateSuite{}) 876 877 func (s *modelManagerStateSuite) SetUpSuite(c *gc.C) { 878 coretesting.SkipUnlessControllerOS(c) 879 s.JujuConnSuite.SetUpSuite(c) 880 } 881 882 func (s *modelManagerStateSuite) SetUpTest(c *gc.C) { 883 s.JujuConnSuite.SetUpTest(c) 884 s.authoriser = apiservertesting.FakeAuthorizer{ 885 Tag: s.AdminUserTag(c), 886 } 887 s.callContext = context.NewCloudCallContext() 888 loggo.GetLogger("juju.apiserver.modelmanager").SetLogLevel(loggo.TRACE) 889 } 890 891 func (s *modelManagerStateSuite) setAPIUser(c *gc.C, user names.UserTag) { 892 s.authoriser.Tag = user 893 modelmanager, err := modelmanager.NewModelManagerAPI( 894 common.NewModelManagerBackend(s.Model, s.StatePool), 895 common.NewModelManagerBackend(s.Model, s.StatePool), 896 stateenvirons.EnvironConfigGetter{s.State, s.Model}, 897 nil, 898 s.authoriser, 899 s.Model, 900 s.callContext, 901 ) 902 c.Assert(err, jc.ErrorIsNil) 903 s.modelmanager = modelmanager 904 } 905 906 func (s *modelManagerStateSuite) TestNewAPIAcceptsClient(c *gc.C) { 907 anAuthoriser := s.authoriser 908 anAuthoriser.Tag = names.NewUserTag("external@remote") 909 endPoint, err := modelmanager.NewModelManagerAPI( 910 common.NewModelManagerBackend(s.Model, s.StatePool), 911 common.NewModelManagerBackend(s.Model, s.StatePool), 912 nil, nil, anAuthoriser, 913 s.Model, 914 s.callContext, 915 ) 916 c.Assert(err, jc.ErrorIsNil) 917 c.Assert(endPoint, gc.NotNil) 918 } 919 920 func (s *modelManagerStateSuite) TestNewAPIRefusesNonClient(c *gc.C) { 921 anAuthoriser := s.authoriser 922 anAuthoriser.Tag = names.NewUnitTag("mysql/0") 923 endPoint, err := modelmanager.NewModelManagerAPI( 924 common.NewModelManagerBackend(s.Model, s.StatePool), 925 common.NewModelManagerBackend(s.Model, s.StatePool), 926 nil, nil, anAuthoriser, s.Model, 927 s.callContext, 928 ) 929 c.Assert(endPoint, gc.IsNil) 930 c.Assert(err, gc.ErrorMatches, "permission denied") 931 } 932 933 func (s *modelManagerStateSuite) createArgsForVersion(c *gc.C, owner names.UserTag, ver interface{}) params.ModelCreateArgs { 934 params := createArgs(owner) 935 params.Config["agent-version"] = ver 936 return params 937 } 938 939 func (s *modelManagerStateSuite) TestUserCanCreateModel(c *gc.C) { 940 owner := names.NewUserTag("admin") 941 s.setAPIUser(c, owner) 942 model, err := s.modelmanager.CreateModel(createArgs(owner)) 943 c.Assert(err, jc.ErrorIsNil) 944 c.Assert(model.OwnerTag, gc.Equals, owner.String()) 945 c.Assert(model.Name, gc.Equals, "test-model") 946 c.Assert(model.Type, gc.Equals, "iaas") 947 } 948 949 func (s *modelManagerStateSuite) TestAdminCanCreateModelForSomeoneElse(c *gc.C) { 950 s.setAPIUser(c, s.AdminUserTag(c)) 951 owner := names.NewUserTag("external@remote") 952 953 model, err := s.modelmanager.CreateModel(createArgs(owner)) 954 c.Assert(err, jc.ErrorIsNil) 955 c.Assert(model.OwnerTag, gc.Equals, owner.String()) 956 c.Assert(model.Name, gc.Equals, "test-model") 957 c.Assert(model.Type, gc.Equals, "iaas") 958 // Make sure that the environment created does actually have the correct 959 // owner, and that owner is actually allowed to use the environment. 960 newState, err := s.StatePool.Get(model.UUID) 961 c.Assert(err, jc.ErrorIsNil) 962 defer newState.Release() 963 964 newModel, err := newState.Model() 965 c.Assert(err, jc.ErrorIsNil) 966 c.Assert(newModel.Owner(), gc.Equals, owner) 967 _, err = newState.UserAccess(owner, newModel.ModelTag()) 968 c.Assert(err, jc.ErrorIsNil) 969 } 970 971 func (s *modelManagerStateSuite) TestNonAdminCannotCreateModelForSomeoneElse(c *gc.C) { 972 s.setAPIUser(c, names.NewUserTag("non-admin@remote")) 973 owner := names.NewUserTag("external@remote") 974 _, err := s.modelmanager.CreateModel(createArgs(owner)) 975 c.Assert(err, gc.ErrorMatches, "permission denied") 976 } 977 978 func (s *modelManagerStateSuite) TestNonAdminCannotCreateModelForSelf(c *gc.C) { 979 owner := names.NewUserTag("non-admin@remote") 980 s.setAPIUser(c, owner) 981 _, err := s.modelmanager.CreateModel(createArgs(owner)) 982 c.Assert(err, gc.ErrorMatches, "permission denied") 983 } 984 985 func (s *modelManagerStateSuite) TestCreateModelValidatesConfig(c *gc.C) { 986 admin := s.AdminUserTag(c) 987 s.setAPIUser(c, admin) 988 args := createArgs(admin) 989 args.Config["controller"] = "maybe" 990 _, err := s.modelmanager.CreateModel(args) 991 c.Assert(err, gc.ErrorMatches, 992 "failed to create config: provider config preparation failed: controller: expected bool, got string\\(\"maybe\"\\)", 993 ) 994 } 995 996 func (s *modelManagerStateSuite) TestCreateModelBadConfig(c *gc.C) { 997 owner := names.NewUserTag("admin") 998 s.setAPIUser(c, owner) 999 for i, test := range []struct { 1000 key string 1001 value interface{} 1002 errMatch string 1003 }{ 1004 { 1005 key: "uuid", 1006 value: "anything", 1007 errMatch: `failed to create config: uuid is generated, you cannot specify one`, 1008 }, 1009 } { 1010 c.Logf("%d: %s", i, test.key) 1011 args := createArgs(owner) 1012 args.Config[test.key] = test.value 1013 _, err := s.modelmanager.CreateModel(args) 1014 c.Assert(err, gc.ErrorMatches, test.errMatch) 1015 1016 } 1017 } 1018 1019 func (s *modelManagerStateSuite) TestCreateModelSameAgentVersion(c *gc.C) { 1020 admin := s.AdminUserTag(c) 1021 s.setAPIUser(c, admin) 1022 args := s.createArgsForVersion(c, admin, jujuversion.Current.String()) 1023 _, err := s.modelmanager.CreateModel(args) 1024 c.Assert(err, jc.ErrorIsNil) 1025 } 1026 1027 func (s *modelManagerStateSuite) TestCreateModelBadAgentVersion(c *gc.C) { 1028 err := s.BackingState.SetModelAgentVersion(coretesting.FakeVersionNumber, false) 1029 c.Assert(err, jc.ErrorIsNil) 1030 1031 admin := s.AdminUserTag(c) 1032 s.setAPIUser(c, admin) 1033 1034 bigger := coretesting.FakeVersionNumber 1035 bigger.Minor += 1 1036 1037 smaller := coretesting.FakeVersionNumber 1038 smaller.Minor -= 1 1039 1040 for i, test := range []struct { 1041 value interface{} 1042 errMatch string 1043 }{ 1044 { 1045 value: 42, 1046 errMatch: `failed to create config: agent-version must be a string but has type 'int'`, 1047 }, { 1048 value: "not a number", 1049 errMatch: `failed to create config: invalid version \"not a number\"`, 1050 }, { 1051 value: bigger.String(), 1052 errMatch: "failed to create config: agent-version .* cannot be greater than the controller .*", 1053 }, { 1054 value: smaller.String(), 1055 errMatch: "failed to create config: no agent binaries found for version .*", 1056 }, 1057 } { 1058 c.Logf("test %d", i) 1059 args := s.createArgsForVersion(c, admin, test.value) 1060 _, err := s.modelmanager.CreateModel(args) 1061 c.Check(err, gc.ErrorMatches, test.errMatch) 1062 } 1063 } 1064 1065 func (s *modelManagerStateSuite) checkModelMatches(c *gc.C, model params.Model, expected *state.Model) { 1066 c.Check(model.Name, gc.Equals, expected.Name()) 1067 c.Check(model.UUID, gc.Equals, expected.UUID()) 1068 c.Check(model.OwnerTag, gc.Equals, expected.Owner().String()) 1069 } 1070 1071 func (s *modelManagerStateSuite) TestListModelsAdminSelf(c *gc.C) { 1072 user := s.AdminUserTag(c) 1073 s.setAPIUser(c, user) 1074 result, err := s.modelmanager.ListModels(params.Entity{Tag: user.String()}) 1075 c.Assert(err, jc.ErrorIsNil) 1076 c.Assert(result.UserModels, gc.HasLen, 1) 1077 expected, err := s.State.Model() 1078 c.Assert(err, jc.ErrorIsNil) 1079 s.checkModelMatches(c, result.UserModels[0].Model, expected) 1080 } 1081 1082 func (s *modelManagerStateSuite) TestListModelsAdminListsOther(c *gc.C) { 1083 user := s.AdminUserTag(c) 1084 s.setAPIUser(c, user) 1085 other := names.NewUserTag("admin") 1086 result, err := s.modelmanager.ListModels(params.Entity{Tag: other.String()}) 1087 c.Assert(err, jc.ErrorIsNil) 1088 c.Assert(result.UserModels, gc.HasLen, 1) 1089 } 1090 1091 func (s *modelManagerStateSuite) TestListModelsDenied(c *gc.C) { 1092 user := names.NewUserTag("external@remote") 1093 s.setAPIUser(c, user) 1094 other := names.NewUserTag("other@remote") 1095 _, err := s.modelmanager.ListModels(params.Entity{Tag: other.String()}) 1096 c.Assert(err, gc.ErrorMatches, "permission denied") 1097 } 1098 1099 func (s *modelManagerStateSuite) TestAdminModelManager(c *gc.C) { 1100 user := s.AdminUserTag(c) 1101 s.setAPIUser(c, user) 1102 c.Assert(modelmanager.AuthCheck(c, s.modelmanager, user), jc.IsTrue) 1103 } 1104 1105 func (s *modelManagerStateSuite) TestNonAdminModelManager(c *gc.C) { 1106 user := names.NewUserTag("external@remote") 1107 s.setAPIUser(c, user) 1108 c.Assert(modelmanager.AuthCheck(c, s.modelmanager, user), jc.IsFalse) 1109 } 1110 1111 func (s *modelManagerStateSuite) TestDestroyOwnModel(c *gc.C) { 1112 // TODO(perrito666) this test is not valid until we have 1113 // proper controller permission since the only users that 1114 // can create models are controller admins. 1115 owner := names.NewUserTag("admin") 1116 s.setAPIUser(c, owner) 1117 m, err := s.modelmanager.CreateModel(createArgs(owner)) 1118 c.Assert(err, jc.ErrorIsNil) 1119 1120 st, err := s.StatePool.Get(m.UUID) 1121 c.Assert(err, jc.ErrorIsNil) 1122 defer st.Release() 1123 model, err := st.Model() 1124 c.Assert(err, jc.ErrorIsNil) 1125 1126 s.modelmanager, err = modelmanager.NewModelManagerAPI( 1127 common.NewModelManagerBackend(model, s.StatePool), 1128 common.NewModelManagerBackend(s.Model, s.StatePool), 1129 nil, nil, s.authoriser, 1130 s.Model, 1131 s.callContext, 1132 ) 1133 c.Assert(err, jc.ErrorIsNil) 1134 1135 results, err := s.modelmanager.DestroyModels(params.DestroyModelsParams{ 1136 Models: []params.DestroyModelParams{{ 1137 ModelTag: "model-" + m.UUID, 1138 }}, 1139 }) 1140 c.Assert(err, jc.ErrorIsNil) 1141 c.Assert(results.Results, gc.HasLen, 1) 1142 c.Assert(results.Results[0].Error, gc.IsNil) 1143 1144 model, err = st.Model() 1145 c.Assert(err, jc.ErrorIsNil) 1146 c.Assert(model.Life(), gc.Not(gc.Equals), state.Alive) 1147 } 1148 1149 func (s *modelManagerStateSuite) TestAdminDestroysOtherModel(c *gc.C) { 1150 // TODO(perrito666) Both users are admins in this case, this tesst is of dubious 1151 // usefulness until proper controller permissions are in place. 1152 owner := names.NewUserTag("admin") 1153 s.setAPIUser(c, owner) 1154 m, err := s.modelmanager.CreateModel(createArgs(owner)) 1155 c.Assert(err, jc.ErrorIsNil) 1156 1157 st, err := s.StatePool.Get(m.UUID) 1158 c.Assert(err, jc.ErrorIsNil) 1159 defer st.Release() 1160 model, err := st.Model() 1161 c.Assert(err, jc.ErrorIsNil) 1162 1163 s.authoriser.Tag = s.AdminUserTag(c) 1164 s.modelmanager, err = modelmanager.NewModelManagerAPI( 1165 common.NewModelManagerBackend(model, s.StatePool), 1166 common.NewModelManagerBackend(s.Model, s.StatePool), 1167 nil, nil, s.authoriser, 1168 s.Model, 1169 s.callContext, 1170 ) 1171 c.Assert(err, jc.ErrorIsNil) 1172 1173 results, err := s.modelmanager.DestroyModels(params.DestroyModelsParams{ 1174 Models: []params.DestroyModelParams{{ 1175 ModelTag: "model-" + m.UUID, 1176 }}, 1177 }) 1178 c.Assert(err, jc.ErrorIsNil) 1179 c.Assert(results.Results, gc.HasLen, 1) 1180 c.Assert(results.Results[0].Error, gc.IsNil) 1181 1182 s.authoriser.Tag = owner 1183 model, err = st.Model() 1184 c.Assert(err, jc.ErrorIsNil) 1185 c.Assert(model.Life(), gc.Not(gc.Equals), state.Alive) 1186 } 1187 1188 func (s *modelManagerStateSuite) TestDestroyModelErrors(c *gc.C) { 1189 owner := names.NewUserTag("admin") 1190 s.setAPIUser(c, owner) 1191 m, err := s.modelmanager.CreateModel(createArgs(owner)) 1192 c.Assert(err, jc.ErrorIsNil) 1193 1194 st, err := s.StatePool.Get(m.UUID) 1195 c.Assert(err, jc.ErrorIsNil) 1196 defer st.Release() 1197 model, err := st.Model() 1198 c.Assert(err, jc.ErrorIsNil) 1199 1200 s.modelmanager, err = modelmanager.NewModelManagerAPI( 1201 common.NewModelManagerBackend(model, s.StatePool), 1202 common.NewModelManagerBackend(s.Model, s.StatePool), 1203 nil, nil, s.authoriser, s.Model, 1204 s.callContext, 1205 ) 1206 c.Assert(err, jc.ErrorIsNil) 1207 1208 user := names.NewUserTag("other@remote") 1209 s.setAPIUser(c, user) 1210 1211 results, err := s.modelmanager.DestroyModels(params.DestroyModelsParams{ 1212 Models: []params.DestroyModelParams{ 1213 {ModelTag: "model-" + m.UUID}, 1214 {ModelTag: "model-9f484882-2f18-4fd2-967d-db9663db7bea"}, 1215 {ModelTag: "machine-42"}, 1216 }, 1217 }) 1218 c.Assert(err, jc.ErrorIsNil) 1219 c.Assert(results.Results, jc.DeepEquals, []params.ErrorResult{{ 1220 // we don't have admin access to the model 1221 ¶ms.Error{ 1222 Message: "permission denied", 1223 Code: params.CodeUnauthorized, 1224 }, 1225 }, { 1226 ¶ms.Error{ 1227 Message: `model "9f484882-2f18-4fd2-967d-db9663db7bea" not found`, 1228 Code: params.CodeNotFound, 1229 }, 1230 }, { 1231 ¶ms.Error{ 1232 Message: `"machine-42" is not a valid model tag`, 1233 }, 1234 }}) 1235 1236 s.setAPIUser(c, owner) 1237 model, err = st.Model() 1238 c.Assert(err, jc.ErrorIsNil) 1239 c.Assert(model.Life(), gc.Equals, state.Alive) 1240 } 1241 1242 func (s *modelManagerStateSuite) modifyAccess(c *gc.C, user names.UserTag, action params.ModelAction, access params.UserAccessPermission, model names.ModelTag) error { 1243 args := params.ModifyModelAccessRequest{ 1244 Changes: []params.ModifyModelAccess{{ 1245 UserTag: user.String(), 1246 Action: action, 1247 Access: access, 1248 ModelTag: model.String(), 1249 }}} 1250 1251 result, err := s.modelmanager.ModifyModelAccess(args) 1252 if err != nil { 1253 return err 1254 } 1255 return result.OneError() 1256 } 1257 1258 func (s *modelManagerStateSuite) grant(c *gc.C, user names.UserTag, access params.UserAccessPermission, model names.ModelTag) error { 1259 return s.modifyAccess(c, user, params.GrantModelAccess, access, model) 1260 } 1261 1262 func (s *modelManagerStateSuite) revoke(c *gc.C, user names.UserTag, access params.UserAccessPermission, model names.ModelTag) error { 1263 return s.modifyAccess(c, user, params.RevokeModelAccess, access, model) 1264 } 1265 1266 func (s *modelManagerStateSuite) TestGrantMissingUserFails(c *gc.C) { 1267 s.setAPIUser(c, s.AdminUserTag(c)) 1268 st := s.Factory.MakeModel(c, nil) 1269 defer st.Close() 1270 1271 m, err := st.Model() 1272 c.Assert(err, jc.ErrorIsNil) 1273 1274 user := names.NewLocalUserTag("foobar") 1275 err = s.grant(c, user, params.ModelReadAccess, m.ModelTag()) 1276 expectedErr := `could not grant model access: user "foobar" does not exist locally: user "foobar" not found` 1277 c.Assert(err, gc.ErrorMatches, expectedErr) 1278 } 1279 1280 func (s *modelManagerStateSuite) TestGrantMissingModelFails(c *gc.C) { 1281 s.setAPIUser(c, s.AdminUserTag(c)) 1282 user := s.Factory.MakeModelUser(c, nil) 1283 model := names.NewModelTag("17e4bd2d-3e08-4f3d-b945-087be7ebdce4") 1284 err := s.grant(c, user.UserTag, params.ModelReadAccess, model) 1285 expectedErr := `.*model "17e4bd2d-3e08-4f3d-b945-087be7ebdce4" not found` 1286 c.Assert(err, gc.ErrorMatches, expectedErr) 1287 } 1288 1289 func (s *modelManagerStateSuite) TestRevokeAdminLeavesReadAccess(c *gc.C) { 1290 s.setAPIUser(c, s.AdminUserTag(c)) 1291 user := s.Factory.MakeModelUser(c, &factory.ModelUserParams{Access: permission.WriteAccess}) 1292 1293 err := s.revoke(c, user.UserTag, params.ModelWriteAccess, user.Object.(names.ModelTag)) 1294 c.Assert(err, gc.IsNil) 1295 1296 modelUser, err := s.State.UserAccess(user.UserTag, user.Object) 1297 c.Assert(err, jc.ErrorIsNil) 1298 c.Assert(modelUser.Access, gc.Equals, permission.ReadAccess) 1299 } 1300 1301 func (s *modelManagerStateSuite) TestRevokeReadRemovesModelUser(c *gc.C) { 1302 s.setAPIUser(c, s.AdminUserTag(c)) 1303 user := s.Factory.MakeModelUser(c, nil) 1304 1305 err := s.revoke(c, user.UserTag, params.ModelReadAccess, user.Object.(names.ModelTag)) 1306 c.Assert(err, gc.IsNil) 1307 1308 _, err = s.State.UserAccess(user.UserTag, user.Object) 1309 c.Assert(errors.IsNotFound(err), jc.IsTrue) 1310 } 1311 1312 func (s *modelManagerStateSuite) TestRevokeModelMissingUser(c *gc.C) { 1313 s.setAPIUser(c, s.AdminUserTag(c)) 1314 st := s.Factory.MakeModel(c, nil) 1315 defer st.Close() 1316 1317 m, err := st.Model() 1318 c.Assert(err, jc.ErrorIsNil) 1319 1320 user := names.NewUserTag("bob") 1321 err = s.revoke(c, user, params.ModelReadAccess, m.ModelTag()) 1322 c.Assert(err, gc.ErrorMatches, `could not revoke model access: model user "bob" does not exist`) 1323 1324 _, err = st.UserAccess(user, m.ModelTag()) 1325 c.Assert(errors.IsNotFound(err), jc.IsTrue) 1326 } 1327 1328 func (s *modelManagerStateSuite) TestGrantOnlyGreaterAccess(c *gc.C) { 1329 user := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar", NoModelUser: true}) 1330 s.setAPIUser(c, s.AdminUserTag(c)) 1331 st := s.Factory.MakeModel(c, nil) 1332 defer st.Close() 1333 1334 m, err := st.Model() 1335 c.Assert(err, jc.ErrorIsNil) 1336 1337 err = s.grant(c, user.UserTag(), params.ModelReadAccess, m.ModelTag()) 1338 c.Assert(err, jc.ErrorIsNil) 1339 1340 err = s.grant(c, user.UserTag(), params.ModelReadAccess, m.ModelTag()) 1341 c.Assert(err, gc.ErrorMatches, `user already has "read" access or greater`) 1342 } 1343 1344 func (s *modelManagerStateSuite) assertNewUser(c *gc.C, modelUser permission.UserAccess, userTag, creatorTag names.UserTag) { 1345 c.Assert(modelUser.UserTag, gc.Equals, userTag) 1346 c.Assert(modelUser.CreatedBy, gc.Equals, creatorTag) 1347 _, err := s.Model.LastModelConnection(modelUser.UserTag) 1348 c.Assert(err, jc.Satisfies, state.IsNeverConnectedError) 1349 } 1350 1351 func (s *modelManagerStateSuite) assertModelAccess(c *gc.C, st *state.State) { 1352 m, err := st.Model() 1353 c.Assert(err, jc.ErrorIsNil) 1354 1355 result, err := s.modelmanager.ModelInfo(params.Entities{Entities: []params.Entity{{Tag: m.ModelTag().String()}}}) 1356 c.Assert(err, jc.ErrorIsNil) 1357 c.Assert(result.Results, gc.HasLen, 1) 1358 c.Assert(result.Results[0].Error, gc.IsNil) 1359 } 1360 1361 func (s *modelManagerStateSuite) TestGrantModelAddLocalUser(c *gc.C) { 1362 user := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar", NoModelUser: true}) 1363 apiUser := s.AdminUserTag(c) 1364 s.setAPIUser(c, apiUser) 1365 st := s.Factory.MakeModel(c, nil) 1366 defer st.Close() 1367 1368 m, err := st.Model() 1369 c.Assert(err, jc.ErrorIsNil) 1370 1371 err = s.grant(c, user.UserTag(), params.ModelReadAccess, m.ModelTag()) 1372 c.Assert(err, jc.ErrorIsNil) 1373 1374 modelUser, err := st.UserAccess(user.UserTag(), m.ModelTag()) 1375 c.Assert(err, jc.ErrorIsNil) 1376 s.assertNewUser(c, modelUser, user.UserTag(), apiUser) 1377 c.Assert(modelUser.Access, gc.Equals, permission.ReadAccess) 1378 s.setAPIUser(c, user.UserTag()) 1379 s.assertModelAccess(c, st) 1380 } 1381 1382 func (s *modelManagerStateSuite) TestGrantModelAddRemoteUser(c *gc.C) { 1383 userTag := names.NewUserTag("foobar@ubuntuone") 1384 apiUser := s.AdminUserTag(c) 1385 s.setAPIUser(c, apiUser) 1386 st := s.Factory.MakeModel(c, nil) 1387 defer st.Close() 1388 1389 m, err := st.Model() 1390 c.Assert(err, jc.ErrorIsNil) 1391 1392 err = s.grant(c, userTag, params.ModelReadAccess, m.ModelTag()) 1393 c.Assert(err, jc.ErrorIsNil) 1394 1395 modelUser, err := st.UserAccess(userTag, m.ModelTag()) 1396 c.Assert(err, jc.ErrorIsNil) 1397 1398 s.assertNewUser(c, modelUser, userTag, apiUser) 1399 c.Assert(modelUser.Access, gc.Equals, permission.ReadAccess) 1400 s.setAPIUser(c, userTag) 1401 s.assertModelAccess(c, st) 1402 } 1403 1404 func (s *modelManagerStateSuite) TestGrantModelAddAdminUser(c *gc.C) { 1405 user := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar", NoModelUser: true}) 1406 apiUser := s.AdminUserTag(c) 1407 s.setAPIUser(c, apiUser) 1408 st := s.Factory.MakeModel(c, nil) 1409 defer st.Close() 1410 1411 m, err := st.Model() 1412 c.Assert(err, jc.ErrorIsNil) 1413 1414 err = s.grant(c, user.UserTag(), params.ModelWriteAccess, m.ModelTag()) 1415 1416 modelUser, err := st.UserAccess(user.UserTag(), m.ModelTag()) 1417 c.Assert(err, jc.ErrorIsNil) 1418 s.assertNewUser(c, modelUser, user.UserTag(), apiUser) 1419 c.Assert(modelUser.Access, gc.Equals, permission.WriteAccess) 1420 s.setAPIUser(c, user.UserTag()) 1421 s.assertModelAccess(c, st) 1422 } 1423 1424 func (s *modelManagerStateSuite) TestGrantModelIncreaseAccess(c *gc.C) { 1425 s.setAPIUser(c, s.AdminUserTag(c)) 1426 st := s.Factory.MakeModel(c, nil) 1427 defer st.Close() 1428 stFactory := factory.NewFactory(st, s.StatePool) 1429 user := stFactory.MakeModelUser(c, &factory.ModelUserParams{Access: permission.ReadAccess}) 1430 1431 m, err := st.Model() 1432 c.Assert(err, jc.ErrorIsNil) 1433 1434 err = s.grant(c, user.UserTag, params.ModelWriteAccess, m.ModelTag()) 1435 c.Assert(err, jc.ErrorIsNil) 1436 1437 modelUser, err := st.UserAccess(user.UserTag, m.ModelTag()) 1438 c.Assert(err, jc.ErrorIsNil) 1439 c.Assert(modelUser.Access, gc.Equals, permission.WriteAccess) 1440 } 1441 1442 func (s *modelManagerStateSuite) TestGrantToModelNoAccess(c *gc.C) { 1443 s.setAPIUser(c, s.AdminUserTag(c)) 1444 st := s.Factory.MakeModel(c, nil) 1445 defer st.Close() 1446 1447 m, err := st.Model() 1448 c.Assert(err, jc.ErrorIsNil) 1449 1450 apiUser := names.NewUserTag("bob@remote") 1451 s.setAPIUser(c, apiUser) 1452 1453 other := names.NewUserTag("other@remote") 1454 err = s.grant(c, other, params.ModelReadAccess, m.ModelTag()) 1455 c.Assert(err, gc.ErrorMatches, "permission denied") 1456 } 1457 1458 func (s *modelManagerStateSuite) TestGrantToModelReadAccess(c *gc.C) { 1459 s.setAPIUser(c, s.AdminUserTag(c)) 1460 st := s.Factory.MakeModel(c, nil) 1461 defer st.Close() 1462 1463 apiUser := names.NewUserTag("bob@remote") 1464 s.setAPIUser(c, apiUser) 1465 1466 stFactory := factory.NewFactory(st, s.StatePool) 1467 stFactory.MakeModelUser(c, &factory.ModelUserParams{ 1468 User: apiUser.Id(), Access: permission.ReadAccess}) 1469 1470 other := names.NewUserTag("other@remote") 1471 m, err := st.Model() 1472 c.Assert(err, jc.ErrorIsNil) 1473 1474 err = s.grant(c, other, params.ModelReadAccess, m.ModelTag()) 1475 c.Assert(err, gc.ErrorMatches, "permission denied") 1476 } 1477 1478 func (s *modelManagerStateSuite) TestGrantToModelWriteAccess(c *gc.C) { 1479 s.setAPIUser(c, s.AdminUserTag(c)) 1480 st := s.Factory.MakeModel(c, nil) 1481 defer st.Close() 1482 1483 apiUser := names.NewUserTag("admin@remote") 1484 s.setAPIUser(c, apiUser) 1485 stFactory := factory.NewFactory(st, s.StatePool) 1486 stFactory.MakeModelUser(c, &factory.ModelUserParams{ 1487 User: apiUser.Id(), Access: permission.AdminAccess}) 1488 1489 other := names.NewUserTag("other@remote") 1490 m, err := st.Model() 1491 c.Assert(err, jc.ErrorIsNil) 1492 1493 err = s.grant(c, other, params.ModelReadAccess, m.ModelTag()) 1494 c.Assert(err, jc.ErrorIsNil) 1495 1496 modelUser, err := st.UserAccess(other, m.ModelTag()) 1497 c.Assert(err, jc.ErrorIsNil) 1498 s.assertNewUser(c, modelUser, other, apiUser) 1499 c.Assert(modelUser.Access, gc.Equals, permission.ReadAccess) 1500 } 1501 1502 func (s *modelManagerStateSuite) TestGrantModelInvalidUserTag(c *gc.C) { 1503 s.setAPIUser(c, s.AdminUserTag(c)) 1504 for _, testParam := range []struct { 1505 tag string 1506 validTag bool 1507 }{{ 1508 tag: "unit-foo/0", 1509 validTag: true, 1510 }, { 1511 tag: "application-foo", 1512 validTag: true, 1513 }, { 1514 tag: "relation-wordpress:db mysql:db", 1515 validTag: true, 1516 }, { 1517 tag: "machine-0", 1518 validTag: true, 1519 }, { 1520 tag: "user", 1521 validTag: false, 1522 }, { 1523 tag: "user-Mua^h^h^h^arh", 1524 validTag: true, 1525 }, { 1526 tag: "user@", 1527 validTag: false, 1528 }, { 1529 tag: "user@ubuntuone", 1530 validTag: false, 1531 }, { 1532 tag: "user@ubuntuone", 1533 validTag: false, 1534 }, { 1535 tag: "@ubuntuone", 1536 validTag: false, 1537 }, { 1538 tag: "in^valid.", 1539 validTag: false, 1540 }, { 1541 tag: "", 1542 validTag: false, 1543 }, 1544 } { 1545 var expectedErr string 1546 errPart := `could not modify model access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid ` 1547 1548 if testParam.validTag { 1549 // The string is a valid tag, but not a user tag. 1550 expectedErr = errPart + `user tag` 1551 } else { 1552 // The string is not a valid tag of any kind. 1553 expectedErr = errPart + `tag` 1554 } 1555 1556 args := params.ModifyModelAccessRequest{ 1557 Changes: []params.ModifyModelAccess{{ 1558 ModelTag: "model-deadbeef-0bad-400d-8000-4b1d0d06f00d", 1559 UserTag: testParam.tag, 1560 Action: params.GrantModelAccess, 1561 Access: params.ModelReadAccess, 1562 }}} 1563 1564 result, err := s.modelmanager.ModifyModelAccess(args) 1565 c.Assert(err, jc.ErrorIsNil) 1566 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 1567 } 1568 } 1569 1570 func (s *modelManagerStateSuite) TestModifyModelAccessEmptyArgs(c *gc.C) { 1571 s.setAPIUser(c, s.AdminUserTag(c)) 1572 args := params.ModifyModelAccessRequest{Changes: []params.ModifyModelAccess{{}}} 1573 1574 result, err := s.modelmanager.ModifyModelAccess(args) 1575 c.Assert(err, jc.ErrorIsNil) 1576 expectedErr := `could not modify model access: "" model access not valid` 1577 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 1578 } 1579 1580 func (s *modelManagerStateSuite) TestModifyModelAccessInvalidAction(c *gc.C) { 1581 s.setAPIUser(c, s.AdminUserTag(c)) 1582 var dance params.ModelAction = "dance" 1583 args := params.ModifyModelAccessRequest{ 1584 Changes: []params.ModifyModelAccess{{ 1585 UserTag: "user-user", 1586 Action: dance, 1587 Access: params.ModelReadAccess, 1588 ModelTag: s.Model.ModelTag().String(), 1589 }}} 1590 1591 result, err := s.modelmanager.ModifyModelAccess(args) 1592 c.Assert(err, jc.ErrorIsNil) 1593 expectedErr := `unknown action "dance"` 1594 c.Assert(result.OneError(), gc.ErrorMatches, expectedErr) 1595 } 1596 1597 func (s *modelManagerSuite) TestModelStatusV2(c *gc.C) { 1598 api := &modelmanager.ModelManagerAPIV2{ 1599 &modelmanager.ModelManagerAPIV3{ 1600 &modelmanager.ModelManagerAPIV4{ 1601 s.api, 1602 }, 1603 }, 1604 } 1605 // Check that we err out immediately if a model errs. 1606 results, err := api.ModelStatus(params.Entities{[]params.Entity{{ 1607 Tag: "bad-tag", 1608 }, { 1609 Tag: s.st.ModelTag().String(), 1610 }}}) 1611 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1612 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 1613 1614 // Check that we err out if a model errs even if some firsts in collection pass. 1615 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 1616 Tag: s.st.ModelTag().String(), 1617 }, { 1618 Tag: "bad-tag", 1619 }}}) 1620 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1621 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 1622 1623 // Check that we return successfully if no errors. 1624 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 1625 Tag: s.st.ModelTag().String(), 1626 }}}) 1627 c.Assert(err, jc.ErrorIsNil) 1628 c.Assert(results.Results, gc.HasLen, 1) 1629 } 1630 1631 func (s *modelManagerSuite) TestModelStatusV3(c *gc.C) { 1632 api := &modelmanager.ModelManagerAPIV3{ 1633 &modelmanager.ModelManagerAPIV4{ 1634 s.api, 1635 }, 1636 } 1637 1638 // Check that we err out immediately if a model errs. 1639 results, err := api.ModelStatus(params.Entities{[]params.Entity{{ 1640 Tag: "bad-tag", 1641 }, { 1642 Tag: s.st.ModelTag().String(), 1643 }}}) 1644 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1645 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 1646 1647 // Check that we err out if a model errs even if some firsts in collection pass. 1648 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 1649 Tag: s.st.ModelTag().String(), 1650 }, { 1651 Tag: "bad-tag", 1652 }}}) 1653 c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1654 c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)}) 1655 1656 // Check that we return successfully if no errors. 1657 results, err = api.ModelStatus(params.Entities{[]params.Entity{{ 1658 Tag: s.st.ModelTag().String(), 1659 }}}) 1660 c.Assert(err, jc.ErrorIsNil) 1661 c.Assert(results.Results, gc.HasLen, 1) 1662 } 1663 1664 func (s *modelManagerSuite) TestModelStatus(c *gc.C) { 1665 // Check that we don't err out immediately if a model errs. 1666 results, err := s.api.ModelStatus(params.Entities{[]params.Entity{{ 1667 Tag: "bad-tag", 1668 }, { 1669 Tag: s.st.ModelTag().String(), 1670 }}}) 1671 c.Assert(err, jc.ErrorIsNil) 1672 c.Assert(results.Results, gc.HasLen, 2) 1673 c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1674 1675 // Check that we don't err out if a model errs even if some firsts in collection pass. 1676 results, err = s.api.ModelStatus(params.Entities{[]params.Entity{{ 1677 Tag: s.st.ModelTag().String(), 1678 }, { 1679 Tag: "bad-tag", 1680 }}}) 1681 c.Assert(err, jc.ErrorIsNil) 1682 c.Assert(results.Results, gc.HasLen, 2) 1683 c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`) 1684 1685 // Check that we return successfully if no errors. 1686 results, err = s.api.ModelStatus(params.Entities{[]params.Entity{{ 1687 Tag: s.st.ModelTag().String(), 1688 }}}) 1689 c.Assert(err, jc.ErrorIsNil) 1690 c.Assert(results.Results, gc.HasLen, 1) 1691 } 1692 1693 func (s *modelManagerSuite) TestChangeModelCredential(c *gc.C) { 1694 s.st.model.setCloudCredentialF = func(tag names.CloudCredentialTag) (bool, error) { return true, nil } 1695 credentialTag := names.NewCloudCredentialTag("foo/bob/bar").String() 1696 results, err := s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1697 []params.ChangeModelCredentialParams{ 1698 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: credentialTag}, 1699 }, 1700 }) 1701 c.Assert(err, jc.ErrorIsNil) 1702 c.Assert(results.Results, gc.HasLen, 1) 1703 c.Assert(results.Results[0].Error, gc.IsNil) 1704 } 1705 1706 func (s *modelManagerSuite) TestChangeModelCredentialBulkUninterrupted(c *gc.C) { 1707 s.st.model.setCloudCredentialF = func(tag names.CloudCredentialTag) (bool, error) { return true, nil } 1708 credentialTag := names.NewCloudCredentialTag("foo/bob/bar").String() 1709 // Check that we don't err out immediately if a model errs. 1710 results, err := s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1711 []params.ChangeModelCredentialParams{ 1712 {ModelTag: "bad-model-tag"}, 1713 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: credentialTag}, 1714 }, 1715 }) 1716 1717 c.Assert(err, jc.ErrorIsNil) 1718 c.Assert(results.Results, gc.HasLen, 2) 1719 c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-model-tag" is not a valid tag`) 1720 c.Assert(results.Results[1].Error, gc.IsNil) 1721 1722 // Check that we don't err out if a model errs even if some firsts in collection pass. 1723 results, err = s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1724 []params.ChangeModelCredentialParams{ 1725 {ModelTag: s.st.ModelTag().String()}, 1726 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: "bad-credential-tag"}, 1727 }, 1728 }) 1729 c.Assert(err, jc.ErrorIsNil) 1730 c.Assert(results.Results, gc.HasLen, 2) 1731 c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-credential-tag" is not a valid tag`) 1732 } 1733 1734 func (s *modelManagerSuite) TestChangeModelCredentialUnauthorisedUser(c *gc.C) { 1735 credentialTag := names.NewCloudCredentialTag("foo/bob/bar").String() 1736 apiUser := names.NewUserTag("bob@remote") 1737 s.setAPIUser(c, apiUser) 1738 1739 results, err := s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1740 []params.ChangeModelCredentialParams{ 1741 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: credentialTag}, 1742 }, 1743 }) 1744 1745 c.Assert(err, jc.ErrorIsNil) 1746 c.Assert(results.Results[0].Error, gc.ErrorMatches, `permission denied`) 1747 } 1748 1749 func (s *modelManagerSuite) TestChangeModelCredentialGetModelFail(c *gc.C) { 1750 s.st.SetErrors(errors.New("getting model")) 1751 credentialTag := names.NewCloudCredentialTag("foo/bob/bar").String() 1752 1753 results, err := s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1754 []params.ChangeModelCredentialParams{ 1755 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: credentialTag}, 1756 }, 1757 }) 1758 1759 c.Assert(err, jc.ErrorIsNil) 1760 c.Assert(results.Results[0].Error, gc.ErrorMatches, `getting model`) 1761 s.st.CheckCallNames(c, "ControllerTag", "ModelUUID", "ModelTag", "GetBlockForType", "ControllerTag", "GetModel") 1762 } 1763 1764 func (s *modelManagerSuite) TestChangeModelCredentialNotUpdated(c *gc.C) { 1765 s.st.model.setCloudCredentialF = func(tag names.CloudCredentialTag) (bool, error) { return false, nil } 1766 credentialTag := names.NewCloudCredentialTag("foo/bob/bar").String() 1767 results, err := s.api.ChangeModelCredential(params.ChangeModelCredentialsParams{ 1768 []params.ChangeModelCredentialParams{ 1769 {ModelTag: s.st.ModelTag().String(), CloudCredentialTag: credentialTag}, 1770 }, 1771 }) 1772 c.Assert(err, jc.ErrorIsNil) 1773 c.Assert(results.Results, gc.HasLen, 1) 1774 c.Assert(results.Results[0].Error, gc.ErrorMatches, `model deadbeef-0bad-400d-8000-4b1d0d06f00d already uses credential foo/bob/bar`) 1775 } 1776 1777 type fakeProvider struct { 1778 environs.CloudEnvironProvider 1779 } 1780 1781 func (*fakeProvider) Validate(cfg, old *config.Config) (*config.Config, error) { 1782 return cfg, nil 1783 } 1784 1785 func (*fakeProvider) PrepareForCreateEnvironment(controllerUUID string, cfg *config.Config) (*config.Config, error) { 1786 return cfg, nil 1787 } 1788 1789 func init() { 1790 environs.RegisterProvider("fake", &fakeProvider{}) 1791 }