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 }