launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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  	"net/url"
    10  	"path/filepath"
    11  
    12  	"labix.org/v2/mgo"
    13  	"launchpad.net/errgo/errors"
    14  	gc "launchpad.net/gocheck"
    15  
    16  	"labix.org/v2/mgo/txn"
    17  	"launchpad.net/juju-core/charm"
    18  	"launchpad.net/juju-core/environs/config"
    19  	"launchpad.net/juju-core/instance"
    20  	"launchpad.net/juju-core/testing"
    21  )
    22  
    23  // transactionHook holds Before and After func()s that will be called
    24  // respectively before and after a particular state transaction is executed.
    25  type TransactionHook transactionHook
    26  
    27  // TransactionChecker values are returned from the various Set*Hooks calls,
    28  // and should be run after the code under test has been executed to check
    29  // that the expected number of transactions were run.
    30  type TransactionChecker func()
    31  
    32  func (c TransactionChecker) Check() {
    33  	c()
    34  }
    35  
    36  // SetTransactionHooks queues up hooks to be applied to the next transactions,
    37  // and returns a function that asserts all hooks have been run (and removes any
    38  // that have not). Each hook function can freely execute its own transactions
    39  // without causing other hooks to be triggered.
    40  // It returns a function that asserts that all hooks have been run, and removes
    41  // any that have not. It is an error to set transaction hooks when any are
    42  // already queued; and setting transaction hooks renders the *State goroutine-
    43  // unsafe.
    44  func SetTransactionHooks(c *gc.C, st *State, transactionHooks ...TransactionHook) TransactionChecker {
    45  	converted := make([]transactionHook, len(transactionHooks))
    46  	for i, hook := range transactionHooks {
    47  		converted[i] = transactionHook(hook)
    48  		c.Logf("%d: %#v", i, converted[i])
    49  	}
    50  	original := <-st.transactionHooks
    51  	st.transactionHooks <- converted
    52  	c.Assert(original, gc.HasLen, 0)
    53  	return func() {
    54  		remaining := <-st.transactionHooks
    55  		st.transactionHooks <- nil
    56  		c.Assert(remaining, gc.HasLen, 0)
    57  	}
    58  }
    59  
    60  // SetBeforeHooks uses SetTransactionHooks to queue N functions to be run
    61  // immediately before the next N transactions. The first function is executed
    62  // before the first transaction, the second function before the second
    63  // transaction and so on. Nil values are accepted, and useful, in that they can
    64  // be used to ensure that a transaction is run at the expected time, without
    65  // having to make any changes or assert any state.
    66  func SetBeforeHooks(c *gc.C, st *State, fs ...func()) TransactionChecker {
    67  	transactionHooks := make([]TransactionHook, len(fs))
    68  	for i, f := range fs {
    69  		transactionHooks[i] = TransactionHook{Before: f}
    70  	}
    71  	return SetTransactionHooks(c, st, transactionHooks...)
    72  }
    73  
    74  // SetAfterHooks uses SetTransactionHooks to queue N functions to be run
    75  // immediately after the next N transactions. The first function is executed
    76  // after the first transaction, the second function after the second
    77  // transaction and so on.
    78  func SetAfterHooks(c *gc.C, st *State, fs ...func()) TransactionChecker {
    79  	transactionHooks := make([]TransactionHook, len(fs))
    80  	for i, f := range fs {
    81  		transactionHooks[i] = TransactionHook{After: f}
    82  	}
    83  	return SetTransactionHooks(c, st, transactionHooks...)
    84  }
    85  
    86  // SetRetryHooks uses SetTransactionHooks to inject a block function designed
    87  // to disrupt a transaction built against recent state, and a check function
    88  // designed to verify that the replacement transaction against the new state
    89  // has been applied as expected.
    90  func SetRetryHooks(c *gc.C, st *State, block, check func()) TransactionChecker {
    91  	return SetTransactionHooks(c, st, TransactionHook{
    92  		Before: block,
    93  	}, TransactionHook{
    94  		After: check,
    95  	})
    96  }
    97  
    98  // TestingInitialize initializes the state and returns it. If state was not
    99  // already initialized, and cfg is nil, the minimal default environment
   100  // configuration will be used.
   101  func TestingInitialize(c *gc.C, cfg *config.Config) *State {
   102  	if cfg == nil {
   103  		cfg = testing.EnvironConfig(c)
   104  	}
   105  	st, err := Initialize(TestingStateInfo(), cfg, TestingDialOpts())
   106  	c.Assert(err, gc.IsNil)
   107  	return st
   108  }
   109  
   110  type (
   111  	CharmDoc    charmDoc
   112  	MachineDoc  machineDoc
   113  	RelationDoc relationDoc
   114  	ServiceDoc  serviceDoc
   115  	UnitDoc     unitDoc
   116  )
   117  
   118  func (doc *MachineDoc) String() string {
   119  	m := &Machine{doc: machineDoc(*doc)}
   120  	return m.String()
   121  }
   122  
   123  func ServiceSettingsRefCount(st *State, serviceName string, curl *charm.URL) (int, error) {
   124  	key := serviceSettingsKey(serviceName, curl)
   125  	var doc settingsRefsDoc
   126  	if err := st.settingsrefs.FindId(key).One(&doc); err == nil {
   127  		return doc.RefCount, nil
   128  	}
   129  	return 0, mgo.ErrNotFound
   130  }
   131  
   132  func AddTestingCharm(c *gc.C, st *State, name string) *Charm {
   133  	return addCharm(c, st, "quantal", testing.Charms.Dir(name))
   134  }
   135  
   136  func AddTestingService(c *gc.C, st *State, name string, ch *Charm) *Service {
   137  	service, err := st.AddService(name, "user-admin", ch)
   138  	c.Assert(err, gc.IsNil)
   139  	return service
   140  }
   141  
   142  func AddCustomCharm(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm {
   143  	path := testing.Charms.ClonedDirPath(c.MkDir(), name)
   144  	if filename != "" {
   145  		config := filepath.Join(path, filename)
   146  		err := ioutil.WriteFile(config, []byte(content), 0644)
   147  		c.Assert(err, gc.IsNil)
   148  	}
   149  	ch, err := charm.ReadDir(path)
   150  	c.Assert(err, gc.IsNil)
   151  	if revision != -1 {
   152  		ch.SetRevision(revision)
   153  	}
   154  	return addCharm(c, st, series, ch)
   155  }
   156  
   157  func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm {
   158  	ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision())
   159  	curl := charm.MustParseURL("local:" + series + "/" + ident)
   160  	bundleURL, err := url.Parse("http://bundles.testing.invalid/" + ident)
   161  	c.Assert(err, gc.IsNil)
   162  	sch, err := st.AddCharm(ch, curl, bundleURL, ident+"-sha256")
   163  	c.Assert(err, gc.IsNil)
   164  	return sch
   165  }
   166  
   167  var MachineIdLessThan = machineIdLessThan
   168  
   169  var JobNames = jobNames
   170  
   171  // SCHEMACHANGE
   172  // This method is used to reset a deprecated machine attribute.
   173  func SetMachineInstanceId(m *Machine, instanceId string) {
   174  	m.doc.InstanceId = instance.Id(instanceId)
   175  }
   176  
   177  // SCHEMACHANGE
   178  // ClearInstanceDocId sets instanceid on instanceData for machine to "".
   179  func ClearInstanceDocId(c *gc.C, m *Machine) {
   180  	ops := []txn.Op{
   181  		{
   182  			C:      m.st.instanceData.Name,
   183  			Id:     m.doc.Id,
   184  			Assert: txn.DocExists,
   185  			Update: D{{"$set", D{{"instanceid", ""}}}},
   186  		},
   187  	}
   188  
   189  	err := m.st.runTransaction(ops)
   190  	c.Assert(err, gc.IsNil)
   191  }
   192  
   193  // SCHEMACHANGE
   194  // This method is used to reset the ownertag attribute
   195  func SetServiceOwnerTag(s *Service, ownerTag string) {
   196  	s.doc.OwnerTag = ownerTag
   197  }
   198  
   199  // SCHEMACHANGE
   200  // Get the owner directly
   201  func GetServiceOwnerTag(s *Service) string {
   202  	return s.doc.OwnerTag
   203  }
   204  
   205  func SetPasswordHash(e Authenticator, passwordHash string) error {
   206  	type hasSetPasswordHash interface {
   207  		setPasswordHash(string) error
   208  	}
   209  	return e.(hasSetPasswordHash).setPasswordHash(passwordHash)
   210  }
   211  
   212  // Return the underlying PasswordHash stored in the database. Used by the test
   213  // suite to check that the PasswordHash gets properly updated to new values
   214  // when compatibility mode is detected.
   215  func GetPasswordHash(e Authenticator) string {
   216  	type hasGetPasswordHash interface {
   217  		getPasswordHash() string
   218  	}
   219  	return e.(hasGetPasswordHash).getPasswordHash()
   220  }
   221  
   222  func init() {
   223  	logSize = logSizeTests
   224  }
   225  
   226  // MinUnitsRevno returns the Revno of the minUnits document
   227  // associated with the given service name.
   228  func MinUnitsRevno(st *State, serviceName string) (int, error) {
   229  	var doc minUnitsDoc
   230  	if err := st.minUnits.FindId(serviceName).One(&doc); err != nil {
   231  		return 0, mask(err, errors.Is(mgo.ErrNotFound))
   232  	}
   233  	return doc.Revno, nil
   234  }
   235  
   236  func ParseTag(st *State, tag string) (string, string, error) {
   237  	return st.parseTag(tag)
   238  }
   239  
   240  // Return the PasswordSalt that goes along with the PasswordHash
   241  func GetUserPasswordSaltAndHash(u *User) (string, string) {
   242  	return u.doc.PasswordSalt, u.doc.PasswordHash
   243  }
   244  
   245  var NewAddress = newAddress
   246  
   247  func CheckUserExists(st *State, name string) (bool, error) {
   248  	return st.checkUserExists(name)
   249  }