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 }