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