github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	"github.com/juju/utils/arch"
    16  	"github.com/juju/utils/series"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/charm.v6-unstable"
    19  	"gopkg.in/juju/names.v2"
    20  
    21  	"github.com/juju/juju/constraints"
    22  	"github.com/juju/juju/instance"
    23  	"github.com/juju/juju/network"
    24  	"github.com/juju/juju/permission"
    25  	"github.com/juju/juju/state"
    26  	"github.com/juju/juju/status"
    27  	"github.com/juju/juju/storage"
    28  	"github.com/juju/juju/storage/provider"
    29  	"github.com/juju/juju/testcharms"
    30  	"github.com/juju/juju/testing"
    31  	jujuversion "github.com/juju/juju/version"
    32  	"github.com/juju/version"
    33  )
    34  
    35  const (
    36  	symbols = "abcdefghijklmopqrstuvwxyz"
    37  )
    38  
    39  type Factory struct {
    40  	st *state.State
    41  }
    42  
    43  var index uint32
    44  
    45  func NewFactory(st *state.State) *Factory {
    46  	return &Factory{st: st}
    47  }
    48  
    49  // UserParams defines the parameters for creating a user with MakeUser.
    50  type UserParams struct {
    51  	Name        string
    52  	DisplayName string
    53  	Password    string
    54  	Creator     names.Tag
    55  	NoModelUser bool
    56  	Disabled    bool
    57  	Access      permission.Access
    58  }
    59  
    60  // ModelUserParams defines the parameters for creating an environment user.
    61  type ModelUserParams struct {
    62  	User        string
    63  	DisplayName string
    64  	CreatedBy   names.Tag
    65  	Access      permission.Access
    66  }
    67  
    68  // CharmParams defines the parameters for creating a charm.
    69  type CharmParams struct {
    70  	Name     string
    71  	Series   string
    72  	Revision string
    73  	URL      string
    74  }
    75  
    76  // Params for creating a machine.
    77  type MachineParams struct {
    78  	Series          string
    79  	Jobs            []state.MachineJob
    80  	Password        string
    81  	Nonce           string
    82  	Constraints     constraints.Value
    83  	InstanceId      instance.Id
    84  	Characteristics *instance.HardwareCharacteristics
    85  	Addresses       []network.Address
    86  	Volumes         []state.MachineVolumeParams
    87  	Filesystems     []state.MachineFilesystemParams
    88  }
    89  
    90  // ApplicationParams is used when specifying parameters for a new application.
    91  type ApplicationParams struct {
    92  	Name        string
    93  	Charm       *state.Charm
    94  	Status      *status.StatusInfo
    95  	Settings    map[string]interface{}
    96  	Storage     map[string]state.StorageConstraints
    97  	Constraints constraints.Value
    98  }
    99  
   100  // UnitParams are used to create units.
   101  type UnitParams struct {
   102  	Application *state.Application
   103  	Machine     *state.Machine
   104  	Password    string
   105  	SetCharmURL bool
   106  	Status      *status.StatusInfo
   107  	Constraints constraints.Value
   108  }
   109  
   110  // RelationParams are used to create relations.
   111  type RelationParams struct {
   112  	Endpoints []state.Endpoint
   113  }
   114  
   115  type MetricParams struct {
   116  	Unit       *state.Unit
   117  	Time       *time.Time
   118  	Metrics    []state.Metric
   119  	Sent       bool
   120  	DeleteTime *time.Time
   121  }
   122  
   123  type ModelParams struct {
   124  	Name                    string
   125  	Owner                   names.Tag
   126  	ConfigAttrs             testing.Attrs
   127  	CloudName               string
   128  	CloudRegion             string
   129  	CloudCredential         names.CloudCredentialTag
   130  	StorageProviderRegistry storage.ProviderRegistry
   131  }
   132  
   133  type SpaceParams struct {
   134  	Name       string
   135  	ProviderID network.Id
   136  	Subnets    []string
   137  	IsPublic   bool
   138  }
   139  
   140  // RandomSuffix adds a random 5 character suffix to the presented string.
   141  func (*Factory) RandomSuffix(prefix string) string {
   142  	result := prefix
   143  	for i := 0; i < 5; i++ {
   144  		result += string(symbols[rand.Intn(len(symbols))])
   145  	}
   146  	return result
   147  }
   148  
   149  func uniqueInteger() int {
   150  	return int(atomic.AddUint32(&index, 1))
   151  }
   152  
   153  func uniqueString(prefix string) string {
   154  	if prefix == "" {
   155  		prefix = "no-prefix"
   156  	}
   157  	return fmt.Sprintf("%s-%d", prefix, uniqueInteger())
   158  }
   159  
   160  // MakeUser will create a user with values defined by the params.
   161  // For attributes of UserParams that are the default empty values,
   162  // some meaningful valid values are used instead.
   163  // If params is not specified, defaults are used.
   164  // If params.NoModelUser is false, the user will also be created
   165  // in the current model.
   166  func (factory *Factory) MakeUser(c *gc.C, params *UserParams) *state.User {
   167  	if params == nil {
   168  		params = &UserParams{}
   169  	}
   170  	if params.Name == "" {
   171  		params.Name = uniqueString("username")
   172  	}
   173  	if params.DisplayName == "" {
   174  		params.DisplayName = uniqueString("display name")
   175  	}
   176  	if params.Password == "" {
   177  		params.Password = "password"
   178  	}
   179  	if params.Creator == nil {
   180  		env, err := factory.st.Model()
   181  		c.Assert(err, jc.ErrorIsNil)
   182  		params.Creator = env.Owner()
   183  	}
   184  	if params.Access == permission.NoAccess {
   185  		params.Access = permission.AdminAccess
   186  	}
   187  	creatorUserTag := params.Creator.(names.UserTag)
   188  	user, err := factory.st.AddUser(
   189  		params.Name, params.DisplayName, params.Password, creatorUserTag.Name())
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	if !params.NoModelUser {
   192  		_, err := factory.st.AddModelUser(factory.st.ModelUUID(), state.UserAccessSpec{
   193  			User:        user.UserTag(),
   194  			CreatedBy:   names.NewUserTag(user.CreatedBy()),
   195  			DisplayName: params.DisplayName,
   196  			Access:      params.Access,
   197  		})
   198  		c.Assert(err, jc.ErrorIsNil)
   199  	}
   200  	if params.Disabled {
   201  		err := user.Disable()
   202  		c.Assert(err, jc.ErrorIsNil)
   203  	}
   204  	return user
   205  }
   206  
   207  // MakeModelUser will create a modelUser with values defined by the params. For
   208  // attributes of ModelUserParams that are the default empty values, some
   209  // meaningful valid values are used instead. If params is not specified,
   210  // defaults are used.
   211  func (factory *Factory) MakeModelUser(c *gc.C, params *ModelUserParams) permission.UserAccess {
   212  	if params == nil {
   213  		params = &ModelUserParams{}
   214  	}
   215  	if params.User == "" {
   216  		user := factory.MakeUser(c, &UserParams{NoModelUser: true})
   217  		params.User = user.UserTag().Id()
   218  	}
   219  	if params.DisplayName == "" {
   220  		params.DisplayName = uniqueString("display name")
   221  	}
   222  	if params.Access == permission.NoAccess {
   223  		params.Access = permission.AdminAccess
   224  	}
   225  	if params.CreatedBy == nil {
   226  		env, err := factory.st.Model()
   227  		c.Assert(err, jc.ErrorIsNil)
   228  		params.CreatedBy = env.Owner()
   229  	}
   230  	createdByUserTag := params.CreatedBy.(names.UserTag)
   231  	modelUser, err := factory.st.AddModelUser(factory.st.ModelUUID(), state.UserAccessSpec{
   232  		User:        names.NewUserTag(params.User),
   233  		CreatedBy:   createdByUserTag,
   234  		DisplayName: params.DisplayName,
   235  		Access:      params.Access,
   236  	})
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	return modelUser
   239  }
   240  
   241  func (factory *Factory) paramsFillDefaults(c *gc.C, params *MachineParams) *MachineParams {
   242  	if params == nil {
   243  		params = &MachineParams{}
   244  	}
   245  	if params.Series == "" {
   246  		params.Series = "quantal"
   247  	}
   248  	if params.Nonce == "" {
   249  		params.Nonce = "nonce"
   250  	}
   251  	if len(params.Jobs) == 0 {
   252  		params.Jobs = []state.MachineJob{state.JobHostUnits}
   253  	}
   254  	if params.InstanceId == "" {
   255  		params.InstanceId = instance.Id(uniqueString("id"))
   256  	}
   257  	if params.Password == "" {
   258  		var err error
   259  		params.Password, err = utils.RandomPassword()
   260  		c.Assert(err, jc.ErrorIsNil)
   261  	}
   262  	if params.Characteristics == nil {
   263  		arch := "amd64"
   264  		mem := uint64(64 * 1024 * 1024 * 1024)
   265  		hardware := instance.HardwareCharacteristics{
   266  			Arch: &arch,
   267  			Mem:  &mem,
   268  		}
   269  		params.Characteristics = &hardware
   270  	}
   271  
   272  	return params
   273  }
   274  
   275  // MakeMachineNested will make a machine nested in the machine with ID given.
   276  func (factory *Factory) MakeMachineNested(c *gc.C, parentId string, params *MachineParams) *state.Machine {
   277  	params = factory.paramsFillDefaults(c, params)
   278  	machineTemplate := state.MachineTemplate{
   279  		Series:      params.Series,
   280  		Jobs:        params.Jobs,
   281  		Volumes:     params.Volumes,
   282  		Filesystems: params.Filesystems,
   283  		Constraints: params.Constraints,
   284  	}
   285  
   286  	m, err := factory.st.AddMachineInsideMachine(
   287  		machineTemplate,
   288  		parentId,
   289  		instance.LXD,
   290  	)
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	err = m.SetProvisioned(params.InstanceId, params.Nonce, params.Characteristics)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	current := version.Binary{
   295  		Number: jujuversion.Current,
   296  		Arch:   arch.HostArch(),
   297  		Series: series.HostSeries(),
   298  	}
   299  	err = m.SetAgentVersion(current)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	return m
   302  }
   303  
   304  // MakeMachine will add a machine with values defined in params. For some
   305  // values in params, if they are missing, some meaningful empty values will be
   306  // set.
   307  // If params is not specified, defaults are used.
   308  func (factory *Factory) MakeMachine(c *gc.C, params *MachineParams) *state.Machine {
   309  	machine, _ := factory.MakeMachineReturningPassword(c, params)
   310  	return machine
   311  }
   312  
   313  // MakeMachineReturningPassword will add a machine with values defined in
   314  // params. For some values in params, if they are missing, some meaningful
   315  // empty values will be set. If params is not specified, defaults are used.
   316  // The machine and its password are returned.
   317  func (factory *Factory) MakeMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) {
   318  	params = factory.paramsFillDefaults(c, params)
   319  	return factory.makeMachineReturningPassword(c, params, true)
   320  }
   321  
   322  // MakeUnprovisionedMachineReturningPassword will add a machine with values
   323  // defined in params. For some values in params, if they are missing, some
   324  // meaningful empty values will be set. If params is not specified, defaults
   325  // are used. The machine and its password are returned; the machine will not
   326  // be provisioned.
   327  func (factory *Factory) MakeUnprovisionedMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) {
   328  	if params != nil {
   329  		c.Assert(params.Nonce, gc.Equals, "")
   330  		c.Assert(params.InstanceId, gc.Equals, instance.Id(""))
   331  		c.Assert(params.Characteristics, gc.IsNil)
   332  	}
   333  	params = factory.paramsFillDefaults(c, params)
   334  	params.Nonce = ""
   335  	params.InstanceId = ""
   336  	params.Characteristics = nil
   337  	return factory.makeMachineReturningPassword(c, params, false)
   338  }
   339  
   340  func (factory *Factory) makeMachineReturningPassword(c *gc.C, params *MachineParams, setProvisioned bool) (*state.Machine, string) {
   341  	machineTemplate := state.MachineTemplate{
   342  		Series:      params.Series,
   343  		Jobs:        params.Jobs,
   344  		Volumes:     params.Volumes,
   345  		Filesystems: params.Filesystems,
   346  		Constraints: params.Constraints,
   347  	}
   348  	machine, err := factory.st.AddOneMachine(machineTemplate)
   349  	c.Assert(err, jc.ErrorIsNil)
   350  	if setProvisioned {
   351  		err = machine.SetProvisioned(params.InstanceId, params.Nonce, params.Characteristics)
   352  		c.Assert(err, jc.ErrorIsNil)
   353  	}
   354  	err = machine.SetPassword(params.Password)
   355  	c.Assert(err, jc.ErrorIsNil)
   356  	if len(params.Addresses) > 0 {
   357  		err := machine.SetProviderAddresses(params.Addresses...)
   358  		c.Assert(err, jc.ErrorIsNil)
   359  	}
   360  	current := version.Binary{
   361  		Number: jujuversion.Current,
   362  		Arch:   arch.HostArch(),
   363  		Series: series.HostSeries(),
   364  	}
   365  	err = machine.SetAgentVersion(current)
   366  	c.Assert(err, jc.ErrorIsNil)
   367  	return machine, params.Password
   368  }
   369  
   370  // MakeCharm creates a charm with the values specified in params.
   371  // Sensible default values are substituted for missing ones.
   372  // Supported charms depend on the charm/testing package.
   373  // Currently supported charms:
   374  //   all-hooks, category, dummy, format2, logging, monitoring, mysql,
   375  //   mysql-alternative, riak, terracotta, upgrade1, upgrade2, varnish,
   376  //   varnish-alternative, wordpress.
   377  // If params is not specified, defaults are used.
   378  func (factory *Factory) MakeCharm(c *gc.C, params *CharmParams) *state.Charm {
   379  	if params == nil {
   380  		params = &CharmParams{}
   381  	}
   382  	if params.Name == "" {
   383  		params.Name = "mysql"
   384  	}
   385  	if params.Series == "" {
   386  		params.Series = "quantal"
   387  	}
   388  	if params.Revision == "" {
   389  		params.Revision = fmt.Sprintf("%d", uniqueInteger())
   390  	}
   391  	if params.URL == "" {
   392  		params.URL = fmt.Sprintf("cs:%s/%s-%s", params.Series, params.Name, params.Revision)
   393  	}
   394  
   395  	ch := testcharms.Repo.CharmDir(params.Name)
   396  
   397  	curl := charm.MustParseURL(params.URL)
   398  	bundleSHA256 := uniqueString("bundlesha")
   399  	info := state.CharmInfo{
   400  		Charm:       ch,
   401  		ID:          curl,
   402  		StoragePath: "fake-storage-path",
   403  		SHA256:      bundleSHA256,
   404  	}
   405  	charm, err := factory.st.AddCharm(info)
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	return charm
   408  }
   409  
   410  // MakeApplication creates an application with the specified parameters, substituting
   411  // sane defaults for missing values.
   412  // If params is not specified, defaults are used.
   413  func (factory *Factory) MakeApplication(c *gc.C, params *ApplicationParams) *state.Application {
   414  	if params == nil {
   415  		params = &ApplicationParams{}
   416  	}
   417  	if params.Charm == nil {
   418  		params.Charm = factory.MakeCharm(c, nil)
   419  	}
   420  	if params.Name == "" {
   421  		params.Name = params.Charm.Meta().Name
   422  	}
   423  	application, err := factory.st.AddApplication(state.AddApplicationArgs{
   424  		Name:        params.Name,
   425  		Charm:       params.Charm,
   426  		Settings:    charm.Settings(params.Settings),
   427  		Storage:     params.Storage,
   428  		Constraints: params.Constraints,
   429  	})
   430  	c.Assert(err, jc.ErrorIsNil)
   431  
   432  	if params.Status != nil {
   433  		now := time.Now()
   434  		s := status.StatusInfo{
   435  			Status:  params.Status.Status,
   436  			Message: params.Status.Message,
   437  			Data:    params.Status.Data,
   438  			Since:   &now,
   439  		}
   440  		err = application.SetStatus(s)
   441  		c.Assert(err, jc.ErrorIsNil)
   442  	}
   443  
   444  	return application
   445  }
   446  
   447  // MakeUnit creates an application unit with specified params, filling in
   448  // sane defaults for missing values.
   449  // If params is not specified, defaults are used.
   450  func (factory *Factory) MakeUnit(c *gc.C, params *UnitParams) *state.Unit {
   451  	unit, _ := factory.MakeUnitReturningPassword(c, params)
   452  	return unit
   453  }
   454  
   455  // MakeUnit creates an application unit with specified params, filling in sane
   456  // defaults for missing values. If params is not specified, defaults are used.
   457  // The unit and its password are returned.
   458  func (factory *Factory) MakeUnitReturningPassword(c *gc.C, params *UnitParams) (*state.Unit, string) {
   459  	if params == nil {
   460  		params = &UnitParams{}
   461  	}
   462  	if params.Machine == nil {
   463  		params.Machine = factory.MakeMachine(c, nil)
   464  	}
   465  	if params.Application == nil {
   466  		params.Application = factory.MakeApplication(c, &ApplicationParams{
   467  			Constraints: params.Constraints,
   468  		})
   469  	}
   470  	if params.Password == "" {
   471  		var err error
   472  		params.Password, err = utils.RandomPassword()
   473  		c.Assert(err, jc.ErrorIsNil)
   474  	}
   475  	unit, err := params.Application.AddUnit()
   476  	c.Assert(err, jc.ErrorIsNil)
   477  	err = unit.AssignToMachine(params.Machine)
   478  	c.Assert(err, jc.ErrorIsNil)
   479  
   480  	agentTools := version.Binary{
   481  		Number: jujuversion.Current,
   482  		Arch:   arch.HostArch(),
   483  		Series: params.Application.Series(),
   484  	}
   485  	err = unit.SetAgentVersion(agentTools)
   486  	c.Assert(err, jc.ErrorIsNil)
   487  	if params.SetCharmURL {
   488  		applicationCharmURL, _ := params.Application.CharmURL()
   489  		err = unit.SetCharmURL(applicationCharmURL)
   490  		c.Assert(err, jc.ErrorIsNil)
   491  	}
   492  	err = unit.SetPassword(params.Password)
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	if params.Status != nil {
   496  		now := time.Now()
   497  		s := status.StatusInfo{
   498  			Status:  params.Status.Status,
   499  			Message: params.Status.Message,
   500  			Data:    params.Status.Data,
   501  			Since:   &now,
   502  		}
   503  		err = unit.SetStatus(s)
   504  		c.Assert(err, jc.ErrorIsNil)
   505  	}
   506  
   507  	return unit, params.Password
   508  }
   509  
   510  // MakeMetric makes a metric with specified params, filling in
   511  // sane defaults for missing values.
   512  // If params is not specified, defaults are used.
   513  func (factory *Factory) MakeMetric(c *gc.C, params *MetricParams) *state.MetricBatch {
   514  	now := time.Now().Round(time.Second).UTC()
   515  	if params == nil {
   516  		params = &MetricParams{}
   517  	}
   518  	if params.Unit == nil {
   519  		meteredCharm := factory.MakeCharm(c, &CharmParams{Name: "metered", URL: "cs:quantal/metered"})
   520  		meteredApplication := factory.MakeApplication(c, &ApplicationParams{Charm: meteredCharm})
   521  		params.Unit = factory.MakeUnit(c, &UnitParams{Application: meteredApplication, SetCharmURL: true})
   522  	}
   523  	if params.Time == nil {
   524  		params.Time = &now
   525  	}
   526  	if params.Metrics == nil {
   527  		params.Metrics = []state.Metric{{"pings", strconv.Itoa(uniqueInteger()), *params.Time}}
   528  	}
   529  
   530  	chURL, ok := params.Unit.CharmURL()
   531  	c.Assert(ok, gc.Equals, true)
   532  
   533  	metric, err := factory.st.AddMetrics(
   534  		state.BatchParam{
   535  			UUID:     utils.MustNewUUID().String(),
   536  			Created:  *params.Time,
   537  			CharmURL: chURL.String(),
   538  			Metrics:  params.Metrics,
   539  			Unit:     params.Unit.UnitTag(),
   540  		})
   541  	c.Assert(err, jc.ErrorIsNil)
   542  	if params.Sent {
   543  		t := now
   544  		if params.DeleteTime != nil {
   545  			t = *params.DeleteTime
   546  		}
   547  		err := metric.SetSent(t)
   548  		c.Assert(err, jc.ErrorIsNil)
   549  	}
   550  	return metric
   551  }
   552  
   553  // MakeRelation create a relation with specified params, filling in sane
   554  // defaults for missing values.
   555  // If params is not specified, defaults are used.
   556  func (factory *Factory) MakeRelation(c *gc.C, params *RelationParams) *state.Relation {
   557  	if params == nil {
   558  		params = &RelationParams{}
   559  	}
   560  	if len(params.Endpoints) == 0 {
   561  		s1 := factory.MakeApplication(c, &ApplicationParams{
   562  			Charm: factory.MakeCharm(c, &CharmParams{
   563  				Name: "mysql",
   564  			}),
   565  		})
   566  		e1, err := s1.Endpoint("server")
   567  		c.Assert(err, jc.ErrorIsNil)
   568  
   569  		s2 := factory.MakeApplication(c, &ApplicationParams{
   570  			Charm: factory.MakeCharm(c, &CharmParams{
   571  				Name: "wordpress",
   572  			}),
   573  		})
   574  		e2, err := s2.Endpoint("db")
   575  		c.Assert(err, jc.ErrorIsNil)
   576  
   577  		params.Endpoints = []state.Endpoint{e1, e2}
   578  	}
   579  
   580  	relation, err := factory.st.AddRelation(params.Endpoints...)
   581  	c.Assert(err, jc.ErrorIsNil)
   582  
   583  	return relation
   584  }
   585  
   586  // MakeModel creates an model with specified params,
   587  // filling in sane defaults for missing values. If params is nil,
   588  // defaults are used for all values.
   589  //
   590  // By default the new model shares the same owner as the calling
   591  // Factory's model.
   592  func (factory *Factory) MakeModel(c *gc.C, params *ModelParams) *state.State {
   593  	if params == nil {
   594  		params = new(ModelParams)
   595  	}
   596  	if params.Name == "" {
   597  		params.Name = uniqueString("testenv")
   598  	}
   599  	if params.CloudName == "" {
   600  		params.CloudName = "dummy"
   601  	}
   602  	if params.CloudRegion == "" {
   603  		params.CloudRegion = "dummy-region"
   604  	}
   605  	if params.Owner == nil {
   606  		origEnv, err := factory.st.Model()
   607  		c.Assert(err, jc.ErrorIsNil)
   608  		params.Owner = origEnv.Owner()
   609  	}
   610  	if params.StorageProviderRegistry == nil {
   611  		params.StorageProviderRegistry = provider.CommonStorageProviders()
   612  	}
   613  	// It only makes sense to make an model with the same provider
   614  	// as the initial model, or things will break elsewhere.
   615  	currentCfg, err := factory.st.ModelConfig()
   616  	c.Assert(err, jc.ErrorIsNil)
   617  
   618  	uuid, err := utils.NewUUID()
   619  	c.Assert(err, jc.ErrorIsNil)
   620  	cfg := testing.CustomModelConfig(c, testing.Attrs{
   621  		"name": params.Name,
   622  		"uuid": uuid.String(),
   623  		"type": currentCfg.Type(),
   624  	}.Merge(params.ConfigAttrs))
   625  	_, st, err := factory.st.NewModel(state.ModelArgs{
   626  		CloudName:       params.CloudName,
   627  		CloudRegion:     params.CloudRegion,
   628  		CloudCredential: params.CloudCredential,
   629  		Config:          cfg,
   630  		Owner:           params.Owner.(names.UserTag),
   631  		StorageProviderRegistry: params.StorageProviderRegistry,
   632  	})
   633  	c.Assert(err, jc.ErrorIsNil)
   634  	return st
   635  }
   636  
   637  // MakeSpace will create a new space with the specified params. If the space
   638  // name is not set, a unique space name is created.
   639  func (factory *Factory) MakeSpace(c *gc.C, params *SpaceParams) *state.Space {
   640  	if params == nil {
   641  		params = new(SpaceParams)
   642  	}
   643  	if params.Name == "" {
   644  		params.Name = uniqueString("space-")
   645  	}
   646  	space, err := factory.st.AddSpace(params.Name, params.ProviderID, params.Subnets, params.IsPublic)
   647  	c.Assert(err, jc.ErrorIsNil)
   648  	return space
   649  }