github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/state/export_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"math/rand"
    10  	"path/filepath"
    11  	"time"
    12  
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/names"
    15  	jc "github.com/juju/testing/checkers"
    16  	jujutxn "github.com/juju/txn"
    17  	txntesting "github.com/juju/txn/testing"
    18  	gc "gopkg.in/check.v1"
    19  	"gopkg.in/juju/charm.v5"
    20  	"gopkg.in/mgo.v2"
    21  	"gopkg.in/mgo.v2/bson"
    22  	"gopkg.in/mgo.v2/txn"
    23  
    24  	"github.com/juju/juju/mongo"
    25  	"github.com/juju/juju/network"
    26  	"github.com/juju/juju/testcharms"
    27  )
    28  
    29  const (
    30  	InstanceDataC      = instanceDataC
    31  	MachinesC          = machinesC
    32  	NetworkInterfacesC = networkInterfacesC
    33  	ServicesC          = servicesC
    34  	SettingsC          = settingsC
    35  	UnitsC             = unitsC
    36  	UsersC             = usersC
    37  	BlockDevicesC      = blockDevicesC
    38  	StorageInstancesC  = storageInstancesC
    39  	StatusesHistoryC   = statusesHistoryC
    40  )
    41  
    42  var (
    43  	ToolstorageNewStorage  = &toolstorageNewStorage
    44  	ImageStorageNewStorage = &imageStorageNewStorage
    45  	MachineIdLessThan      = machineIdLessThan
    46  	StateServerAvailable   = &stateServerAvailable
    47  	GetOrCreatePorts       = getOrCreatePorts
    48  	GetPorts               = getPorts
    49  	PortsGlobalKey         = portsGlobalKey
    50  	CurrentUpgradeId       = currentUpgradeId
    51  	NowToTheSecond         = nowToTheSecond
    52  	PickAddress            = &pickAddress
    53  	AddVolumeOps           = (*State).addVolumeOps
    54  	CombineMeterStatus     = combineMeterStatus
    55  )
    56  
    57  type (
    58  	CharmDoc        charmDoc
    59  	MachineDoc      machineDoc
    60  	RelationDoc     relationDoc
    61  	ServiceDoc      serviceDoc
    62  	UnitDoc         unitDoc
    63  	BlockDevicesDoc blockDevicesDoc
    64  )
    65  
    66  func SetTestHooks(c *gc.C, st *State, hooks ...jujutxn.TestHook) txntesting.TransactionChecker {
    67  	return txntesting.SetTestHooks(c, newRunnerForHooks(st), hooks...)
    68  }
    69  
    70  func SetBeforeHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker {
    71  	return txntesting.SetBeforeHooks(c, newRunnerForHooks(st), fs...)
    72  }
    73  
    74  func SetAfterHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker {
    75  	return txntesting.SetAfterHooks(c, newRunnerForHooks(st), fs...)
    76  }
    77  
    78  func SetRetryHooks(c *gc.C, st *State, block, check func()) txntesting.TransactionChecker {
    79  	return txntesting.SetRetryHooks(c, newRunnerForHooks(st), block, check)
    80  }
    81  
    82  func newRunnerForHooks(st *State) jujutxn.Runner {
    83  	db := st.database.(*database)
    84  	runner := jujutxn.NewRunner(jujutxn.RunnerParams{Database: db.raw})
    85  	db.runner = runner
    86  	return runner
    87  }
    88  
    89  // SetPolicy updates the State's policy field to the
    90  // given Policy, and returns the old value.
    91  func SetPolicy(st *State, p Policy) Policy {
    92  	old := st.policy
    93  	st.policy = p
    94  	return old
    95  }
    96  
    97  func (doc *MachineDoc) String() string {
    98  	m := &Machine{doc: machineDoc(*doc)}
    99  	return m.String()
   100  }
   101  
   102  func ServiceSettingsRefCount(st *State, serviceName string, curl *charm.URL) (int, error) {
   103  	settingsRefsCollection, closer := st.getCollection(settingsrefsC)
   104  	defer closer()
   105  
   106  	key := serviceSettingsKey(serviceName, curl)
   107  	var doc settingsRefsDoc
   108  	if err := settingsRefsCollection.FindId(key).One(&doc); err == nil {
   109  		return doc.RefCount, nil
   110  	}
   111  	return 0, mgo.ErrNotFound
   112  }
   113  
   114  func AddTestingCharm(c *gc.C, st *State, name string) *Charm {
   115  	return addCharm(c, st, "quantal", testcharms.Repo.CharmDir(name))
   116  }
   117  
   118  func AddTestingService(c *gc.C, st *State, name string, ch *Charm, owner names.UserTag) *Service {
   119  	return addTestingService(c, st, name, ch, owner, nil, nil)
   120  }
   121  
   122  func AddTestingServiceWithNetworks(c *gc.C, st *State, name string, ch *Charm, owner names.UserTag, networks []string) *Service {
   123  	return addTestingService(c, st, name, ch, owner, networks, nil)
   124  }
   125  
   126  func AddTestingServiceWithStorage(c *gc.C, st *State, name string, ch *Charm, owner names.UserTag, storage map[string]StorageConstraints) *Service {
   127  	return addTestingService(c, st, name, ch, owner, nil, storage)
   128  }
   129  
   130  func addTestingService(c *gc.C, st *State, name string, ch *Charm, owner names.UserTag, networks []string, storage map[string]StorageConstraints) *Service {
   131  	c.Assert(ch, gc.NotNil)
   132  	service, err := st.AddService(name, owner.String(), ch, networks, storage)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	return service
   135  }
   136  
   137  func AddCustomCharm(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm {
   138  	path := testcharms.Repo.ClonedDirPath(c.MkDir(), name)
   139  	if filename != "" {
   140  		config := filepath.Join(path, filename)
   141  		err := ioutil.WriteFile(config, []byte(content), 0644)
   142  		c.Assert(err, jc.ErrorIsNil)
   143  	}
   144  	ch, err := charm.ReadCharmDir(path)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	if revision != -1 {
   147  		ch.SetRevision(revision)
   148  	}
   149  	return addCharm(c, st, series, ch)
   150  }
   151  
   152  func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm {
   153  	ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision())
   154  	curl := charm.MustParseURL("local:" + series + "/" + ident)
   155  	sch, err := st.AddCharm(ch, curl, "dummy-path", ident+"-sha256")
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	return sch
   158  }
   159  
   160  // SetCharmBundleURL sets the deprecated bundleurl field in the
   161  // charm document for the charm with the specified URL.
   162  func SetCharmBundleURL(c *gc.C, st *State, curl *charm.URL, bundleURL string) {
   163  	ops := []txn.Op{{
   164  		C:      charmsC,
   165  		Id:     st.docID(curl.String()),
   166  		Assert: txn.DocExists,
   167  		Update: bson.D{{"$set", bson.D{{"bundleurl", bundleURL}}}},
   168  	}}
   169  	err := st.runTransaction(ops)
   170  	c.Assert(err, jc.ErrorIsNil)
   171  }
   172  
   173  // SCHEMACHANGE
   174  // This method is used to reset the ownertag attribute
   175  func SetServiceOwnerTag(s *Service, ownerTag string) {
   176  	s.doc.OwnerTag = ownerTag
   177  }
   178  
   179  // SCHEMACHANGE
   180  // Get the owner directly
   181  func GetServiceOwnerTag(s *Service) string {
   182  	return s.doc.OwnerTag
   183  }
   184  
   185  func SetPasswordHash(e Authenticator, passwordHash string) error {
   186  	type hasSetPasswordHash interface {
   187  		setPasswordHash(string) error
   188  	}
   189  	return e.(hasSetPasswordHash).setPasswordHash(passwordHash)
   190  }
   191  
   192  // Return the underlying PasswordHash stored in the database. Used by the test
   193  // suite to check that the PasswordHash gets properly updated to new values
   194  // when compatibility mode is detected.
   195  func GetPasswordHash(e Authenticator) string {
   196  	type hasGetPasswordHash interface {
   197  		getPasswordHash() string
   198  	}
   199  	return e.(hasGetPasswordHash).getPasswordHash()
   200  }
   201  
   202  func init() {
   203  	txnLogSize = txnLogSizeTests
   204  }
   205  
   206  // TxnRevno returns the txn-revno field of the document
   207  // associated with the given Id in the given collection.
   208  func TxnRevno(st *State, collName string, id interface{}) (int64, error) {
   209  	var doc struct {
   210  		TxnRevno int64 `bson:"txn-revno"`
   211  	}
   212  	coll, closer := st.getCollection(collName)
   213  	defer closer()
   214  	err := coll.FindId(id).One(&doc)
   215  	if err != nil {
   216  		return 0, err
   217  	}
   218  	return doc.TxnRevno, nil
   219  }
   220  
   221  // MinUnitsRevno returns the Revno of the minUnits document
   222  // associated with the given service name.
   223  func MinUnitsRevno(st *State, serviceName string) (int, error) {
   224  	minUnitsCollection, closer := st.getCollection(minUnitsC)
   225  	defer closer()
   226  	var doc minUnitsDoc
   227  	if err := minUnitsCollection.FindId(serviceName).One(&doc); err != nil {
   228  		return 0, err
   229  	}
   230  	return doc.Revno, nil
   231  }
   232  
   233  func ConvertTagToCollectionNameAndId(st *State, tag names.Tag) (string, interface{}, error) {
   234  	return st.tagToCollectionAndId(tag)
   235  }
   236  
   237  func RunTransaction(st *State, ops []txn.Op) error {
   238  	return st.runTransaction(ops)
   239  }
   240  
   241  // Return the PasswordSalt that goes along with the PasswordHash
   242  func GetUserPasswordSaltAndHash(u *User) (string, string) {
   243  	return u.doc.PasswordSalt, u.doc.PasswordHash
   244  }
   245  
   246  func CheckUserExists(st *State, name string) (bool, error) {
   247  	return st.checkUserExists(name)
   248  }
   249  
   250  func WatcherMergeIds(st *State, changeset *[]string, updates map[interface{}]bool) error {
   251  	return mergeIds(st, changeset, updates)
   252  }
   253  
   254  func WatcherEnsureSuffixFn(marker string) func(string) string {
   255  	return ensureSuffixFn(marker)
   256  }
   257  
   258  func WatcherMakeIdFilter(st *State, marker string, receivers ...ActionReceiver) func(interface{}) bool {
   259  	return makeIdFilter(st, marker, receivers...)
   260  }
   261  
   262  func NewActionStatusWatcher(st *State, receivers []ActionReceiver, statuses ...ActionStatus) StringsWatcher {
   263  	return newActionStatusWatcher(st, receivers, statuses...)
   264  }
   265  
   266  func GetAllUpgradeInfos(st *State) ([]*UpgradeInfo, error) {
   267  	upgradeInfos, closer := st.getCollection(upgradeInfoC)
   268  	defer closer()
   269  
   270  	var out []*UpgradeInfo
   271  	var doc upgradeInfoDoc
   272  	iter := upgradeInfos.Find(nil).Iter()
   273  	defer iter.Close()
   274  	for iter.Next(&doc) {
   275  		out = append(out, &UpgradeInfo{st: st, doc: doc})
   276  	}
   277  	if err := iter.Err(); err != nil {
   278  		return nil, err
   279  	}
   280  	return out, nil
   281  }
   282  
   283  func UserEnvNameIndex(username, envName string) string {
   284  	return userEnvNameIndex(username, envName)
   285  }
   286  
   287  func DocID(st *State, id string) string {
   288  	return st.docID(id)
   289  }
   290  
   291  func LocalID(st *State, id string) string {
   292  	return st.localID(id)
   293  }
   294  
   295  func StrictLocalID(st *State, id string) (string, error) {
   296  	return st.strictLocalID(id)
   297  }
   298  
   299  func GetUnitEnvUUID(unit *Unit) string {
   300  	return unit.doc.EnvUUID
   301  }
   302  
   303  func GetCollection(st *State, name string) (mongo.Collection, func()) {
   304  	return st.getCollection(name)
   305  }
   306  
   307  func GetRawCollection(st *State, name string) (*mgo.Collection, func()) {
   308  	return st.getRawCollection(name)
   309  }
   310  
   311  func HasRawAccess(collectionName string) bool {
   312  	return allCollections()[collectionName].rawAccess
   313  }
   314  
   315  func MultiEnvCollections() []string {
   316  	var result []string
   317  	for name, info := range allCollections() {
   318  		if !info.global {
   319  			result = append(result, name)
   320  		}
   321  	}
   322  	return result
   323  }
   324  
   325  func Sequence(st *State, name string) (int, error) {
   326  	return st.sequence(name)
   327  }
   328  
   329  // This is a naive environment destruction function, used to test environment
   330  // watching after the client calls DestroyEnvironment and the environ doc is removed.
   331  // It is also used to test annotations.
   332  func RemoveEnvironment(st *State, uuid string) error {
   333  	ops := []txn.Op{{
   334  		C:      environmentsC,
   335  		Id:     uuid,
   336  		Assert: txn.DocExists,
   337  		Remove: true,
   338  	}}
   339  	return st.runTransaction(ops)
   340  }
   341  
   342  func SetEnvLifeDying(st *State, envUUID string) error {
   343  	ops := []txn.Op{{
   344  		C:      environmentsC,
   345  		Id:     envUUID,
   346  		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
   347  		Assert: isEnvAliveDoc,
   348  	}}
   349  	return st.runTransaction(ops)
   350  }
   351  
   352  func HostedEnvironCount(c *gc.C, st *State) int {
   353  	count, err := hostedEnvironCount(st)
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	return count
   356  }
   357  
   358  type MockGlobalEntity struct {
   359  }
   360  
   361  func (m MockGlobalEntity) globalKey() string {
   362  	return "globalKey"
   363  }
   364  func (m MockGlobalEntity) Tag() names.Tag {
   365  	return names.NewMachineTag("42")
   366  }
   367  
   368  var (
   369  	_                    GlobalEntity = (*MockGlobalEntity)(nil)
   370  	TagToCollectionAndId              = (*State).tagToCollectionAndId
   371  )
   372  
   373  func AssertAddressConversion(c *gc.C, netAddr network.Address) {
   374  	addr := fromNetworkAddress(netAddr)
   375  	newNetAddr := addr.networkAddress()
   376  	c.Assert(netAddr, gc.DeepEquals, newNetAddr)
   377  
   378  	size := 5
   379  	netAddrs := make([]network.Address, size)
   380  	for i := 0; i < size; i++ {
   381  		netAddrs[i] = netAddr
   382  	}
   383  	addrs := fromNetworkAddresses(netAddrs)
   384  	newNetAddrs := networkAddresses(addrs)
   385  	c.Assert(netAddrs, gc.DeepEquals, newNetAddrs)
   386  }
   387  
   388  func AssertHostPortConversion(c *gc.C, netHostPort network.HostPort) {
   389  	hostPort := fromNetworkHostPort(netHostPort)
   390  	newNetHostPort := hostPort.networkHostPort()
   391  	c.Assert(netHostPort, gc.DeepEquals, newNetHostPort)
   392  
   393  	size := 5
   394  	netHostsPorts := make([][]network.HostPort, size)
   395  	for i := 0; i < size; i++ {
   396  		netHostsPorts[i] = make([]network.HostPort, size)
   397  		for j := 0; j < size; j++ {
   398  			netHostsPorts[i][j] = netHostPort
   399  		}
   400  	}
   401  	hostsPorts := fromNetworkHostsPorts(netHostsPorts)
   402  	newNetHostsPorts := networkHostsPorts(hostsPorts)
   403  	c.Assert(netHostsPorts, gc.DeepEquals, newNetHostsPorts)
   404  }
   405  
   406  // WriteLogWithOplog writes out a log record to the a (probably fake)
   407  // oplog collection and the logs collection.
   408  func WriteLogWithOplog(
   409  	oplog *mgo.Collection,
   410  	envUUID string,
   411  	entity names.Tag,
   412  	t time.Time,
   413  	module string,
   414  	location string,
   415  	level loggo.Level,
   416  	msg string,
   417  ) error {
   418  	doc := &logDoc{
   419  		Id:       bson.NewObjectId(),
   420  		Time:     t,
   421  		EnvUUID:  envUUID,
   422  		Entity:   entity.String(),
   423  		Module:   module,
   424  		Location: location,
   425  		Level:    level,
   426  		Message:  msg,
   427  	}
   428  	err := oplog.Insert(bson.D{
   429  		{"ts", bson.MongoTimestamp(time.Now().Unix() << 32)}, // an approximation which will do
   430  		{"h", rand.Int63()},                                  // again, a suitable fake
   431  		{"op", "i"},                                          // this will always be an insert
   432  		{"ns", "logs.logs"},
   433  		{"o", doc},
   434  	})
   435  	if err != nil {
   436  		return err
   437  	}
   438  
   439  	session := oplog.Database.Session
   440  	logs := session.DB("logs").C("logs")
   441  	return logs.Insert(doc)
   442  }
   443  
   444  func SpaceDoc(s *Space) spaceDoc {
   445  	return s.doc
   446  }