github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/names"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/charm.v4"
    18  
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/instance"
    21  	"github.com/juju/juju/network"
    22  	"github.com/juju/juju/state"
    23  	"github.com/juju/juju/testcharms"
    24  	"github.com/juju/juju/testing"
    25  )
    26  
    27  const (
    28  	symbols = "abcdefghijklmopqrstuvwxyz"
    29  )
    30  
    31  type Factory struct {
    32  	st *state.State
    33  }
    34  
    35  var index uint32
    36  
    37  func NewFactory(st *state.State) *Factory {
    38  	return &Factory{st: st}
    39  }
    40  
    41  type UserParams struct {
    42  	Name        string
    43  	DisplayName string
    44  	Password    string
    45  	Creator     names.Tag
    46  	NoEnvUser   bool
    47  	Disabled    bool
    48  }
    49  
    50  // EnvUserParams defines the parameters for creating an environment user.
    51  type EnvUserParams struct {
    52  	User        string
    53  	DisplayName string
    54  	CreatedBy   names.Tag
    55  }
    56  
    57  // CharmParams defines the parameters for creating a charm.
    58  type CharmParams struct {
    59  	Name     string
    60  	Series   string
    61  	Revision string
    62  	URL      string
    63  }
    64  
    65  // Params for creating a machine.
    66  type MachineParams struct {
    67  	Series          string
    68  	Jobs            []state.MachineJob
    69  	Password        string
    70  	Nonce           string
    71  	InstanceId      instance.Id
    72  	Characteristics *instance.HardwareCharacteristics
    73  	Addresses       []network.Address
    74  }
    75  
    76  // ServiceParams is used when specifying parameters for a new service.
    77  type ServiceParams struct {
    78  	Name    string
    79  	Charm   *state.Charm
    80  	Creator names.Tag
    81  }
    82  
    83  // UnitParams are used to create units.
    84  type UnitParams struct {
    85  	Service     *state.Service
    86  	Machine     *state.Machine
    87  	SetCharmURL bool
    88  }
    89  
    90  // RelationParams are used to create relations.
    91  type RelationParams struct {
    92  	Endpoints []state.Endpoint
    93  }
    94  
    95  type MetricParams struct {
    96  	Unit    *state.Unit
    97  	Time    *time.Time
    98  	Metrics []state.Metric
    99  	Sent    bool
   100  }
   101  
   102  type EnvParams struct {
   103  	Name        string
   104  	Owner       names.Tag
   105  	ConfigAttrs testing.Attrs
   106  	Prepare     bool
   107  }
   108  
   109  // RandomSuffix adds a random 5 character suffix to the presented string.
   110  func (*Factory) RandomSuffix(prefix string) string {
   111  	result := prefix
   112  	for i := 0; i < 5; i++ {
   113  		result += string(symbols[rand.Intn(len(symbols))])
   114  	}
   115  	return result
   116  }
   117  
   118  func uniqueInteger() int {
   119  	index = atomic.AddUint32(&index, 1)
   120  	return int(index)
   121  }
   122  
   123  func uniqueString(prefix string) string {
   124  	if prefix == "" {
   125  		prefix = "no-prefix"
   126  	}
   127  	return fmt.Sprintf("%s-%d", prefix, uniqueInteger())
   128  }
   129  
   130  // MakeUser will create a user with values defined by the params.
   131  // For attributes of UserParams that are the default empty values,
   132  // some meaningful valid values are used instead.
   133  // If params is not specified, defaults are used.
   134  func (factory *Factory) MakeUser(c *gc.C, params *UserParams) *state.User {
   135  	if params == nil {
   136  		params = &UserParams{}
   137  	}
   138  	if params.Name == "" {
   139  		params.Name = uniqueString("username")
   140  	}
   141  	if params.DisplayName == "" {
   142  		params.DisplayName = uniqueString("display name")
   143  	}
   144  	if params.Password == "" {
   145  		params.Password = "password"
   146  	}
   147  	if params.Creator == nil {
   148  		env, err := factory.st.Environment()
   149  		c.Assert(err, jc.ErrorIsNil)
   150  		params.Creator = env.Owner()
   151  	}
   152  	creatorUserTag := params.Creator.(names.UserTag)
   153  	user, err := factory.st.AddUser(
   154  		params.Name, params.DisplayName, params.Password, creatorUserTag.Name())
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	if !params.NoEnvUser {
   157  		_, err := factory.st.AddEnvironmentUser(user.UserTag(), names.NewUserTag(user.CreatedBy()))
   158  		c.Assert(err, jc.ErrorIsNil)
   159  	}
   160  	if params.Disabled {
   161  		err := user.Disable()
   162  		c.Assert(err, jc.ErrorIsNil)
   163  	}
   164  	return user
   165  }
   166  
   167  // MakeEnvUser will create a envUser with values defined by the params. For
   168  // attributes of EnvUserParams that are the default empty values, some
   169  // meaningful valid values are used instead. If params is not specified,
   170  // defaults are used.
   171  func (factory *Factory) MakeEnvUser(c *gc.C, params *EnvUserParams) *state.EnvironmentUser {
   172  	if params == nil {
   173  		params = &EnvUserParams{}
   174  	}
   175  	if params.User == "" {
   176  		user := factory.MakeUser(c, &UserParams{NoEnvUser: true})
   177  		params.User = user.UserTag().Username()
   178  	}
   179  	if params.CreatedBy == nil {
   180  		user := factory.MakeUser(c, nil)
   181  		params.CreatedBy = user.UserTag()
   182  	}
   183  	createdByUserTag := params.CreatedBy.(names.UserTag)
   184  	envUser, err := factory.st.AddEnvironmentUser(names.NewUserTag(params.User), createdByUserTag)
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	return envUser
   187  }
   188  
   189  func (factory *Factory) paramsFillDefaults(c *gc.C, params *MachineParams) *MachineParams {
   190  	if params == nil {
   191  		params = &MachineParams{}
   192  	}
   193  	if params.Series == "" {
   194  		params.Series = "quantal"
   195  	}
   196  	if params.Nonce == "" {
   197  		params.Nonce = "nonce"
   198  	}
   199  	if len(params.Jobs) == 0 {
   200  		params.Jobs = []state.MachineJob{state.JobHostUnits}
   201  	}
   202  	if params.InstanceId == "" {
   203  		params.InstanceId = instance.Id(uniqueString("id"))
   204  	}
   205  	if params.Password == "" {
   206  		var err error
   207  		params.Password, err = utils.RandomPassword()
   208  		c.Assert(err, jc.ErrorIsNil)
   209  	}
   210  	return params
   211  }
   212  
   213  func machineParamsToTemplate(p *MachineParams) state.MachineTemplate {
   214  	return state.MachineTemplate{
   215  		Series:     p.Series,
   216  		Nonce:      p.Nonce,
   217  		Jobs:       p.Jobs,
   218  		InstanceId: p.InstanceId,
   219  	}
   220  }
   221  
   222  // MakeMachineNested will make a machine nested in the machine with ID given.
   223  func (factory *Factory) MakeMachineNested(c *gc.C, parentId string, params *MachineParams) *state.Machine {
   224  	params = factory.paramsFillDefaults(c, params)
   225  	mTmpl := machineParamsToTemplate(params)
   226  	// Cannot specify an instance id for a new container.
   227  	mTmpl.InstanceId = ""
   228  	// Cannot specify a nonce without an instance ID.
   229  	mTmpl.Nonce = ""
   230  
   231  	m, err := factory.st.AddMachineInsideMachine(
   232  		mTmpl,
   233  		parentId,
   234  		instance.LXC,
   235  	)
   236  	c.Assert(err, jc.ErrorIsNil)
   237  	return m
   238  }
   239  
   240  // MakeMachine will add a machine with values defined in params. For some
   241  // values in params, if they are missing, some meaningful empty values will be
   242  // set.
   243  // If params is not specified, defaults are used.
   244  func (factory *Factory) MakeMachine(c *gc.C, params *MachineParams) *state.Machine {
   245  	machine, _ := factory.MakeMachineReturningPassword(c, params)
   246  	return machine
   247  }
   248  
   249  // MakeMachineReturningPassword will add a machine with values defined in
   250  // params. For some values in params, if they are missing, some meaningful
   251  // empty values will be set. If params is not specified, defaults are used.
   252  // The machine and its password are returned.
   253  func (factory *Factory) MakeMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) {
   254  	params = factory.paramsFillDefaults(c, params)
   255  	machine, err := factory.st.AddMachine(params.Series, params.Jobs...)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	err = machine.SetProvisioned(params.InstanceId, params.Nonce, params.Characteristics)
   258  	c.Assert(err, jc.ErrorIsNil)
   259  	err = machine.SetPassword(params.Password)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  	if len(params.Addresses) > 0 {
   262  		err := machine.SetAddresses(params.Addresses...)
   263  		c.Assert(err, jc.ErrorIsNil)
   264  	}
   265  	return machine, params.Password
   266  }
   267  
   268  // MakeCharm creates a charm with the values specified in params.
   269  // Sensible default values are substituted for missing ones.
   270  // Supported charms depend on the charm/testing package.
   271  // Currently supported charms:
   272  //   all-hooks, category, dummy, format2, logging, monitoring, mysql,
   273  //   mysql-alternative, riak, terracotta, upgrade1, upgrade2, varnish,
   274  //   varnish-alternative, wordpress.
   275  // If params is not specified, defaults are used.
   276  func (factory *Factory) MakeCharm(c *gc.C, params *CharmParams) *state.Charm {
   277  	if params == nil {
   278  		params = &CharmParams{}
   279  	}
   280  	if params.Name == "" {
   281  		params.Name = "mysql"
   282  	}
   283  	if params.Series == "" {
   284  		params.Series = "quantal"
   285  	}
   286  	if params.Revision == "" {
   287  		params.Revision = fmt.Sprintf("%d", uniqueInteger())
   288  	}
   289  	if params.URL == "" {
   290  		params.URL = fmt.Sprintf("cs:%s/%s-%s", params.Series, params.Name, params.Revision)
   291  	}
   292  
   293  	ch := testcharms.Repo.CharmDir(params.Name)
   294  
   295  	curl := charm.MustParseURL(params.URL)
   296  	bundleSHA256 := uniqueString("bundlesha")
   297  	charm, err := factory.st.AddCharm(ch, curl, "fake-storage-path", bundleSHA256)
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	return charm
   300  }
   301  
   302  // MakeService creates a service with the specified parameters, substituting
   303  // sane defaults for missing values.
   304  // If params is not specified, defaults are used.
   305  func (factory *Factory) MakeService(c *gc.C, params *ServiceParams) *state.Service {
   306  	if params == nil {
   307  		params = &ServiceParams{}
   308  	}
   309  	if params.Charm == nil {
   310  		params.Charm = factory.MakeCharm(c, nil)
   311  	}
   312  	if params.Name == "" {
   313  		params.Name = params.Charm.Meta().Name
   314  	}
   315  	if params.Creator == nil {
   316  		creator := factory.MakeUser(c, nil)
   317  		params.Creator = creator.Tag()
   318  	}
   319  	_ = params.Creator.(names.UserTag)
   320  	service, err := factory.st.AddService(params.Name, params.Creator.String(), params.Charm, nil, nil)
   321  	c.Assert(err, jc.ErrorIsNil)
   322  	return service
   323  }
   324  
   325  // MakeUnit creates a service unit with specified params, filling in
   326  // sane defaults for missing values.
   327  // If params is not specified, defaults are used.
   328  func (factory *Factory) MakeUnit(c *gc.C, params *UnitParams) *state.Unit {
   329  	if params == nil {
   330  		params = &UnitParams{}
   331  	}
   332  	if params.Machine == nil {
   333  		params.Machine = factory.MakeMachine(c, nil)
   334  	}
   335  	if params.Service == nil {
   336  		params.Service = factory.MakeService(c, nil)
   337  	}
   338  	unit, err := params.Service.AddUnit()
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	err = unit.AssignToMachine(params.Machine)
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	if params.SetCharmURL {
   343  		serviceCharmURL, _ := params.Service.CharmURL()
   344  		err = unit.SetCharmURL(serviceCharmURL)
   345  		c.Assert(err, jc.ErrorIsNil)
   346  	}
   347  	return unit
   348  }
   349  
   350  // MakeMetric makes a metric with specified params, filling in
   351  // sane defaults for missing values.
   352  // If params is not specified, defaults are used.
   353  func (factory *Factory) MakeMetric(c *gc.C, params *MetricParams) *state.MetricBatch {
   354  	now := time.Now().Round(time.Second).UTC()
   355  	if params == nil {
   356  		params = &MetricParams{}
   357  	}
   358  	if params.Unit == nil {
   359  		meteredCharm := factory.MakeCharm(c, &CharmParams{Name: "metered", URL: "cs:quantal/metered"})
   360  		meteredService := factory.MakeService(c, &ServiceParams{Charm: meteredCharm})
   361  		params.Unit = factory.MakeUnit(c, &UnitParams{Service: meteredService, SetCharmURL: true})
   362  	}
   363  	if params.Time == nil {
   364  		params.Time = &now
   365  	}
   366  	if params.Metrics == nil {
   367  		params.Metrics = []state.Metric{{"pings", strconv.Itoa(uniqueInteger()), *params.Time}}
   368  	}
   369  
   370  	metric, err := params.Unit.AddMetrics(*params.Time, params.Metrics)
   371  	c.Assert(err, jc.ErrorIsNil)
   372  	if params.Sent {
   373  		err := metric.SetSent()
   374  		c.Assert(err, jc.ErrorIsNil)
   375  	}
   376  	return metric
   377  }
   378  
   379  // MakeRelation create a relation with specified params, filling in sane
   380  // defaults for missing values.
   381  // If params is not specified, defaults are used.
   382  func (factory *Factory) MakeRelation(c *gc.C, params *RelationParams) *state.Relation {
   383  	if params == nil {
   384  		params = &RelationParams{}
   385  	}
   386  	if len(params.Endpoints) == 0 {
   387  		s1 := factory.MakeService(c, &ServiceParams{
   388  			Charm: factory.MakeCharm(c, &CharmParams{
   389  				Name: "mysql",
   390  			}),
   391  		})
   392  		e1, err := s1.Endpoint("server")
   393  		c.Assert(err, jc.ErrorIsNil)
   394  
   395  		s2 := factory.MakeService(c, &ServiceParams{
   396  			Charm: factory.MakeCharm(c, &CharmParams{
   397  				Name: "wordpress",
   398  			}),
   399  		})
   400  		e2, err := s2.Endpoint("db")
   401  		c.Assert(err, jc.ErrorIsNil)
   402  
   403  		params.Endpoints = []state.Endpoint{e1, e2}
   404  	}
   405  
   406  	relation, err := factory.st.AddRelation(params.Endpoints...)
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	return relation
   410  }
   411  
   412  // MakeEnvironment creates an environment with specified params,
   413  // filling in sane defaults for missing values. If params is nil,
   414  // defaults are used for all values.
   415  //
   416  // By default the new enviroment shares the same owner as the calling
   417  // Factory's environment.
   418  func (factory *Factory) MakeEnvironment(c *gc.C, params *EnvParams) *state.State {
   419  	if params == nil {
   420  		params = new(EnvParams)
   421  	}
   422  	if params.Name == "" {
   423  		params.Name = uniqueString("testenv")
   424  	}
   425  	if params.Owner == nil {
   426  		origEnv, err := factory.st.Environment()
   427  		c.Assert(err, jc.ErrorIsNil)
   428  		params.Owner = origEnv.Owner()
   429  	}
   430  	// It only makes sense to make an environment with the same provider
   431  	// as the initial environment, or things will break elsewhere.
   432  	currentCfg, err := factory.st.EnvironConfig()
   433  	c.Assert(err, jc.ErrorIsNil)
   434  
   435  	uuid, err := utils.NewUUID()
   436  	c.Assert(err, jc.ErrorIsNil)
   437  	cfg := testing.CustomEnvironConfig(c, testing.Attrs{
   438  		"name":       params.Name,
   439  		"uuid":       uuid.String(),
   440  		"type":       currentCfg.Type(),
   441  		"state-port": currentCfg.StatePort(),
   442  		"api-port":   currentCfg.APIPort(),
   443  	}.Merge(params.ConfigAttrs))
   444  	_, st, err := factory.st.NewEnvironment(cfg, params.Owner.(names.UserTag))
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	if params.Prepare {
   447  		// Prepare the environment.
   448  		provider, err := environs.Provider(cfg.Type())
   449  		c.Assert(err, jc.ErrorIsNil)
   450  		cfg, err = provider.PrepareForCreateEnvironment(cfg)
   451  		c.Assert(err, jc.ErrorIsNil)
   452  		// Now save the config back.
   453  		err = st.UpdateEnvironConfig(cfg.AllAttrs(), nil, nil)
   454  		c.Assert(err, jc.ErrorIsNil)
   455  	}
   456  	return st
   457  }