github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "os" 9 "path/filepath" 10 "strconv" 11 "time" // Only used for time types. 12 13 "github.com/juju/charm/v12" 14 "github.com/juju/clock" 15 "github.com/juju/clock/testclock" 16 "github.com/juju/description/v5" 17 "github.com/juju/errors" 18 "github.com/juju/loggo" 19 "github.com/juju/mgo/v3" 20 "github.com/juju/mgo/v3/bson" 21 "github.com/juju/mgo/v3/txn" 22 "github.com/juju/names/v5" 23 jc "github.com/juju/testing/checkers" 24 jujutxn "github.com/juju/txn/v3" 25 txntesting "github.com/juju/txn/v3/testing" 26 jutils "github.com/juju/utils/v3" 27 "github.com/juju/worker/v3" 28 "github.com/kr/pretty" 29 gc "gopkg.in/check.v1" 30 31 corebase "github.com/juju/juju/core/base" 32 "github.com/juju/juju/core/network" 33 "github.com/juju/juju/core/permission" 34 "github.com/juju/juju/core/resources" 35 "github.com/juju/juju/core/secrets" 36 "github.com/juju/juju/core/status" 37 "github.com/juju/juju/mongo" 38 "github.com/juju/juju/mongo/utils" 39 "github.com/juju/juju/state/storage" 40 "github.com/juju/juju/state/watcher" 41 "github.com/juju/juju/testcharms" 42 "github.com/juju/juju/testcharms/repo" 43 "github.com/juju/juju/version" 44 ) 45 46 const ( 47 MachinesC = machinesC 48 ModelEntityRefsC = modelEntityRefsC 49 ApplicationsC = applicationsC 50 OfferConnectionsC = offerConnectionsC 51 EndpointBindingsC = endpointBindingsC 52 ControllersC = controllersC 53 UsersC = usersC 54 BlockDevicesC = blockDevicesC 55 StorageInstancesC = storageInstancesC 56 GlobalSettingsC = globalSettingsC 57 SettingsC = settingsC 58 UnitsC = unitsC 59 ) 60 61 var ( 62 BinarystorageNew = &binarystorageNew 63 MachineIdLessThan = machineIdLessThan 64 CombineMeterStatus = combineMeterStatus 65 ApplicationGlobalKey = applicationGlobalKey 66 CloudGlobalKey = cloudGlobalKey 67 RegionSettingsGlobalKey = regionSettingsGlobalKey 68 ModelGlobalKey = modelGlobalKey 69 DBCollectionSizeToInt = dbCollectionSizeToInt 70 NewEntityWatcher = newEntityWatcher 71 ApplicationHasConnectedOffers = applicationHasConnectedOffers 72 NewActionNotificationWatcher = newActionNotificationWatcher 73 ) 74 75 type ( 76 CharmDoc charmDoc 77 ApplicationDoc = applicationDoc 78 ConstraintsDoc = constraintsDoc 79 80 StorageBackend = storageBackend 81 DeviceBackend = deviceBackend 82 ControllerNodeInstance = controllerNode 83 ) 84 85 var ( 86 IsDying = isDying 87 ) 88 89 func NewStateSettingsForCollection(backend modelBackend, collection string) *StateSettings { 90 return &StateSettings{backend, globalSettingsC} 91 } 92 93 // EnsureWorkersStarted ensures that all the automatically 94 // started state workers are running, so that tests which 95 // insert transaction hooks are less likely to have the hooks 96 // run by some other worker, and any side effects of starting 97 // the workers (for example, creating collections) will have 98 // taken effect. 99 func EnsureWorkersStarted(st *State) { 100 // Note: we don't start the all-watcher workers, as 101 // they're started on demand anyway. 102 st.workers.txnLogWatcher() 103 } 104 105 func SetTestHooks(c *gc.C, st *State, hooks ...jujutxn.TestHook) txntesting.TransactionChecker { 106 EnsureWorkersStarted(st) 107 return txntesting.SetTestHooks(c, newRunnerForHooks(st), hooks...) 108 } 109 110 func SetBeforeHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker { 111 EnsureWorkersStarted(st) 112 return txntesting.SetBeforeHooks(c, newRunnerForHooks(st), fs...) 113 } 114 115 // SetFailIfTransaction will set a transaction hook that marks the test as an error 116 // if there is a transaction run. This is used if you know a given set of operations 117 // should *not* trigger database updates. 118 func SetFailIfTransaction(c *gc.C, st *State) txntesting.TransactionChecker { 119 EnsureWorkersStarted(st) 120 return txntesting.SetFailIfTransaction(c, newRunnerForHooks(st)) 121 } 122 123 func SetAfterHooks(c *gc.C, st *State, fs ...func()) txntesting.TransactionChecker { 124 EnsureWorkersStarted(st) 125 return txntesting.SetAfterHooks(c, newRunnerForHooks(st), fs...) 126 } 127 128 func SetRetryHooks(c *gc.C, st *State, block, check func()) txntesting.TransactionChecker { 129 EnsureWorkersStarted(st) 130 return txntesting.SetRetryHooks(c, newRunnerForHooks(st), block, check) 131 } 132 133 func SetMaxTxnAttempts(c *gc.C, st *State, n int) { 134 st.maxTxnAttempts = n 135 db := st.database.(*database) 136 db.maxTxnAttempts = n 137 runner := jujutxn.NewRunner(jujutxn.RunnerParams{ 138 Database: db.raw, 139 Clock: st.stateClock, 140 TransactionCollectionName: "txns", 141 ChangeLogName: "-", 142 ServerSideTransactions: true, 143 MaxRetryAttempts: db.maxTxnAttempts, 144 }) 145 db.runner = runner 146 return 147 } 148 149 func newRunnerForHooks(st *State) jujutxn.Runner { 150 db := st.database.(*database) 151 runner := jujutxn.NewRunner(jujutxn.RunnerParams{ 152 Database: db.raw, 153 Clock: st.stateClock, 154 TransactionCollectionName: "txns", 155 ChangeLogName: "-", 156 ServerSideTransactions: true, 157 RunTransactionObserver: func(t jujutxn.Transaction) { 158 txnLogger.Tracef("ran transaction in %.3fs (retries: %d) %# v\nerr: %v", 159 t.Duration.Seconds(), t.Attempt, pretty.Formatter(t.Ops), t.Error) 160 }, 161 MaxRetryAttempts: db.maxTxnAttempts, 162 }) 163 db.runner = runner 164 return runner 165 } 166 167 // SetPolicy updates the State's policy field to the 168 // given Policy, and returns the old value. 169 func SetPolicy(st *State, p Policy) Policy { 170 old := st.policy 171 st.policy = p 172 return old 173 } 174 175 func CloudModelRefCount(st *State, cloudName string) (int, error) { 176 refcounts, closer := st.db().GetCollection(globalRefcountsC) 177 defer closer() 178 179 key := cloudModelRefCountKey(cloudName) 180 return nsRefcounts.read(refcounts, key) 181 } 182 183 func ApplicationSettingsRefCount(st *State, appName string, curl *string) (int, error) { 184 refcounts, closer := st.db().GetCollection(refcountsC) 185 defer closer() 186 187 key := applicationCharmConfigKey(appName, curl) 188 return nsRefcounts.read(refcounts, key) 189 } 190 191 func ApplicationOffersRefCount(st *State, appName string) (int, error) { 192 refcounts, closer := st.db().GetCollection(refcountsC) 193 defer closer() 194 195 key := applicationOffersRefCountKey(appName) 196 return nsRefcounts.read(refcounts, key) 197 } 198 199 func ControllerRefCount(st *State, controllerUUID string) (int, error) { 200 refcounts, closer := st.db().GetCollection(globalRefcountsC) 201 defer closer() 202 203 key := externalControllerRefCountKey(controllerUUID) 204 return nsRefcounts.read(refcounts, key) 205 } 206 207 func IncSecretConsumerRefCount(st *State, uri *secrets.URI, inc int) error { 208 refCountCollection, ccloser := st.db().GetCollection(refcountsC) 209 defer ccloser() 210 incOp, err := nsRefcounts.CreateOrIncRefOp(refCountCollection, uri.ID, inc) 211 if err != nil { 212 return errors.Trace(err) 213 } 214 return st.db().RunTransaction([]txn.Op{incOp}) 215 } 216 217 func SecretBackendRefCount(st *State, backendID string) (int, error) { 218 refcounts, closer := st.db().GetCollection(globalRefcountsC) 219 defer closer() 220 221 key := secretBackendRefCountKey(backendID) 222 return nsRefcounts.read(refcounts, key) 223 } 224 225 func AddTestingCharm(c *gc.C, st *State, name string) *Charm { 226 return addCharm(c, st, "quantal", testcharms.Repo.CharmDir(name)) 227 } 228 229 func AddTestingCharmFromRepo(c *gc.C, st *State, name string, repo *repo.CharmRepo) *Charm { 230 return addCharm(c, st, "quantal", repo.CharmDir(name)) 231 } 232 233 func AddTestingCharmWithSeries(c *gc.C, st *State, name string, series string) *Charm { 234 return addCharm(c, st, series, testcharms.Repo.CharmDir(name)) 235 } 236 237 func getCharmRepo(series string) *repo.CharmRepo { 238 // All testing charms for state are under `quantal` except `kubernetes`. 239 if series == "kubernetes" { 240 return testcharms.RepoForSeries(series) 241 } 242 return testcharms.Repo 243 } 244 245 func AddTestingCharmForSeries(c *gc.C, st *State, series, name string) *Charm { 246 // Existing logic! 247 // Get charm from `quantal` dir or `kubernetes`. 248 return addCharm(c, st, series, getCharmRepo(series).CharmDir(name)) 249 } 250 251 func AddTestingCharmhubCharmForSeries(c *gc.C, st *State, series, name string) *Charm { 252 ch := getCharmRepo(series).CharmDir(name) 253 ident := fmt.Sprintf("amd64/%s/%s-%d", series, name, ch.Revision()) 254 curl := "ch:" + ident 255 info := CharmInfo{ 256 Charm: ch, 257 ID: curl, 258 StoragePath: "dummy-path", 259 SHA256: ident + "-sha256", 260 } 261 sch, err := st.AddCharm(info) 262 c.Assert(err, jc.ErrorIsNil) 263 return sch 264 } 265 266 func AddTestingCharmMultiSeries(c *gc.C, st *State, name string) *Charm { 267 ch := testcharms.Repo.CharmDir(name) 268 ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) 269 curl := "ch:" + ident 270 info := CharmInfo{ 271 Charm: ch, 272 ID: curl, 273 StoragePath: "dummy-path", 274 SHA256: ident + "-sha256", 275 } 276 sch, err := st.AddCharm(info) 277 c.Assert(err, jc.ErrorIsNil) 278 return sch 279 } 280 281 func AddTestingApplication(c *gc.C, st *State, name string, ch *Charm) *Application { 282 return addTestingApplication(c, addTestingApplicationParams{ 283 st: st, 284 name: name, 285 ch: ch, 286 }) 287 } 288 289 func AddTestingApplicationForBase(c *gc.C, st *State, base Base, name string, ch *Charm) *Application { 290 return addTestingApplication(c, addTestingApplicationParams{ 291 st: st, 292 origin: &CharmOrigin{Platform: &Platform{ 293 OS: base.OS, 294 Channel: base.Channel, 295 }}, 296 name: name, 297 ch: ch, 298 }) 299 } 300 301 func AddTestingApplicationWithNumUnits(c *gc.C, st *State, numUnits int, name string, ch *Charm) *Application { 302 return addTestingApplication(c, addTestingApplicationParams{ 303 st: st, 304 numUnits: numUnits, 305 name: name, 306 ch: ch, 307 }) 308 } 309 310 func AddTestingApplicationWithStorage(c *gc.C, st *State, name string, ch *Charm, storage map[string]StorageConstraints) *Application { 311 curl := charm.MustParseURL(ch.URL()) 312 series := curl.Series 313 if series == "kubernetes" { 314 series = "focal" 315 } 316 base, err := corebase.GetBaseFromSeries(series) 317 c.Assert(err, jc.ErrorIsNil) 318 var source string 319 switch curl.Schema { 320 case "local": 321 source = "local" 322 case "ch": 323 source = "charm-hub" 324 case "cs": 325 source = "charm-store" 326 } 327 origin := &CharmOrigin{ 328 Source: source, 329 Platform: &Platform{ 330 OS: base.OS, 331 Channel: base.Channel.String(), 332 }, 333 } 334 return addTestingApplication(c, addTestingApplicationParams{ 335 st: st, 336 name: name, 337 ch: ch, 338 origin: origin, 339 storage: storage, 340 }) 341 } 342 343 func AddTestingApplicationWithDevices(c *gc.C, st *State, name string, ch *Charm, devices map[string]DeviceConstraints) *Application { 344 return addTestingApplication(c, addTestingApplicationParams{ 345 st: st, 346 name: name, 347 ch: ch, 348 devices: devices, 349 }) 350 } 351 352 func AddTestingApplicationWithBindings(c *gc.C, st *State, name string, ch *Charm, bindings map[string]string) *Application { 353 return addTestingApplication(c, addTestingApplicationParams{ 354 st: st, 355 name: name, 356 ch: ch, 357 bindings: bindings, 358 }) 359 } 360 361 type addTestingApplicationParams struct { 362 st *State 363 name string 364 ch *Charm 365 origin *CharmOrigin 366 bindings map[string]string 367 storage map[string]StorageConstraints 368 devices map[string]DeviceConstraints 369 numUnits int 370 } 371 372 func addTestingApplication(c *gc.C, params addTestingApplicationParams) *Application { 373 c.Assert(params.ch, gc.NotNil) 374 origin := params.origin 375 curl := charm.MustParseURL(params.ch.URL()) 376 if origin == nil { 377 base, err := corebase.GetBaseFromSeries(curl.Series) 378 c.Assert(err, jc.ErrorIsNil) 379 var channel *Channel 380 // local charms cannot have a channel 381 if charm.CharmHub.Matches(curl.Schema) { 382 channel = &Channel{Risk: "stable"} 383 } 384 var source string 385 switch curl.Schema { 386 case "local": 387 source = "local" 388 case "ch": 389 source = "charm-hub" 390 case "cs": 391 source = "charm-store" 392 } 393 origin = &CharmOrigin{ 394 Channel: channel, 395 Source: source, 396 Platform: &Platform{ 397 OS: base.OS, 398 Channel: base.Channel.String(), 399 }, 400 } 401 } 402 app, err := params.st.AddApplication(AddApplicationArgs{ 403 Name: params.name, 404 Charm: params.ch, 405 CharmOrigin: origin, 406 EndpointBindings: params.bindings, 407 Storage: params.storage, 408 Devices: params.devices, 409 NumUnits: params.numUnits, 410 }) 411 c.Assert(err, jc.ErrorIsNil) 412 return app 413 } 414 415 func addCustomCharmWithManifest(c *gc.C, st *State, repo *repo.CharmRepo, name, filename, content, series string, revision int, manifest bool) *Charm { 416 path := repo.ClonedDirPath(c.MkDir(), name) 417 if filename != "" { 418 if manifest { 419 manifestContent := ` 420 bases: 421 - name: ubuntu 422 channel: "18.04" 423 ` 424 manifestYAML := filepath.Join(path, "manifest.yaml") 425 err := os.WriteFile(manifestYAML, []byte(manifestContent), 0644) 426 c.Assert(err, jc.ErrorIsNil) 427 } 428 config := filepath.Join(path, filename) 429 err := os.WriteFile(config, []byte(content), 0644) 430 c.Assert(err, jc.ErrorIsNil) 431 } 432 ch, err := charm.ReadCharmDir(path) 433 c.Assert(err, jc.ErrorIsNil) 434 if revision != -1 { 435 ch.SetRevision(revision) 436 } 437 return addCharm(c, st, series, ch) 438 } 439 440 func addCustomCharm(c *gc.C, st *State, repo *repo.CharmRepo, name, filename, content, series string, revision int) *Charm { 441 return addCustomCharmWithManifest(c, st, repo, name, filename, content, series, revision, false) 442 } 443 444 func AddCustomCharmWithManifest(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm { 445 return addCustomCharmWithManifest(c, st, testcharms.RepoForSeries(series), name, filename, content, series, revision, true) 446 } 447 448 func AddCustomCharmForSeries(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm { 449 // Copy charm from `series` dir. 450 return addCustomCharm(c, st, testcharms.RepoForSeries(series), name, filename, content, series, revision) 451 } 452 453 func AddCustomCharm(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm { 454 return addCustomCharm(c, st, getCharmRepo(series), name, filename, content, series, revision) 455 } 456 457 func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm { 458 ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision()) 459 curl := "local:" + series + "/" + ident 460 if series == "" { 461 ident = fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) 462 curl = "local:" + ident 463 } 464 info := CharmInfo{ 465 Charm: ch, 466 ID: curl, 467 StoragePath: "dummy-path", 468 SHA256: ident + "-sha256", 469 } 470 sch, err := st.AddCharm(info) 471 c.Assert(err, jc.ErrorIsNil) 472 return sch 473 } 474 475 // TxnRevno returns the txn-revno field of the document 476 // associated with the given Id in the given collection. 477 func TxnRevno(st *State, collName string, id interface{}) (int64, error) { 478 var doc struct { 479 TxnRevno int64 `bson:"txn-revno"` 480 } 481 coll, closer := st.db().GetCollection(collName) 482 defer closer() 483 err := coll.FindId(id).One(&doc) 484 if err != nil { 485 return 0, err 486 } 487 return doc.TxnRevno, nil 488 } 489 490 // MinUnitsRevno returns the Revno of the minUnits document 491 // associated with the given application name. 492 func MinUnitsRevno(st *State, applicationname string) (int, error) { 493 minUnitsCollection, closer := st.db().GetCollection(minUnitsC) 494 defer closer() 495 var doc minUnitsDoc 496 if err := minUnitsCollection.FindId(applicationname).One(&doc); err != nil { 497 return 0, err 498 } 499 return doc.Revno, nil 500 } 501 502 func ConvertTagToCollectionNameAndId(st *State, tag names.Tag) (string, interface{}, error) { 503 return st.tagToCollectionAndId(tag) 504 } 505 506 func NowToTheSecond(st *State) time.Time { 507 return st.nowToTheSecond() 508 } 509 510 // Return the PasswordSalt that goes along with the PasswordHash 511 func GetUserPasswordSaltAndHash(u *User) (string, string) { 512 return u.doc.PasswordSalt, u.doc.PasswordHash 513 } 514 515 func CheckUserExists(st *State, name string) (bool, error) { 516 return st.checkUserExists(name) 517 } 518 519 func WatcherMergeIds(changeset *[]string, updates map[interface{}]bool, idconv func(string) (string, error)) error { 520 return mergeIds(changeset, updates, idconv) 521 } 522 523 func WatcherEnsureSuffixFn(marker string) func(string) string { 524 return ensureSuffixFn(marker) 525 } 526 527 func WatcherMakeIdFilter(st *State, marker string, receivers ...ActionReceiver) func(interface{}) bool { 528 return makeIdFilter(st, marker, receivers...) 529 } 530 531 func GetAllUpgradeInfos(st *State) ([]*UpgradeInfo, error) { 532 upgradeInfos, closer := st.db().GetCollection(upgradeInfoC) 533 defer closer() 534 535 var out []*UpgradeInfo 536 var doc upgradeInfoDoc 537 iter := upgradeInfos.Find(nil).Iter() 538 defer iter.Close() 539 for iter.Next(&doc) { 540 out = append(out, &UpgradeInfo{st: st, doc: doc}) 541 } 542 if err := iter.Close(); err != nil { 543 return nil, err 544 } 545 return out, nil 546 } 547 548 func UserModelNameIndex(username, modelName string) string { 549 return userModelNameIndex(username, modelName) 550 } 551 552 func (m *Model) UniqueIndexExists() bool { 553 coll, closer := m.st.db().GetCollection(usermodelnameC) 554 defer closer() 555 556 var doc bson.M 557 err := coll.FindId(m.uniqueIndexID()).One(&doc) 558 559 return err == nil 560 } 561 562 func DocID(mb modelBackend, id string) string { 563 return mb.docID(id) 564 } 565 566 func LocalID(mb modelBackend, id string) string { 567 return mb.localID(id) 568 } 569 570 func StrictLocalID(mb modelBackend, id string) (string, error) { 571 return mb.strictLocalID(id) 572 } 573 574 func GetUnitModelUUID(unit *Unit) string { 575 return unit.doc.ModelUUID 576 } 577 578 func GetCollection(mb modelBackend, name string) (mongo.Collection, func()) { 579 return mb.db().GetCollection(name) 580 } 581 582 func GetRawCollection(mb modelBackend, name string) (*mgo.Collection, func()) { 583 return mb.db().GetRawCollection(name) 584 } 585 586 func HasRawAccess(collectionName string) bool { 587 return allCollections()[collectionName].rawAccess 588 } 589 590 func MultiModelCollections() []string { 591 var result []string 592 for name, info := range allCollections() { 593 if !info.global { 594 result = append(result, name) 595 } 596 } 597 return result 598 } 599 600 func Sequence(st *State, name string) (int, error) { 601 return sequence(st, name) 602 } 603 604 func ResetSequence(mb modelBackend, name string) error { 605 return resetSequence(mb, name) 606 } 607 608 func SequenceWithMin(st *State, name string, minVal int) (int, error) { 609 return sequenceWithMin(st, name, minVal) 610 } 611 612 func SequenceEnsure(st *State, name string, nextVal int) error { 613 sequences, closer := st.db().GetRawCollection(sequenceC) 614 defer closer() 615 updater := newDbSeqUpdater(sequences, st.ModelUUID(), name) 616 return updater.ensure(nextVal) 617 } 618 619 func (m *Model) SetDead() error { 620 ops := []txn.Op{{ 621 C: modelsC, 622 Id: m.doc.UUID, 623 Update: bson.D{{"$set", bson.D{{"life", Dead}}}}, 624 }, { 625 C: usermodelnameC, 626 Id: m.uniqueIndexID(), 627 Remove: true, 628 }} 629 return m.st.db().RunTransaction(ops) 630 } 631 632 func (st *State) SetDyingModelToDead() error { 633 return st.setDyingModelToDead() 634 } 635 636 func HostedModelCount(c *gc.C, st *State) int { 637 count, err := hostedModelCount(st) 638 c.Assert(err, jc.ErrorIsNil) 639 return count 640 } 641 642 type MockGlobalEntity struct { 643 } 644 645 func (m MockGlobalEntity) globalKey() string { 646 return "globalKey" 647 } 648 func (m MockGlobalEntity) Tag() names.Tag { 649 return names.NewMachineTag("42") 650 } 651 652 var ( 653 _ GlobalEntity = (*MockGlobalEntity)(nil) 654 TagToCollectionAndId = (*State).tagToCollectionAndId 655 ) 656 657 func AssertAddressConversion(c *gc.C, netAddr network.SpaceAddress) { 658 addr := fromNetworkAddress(netAddr, network.OriginUnknown) 659 newNetAddr := addr.networkAddress() 660 c.Assert(netAddr, gc.DeepEquals, newNetAddr) 661 662 size := 5 663 netAddrs := make(network.SpaceAddresses, size) 664 for i := 0; i < size; i++ { 665 netAddrs[i] = netAddr 666 } 667 addrs := fromNetworkAddresses(netAddrs, network.OriginUnknown) 668 newNetAddrs := networkAddresses(addrs) 669 c.Assert(netAddrs, gc.DeepEquals, newNetAddrs) 670 } 671 672 func AssertHostPortConversion(c *gc.C, netHostPort network.SpaceHostPort) { 673 hostPort := fromNetworkHostPort(netHostPort) 674 newNetHostPort := hostPort.networkHostPort() 675 c.Assert(netHostPort, gc.DeepEquals, newNetHostPort) 676 677 size := 5 678 netHostsPorts := make([]network.SpaceHostPorts, size) 679 for i := 0; i < size; i++ { 680 netHostsPorts[i] = make(network.SpaceHostPorts, size) 681 for j := 0; j < size; j++ { 682 netHostsPorts[i][j] = netHostPort 683 } 684 } 685 hostsPorts := fromNetworkHostsPorts(netHostsPorts) 686 newNetHostsPorts := networkHostsPorts(hostsPorts) 687 c.Assert(netHostsPorts, gc.DeepEquals, newNetHostsPorts) 688 } 689 690 // MakeLogDoc creates a database document for a single log message. 691 func MakeLogDoc( 692 entity string, 693 t time.Time, 694 module string, 695 location string, 696 level loggo.Level, 697 msg string, 698 labels []string, 699 ) *logDoc { 700 return &logDoc{ 701 Id: bson.NewObjectId(), 702 Time: t.UnixNano(), 703 Entity: entity, 704 Version: version.Current.String(), 705 Module: module, 706 Location: location, 707 Level: int(level), 708 Message: msg, 709 Labels: labels, 710 } 711 } 712 713 func SpaceDoc(s *Space) spaceDoc { 714 return s.doc 715 } 716 717 func ForceDestroyMachineOps(m *Machine) ([]txn.Op, error) { 718 // For test we do not want to wait for the force. 719 return m.forceDestroyOps(time.Duration(0)) 720 } 721 722 func MakeActionIdConverter(st *State) func(string) (string, error) { 723 return func(id string) (string, error) { 724 id, err := st.strictLocalID(id) 725 if err != nil { 726 return "", errors.Trace(err) 727 } 728 return actionNotificationIdToActionId(id), err 729 } 730 } 731 732 func UpdateModelUserLastConnection(st *State, e permission.UserAccess, when time.Time) error { 733 model, err := st.Model() 734 if err != nil { 735 return errors.Trace(err) 736 } 737 738 return model.updateLastModelConnection(e.UserTag, when) 739 } 740 741 func SetWantsVote(st *State, id string, wantsVote bool) error { 742 op := setControllerWantsVoteOp(st, id, wantsVote) 743 return st.runRawTransaction([]txn.Op{op}) 744 } 745 746 func RemoveEndpointBindingsForApplication(c *gc.C, app *Application) { 747 globalKey := app.globalKey() 748 removeOp := removeEndpointBindingsOp(globalKey) 749 750 txnError := app.st.db().RunTransaction([]txn.Op{removeOp}) 751 err := onAbort(txnError, nil) // ignore ErrAborted as it asserts DocExists 752 c.Assert(err, jc.ErrorIsNil) 753 } 754 755 func RemoveOfferConnectionsForRelation(c *gc.C, rel *Relation) { 756 removeOps := removeOfferConnectionsForRelationOps(rel.Id()) 757 txnError := rel.st.db().RunTransaction(removeOps) 758 err := onAbort(txnError, nil) // ignore ErrAborted as it asserts DocExists 759 c.Assert(err, jc.ErrorIsNil) 760 } 761 762 func RelationCount(app *Application) int { 763 return app.doc.RelationCount 764 } 765 766 func AssertEndpointBindingsNotFoundForApplication(c *gc.C, app *Application) { 767 globalKey := app.globalKey() 768 storedBindings, _, err := readEndpointBindings(app.st, globalKey) 769 c.Assert(storedBindings, gc.IsNil) 770 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("endpoint bindings for %q not found", globalKey)) 771 c.Assert(err, jc.Satisfies, errors.IsNotFound) 772 } 773 774 func StorageAttachmentCount(instance StorageInstance) int { 775 internal, ok := instance.(*storageInstance) 776 if !ok { 777 return -1 778 } 779 return internal.doc.AttachmentCount 780 } 781 782 func ResetMigrationMode(c *gc.C, st *State) { 783 ops := []txn.Op{{ 784 C: modelsC, 785 Id: st.ModelUUID(), 786 Assert: txn.DocExists, 787 Update: bson.M{ 788 "$set": bson.M{"migration-mode": MigrationModeNone}, 789 }, 790 }} 791 err := st.db().RunTransaction(ops) 792 c.Assert(err, jc.ErrorIsNil) 793 } 794 795 func (a *RemoteApplication) SetDead() error { 796 ops := []txn.Op{{ 797 C: remoteApplicationsC, 798 Id: a.doc.Name, 799 Update: bson.D{{"$set", bson.D{{"life", Dead}}}}, 800 }} 801 return a.st.db().RunTransaction(ops) 802 } 803 804 func RemoveRelationStatus(c *gc.C, rel *Relation) { 805 st := rel.st 806 ops := []txn.Op{removeStatusOp(st, rel.globalScope())} 807 err := st.db().RunTransaction(ops) 808 c.Assert(err, jc.ErrorIsNil) 809 } 810 811 func RemoveUnitRelations(c *gc.C, rel *Relation) { 812 st := rel.st 813 scopes, closer := st.db().GetCollection(relationScopesC) 814 defer closer() 815 scopesW := scopes.Writeable() 816 _, err := scopesW.RemoveAll(nil) 817 c.Assert(err, jc.ErrorIsNil) 818 } 819 820 // PrimeUnitStatusHistory will add count history elements, advancing the test clock by 821 // one second for each entry. 822 func PrimeUnitStatusHistory( 823 c *gc.C, clock testclock.AdvanceableClock, 824 unit *Unit, statusVal status.Status, 825 count, batchSize int, 826 nextData func(int) map[string]interface{}, 827 ) { 828 globalKey := unit.globalKey() 829 830 history, closer := unit.st.db().GetCollection(statusesHistoryC) 831 defer closer() 832 historyW := history.Writeable() 833 834 var data map[string]interface{} 835 for i := 0; i < count; { 836 var docs []interface{} 837 for j := 0; j < batchSize && i < count; j++ { 838 clock.Advance(time.Second) 839 if nextData != nil { 840 data = utils.EscapeKeys(nextData(i)) 841 } 842 docs = append(docs, &historicalStatusDoc{ 843 Status: statusVal, 844 StatusData: data, 845 Updated: clock.Now().UnixNano(), 846 GlobalKey: globalKey, 847 }) 848 // Seems like you can't increment two values in one loop 849 i++ 850 } 851 err := historyW.Insert(docs...) 852 c.Assert(err, jc.ErrorIsNil) 853 } 854 // Set the status for the unit itself. 855 doc := statusDoc{ 856 Status: statusVal, 857 StatusData: data, 858 Updated: clock.Now().UnixNano(), 859 } 860 861 var buildTxn jujutxn.TransactionSource = func(int) ([]txn.Op, error) { 862 return statusSetOps(unit.st.db(), doc, globalKey) 863 } 864 865 err := unit.st.db().Run(buildTxn) 866 c.Assert(err, jc.ErrorIsNil) 867 } 868 869 // PrimeOperations generates operations and tasks to be pruned. 870 // The method pads each entry with a 1MB string. This should allow us to infer the 871 // approximate size of the entry and limit the number of entries that 872 // must be generated for size related tests. 873 func PrimeOperations(c *gc.C, age time.Time, unit *Unit, count, actionsPerOperation int) { 874 operationsCollection, closer := unit.st.db().GetCollection(operationsC) 875 defer closer() 876 actionCollection, closer := unit.st.db().GetCollection(actionsC) 877 defer closer() 878 879 operationsCollectionWriter := operationsCollection.Writeable() 880 actionCollectionWriter := actionCollection.Writeable() 881 882 const numBytes = 1 * 500 * 1000 883 var padding [numBytes]byte 884 var operationDocs []interface{} 885 var actionDocs []interface{} 886 for i := 0; i < count; i++ { 887 nextID, err := sequenceWithMin(unit.st, "task", 1) 888 c.Assert(err, jc.ErrorIsNil) 889 operationID := strconv.Itoa(nextID) 890 operationDocs = append(operationDocs, operationDoc{ 891 DocId: operationID, 892 ModelUUID: unit.st.ModelUUID(), 893 Summary: "an operation", 894 Completed: age, 895 Status: ActionCompleted, 896 }) 897 for j := 0; j < actionsPerOperation; j++ { 898 id, err := jutils.NewUUID() 899 c.Assert(err, jc.ErrorIsNil) 900 actionDocs = append(actionDocs, actionDoc{ 901 DocId: id.String(), 902 ModelUUID: unit.st.ModelUUID(), 903 Receiver: unit.Name(), 904 Completed: age, 905 Operation: operationID, 906 Status: ActionCompleted, 907 Message: string(padding[:numBytes]), 908 }) 909 } 910 } 911 912 err := operationsCollectionWriter.Insert(operationDocs...) 913 c.Assert(err, jc.ErrorIsNil) 914 err = actionCollectionWriter.Insert(actionDocs...) 915 c.Assert(err, jc.ErrorIsNil) 916 } 917 918 // PrimeLegacyActions creates actions without a parent operation. 919 func PrimeLegacyActions(c *gc.C, age time.Time, unit *Unit, count int) { 920 actionCollection, closer := unit.st.db().GetCollection(actionsC) 921 defer closer() 922 923 actionCollectionWriter := actionCollection.Writeable() 924 925 const numBytes = 1 * 500 * 1000 926 var padding [numBytes]byte 927 var actionDocs []interface{} 928 var ids []string 929 for i := 0; i < count; i++ { 930 nextID, err := sequenceWithMin(unit.st, "task", 1) 931 c.Assert(err, jc.ErrorIsNil) 932 ids = append(ids, fmt.Sprintf("%v:%d", unit.st.ModelUUID(), nextID)) 933 actionDocs = append(actionDocs, actionDoc{ 934 DocId: strconv.Itoa(nextID), 935 ModelUUID: unit.st.ModelUUID(), 936 Receiver: unit.Name(), 937 Completed: age, 938 Status: ActionCompleted, 939 Message: string(padding[:numBytes]), 940 }) 941 } 942 943 err := actionCollectionWriter.Insert(actionDocs...) 944 c.Assert(err, jc.ErrorIsNil) 945 for _, id := range ids { 946 err = actionCollectionWriter.UpdateId(id, bson.D{{"$unset", bson.M{"operation": 1}}}) 947 c.Assert(err, jc.ErrorIsNil) 948 } 949 } 950 951 // ActionOperationId returns the parent operation of an action. 952 func ActionOperationId(a Action) string { 953 return a.(*action).doc.Operation 954 } 955 956 // GetInternalWorkers returns the internal workers managed by a State 957 // to allow inspection in tests. 958 func GetInternalWorkers(st *State) worker.Worker { 959 return st.workers 960 } 961 962 // ResourceStoragePath returns the path used to store resource content 963 // in the managed blob store, given the resource ID. 964 func ResourceStoragePath(c *gc.C, st *State, id string) string { 965 p := st.Resources().(*resourcePersistence) 966 _, storagePath, err := p.getResource(id) 967 c.Assert(err, jc.ErrorIsNil) 968 return storagePath 969 } 970 971 func StagedResourceForTest(c *gc.C, st *State, res resources.Resource) *StagedResource { 972 persist := st.Resources().(*resourcePersistence) 973 storagePath := storagePath(res.Name, res.ApplicationID, res.PendingID) 974 r, err := persist.stageResource(res, storagePath) 975 c.Assert(err, jc.ErrorIsNil) 976 return r 977 } 978 979 // IsBlobStored returns true if a given storage path is in used in the 980 // managed blob store. 981 func IsBlobStored(c *gc.C, st *State, storagePath string) bool { 982 stor := storage.NewStorage(st.ModelUUID(), st.MongoSession()) 983 r, _, err := stor.Get(storagePath) 984 if err != nil { 985 if errors.IsNotFound(err) { 986 return false 987 } 988 c.Fatalf("Get failed: %v", err) 989 return false 990 } 991 r.Close() 992 return true 993 } 994 995 // AssertNoCleanupsWithKind checks that there are no cleanups 996 // of a given kind scheduled. 997 func AssertNoCleanupsWithKind(c *gc.C, st *State, kind cleanupKind) { 998 var docs []cleanupDoc 999 cleanups, closer := st.db().GetCollection(cleanupsC) 1000 defer closer() 1001 err := cleanups.Find(nil).All(&docs) 1002 c.Assert(err, jc.ErrorIsNil) 1003 for _, doc := range docs { 1004 if doc.Kind == kind { 1005 c.Fatalf("found cleanup of kind %q", kind) 1006 } 1007 } 1008 } 1009 1010 // AssertNoCleanupsWithKind checks that there is at least 1011 // one cleanup of a given kind scheduled. 1012 func AssertCleanupsWithKind(c *gc.C, st *State, kind cleanupKind) { 1013 var docs []cleanupDoc 1014 cleanups, closer := st.db().GetCollection(cleanupsC) 1015 defer closer() 1016 err := cleanups.Find(nil).All(&docs) 1017 c.Assert(err, jc.ErrorIsNil) 1018 for _, doc := range docs { 1019 if doc.Kind == kind { 1020 return 1021 } 1022 } 1023 c.Fatalf("found no cleanups of kind %q", kind) 1024 } 1025 1026 // AssertNoCleanups checks that there are no cleanups scheduled. 1027 func AssertNoCleanups(c *gc.C, st *State) { 1028 var docs []cleanupDoc 1029 cleanups, closer := st.db().GetCollection(cleanupsC) 1030 defer closer() 1031 err := cleanups.Find(nil).All(&docs) 1032 c.Assert(err, jc.ErrorIsNil) 1033 if len(docs) > 0 { 1034 c.Fatalf("unexpected cleanups: %+v", docs) 1035 } 1036 } 1037 1038 // GetApplicationCharmConfig allows access to settings collection for a 1039 // given application in order to get the charm config. 1040 func GetApplicationCharmConfig(st *State, app *Application) *Settings { 1041 return newSettings(st.db(), settingsC, app.charmConfigKey()) 1042 } 1043 1044 // GetApplicationConfig allows access to settings collection for a 1045 // given application in order to get the application config. 1046 func GetApplicationConfig(st *State, app *Application) *Settings { 1047 return newSettings(st.db(), settingsC, app.applicationConfigKey()) 1048 } 1049 1050 // GetApplicationHasResources returns the app's HasResources value. 1051 func GetApplicationHasResources(app *Application) bool { 1052 return app.doc.HasResources 1053 } 1054 1055 // GetControllerSettings allows access to settings collection for 1056 // the controller. 1057 func GetControllerSettings(st *State) *Settings { 1058 return newSettings(st.db(), controllersC, ControllerSettingsGlobalKey) 1059 } 1060 1061 // GetPopulatedSettings returns a reference to settings with the input values 1062 // populated. Attempting to read/write will cause nil-reference panics. 1063 func GetPopulatedSettings(cfg map[string]interface{}) *Settings { 1064 return &Settings{ 1065 disk: copyMap(cfg, nil), 1066 core: copyMap(cfg, nil), 1067 } 1068 } 1069 1070 // NewSLALevel returns a new SLA level. 1071 func NewSLALevel(level string) (slaLevel, error) { 1072 return newSLALevel(level) 1073 } 1074 1075 func AppStorageConstraints(app *Application) (map[string]StorageConstraints, error) { 1076 return readStorageConstraints(app.st, app.storageConstraintsKey()) 1077 } 1078 1079 func RemoveRelation(c *gc.C, rel *Relation, force bool) { 1080 op := &ForcedOperation{Force: force} 1081 ops, err := rel.removeOps("", "", op) 1082 c.Assert(err, jc.ErrorIsNil) 1083 c.Logf("operational errors %v", op.Errors) 1084 c.Assert(op.Errors, gc.HasLen, 0) 1085 err = rel.st.db().RunTransaction(ops) 1086 c.Assert(err, jc.ErrorIsNil) 1087 } 1088 1089 func AddVolumeOps(st *State, params VolumeParams, machineId string) ([]txn.Op, names.VolumeTag, error) { 1090 sb, err := NewStorageBackend(st) 1091 if err != nil { 1092 return nil, names.VolumeTag{}, err 1093 } 1094 return sb.addVolumeOps(params, machineId) 1095 } 1096 1097 func ModelBackendFromStorageBackend(sb *StorageBackend) modelBackend { 1098 return sb.mb 1099 } 1100 1101 func (st *State) ModelQueryForUser(user names.UserTag, isSuperuser bool) (mongo.Query, SessionCloser, error) { 1102 return st.modelQueryForUser(user, isSuperuser) 1103 } 1104 1105 func UnitsHaveChanged(m *Machine, unitNames []string) (bool, error) { 1106 return m.unitsHaveChanged(unitNames) 1107 } 1108 1109 func GetCloudContainerStatus(st *State, name string) (status.StatusInfo, error) { 1110 return getStatus(st.db(), globalCloudContainerKey(name), "unit") 1111 } 1112 1113 func GetCloudContainerStatusHistory(st *State, name string, filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1114 args := &statusHistoryArgs{ 1115 db: st.db(), 1116 globalKey: globalCloudContainerKey(name), 1117 filter: filter, 1118 clock: st.clock(), 1119 } 1120 return statusHistory(args) 1121 } 1122 1123 func ApplicationOperatorStatus(st *State, appName string) (status.StatusInfo, error) { 1124 return getStatus(st.db(), applicationGlobalOperatorKey(appName), "operator") 1125 } 1126 1127 func NewInstanceCharmProfileDataCompatibilityWatcher(backend ModelBackendShim, memberId string) StringsWatcher { 1128 return watchInstanceCharmProfileCompatibilityData(backend, memberId) 1129 } 1130 1131 func UnitBranch(m *Model, unitName string) (*Generation, error) { 1132 return m.unitBranch(unitName) 1133 } 1134 1135 func ApplicationBranches(m *Model, appName string) ([]*Generation, error) { 1136 return m.applicationBranches(appName) 1137 } 1138 1139 func MachinePortOps(st *State, m description.Machine) ([]txn.Op, error) { 1140 resolver := &importer{st: st} 1141 return []txn.Op{resolver.machinePortsOp(m)}, nil 1142 } 1143 1144 func ApplicationPortOps(st *State, a description.Application) ([]txn.Op, error) { 1145 resolver := &importer{st: st} 1146 return []txn.Op{resolver.applicationPortsOp(a)}, nil 1147 } 1148 1149 func GetSecretNextRotateTime(c *gc.C, st *State, id string) time.Time { 1150 secretRotateCollection, closer := st.db().GetCollection(secretRotateC) 1151 defer closer() 1152 1153 var doc secretRotationDoc 1154 err := secretRotateCollection.FindId(id).One(&doc) 1155 c.Assert(err, jc.ErrorIsNil) 1156 return doc.NextRotateTime.UTC() 1157 } 1158 1159 func GetSecretBackendNextRotateInfo(c *gc.C, st *State, id string) (string, time.Time) { 1160 secretBackendRotateCollection, closer := st.db().GetCollection(secretBackendsRotateC) 1161 defer closer() 1162 1163 var doc secretBackendRotationDoc 1164 err := secretBackendRotateCollection.FindId(id).One(&doc) 1165 c.Assert(err, jc.ErrorIsNil) 1166 return doc.Name, doc.NextRotateTime.UTC() 1167 } 1168 1169 // ModelBackendShim is required to live here in the export_test.go file because 1170 // there is issues placing this in the test files themselves. The strangeness 1171 // exhibits itself from the fact that `clock() clock.Clock` doesn't type 1172 // check correctly and the go compiler thinks it should be 1173 // `state.clock() clock.Clock`, which makes no sense. 1174 type ModelBackendShim struct { 1175 Clock clock.Clock 1176 Database Database 1177 Watcher watcher.BaseWatcher 1178 } 1179 1180 func (s ModelBackendShim) docID(id string) string { 1181 return "" 1182 } 1183 1184 func (s ModelBackendShim) localID(id string) string { 1185 return "" 1186 } 1187 1188 func (s ModelBackendShim) strictLocalID(id string) (string, error) { 1189 return "", nil 1190 } 1191 1192 func (s ModelBackendShim) nowToTheSecond() time.Time { 1193 return s.Clock.Now().Round(time.Second).UTC() 1194 } 1195 1196 func (s ModelBackendShim) clock() clock.Clock { 1197 return s.Clock 1198 } 1199 1200 func (s ModelBackendShim) db() Database { 1201 return s.Database 1202 } 1203 1204 func (s ModelBackendShim) ModelUUID() string { 1205 return "" 1206 } 1207 1208 func (s ModelBackendShim) modelName() (string, error) { 1209 return "", nil 1210 } 1211 1212 func (s ModelBackendShim) IsController() bool { 1213 return false 1214 } 1215 1216 func (s ModelBackendShim) txnLogWatcher() watcher.BaseWatcher { 1217 return s.Watcher 1218 } 1219 1220 // SetClockForTesting is an exported function to allow tests 1221 // to set the internal clock for the State instance. It is named such 1222 // that it should be obvious if it is ever called from a non-test package. 1223 // TODO (thumper): This is a terrible method and we should remove it. 1224 // NOTE: this should almost never be needed. 1225 func (st *State) SetClockForTesting(clock clock.Clock) error { 1226 // Need to restart the lease workers so they get the new clock. 1227 // Stop them first so they don't try to use it when we're setting it. 1228 hub := st.workers.hub 1229 st.workers.Kill() 1230 err := st.workers.Wait() 1231 if err != nil { 1232 return errors.Trace(err) 1233 } 1234 st.stateClock = clock 1235 if db, ok := st.database.(*database); ok { 1236 db.clock = clock 1237 } 1238 err = st.startWorkers(hub) 1239 if err != nil { 1240 return errors.Trace(err) 1241 } 1242 return nil 1243 } 1244 1245 var ( 1246 CleanupForceDestroyedUnit = cleanupForceDestroyedUnit 1247 CleanupForceRemoveUnit = cleanupForceRemoveUnit 1248 CleanupForceApplication = cleanupForceApplication 1249 ) 1250 1251 func (st *State) ScheduleForceCleanup(kind cleanupKind, name string, maxWait time.Duration) { 1252 st.scheduleForceCleanup(kind, name, maxWait) 1253 } 1254 1255 func GetCollectionCappedInfo(coll *mgo.Collection) (bool, int, error) { 1256 return getCollectionCappedInfo(coll) 1257 } 1258 1259 func (m *Model) AllActionIDsHasActionNotifications() ([]string, error) { 1260 actionNotifications, closer := m.st.db().GetCollection(actionNotificationsC) 1261 defer closer() 1262 1263 docs := []actionNotificationDoc{} 1264 err := actionNotifications.Find(nil).All(&docs) 1265 if err != nil { 1266 return nil, errors.Annotatef(err, "cannot get all actions") 1267 } 1268 actionIDs := make([]string, len(docs)) 1269 for i, doc := range docs { 1270 actionIDs[i] = doc.ActionID 1271 } 1272 return actionIDs, nil 1273 }