github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  	"path/filepath"
    10  	"time" // Only used for time types.
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/testing"
    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.v6-unstable"
    20  	"gopkg.in/juju/names.v2"
    21  	"gopkg.in/mgo.v2"
    22  	"gopkg.in/mgo.v2/bson"
    23  	"gopkg.in/mgo.v2/txn"
    24  
    25  	"github.com/juju/juju/core/lease"
    26  	"github.com/juju/juju/mongo"
    27  	"github.com/juju/juju/mongo/utils"
    28  	"github.com/juju/juju/network"
    29  	"github.com/juju/juju/permission"
    30  	"github.com/juju/juju/status"
    31  	"github.com/juju/juju/testcharms"
    32  	"github.com/juju/juju/version"
    33  )
    34  
    35  const (
    36  	MachinesC         = machinesC
    37  	ApplicationsC     = applicationsC
    38  	EndpointBindingsC = endpointBindingsC
    39  	ControllersC      = controllersC
    40  	UsersC            = usersC
    41  	BlockDevicesC     = blockDevicesC
    42  	StorageInstancesC = storageInstancesC
    43  	GUISettingsC      = guisettingsC
    44  	GlobalSettingsC   = globalSettingsC
    45  	SettingsC         = settingsC
    46  )
    47  
    48  var (
    49  	BinarystorageNew                     = &binarystorageNew
    50  	ImageStorageNewStorage               = &imageStorageNewStorage
    51  	MachineIdLessThan                    = machineIdLessThan
    52  	ControllerAvailable                  = &controllerAvailable
    53  	GetOrCreatePorts                     = getOrCreatePorts
    54  	GetPorts                             = getPorts
    55  	AddVolumeOps                         = (*State).addVolumeOps
    56  	CombineMeterStatus                   = combineMeterStatus
    57  	ApplicationGlobalKey                 = applicationGlobalKey
    58  	ReadSettings                         = readSettings
    59  	ControllerInheritedSettingsGlobalKey = controllerInheritedSettingsGlobalKey
    60  	ModelGlobalKey                       = modelGlobalKey
    61  	MergeBindings                        = mergeBindings
    62  	UpgradeInProgressError               = errUpgradeInProgress
    63  )
    64  
    65  type (
    66  	CharmDoc        charmDoc
    67  	MachineDoc      machineDoc
    68  	RelationDoc     relationDoc
    69  	ApplicationDoc  applicationDoc
    70  	UnitDoc         unitDoc
    71  	BlockDevicesDoc blockDevicesDoc
    72  )
    73  
    74  func SetTestHooks(c *gc.C, st *State, hooks ...jujutxn.TestHook) txntesting.TransactionChecker {
    75  	return txntesting.SetTestHooks(c, newRunnerForHooks(st), hooks...)
    76  }
    77  
    78  func SetBeforeHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker {
    79  	return txntesting.SetBeforeHooks(c, newRunnerForHooks(st), fs...)
    80  }
    81  
    82  func SetAfterHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker {
    83  	return txntesting.SetAfterHooks(c, newRunnerForHooks(st), fs...)
    84  }
    85  
    86  func SetRetryHooks(c *gc.C, st *State, block, check func()) txntesting.TransactionChecker {
    87  	return txntesting.SetRetryHooks(c, newRunnerForHooks(st), block, check)
    88  }
    89  
    90  func newRunnerForHooks(st *State) jujutxn.Runner {
    91  	db := st.database.(*database)
    92  	runner := jujutxn.NewRunner(jujutxn.RunnerParams{Database: db.raw})
    93  	db.runner = runner
    94  	return runner
    95  }
    96  
    97  // SetPolicy updates the State's policy field to the
    98  // given Policy, and returns the old value.
    99  func SetPolicy(st *State, p Policy) Policy {
   100  	old := st.policy
   101  	st.policy = p
   102  	return old
   103  }
   104  
   105  func (doc *MachineDoc) String() string {
   106  	m := &Machine{doc: machineDoc(*doc)}
   107  	return m.String()
   108  }
   109  
   110  func ServiceSettingsRefCount(st *State, appName string, curl *charm.URL) (int, error) {
   111  	refcounts, closer := st.getCollection(refcountsC)
   112  	defer closer()
   113  
   114  	key := applicationSettingsKey(appName, curl)
   115  	return nsRefcounts.read(refcounts, key)
   116  }
   117  
   118  func AddTestingCharm(c *gc.C, st *State, name string) *Charm {
   119  	return addCharm(c, st, "quantal", testcharms.Repo.CharmDir(name))
   120  }
   121  
   122  func AddTestingCharmForSeries(c *gc.C, st *State, series, name string) *Charm {
   123  	return addCharm(c, st, series, testcharms.Repo.CharmDir(name))
   124  }
   125  
   126  func AddTestingCharmMultiSeries(c *gc.C, st *State, name string) *Charm {
   127  	ch := testcharms.Repo.CharmDir(name)
   128  	ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision())
   129  	curl := charm.MustParseURL("cs:" + ident)
   130  	info := CharmInfo{
   131  		Charm:       ch,
   132  		ID:          curl,
   133  		StoragePath: "dummy-path",
   134  		SHA256:      ident + "-sha256",
   135  	}
   136  	sch, err := st.AddCharm(info)
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	return sch
   139  }
   140  
   141  func AddTestingService(c *gc.C, st *State, name string, ch *Charm) *Application {
   142  	return addTestingService(c, st, "", name, ch, nil, nil)
   143  }
   144  
   145  func AddTestingServiceForSeries(c *gc.C, st *State, series, name string, ch *Charm) *Application {
   146  	return addTestingService(c, st, series, name, ch, nil, nil)
   147  }
   148  
   149  func AddTestingServiceWithStorage(c *gc.C, st *State, name string, ch *Charm, storage map[string]StorageConstraints) *Application {
   150  	return addTestingService(c, st, "", name, ch, nil, storage)
   151  }
   152  
   153  func AddTestingServiceWithBindings(c *gc.C, st *State, name string, ch *Charm, bindings map[string]string) *Application {
   154  	return addTestingService(c, st, "", name, ch, bindings, nil)
   155  }
   156  
   157  func addTestingService(c *gc.C, st *State, series, name string, ch *Charm, bindings map[string]string, storage map[string]StorageConstraints) *Application {
   158  	c.Assert(ch, gc.NotNil)
   159  	service, err := st.AddApplication(AddApplicationArgs{
   160  		Name:             name,
   161  		Series:           series,
   162  		Charm:            ch,
   163  		EndpointBindings: bindings,
   164  		Storage:          storage,
   165  	})
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	return service
   168  }
   169  
   170  func AddCustomCharm(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm {
   171  	path := testcharms.Repo.ClonedDirPath(c.MkDir(), name)
   172  	if filename != "" {
   173  		config := filepath.Join(path, filename)
   174  		err := ioutil.WriteFile(config, []byte(content), 0644)
   175  		c.Assert(err, jc.ErrorIsNil)
   176  	}
   177  	ch, err := charm.ReadCharmDir(path)
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	if revision != -1 {
   180  		ch.SetRevision(revision)
   181  	}
   182  	return addCharm(c, st, series, ch)
   183  }
   184  
   185  func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm {
   186  	ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision())
   187  	url := "local:" + series + "/" + ident
   188  	if series == "" {
   189  		ident = fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision())
   190  		url = "local:" + ident
   191  	}
   192  	curl := charm.MustParseURL(url)
   193  	info := CharmInfo{
   194  		Charm:       ch,
   195  		ID:          curl,
   196  		StoragePath: "dummy-path",
   197  		SHA256:      ident + "-sha256",
   198  	}
   199  	sch, err := st.AddCharm(info)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	return sch
   202  }
   203  
   204  // SetCharmBundleURL sets the deprecated bundleurl field in the
   205  // charm document for the charm with the specified URL.
   206  func SetCharmBundleURL(c *gc.C, st *State, curl *charm.URL, bundleURL string) {
   207  	ops := []txn.Op{{
   208  		C:      charmsC,
   209  		Id:     st.docID(curl.String()),
   210  		Assert: txn.DocExists,
   211  		Update: bson.D{{"$set", bson.D{{"bundleurl", bundleURL}}}},
   212  	}}
   213  	err := st.runTransaction(ops)
   214  	c.Assert(err, jc.ErrorIsNil)
   215  }
   216  
   217  func SetPasswordHash(e Authenticator, passwordHash string) error {
   218  	type hasSetPasswordHash interface {
   219  		setPasswordHash(string) error
   220  	}
   221  	return e.(hasSetPasswordHash).setPasswordHash(passwordHash)
   222  }
   223  
   224  // Return the underlying PasswordHash stored in the database. Used by the test
   225  // suite to check that the PasswordHash gets properly updated to new values
   226  // when compatibility mode is detected.
   227  func GetPasswordHash(e Authenticator) string {
   228  	type hasGetPasswordHash interface {
   229  		getPasswordHash() string
   230  	}
   231  	return e.(hasGetPasswordHash).getPasswordHash()
   232  }
   233  
   234  func init() {
   235  	txnLogSize = txnLogSizeTests
   236  }
   237  
   238  // TxnRevno returns the txn-revno field of the document
   239  // associated with the given Id in the given collection.
   240  func TxnRevno(st *State, collName string, id interface{}) (int64, error) {
   241  	var doc struct {
   242  		TxnRevno int64 `bson:"txn-revno"`
   243  	}
   244  	coll, closer := st.getCollection(collName)
   245  	defer closer()
   246  	err := coll.FindId(id).One(&doc)
   247  	if err != nil {
   248  		return 0, err
   249  	}
   250  	return doc.TxnRevno, nil
   251  }
   252  
   253  // MinUnitsRevno returns the Revno of the minUnits document
   254  // associated with the given application name.
   255  func MinUnitsRevno(st *State, applicationname string) (int, error) {
   256  	minUnitsCollection, closer := st.getCollection(minUnitsC)
   257  	defer closer()
   258  	var doc minUnitsDoc
   259  	if err := minUnitsCollection.FindId(applicationname).One(&doc); err != nil {
   260  		return 0, err
   261  	}
   262  	return doc.Revno, nil
   263  }
   264  
   265  func ConvertTagToCollectionNameAndId(st *State, tag names.Tag) (string, interface{}, error) {
   266  	return st.tagToCollectionAndId(tag)
   267  }
   268  
   269  func RunTransaction(st *State, ops []txn.Op) error {
   270  	return st.runTransaction(ops)
   271  }
   272  
   273  // Return the PasswordSalt that goes along with the PasswordHash
   274  func GetUserPasswordSaltAndHash(u *User) (string, string) {
   275  	return u.doc.PasswordSalt, u.doc.PasswordHash
   276  }
   277  
   278  func CheckUserExists(st *State, name string) (bool, error) {
   279  	return st.checkUserExists(name)
   280  }
   281  
   282  func WatcherMergeIds(st *State, changeset *[]string, updates map[interface{}]bool, idconv func(string) string) error {
   283  	return mergeIds(st, changeset, updates, idconv)
   284  }
   285  
   286  func WatcherEnsureSuffixFn(marker string) func(string) string {
   287  	return ensureSuffixFn(marker)
   288  }
   289  
   290  func WatcherMakeIdFilter(st *State, marker string, receivers ...ActionReceiver) func(interface{}) bool {
   291  	return makeIdFilter(st, marker, receivers...)
   292  }
   293  
   294  func NewActionStatusWatcher(st *State, receivers []ActionReceiver, statuses ...ActionStatus) StringsWatcher {
   295  	return newActionStatusWatcher(st, receivers, statuses...)
   296  }
   297  
   298  func GetAllUpgradeInfos(st *State) ([]*UpgradeInfo, error) {
   299  	upgradeInfos, closer := st.getCollection(upgradeInfoC)
   300  	defer closer()
   301  
   302  	var out []*UpgradeInfo
   303  	var doc upgradeInfoDoc
   304  	iter := upgradeInfos.Find(nil).Iter()
   305  	defer iter.Close()
   306  	for iter.Next(&doc) {
   307  		out = append(out, &UpgradeInfo{st: st, doc: doc})
   308  	}
   309  	if err := iter.Err(); err != nil {
   310  		return nil, err
   311  	}
   312  	return out, nil
   313  }
   314  
   315  func UserModelNameIndex(username, modelName string) string {
   316  	return userModelNameIndex(username, modelName)
   317  }
   318  
   319  func DocID(st *State, id string) string {
   320  	return st.docID(id)
   321  }
   322  
   323  func LocalID(st *State, id string) string {
   324  	return st.localID(id)
   325  }
   326  
   327  func StrictLocalID(st *State, id string) (string, error) {
   328  	return st.strictLocalID(id)
   329  }
   330  
   331  func GetUnitModelUUID(unit *Unit) string {
   332  	return unit.doc.ModelUUID
   333  }
   334  
   335  func GetCollection(st *State, name string) (mongo.Collection, func()) {
   336  	return st.getCollection(name)
   337  }
   338  
   339  func GetRawCollection(st *State, name string) (*mgo.Collection, func()) {
   340  	return st.getRawCollection(name)
   341  }
   342  
   343  func HasRawAccess(collectionName string) bool {
   344  	return allCollections()[collectionName].rawAccess
   345  }
   346  
   347  func MultiEnvCollections() []string {
   348  	var result []string
   349  	for name, info := range allCollections() {
   350  		if !info.global {
   351  			result = append(result, name)
   352  		}
   353  	}
   354  	return result
   355  }
   356  
   357  func Sequence(st *State, name string) (int, error) {
   358  	return st.sequence(name)
   359  }
   360  
   361  func SetModelLifeDead(st *State, modelUUID string) error {
   362  	ops := []txn.Op{{
   363  		C:      modelsC,
   364  		Id:     modelUUID,
   365  		Update: bson.D{{"$set", bson.D{{"life", Dead}}}},
   366  	}}
   367  	return st.runTransaction(ops)
   368  }
   369  
   370  func HostedModelCount(c *gc.C, st *State) int {
   371  	count, err := hostedModelCount(st)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	return count
   374  }
   375  
   376  type MockGlobalEntity struct {
   377  }
   378  
   379  func (m MockGlobalEntity) globalKey() string {
   380  	return "globalKey"
   381  }
   382  func (m MockGlobalEntity) Tag() names.Tag {
   383  	return names.NewMachineTag("42")
   384  }
   385  
   386  var (
   387  	_                    GlobalEntity = (*MockGlobalEntity)(nil)
   388  	TagToCollectionAndId              = (*State).tagToCollectionAndId
   389  )
   390  
   391  func AssertAddressConversion(c *gc.C, netAddr network.Address) {
   392  	addr := fromNetworkAddress(netAddr, OriginUnknown)
   393  	newNetAddr := addr.networkAddress()
   394  	c.Assert(netAddr, gc.DeepEquals, newNetAddr)
   395  
   396  	size := 5
   397  	netAddrs := make([]network.Address, size)
   398  	for i := 0; i < size; i++ {
   399  		netAddrs[i] = netAddr
   400  	}
   401  	addrs := fromNetworkAddresses(netAddrs, OriginUnknown)
   402  	newNetAddrs := networkAddresses(addrs)
   403  	c.Assert(netAddrs, gc.DeepEquals, newNetAddrs)
   404  }
   405  
   406  func AssertHostPortConversion(c *gc.C, netHostPort network.HostPort) {
   407  	hostPort := fromNetworkHostPort(netHostPort)
   408  	newNetHostPort := hostPort.networkHostPort()
   409  	c.Assert(netHostPort, gc.DeepEquals, newNetHostPort)
   410  
   411  	size := 5
   412  	netHostsPorts := make([][]network.HostPort, size)
   413  	for i := 0; i < size; i++ {
   414  		netHostsPorts[i] = make([]network.HostPort, size)
   415  		for j := 0; j < size; j++ {
   416  			netHostsPorts[i][j] = netHostPort
   417  		}
   418  	}
   419  	hostsPorts := fromNetworkHostsPorts(netHostsPorts)
   420  	newNetHostsPorts := networkHostsPorts(hostsPorts)
   421  	c.Assert(netHostsPorts, gc.DeepEquals, newNetHostsPorts)
   422  }
   423  
   424  // MakeLogDoc creates a database document for a single log message.
   425  func MakeLogDoc(
   426  	modelUUID string,
   427  	entity names.Tag,
   428  	t time.Time,
   429  	module string,
   430  	location string,
   431  	level loggo.Level,
   432  	msg string,
   433  ) *logDoc {
   434  	return &logDoc{
   435  		Id:        bson.NewObjectId(),
   436  		Time:      t.UnixNano(),
   437  		ModelUUID: modelUUID,
   438  		Entity:    entity.String(),
   439  		Version:   version.Current.String(),
   440  		Module:    module,
   441  		Location:  location,
   442  		Level:     int(level),
   443  		Message:   msg,
   444  	}
   445  }
   446  
   447  func SpaceDoc(s *Space) spaceDoc {
   448  	return s.doc
   449  }
   450  
   451  func ForceDestroyMachineOps(m *Machine) ([]txn.Op, error) {
   452  	return m.forceDestroyOps()
   453  }
   454  
   455  func IsManagerMachineError(err error) bool {
   456  	return errors.Cause(err) == managerMachineError
   457  }
   458  
   459  var ActionNotificationIdToActionId = actionNotificationIdToActionId
   460  
   461  func UpdateModelUserLastConnection(st *State, e permission.UserAccess, when time.Time) error {
   462  	return st.updateLastModelConnection(e.UserTag, when)
   463  }
   464  
   465  func RemoveEndpointBindingsForService(c *gc.C, service *Application) {
   466  	globalKey := service.globalKey()
   467  	removeOp := removeEndpointBindingsOp(globalKey)
   468  
   469  	txnError := service.st.runTransaction([]txn.Op{removeOp})
   470  	err := onAbort(txnError, nil) // ignore ErrAborted as it asserts DocExists
   471  	c.Assert(err, jc.ErrorIsNil)
   472  }
   473  
   474  func RelationCount(service *Application) int {
   475  	return service.doc.RelationCount
   476  }
   477  
   478  func AssertEndpointBindingsNotFoundForService(c *gc.C, service *Application) {
   479  	globalKey := service.globalKey()
   480  	storedBindings, _, err := readEndpointBindings(service.st, globalKey)
   481  	c.Assert(storedBindings, gc.IsNil)
   482  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf("endpoint bindings for %q not found", globalKey))
   483  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   484  }
   485  
   486  func LeadershipLeases(st *State) (map[string]lease.Info, error) {
   487  	client, err := st.getLeadershipLeaseClient()
   488  	if err != nil {
   489  		return nil, errors.Trace(err)
   490  	}
   491  	return client.Leases(), nil
   492  }
   493  
   494  func StorageAttachmentCount(instance StorageInstance) int {
   495  	internal, ok := instance.(*storageInstance)
   496  	if !ok {
   497  		return -1
   498  	}
   499  	return internal.doc.AttachmentCount
   500  }
   501  
   502  func ResetMigrationMode(c *gc.C, st *State) {
   503  	ops := []txn.Op{{
   504  		C:      modelsC,
   505  		Id:     st.ModelUUID(),
   506  		Assert: txn.DocExists,
   507  		Update: bson.M{
   508  			"$set": bson.M{"migration-mode": MigrationModeNone},
   509  		},
   510  	}}
   511  	err := st.runTransaction(ops)
   512  	c.Assert(err, jc.ErrorIsNil)
   513  }
   514  
   515  // PrimeUnitStatusHistory will add count history elements, advancing the test clock by
   516  // one second for each entry.
   517  func PrimeUnitStatusHistory(
   518  	c *gc.C, clock *testing.Clock,
   519  	unit *Unit, statusVal status.Status,
   520  	count, batchSize int,
   521  	nextData func(int) map[string]interface{},
   522  ) {
   523  	globalKey := unit.globalKey()
   524  
   525  	history, closer := unit.st.getCollection(statusesHistoryC)
   526  	defer closer()
   527  	historyW := history.Writeable()
   528  
   529  	var data map[string]interface{}
   530  	for i := 0; i < count; {
   531  		var docs []interface{}
   532  		for j := 0; j < batchSize && i < count; j++ {
   533  			clock.Advance(time.Second)
   534  			if nextData != nil {
   535  				data = utils.EscapeKeys(nextData(i))
   536  			}
   537  			docs = append(docs, &historicalStatusDoc{
   538  				Status:     statusVal,
   539  				StatusData: data,
   540  				Updated:    clock.Now().UnixNano(),
   541  				GlobalKey:  globalKey,
   542  			})
   543  			// Seems like you can't increment two values in one loop
   544  			i++
   545  		}
   546  		err := historyW.Insert(docs...)
   547  		c.Assert(err, jc.ErrorIsNil)
   548  	}
   549  	// Set the status for the unit itself.
   550  	doc := statusDoc{
   551  		Status:     statusVal,
   552  		StatusData: data,
   553  		Updated:    clock.Now().UnixNano(),
   554  	}
   555  	buildTxn := updateStatusSource(unit.st, globalKey, doc)
   556  	err := unit.st.run(buildTxn)
   557  	c.Assert(err, jc.ErrorIsNil)
   558  }