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 }