github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/testing/factory/factory.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package factory 5 6 import ( 7 "fmt" 8 "math/rand" 9 "strconv" 10 "sync/atomic" 11 "time" 12 13 "github.com/juju/os/series" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 "github.com/juju/utils/arch" 17 "github.com/juju/version" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/charm.v6" 20 charmresource "gopkg.in/juju/charm.v6/resource" 21 "gopkg.in/juju/environschema.v1" 22 "gopkg.in/juju/names.v2" 23 24 "github.com/juju/juju/cloud" 25 "github.com/juju/juju/core/application" 26 "github.com/juju/juju/core/constraints" 27 "github.com/juju/juju/core/instance" 28 "github.com/juju/juju/core/status" 29 "github.com/juju/juju/environs/config" 30 "github.com/juju/juju/network" 31 "github.com/juju/juju/permission" 32 "github.com/juju/juju/state" 33 "github.com/juju/juju/storage" 34 "github.com/juju/juju/storage/provider" 35 "github.com/juju/juju/testcharms" 36 "github.com/juju/juju/testing" 37 jujuversion "github.com/juju/juju/version" 38 ) 39 40 const ( 41 symbols = "abcdefghijklmopqrstuvwxyz" 42 ) 43 44 type Factory struct { 45 pool *state.StatePool 46 st *state.State 47 } 48 49 var index uint32 50 51 func NewFactory(st *state.State, pool *state.StatePool) *Factory { 52 return &Factory{ 53 st: st, 54 pool: pool, 55 } 56 } 57 58 // UserParams defines the parameters for creating a user with MakeUser. 59 type UserParams struct { 60 Name string 61 DisplayName string 62 Password string 63 Creator names.Tag 64 NoModelUser bool 65 Disabled bool 66 Access permission.Access 67 } 68 69 // ModelUserParams defines the parameters for creating an environment user. 70 type ModelUserParams struct { 71 User string 72 DisplayName string 73 CreatedBy names.Tag 74 Access permission.Access 75 } 76 77 // CharmParams defines the parameters for creating a charm. 78 type CharmParams struct { 79 Name string 80 Series string 81 Revision string 82 URL string 83 } 84 85 // Params for creating a machine. 86 type MachineParams struct { 87 Series string 88 Jobs []state.MachineJob 89 Password string 90 Nonce string 91 Constraints constraints.Value 92 InstanceId instance.Id 93 DisplayName string 94 Characteristics *instance.HardwareCharacteristics 95 Addresses []network.Address 96 Volumes []state.HostVolumeParams 97 Filesystems []state.HostFilesystemParams 98 } 99 100 // ApplicationParams is used when specifying parameters for a new application. 101 type ApplicationParams struct { 102 Name string 103 Charm *state.Charm 104 Status *status.StatusInfo 105 ApplicationConfig map[string]interface{} 106 ApplicationConfigFields environschema.Fields 107 CharmConfig map[string]interface{} 108 Storage map[string]state.StorageConstraints 109 Constraints constraints.Value 110 EndpointBindings map[string]string 111 Password string 112 Placement []*instance.Placement 113 DesiredScale int 114 } 115 116 // UnitParams are used to create units. 117 type UnitParams struct { 118 Application *state.Application 119 Machine *state.Machine 120 Password string 121 SetCharmURL bool 122 Status *status.StatusInfo 123 Constraints constraints.Value 124 } 125 126 // RelationParams are used to create relations. 127 type RelationParams struct { 128 Endpoints []state.Endpoint 129 } 130 131 type MetricParams struct { 132 Unit *state.Unit 133 Time *time.Time 134 Metrics []state.Metric 135 Sent bool 136 DeleteTime *time.Time 137 } 138 139 type ModelParams struct { 140 Type state.ModelType 141 Name string 142 Owner names.Tag 143 ConfigAttrs testing.Attrs 144 CloudName string 145 CloudRegion string 146 CloudCredential names.CloudCredentialTag 147 StorageProviderRegistry storage.ProviderRegistry 148 EnvironVersion int 149 } 150 151 type SpaceParams struct { 152 Name string 153 ProviderID network.Id 154 Subnets []string 155 IsPublic bool 156 } 157 158 // RandomSuffix adds a random 5 character suffix to the presented string. 159 func (*Factory) RandomSuffix(prefix string) string { 160 result := prefix 161 for i := 0; i < 5; i++ { 162 result += string(symbols[rand.Intn(len(symbols))]) 163 } 164 return result 165 } 166 167 func uniqueInteger() int { 168 return int(atomic.AddUint32(&index, 1)) 169 } 170 171 func uniqueString(prefix string) string { 172 if prefix == "" { 173 prefix = "no-prefix" 174 } 175 return fmt.Sprintf("%s-%d", prefix, uniqueInteger()) 176 } 177 178 // MakeUser will create a user with values defined by the params. 179 // For attributes of UserParams that are the default empty values, 180 // some meaningful valid values are used instead. 181 // If params is not specified, defaults are used. 182 // If params.NoModelUser is false, the user will also be created 183 // in the current model. 184 func (factory *Factory) MakeUser(c *gc.C, params *UserParams) *state.User { 185 if params == nil { 186 params = &UserParams{} 187 } 188 if params.Name == "" { 189 params.Name = uniqueString("username") 190 } 191 if params.DisplayName == "" { 192 params.DisplayName = uniqueString("display name") 193 } 194 if params.Password == "" { 195 params.Password = "password" 196 } 197 if params.Creator == nil { 198 env, err := factory.st.Model() 199 c.Assert(err, jc.ErrorIsNil) 200 params.Creator = env.Owner() 201 } 202 if params.Access == permission.NoAccess { 203 params.Access = permission.AdminAccess 204 } 205 creatorUserTag := params.Creator.(names.UserTag) 206 user, err := factory.st.AddUser( 207 params.Name, params.DisplayName, params.Password, creatorUserTag.Name()) 208 c.Assert(err, jc.ErrorIsNil) 209 if !params.NoModelUser { 210 model, err := factory.st.Model() 211 c.Assert(err, jc.ErrorIsNil) 212 _, err = model.AddUser(state.UserAccessSpec{ 213 User: user.UserTag(), 214 CreatedBy: names.NewUserTag(user.CreatedBy()), 215 DisplayName: params.DisplayName, 216 Access: params.Access, 217 }) 218 c.Assert(err, jc.ErrorIsNil) 219 } 220 if params.Disabled { 221 err := user.Disable() 222 c.Assert(err, jc.ErrorIsNil) 223 } 224 return user 225 } 226 227 // MakeModelUser will create a modelUser with values defined by the params. For 228 // attributes of ModelUserParams that are the default empty values, some 229 // meaningful valid values are used instead. If params is not specified, 230 // defaults are used. 231 func (factory *Factory) MakeModelUser(c *gc.C, params *ModelUserParams) permission.UserAccess { 232 if params == nil { 233 params = &ModelUserParams{} 234 } 235 if params.User == "" { 236 user := factory.MakeUser(c, &UserParams{NoModelUser: true}) 237 params.User = user.UserTag().Id() 238 } 239 if params.DisplayName == "" { 240 params.DisplayName = uniqueString("display name") 241 } 242 if params.Access == permission.NoAccess { 243 params.Access = permission.AdminAccess 244 } 245 if params.CreatedBy == nil { 246 env, err := factory.st.Model() 247 c.Assert(err, jc.ErrorIsNil) 248 params.CreatedBy = env.Owner() 249 } 250 model, err := factory.st.Model() 251 c.Assert(err, jc.ErrorIsNil) 252 253 createdByUserTag := params.CreatedBy.(names.UserTag) 254 modelUser, err := model.AddUser(state.UserAccessSpec{ 255 User: names.NewUserTag(params.User), 256 CreatedBy: createdByUserTag, 257 DisplayName: params.DisplayName, 258 Access: params.Access, 259 }) 260 c.Assert(err, jc.ErrorIsNil) 261 return modelUser 262 } 263 264 func (factory *Factory) paramsFillDefaults(c *gc.C, params *MachineParams) *MachineParams { 265 if params == nil { 266 params = &MachineParams{} 267 } 268 if params.Series == "" { 269 params.Series = "quantal" 270 } 271 if params.Nonce == "" { 272 params.Nonce = "nonce" 273 } 274 if len(params.Jobs) == 0 { 275 params.Jobs = []state.MachineJob{state.JobHostUnits} 276 } 277 if params.InstanceId == "" { 278 params.InstanceId = instance.Id(uniqueString("id")) 279 } 280 if params.Password == "" { 281 var err error 282 params.Password, err = utils.RandomPassword() 283 c.Assert(err, jc.ErrorIsNil) 284 } 285 if params.Characteristics == nil { 286 arch := "amd64" 287 mem := uint64(64 * 1024 * 1024 * 1024) 288 hardware := instance.HardwareCharacteristics{ 289 Arch: &arch, 290 Mem: &mem, 291 } 292 params.Characteristics = &hardware 293 } 294 295 return params 296 } 297 298 // MakeMachineNested will make a machine nested in the machine with ID given. 299 func (factory *Factory) MakeMachineNested(c *gc.C, parentId string, params *MachineParams) *state.Machine { 300 params = factory.paramsFillDefaults(c, params) 301 machineTemplate := state.MachineTemplate{ 302 Series: params.Series, 303 Jobs: params.Jobs, 304 Volumes: params.Volumes, 305 Filesystems: params.Filesystems, 306 Constraints: params.Constraints, 307 } 308 309 m, err := factory.st.AddMachineInsideMachine( 310 machineTemplate, 311 parentId, 312 instance.LXD, 313 ) 314 c.Assert(err, jc.ErrorIsNil) 315 err = m.SetProvisioned(params.InstanceId, params.DisplayName, params.Nonce, params.Characteristics) 316 c.Assert(err, jc.ErrorIsNil) 317 current := version.Binary{ 318 Number: jujuversion.Current, 319 Arch: arch.HostArch(), 320 Series: series.MustHostSeries(), 321 } 322 err = m.SetAgentVersion(current) 323 c.Assert(err, jc.ErrorIsNil) 324 return m 325 } 326 327 // MakeMachine will add a machine with values defined in params. For some 328 // values in params, if they are missing, some meaningful empty values will be 329 // set. 330 // If params is not specified, defaults are used. 331 func (factory *Factory) MakeMachine(c *gc.C, params *MachineParams) *state.Machine { 332 machine, _ := factory.MakeMachineReturningPassword(c, params) 333 return machine 334 } 335 336 // MakeMachineReturningPassword will add a machine with values defined in 337 // params. For some values in params, if they are missing, some meaningful 338 // empty values will be set. If params is not specified, defaults are used. 339 // The machine and its password are returned. 340 func (factory *Factory) MakeMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) { 341 params = factory.paramsFillDefaults(c, params) 342 return factory.makeMachineReturningPassword(c, params, true) 343 } 344 345 // MakeUnprovisionedMachineReturningPassword will add a machine with values 346 // defined in params. For some values in params, if they are missing, some 347 // meaningful empty values will be set. If params is not specified, defaults 348 // are used. The machine and its password are returned; the machine will not 349 // be provisioned. 350 func (factory *Factory) MakeUnprovisionedMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) { 351 if params != nil { 352 c.Assert(params.Nonce, gc.Equals, "") 353 c.Assert(params.InstanceId, gc.Equals, instance.Id("")) 354 c.Assert(params.Characteristics, gc.IsNil) 355 } 356 params = factory.paramsFillDefaults(c, params) 357 params.Nonce = "" 358 params.InstanceId = "" 359 params.Characteristics = nil 360 return factory.makeMachineReturningPassword(c, params, false) 361 } 362 363 func (factory *Factory) makeMachineReturningPassword(c *gc.C, params *MachineParams, setProvisioned bool) (*state.Machine, string) { 364 machineTemplate := state.MachineTemplate{ 365 Series: params.Series, 366 Jobs: params.Jobs, 367 Volumes: params.Volumes, 368 Filesystems: params.Filesystems, 369 Constraints: params.Constraints, 370 } 371 372 if params.Characteristics != nil { 373 machineTemplate.HardwareCharacteristics = *params.Characteristics 374 } 375 machine, err := factory.st.AddOneMachine(machineTemplate) 376 c.Assert(err, jc.ErrorIsNil) 377 if setProvisioned { 378 err = machine.SetProvisioned(params.InstanceId, params.DisplayName, params.Nonce, params.Characteristics) 379 c.Assert(err, jc.ErrorIsNil) 380 } 381 err = machine.SetPassword(params.Password) 382 c.Assert(err, jc.ErrorIsNil) 383 if len(params.Addresses) > 0 { 384 err := machine.SetProviderAddresses(params.Addresses...) 385 c.Assert(err, jc.ErrorIsNil) 386 } 387 current := version.Binary{ 388 Number: jujuversion.Current, 389 Arch: arch.HostArch(), 390 Series: series.MustHostSeries(), 391 } 392 err = machine.SetAgentVersion(current) 393 c.Assert(err, jc.ErrorIsNil) 394 return machine, params.Password 395 } 396 397 // MakeCharm creates a charm with the values specified in params. 398 // Sensible default values are substituted for missing ones. 399 // Supported charms depend on the charm/testing package. 400 // Currently supported charms: 401 // all-hooks, category, dummy, format2, logging, monitoring, mysql, 402 // mysql-alternative, riak, terracotta, upgrade1, upgrade2, varnish, 403 // varnish-alternative, wordpress. 404 // If params is not specified, defaults are used. 405 func (factory *Factory) MakeCharm(c *gc.C, params *CharmParams) *state.Charm { 406 if params == nil { 407 params = &CharmParams{} 408 } 409 if params.Name == "" { 410 params.Name = "mysql" 411 } 412 if params.Series == "" { 413 params.Series = "quantal" 414 } 415 if params.Revision == "" { 416 params.Revision = fmt.Sprintf("%d", uniqueInteger()) 417 } 418 if params.URL == "" { 419 params.URL = fmt.Sprintf("cs:%s/%s-%s", params.Series, params.Name, params.Revision) 420 } 421 422 ch := testcharms.RepoForSeries(params.Series).CharmDir(params.Name) 423 424 curl := charm.MustParseURL(params.URL) 425 bundleSHA256 := uniqueString("bundlesha") 426 info := state.CharmInfo{ 427 Charm: ch, 428 ID: curl, 429 StoragePath: "fake-storage-path", 430 SHA256: bundleSHA256, 431 } 432 charm, err := factory.st.AddCharm(info) 433 c.Assert(err, jc.ErrorIsNil) 434 return charm 435 } 436 437 // MakeApplication creates an application with the specified parameters, substituting 438 // sane defaults for missing values. 439 // If params is not specified, defaults are used. 440 func (factory *Factory) MakeApplication(c *gc.C, params *ApplicationParams) *state.Application { 441 app, _ := factory.MakeApplicationReturningPassword(c, params) 442 return app 443 } 444 445 // MakeApplication creates an application with the specified parameters, substituting 446 // sane defaults for missing values. 447 // If params is not specified, defaults are used. 448 // It returns the application and its password. 449 func (factory *Factory) MakeApplicationReturningPassword(c *gc.C, params *ApplicationParams) (*state.Application, string) { 450 if params == nil { 451 params = &ApplicationParams{} 452 } 453 if params.Charm == nil { 454 params.Charm = factory.MakeCharm(c, nil) 455 } 456 if params.Name == "" { 457 params.Name = params.Charm.Meta().Name 458 } 459 if params.Password == "" { 460 var err error 461 params.Password, err = utils.RandomPassword() 462 c.Assert(err, jc.ErrorIsNil) 463 } 464 465 rSt, err := factory.st.Resources() 466 c.Assert(err, jc.ErrorIsNil) 467 468 resourceMap := make(map[string]string) 469 for name, res := range params.Charm.Meta().Resources { 470 pendingID, err := rSt.AddPendingResource(params.Name, "", charmresource.Resource{ 471 Meta: res, 472 Origin: charmresource.OriginUpload, 473 }) 474 c.Assert(err, jc.ErrorIsNil) 475 resourceMap[name] = pendingID 476 } 477 478 appConfig, err := application.NewConfig(params.ApplicationConfig, params.ApplicationConfigFields, nil) 479 c.Assert(err, jc.ErrorIsNil) 480 application, err := factory.st.AddApplication(state.AddApplicationArgs{ 481 Name: params.Name, 482 Charm: params.Charm, 483 Series: params.Charm.URL().Series, 484 CharmConfig: charm.Settings(params.CharmConfig), 485 ApplicationConfig: appConfig, 486 Storage: params.Storage, 487 Constraints: params.Constraints, 488 Resources: resourceMap, 489 EndpointBindings: params.EndpointBindings, 490 Placement: params.Placement, 491 }) 492 c.Assert(err, jc.ErrorIsNil) 493 err = application.SetPassword(params.Password) 494 c.Assert(err, jc.ErrorIsNil) 495 err = application.Scale(params.DesiredScale) 496 c.Assert(err, jc.ErrorIsNil) 497 498 if params.Status != nil { 499 now := time.Now() 500 s := status.StatusInfo{ 501 Status: params.Status.Status, 502 Message: params.Status.Message, 503 Data: params.Status.Data, 504 Since: &now, 505 } 506 err = application.SetStatus(s) 507 c.Assert(err, jc.ErrorIsNil) 508 } 509 510 model, err := factory.st.Model() 511 c.Assert(err, jc.ErrorIsNil) 512 513 if model.Type() == state.ModelTypeCAAS { 514 agentTools := version.Binary{ 515 Number: jujuversion.Current, 516 Arch: arch.HostArch(), 517 Series: application.Series(), 518 } 519 err = application.SetAgentVersion(agentTools) 520 c.Assert(err, jc.ErrorIsNil) 521 } 522 523 return application, params.Password 524 } 525 526 // MakeUnit creates an application unit with specified params, filling in 527 // sane defaults for missing values. If params is not specified, defaults 528 // are used. 529 // 530 // If the unit is being added to an IAAS model, then it will be assigned 531 // to a machine. 532 func (factory *Factory) MakeUnit(c *gc.C, params *UnitParams) *state.Unit { 533 unit, _ := factory.MakeUnitReturningPassword(c, params) 534 return unit 535 } 536 537 // MakeUnit creates an application unit with specified params, filling in sane 538 // defaults for missing values. If params is not specified, defaults are used. 539 // The unit and its password are returned. 540 // 541 // If the unit is being added to an IAAS model, then it will be assigned to a 542 // machine. 543 func (factory *Factory) MakeUnitReturningPassword(c *gc.C, params *UnitParams) (*state.Unit, string) { 544 if params == nil { 545 params = &UnitParams{} 546 } 547 model, err := factory.st.Model() 548 c.Assert(err, jc.ErrorIsNil) 549 switch model.Type() { 550 case state.ModelTypeIAAS: 551 if params.Machine == nil { 552 var mParams *MachineParams 553 if params.Application != nil { 554 mParams = &MachineParams{ 555 Series: params.Application.Series(), 556 } 557 } 558 params.Machine = factory.MakeMachine(c, mParams) 559 } 560 default: 561 if params.Machine != nil { 562 c.Fatalf("machines not supported by model of type %q", model.Type()) 563 } 564 } 565 if params.Application == nil { 566 series := "quantal" 567 if model.Type() == state.ModelTypeCAAS { 568 series = "kubernetes" 569 } 570 ch := factory.MakeCharm(c, &CharmParams{Series: series}) 571 params.Application = factory.MakeApplication(c, &ApplicationParams{ 572 Constraints: params.Constraints, 573 Charm: ch, 574 }) 575 } 576 if params.Password == "" { 577 var err error 578 params.Password, err = utils.RandomPassword() 579 c.Assert(err, jc.ErrorIsNil) 580 } 581 unit, err := params.Application.AddUnit(state.AddUnitParams{}) 582 c.Assert(err, jc.ErrorIsNil) 583 584 if params.Machine != nil { 585 err = unit.AssignToMachine(params.Machine) 586 c.Assert(err, jc.ErrorIsNil) 587 } 588 589 if model.Type() == state.ModelTypeIAAS { 590 agentTools := version.Binary{ 591 Number: jujuversion.Current, 592 Arch: arch.HostArch(), 593 Series: params.Application.Series(), 594 } 595 err = unit.SetAgentVersion(agentTools) 596 c.Assert(err, jc.ErrorIsNil) 597 } 598 599 if params.SetCharmURL { 600 applicationCharmURL, _ := params.Application.CharmURL() 601 err = unit.SetCharmURL(applicationCharmURL) 602 c.Assert(err, jc.ErrorIsNil) 603 } 604 err = unit.SetPassword(params.Password) 605 c.Assert(err, jc.ErrorIsNil) 606 607 if params.Status != nil { 608 now := time.Now() 609 s := status.StatusInfo{ 610 Status: params.Status.Status, 611 Message: params.Status.Message, 612 Data: params.Status.Data, 613 Since: &now, 614 } 615 err = unit.SetStatus(s) 616 c.Assert(err, jc.ErrorIsNil) 617 } 618 619 return unit, params.Password 620 } 621 622 // MakeMetric makes a metric with specified params, filling in 623 // sane defaults for missing values. 624 // If params is not specified, defaults are used. 625 func (factory *Factory) MakeMetric(c *gc.C, params *MetricParams) *state.MetricBatch { 626 now := time.Now().Round(time.Second).UTC() 627 if params == nil { 628 params = &MetricParams{} 629 } 630 if params.Unit == nil { 631 meteredCharm := factory.MakeCharm(c, &CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 632 meteredApplication := factory.MakeApplication(c, &ApplicationParams{Charm: meteredCharm}) 633 params.Unit = factory.MakeUnit(c, &UnitParams{Application: meteredApplication, SetCharmURL: true}) 634 } 635 if params.Time == nil { 636 params.Time = &now 637 } 638 if params.Metrics == nil { 639 params.Metrics = []state.Metric{{ 640 Key: "pings", 641 Value: strconv.Itoa(uniqueInteger()), 642 Time: *params.Time, 643 Labels: map[string]string{"foo": "bar"}, 644 }} 645 } 646 647 chURL, ok := params.Unit.CharmURL() 648 c.Assert(ok, gc.Equals, true) 649 650 metric, err := factory.st.AddMetrics( 651 state.BatchParam{ 652 UUID: utils.MustNewUUID().String(), 653 Created: *params.Time, 654 CharmURL: chURL.String(), 655 Metrics: params.Metrics, 656 Unit: params.Unit.UnitTag(), 657 }) 658 c.Assert(err, jc.ErrorIsNil) 659 if params.Sent { 660 t := now 661 if params.DeleteTime != nil { 662 t = *params.DeleteTime 663 } 664 err := metric.SetSent(t) 665 c.Assert(err, jc.ErrorIsNil) 666 } 667 return metric 668 } 669 670 // MakeRelation create a relation with specified params, filling in sane 671 // defaults for missing values. 672 // If params is not specified, defaults are used. 673 func (factory *Factory) MakeRelation(c *gc.C, params *RelationParams) *state.Relation { 674 if params == nil { 675 params = &RelationParams{} 676 } 677 if len(params.Endpoints) == 0 { 678 s1 := factory.MakeApplication(c, &ApplicationParams{ 679 Charm: factory.MakeCharm(c, &CharmParams{ 680 Name: "mysql", 681 }), 682 }) 683 e1, err := s1.Endpoint("server") 684 c.Assert(err, jc.ErrorIsNil) 685 686 s2 := factory.MakeApplication(c, &ApplicationParams{ 687 Charm: factory.MakeCharm(c, &CharmParams{ 688 Name: "wordpress", 689 }), 690 }) 691 e2, err := s2.Endpoint("db") 692 c.Assert(err, jc.ErrorIsNil) 693 694 params.Endpoints = []state.Endpoint{e1, e2} 695 } 696 697 relation, err := factory.st.AddRelation(params.Endpoints...) 698 c.Assert(err, jc.ErrorIsNil) 699 700 return relation 701 } 702 703 // MakeModel creates an model with specified params, 704 // filling in sane defaults for missing values. If params is nil, 705 // defaults are used for all values. 706 // 707 // By default the new model shares the same owner as the calling Factory's 708 // model. TODO(ericclaudejones) MakeModel should return the model itself rather 709 // than the state. 710 func (factory *Factory) MakeModel(c *gc.C, params *ModelParams) *state.State { 711 if params == nil { 712 params = new(ModelParams) 713 } 714 if params.Type == state.ModelType("") { 715 params.Type = state.ModelTypeIAAS 716 } 717 if params.Name == "" { 718 params.Name = uniqueString("testmodel") 719 } 720 if params.CloudName == "" { 721 params.CloudName = "dummy" 722 } 723 if params.CloudRegion == "" { 724 params.CloudRegion = "dummy-region" 725 } 726 if params.CloudRegion == "<none>" { 727 params.CloudRegion = "" 728 } 729 if params.Owner == nil { 730 origEnv, err := factory.st.Model() 731 c.Assert(err, jc.ErrorIsNil) 732 params.Owner = origEnv.Owner() 733 } 734 if params.StorageProviderRegistry == nil { 735 params.StorageProviderRegistry = provider.CommonStorageProviders() 736 } 737 738 // For IAAS models, it only makes sense to make a model with the same provider 739 // as the initial model, or things will break elsewhere. 740 // For CAAS models, the type is "kubernetes". 741 currentCfg := factory.currentCfg(c) 742 cfgType := currentCfg.Type() 743 if params.Type == state.ModelTypeCAAS { 744 cfgType = "kubernetes" 745 } 746 747 uuid, err := utils.NewUUID() 748 c.Assert(err, jc.ErrorIsNil) 749 cfg := testing.CustomModelConfig(c, testing.Attrs{ 750 "name": params.Name, 751 "uuid": uuid.String(), 752 "type": cfgType, 753 }.Merge(params.ConfigAttrs)) 754 controller := state.NewController(factory.pool) 755 _, st, err := controller.NewModel(state.ModelArgs{ 756 Type: params.Type, 757 CloudName: params.CloudName, 758 CloudRegion: params.CloudRegion, 759 CloudCredential: params.CloudCredential, 760 Config: cfg, 761 Owner: params.Owner.(names.UserTag), 762 StorageProviderRegistry: params.StorageProviderRegistry, 763 EnvironVersion: params.EnvironVersion, 764 }) 765 c.Assert(err, jc.ErrorIsNil) 766 return st 767 } 768 769 // MakeCAASModel creates a CAAS model with specified params, 770 // filling in sane defaults for missing values. If params is nil, 771 // defaults are used for all values. 772 func (factory *Factory) MakeCAASModel(c *gc.C, params *ModelParams) *state.State { 773 if params == nil { 774 params = &ModelParams{} 775 } 776 params.Type = state.ModelTypeCAAS 777 params.CloudRegion = "<none>" 778 if params.Owner == nil { 779 origEnv, err := factory.st.Model() 780 c.Assert(err, jc.ErrorIsNil) 781 params.Owner = origEnv.Owner() 782 } 783 if params.CloudName == "" { 784 err := factory.st.AddCloud(cloud.Cloud{ 785 Name: "caascloud", 786 Type: "kubernetes", 787 AuthTypes: []cloud.AuthType{cloud.UserPassAuthType}, 788 }, params.Owner.Id()) 789 c.Assert(err, jc.ErrorIsNil) 790 params.CloudName = "caascloud" 791 } 792 if params.CloudCredential.IsZero() { 793 if params.Owner == nil { 794 origEnv, err := factory.st.Model() 795 c.Assert(err, jc.ErrorIsNil) 796 params.Owner = origEnv.Owner() 797 } 798 cred := cloud.NewCredential(cloud.UserPassAuthType, nil) 799 tag := names.NewCloudCredentialTag( 800 fmt.Sprintf("%s/%s/dummy-credential", params.CloudName, params.Owner.Id())) 801 err := factory.st.UpdateCloudCredential(tag, cred) 802 c.Assert(err, jc.ErrorIsNil) 803 params.CloudCredential = tag 804 } 805 return factory.MakeModel(c, params) 806 } 807 808 // MakeSpace will create a new space with the specified params. If the space 809 // name is not set, a unique space name is created. 810 func (factory *Factory) MakeSpace(c *gc.C, params *SpaceParams) *state.Space { 811 if params == nil { 812 params = new(SpaceParams) 813 } 814 if params.Name == "" { 815 params.Name = uniqueString("space-") 816 } 817 space, err := factory.st.AddSpace(params.Name, params.ProviderID, params.Subnets, params.IsPublic) 818 c.Assert(err, jc.ErrorIsNil) 819 return space 820 } 821 822 func (factory *Factory) currentCfg(c *gc.C) *config.Config { 823 model, err := factory.st.Model() 824 c.Assert(err, jc.ErrorIsNil) 825 826 currentCfg, err := model.ModelConfig() 827 c.Assert(err, jc.ErrorIsNil) 828 829 return currentCfg 830 }