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