github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/state.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package state enables reading, observing, and changing 5 // the state stored in MongoDB of a whole model 6 // managed by juju. 7 package state 8 9 import ( 10 "fmt" 11 "regexp" 12 "sort" 13 "strconv" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/juju/errors" 19 "github.com/juju/loggo" 20 jujutxn "github.com/juju/txn" 21 "github.com/juju/utils" 22 "github.com/juju/utils/clock" 23 "github.com/juju/utils/os" 24 "github.com/juju/utils/series" 25 "github.com/juju/utils/set" 26 "github.com/juju/version" 27 "gopkg.in/juju/charm.v6-unstable" 28 csparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params" 29 "gopkg.in/juju/names.v2" 30 "gopkg.in/mgo.v2" 31 "gopkg.in/mgo.v2/bson" 32 "gopkg.in/mgo.v2/txn" 33 34 "github.com/juju/juju/apiserver/params" 35 "github.com/juju/juju/audit" 36 "github.com/juju/juju/constraints" 37 "github.com/juju/juju/core/lease" 38 "github.com/juju/juju/instance" 39 "github.com/juju/juju/mongo" 40 "github.com/juju/juju/network" 41 "github.com/juju/juju/permission" 42 "github.com/juju/juju/state/cloudimagemetadata" 43 stateaudit "github.com/juju/juju/state/internal/audit" 44 statelease "github.com/juju/juju/state/lease" 45 "github.com/juju/juju/state/workers" 46 "github.com/juju/juju/status" 47 jujuversion "github.com/juju/juju/version" 48 ) 49 50 var logger = loggo.GetLogger("juju.state") 51 52 const ( 53 // jujuDB is the name of the main juju database. 54 jujuDB = "juju" 55 56 // presenceDB is the name of the database used to hold presence pinger data. 57 presenceDB = "presence" 58 presenceC = "presence" 59 60 // blobstoreDB is the name of the blobstore GridFS database. 61 blobstoreDB = "blobstore" 62 63 // applicationLeadershipNamespace is the name of the lease.Client namespace 64 // used by the leadership manager. 65 applicationLeadershipNamespace = "application-leadership" 66 67 // singularControllerNamespace is the name of the lease.Client namespace 68 // used by the singular manager 69 singularControllerNamespace = "singular-controller" 70 ) 71 72 type providerIdDoc struct { 73 ID string `bson:"_id"` // format: "<model-uuid>:<global-key>:<provider-id>" 74 } 75 76 // State represents the state of an model 77 // managed by juju. 78 type State struct { 79 clock clock.Clock 80 modelTag names.ModelTag 81 controllerModelTag names.ModelTag 82 controllerTag names.ControllerTag 83 mongoInfo *mongo.MongoInfo 84 session *mgo.Session 85 database Database 86 policy Policy 87 newPolicy NewPolicyFunc 88 89 // cloudName is the name of the cloud on which the model 90 // represented by this state runs. 91 cloudName string 92 93 // leaseClientId is used by the lease infrastructure to 94 // differentiate between machines whose clocks may be 95 // relatively-skewed. 96 leaseClientId string 97 98 // workers is responsible for keeping the various sub-workers 99 // available by starting new ones as they fail. It doesn't do 100 // that yet, but having a type that collects them together is the 101 // first step. 102 // 103 // note that the allManager stuff below probably ought to be 104 // folded in as well, but that feels like its own task. 105 workers workers.Workers 106 107 // mu guards allManager, allModelManager & allModelWatcherBacking 108 mu sync.Mutex 109 allManager *storeManager 110 allModelManager *storeManager 111 allModelWatcherBacking Backing 112 113 // TODO(anastasiamac 2015-07-16) As state gets broken up, remove this. 114 CloudImageMetadataStorage cloudimagemetadata.Storage 115 } 116 117 // StateServingInfo holds information needed by a controller. 118 // This type is a copy of the type of the same name from the api/params package. 119 // It is replicated here to avoid the state pacakge depending on api/params. 120 // 121 // NOTE(fwereade): the api/params type exists *purely* for representing 122 // this data over the wire, and has a legitimate reason to exist. This 123 // type does not: it's non-implementation-specific and shoudl be defined 124 // under core/ somewhere, so it can be used both here and in the agent 125 // without dragging unnecessary/irrelevant packages into scope. 126 type StateServingInfo struct { 127 APIPort int 128 StatePort int 129 Cert string 130 PrivateKey string 131 CAPrivateKey string 132 // this will be passed as the KeyFile argument to MongoDB 133 SharedSecret string 134 SystemIdentity string 135 } 136 137 // IsController returns true if this state instance has the bootstrap 138 // model UUID. 139 func (st *State) IsController() bool { 140 return st.modelTag == st.controllerModelTag 141 } 142 143 // ControllerUUID returns the UUID for the controller 144 // of this state instance. 145 func (st *State) ControllerUUID() string { 146 return st.controllerTag.Id() 147 } 148 func (st *State) ControllerTag() names.ControllerTag { 149 return st.controllerTag 150 } 151 152 func ControllerAccess(st *State, tag names.Tag) (permission.UserAccess, error) { 153 return st.UserAccess(tag.(names.UserTag), st.controllerTag) 154 } 155 156 // RemoveAllModelDocs removes all documents from multi-model 157 // collections. The model should be put into a dying state before call 158 // this method. Otherwise, there is a race condition in which collections 159 // could be added to during or after the running of this method. 160 func (st *State) RemoveAllModelDocs() error { 161 err := st.removeAllModelDocs(bson.D{{"life", Dead}}) 162 if errors.Cause(err) == txn.ErrAborted { 163 return errors.New("can't remove model: model not dead") 164 } 165 return errors.Trace(err) 166 } 167 168 // RemoveImportingModelDocs removes all documents from multi-model collections 169 // for the current model. This method asserts that the model's migration mode 170 // is "importing". 171 func (st *State) RemoveImportingModelDocs() error { 172 err := st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeImporting}}) 173 if errors.Cause(err) == txn.ErrAborted { 174 return errors.New("can't remove model: model not being imported for migration") 175 } 176 return errors.Trace(err) 177 } 178 179 // RemoveExportingModelDocs removes all documents from multi-model collections 180 // for the current model. This method asserts that the model's migration mode 181 // is "exporting". 182 func (st *State) RemoveExportingModelDocs() error { 183 err := st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeExporting}}) 184 if errors.Cause(err) == txn.ErrAborted { 185 return errors.New("can't remove model: model not being exported for migration") 186 } 187 return errors.Trace(err) 188 } 189 190 func (st *State) removeAllModelDocs(modelAssertion bson.D) error { 191 modelUUID := st.ModelUUID() 192 193 // Remove each collection in its own transaction. 194 for name, info := range st.database.Schema() { 195 if info.global || info.rawAccess { 196 continue 197 } 198 199 ops, err := st.removeAllInCollectionOps(name) 200 if err != nil { 201 return errors.Trace(err) 202 } 203 // Make sure we gate everything on the model assertion. 204 ops = append([]txn.Op{{ 205 C: modelsC, 206 Id: modelUUID, 207 Assert: modelAssertion, 208 }}, ops...) 209 err = st.runTransaction(ops) 210 if err != nil { 211 return errors.Trace(err) 212 } 213 } 214 215 // Remove from the raw (non-transactional) collections. 216 for name, info := range st.database.Schema() { 217 if !info.global && info.rawAccess { 218 if err := st.removeAllInCollectionRaw(name); err != nil { 219 return errors.Trace(err) 220 } 221 } 222 } 223 224 // Remove all user permissions for the model. 225 permPattern := bson.M{ 226 "_id": bson.M{"$regex": "^" + permissionID(modelKey(modelUUID), "")}, 227 } 228 ops, err := st.removeInCollectionOps(permissionsC, permPattern) 229 if err != nil { 230 return errors.Trace(err) 231 } 232 err = st.runTransaction(ops) 233 if err != nil { 234 return errors.Trace(err) 235 } 236 237 // Now remove remove the model. 238 env, err := st.Model() 239 if err != nil { 240 return errors.Trace(err) 241 } 242 id := userModelNameIndex(env.Owner().Canonical(), env.Name()) 243 ops = []txn.Op{{ 244 // Cleanup the owner:envName unique key. 245 C: usermodelnameC, 246 Id: id, 247 Remove: true, 248 }, { 249 C: modelEntityRefsC, 250 Id: modelUUID, 251 Remove: true, 252 }, { 253 C: modelsC, 254 Id: modelUUID, 255 Assert: modelAssertion, 256 Remove: true, 257 }} 258 if !st.IsController() { 259 ops = append(ops, decHostedModelCountOp()) 260 } 261 return st.runTransaction(ops) 262 } 263 264 // removeAllInCollectionRaw removes all the documents from the given 265 // named collection. 266 func (st *State) removeAllInCollectionRaw(name string) error { 267 coll, closer := st.getCollection(name) 268 defer closer() 269 _, err := coll.Writeable().RemoveAll(nil) 270 return errors.Trace(err) 271 } 272 273 // removeAllInCollectionOps appends to ops operations to 274 // remove all the documents in the given named collection. 275 func (st *State) removeAllInCollectionOps(name string) ([]txn.Op, error) { 276 return st.removeInCollectionOps(name, nil) 277 } 278 279 // removeInCollectionOps generates operations to remove all documents 280 // from the named collection matching a specific selector. 281 func (st *State) removeInCollectionOps(name string, sel interface{}) ([]txn.Op, error) { 282 coll, closer := st.getCollection(name) 283 defer closer() 284 285 var ids []bson.M 286 err := coll.Find(sel).Select(bson.D{{"_id", 1}}).All(&ids) 287 if err != nil { 288 return nil, errors.Trace(err) 289 } 290 var ops []txn.Op 291 for _, id := range ids { 292 ops = append(ops, txn.Op{ 293 C: name, 294 Id: id["_id"], 295 Remove: true, 296 }) 297 } 298 return ops, nil 299 } 300 301 // ForModel returns a connection to mongo for the specified model. The 302 // connection uses the same credentials and policy as the existing connection. 303 func (st *State) ForModel(modelTag names.ModelTag) (*State, error) { 304 session := st.session.Copy() 305 newSt, err := newState( 306 modelTag, st.controllerModelTag, session, st.mongoInfo, st.newPolicy, st.clock, 307 ) 308 if err != nil { 309 return nil, errors.Trace(err) 310 } 311 if err := newSt.start(st.controllerTag); err != nil { 312 return nil, errors.Trace(err) 313 } 314 return newSt, nil 315 } 316 317 // start makes a *State functional post-creation, by: 318 // * setting controllerTag, cloudName and leaseClientId 319 // * starting lease managers and watcher backends 320 // * creating cloud metadata storage 321 // 322 // start will close the *State if it fails. 323 func (st *State) start(controllerTag names.ControllerTag) (err error) { 324 defer func() { 325 if err == nil { 326 return 327 } 328 if err2 := st.Close(); err2 != nil { 329 logger.Errorf("closing State for %s: %v", st.modelTag, err2) 330 } 331 }() 332 333 st.controllerTag = controllerTag 334 335 if identity := st.mongoInfo.Tag; identity != nil { 336 // TODO(fwereade): it feels a bit wrong to take this from MongoInfo -- I 337 // think it's just coincidental that the mongodb user happens to map to 338 // the machine that's executing the code -- but there doesn't seem to be 339 // an accessible alternative. 340 st.leaseClientId = identity.String() 341 } else { 342 // If we're running state anonymously, we can still use the lease 343 // manager; but we need to make sure we use a unique client ID, and 344 // will thus not be very performant. 345 logger.Infof("running state anonymously; using unique client id") 346 uuid, err := utils.NewUUID() 347 if err != nil { 348 return errors.Trace(err) 349 } 350 st.leaseClientId = fmt.Sprintf("anon-%s", uuid.String()) 351 } 352 // now we've set up leaseClientId, we can use workersFactory 353 354 logger.Infof("starting standard state workers") 355 factory := workersFactory{ 356 st: st, 357 clock: st.clock, 358 } 359 workers, err := workers.NewRestartWorkers(workers.RestartConfig{ 360 Factory: factory, 361 Logger: loggo.GetLogger(logger.Name() + ".workers"), 362 Clock: st.clock, 363 Delay: time.Second, 364 }) 365 if err != nil { 366 return errors.Annotatef(err, "cannot create standard state workers") 367 } 368 st.workers = workers 369 370 logger.Infof("creating cloud image metadata storage") 371 st.CloudImageMetadataStorage = cloudimagemetadata.NewStorage( 372 cloudimagemetadataC, 373 &environMongo{st}, 374 ) 375 376 logger.Infof("started state for %s successfully", st.modelTag) 377 return nil 378 } 379 380 // ApplicationLeaders returns a map of the application name to the 381 // unit name that is the current leader. 382 func (st *State) ApplicationLeaders() (map[string]string, error) { 383 client, err := st.getLeadershipLeaseClient() 384 if err != nil { 385 return nil, errors.Trace(err) 386 } 387 leases := client.Leases() 388 result := make(map[string]string, len(leases)) 389 for key, value := range leases { 390 result[key] = value.Holder 391 } 392 return result, nil 393 } 394 395 func (st *State) getLeadershipLeaseClient() (lease.Client, error) { 396 client, err := statelease.NewClient(statelease.ClientConfig{ 397 Id: st.leaseClientId, 398 Namespace: applicationLeadershipNamespace, 399 Collection: leasesC, 400 Mongo: &environMongo{st}, 401 Clock: st.clock, 402 }) 403 if err != nil { 404 return nil, errors.Annotatef(err, "cannot create leadership lease client") 405 } 406 return client, nil 407 } 408 409 func (st *State) getSingularLeaseClient() (lease.Client, error) { 410 client, err := statelease.NewClient(statelease.ClientConfig{ 411 Id: st.leaseClientId, 412 Namespace: singularControllerNamespace, 413 Collection: leasesC, 414 Mongo: &environMongo{st}, 415 Clock: st.clock, 416 }) 417 if err != nil { 418 return nil, errors.Annotatef(err, "cannot create singular lease client") 419 } 420 return client, nil 421 } 422 423 // ModelTag() returns the model tag for the model controlled by 424 // this state instance. 425 func (st *State) ModelTag() names.ModelTag { 426 return st.modelTag 427 } 428 429 // ModelUUID returns the model UUID for the model 430 // controlled by this state instance. 431 func (st *State) ModelUUID() string { 432 return st.modelTag.Id() 433 } 434 435 // userModelNameIndex returns a string to be used as a usermodelnameC unique index. 436 func userModelNameIndex(username, envName string) string { 437 return strings.ToLower(username) + ":" + envName 438 } 439 440 // EnsureModelRemoved returns an error if any multi-model 441 // documents for this model are found. It is intended only to be used in 442 // tests and exported so it can be used in the tests of other packages. 443 func (st *State) EnsureModelRemoved() error { 444 found := map[string]int{} 445 var foundOrdered []string 446 for name, info := range st.database.Schema() { 447 if info.global { 448 continue 449 } 450 coll, closer := st.getCollection(name) 451 defer closer() 452 n, err := coll.Find(nil).Count() 453 if err != nil { 454 return errors.Trace(err) 455 } 456 if n != 0 { 457 found[name] = n 458 foundOrdered = append(foundOrdered, name) 459 } 460 } 461 462 if len(found) != 0 { 463 errMessage := fmt.Sprintf("found documents for model with uuid %s:", st.ModelUUID()) 464 sort.Strings(foundOrdered) 465 for _, name := range foundOrdered { 466 number := found[name] 467 errMessage += fmt.Sprintf(" %d %s doc,", number, name) 468 } 469 // Remove trailing comma. 470 errMessage = errMessage[:len(errMessage)-1] 471 return errors.New(errMessage) 472 } 473 return nil 474 } 475 476 // getPresenceCollection returns the raw mongodb presence collection, 477 // which is needed to interact with the state/presence package. 478 func (st *State) getPresenceCollection() *mgo.Collection { 479 return st.session.DB(presenceDB).C(presenceC) 480 } 481 482 // getTxnLogCollection returns the raw mongodb txns collection, which is 483 // needed to interact with the state/watcher package. 484 func (st *State) getTxnLogCollection() *mgo.Collection { 485 return st.session.DB(jujuDB).C(txnLogC) 486 } 487 488 // newDB returns a database connection using a new session, along with 489 // a closer function for the session. This is useful where you need to work 490 // with various collections in a single session, so don't want to call 491 // getCollection multiple times. 492 func (st *State) newDB() (Database, func()) { 493 return st.database.Copy() 494 } 495 496 // Ping probes the state's database connection to ensure 497 // that it is still alive. 498 func (st *State) Ping() error { 499 return st.session.Ping() 500 } 501 502 // MongoVersion return the string repre 503 func (st *State) MongoVersion() (string, error) { 504 binfo, err := st.session.BuildInfo() 505 if err != nil { 506 return "", errors.Annotate(err, "cannot obtain mongo build info") 507 } 508 return binfo.Version, nil 509 } 510 511 // MongoSession returns the underlying mongodb session 512 // used by the state. It is exposed so that external code 513 // can maintain the mongo replica set and should not 514 // otherwise be used. 515 func (st *State) MongoSession() *mgo.Session { 516 return st.session 517 } 518 519 func (st *State) Watch() *Multiwatcher { 520 st.mu.Lock() 521 if st.allManager == nil { 522 st.allManager = newStoreManager(newAllWatcherStateBacking(st)) 523 } 524 st.mu.Unlock() 525 return NewMultiwatcher(st.allManager) 526 } 527 528 func (st *State) WatchAllModels() *Multiwatcher { 529 st.mu.Lock() 530 if st.allModelManager == nil { 531 st.allModelWatcherBacking = NewAllModelWatcherStateBacking(st) 532 st.allModelManager = newStoreManager(st.allModelWatcherBacking) 533 } 534 st.mu.Unlock() 535 return NewMultiwatcher(st.allModelManager) 536 } 537 538 // versionInconsistentError indicates one or more agents have a 539 // different version from the current one (even empty, when not yet 540 // set). 541 type versionInconsistentError struct { 542 currentVersion version.Number 543 agents []string 544 } 545 546 func (e *versionInconsistentError) Error() string { 547 sort.Strings(e.agents) 548 return fmt.Sprintf("some agents have not upgraded to the current model version %s: %s", e.currentVersion, strings.Join(e.agents, ", ")) 549 } 550 551 // newVersionInconsistentError returns a new instance of 552 // versionInconsistentError. 553 func newVersionInconsistentError(currentVersion version.Number, agents []string) *versionInconsistentError { 554 return &versionInconsistentError{currentVersion, agents} 555 } 556 557 // IsVersionInconsistentError returns if the given error is 558 // versionInconsistentError. 559 func IsVersionInconsistentError(e interface{}) bool { 560 value := e 561 // In case of a wrapped error, check the cause first. 562 cause := errors.Cause(e.(error)) 563 if cause != nil { 564 value = cause 565 } 566 _, ok := value.(*versionInconsistentError) 567 return ok 568 } 569 570 func (st *State) checkCanUpgrade(currentVersion, newVersion string) error { 571 matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-" 572 matchNew := "^" + regexp.QuoteMeta(newVersion) + "-" 573 // Get all machines and units with a different or empty version. 574 sel := bson.D{{"$or", []bson.D{ 575 {{"tools", bson.D{{"$exists", false}}}}, 576 {{"$and", []bson.D{ 577 {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}}, 578 {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}}, 579 }}}, 580 }}} 581 var agentTags []string 582 for _, name := range []string{machinesC, unitsC} { 583 collection, closer := st.getCollection(name) 584 defer closer() 585 var doc struct { 586 DocID string `bson:"_id"` 587 } 588 iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter() 589 for iter.Next(&doc) { 590 localID, err := st.strictLocalID(doc.DocID) 591 if err != nil { 592 return errors.Trace(err) 593 } 594 switch name { 595 case machinesC: 596 agentTags = append(agentTags, names.NewMachineTag(localID).String()) 597 case unitsC: 598 agentTags = append(agentTags, names.NewUnitTag(localID).String()) 599 } 600 } 601 if err := iter.Close(); err != nil { 602 return errors.Trace(err) 603 } 604 } 605 if len(agentTags) > 0 { 606 err := newVersionInconsistentError(version.MustParse(currentVersion), agentTags) 607 return errors.Trace(err) 608 } 609 return nil 610 } 611 612 var errUpgradeInProgress = errors.New(params.CodeUpgradeInProgress) 613 614 // IsUpgradeInProgressError returns true if the error is caused by an 615 // in-progress upgrade. 616 func IsUpgradeInProgressError(err error) bool { 617 return errors.Cause(err) == errUpgradeInProgress 618 } 619 620 // SetModelAgentVersion changes the agent version for the model to the 621 // given version, only if the model is in a stable state (all agents are 622 // running the current version). If this is a hosted model, newVersion 623 // cannot be higher than the controller version. 624 func (st *State) SetModelAgentVersion(newVersion version.Number) (err error) { 625 if newVersion.Compare(jujuversion.Current) > 0 && !st.IsController() { 626 return errors.Errorf("a hosted model cannot have a higher version than the server model: %s > %s", 627 newVersion.String(), 628 jujuversion.Current, 629 ) 630 } 631 632 buildTxn := func(attempt int) ([]txn.Op, error) { 633 settings, err := readSettings(st, settingsC, modelGlobalKey) 634 if err != nil { 635 return nil, errors.Trace(err) 636 } 637 agentVersion, ok := settings.Get("agent-version") 638 if !ok { 639 return nil, errors.Errorf("no agent version set in the model") 640 } 641 currentVersion, ok := agentVersion.(string) 642 if !ok { 643 return nil, errors.Errorf("invalid agent version format: expected string, got %v", agentVersion) 644 } 645 if newVersion.String() == currentVersion { 646 // Nothing to do. 647 return nil, jujutxn.ErrNoOperations 648 } 649 650 if err := st.checkCanUpgrade(currentVersion, newVersion.String()); err != nil { 651 return nil, errors.Trace(err) 652 } 653 654 ops := []txn.Op{ 655 // Can't set agent-version if there's an active upgradeInfo doc. 656 { 657 C: upgradeInfoC, 658 Id: currentUpgradeId, 659 Assert: txn.DocMissing, 660 }, { 661 C: settingsC, 662 Id: st.docID(modelGlobalKey), 663 Assert: bson.D{{"version", settings.version}}, 664 Update: bson.D{ 665 {"$set", bson.D{{"settings.agent-version", newVersion.String()}}}, 666 }, 667 }, 668 } 669 return ops, nil 670 } 671 if err = st.run(buildTxn); err == jujutxn.ErrExcessiveContention { 672 // Although there is a small chance of a race here, try to 673 // return a more helpful error message in the case of an 674 // active upgradeInfo document being in place. 675 if upgrading, _ := st.IsUpgrading(); upgrading { 676 err = errUpgradeInProgress 677 } else { 678 err = errors.Annotate(err, "cannot set agent version") 679 } 680 } 681 return errors.Trace(err) 682 } 683 684 // ModelConstraints returns the current model constraints. 685 func (st *State) ModelConstraints() (constraints.Value, error) { 686 cons, err := readConstraints(st, modelGlobalKey) 687 return cons, errors.Trace(err) 688 } 689 690 // SetModelConstraints replaces the current model constraints. 691 func (st *State) SetModelConstraints(cons constraints.Value) error { 692 unsupported, err := st.validateConstraints(cons) 693 if len(unsupported) > 0 { 694 logger.Warningf( 695 "setting model constraints: unsupported constraints: %v", strings.Join(unsupported, ",")) 696 } else if err != nil { 697 return errors.Trace(err) 698 } 699 return writeConstraints(st, modelGlobalKey, cons) 700 } 701 702 func (st *State) allMachines(machinesCollection mongo.Collection) ([]*Machine, error) { 703 mdocs := machineDocSlice{} 704 err := machinesCollection.Find(nil).All(&mdocs) 705 if err != nil { 706 return nil, errors.Annotatef(err, "cannot get all machines") 707 } 708 sort.Sort(mdocs) 709 machines := make([]*Machine, len(mdocs)) 710 for i, doc := range mdocs { 711 machines[i] = newMachine(st, &doc) 712 } 713 return machines, nil 714 } 715 716 // AllMachines returns all machines in the model 717 // ordered by id. 718 func (st *State) AllMachines() ([]*Machine, error) { 719 machinesCollection, closer := st.getCollection(machinesC) 720 defer closer() 721 return st.allMachines(machinesCollection) 722 } 723 724 // AllMachinesFor returns all machines for the model represented 725 // by the given modeluuid 726 func (st *State) AllMachinesFor(modelUUID string) ([]*Machine, error) { 727 machinesCollection, closer := st.getCollectionFor(modelUUID, machinesC) 728 defer closer() 729 return st.allMachines(machinesCollection) 730 } 731 732 type machineDocSlice []machineDoc 733 734 func (ms machineDocSlice) Len() int { return len(ms) } 735 func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } 736 func (ms machineDocSlice) Less(i, j int) bool { 737 return machineIdLessThan(ms[i].Id, ms[j].Id) 738 } 739 740 // machineIdLessThan returns true if id1 < id2, false otherwise. 741 // Machine ids may include "/" separators if they are for a container so 742 // the comparison is done by comparing the id component values from 743 // left to right (most significant part to least significant). Ids for 744 // host machines are always less than ids for their containers. 745 func machineIdLessThan(id1, id2 string) bool { 746 // Most times, we are dealing with host machines and not containers, so we will 747 // try interpreting the ids as ints - this will be faster than dealing with the 748 // container ids below. 749 mint1, err1 := strconv.Atoi(id1) 750 mint2, err2 := strconv.Atoi(id2) 751 if err1 == nil && err2 == nil { 752 return mint1 < mint2 753 } 754 // We have at least one container id so it gets complicated. 755 idParts1 := strings.Split(id1, "/") 756 idParts2 := strings.Split(id2, "/") 757 nrParts1 := len(idParts1) 758 nrParts2 := len(idParts2) 759 minLen := nrParts1 760 if nrParts2 < minLen { 761 minLen = nrParts2 762 } 763 for x := 0; x < minLen; x++ { 764 m1 := idParts1[x] 765 m2 := idParts2[x] 766 if m1 == m2 { 767 continue 768 } 769 // See if the id part is a container type, and if so compare directly. 770 if x%2 == 1 { 771 return m1 < m2 772 } 773 // Compare the integer ids. 774 // There's nothing we can do with errors at this point. 775 mint1, _ := strconv.Atoi(m1) 776 mint2, _ := strconv.Atoi(m2) 777 return mint1 < mint2 778 } 779 return nrParts1 < nrParts2 780 } 781 782 // Machine returns the machine with the given id. 783 func (st *State) Machine(id string) (*Machine, error) { 784 mdoc, err := st.getMachineDoc(id) 785 if err != nil { 786 return nil, err 787 } 788 return newMachine(st, mdoc), nil 789 } 790 791 func (st *State) getMachineDoc(id string) (*machineDoc, error) { 792 machinesCollection, closer := st.getCollection(machinesC) 793 defer closer() 794 795 var err error 796 mdoc := &machineDoc{} 797 err = machinesCollection.FindId(id).One(mdoc) 798 799 switch err { 800 case nil: 801 return mdoc, nil 802 case mgo.ErrNotFound: 803 return nil, errors.NotFoundf("machine %s", id) 804 default: 805 return nil, errors.Annotatef(err, "cannot get machine %s", id) 806 } 807 } 808 809 // FindEntity returns the entity with the given tag. 810 // 811 // The returned value can be of type *Machine, *Unit, 812 // *User, *Service, *Model, or *Action, depending 813 // on the tag. 814 func (st *State) FindEntity(tag names.Tag) (Entity, error) { 815 id := tag.Id() 816 switch tag := tag.(type) { 817 case names.MachineTag: 818 return st.Machine(id) 819 case names.UnitTag: 820 return st.Unit(id) 821 case names.UserTag: 822 return st.User(tag) 823 case names.ApplicationTag: 824 return st.Application(id) 825 case names.ModelTag: 826 env, err := st.Model() 827 if err != nil { 828 return nil, errors.Trace(err) 829 } 830 // Return an invalid entity error if the requested model is not 831 // the current one. 832 if id != env.UUID() { 833 if utils.IsValidUUIDString(id) { 834 return nil, errors.NotFoundf("model %q", id) 835 } 836 // TODO(axw) 2013-12-04 #1257587 837 // We should not accept model tags that do not match the 838 // model's UUID. We accept anything for now, to cater 839 // both for past usage, and for potentially supporting aliases. 840 logger.Warningf("model-tag does not match current model UUID: %q != %q", id, env.UUID()) 841 conf, err := st.ModelConfig() 842 if err != nil { 843 logger.Warningf("ModelConfig failed: %v", err) 844 } else if id != conf.Name() { 845 logger.Warningf("model-tag does not match current model name: %q != %q", id, conf.Name()) 846 } 847 } 848 return env, nil 849 case names.RelationTag: 850 return st.KeyRelation(id) 851 case names.ActionTag: 852 return st.ActionByTag(tag) 853 case names.CharmTag: 854 if url, err := charm.ParseURL(id); err != nil { 855 logger.Warningf("Parsing charm URL %q failed: %v", id, err) 856 return nil, errors.NotFoundf("could not find charm %q in state", id) 857 } else { 858 return st.Charm(url) 859 } 860 case names.VolumeTag: 861 return st.Volume(tag) 862 case names.FilesystemTag: 863 return st.Filesystem(tag) 864 default: 865 return nil, errors.Errorf("unsupported tag %T", tag) 866 } 867 } 868 869 // tagToCollectionAndId, given an entity tag, returns the collection name and id 870 // of the entity document. 871 func (st *State) tagToCollectionAndId(tag names.Tag) (string, interface{}, error) { 872 if tag == nil { 873 return "", nil, errors.Errorf("tag is nil") 874 } 875 coll := "" 876 id := tag.Id() 877 switch tag := tag.(type) { 878 case names.MachineTag: 879 coll = machinesC 880 id = st.docID(id) 881 case names.ApplicationTag: 882 coll = applicationsC 883 id = st.docID(id) 884 case names.UnitTag: 885 coll = unitsC 886 id = st.docID(id) 887 case names.UserTag: 888 coll = usersC 889 if !tag.IsLocal() { 890 return "", nil, fmt.Errorf("%q is not a local user", tag.Canonical()) 891 } 892 id = tag.Name() 893 case names.RelationTag: 894 coll = relationsC 895 id = st.docID(id) 896 case names.ModelTag: 897 coll = modelsC 898 case names.ActionTag: 899 coll = actionsC 900 id = tag.Id() 901 case names.CharmTag: 902 coll = charmsC 903 id = tag.Id() 904 default: 905 return "", nil, errors.Errorf("%q is not a valid collection tag", tag) 906 } 907 return coll, id, nil 908 } 909 910 // addPeerRelationsOps returns the operations necessary to add the 911 // specified service peer relations to the state. 912 func (st *State) addPeerRelationsOps(applicationname string, peers map[string]charm.Relation) ([]txn.Op, error) { 913 var ops []txn.Op 914 for _, rel := range peers { 915 relId, err := st.sequence("relation") 916 if err != nil { 917 return nil, errors.Trace(err) 918 } 919 eps := []Endpoint{{ 920 ApplicationName: applicationname, 921 Relation: rel, 922 }} 923 relKey := relationKey(eps) 924 relDoc := &relationDoc{ 925 DocID: st.docID(relKey), 926 Key: relKey, 927 ModelUUID: st.ModelUUID(), 928 Id: relId, 929 Endpoints: eps, 930 Life: Alive, 931 } 932 ops = append(ops, txn.Op{ 933 C: relationsC, 934 Id: relDoc.DocID, 935 Assert: txn.DocMissing, 936 Insert: relDoc, 937 }) 938 } 939 return ops, nil 940 } 941 942 type AddApplicationArgs struct { 943 Name string 944 Series string 945 Charm *Charm 946 Channel csparams.Channel 947 Storage map[string]StorageConstraints 948 EndpointBindings map[string]string 949 Settings charm.Settings 950 NumUnits int 951 Placement []*instance.Placement 952 Constraints constraints.Value 953 Resources map[string]string 954 } 955 956 // AddApplication creates a new application, running the supplied charm, with the 957 // supplied name (which must be unique). If the charm defines peer relations, 958 // they will be created automatically. 959 func (st *State) AddApplication(args AddApplicationArgs) (_ *Application, err error) { 960 defer errors.DeferredAnnotatef(&err, "cannot add application %q", args.Name) 961 // Sanity checks. 962 if !names.IsValidApplication(args.Name) { 963 return nil, errors.Errorf("invalid name") 964 } 965 if args.Charm == nil { 966 return nil, errors.Errorf("charm is nil") 967 } 968 969 if err := validateCharmVersion(args.Charm); err != nil { 970 return nil, errors.Trace(err) 971 } 972 973 if exists, err := isNotDead(st, applicationsC, args.Name); err != nil { 974 return nil, errors.Trace(err) 975 } else if exists { 976 return nil, errors.Errorf("application already exists") 977 } 978 if err := checkModelActive(st); err != nil { 979 return nil, errors.Trace(err) 980 } 981 if args.Storage == nil { 982 args.Storage = make(map[string]StorageConstraints) 983 } 984 if err := addDefaultStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil { 985 return nil, errors.Trace(err) 986 } 987 if err := validateStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil { 988 return nil, errors.Trace(err) 989 } 990 storagePools := make(set.Strings) 991 for _, storageParams := range args.Storage { 992 storagePools.Add(storageParams.Pool) 993 } 994 995 if args.Series == "" { 996 // args.Series is not set, so use the series in the URL. 997 args.Series = args.Charm.URL().Series 998 if args.Series == "" { 999 // Should not happen, but just in case. 1000 return nil, errors.New("series is empty") 1001 } 1002 } else { 1003 // User has specified series. Overriding supported series is 1004 // handled by the client, so args.Series is not necessarily 1005 // one of the charm's supported series. We require that the 1006 // specified series is of the same operating system as one of 1007 // the supported series. For old-style charms with the series 1008 // in the URL, that series is the one and only supported 1009 // series. 1010 var supportedSeries []string 1011 if series := args.Charm.URL().Series; series != "" { 1012 supportedSeries = []string{series} 1013 } else { 1014 supportedSeries = args.Charm.Meta().Series 1015 } 1016 if len(supportedSeries) > 0 { 1017 seriesOS, err := series.GetOSFromSeries(args.Series) 1018 if err != nil { 1019 return nil, errors.Trace(err) 1020 } 1021 supportedOperatingSystems := make(map[os.OSType]bool) 1022 for _, supportedSeries := range supportedSeries { 1023 os, err := series.GetOSFromSeries(supportedSeries) 1024 if err != nil { 1025 return nil, errors.Trace(err) 1026 } 1027 supportedOperatingSystems[os] = true 1028 } 1029 if !supportedOperatingSystems[seriesOS] { 1030 return nil, errors.NewNotSupported(errors.Errorf( 1031 "series %q (OS %q) not supported by charm, supported series are %q", 1032 args.Series, seriesOS, strings.Join(supportedSeries, ", "), 1033 ), "") 1034 } 1035 } 1036 } 1037 1038 // Ignore constraints that result from this call as 1039 // these would be accumulation of model and application constraints 1040 // but we only want application constraints to be persisted here. 1041 _, err = st.resolveConstraints(args.Constraints) 1042 if err != nil { 1043 return nil, errors.Trace(err) 1044 } 1045 1046 for _, placement := range args.Placement { 1047 data, err := st.parsePlacement(placement) 1048 if err != nil { 1049 return nil, errors.Trace(err) 1050 } 1051 switch data.placementType() { 1052 case machinePlacement: 1053 // Ensure that the machine and charm series match. 1054 m, err := st.Machine(data.machineId) 1055 if err != nil { 1056 return nil, errors.Trace(err) 1057 } 1058 subordinate := args.Charm.Meta().Subordinate 1059 if err := validateUnitMachineAssignment( 1060 m, args.Series, subordinate, storagePools, 1061 ); err != nil { 1062 return nil, errors.Annotatef( 1063 err, "cannot deploy to machine %s", m, 1064 ) 1065 } 1066 1067 case directivePlacement: 1068 if err := st.precheckInstance(args.Series, args.Constraints, data.directive); err != nil { 1069 return nil, errors.Trace(err) 1070 } 1071 } 1072 } 1073 1074 applicationID := st.docID(args.Name) 1075 1076 // Create the service addition operations. 1077 peers := args.Charm.Meta().Peers 1078 1079 // The doc defaults to CharmModifiedVersion = 0, which is correct, since it 1080 // has, by definition, at its initial state. 1081 svcDoc := &applicationDoc{ 1082 DocID: applicationID, 1083 Name: args.Name, 1084 ModelUUID: st.ModelUUID(), 1085 Series: args.Series, 1086 Subordinate: args.Charm.Meta().Subordinate, 1087 CharmURL: args.Charm.URL(), 1088 Channel: string(args.Channel), 1089 RelationCount: len(peers), 1090 Life: Alive, 1091 } 1092 1093 svc := newApplication(st, svcDoc) 1094 1095 endpointBindingsOp, err := createEndpointBindingsOp( 1096 st, svc.globalKey(), 1097 args.EndpointBindings, args.Charm.Meta(), 1098 ) 1099 if err != nil { 1100 return nil, errors.Trace(err) 1101 } 1102 1103 statusDoc := statusDoc{ 1104 ModelUUID: st.ModelUUID(), 1105 Status: status.Waiting, 1106 StatusInfo: status.MessageWaitForMachine, 1107 Updated: st.clock.Now().UnixNano(), 1108 // This exists to preserve questionable unit-aggregation behaviour 1109 // while we work out how to switch to an implementation that makes 1110 // sense. It is also set in AddMissingServiceStatuses. 1111 NeverSet: true, 1112 } 1113 1114 // The addServiceOps does not include the environment alive assertion, 1115 // so we add it here. 1116 ops := []txn.Op{ 1117 assertModelActiveOp(st.ModelUUID()), 1118 endpointBindingsOp, 1119 } 1120 addOps, err := addApplicationOps(st, addApplicationOpsArgs{ 1121 applicationDoc: svcDoc, 1122 statusDoc: statusDoc, 1123 constraints: args.Constraints, 1124 storage: args.Storage, 1125 settings: map[string]interface{}(args.Settings), 1126 }) 1127 if err != nil { 1128 return nil, errors.Trace(err) 1129 } 1130 ops = append(ops, addOps...) 1131 1132 // Collect peer relation addition operations. 1133 // 1134 // TODO(dimitern): Ensure each st.Endpoint has a space name associated in a 1135 // follow-up. 1136 peerOps, err := st.addPeerRelationsOps(args.Name, peers) 1137 if err != nil { 1138 return nil, errors.Trace(err) 1139 } 1140 ops = append(ops, peerOps...) 1141 1142 if len(args.Resources) > 0 { 1143 // Collect pending resource resolution operations. 1144 resources, err := st.Resources() 1145 if err != nil { 1146 return nil, errors.Trace(err) 1147 } 1148 resOps, err := resources.NewResolvePendingResourcesOps(args.Name, args.Resources) 1149 if err != nil { 1150 return nil, errors.Trace(err) 1151 } 1152 ops = append(ops, resOps...) 1153 } 1154 1155 // Collect unit-adding operations. 1156 for x := 0; x < args.NumUnits; x++ { 1157 unitName, unitOps, err := svc.addServiceUnitOps(applicationAddUnitOpsArgs{cons: args.Constraints, storageCons: args.Storage}) 1158 if err != nil { 1159 return nil, errors.Trace(err) 1160 } 1161 ops = append(ops, unitOps...) 1162 placement := instance.Placement{} 1163 if x < len(args.Placement) { 1164 placement = *args.Placement[x] 1165 } 1166 ops = append(ops, assignUnitOps(unitName, placement)...) 1167 } 1168 // At the last moment before inserting the service, prime status history. 1169 probablyUpdateStatusHistory(st, svc.globalKey(), statusDoc) 1170 1171 if err := st.runTransaction(ops); err == txn.ErrAborted { 1172 if err := checkModelActive(st); err != nil { 1173 return nil, errors.Trace(err) 1174 } 1175 // TODO(fwereade): 2016-09-09 lp:1621754 1176 // This is not always correct -- there are a million 1177 // operations collected in this func, not *all* of them 1178 // imply that this is the problem. (e.g. the charm being 1179 // destroyed just as we add application will fail, but 1180 // not because "application already exists") 1181 return nil, errors.Errorf("application already exists") 1182 } else if err != nil { 1183 return nil, errors.Trace(err) 1184 } 1185 // Refresh to pick the txn-revno. 1186 if err = svc.Refresh(); err != nil { 1187 return nil, errors.Trace(err) 1188 } 1189 1190 return svc, nil 1191 } 1192 1193 // TODO(natefinch) DEMO code, revisit after demo! 1194 var AddServicePostFuncs = map[string]func(*State, AddApplicationArgs) error{} 1195 1196 // assignUnitOps returns the db ops to save unit assignment for use by the 1197 // UnitAssigner worker. 1198 func assignUnitOps(unitName string, placement instance.Placement) []txn.Op { 1199 udoc := assignUnitDoc{ 1200 DocId: unitName, 1201 Scope: placement.Scope, 1202 Directive: placement.Directive, 1203 } 1204 return []txn.Op{{ 1205 C: assignUnitC, 1206 Id: udoc.DocId, 1207 Assert: txn.DocMissing, 1208 Insert: udoc, 1209 }} 1210 } 1211 1212 // AssignStagedUnits gets called by the UnitAssigner worker, and runs the given 1213 // assignments. 1214 func (st *State) AssignStagedUnits(ids []string) ([]UnitAssignmentResult, error) { 1215 query := bson.D{{"_id", bson.D{{"$in", ids}}}} 1216 unitAssignments, err := st.unitAssignments(query) 1217 if err != nil { 1218 return nil, errors.Annotate(err, "getting staged unit assignments") 1219 } 1220 results := make([]UnitAssignmentResult, len(unitAssignments)) 1221 for i, a := range unitAssignments { 1222 err := st.assignStagedUnit(a) 1223 results[i].Unit = a.Unit 1224 results[i].Error = err 1225 } 1226 return results, nil 1227 } 1228 1229 // UnitAssignments returns all staged unit assignments in the model. 1230 func (st *State) AllUnitAssignments() ([]UnitAssignment, error) { 1231 return st.unitAssignments(nil) 1232 } 1233 1234 func (st *State) unitAssignments(query bson.D) ([]UnitAssignment, error) { 1235 col, close := st.getCollection(assignUnitC) 1236 defer close() 1237 1238 var docs []assignUnitDoc 1239 if err := col.Find(query).All(&docs); err != nil { 1240 return nil, errors.Annotatef(err, "cannot get unit assignment docs") 1241 } 1242 results := make([]UnitAssignment, len(docs)) 1243 for i, doc := range docs { 1244 results[i] = UnitAssignment{ 1245 st.localID(doc.DocId), 1246 doc.Scope, 1247 doc.Directive, 1248 } 1249 } 1250 return results, nil 1251 } 1252 1253 func removeStagedAssignmentOp(id string) txn.Op { 1254 return txn.Op{ 1255 C: assignUnitC, 1256 Id: id, 1257 Remove: true, 1258 } 1259 } 1260 1261 func (st *State) assignStagedUnit(a UnitAssignment) error { 1262 u, err := st.Unit(a.Unit) 1263 if err != nil { 1264 return errors.Trace(err) 1265 } 1266 if a.Scope == "" && a.Directive == "" { 1267 return errors.Trace(st.AssignUnit(u, AssignCleanEmpty)) 1268 } 1269 1270 placement := &instance.Placement{Scope: a.Scope, Directive: a.Directive} 1271 1272 return errors.Trace(st.AssignUnitWithPlacement(u, placement)) 1273 } 1274 1275 // AssignUnitWithPlacement chooses a machine using the given placement directive 1276 // and then assigns the unit to it. 1277 func (st *State) AssignUnitWithPlacement(unit *Unit, placement *instance.Placement) error { 1278 // TODO(natefinch) this should be done as a single transaction, not two. 1279 // Mark https://launchpad.net/bugs/1506994 fixed when done. 1280 1281 m, err := st.addMachineWithPlacement(unit, placement) 1282 if err != nil { 1283 return errors.Trace(err) 1284 } 1285 return unit.AssignToMachine(m) 1286 } 1287 1288 // placementData is a helper type that encodes some of the logic behind how an 1289 // instance.Placement gets translated into a placement directive the providers 1290 // understand. 1291 type placementData struct { 1292 machineId string 1293 directive string 1294 containerType instance.ContainerType 1295 } 1296 1297 type placementType int 1298 1299 const ( 1300 containerPlacement placementType = iota 1301 directivePlacement 1302 machinePlacement 1303 ) 1304 1305 // placementType returns the type of placement that this data represents. 1306 func (p placementData) placementType() placementType { 1307 if p.containerType != "" { 1308 return containerPlacement 1309 } 1310 if p.directive != "" { 1311 return directivePlacement 1312 } 1313 return machinePlacement 1314 } 1315 1316 func (st *State) parsePlacement(placement *instance.Placement) (*placementData, error) { 1317 // Extract container type and parent from container placement directives. 1318 if container, err := instance.ParseContainerType(placement.Scope); err == nil { 1319 return &placementData{ 1320 containerType: container, 1321 machineId: placement.Directive, 1322 }, nil 1323 } 1324 switch placement.Scope { 1325 case st.ModelUUID(): 1326 return &placementData{directive: placement.Directive}, nil 1327 case instance.MachineScope: 1328 return &placementData{machineId: placement.Directive}, nil 1329 default: 1330 return nil, errors.Errorf("placement scope: invalid model UUID %q", placement.Scope) 1331 } 1332 } 1333 1334 // addMachineWithPlacement finds a machine that matches the given placement directive for the given unit. 1335 func (st *State) addMachineWithPlacement(unit *Unit, placement *instance.Placement) (*Machine, error) { 1336 unitCons, err := unit.Constraints() 1337 if err != nil { 1338 return nil, err 1339 } 1340 1341 data, err := st.parsePlacement(placement) 1342 if err != nil { 1343 return nil, errors.Trace(err) 1344 } 1345 1346 // Create any new machine marked as dirty so that 1347 // nothing else will grab it before we assign the unit to it. 1348 // TODO(natefinch) fix this when we put assignment in the same 1349 // transaction as adding a machine. See bug 1350 // https://launchpad.net/bugs/1506994 1351 1352 switch data.placementType() { 1353 case containerPlacement: 1354 // If a container is to be used, create it. 1355 template := MachineTemplate{ 1356 Series: unit.Series(), 1357 Jobs: []MachineJob{JobHostUnits}, 1358 Dirty: true, 1359 Constraints: *unitCons, 1360 } 1361 if data.machineId != "" { 1362 return st.AddMachineInsideMachine(template, data.machineId, data.containerType) 1363 } 1364 return st.AddMachineInsideNewMachine(template, template, data.containerType) 1365 case directivePlacement: 1366 // If a placement directive is to be used, do that here. 1367 template := MachineTemplate{ 1368 Series: unit.Series(), 1369 Jobs: []MachineJob{JobHostUnits}, 1370 Dirty: true, 1371 Constraints: *unitCons, 1372 Placement: data.directive, 1373 } 1374 return st.AddOneMachine(template) 1375 default: 1376 // Otherwise use an existing machine. 1377 return st.Machine(data.machineId) 1378 } 1379 } 1380 1381 // Service returns a service state by name. 1382 func (st *State) Application(name string) (_ *Application, err error) { 1383 applications, closer := st.getCollection(applicationsC) 1384 defer closer() 1385 1386 if !names.IsValidApplication(name) { 1387 return nil, errors.Errorf("%q is not a valid application name", name) 1388 } 1389 sdoc := &applicationDoc{} 1390 err = applications.FindId(name).One(sdoc) 1391 if err == mgo.ErrNotFound { 1392 return nil, errors.NotFoundf("application %q", name) 1393 } 1394 if err != nil { 1395 return nil, errors.Annotatef(err, "cannot get application %q", name) 1396 } 1397 return newApplication(st, sdoc), nil 1398 } 1399 1400 // AllApplications returns all deployed services in the model. 1401 func (st *State) AllApplications() (applications []*Application, err error) { 1402 applicationsCollection, closer := st.getCollection(applicationsC) 1403 defer closer() 1404 1405 sdocs := []applicationDoc{} 1406 err = applicationsCollection.Find(bson.D{}).All(&sdocs) 1407 if err != nil { 1408 return nil, errors.Errorf("cannot get all applications") 1409 } 1410 for _, v := range sdocs { 1411 applications = append(applications, newApplication(st, &v)) 1412 } 1413 return applications, nil 1414 } 1415 1416 // InferEndpoints returns the endpoints corresponding to the supplied names. 1417 // There must be 1 or 2 supplied names, of the form <service>[:<relation>]. 1418 // If the supplied names uniquely specify a possible relation, or if they 1419 // uniquely specify a possible relation once all implicit relations have been 1420 // filtered, the endpoints corresponding to that relation will be returned. 1421 func (st *State) InferEndpoints(names ...string) ([]Endpoint, error) { 1422 // Collect all possible sane endpoint lists. 1423 var candidates [][]Endpoint 1424 switch len(names) { 1425 case 1: 1426 eps, err := st.endpoints(names[0], isPeer) 1427 if err != nil { 1428 return nil, errors.Trace(err) 1429 } 1430 for _, ep := range eps { 1431 candidates = append(candidates, []Endpoint{ep}) 1432 } 1433 case 2: 1434 eps1, err := st.endpoints(names[0], notPeer) 1435 if err != nil { 1436 return nil, errors.Trace(err) 1437 } 1438 eps2, err := st.endpoints(names[1], notPeer) 1439 if err != nil { 1440 return nil, errors.Trace(err) 1441 } 1442 for _, ep1 := range eps1 { 1443 for _, ep2 := range eps2 { 1444 if ep1.CanRelateTo(ep2) && containerScopeOk(st, ep1, ep2) { 1445 candidates = append(candidates, []Endpoint{ep1, ep2}) 1446 } 1447 } 1448 } 1449 default: 1450 return nil, errors.Errorf("cannot relate %d endpoints", len(names)) 1451 } 1452 // If there's ambiguity, try discarding implicit relations. 1453 switch len(candidates) { 1454 case 0: 1455 return nil, errors.Errorf("no relations found") 1456 case 1: 1457 return candidates[0], nil 1458 } 1459 var filtered [][]Endpoint 1460 outer: 1461 for _, cand := range candidates { 1462 for _, ep := range cand { 1463 if ep.IsImplicit() { 1464 continue outer 1465 } 1466 } 1467 filtered = append(filtered, cand) 1468 } 1469 if len(filtered) == 1 { 1470 return filtered[0], nil 1471 } 1472 keys := []string{} 1473 for _, cand := range candidates { 1474 keys = append(keys, fmt.Sprintf("%q", relationKey(cand))) 1475 } 1476 sort.Strings(keys) 1477 return nil, errors.Errorf("ambiguous relation: %q could refer to %s", 1478 strings.Join(names, " "), strings.Join(keys, "; ")) 1479 } 1480 1481 func isPeer(ep Endpoint) bool { 1482 return ep.Role == charm.RolePeer 1483 } 1484 1485 func notPeer(ep Endpoint) bool { 1486 return ep.Role != charm.RolePeer 1487 } 1488 1489 func containerScopeOk(st *State, ep1, ep2 Endpoint) bool { 1490 if ep1.Scope != charm.ScopeContainer && ep2.Scope != charm.ScopeContainer { 1491 return true 1492 } 1493 var subordinateCount int 1494 for _, ep := range []Endpoint{ep1, ep2} { 1495 svc, err := st.Application(ep.ApplicationName) 1496 if err != nil { 1497 return false 1498 } 1499 if svc.doc.Subordinate { 1500 subordinateCount++ 1501 } 1502 } 1503 return subordinateCount >= 1 1504 } 1505 1506 // endpoints returns all endpoints that could be intended by the 1507 // supplied endpoint name, and which cause the filter param to 1508 // return true. 1509 func (st *State) endpoints(name string, filter func(ep Endpoint) bool) ([]Endpoint, error) { 1510 var svcName, relName string 1511 if i := strings.Index(name, ":"); i == -1 { 1512 svcName = name 1513 } else if i != 0 && i != len(name)-1 { 1514 svcName = name[:i] 1515 relName = name[i+1:] 1516 } else { 1517 return nil, errors.Errorf("invalid endpoint %q", name) 1518 } 1519 svc, err := st.Application(svcName) 1520 if err != nil { 1521 return nil, errors.Trace(err) 1522 } 1523 eps := []Endpoint{} 1524 if relName != "" { 1525 ep, err := svc.Endpoint(relName) 1526 if err != nil { 1527 return nil, errors.Trace(err) 1528 } 1529 eps = append(eps, ep) 1530 } else { 1531 eps, err = svc.Endpoints() 1532 if err != nil { 1533 return nil, errors.Trace(err) 1534 } 1535 } 1536 final := []Endpoint{} 1537 for _, ep := range eps { 1538 if filter(ep) { 1539 final = append(final, ep) 1540 } 1541 } 1542 return final, nil 1543 } 1544 1545 // AddRelation creates a new relation with the given endpoints. 1546 func (st *State) AddRelation(eps ...Endpoint) (r *Relation, err error) { 1547 key := relationKey(eps) 1548 defer errors.DeferredAnnotatef(&err, "cannot add relation %q", key) 1549 // Enforce basic endpoint sanity. The epCount restrictions may be relaxed 1550 // in the future; if so, this method is likely to need significant rework. 1551 if len(eps) != 2 { 1552 return nil, errors.Errorf("relation must have two endpoints") 1553 } 1554 if !eps[0].CanRelateTo(eps[1]) { 1555 return nil, errors.Errorf("endpoints do not relate") 1556 } 1557 // If either endpoint has container scope, so must the other; and the 1558 // services's series must also match, because they'll be deployed to 1559 // the same machines. 1560 matchSeries := true 1561 if eps[0].Scope == charm.ScopeContainer { 1562 eps[1].Scope = charm.ScopeContainer 1563 } else if eps[1].Scope == charm.ScopeContainer { 1564 eps[0].Scope = charm.ScopeContainer 1565 } else { 1566 matchSeries = false 1567 } 1568 // We only get a unique relation id once, to save on roundtrips. If it's 1569 // -1, we haven't got it yet (we don't get it at this stage, because we 1570 // still don't know whether it's sane to even attempt creation). 1571 id := -1 1572 // If a service's charm is upgraded while we're trying to add a relation, 1573 // we'll need to re-validate service sanity. 1574 var doc *relationDoc 1575 buildTxn := func(attempt int) ([]txn.Op, error) { 1576 // Perform initial relation sanity check. 1577 if exists, err := isNotDead(st, relationsC, key); err != nil { 1578 return nil, errors.Trace(err) 1579 } else if exists { 1580 return nil, errors.Errorf("relation already exists") 1581 } 1582 // Collect per-service operations, checking sanity as we go. 1583 var ops []txn.Op 1584 var subordinateCount int 1585 series := map[string]bool{} 1586 for _, ep := range eps { 1587 svc, err := st.Application(ep.ApplicationName) 1588 if errors.IsNotFound(err) { 1589 return nil, errors.Errorf("application %q does not exist", ep.ApplicationName) 1590 } else if err != nil { 1591 return nil, errors.Trace(err) 1592 } else if svc.doc.Life != Alive { 1593 return nil, errors.Errorf("application %q is not alive", ep.ApplicationName) 1594 } 1595 if svc.doc.Subordinate { 1596 subordinateCount++ 1597 } 1598 series[svc.doc.Series] = true 1599 ch, _, err := svc.Charm() 1600 if err != nil { 1601 return nil, errors.Trace(err) 1602 } 1603 if !ep.ImplementedBy(ch) { 1604 return nil, errors.Errorf("%q does not implement %q", ep.ApplicationName, ep) 1605 } 1606 ops = append(ops, txn.Op{ 1607 C: applicationsC, 1608 Id: st.docID(ep.ApplicationName), 1609 Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}}, 1610 Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}}, 1611 }) 1612 } 1613 if matchSeries && len(series) != 1 { 1614 return nil, errors.Errorf("principal and subordinate applications' series must match") 1615 } 1616 if eps[0].Scope == charm.ScopeContainer && subordinateCount < 1 { 1617 return nil, errors.Errorf("container scoped relation requires at least one subordinate application") 1618 } 1619 1620 // Create a new unique id if that has not already been done, and add 1621 // an operation to create the relation document. 1622 if id == -1 { 1623 var err error 1624 if id, err = st.sequence("relation"); err != nil { 1625 return nil, errors.Trace(err) 1626 } 1627 } 1628 docID := st.docID(key) 1629 doc = &relationDoc{ 1630 DocID: docID, 1631 Key: key, 1632 ModelUUID: st.ModelUUID(), 1633 Id: id, 1634 Endpoints: eps, 1635 Life: Alive, 1636 } 1637 ops = append(ops, txn.Op{ 1638 C: relationsC, 1639 Id: docID, 1640 Assert: txn.DocMissing, 1641 Insert: doc, 1642 }) 1643 return ops, nil 1644 } 1645 if err = st.run(buildTxn); err == nil { 1646 return &Relation{st, *doc}, nil 1647 } 1648 return nil, errors.Trace(err) 1649 } 1650 1651 // EndpointsRelation returns the existing relation with the given endpoints. 1652 func (st *State) EndpointsRelation(endpoints ...Endpoint) (*Relation, error) { 1653 return st.KeyRelation(relationKey(endpoints)) 1654 } 1655 1656 // KeyRelation returns the existing relation with the given key (which can 1657 // be derived unambiguously from the relation's endpoints). 1658 func (st *State) KeyRelation(key string) (*Relation, error) { 1659 relations, closer := st.getCollection(relationsC) 1660 defer closer() 1661 1662 doc := relationDoc{} 1663 err := relations.FindId(key).One(&doc) 1664 if err == mgo.ErrNotFound { 1665 return nil, errors.NotFoundf("relation %q", key) 1666 } 1667 if err != nil { 1668 return nil, errors.Annotatef(err, "cannot get relation %q", key) 1669 } 1670 return newRelation(st, &doc), nil 1671 } 1672 1673 // Relation returns the existing relation with the given id. 1674 func (st *State) Relation(id int) (*Relation, error) { 1675 relations, closer := st.getCollection(relationsC) 1676 defer closer() 1677 1678 doc := relationDoc{} 1679 err := relations.Find(bson.D{{"id", id}}).One(&doc) 1680 if err == mgo.ErrNotFound { 1681 return nil, errors.NotFoundf("relation %d", id) 1682 } 1683 if err != nil { 1684 return nil, errors.Annotatef(err, "cannot get relation %d", id) 1685 } 1686 return newRelation(st, &doc), nil 1687 } 1688 1689 // AllRelations returns all relations in the model ordered by id. 1690 func (st *State) AllRelations() (relations []*Relation, err error) { 1691 relationsCollection, closer := st.getCollection(relationsC) 1692 defer closer() 1693 1694 docs := relationDocSlice{} 1695 err = relationsCollection.Find(nil).All(&docs) 1696 if err != nil { 1697 return nil, errors.Annotate(err, "cannot get all relations") 1698 } 1699 sort.Sort(docs) 1700 for _, v := range docs { 1701 relations = append(relations, newRelation(st, &v)) 1702 } 1703 return 1704 } 1705 1706 type relationDocSlice []relationDoc 1707 1708 func (rdc relationDocSlice) Len() int { return len(rdc) } 1709 func (rdc relationDocSlice) Swap(i, j int) { rdc[i], rdc[j] = rdc[j], rdc[i] } 1710 func (rdc relationDocSlice) Less(i, j int) bool { 1711 return rdc[i].Id < rdc[j].Id 1712 } 1713 1714 // Unit returns a unit by name. 1715 func (st *State) Unit(name string) (*Unit, error) { 1716 if !names.IsValidUnit(name) { 1717 return nil, errors.Errorf("%q is not a valid unit name", name) 1718 } 1719 units, closer := st.getCollection(unitsC) 1720 defer closer() 1721 1722 doc := unitDoc{} 1723 err := units.FindId(name).One(&doc) 1724 if err == mgo.ErrNotFound { 1725 return nil, errors.NotFoundf("unit %q", name) 1726 } 1727 if err != nil { 1728 return nil, errors.Annotatef(err, "cannot get unit %q", name) 1729 } 1730 return newUnit(st, &doc), nil 1731 } 1732 1733 // UnitsFor returns the units placed in the given machine id. 1734 func (st *State) UnitsFor(machineId string) ([]*Unit, error) { 1735 if !names.IsValidMachine(machineId) { 1736 return nil, errors.Errorf("%q is not a valid machine id", machineId) 1737 } 1738 m := &Machine{ 1739 st: st, 1740 doc: machineDoc{ 1741 Id: machineId, 1742 }, 1743 } 1744 return m.Units() 1745 } 1746 1747 // AssignUnit places the unit on a machine. Depending on the policy, and the 1748 // state of the model, this may lead to new instances being launched 1749 // within the model. 1750 func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) { 1751 if !u.IsPrincipal() { 1752 return errors.Errorf("subordinate unit %q cannot be assigned directly to a machine", u) 1753 } 1754 defer errors.DeferredAnnotatef(&err, "cannot assign unit %q to machine", u) 1755 var m *Machine 1756 switch policy { 1757 case AssignLocal: 1758 m, err = st.Machine("0") 1759 if err != nil { 1760 return errors.Trace(err) 1761 } 1762 return u.AssignToMachine(m) 1763 case AssignClean: 1764 if _, err = u.AssignToCleanMachine(); errors.Cause(err) != noCleanMachines { 1765 return errors.Trace(err) 1766 } 1767 return u.AssignToNewMachineOrContainer() 1768 case AssignCleanEmpty: 1769 if _, err = u.AssignToCleanEmptyMachine(); errors.Cause(err) != noCleanMachines { 1770 return errors.Trace(err) 1771 } 1772 return u.AssignToNewMachineOrContainer() 1773 case AssignNew: 1774 return errors.Trace(u.AssignToNewMachine()) 1775 } 1776 return errors.Errorf("unknown unit assignment policy: %q", policy) 1777 } 1778 1779 // StartSync forces watchers to resynchronize their state with the 1780 // database immediately. This will happen periodically automatically. 1781 func (st *State) StartSync() { 1782 st.workers.TxnLogWatcher().StartSync() 1783 st.workers.PresenceWatcher().Sync() 1784 } 1785 1786 // SetAdminMongoPassword sets the administrative password 1787 // to access the state. If the password is non-empty, 1788 // all subsequent attempts to access the state must 1789 // be authorized; otherwise no authorization is required. 1790 func (st *State) SetAdminMongoPassword(password string) error { 1791 err := mongo.SetAdminMongoPassword(st.session, mongo.AdminUser, password) 1792 return errors.Trace(err) 1793 } 1794 1795 type controllersDoc struct { 1796 Id string `bson:"_id"` 1797 CloudName string `bson:"cloud"` 1798 ModelUUID string `bson:"model-uuid"` 1799 MachineIds []string 1800 VotingMachineIds []string 1801 MongoSpaceName string `bson:"mongo-space-name"` 1802 MongoSpaceState string `bson:"mongo-space-state"` 1803 } 1804 1805 // ControllerInfo holds information about currently 1806 // configured controller machines. 1807 type ControllerInfo struct { 1808 // CloudName is the name of the cloud to which this controller is deployed. 1809 CloudName string 1810 1811 // ModelTag identifies the initial model. Only the initial 1812 // model is able to have machines that manage state. The initial 1813 // model is the model that is created when bootstrapping. 1814 ModelTag names.ModelTag 1815 1816 // MachineIds holds the ids of all machines configured 1817 // to run a controller. It includes all the machine 1818 // ids in VotingMachineIds. 1819 MachineIds []string 1820 1821 // VotingMachineIds holds the ids of all machines 1822 // configured to run a controller and to have a vote 1823 // in peer election. 1824 VotingMachineIds []string 1825 1826 // MongoSpaceName is the space that contains all Mongo servers. 1827 MongoSpaceName string 1828 1829 // MongoSpaceState records the state of the mongo space selection state machine. Valid states are: 1830 // * We haven't looked for a Mongo space yet (MongoSpaceUnknown) 1831 // * We have looked for a Mongo space, but we didn't find one (MongoSpaceInvalid) 1832 // * We have looked for and found a Mongo space (MongoSpaceValid) 1833 // * We didn't try to find a Mongo space because the provider doesn't support spaces (MongoSpaceUnsupported) 1834 MongoSpaceState MongoSpaceStates 1835 } 1836 1837 type MongoSpaceStates string 1838 1839 const ( 1840 MongoSpaceUnknown MongoSpaceStates = "" 1841 MongoSpaceValid MongoSpaceStates = "valid" 1842 MongoSpaceInvalid MongoSpaceStates = "invalid" 1843 MongoSpaceUnsupported MongoSpaceStates = "unsupported" 1844 ) 1845 1846 // ControllerInfo returns information about 1847 // the currently configured controller machines. 1848 func (st *State) ControllerInfo() (*ControllerInfo, error) { 1849 session := st.session.Copy() 1850 defer session.Close() 1851 return readRawControllerInfo(st.session) 1852 } 1853 1854 // readRawControllerInfo reads ControllerInfo direct from the supplied session, 1855 // falling back to the bootstrap model document to extract the UUID when 1856 // required. 1857 func readRawControllerInfo(session *mgo.Session) (*ControllerInfo, error) { 1858 db := session.DB(jujuDB) 1859 controllers := db.C(controllersC) 1860 1861 var doc controllersDoc 1862 err := controllers.Find(bson.D{{"_id", modelGlobalKey}}).One(&doc) 1863 if err == mgo.ErrNotFound { 1864 return nil, errors.NotFoundf("controllers document") 1865 } 1866 if err != nil { 1867 return nil, errors.Annotatef(err, "cannot get controllers document") 1868 } 1869 return &ControllerInfo{ 1870 CloudName: doc.CloudName, 1871 ModelTag: names.NewModelTag(doc.ModelUUID), 1872 MachineIds: doc.MachineIds, 1873 VotingMachineIds: doc.VotingMachineIds, 1874 MongoSpaceName: doc.MongoSpaceName, 1875 MongoSpaceState: MongoSpaceStates(doc.MongoSpaceState), 1876 }, nil 1877 } 1878 1879 const stateServingInfoKey = "stateServingInfo" 1880 1881 // StateServingInfo returns information for running a controller machine 1882 func (st *State) StateServingInfo() (StateServingInfo, error) { 1883 controllers, closer := st.getCollection(controllersC) 1884 defer closer() 1885 1886 var info StateServingInfo 1887 err := controllers.Find(bson.D{{"_id", stateServingInfoKey}}).One(&info) 1888 if err != nil { 1889 return info, errors.Trace(err) 1890 } 1891 if info.StatePort == 0 { 1892 return StateServingInfo{}, errors.NotFoundf("state serving info") 1893 } 1894 return info, nil 1895 } 1896 1897 // SetStateServingInfo stores information needed for running a controller 1898 func (st *State) SetStateServingInfo(info StateServingInfo) error { 1899 if info.StatePort == 0 || info.APIPort == 0 || 1900 info.Cert == "" || info.PrivateKey == "" { 1901 return errors.Errorf("incomplete state serving info set in state") 1902 } 1903 if info.CAPrivateKey == "" { 1904 // No CA certificate key means we can't generate new controller 1905 // certificates when needed to add to the certificate SANs. 1906 // Older Juju deployments discard the key because no one realised 1907 // the certificate was flawed, so at best we can log a warning 1908 // until an upgrade process is written. 1909 logger.Warningf("state serving info has no CA certificate key") 1910 } 1911 ops := []txn.Op{{ 1912 C: controllersC, 1913 Id: stateServingInfoKey, 1914 Update: bson.D{{"$set", info}}, 1915 }} 1916 if err := st.runTransaction(ops); err != nil { 1917 return errors.Annotatef(err, "cannot set state serving info") 1918 } 1919 return nil 1920 } 1921 1922 // SetSystemIdentity sets the system identity value in the database 1923 // if and only iff it is empty. 1924 func SetSystemIdentity(st *State, identity string) error { 1925 ops := []txn.Op{{ 1926 C: controllersC, 1927 Id: stateServingInfoKey, 1928 Assert: bson.D{{"systemidentity", ""}}, 1929 Update: bson.D{{"$set", bson.D{{"systemidentity", identity}}}}, 1930 }} 1931 1932 if err := st.runTransaction(ops); err != nil { 1933 return errors.Trace(err) 1934 } 1935 return nil 1936 } 1937 1938 // SetOrGetMongoSpaceName attempts to set the Mongo space or, if that fails, look 1939 // up the current Mongo space. Either way, it always returns what is in the 1940 // database by the end of the call. 1941 func (st *State) SetOrGetMongoSpaceName(mongoSpaceName network.SpaceName) (network.SpaceName, error) { 1942 err := st.setMongoSpaceName(mongoSpaceName) 1943 if err == txn.ErrAborted { 1944 // Failed to set the new space name. Return what is already stored in state. 1945 controllerInfo, err := st.ControllerInfo() 1946 if err != nil { 1947 return network.SpaceName(""), errors.Trace(err) 1948 } 1949 return network.SpaceName(controllerInfo.MongoSpaceName), nil 1950 } else if err != nil { 1951 return network.SpaceName(""), errors.Trace(err) 1952 } 1953 return mongoSpaceName, nil 1954 } 1955 1956 // SetMongoSpaceState attempts to set the Mongo space state or, if that fails, look 1957 // up the current Mongo state. Either way, it always returns what is in the 1958 // database by the end of the call. 1959 func (st *State) SetMongoSpaceState(mongoSpaceState MongoSpaceStates) error { 1960 1961 if mongoSpaceState != MongoSpaceUnknown && 1962 mongoSpaceState != MongoSpaceValid && 1963 mongoSpaceState != MongoSpaceInvalid && 1964 mongoSpaceState != MongoSpaceUnsupported { 1965 return errors.NotValidf("mongoSpaceState: %s", mongoSpaceState) 1966 } 1967 1968 err := st.setMongoSpaceState(mongoSpaceState) 1969 if err != nil { 1970 return errors.Trace(err) 1971 } 1972 return nil 1973 } 1974 1975 func (st *State) setMongoSpaceName(mongoSpaceName network.SpaceName) error { 1976 ops := []txn.Op{{ 1977 C: controllersC, 1978 Id: modelGlobalKey, 1979 Assert: bson.D{{"mongo-space-state", string(MongoSpaceUnknown)}}, 1980 Update: bson.D{{ 1981 "$set", 1982 bson.D{ 1983 {"mongo-space-name", string(mongoSpaceName)}, 1984 {"mongo-space-state", MongoSpaceValid}, 1985 }, 1986 }}, 1987 }} 1988 1989 return st.runTransaction(ops) 1990 } 1991 1992 func (st *State) setMongoSpaceState(mongoSpaceState MongoSpaceStates) error { 1993 ops := []txn.Op{{ 1994 C: controllersC, 1995 Id: modelGlobalKey, 1996 Update: bson.D{{"$set", bson.D{{"mongo-space-state", mongoSpaceState}}}}, 1997 }} 1998 1999 return st.runTransaction(ops) 2000 } 2001 2002 func (st *State) networkEntityGlobalKeyOp(globalKey string, providerId network.Id) txn.Op { 2003 key := st.networkEntityGlobalKey(globalKey, providerId) 2004 return txn.Op{ 2005 C: providerIDsC, 2006 Id: key, 2007 Assert: txn.DocMissing, 2008 Insert: providerIdDoc{ID: key}, 2009 } 2010 } 2011 2012 func (st *State) networkEntityGlobalKeyRemoveOp(globalKey string, providerId network.Id) txn.Op { 2013 key := st.networkEntityGlobalKey(globalKey, providerId) 2014 return txn.Op{ 2015 C: providerIDsC, 2016 Id: key, 2017 Remove: true, 2018 } 2019 } 2020 2021 func (st *State) networkEntityGlobalKey(globalKey string, providerId network.Id) string { 2022 return st.docID(globalKey + ":" + string(providerId)) 2023 } 2024 2025 // PutAuditEntryFn returns a function which will persist 2026 // audit.AuditEntry instances to the database. 2027 func (st *State) PutAuditEntryFn() func(audit.AuditEntry) error { 2028 insert := func(collectionName string, docs ...interface{}) error { 2029 collection, closeCollection := st.getCollection(collectionName) 2030 defer closeCollection() 2031 2032 writeableCollection := collection.Writeable() 2033 2034 return errors.Trace(writeableCollection.Insert(docs...)) 2035 } 2036 return stateaudit.PutAuditEntryFn(auditingC, insert) 2037 } 2038 2039 var tagPrefix = map[byte]string{ 2040 'm': names.MachineTagKind + "-", 2041 'a': names.ApplicationTagKind + "-", 2042 'u': names.UnitTagKind + "-", 2043 'e': names.ModelTagKind + "-", 2044 'r': names.RelationTagKind + "-", 2045 } 2046 2047 func tagForGlobalKey(key string) (string, bool) { 2048 if len(key) < 3 || key[1] != '#' { 2049 return "", false 2050 } 2051 p, ok := tagPrefix[key[0]] 2052 if !ok { 2053 return "", false 2054 } 2055 return p + key[2:], true 2056 } 2057 2058 // SetClockForTesting is an exported function to allow other packages 2059 // to set the internal clock for the State instance. It is named such 2060 // that it should be obvious if it is ever called from a non-test packgae. 2061 func (st *State) SetClockForTesting(clock clock.Clock) error { 2062 st.clock = clock 2063 // Need to restart the lease workers so they get the new clock. 2064 st.workers.Kill() 2065 err := st.workers.Wait() 2066 if err != nil { 2067 return errors.Trace(err) 2068 } 2069 err = st.start(st.controllerTag) 2070 if err != nil { 2071 return errors.Trace(err) 2072 } 2073 return nil 2074 }