github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelmanager/modelinfo_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelmanager_test 5 6 import ( 7 "strings" 8 "time" 9 10 "github.com/juju/description" 11 "github.com/juju/errors" 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 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/facades/client/modelmanager" 19 "github.com/juju/juju/apiserver/params" 20 apiservertesting "github.com/juju/juju/apiserver/testing" 21 "github.com/juju/juju/caas" 22 "github.com/juju/juju/cloud" 23 "github.com/juju/juju/controller" 24 "github.com/juju/juju/core/instance" 25 "github.com/juju/juju/core/status" 26 "github.com/juju/juju/environs" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/environs/context" 29 "github.com/juju/juju/juju/version" 30 "github.com/juju/juju/permission" 31 "github.com/juju/juju/state" 32 coretesting "github.com/juju/juju/testing" 33 ) 34 35 type modelInfoSuite struct { 36 coretesting.BaseSuite 37 authorizer apiservertesting.FakeAuthorizer 38 st *mockState 39 ctlrSt *mockState 40 modelmanager *modelmanager.ModelManagerAPI 41 42 callContext context.ProviderCallContext 43 } 44 45 func pUint64(v uint64) *uint64 { 46 return &v 47 } 48 49 var _ = gc.Suite(&modelInfoSuite{}) 50 51 func (s *modelInfoSuite) SetUpTest(c *gc.C) { 52 s.BaseSuite.SetUpTest(c) 53 s.authorizer = apiservertesting.FakeAuthorizer{ 54 Tag: names.NewUserTag("admin@local"), 55 } 56 s.st = &mockState{ 57 controllerUUID: coretesting.ControllerTag.Id(), 58 cloud: cloud.Cloud{ 59 Type: "dummy", 60 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 61 }, 62 cfgDefaults: config.ModelDefaultAttributes{ 63 "attr": config.AttributeDefaultValues{ 64 Default: "", 65 Controller: "val", 66 Regions: []config.RegionDefaultValue{{ 67 Name: "dummy", 68 Value: "val++"}}}, 69 "attr2": config.AttributeDefaultValues{ 70 Controller: "val3", 71 Default: "val2", 72 Regions: []config.RegionDefaultValue{{ 73 Name: "left", 74 Value: "spam"}}}, 75 }, 76 } 77 controllerModel := &mockModel{ 78 owner: names.NewUserTag("admin@local"), 79 life: state.Alive, 80 cfg: coretesting.ModelConfig(c), 81 // This feels kind of wrong as both controller model and 82 // default model will end up with the same model tag. 83 tag: coretesting.ModelTag, 84 controllerUUID: s.st.controllerUUID, 85 isController: true, 86 status: status.StatusInfo{ 87 Status: status.Available, 88 Since: &time.Time{}, 89 }, 90 users: []*mockModelUser{{ 91 userName: "admin", 92 access: permission.AdminAccess, 93 }, { 94 userName: "otheruser", 95 access: permission.AdminAccess, 96 }}, 97 } 98 s.st.controllerModel = controllerModel 99 100 s.ctlrSt = &mockState{ 101 model: controllerModel, 102 controllerModel: controllerModel, 103 } 104 105 s.st.model = &mockModel{ 106 owner: names.NewUserTag("bob@local"), 107 cfg: coretesting.ModelConfig(c), 108 tag: coretesting.ModelTag, 109 controllerUUID: s.st.controllerUUID, 110 isController: false, 111 life: state.Dying, 112 status: status.StatusInfo{ 113 Status: status.Destroying, 114 Since: &time.Time{}, 115 }, 116 users: []*mockModelUser{{ 117 userName: "admin", 118 access: permission.AdminAccess, 119 }, { 120 userName: "bob", 121 displayName: "Bob", 122 access: permission.ReadAccess, 123 }, { 124 userName: "charlotte", 125 displayName: "Charlotte", 126 access: permission.ReadAccess, 127 }, { 128 userName: "mary", 129 displayName: "Mary", 130 access: permission.WriteAccess, 131 }}, 132 } 133 s.st.machines = []common.Machine{ 134 &mockMachine{ 135 id: "1", 136 containerType: "none", 137 life: state.Alive, 138 hw: &instance.HardwareCharacteristics{CpuCores: pUint64(1)}, 139 }, 140 &mockMachine{ 141 id: "2", 142 life: state.Alive, 143 containerType: "lxc", 144 }, 145 &mockMachine{ 146 id: "3", 147 life: state.Dead, 148 }, 149 } 150 151 s.callContext = context.NewCloudCallContext() 152 153 var err error 154 s.modelmanager, err = modelmanager.NewModelManagerAPI(s.st, s.ctlrSt, nil, nil, &s.authorizer, s.st.model, s.callContext) 155 c.Assert(err, jc.ErrorIsNil) 156 } 157 158 func (s *modelInfoSuite) setAPIUser(c *gc.C, user names.UserTag) { 159 s.authorizer.Tag = user 160 var err error 161 s.modelmanager, err = modelmanager.NewModelManagerAPI(s.st, s.ctlrSt, nil, nil, s.authorizer, s.st.model, s.callContext) 162 c.Assert(err, jc.ErrorIsNil) 163 } 164 165 func (s *modelInfoSuite) TestModelInfo(c *gc.C) { 166 info := s.getModelInfo(c, s.st.model.cfg.UUID()) 167 expectedAgentVersion, exists := s.st.model.cfg.AgentVersion() 168 c.Assert(exists, jc.IsTrue) 169 c.Assert(info, jc.DeepEquals, params.ModelInfo{ 170 Name: "testmodel", 171 UUID: s.st.model.cfg.UUID(), 172 Type: string(s.st.model.Type()), 173 ControllerUUID: "deadbeef-1bad-500d-9000-4b1d0d06f00d", 174 IsController: false, 175 OwnerTag: "user-bob", 176 ProviderType: "someprovider", 177 CloudTag: "cloud-some-cloud", 178 CloudRegion: "some-region", 179 CloudCredentialTag: "cloudcred-some-cloud_bob_some-credential", 180 DefaultSeries: version.SupportedLTS(), 181 Life: params.Dying, 182 Status: params.EntityStatus{ 183 Status: status.Destroying, 184 Since: &time.Time{}, 185 }, 186 Users: []params.ModelUserInfo{{ 187 UserName: "admin", 188 LastConnection: &time.Time{}, 189 Access: params.ModelAdminAccess, 190 }, { 191 UserName: "bob", 192 DisplayName: "Bob", 193 LastConnection: &time.Time{}, 194 Access: params.ModelReadAccess, 195 }, { 196 UserName: "charlotte", 197 DisplayName: "Charlotte", 198 LastConnection: &time.Time{}, 199 Access: params.ModelReadAccess, 200 }, { 201 UserName: "mary", 202 DisplayName: "Mary", 203 LastConnection: &time.Time{}, 204 Access: params.ModelWriteAccess, 205 }}, 206 Machines: []params.ModelMachineInfo{{ 207 Id: "1", 208 Hardware: ¶ms.MachineHardware{Cores: pUint64(1)}, 209 }, { 210 Id: "2", 211 }}, 212 SLA: ¶ms.ModelSLAInfo{ 213 Level: "essential", 214 Owner: "user", 215 }, 216 AgentVersion: &expectedAgentVersion, 217 }) 218 s.st.CheckCalls(c, []gitjujutesting.StubCall{ 219 {"ControllerTag", nil}, 220 {"ModelUUID", nil}, 221 {"GetBackend", []interface{}{s.st.model.cfg.UUID()}}, 222 {"Model", nil}, 223 {"IsController", nil}, 224 {"AllMachines", nil}, 225 {"LatestMigration", nil}, 226 }) 227 s.st.model.CheckCalls(c, []gitjujutesting.StubCall{ 228 {"UUID", nil}, 229 {"Name", nil}, 230 {"Type", nil}, 231 {"UUID", nil}, 232 {"ControllerUUID", nil}, 233 {"UUID", nil}, 234 {"Owner", nil}, 235 {"Life", nil}, 236 {"Cloud", nil}, 237 {"CloudRegion", nil}, 238 {"CloudCredential", nil}, 239 {"SLALevel", nil}, 240 {"SLAOwner", nil}, 241 {"Life", nil}, 242 {"Config", nil}, 243 {"Status", nil}, 244 {"Users", nil}, 245 {"ModelTag", nil}, 246 {"ModelTag", nil}, 247 {"ModelTag", nil}, 248 {"ModelTag", nil}, 249 {"LastModelConnection", []interface{}{names.NewUserTag("admin")}}, 250 {"LastModelConnection", []interface{}{names.NewLocalUserTag("bob")}}, 251 {"LastModelConnection", []interface{}{names.NewLocalUserTag("charlotte")}}, 252 {"LastModelConnection", []interface{}{names.NewLocalUserTag("mary")}}, 253 {"Type", nil}, 254 }) 255 } 256 257 func (s *modelInfoSuite) TestModelInfoWriteAccess(c *gc.C) { 258 mary := names.NewUserTag("mary@local") 259 s.authorizer.HasWriteTag = mary 260 s.setAPIUser(c, mary) 261 info := s.getModelInfo(c, s.st.model.cfg.UUID()) 262 c.Assert(info.Users, gc.HasLen, 1) 263 c.Assert(info.Users[0].UserName, gc.Equals, "mary") 264 c.Assert(info.Machines, gc.HasLen, 2) 265 } 266 267 func (s *modelInfoSuite) TestModelInfoNonOwner(c *gc.C) { 268 s.setAPIUser(c, names.NewUserTag("charlotte@local")) 269 info := s.getModelInfo(c, s.st.model.cfg.UUID()) 270 c.Assert(info.Users, gc.HasLen, 1) 271 c.Assert(info.Users[0].UserName, gc.Equals, "charlotte") 272 c.Assert(info.Machines, gc.HasLen, 0) 273 } 274 275 func (s *modelInfoSuite) getModelInfo(c *gc.C, modelUUID string) params.ModelInfo { 276 results, err := s.modelmanager.ModelInfo(params.Entities{ 277 Entities: []params.Entity{{ 278 names.NewModelTag(modelUUID).String(), 279 }}, 280 }) 281 c.Assert(err, jc.ErrorIsNil) 282 c.Assert(results.Results, gc.HasLen, 1) 283 c.Check(results.Results[0].Result, gc.NotNil) 284 c.Check(results.Results[0].Error, gc.IsNil) 285 return *results.Results[0].Result 286 } 287 288 func (s *modelInfoSuite) TestModelInfoErrorInvalidTag(c *gc.C) { 289 s.testModelInfoError(c, "user-bob", `"user-bob" is not a valid model tag`) 290 } 291 292 func (s *modelInfoSuite) TestModelInfoErrorGetModelNotFound(c *gc.C) { 293 s.st.SetErrors(errors.NotFoundf("model")) 294 s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`) 295 } 296 297 func (s *modelInfoSuite) TestModelInfoErrorModelConfig(c *gc.C) { 298 s.st.model.SetErrors(errors.Errorf("no config for you")) 299 s.testModelInfoError(c, coretesting.ModelTag.String(), `no config for you`) 300 } 301 302 func (s *modelInfoSuite) TestModelInfoErrorModelUsers(c *gc.C) { 303 s.st.model.SetErrors( 304 nil, //Config 305 nil, //Status 306 errors.Errorf("no users for you"), // Users 307 ) 308 s.testModelInfoError(c, coretesting.ModelTag.String(), `no users for you`) 309 } 310 311 func (s *modelInfoSuite) TestModelInfoErrorNoModelUsers(c *gc.C) { 312 s.st.model.users = nil 313 s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`) 314 } 315 316 func (s *modelInfoSuite) TestModelInfoErrorNoAccess(c *gc.C) { 317 s.setAPIUser(c, names.NewUserTag("nemo@local")) 318 s.testModelInfoError(c, coretesting.ModelTag.String(), `permission denied`) 319 } 320 321 func (s *modelInfoSuite) TestRunningMigration(c *gc.C) { 322 start := time.Now().Add(-20 * time.Minute) 323 s.st.migration = &mockMigration{ 324 status: "computing optimal bin packing", 325 start: start, 326 } 327 328 results, err := s.modelmanager.ModelInfo(params.Entities{ 329 Entities: []params.Entity{{coretesting.ModelTag.String()}}, 330 }) 331 332 c.Assert(err, jc.ErrorIsNil) 333 migrationResult := results.Results[0].Result.Migration 334 c.Assert(migrationResult.Status, gc.Equals, "computing optimal bin packing") 335 c.Assert(*migrationResult.Start, gc.Equals, start) 336 c.Assert(migrationResult.End, gc.IsNil) 337 } 338 339 func (s *modelInfoSuite) TestFailedMigration(c *gc.C) { 340 start := time.Now().Add(-20 * time.Minute) 341 end := time.Now().Add(-10 * time.Minute) 342 s.st.migration = &mockMigration{ 343 status: "couldn't realign alternate time frames", 344 start: start, 345 end: end, 346 } 347 348 results, err := s.modelmanager.ModelInfo(params.Entities{ 349 Entities: []params.Entity{{coretesting.ModelTag.String()}}, 350 }) 351 352 c.Assert(err, jc.ErrorIsNil) 353 migrationResult := results.Results[0].Result.Migration 354 c.Assert(migrationResult.Status, gc.Equals, "couldn't realign alternate time frames") 355 c.Assert(*migrationResult.Start, gc.Equals, start) 356 c.Assert(*migrationResult.End, gc.Equals, end) 357 } 358 359 func (s *modelInfoSuite) TestNoMigration(c *gc.C) { 360 results, err := s.modelmanager.ModelInfo(params.Entities{ 361 Entities: []params.Entity{{coretesting.ModelTag.String()}}, 362 }) 363 c.Assert(err, jc.ErrorIsNil) 364 c.Assert(results.Results[0].Result.Migration, gc.IsNil) 365 } 366 367 func (s *modelInfoSuite) TestAliveModelGetsAllInfo(c *gc.C) { 368 s.assertSuccess(c, s.st.model.cfg.UUID(), state.Alive, params.Alive) 369 } 370 371 func (s *modelInfoSuite) TestAliveModelWithConfigFailure(c *gc.C) { 372 s.st.model.life = state.Alive 373 s.setModelConfigError() 374 s.testModelInfoError(c, s.st.model.tag.String(), "config not found") 375 } 376 377 func (s *modelInfoSuite) TestAliveModelWithStatusFailure(c *gc.C) { 378 s.st.model.life = state.Alive 379 s.setModelStatusError() 380 s.testModelInfoError(c, s.st.model.tag.String(), "status not found") 381 } 382 383 func (s *modelInfoSuite) TestAliveModelWithUsersFailure(c *gc.C) { 384 s.st.model.life = state.Alive 385 s.setModelUsersError() 386 s.testModelInfoError(c, s.st.model.tag.String(), "users not found") 387 } 388 389 func (s *modelInfoSuite) TestDeadModelGetsAllInfo(c *gc.C) { 390 s.assertSuccess(c, s.st.model.cfg.UUID(), state.Dead, params.Dead) 391 } 392 393 func (s *modelInfoSuite) TestDeadModelWithConfigFailure(c *gc.C) { 394 testData := incompleteModelInfoTest{ 395 failModel: s.setModelConfigError, 396 desiredLife: state.Dead, 397 expectedLife: params.Dead, 398 } 399 s.assertSuccessWithMissingData(c, testData) 400 } 401 402 func (s *modelInfoSuite) TestDeadModelWithStatusFailure(c *gc.C) { 403 testData := incompleteModelInfoTest{ 404 failModel: s.setModelStatusError, 405 desiredLife: state.Dead, 406 expectedLife: params.Dead, 407 } 408 s.assertSuccessWithMissingData(c, testData) 409 } 410 411 func (s *modelInfoSuite) TestDeadModelWithUsersFailure(c *gc.C) { 412 testData := incompleteModelInfoTest{ 413 failModel: s.setModelUsersError, 414 desiredLife: state.Dead, 415 expectedLife: params.Dead, 416 } 417 s.assertSuccessWithMissingData(c, testData) 418 } 419 420 func (s *modelInfoSuite) TestDyingModelWithConfigFailure(c *gc.C) { 421 testData := incompleteModelInfoTest{ 422 failModel: s.setModelConfigError, 423 desiredLife: state.Dying, 424 expectedLife: params.Dying, 425 } 426 s.assertSuccessWithMissingData(c, testData) 427 } 428 429 func (s *modelInfoSuite) TestDyingModelWithStatusFailure(c *gc.C) { 430 testData := incompleteModelInfoTest{ 431 failModel: s.setModelStatusError, 432 desiredLife: state.Dying, 433 expectedLife: params.Dying, 434 } 435 s.assertSuccessWithMissingData(c, testData) 436 } 437 438 func (s *modelInfoSuite) TestDyingModelWithUsersFailure(c *gc.C) { 439 testData := incompleteModelInfoTest{ 440 failModel: s.setModelUsersError, 441 desiredLife: state.Dying, 442 expectedLife: params.Dying, 443 } 444 s.assertSuccessWithMissingData(c, testData) 445 } 446 447 func (s *modelInfoSuite) TestImportingModelGetsAllInfo(c *gc.C) { 448 s.st.model.migrationStatus = state.MigrationModeImporting 449 s.assertSuccess(c, s.st.model.cfg.UUID(), state.Alive, params.Alive) 450 } 451 452 func (s *modelInfoSuite) TestImportingModelWithConfigFailure(c *gc.C) { 453 s.st.model.migrationStatus = state.MigrationModeImporting 454 testData := incompleteModelInfoTest{ 455 failModel: s.setModelConfigError, 456 desiredLife: state.Alive, 457 expectedLife: params.Alive, 458 } 459 s.assertSuccessWithMissingData(c, testData) 460 } 461 462 func (s *modelInfoSuite) TestImportingModelWithStatusFailure(c *gc.C) { 463 s.st.model.migrationStatus = state.MigrationModeImporting 464 testData := incompleteModelInfoTest{ 465 failModel: s.setModelStatusError, 466 desiredLife: state.Alive, 467 expectedLife: params.Alive, 468 } 469 s.assertSuccessWithMissingData(c, testData) 470 } 471 472 func (s *modelInfoSuite) TestImportingModelWithUsersFailure(c *gc.C) { 473 s.st.model.migrationStatus = state.MigrationModeImporting 474 testData := incompleteModelInfoTest{ 475 failModel: s.setModelUsersError, 476 desiredLife: state.Alive, 477 expectedLife: params.Alive, 478 } 479 s.assertSuccessWithMissingData(c, testData) 480 } 481 482 type incompleteModelInfoTest struct { 483 failModel func() 484 desiredLife state.Life 485 expectedLife params.Life 486 } 487 488 func (s *modelInfoSuite) setModelConfigError() { 489 s.st.model.SetErrors(errors.NotFoundf("config")) 490 } 491 492 func (s *modelInfoSuite) setModelStatusError() { 493 s.st.model.SetErrors( 494 nil, //Config 495 errors.NotFoundf("status"), //Status 496 ) 497 } 498 499 func (s *modelInfoSuite) setModelUsersError() { 500 s.st.model.SetErrors( 501 nil, //Config 502 nil, //Status 503 errors.NotFoundf("users"), //Users 504 ) 505 } 506 507 func (s *modelInfoSuite) assertSuccessWithMissingData(c *gc.C, test incompleteModelInfoTest) { 508 test.failModel() 509 // We do not expect any errors to surface and still want to get basic model info. 510 s.assertSuccess(c, s.st.model.cfg.UUID(), test.desiredLife, test.expectedLife) 511 } 512 513 func (s *modelInfoSuite) assertSuccess(c *gc.C, modelUUID string, desiredLife state.Life, expectedLife params.Life) { 514 s.st.model.life = desiredLife 515 // should get no errors 516 info := s.getModelInfo(c, modelUUID) 517 c.Assert(info.UUID, gc.Equals, modelUUID) 518 c.Assert(info.Life, gc.Equals, expectedLife) 519 } 520 521 func (s *modelInfoSuite) testModelInfoError(c *gc.C, modelTag, expectedErr string) { 522 results, err := s.modelmanager.ModelInfo(params.Entities{ 523 Entities: []params.Entity{{modelTag}}, 524 }) 525 c.Assert(err, jc.ErrorIsNil) 526 c.Assert(results.Results, gc.HasLen, 1) 527 c.Assert(results.Results[0].Result, gc.IsNil) 528 c.Assert(results.Results[0].Error, gc.ErrorMatches, expectedErr) 529 } 530 531 type unitRetriever interface { 532 Unit(name string) (*state.Unit, error) 533 } 534 535 // metricSender defines methods required by the metricsender package. 536 type metricSender interface { 537 MetricsManager() (*state.MetricsManager, error) 538 MetricsToSend(batchSize int) ([]*state.MetricBatch, error) 539 SetMetricBatchesSent(batchUUIDs []string) error 540 CountOfUnsentMetrics() (int, error) 541 CountOfSentMetrics() (int, error) 542 CleanupOldMetrics() error 543 } 544 545 type mockCaasBroker struct { 546 gitjujutesting.Stub 547 caas.Broker 548 549 namespaces []string 550 } 551 552 func (m *mockCaasBroker) Namespaces() ([]string, error) { 553 m.MethodCall(m, "Namespaces") 554 return m.namespaces, nil 555 } 556 557 type mockState struct { 558 gitjujutesting.Stub 559 560 environs.EnvironConfigGetter 561 common.APIHostPortsForAgentsGetter 562 common.ToolsStorageGetter 563 common.BlockGetter 564 metricSender 565 unitRetriever 566 567 controllerUUID string 568 cloud cloud.Cloud 569 clouds map[names.CloudTag]cloud.Cloud 570 cloudUsers map[string]permission.Access 571 model *mockModel 572 controllerModel *mockModel 573 users []permission.UserAccess 574 cred state.Credential 575 machines []common.Machine 576 cfgDefaults config.ModelDefaultAttributes 577 blockMsg string 578 block state.BlockType 579 migration *mockMigration 580 modelConfig *config.Config 581 582 modelDetailsForUser func() ([]state.ModelSummary, error) 583 } 584 585 type fakeModelDescription struct { 586 description.Model `yaml:"-"` 587 588 UUID string `yaml:"model-uuid"` 589 } 590 591 func (st *mockState) ModelUUID() string { 592 st.MethodCall(st, "ModelUUID") 593 return st.model.UUID() 594 } 595 596 func (st *mockState) Name() string { 597 st.MethodCall(st, "Name") 598 return "test-model" 599 } 600 601 func (st *mockState) ControllerModelUUID() string { 602 st.MethodCall(st, "ControllerModelUUID") 603 return st.controllerModel.tag.Id() 604 } 605 606 func (st *mockState) ControllerModelTag() names.ModelTag { 607 st.MethodCall(st, "ControllerModelTag") 608 return st.controllerModel.tag 609 } 610 611 func (st *mockState) Export() (description.Model, error) { 612 st.MethodCall(st, "Export") 613 return &fakeModelDescription{UUID: st.model.UUID()}, nil 614 } 615 616 func (st *mockState) ExportPartial(cfg state.ExportConfig) (description.Model, error) { 617 st.MethodCall(st, "ExportPartial", cfg) 618 return st.Export() 619 } 620 621 func (st *mockState) AllModelUUIDs() ([]string, error) { 622 st.MethodCall(st, "AllModelUUIDs") 623 return []string{st.model.UUID()}, st.NextErr() 624 } 625 626 func (st *mockState) GetBackend(modelUUID string) (common.ModelManagerBackend, func() bool, error) { 627 st.MethodCall(st, "GetBackend", modelUUID) 628 return st, func() bool { return true }, st.NextErr() 629 } 630 631 func (st *mockState) GetModel(modelUUID string) (common.Model, func() bool, error) { 632 st.MethodCall(st, "GetModel", modelUUID) 633 return st.model, func() bool { return true }, st.NextErr() 634 } 635 636 func (st *mockState) ModelUUIDsForUser(user names.UserTag) ([]string, error) { 637 st.MethodCall(st, "ModelUUIDsForUser", user) 638 return nil, st.NextErr() 639 } 640 641 func (st *mockState) AllApplications() ([]common.Application, error) { 642 st.MethodCall(st, "AllApplications") 643 return nil, st.NextErr() 644 } 645 646 func (st *mockState) AllVolumes() ([]state.Volume, error) { 647 st.MethodCall(st, "AllVolumes") 648 return nil, st.NextErr() 649 } 650 651 func (st *mockState) AllFilesystems() ([]state.Filesystem, error) { 652 st.MethodCall(st, "AllFilesystems") 653 return nil, st.NextErr() 654 } 655 656 func (st *mockState) IsControllerAdmin(user names.UserTag) (bool, error) { 657 st.MethodCall(st, "IsControllerAdmin", user) 658 if st.controllerModel == nil { 659 return user.Id() == "admin", st.NextErr() 660 } 661 if st.controllerModel.users == nil { 662 return user.Id() == "admin", st.NextErr() 663 } 664 665 for _, u := range st.controllerModel.users { 666 if user.Name() == u.userName && u.access == permission.AdminAccess { 667 nextErr := st.NextErr() 668 if user.Name() != "admin" { 669 panic(user.Name()) 670 } 671 return true, nextErr 672 } 673 } 674 return false, st.NextErr() 675 } 676 677 func (st *mockState) GetCloudAccess(cloud string, user names.UserTag) (permission.Access, error) { 678 st.MethodCall(st, "GetCloudAccess", user) 679 if perm, ok := st.cloudUsers[user.Id()]; ok { 680 return perm, nil 681 } 682 return permission.NoAccess, errors.NotFoundf("user %v", user.Id()) 683 } 684 685 func (st *mockState) NewModel(args state.ModelArgs) (common.Model, common.ModelManagerBackend, error) { 686 st.MethodCall(st, "NewModel", args) 687 st.model.tag = names.NewModelTag(args.Config.UUID()) 688 return st.model, st, st.NextErr() 689 } 690 691 func (st *mockState) ControllerModel() (common.Model, error) { 692 st.MethodCall(st, "ControllerModel") 693 return st.controllerModel, st.NextErr() 694 } 695 696 func (st *mockState) ControllerTag() names.ControllerTag { 697 st.MethodCall(st, "ControllerTag") 698 return names.NewControllerTag(st.controllerUUID) 699 } 700 701 func (st *mockState) ComposeNewModelConfig(modelAttr map[string]interface{}, regionSpec *environs.RegionSpec) (map[string]interface{}, error) { 702 st.MethodCall(st, "ComposeNewModelConfig") 703 attr := make(map[string]interface{}) 704 for attrName, val := range modelAttr { 705 attr[attrName] = val 706 } 707 attr["something"] = "value" 708 return attr, st.NextErr() 709 } 710 711 func (st *mockState) ControllerUUID() string { 712 st.MethodCall(st, "ControllerUUID") 713 return st.controllerUUID 714 } 715 716 func (st *mockState) IsController() bool { 717 st.MethodCall(st, "IsController") 718 return st.controllerUUID == st.model.UUID() 719 } 720 721 func (st *mockState) ControllerConfig() (controller.Config, error) { 722 st.MethodCall(st, "ControllerConfig") 723 return controller.Config{ 724 controller.ControllerUUIDKey: "deadbeef-1bad-500d-9000-4b1d0d06f00d", 725 }, st.NextErr() 726 } 727 728 func (st *mockState) Model() (common.Model, error) { 729 st.MethodCall(st, "Model") 730 return st.model, st.NextErr() 731 } 732 733 func (st *mockState) ModelTag() names.ModelTag { 734 st.MethodCall(st, "ModelTag") 735 return st.model.ModelTag() 736 } 737 738 func (st *mockState) AllMachines() ([]common.Machine, error) { 739 st.MethodCall(st, "AllMachines") 740 return st.machines, st.NextErr() 741 } 742 743 func (st *mockState) Clouds() (map[names.CloudTag]cloud.Cloud, error) { 744 st.MethodCall(st, "Clouds") 745 return st.clouds, st.NextErr() 746 } 747 748 func (st *mockState) Cloud(name string) (cloud.Cloud, error) { 749 st.MethodCall(st, "Cloud", name) 750 return st.cloud, st.NextErr() 751 } 752 753 func (st *mockState) CloudCredential(tag names.CloudCredentialTag) (state.Credential, error) { 754 st.MethodCall(st, "CloudCredential", tag) 755 return st.cred, st.NextErr() 756 } 757 758 func (st *mockState) Close() error { 759 st.MethodCall(st, "Close") 760 return st.NextErr() 761 } 762 763 func (st *mockState) AddControllerUser(spec state.UserAccessSpec) (permission.UserAccess, error) { 764 st.MethodCall(st, "AddControllerUser", spec) 765 return permission.UserAccess{}, st.NextErr() 766 } 767 768 func (st *mockState) UserAccess(tag names.UserTag, target names.Tag) (permission.UserAccess, error) { 769 st.MethodCall(st, "ModelUser", tag, target) 770 for _, user := range st.users { 771 if user.UserTag != tag { 772 continue 773 } 774 nextErr := st.NextErr() 775 if nextErr != nil { 776 return permission.UserAccess{}, nextErr 777 } 778 return user, nil 779 } 780 return permission.UserAccess{}, st.NextErr() 781 } 782 783 func (st *mockState) ModelSummariesForUser(user names.UserTag, all bool) ([]state.ModelSummary, error) { 784 st.MethodCall(st, "ModelSummariesForUser", user, all) 785 return st.modelDetailsForUser() 786 } 787 788 func (st *mockState) ModelBasicInfoForUser(user names.UserTag) ([]state.ModelAccessInfo, error) { 789 st.MethodCall(st, "ModelBasicInfoForUser", user) 790 return []state.ModelAccessInfo{}, st.NextErr() 791 } 792 793 func (st *mockState) RemoveUserAccess(subject names.UserTag, target names.Tag) error { 794 st.MethodCall(st, "RemoveUserAccess", subject, target) 795 return st.NextErr() 796 } 797 798 func (st *mockState) SetUserAccess(subject names.UserTag, target names.Tag, access permission.Access) (permission.UserAccess, error) { 799 st.MethodCall(st, "SetUserAccess", subject, target, access) 800 return permission.UserAccess{}, st.NextErr() 801 } 802 803 func (st *mockState) ModelConfigDefaultValues() (config.ModelDefaultAttributes, error) { 804 st.MethodCall(st, "ModelConfigDefaultValues") 805 return st.cfgDefaults, nil 806 } 807 808 func (st *mockState) UpdateModelConfigDefaultValues(update map[string]interface{}, remove []string, rspec *environs.RegionSpec) error { 809 st.MethodCall(st, "UpdateModelConfigDefaultValues", update, remove, rspec) 810 for k, v := range update { 811 if rspec != nil { 812 adv := st.cfgDefaults[k] 813 adv.Regions = append(adv.Regions, config.RegionDefaultValue{ 814 Name: rspec.Region, 815 Value: v}) 816 817 } else { 818 st.cfgDefaults[k] = config.AttributeDefaultValues{Controller: v} 819 } 820 } 821 for _, n := range remove { 822 if rspec != nil { 823 for i, r := range st.cfgDefaults[n].Regions { 824 if r.Name == rspec.Region { 825 adv := st.cfgDefaults[n] 826 adv.Regions = append(adv.Regions[:i], adv.Regions[i+1:]...) 827 st.cfgDefaults[n] = adv 828 } 829 } 830 } else { 831 if len(st.cfgDefaults[n].Regions) == 0 { 832 delete(st.cfgDefaults, n) 833 } else { 834 835 st.cfgDefaults[n] = config.AttributeDefaultValues{ 836 Regions: st.cfgDefaults[n].Regions} 837 } 838 } 839 } 840 return nil 841 } 842 843 func (st *mockState) GetBlockForType(t state.BlockType) (state.Block, bool, error) { 844 st.MethodCall(st, "GetBlockForType", t) 845 if st.block == t { 846 return &mockBlock{t: t, m: st.blockMsg}, true, nil 847 } else { 848 return nil, false, nil 849 } 850 } 851 852 func (st *mockState) ReloadSpaces(environ environs.Environ) error { 853 st.MethodCall(st, "ReloadSpaces", environ) 854 return st.NextErr() 855 } 856 857 func (st *mockState) DumpAll() (map[string]interface{}, error) { 858 st.MethodCall(st, "DumpAll") 859 return map[string]interface{}{ 860 "models": "lots of data", 861 }, st.NextErr() 862 } 863 864 func (st *mockState) LatestMigration() (state.ModelMigration, error) { 865 st.MethodCall(st, "LatestMigration") 866 if st.migration == nil { 867 // Handle nil->notfound directly here rather than having to 868 // count errors. 869 return nil, errors.NotFoundf("") 870 } 871 return st.migration, st.NextErr() 872 } 873 874 func (st *mockState) SetModelMeterStatus(level, message string) error { 875 st.MethodCall(st, "SetModelMeterStatus", level, message) 876 return st.NextErr() 877 } 878 879 func (st *mockState) ModelConfig() (*config.Config, error) { 880 st.MethodCall(st, "ModelConfig") 881 return st.modelConfig, st.NextErr() 882 } 883 884 func (st *mockState) MetricsManager() (*state.MetricsManager, error) { 885 st.MethodCall(st, "MetricsManager") 886 return nil, errors.New("nope") 887 } 888 889 type mockBlock struct { 890 state.Block 891 t state.BlockType 892 m string 893 } 894 895 func (m mockBlock) Id() string { return "" } 896 897 func (m mockBlock) Tag() (names.Tag, error) { return names.NewModelTag("mocktesting"), nil } 898 899 func (m mockBlock) Type() state.BlockType { return m.t } 900 901 func (m mockBlock) Message() string { return m.m } 902 903 func (m mockBlock) ModelUUID() string { return "" } 904 905 type mockMachine struct { 906 common.Machine 907 id string 908 life state.Life 909 containerType instance.ContainerType 910 hw *instance.HardwareCharacteristics 911 } 912 913 func (m *mockMachine) Id() string { 914 return m.id 915 } 916 917 func (m *mockMachine) Life() state.Life { 918 return m.life 919 } 920 921 func (m *mockMachine) ContainerType() instance.ContainerType { 922 return m.containerType 923 } 924 925 func (m *mockMachine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) { 926 return m.hw, nil 927 } 928 929 func (m *mockMachine) AgentPresence() (bool, error) { 930 return true, nil 931 } 932 933 func (m *mockMachine) InstanceId() (instance.Id, error) { 934 return "", nil 935 } 936 937 func (m *mockMachine) InstanceNames() (instance.Id, string, error) { 938 return "", "", nil 939 } 940 941 func (m *mockMachine) WantsVote() bool { 942 return false 943 } 944 945 func (m *mockMachine) HasVote() bool { 946 return false 947 } 948 949 func (m *mockMachine) Status() (status.StatusInfo, error) { 950 return status.StatusInfo{}, nil 951 } 952 953 type mockModel struct { 954 gitjujutesting.Stub 955 owner names.UserTag 956 life state.Life 957 tag names.ModelTag 958 status status.StatusInfo 959 cfg *config.Config 960 users []*mockModelUser 961 migrationStatus state.MigrationMode 962 controllerUUID string 963 isController bool 964 cfgDefaults config.ModelDefaultAttributes 965 setCloudCredentialF func(tag names.CloudCredentialTag) (bool, error) 966 } 967 968 func (m *mockModel) Config() (*config.Config, error) { 969 m.MethodCall(m, "Config") 970 return m.cfg, m.NextErr() 971 } 972 973 func (m *mockModel) Owner() names.UserTag { 974 m.MethodCall(m, "Owner") 975 return m.owner 976 } 977 978 func (m *mockModel) ModelTag() names.ModelTag { 979 m.MethodCall(m, "ModelTag") 980 return m.tag 981 } 982 983 func (m *mockModel) Type() state.ModelType { 984 m.MethodCall(m, "Type") 985 return state.ModelTypeIAAS 986 } 987 988 func (m *mockModel) Life() state.Life { 989 m.MethodCall(m, "Life") 990 return m.life 991 } 992 993 func (m *mockModel) Status() (status.StatusInfo, error) { 994 m.MethodCall(m, "Status") 995 return m.status, m.NextErr() 996 } 997 998 func (m *mockModel) Cloud() string { 999 m.MethodCall(m, "Cloud") 1000 return "some-cloud" 1001 } 1002 1003 func (m *mockModel) CloudRegion() string { 1004 m.MethodCall(m, "CloudRegion") 1005 return "some-region" 1006 } 1007 1008 func (m *mockModel) CloudCredential() (names.CloudCredentialTag, bool) { 1009 m.MethodCall(m, "CloudCredential") 1010 return names.NewCloudCredentialTag("some-cloud/bob/some-credential"), true 1011 } 1012 1013 func (m *mockModel) Users() ([]permission.UserAccess, error) { 1014 m.MethodCall(m, "Users") 1015 if err := m.NextErr(); err != nil { 1016 return nil, err 1017 } 1018 users := make([]permission.UserAccess, len(m.users)) 1019 for i, user := range m.users { 1020 users[i] = permission.UserAccess{ 1021 UserID: strings.ToLower(user.userName), 1022 UserTag: names.NewUserTag(user.userName), 1023 Object: m.ModelTag(), 1024 Access: user.access, 1025 DisplayName: user.displayName, 1026 UserName: user.userName, 1027 } 1028 } 1029 return users, nil 1030 } 1031 1032 func (m *mockModel) Destroy(args state.DestroyModelParams) error { 1033 m.MethodCall(m, "Destroy", args) 1034 return m.NextErr() 1035 } 1036 1037 func (m *mockModel) SLALevel() string { 1038 m.MethodCall(m, "SLALevel") 1039 return "essential" 1040 } 1041 1042 func (m *mockModel) SLAOwner() string { 1043 m.MethodCall(m, "SLAOwner") 1044 return "user" 1045 } 1046 1047 func (m *mockModel) ControllerUUID() string { 1048 m.MethodCall(m, "ControllerUUID") 1049 return m.controllerUUID 1050 } 1051 1052 func (m *mockModel) UUID() string { 1053 m.MethodCall(m, "UUID") 1054 return m.cfg.UUID() 1055 } 1056 1057 func (m *mockModel) Name() string { 1058 m.MethodCall(m, "Name") 1059 return m.cfg.Name() 1060 } 1061 1062 func (m *mockModel) MigrationMode() state.MigrationMode { 1063 m.MethodCall(m, "MigrationMode") 1064 return m.migrationStatus 1065 } 1066 1067 func (m *mockModel) AddUser(spec state.UserAccessSpec) (permission.UserAccess, error) { 1068 m.MethodCall(m, "AddUser", spec) 1069 return permission.UserAccess{}, m.NextErr() 1070 } 1071 func (m *mockModel) LastModelConnection(user names.UserTag) (time.Time, error) { 1072 m.MethodCall(m, "LastModelConnection", user) 1073 return time.Time{}, m.NextErr() 1074 } 1075 1076 func (m *mockModel) AutoConfigureContainerNetworking(environ environs.Environ) error { 1077 m.MethodCall(m, "AutoConfigureContainerNetworking", environ) 1078 return m.NextErr() 1079 } 1080 1081 func (m *mockModel) ModelConfigDefaultValues() (config.ModelDefaultAttributes, error) { 1082 m.MethodCall(m, "ModelConfigDefaultValues") 1083 return m.cfgDefaults, nil 1084 } 1085 1086 func (m *mockModel) getModelDetails() state.ModelSummary { 1087 cred, _ := m.CloudCredential() 1088 return state.ModelSummary{ 1089 Name: m.Name(), 1090 UUID: m.UUID(), 1091 Type: m.Type(), 1092 Life: m.Life(), 1093 Owner: m.Owner().Id(), 1094 ControllerUUID: m.ControllerUUID(), 1095 SLALevel: m.SLALevel(), 1096 SLAOwner: m.SLAOwner(), 1097 CloudTag: m.Cloud(), 1098 CloudRegion: m.CloudRegion(), 1099 CloudCredentialTag: cred.String(), 1100 } 1101 } 1102 1103 func (m *mockModel) SetCloudCredential(tag names.CloudCredentialTag) (bool, error) { 1104 m.MethodCall(m, "SetCloudCredential", tag) 1105 return m.setCloudCredentialF(tag) 1106 } 1107 1108 type mockModelUser struct { 1109 gitjujutesting.Stub 1110 userName string 1111 displayName string 1112 lastConnection time.Time 1113 access permission.Access 1114 } 1115 1116 type mockMigration struct { 1117 state.ModelMigration 1118 1119 status string 1120 start time.Time 1121 end time.Time 1122 } 1123 1124 func (m *mockMigration) StatusMessage() string { 1125 return m.status 1126 } 1127 1128 func (m *mockMigration) StartTime() time.Time { 1129 return m.start 1130 } 1131 1132 func (m *mockMigration) EndTime() time.Time { 1133 return m.end 1134 }