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  }