github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "github.com/juju/names" 21 jujutxn "github.com/juju/txn" 22 "github.com/juju/utils" 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/mgo.v2" 30 "gopkg.in/mgo.v2/bson" 31 "gopkg.in/mgo.v2/txn" 32 33 "github.com/juju/juju/apiserver/params" 34 "github.com/juju/juju/constraints" 35 corelease "github.com/juju/juju/core/lease" 36 "github.com/juju/juju/environs/config" 37 "github.com/juju/juju/instance" 38 "github.com/juju/juju/mongo" 39 "github.com/juju/juju/network" 40 "github.com/juju/juju/state/cloudimagemetadata" 41 statelease "github.com/juju/juju/state/lease" 42 "github.com/juju/juju/state/presence" 43 "github.com/juju/juju/state/watcher" 44 "github.com/juju/juju/status" 45 jujuversion "github.com/juju/juju/version" 46 "github.com/juju/juju/worker/lease" 47 ) 48 49 var logger = loggo.GetLogger("juju.state") 50 51 const ( 52 // jujuDB is the name of the main juju database. 53 jujuDB = "juju" 54 55 // presenceDB is the name of the database used to hold presence pinger data. 56 presenceDB = "presence" 57 presenceC = "presence" 58 59 // blobstoreDB is the name of the blobstore GridFS database. 60 blobstoreDB = "blobstore" 61 62 // serviceLeadershipNamespace is the name of the lease.Client namespace 63 // used by the leadership manager. 64 serviceLeadershipNamespace = "service-leadership" 65 66 // singularControllerNamespace is the name of the lease.Client namespace 67 // used by the singular manager 68 singularControllerNamespace = "singular-controller" 69 ) 70 71 // State represents the state of an model 72 // managed by juju. 73 type State struct { 74 modelTag names.ModelTag 75 controllerTag names.ModelTag 76 mongoInfo *mongo.MongoInfo 77 mongoDialOpts mongo.DialOpts 78 session *mgo.Session 79 database Database 80 policy Policy 81 82 // TODO(fwereade): move these out of state and make them independent 83 // workers on which state depends. 84 watcher *watcher.Watcher 85 pwatcher *presence.Watcher 86 // leadershipManager keeps track of units' service leadership leases 87 // within this environment. 88 leadershipClient corelease.Client 89 leadershipManager *lease.Manager 90 // singularManager keeps track of which controller machine is responsible 91 // for managing this state's environment. 92 singularManager *lease.Manager 93 94 // mu guards allManager, allModelManager & allModelWatcherBacking 95 mu sync.Mutex 96 allManager *storeManager 97 allModelManager *storeManager 98 allModelWatcherBacking Backing 99 100 // TODO(anastasiamac 2015-07-16) As state gets broken up, remove this. 101 CloudImageMetadataStorage cloudimagemetadata.Storage 102 } 103 104 // StateServingInfo holds information needed by a controller. 105 // This type is a copy of the type of the same name from the api/params package. 106 // It is replicated here to avoid the state pacakge depending on api/params. 107 type StateServingInfo struct { 108 APIPort int 109 StatePort int 110 Cert string 111 PrivateKey string 112 CAPrivateKey string 113 // this will be passed as the KeyFile argument to MongoDB 114 SharedSecret string 115 SystemIdentity string 116 } 117 118 // IsController returns true if this state instance has the bootstrap 119 // model UUID. 120 func (st *State) IsController() bool { 121 return st.modelTag == st.controllerTag 122 } 123 124 // RemoveAllModelDocs removes all documents from multi-model 125 // collections. The model should be put into a dying state before call 126 // this method. Otherwise, there is a race condition in which collections 127 // could be added to during or after the running of this method. 128 func (st *State) RemoveAllModelDocs() error { 129 return st.removeAllModelDocs(bson.D{{"life", Dead}}) 130 } 131 132 // RemoveImportingModelDocs removes all documents from multi-model collections 133 // for the current model. This method asserts that the model's migration mode 134 // is "importing". 135 func (st *State) RemoveImportingModelDocs() error { 136 return st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeImporting}}) 137 } 138 139 func (st *State) removeAllModelDocs(modelAssertion bson.D) error { 140 env, err := st.Model() 141 if err != nil { 142 return errors.Trace(err) 143 } 144 id := userModelNameIndex(env.Owner().Canonical(), env.Name()) 145 ops := []txn.Op{{ 146 // Cleanup the owner:envName unique key. 147 C: usermodelnameC, 148 Id: id, 149 Remove: true, 150 }, { 151 C: modelEntityRefsC, 152 Id: st.ModelUUID(), 153 Remove: true, 154 }, { 155 C: modelsC, 156 Id: st.ModelUUID(), 157 Assert: modelAssertion, 158 Remove: true, 159 }} 160 if !st.IsController() { 161 ops = append(ops, decHostedModelCountOp()) 162 } 163 164 // Add all per-model docs to the txn. 165 for name, info := range st.database.Schema() { 166 if info.global { 167 continue 168 } 169 coll, closer := st.getCollection(name) 170 defer closer() 171 172 var ids []bson.M 173 err := coll.Find(nil).Select(bson.D{{"_id", 1}}).All(&ids) 174 if err != nil { 175 return errors.Trace(err) 176 } 177 for _, id := range ids { 178 if info.rawAccess { 179 if err := coll.Writeable().RemoveId(id["_id"]); err != nil { 180 return errors.Trace(err) 181 } 182 } else { 183 ops = append(ops, txn.Op{ 184 C: name, 185 Id: id["_id"], 186 Remove: true, 187 }) 188 } 189 } 190 } 191 192 return st.runTransaction(ops) 193 } 194 195 // ForModel returns a connection to mongo for the specified model. The 196 // connection uses the same credentials and policy as the existing connection. 197 func (st *State) ForModel(env names.ModelTag) (*State, error) { 198 newState, err := open(env, st.mongoInfo, st.mongoDialOpts, st.policy) 199 if err != nil { 200 return nil, errors.Trace(err) 201 } 202 if err := newState.start(st.controllerTag); err != nil { 203 return nil, errors.Trace(err) 204 } 205 return newState, nil 206 } 207 208 // start starts the presence watcher, leadership manager and images metadata storage, 209 // and fills in the controllerTag field with the supplied value. 210 func (st *State) start(controllerTag names.ModelTag) error { 211 st.controllerTag = controllerTag 212 213 var clientId string 214 if identity := st.mongoInfo.Tag; identity != nil { 215 // TODO(fwereade): it feels a bit wrong to take this from MongoInfo -- I 216 // think it's just coincidental that the mongodb user happens to map to 217 // the machine that's executing the code -- but there doesn't seem to be 218 // an accessible alternative. 219 clientId = identity.String() 220 } else { 221 // If we're running state anonymously, we can still use the lease 222 // manager; but we need to make sure we use a unique client ID, and 223 // will thus not be very performant. 224 logger.Infof("running state anonymously; using unique client id") 225 uuid, err := utils.NewUUID() 226 if err != nil { 227 return errors.Trace(err) 228 } 229 clientId = fmt.Sprintf("anon-%s", uuid.String()) 230 } 231 232 logger.Infof("creating lease clients as %s", clientId) 233 clock := GetClock() 234 datastore := &environMongo{st} 235 leadershipClient, err := statelease.NewClient(statelease.ClientConfig{ 236 Id: clientId, 237 Namespace: serviceLeadershipNamespace, 238 Collection: leasesC, 239 Mongo: datastore, 240 Clock: clock, 241 }) 242 if err != nil { 243 return errors.Annotatef(err, "cannot create leadership lease client") 244 } 245 st.leadershipClient = leadershipClient 246 logger.Infof("starting leadership lease manager") 247 leadershipManager, err := lease.NewManager(lease.ManagerConfig{ 248 Secretary: leadershipSecretary{}, 249 Client: leadershipClient, 250 Clock: clock, 251 MaxSleep: time.Minute, 252 }) 253 if err != nil { 254 return errors.Annotatef(err, "cannot create leadership lease manager") 255 } 256 st.leadershipManager = leadershipManager 257 258 singularClient, err := statelease.NewClient(statelease.ClientConfig{ 259 Id: clientId, 260 Namespace: singularControllerNamespace, 261 Collection: leasesC, 262 Mongo: datastore, 263 Clock: clock, 264 }) 265 if err != nil { 266 return errors.Annotatef(err, "cannot create singular lease client") 267 } 268 logger.Infof("starting singular lease manager") 269 singularManager, err := lease.NewManager(lease.ManagerConfig{ 270 Secretary: singularSecretary{st.modelTag.Id()}, 271 Client: singularClient, 272 Clock: clock, 273 MaxSleep: time.Minute, 274 }) 275 if err != nil { 276 return errors.Annotatef(err, "cannot create singular lease manager") 277 } 278 st.singularManager = singularManager 279 280 logger.Infof("creating cloud image metadata storage") 281 st.CloudImageMetadataStorage = cloudimagemetadata.NewStorage(st.ModelUUID(), cloudimagemetadataC, datastore) 282 283 logger.Infof("starting presence watcher") 284 st.pwatcher = presence.NewWatcher(st.getPresence(), st.modelTag) 285 return nil 286 } 287 288 // ModelTag() returns the model tag for the model controlled by 289 // this state instance. 290 func (st *State) ModelTag() names.ModelTag { 291 return st.modelTag 292 } 293 294 // ModelUUID returns the model UUID for the model 295 // controlled by this state instance. 296 func (st *State) ModelUUID() string { 297 return st.modelTag.Id() 298 } 299 300 // userModelNameIndex returns a string to be used as a usermodelnameC unique index. 301 func userModelNameIndex(username, envName string) string { 302 return strings.ToLower(username) + ":" + envName 303 } 304 305 // EnsureModelRemoved returns an error if any multi-model 306 // documents for this model are found. It is intended only to be used in 307 // tests and exported so it can be used in the tests of other packages. 308 func (st *State) EnsureModelRemoved() error { 309 found := map[string]int{} 310 var foundOrdered []string 311 for name, info := range st.database.Schema() { 312 if info.global { 313 continue 314 } 315 coll, closer := st.getCollection(name) 316 defer closer() 317 n, err := coll.Find(nil).Count() 318 if err != nil { 319 return errors.Trace(err) 320 } 321 if n != 0 { 322 found[name] = n 323 foundOrdered = append(foundOrdered, name) 324 } 325 } 326 327 if len(found) != 0 { 328 errMessage := fmt.Sprintf("found documents for model with uuid %s:", st.ModelUUID()) 329 sort.Strings(foundOrdered) 330 for _, name := range foundOrdered { 331 number := found[name] 332 errMessage += fmt.Sprintf(" %d %s doc,", number, name) 333 } 334 // Remove trailing comma. 335 errMessage = errMessage[:len(errMessage)-1] 336 return errors.New(errMessage) 337 } 338 return nil 339 } 340 341 // getPresence returns the presence m. 342 func (st *State) getPresence() *mgo.Collection { 343 return st.session.DB(presenceDB).C(presenceC) 344 } 345 346 // newDB returns a database connection using a new session, along with 347 // a closer function for the session. This is useful where you need to work 348 // with various collections in a single session, so don't want to call 349 // getCollection multiple times. 350 func (st *State) newDB() (Database, func()) { 351 return st.database.CopySession() 352 } 353 354 // Ping probes the state's database connection to ensure 355 // that it is still alive. 356 func (st *State) Ping() error { 357 return st.session.Ping() 358 } 359 360 // MongoVersion return the string repre 361 func (st *State) MongoVersion() (string, error) { 362 binfo, err := st.session.BuildInfo() 363 if err != nil { 364 return "", errors.Annotate(err, "cannot obtain mongo build info") 365 } 366 return binfo.Version, nil 367 } 368 369 // MongoSession returns the underlying mongodb session 370 // used by the state. It is exposed so that external code 371 // can maintain the mongo replica set and should not 372 // otherwise be used. 373 func (st *State) MongoSession() *mgo.Session { 374 return st.session 375 } 376 377 func (st *State) Watch() *Multiwatcher { 378 st.mu.Lock() 379 if st.allManager == nil { 380 st.allManager = newStoreManager(newAllWatcherStateBacking(st)) 381 } 382 st.mu.Unlock() 383 return NewMultiwatcher(st.allManager) 384 } 385 386 func (st *State) WatchAllModels() *Multiwatcher { 387 st.mu.Lock() 388 if st.allModelManager == nil { 389 st.allModelWatcherBacking = NewAllModelWatcherStateBacking(st) 390 st.allModelManager = newStoreManager(st.allModelWatcherBacking) 391 } 392 st.mu.Unlock() 393 return NewMultiwatcher(st.allModelManager) 394 } 395 396 func (st *State) ModelConfig() (*config.Config, error) { 397 settings, err := readSettings(st, modelGlobalKey) 398 if err != nil { 399 return nil, errors.Trace(err) 400 } 401 attrs := settings.Map() 402 return config.New(config.NoDefaults, attrs) 403 } 404 405 // checkModelConfig returns an error if the config is definitely invalid. 406 func checkModelConfig(cfg *config.Config) error { 407 if cfg.AdminSecret() != "" { 408 return errors.Errorf("admin-secret should never be written to the state") 409 } 410 if _, ok := cfg.AgentVersion(); !ok { 411 return errors.Errorf("agent-version must always be set in state") 412 } 413 return nil 414 } 415 416 // versionInconsistentError indicates one or more agents have a 417 // different version from the current one (even empty, when not yet 418 // set). 419 type versionInconsistentError struct { 420 currentVersion version.Number 421 agents []string 422 } 423 424 func (e *versionInconsistentError) Error() string { 425 sort.Strings(e.agents) 426 return fmt.Sprintf("some agents have not upgraded to the current model version %s: %s", e.currentVersion, strings.Join(e.agents, ", ")) 427 } 428 429 // newVersionInconsistentError returns a new instance of 430 // versionInconsistentError. 431 func newVersionInconsistentError(currentVersion version.Number, agents []string) *versionInconsistentError { 432 return &versionInconsistentError{currentVersion, agents} 433 } 434 435 // IsVersionInconsistentError returns if the given error is 436 // versionInconsistentError. 437 func IsVersionInconsistentError(e interface{}) bool { 438 value := e 439 // In case of a wrapped error, check the cause first. 440 cause := errors.Cause(e.(error)) 441 if cause != nil { 442 value = cause 443 } 444 _, ok := value.(*versionInconsistentError) 445 return ok 446 } 447 448 func (st *State) checkCanUpgrade(currentVersion, newVersion string) error { 449 matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-" 450 matchNew := "^" + regexp.QuoteMeta(newVersion) + "-" 451 // Get all machines and units with a different or empty version. 452 sel := bson.D{{"$or", []bson.D{ 453 {{"tools", bson.D{{"$exists", false}}}}, 454 {{"$and", []bson.D{ 455 {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}}, 456 {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}}, 457 }}}, 458 }}} 459 var agentTags []string 460 for _, name := range []string{machinesC, unitsC} { 461 collection, closer := st.getCollection(name) 462 defer closer() 463 var doc struct { 464 DocID string `bson:"_id"` 465 } 466 iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter() 467 for iter.Next(&doc) { 468 localID, err := st.strictLocalID(doc.DocID) 469 if err != nil { 470 return errors.Trace(err) 471 } 472 switch name { 473 case machinesC: 474 agentTags = append(agentTags, names.NewMachineTag(localID).String()) 475 case unitsC: 476 agentTags = append(agentTags, names.NewUnitTag(localID).String()) 477 } 478 } 479 if err := iter.Close(); err != nil { 480 return errors.Trace(err) 481 } 482 } 483 if len(agentTags) > 0 { 484 err := newVersionInconsistentError(version.MustParse(currentVersion), agentTags) 485 return errors.Trace(err) 486 } 487 return nil 488 } 489 490 var errUpgradeInProgress = errors.New(params.CodeUpgradeInProgress) 491 492 // IsUpgradeInProgressError returns true if the error is cause by an 493 // upgrade in progress 494 func IsUpgradeInProgressError(err error) bool { 495 return errors.Cause(err) == errUpgradeInProgress 496 } 497 498 // SetModelAgentVersion changes the agent version for the model to the 499 // given version, only if the model is in a stable state (all agents are 500 // running the current version). If this is a hosted model, newVersion 501 // cannot be higher than the controller version. 502 func (st *State) SetModelAgentVersion(newVersion version.Number) (err error) { 503 if newVersion.Compare(jujuversion.Current) > 0 && !st.IsController() { 504 return errors.Errorf("a hosted model cannot have a higher version than the server model: %s > %s", 505 newVersion.String(), 506 jujuversion.Current, 507 ) 508 } 509 510 buildTxn := func(attempt int) ([]txn.Op, error) { 511 settings, err := readSettings(st, modelGlobalKey) 512 if err != nil { 513 return nil, errors.Trace(err) 514 } 515 agentVersion, ok := settings.Get("agent-version") 516 if !ok { 517 return nil, errors.Errorf("no agent version set in the model") 518 } 519 currentVersion, ok := agentVersion.(string) 520 if !ok { 521 return nil, errors.Errorf("invalid agent version format: expected string, got %v", agentVersion) 522 } 523 if newVersion.String() == currentVersion { 524 // Nothing to do. 525 return nil, jujutxn.ErrNoOperations 526 } 527 528 if err := st.checkCanUpgrade(currentVersion, newVersion.String()); err != nil { 529 return nil, errors.Trace(err) 530 } 531 532 ops := []txn.Op{ 533 // Can't set agent-version if there's an active upgradeInfo doc. 534 { 535 C: upgradeInfoC, 536 Id: currentUpgradeId, 537 Assert: txn.DocMissing, 538 }, { 539 C: settingsC, 540 Id: st.docID(modelGlobalKey), 541 Assert: bson.D{{"version", settings.version}}, 542 Update: bson.D{ 543 {"$set", bson.D{{"settings.agent-version", newVersion.String()}}}, 544 }, 545 }, 546 } 547 return ops, nil 548 } 549 if err = st.run(buildTxn); err == jujutxn.ErrExcessiveContention { 550 // Although there is a small chance of a race here, try to 551 // return a more helpful error message in the case of an 552 // active upgradeInfo document being in place. 553 if upgrading, _ := st.IsUpgrading(); upgrading { 554 err = errUpgradeInProgress 555 } else { 556 err = errors.Annotate(err, "cannot set agent version") 557 } 558 } 559 return errors.Trace(err) 560 } 561 562 func (st *State) buildAndValidateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) (validCfg *config.Config, err error) { 563 newConfig, err := oldConfig.Apply(updateAttrs) 564 if err != nil { 565 return nil, errors.Trace(err) 566 } 567 if len(removeAttrs) != 0 { 568 newConfig, err = newConfig.Remove(removeAttrs) 569 if err != nil { 570 return nil, errors.Trace(err) 571 } 572 } 573 if err := checkModelConfig(newConfig); err != nil { 574 return nil, errors.Trace(err) 575 } 576 return st.validate(newConfig, oldConfig) 577 } 578 579 type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error 580 581 // UpdateModelConfig adds, updates or removes attributes in the current 582 // configuration of the model with the provided updateAttrs and 583 // removeAttrs. 584 func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error { 585 if len(updateAttrs)+len(removeAttrs) == 0 { 586 return nil 587 } 588 589 // TODO(axw) 2013-12-6 #1167616 590 // Ensure that the settings on disk have not changed 591 // underneath us. The settings changes are actually 592 // applied as a delta to what's on disk; if there has 593 // been a concurrent update, the change may not be what 594 // the user asked for. 595 settings, err := readSettings(st, modelGlobalKey) 596 if err != nil { 597 return errors.Trace(err) 598 } 599 600 // Get the existing model config from state. 601 oldConfig, err := config.New(config.NoDefaults, settings.Map()) 602 if err != nil { 603 return errors.Trace(err) 604 } 605 if additionalValidation != nil { 606 err = additionalValidation(updateAttrs, removeAttrs, oldConfig) 607 if err != nil { 608 return errors.Trace(err) 609 } 610 } 611 validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig) 612 if err != nil { 613 return errors.Trace(err) 614 } 615 616 validAttrs := validCfg.AllAttrs() 617 for k := range oldConfig.AllAttrs() { 618 if _, ok := validAttrs[k]; !ok { 619 settings.Delete(k) 620 } 621 } 622 settings.Update(validAttrs) 623 _, err = settings.Write() 624 return errors.Trace(err) 625 } 626 627 // ModelConstraints returns the current model constraints. 628 func (st *State) ModelConstraints() (constraints.Value, error) { 629 cons, err := readConstraints(st, modelGlobalKey) 630 return cons, errors.Trace(err) 631 } 632 633 // SetModelConstraints replaces the current model constraints. 634 func (st *State) SetModelConstraints(cons constraints.Value) error { 635 unsupported, err := st.validateConstraints(cons) 636 if len(unsupported) > 0 { 637 logger.Warningf( 638 "setting model constraints: unsupported constraints: %v", strings.Join(unsupported, ",")) 639 } else if err != nil { 640 return errors.Trace(err) 641 } 642 return writeConstraints(st, modelGlobalKey, cons) 643 } 644 645 // AllMachines returns all machines in the model 646 // ordered by id. 647 func (st *State) AllMachines() (machines []*Machine, err error) { 648 machinesCollection, closer := st.getCollection(machinesC) 649 defer closer() 650 651 mdocs := machineDocSlice{} 652 err = machinesCollection.Find(nil).All(&mdocs) 653 if err != nil { 654 return nil, errors.Annotatef(err, "cannot get all machines") 655 } 656 sort.Sort(mdocs) 657 for _, doc := range mdocs { 658 machines = append(machines, newMachine(st, &doc)) 659 } 660 return 661 } 662 663 type machineDocSlice []machineDoc 664 665 func (ms machineDocSlice) Len() int { return len(ms) } 666 func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } 667 func (ms machineDocSlice) Less(i, j int) bool { 668 return machineIdLessThan(ms[i].Id, ms[j].Id) 669 } 670 671 // machineIdLessThan returns true if id1 < id2, false otherwise. 672 // Machine ids may include "/" separators if they are for a container so 673 // the comparison is done by comparing the id component values from 674 // left to right (most significant part to least significant). Ids for 675 // host machines are always less than ids for their containers. 676 func machineIdLessThan(id1, id2 string) bool { 677 // Most times, we are dealing with host machines and not containers, so we will 678 // try interpreting the ids as ints - this will be faster than dealing with the 679 // container ids below. 680 mint1, err1 := strconv.Atoi(id1) 681 mint2, err2 := strconv.Atoi(id2) 682 if err1 == nil && err2 == nil { 683 return mint1 < mint2 684 } 685 // We have at least one container id so it gets complicated. 686 idParts1 := strings.Split(id1, "/") 687 idParts2 := strings.Split(id2, "/") 688 nrParts1 := len(idParts1) 689 nrParts2 := len(idParts2) 690 minLen := nrParts1 691 if nrParts2 < minLen { 692 minLen = nrParts2 693 } 694 for x := 0; x < minLen; x++ { 695 m1 := idParts1[x] 696 m2 := idParts2[x] 697 if m1 == m2 { 698 continue 699 } 700 // See if the id part is a container type, and if so compare directly. 701 if x%2 == 1 { 702 return m1 < m2 703 } 704 // Compare the integer ids. 705 // There's nothing we can do with errors at this point. 706 mint1, _ := strconv.Atoi(m1) 707 mint2, _ := strconv.Atoi(m2) 708 return mint1 < mint2 709 } 710 return nrParts1 < nrParts2 711 } 712 713 // Machine returns the machine with the given id. 714 func (st *State) Machine(id string) (*Machine, error) { 715 mdoc, err := st.getMachineDoc(id) 716 if err != nil { 717 return nil, err 718 } 719 return newMachine(st, mdoc), nil 720 } 721 722 func (st *State) getMachineDoc(id string) (*machineDoc, error) { 723 machinesCollection, closer := st.getRawCollection(machinesC) 724 defer closer() 725 726 var err error 727 mdoc := &machineDoc{} 728 for _, tryId := range []string{st.docID(id), id} { 729 err = machinesCollection.FindId(tryId).One(mdoc) 730 if err != mgo.ErrNotFound { 731 break 732 } 733 } 734 switch err { 735 case nil: 736 // This is required to allow loading of machines before the 737 // model UUID migration has been applied to the machines 738 // collection. Without this, a machine agent can't come up to 739 // run the database migration. 740 if mdoc.Id == "" { 741 mdoc.Id = mdoc.DocID 742 } 743 return mdoc, nil 744 case mgo.ErrNotFound: 745 return nil, errors.NotFoundf("machine %s", id) 746 default: 747 return nil, errors.Annotatef(err, "cannot get machine %s", id) 748 } 749 } 750 751 // FindEntity returns the entity with the given tag. 752 // 753 // The returned value can be of type *Machine, *Unit, 754 // *User, *Service, *Model, or *Action, depending 755 // on the tag. 756 func (st *State) FindEntity(tag names.Tag) (Entity, error) { 757 id := tag.Id() 758 switch tag := tag.(type) { 759 case names.MachineTag: 760 return st.Machine(id) 761 case names.UnitTag: 762 return st.Unit(id) 763 case names.UserTag: 764 return st.User(tag) 765 case names.ServiceTag: 766 return st.Service(id) 767 case names.ModelTag: 768 env, err := st.Model() 769 if err != nil { 770 return nil, errors.Trace(err) 771 } 772 // Return an invalid entity error if the requested model is not 773 // the current one. 774 if id != env.UUID() { 775 if utils.IsValidUUIDString(id) { 776 return nil, errors.NotFoundf("model %q", id) 777 } 778 // TODO(axw) 2013-12-04 #1257587 779 // We should not accept model tags that do not match the 780 // model's UUID. We accept anything for now, to cater 781 // both for past usage, and for potentially supporting aliases. 782 logger.Warningf("model-tag does not match current model UUID: %q != %q", id, env.UUID()) 783 conf, err := st.ModelConfig() 784 if err != nil { 785 logger.Warningf("ModelConfig failed: %v", err) 786 } else if id != conf.Name() { 787 logger.Warningf("model-tag does not match current model name: %q != %q", id, conf.Name()) 788 } 789 } 790 return env, nil 791 case names.RelationTag: 792 return st.KeyRelation(id) 793 case names.IPAddressTag: 794 return st.IPAddressByTag(tag) 795 case names.ActionTag: 796 return st.ActionByTag(tag) 797 case names.CharmTag: 798 if url, err := charm.ParseURL(id); err != nil { 799 logger.Warningf("Parsing charm URL %q failed: %v", id, err) 800 return nil, errors.NotFoundf("could not find charm %q in state", id) 801 } else { 802 return st.Charm(url) 803 } 804 case names.VolumeTag: 805 return st.Volume(tag) 806 case names.FilesystemTag: 807 return st.Filesystem(tag) 808 default: 809 return nil, errors.Errorf("unsupported tag %T", tag) 810 } 811 } 812 813 // tagToCollectionAndId, given an entity tag, returns the collection name and id 814 // of the entity document. 815 func (st *State) tagToCollectionAndId(tag names.Tag) (string, interface{}, error) { 816 if tag == nil { 817 return "", nil, errors.Errorf("tag is nil") 818 } 819 coll := "" 820 id := tag.Id() 821 switch tag := tag.(type) { 822 case names.MachineTag: 823 coll = machinesC 824 id = st.docID(id) 825 case names.ServiceTag: 826 coll = servicesC 827 id = st.docID(id) 828 case names.UnitTag: 829 coll = unitsC 830 id = st.docID(id) 831 case names.UserTag: 832 coll = usersC 833 if !tag.IsLocal() { 834 return "", nil, fmt.Errorf("%q is not a local user", tag.Canonical()) 835 } 836 id = tag.Name() 837 case names.RelationTag: 838 coll = relationsC 839 id = st.docID(id) 840 case names.ModelTag: 841 coll = modelsC 842 case names.ActionTag: 843 coll = actionsC 844 id = tag.Id() 845 case names.CharmTag: 846 coll = charmsC 847 id = tag.Id() 848 default: 849 return "", nil, errors.Errorf("%q is not a valid collection tag", tag) 850 } 851 return coll, id, nil 852 } 853 854 // AddCharm adds the ch charm with curl to the state. 855 // On success the newly added charm state is returned. 856 func (st *State) AddCharm(info CharmInfo) (stch *Charm, err error) { 857 charms, closer := st.getCollection(charmsC) 858 defer closer() 859 860 if err := validateCharmVersion(info.Charm); err != nil { 861 return nil, errors.Trace(err) 862 } 863 864 query := charms.FindId(info.ID.String()).Select(bson.D{{"placeholder", 1}}) 865 866 buildTxn := func(attempt int) ([]txn.Op, error) { 867 var placeholderDoc struct { 868 Placeholder bool `bson:"placeholder"` 869 } 870 if err := query.One(&placeholderDoc); err == mgo.ErrNotFound { 871 872 return insertCharmOps(st, info) 873 } else if err != nil { 874 return nil, errors.Trace(err) 875 } else if placeholderDoc.Placeholder { 876 return updateCharmOps(st, info, stillPlaceholder) 877 } 878 return nil, errors.AlreadyExistsf("charm %q", info.ID) 879 } 880 if err = st.run(buildTxn); err == nil { 881 return st.Charm(info.ID) 882 } 883 return nil, errors.Trace(err) 884 } 885 886 type hasMeta interface { 887 Meta() *charm.Meta 888 } 889 890 func validateCharmVersion(ch hasMeta) error { 891 minver := ch.Meta().MinJujuVersion 892 if minver != version.Zero { 893 if minver.Compare(jujuversion.Current) > 0 { 894 return errors.Errorf("Charm's min version (%s) is higher than this juju environment's version (%s)", minver, jujuversion.Current) 895 } 896 } 897 return nil 898 } 899 900 // AllCharms returns all charms in state. 901 func (st *State) AllCharms() ([]*Charm, error) { 902 charmsCollection, closer := st.getCollection(charmsC) 903 defer closer() 904 var cdoc charmDoc 905 var charms []*Charm 906 iter := charmsCollection.Find(nil).Iter() 907 for iter.Next(&cdoc) { 908 ch := newCharm(st, &cdoc) 909 charms = append(charms, ch) 910 } 911 return charms, errors.Trace(iter.Close()) 912 } 913 914 // Charm returns the charm with the given URL. Charms pending upload 915 // to storage and placeholders are never returned. 916 func (st *State) Charm(curl *charm.URL) (*Charm, error) { 917 charms, closer := st.getCollection(charmsC) 918 defer closer() 919 920 cdoc := &charmDoc{} 921 what := bson.D{ 922 {"_id", curl.String()}, 923 {"placeholder", bson.D{{"$ne", true}}}, 924 {"pendingupload", bson.D{{"$ne", true}}}, 925 } 926 err := charms.Find(what).One(&cdoc) 927 if err == mgo.ErrNotFound { 928 return nil, errors.NotFoundf("charm %q", curl) 929 } 930 if err != nil { 931 return nil, errors.Annotatef(err, "cannot get charm %q", curl) 932 } 933 if err := cdoc.Meta.Check(); err != nil { 934 return nil, errors.Annotatef(err, "malformed charm metadata found in state") 935 } 936 return newCharm(st, cdoc), nil 937 } 938 939 // LatestPlaceholderCharm returns the latest charm described by the 940 // given URL but which is not yet deployed. 941 func (st *State) LatestPlaceholderCharm(curl *charm.URL) (*Charm, error) { 942 charms, closer := st.getCollection(charmsC) 943 defer closer() 944 945 noRevURL := curl.WithRevision(-1) 946 curlRegex := "^" + regexp.QuoteMeta(st.docID(noRevURL.String())) 947 var docs []charmDoc 948 err := charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).All(&docs) 949 if err != nil { 950 return nil, errors.Annotatef(err, "cannot get charm %q", curl) 951 } 952 // Find the highest revision. 953 var latest charmDoc 954 for _, doc := range docs { 955 if latest.URL == nil || doc.URL.Revision > latest.URL.Revision { 956 latest = doc 957 } 958 } 959 if latest.URL == nil { 960 return nil, errors.NotFoundf("placeholder charm %q", noRevURL) 961 } 962 return newCharm(st, &latest), nil 963 } 964 965 // PrepareLocalCharmUpload must be called before a local charm is 966 // uploaded to the provider storage in order to create a charm 967 // document in state. It returns the chosen unique charm URL reserved 968 // in state for the charm. 969 // 970 // The url's schema must be "local" and it must include a revision. 971 func (st *State) PrepareLocalCharmUpload(curl *charm.URL) (chosenUrl *charm.URL, err error) { 972 // Perform a few sanity checks first. 973 if curl.Schema != "local" { 974 return nil, errors.Errorf("expected charm URL with local schema, got %q", curl) 975 } 976 if curl.Revision < 0 { 977 return nil, errors.Errorf("expected charm URL with revision, got %q", curl) 978 } 979 // Get a regex with the charm URL and no revision. 980 noRevURL := curl.WithRevision(-1) 981 curlRegex := "^" + regexp.QuoteMeta(st.docID(noRevURL.String())) 982 983 charms, closer := st.getCollection(charmsC) 984 defer closer() 985 986 buildTxn := func(attempt int) ([]txn.Op, error) { 987 // Find the highest revision of that charm in state. 988 var docs []charmDoc 989 query := bson.D{{"_id", bson.D{{"$regex", curlRegex}}}} 990 err = charms.Find(query).Select(bson.D{{"_id", 1}, {"url", 1}}).All(&docs) 991 if err != nil { 992 return nil, errors.Trace(err) 993 } 994 // Find the highest revision. 995 maxRevision := -1 996 for _, doc := range docs { 997 if doc.URL.Revision > maxRevision { 998 maxRevision = doc.URL.Revision 999 } 1000 } 1001 1002 // Respect the local charm's revision first. 1003 chosenRevision := curl.Revision 1004 if maxRevision >= chosenRevision { 1005 // More recent revision exists in state, pick the next. 1006 chosenRevision = maxRevision + 1 1007 } 1008 chosenUrl = curl.WithRevision(chosenRevision) 1009 return insertPendingCharmOps(st, chosenUrl) 1010 } 1011 if err = st.run(buildTxn); err == nil { 1012 return chosenUrl, nil 1013 } 1014 return nil, errors.Trace(err) 1015 } 1016 1017 // PrepareStoreCharmUpload must be called before a charm store charm 1018 // is uploaded to the provider storage in order to create a charm 1019 // document in state. If a charm with the same URL is already in 1020 // state, it will be returned as a *state.Charm (it can be still 1021 // pending or already uploaded). Otherwise, a new charm document is 1022 // added in state with just the given charm URL and 1023 // PendingUpload=true, which is then returned as a *state.Charm. 1024 // 1025 // The url's schema must be "cs" and it must include a revision. 1026 func (st *State) PrepareStoreCharmUpload(curl *charm.URL) (*Charm, error) { 1027 // Perform a few sanity checks first. 1028 if curl.Schema != "cs" { 1029 return nil, errors.Errorf("expected charm URL with cs schema, got %q", curl) 1030 } 1031 if curl.Revision < 0 { 1032 return nil, errors.Errorf("expected charm URL with revision, got %q", curl) 1033 } 1034 1035 charms, closer := st.getCollection(charmsC) 1036 defer closer() 1037 1038 var ( 1039 uploadedCharm charmDoc 1040 err error 1041 ) 1042 buildTxn := func(attempt int) ([]txn.Op, error) { 1043 // Find an uploaded or pending charm with the given exact curl. 1044 err := charms.FindId(curl.String()).One(&uploadedCharm) 1045 switch { 1046 case err == mgo.ErrNotFound: 1047 uploadedCharm = charmDoc{ 1048 DocID: st.docID(curl.String()), 1049 ModelUUID: st.ModelTag().Id(), 1050 URL: curl, 1051 PendingUpload: true, 1052 } 1053 return insertAnyCharmOps(&uploadedCharm) 1054 case err != nil: 1055 return nil, errors.Trace(err) 1056 case uploadedCharm.Placeholder: 1057 // Update the fields of the document we're returning. 1058 uploadedCharm.PendingUpload = true 1059 uploadedCharm.Placeholder = false 1060 return convertPlaceholderCharmOps(uploadedCharm.DocID) 1061 default: 1062 // The charm exists and it's either uploaded or still 1063 // pending, but it's not a placeholder. In any case, 1064 // there's nothing to do. 1065 return nil, jujutxn.ErrNoOperations 1066 } 1067 } 1068 if err = st.run(buildTxn); err == nil { 1069 return newCharm(st, &uploadedCharm), nil 1070 } 1071 return nil, errors.Trace(err) 1072 } 1073 1074 var ( 1075 stillPending = bson.D{{"pendingupload", true}} 1076 stillPlaceholder = bson.D{{"placeholder", true}} 1077 ) 1078 1079 // AddStoreCharmPlaceholder creates a charm document in state for the given charm URL which 1080 // must reference a charm from the store. The charm document is marked as a placeholder which 1081 // means that if the charm is to be deployed, it will need to first be uploaded to env storage. 1082 func (st *State) AddStoreCharmPlaceholder(curl *charm.URL) (err error) { 1083 // Perform sanity checks first. 1084 if curl.Schema != "cs" { 1085 return errors.Errorf("expected charm URL with cs schema, got %q", curl) 1086 } 1087 if curl.Revision < 0 { 1088 return errors.Errorf("expected charm URL with revision, got %q", curl) 1089 } 1090 charms, closer := st.getCollection(charmsC) 1091 defer closer() 1092 1093 buildTxn := func(attempt int) ([]txn.Op, error) { 1094 // See if the charm already exists in state and exit early if that's the case. 1095 var doc charmDoc 1096 err := charms.Find(bson.D{{"_id", curl.String()}}).Select(bson.D{{"_id", 1}}).One(&doc) 1097 if err != nil && err != mgo.ErrNotFound { 1098 return nil, errors.Trace(err) 1099 } 1100 if err == nil { 1101 return nil, jujutxn.ErrNoOperations 1102 } 1103 1104 // Delete all previous placeholders so we don't fill up the database with unused data. 1105 deleteOps, err := deleteOldPlaceholderCharmsOps(st, charms, curl) 1106 if err != nil { 1107 return nil, errors.Trace(err) 1108 } 1109 insertOps, err := insertPlaceholderCharmOps(st, curl) 1110 if err != nil { 1111 return nil, errors.Trace(err) 1112 } 1113 ops := append(deleteOps, insertOps...) 1114 return ops, nil 1115 } 1116 return errors.Trace(st.run(buildTxn)) 1117 } 1118 1119 // UpdateUploadedCharm marks the given charm URL as uploaded and 1120 // updates the rest of its data, returning it as *state.Charm. 1121 func (st *State) UpdateUploadedCharm(info CharmInfo) (*Charm, error) { 1122 charms, closer := st.getCollection(charmsC) 1123 defer closer() 1124 1125 doc := &charmDoc{} 1126 err := charms.FindId(info.ID.String()).One(&doc) 1127 if err == mgo.ErrNotFound { 1128 return nil, errors.NotFoundf("charm %q", info.ID) 1129 } 1130 if err != nil { 1131 return nil, errors.Trace(err) 1132 } 1133 if !doc.PendingUpload { 1134 return nil, errors.Trace(&ErrCharmAlreadyUploaded{info.ID}) 1135 } 1136 1137 ops, err := updateCharmOps(st, info, stillPending) 1138 if err != nil { 1139 return nil, errors.Trace(err) 1140 } 1141 if err := st.runTransaction(ops); err != nil { 1142 return nil, onAbort(err, ErrCharmRevisionAlreadyModified) 1143 } 1144 return st.Charm(info.ID) 1145 } 1146 1147 // addPeerRelationsOps returns the operations necessary to add the 1148 // specified service peer relations to the state. 1149 func (st *State) addPeerRelationsOps(serviceName string, peers map[string]charm.Relation) ([]txn.Op, error) { 1150 var ops []txn.Op 1151 for _, rel := range peers { 1152 relId, err := st.sequence("relation") 1153 if err != nil { 1154 return nil, errors.Trace(err) 1155 } 1156 eps := []Endpoint{{ 1157 ServiceName: serviceName, 1158 Relation: rel, 1159 }} 1160 relKey := relationKey(eps) 1161 relDoc := &relationDoc{ 1162 DocID: st.docID(relKey), 1163 Key: relKey, 1164 ModelUUID: st.ModelUUID(), 1165 Id: relId, 1166 Endpoints: eps, 1167 Life: Alive, 1168 } 1169 ops = append(ops, txn.Op{ 1170 C: relationsC, 1171 Id: relDoc.DocID, 1172 Assert: txn.DocMissing, 1173 Insert: relDoc, 1174 }) 1175 } 1176 return ops, nil 1177 } 1178 1179 type AddServiceArgs struct { 1180 Name string 1181 Series string 1182 Owner string 1183 Charm *Charm 1184 Channel csparams.Channel 1185 Storage map[string]StorageConstraints 1186 EndpointBindings map[string]string 1187 Settings charm.Settings 1188 NumUnits int 1189 Placement []*instance.Placement 1190 Constraints constraints.Value 1191 Resources map[string]string 1192 } 1193 1194 // AddService creates a new service, running the supplied charm, with the 1195 // supplied name (which must be unique). If the charm defines peer relations, 1196 // they will be created automatically. 1197 func (st *State) AddService(args AddServiceArgs) (service *Service, err error) { 1198 defer errors.DeferredAnnotatef(&err, "cannot add service %q", args.Name) 1199 ownerTag, err := names.ParseUserTag(args.Owner) 1200 if err != nil { 1201 return nil, errors.Annotatef(err, "Invalid ownertag %s", args.Owner) 1202 } 1203 // Sanity checks. 1204 if !names.IsValidService(args.Name) { 1205 return nil, errors.Errorf("invalid name") 1206 } 1207 if args.Charm == nil { 1208 return nil, errors.Errorf("charm is nil") 1209 } 1210 1211 if err := validateCharmVersion(args.Charm); err != nil { 1212 return nil, errors.Trace(err) 1213 } 1214 1215 if exists, err := isNotDead(st, servicesC, args.Name); err != nil { 1216 return nil, errors.Trace(err) 1217 } else if exists { 1218 return nil, errors.Errorf("service already exists") 1219 } 1220 if err := checkModelActive(st); err != nil { 1221 return nil, errors.Trace(err) 1222 } 1223 if _, err := st.ModelUser(ownerTag); err != nil { 1224 return nil, errors.Trace(err) 1225 } 1226 if args.Storage == nil { 1227 args.Storage = make(map[string]StorageConstraints) 1228 } 1229 if err := addDefaultStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil { 1230 return nil, errors.Trace(err) 1231 } 1232 if err := validateStorageConstraints(st, args.Storage, args.Charm.Meta()); err != nil { 1233 return nil, errors.Trace(err) 1234 } 1235 storagePools := make(set.Strings) 1236 for _, storageParams := range args.Storage { 1237 storagePools.Add(storageParams.Pool) 1238 } 1239 1240 if args.Series == "" { 1241 // args.Series is not set, so use the series in the URL. 1242 args.Series = args.Charm.URL().Series 1243 if args.Series == "" { 1244 // Should not happen, but just in case. 1245 return nil, errors.New("series is empty") 1246 } 1247 } else { 1248 // User has specified series. Overriding supported series is 1249 // handled by the client, so args.Series is not necessarily 1250 // one of the charm's supported series. We require that the 1251 // specified series is of the same operating system as one of 1252 // the supported series. For old-style charms with the series 1253 // in the URL, that series is the one and only supported 1254 // series. 1255 var supportedSeries []string 1256 if series := args.Charm.URL().Series; series != "" { 1257 supportedSeries = []string{series} 1258 } else { 1259 supportedSeries = args.Charm.Meta().Series 1260 } 1261 if len(supportedSeries) > 0 { 1262 seriesOS, err := series.GetOSFromSeries(args.Series) 1263 if err != nil { 1264 return nil, errors.Trace(err) 1265 } 1266 supportedOperatingSystems := make(map[os.OSType]bool) 1267 for _, supportedSeries := range supportedSeries { 1268 os, err := series.GetOSFromSeries(supportedSeries) 1269 if err != nil { 1270 return nil, errors.Trace(err) 1271 } 1272 supportedOperatingSystems[os] = true 1273 } 1274 if !supportedOperatingSystems[seriesOS] { 1275 return nil, errors.NewNotSupported(errors.Errorf( 1276 "series %q (OS %q) not supported by charm, supported series are %q", 1277 args.Series, seriesOS, strings.Join(supportedSeries, ", "), 1278 ), "") 1279 } 1280 } 1281 } 1282 1283 for _, placement := range args.Placement { 1284 data, err := st.parsePlacement(placement) 1285 if err != nil { 1286 return nil, errors.Trace(err) 1287 } 1288 switch data.placementType() { 1289 case machinePlacement: 1290 // Ensure that the machine and charm series match. 1291 m, err := st.Machine(data.machineId) 1292 if err != nil { 1293 return nil, errors.Trace(err) 1294 } 1295 subordinate := args.Charm.Meta().Subordinate 1296 if err := validateUnitMachineAssignment( 1297 m, args.Series, subordinate, storagePools, 1298 ); err != nil { 1299 return nil, errors.Annotatef( 1300 err, "cannot deploy to machine %s", m, 1301 ) 1302 } 1303 1304 case directivePlacement: 1305 if err := st.precheckInstance(args.Series, args.Constraints, data.directive); err != nil { 1306 return nil, errors.Trace(err) 1307 } 1308 } 1309 } 1310 1311 serviceID := st.docID(args.Name) 1312 1313 // Create the service addition operations. 1314 peers := args.Charm.Meta().Peers 1315 1316 // The doc defaults to CharmModifiedVersion = 0, which is correct, since it 1317 // has, by definition, at its initial state. 1318 svcDoc := &serviceDoc{ 1319 DocID: serviceID, 1320 Name: args.Name, 1321 ModelUUID: st.ModelUUID(), 1322 Series: args.Series, 1323 Subordinate: args.Charm.Meta().Subordinate, 1324 CharmURL: args.Charm.URL(), 1325 Channel: string(args.Channel), 1326 RelationCount: len(peers), 1327 Life: Alive, 1328 OwnerTag: args.Owner, 1329 } 1330 1331 svc := newService(st, svcDoc) 1332 1333 endpointBindingsOp, err := createEndpointBindingsOp( 1334 st, svc.globalKey(), 1335 args.EndpointBindings, args.Charm.Meta(), 1336 ) 1337 if err != nil { 1338 return nil, errors.Trace(err) 1339 } 1340 1341 statusDoc := statusDoc{ 1342 ModelUUID: st.ModelUUID(), 1343 // TODO(fwereade): this violates the spec. Should be "waiting". 1344 // Implemented like this to be consistent with incorrect add-unit 1345 // behaviour. 1346 Status: status.StatusUnknown, 1347 StatusInfo: MessageWaitForAgentInit, 1348 // TODO(fwereade): 2016-03-17 lp:1558657 1349 Updated: time.Now().UnixNano(), 1350 // This exists to preserve questionable unit-aggregation behaviour 1351 // while we work out how to switch to an implementation that makes 1352 // sense. It is also set in AddMissingServiceStatuses. 1353 NeverSet: true, 1354 } 1355 1356 // The addServiceOps does not include the environment alive assertion, 1357 // so we add it here. 1358 ops := append( 1359 []txn.Op{ 1360 assertModelActiveOp(st.ModelUUID()), 1361 endpointBindingsOp, 1362 }, 1363 addServiceOps(st, addServiceOpsArgs{ 1364 serviceDoc: svcDoc, 1365 statusDoc: statusDoc, 1366 constraints: args.Constraints, 1367 storage: args.Storage, 1368 settings: map[string]interface{}(args.Settings), 1369 settingsRefCount: 1, 1370 })...) 1371 1372 // Collect peer relation addition operations. 1373 // 1374 // TODO(dimitern): Ensure each st.Endpoint has a space name associated in a 1375 // follow-up. 1376 peerOps, err := st.addPeerRelationsOps(args.Name, peers) 1377 if err != nil { 1378 return nil, errors.Trace(err) 1379 } 1380 ops = append(ops, peerOps...) 1381 1382 if len(args.Resources) > 0 { 1383 // Collect pending resource resolution operations. 1384 resources, err := st.Resources() 1385 if err != nil { 1386 return nil, errors.Trace(err) 1387 } 1388 resOps, err := resources.NewResolvePendingResourcesOps(args.Name, args.Resources) 1389 if err != nil { 1390 return nil, errors.Trace(err) 1391 } 1392 ops = append(ops, resOps...) 1393 } 1394 1395 // Collect unit-adding operations. 1396 for x := 0; x < args.NumUnits; x++ { 1397 unitName, unitOps, err := svc.addServiceUnitOps(serviceAddUnitOpsArgs{cons: args.Constraints, storageCons: args.Storage}) 1398 if err != nil { 1399 return nil, errors.Trace(err) 1400 } 1401 ops = append(ops, unitOps...) 1402 placement := instance.Placement{} 1403 if x < len(args.Placement) { 1404 placement = *args.Placement[x] 1405 } 1406 ops = append(ops, assignUnitOps(unitName, placement)...) 1407 } 1408 // At the last moment before inserting the service, prime status history. 1409 probablyUpdateStatusHistory(st, svc.globalKey(), statusDoc) 1410 1411 if err := st.runTransaction(ops); err == txn.ErrAborted { 1412 if err := checkModelActive(st); err != nil { 1413 return nil, errors.Trace(err) 1414 } 1415 return nil, errors.Errorf("service already exists") 1416 } else if err != nil { 1417 return nil, errors.Trace(err) 1418 } 1419 // Refresh to pick the txn-revno. 1420 if err = svc.Refresh(); err != nil { 1421 return nil, errors.Trace(err) 1422 } 1423 1424 return svc, nil 1425 } 1426 1427 // TODO(natefinch) DEMO code, revisit after demo! 1428 var AddServicePostFuncs = map[string]func(*State, AddServiceArgs) error{} 1429 1430 // assignUnitOps returns the db ops to save unit assignment for use by the 1431 // UnitAssigner worker. 1432 func assignUnitOps(unitName string, placement instance.Placement) []txn.Op { 1433 udoc := assignUnitDoc{ 1434 DocId: unitName, 1435 Scope: placement.Scope, 1436 Directive: placement.Directive, 1437 } 1438 return []txn.Op{{ 1439 C: assignUnitC, 1440 Id: udoc.DocId, 1441 Assert: txn.DocMissing, 1442 Insert: udoc, 1443 }} 1444 } 1445 1446 // AssignStagedUnits gets called by the UnitAssigner worker, and runs the given 1447 // assignments. 1448 func (st *State) AssignStagedUnits(ids []string) ([]UnitAssignmentResult, error) { 1449 query := bson.D{{"_id", bson.D{{"$in", ids}}}} 1450 unitAssignments, err := st.unitAssignments(query) 1451 if err != nil { 1452 return nil, errors.Annotate(err, "getting staged unit assignments") 1453 } 1454 results := make([]UnitAssignmentResult, len(unitAssignments)) 1455 for i, a := range unitAssignments { 1456 err := st.assignStagedUnit(a) 1457 results[i].Unit = a.Unit 1458 results[i].Error = err 1459 } 1460 return results, nil 1461 } 1462 1463 // UnitAssignments returns all staged unit assignments in the model. 1464 func (st *State) AllUnitAssignments() ([]UnitAssignment, error) { 1465 return st.unitAssignments(nil) 1466 } 1467 1468 func (st *State) unitAssignments(query bson.D) ([]UnitAssignment, error) { 1469 col, close := st.getCollection(assignUnitC) 1470 defer close() 1471 1472 var docs []assignUnitDoc 1473 if err := col.Find(query).All(&docs); err != nil { 1474 return nil, errors.Annotatef(err, "cannot get unit assignment docs") 1475 } 1476 results := make([]UnitAssignment, len(docs)) 1477 for i, doc := range docs { 1478 results[i] = UnitAssignment{ 1479 st.localID(doc.DocId), 1480 doc.Scope, 1481 doc.Directive, 1482 } 1483 } 1484 return results, nil 1485 } 1486 1487 func removeStagedAssignmentOp(id string) txn.Op { 1488 return txn.Op{ 1489 C: assignUnitC, 1490 Id: id, 1491 Remove: true, 1492 } 1493 } 1494 1495 func (st *State) assignStagedUnit(a UnitAssignment) error { 1496 u, err := st.Unit(a.Unit) 1497 if err != nil { 1498 return errors.Trace(err) 1499 } 1500 if a.Scope == "" && a.Directive == "" { 1501 return errors.Trace(st.AssignUnit(u, AssignCleanEmpty)) 1502 } 1503 1504 placement := &instance.Placement{Scope: a.Scope, Directive: a.Directive} 1505 1506 return errors.Trace(st.AssignUnitWithPlacement(u, placement)) 1507 } 1508 1509 // AssignUnitWithPlacement chooses a machine using the given placement directive 1510 // and then assigns the unit to it. 1511 func (st *State) AssignUnitWithPlacement(unit *Unit, placement *instance.Placement) error { 1512 // TODO(natefinch) this should be done as a single transaction, not two. 1513 // Mark https://launchpad.net/bugs/1506994 fixed when done. 1514 1515 m, err := st.addMachineWithPlacement(unit, placement) 1516 if err != nil { 1517 return errors.Trace(err) 1518 } 1519 return unit.AssignToMachine(m) 1520 } 1521 1522 // placementData is a helper type that encodes some of the logic behind how an 1523 // instance.Placement gets translated into a placement directive the providers 1524 // understand. 1525 type placementData struct { 1526 machineId string 1527 directive string 1528 containerType instance.ContainerType 1529 } 1530 1531 type placementType int 1532 1533 const ( 1534 containerPlacement placementType = iota 1535 directivePlacement 1536 machinePlacement 1537 ) 1538 1539 // placementType returns the type of placement that this data represents. 1540 func (p placementData) placementType() placementType { 1541 if p.containerType != "" { 1542 return containerPlacement 1543 } 1544 if p.directive != "" { 1545 return directivePlacement 1546 } 1547 return machinePlacement 1548 } 1549 1550 func (st *State) parsePlacement(placement *instance.Placement) (*placementData, error) { 1551 // Extract container type and parent from container placement directives. 1552 if container, err := instance.ParseContainerType(placement.Scope); err == nil { 1553 return &placementData{ 1554 containerType: container, 1555 machineId: placement.Directive, 1556 }, nil 1557 } 1558 switch placement.Scope { 1559 case st.ModelUUID(): 1560 return &placementData{directive: placement.Directive}, nil 1561 case instance.MachineScope: 1562 return &placementData{machineId: placement.Directive}, nil 1563 default: 1564 return nil, errors.Errorf("placement scope: invalid model UUID %q", placement.Scope) 1565 } 1566 } 1567 1568 // addMachineWithPlacement finds a machine that matches the given placement directive for the given unit. 1569 func (st *State) addMachineWithPlacement(unit *Unit, placement *instance.Placement) (*Machine, error) { 1570 unitCons, err := unit.Constraints() 1571 if err != nil { 1572 return nil, err 1573 } 1574 1575 data, err := st.parsePlacement(placement) 1576 if err != nil { 1577 return nil, errors.Trace(err) 1578 } 1579 1580 // Create any new machine marked as dirty so that 1581 // nothing else will grab it before we assign the unit to it. 1582 // TODO(natefinch) fix this when we put assignment in the same 1583 // transaction as adding a machine. See bug 1584 // https://launchpad.net/bugs/1506994 1585 1586 switch data.placementType() { 1587 case containerPlacement: 1588 // If a container is to be used, create it. 1589 template := MachineTemplate{ 1590 Series: unit.Series(), 1591 Jobs: []MachineJob{JobHostUnits}, 1592 Dirty: true, 1593 Constraints: *unitCons, 1594 } 1595 return st.AddMachineInsideMachine(template, data.machineId, data.containerType) 1596 case directivePlacement: 1597 // If a placement directive is to be used, do that here. 1598 template := MachineTemplate{ 1599 Series: unit.Series(), 1600 Jobs: []MachineJob{JobHostUnits}, 1601 Dirty: true, 1602 Constraints: *unitCons, 1603 Placement: data.directive, 1604 } 1605 return st.AddOneMachine(template) 1606 default: 1607 // Otherwise use an existing machine. 1608 return st.Machine(data.machineId) 1609 } 1610 } 1611 1612 // AddIPAddress creates and returns a new IP address. It can return an 1613 // error satisfying IsNotValid() or IsAlreadyExists() when the addr 1614 // does not contain a valid IP, or when addr is already added. 1615 func (st *State) AddIPAddress(addr network.Address, subnetID string) (*IPAddress, error) { 1616 return addIPAddress(st, addr, subnetID) 1617 } 1618 1619 // IPAddress returns an existing IP address from the state. 1620 func (st *State) IPAddress(value string) (*IPAddress, error) { 1621 return ipAddress(st, value) 1622 } 1623 1624 // IPAddressByTag returns an existing IP address from the state 1625 // identified by its tag. 1626 func (st *State) IPAddressByTag(tag names.IPAddressTag) (*IPAddress, error) { 1627 return ipAddressByTag(st, tag) 1628 } 1629 1630 // AllocatedIPAddresses returns all the allocated addresses for a machine 1631 func (st *State) AllocatedIPAddresses(machineId string) ([]*IPAddress, error) { 1632 return fetchIPAddresses(st, bson.D{{"machineid", machineId}}) 1633 } 1634 1635 // DeadIPAddresses returns all IP addresses with a Life of Dead 1636 func (st *State) DeadIPAddresses() ([]*IPAddress, error) { 1637 return fetchIPAddresses(st, isDeadDoc) 1638 } 1639 1640 // AddSubnet creates and returns a new subnet 1641 func (st *State) AddSubnet(args SubnetInfo) (subnet *Subnet, err error) { 1642 defer errors.DeferredAnnotatef(&err, "adding subnet %q", args.CIDR) 1643 1644 subnetID := st.docID(args.CIDR) 1645 var modelLocalProviderID string 1646 if args.ProviderId != "" { 1647 modelLocalProviderID = st.docID(string(args.ProviderId)) 1648 } 1649 1650 subDoc := subnetDoc{ 1651 DocID: subnetID, 1652 ModelUUID: st.ModelUUID(), 1653 Life: Alive, 1654 CIDR: args.CIDR, 1655 VLANTag: args.VLANTag, 1656 ProviderId: modelLocalProviderID, 1657 AllocatableIPHigh: args.AllocatableIPHigh, 1658 AllocatableIPLow: args.AllocatableIPLow, 1659 AvailabilityZone: args.AvailabilityZone, 1660 SpaceName: args.SpaceName, 1661 } 1662 subnet = &Subnet{doc: subDoc, st: st} 1663 err = subnet.Validate() 1664 if err != nil { 1665 return nil, err 1666 } 1667 ops := []txn.Op{ 1668 assertModelActiveOp(st.ModelUUID()), 1669 { 1670 C: subnetsC, 1671 Id: subnetID, 1672 Assert: txn.DocMissing, 1673 Insert: subDoc, 1674 }, 1675 } 1676 1677 err = st.runTransaction(ops) 1678 switch err { 1679 case txn.ErrAborted: 1680 if err := checkModelActive(st); err != nil { 1681 return nil, errors.Trace(err) 1682 } 1683 if _, err = st.Subnet(args.CIDR); err == nil { 1684 return nil, errors.AlreadyExistsf("subnet %q", args.CIDR) 1685 } else if err != nil { 1686 return nil, errors.Trace(err) 1687 } 1688 case nil: 1689 // If the ProviderId was not unique adding the subnet can fail without 1690 // an error. Refreshing catches this by returning NotFoundError. 1691 err = subnet.Refresh() 1692 if err != nil { 1693 if errors.IsNotFound(err) { 1694 return nil, errors.Errorf("ProviderId %q not unique", args.ProviderId) 1695 } 1696 return nil, errors.Trace(err) 1697 } 1698 return subnet, nil 1699 } 1700 return nil, errors.Trace(err) 1701 } 1702 1703 func (st *State) Subnet(cidr string) (*Subnet, error) { 1704 subnets, closer := st.getCollection(subnetsC) 1705 defer closer() 1706 1707 doc := &subnetDoc{} 1708 err := subnets.FindId(cidr).One(doc) 1709 if err == mgo.ErrNotFound { 1710 return nil, errors.NotFoundf("subnet %q", cidr) 1711 } 1712 if err != nil { 1713 return nil, errors.Annotatef(err, "cannot get subnet %q", cidr) 1714 } 1715 return &Subnet{st, *doc}, nil 1716 } 1717 1718 // AllSubnets returns all known subnets in the model. 1719 func (st *State) AllSubnets() (subnets []*Subnet, err error) { 1720 subnetsCollection, closer := st.getCollection(subnetsC) 1721 defer closer() 1722 1723 docs := []subnetDoc{} 1724 err = subnetsCollection.Find(nil).All(&docs) 1725 if err != nil { 1726 return nil, errors.Annotatef(err, "cannot get all subnets") 1727 } 1728 for _, doc := range docs { 1729 subnets = append(subnets, &Subnet{st, doc}) 1730 } 1731 return subnets, nil 1732 } 1733 1734 // Service returns a service state by name. 1735 func (st *State) Service(name string) (service *Service, err error) { 1736 services, closer := st.getCollection(servicesC) 1737 defer closer() 1738 1739 if !names.IsValidService(name) { 1740 return nil, errors.Errorf("%q is not a valid service name", name) 1741 } 1742 sdoc := &serviceDoc{} 1743 err = services.FindId(name).One(sdoc) 1744 if err == mgo.ErrNotFound { 1745 return nil, errors.NotFoundf("service %q", name) 1746 } 1747 if err != nil { 1748 return nil, errors.Annotatef(err, "cannot get service %q", name) 1749 } 1750 return newService(st, sdoc), nil 1751 } 1752 1753 // AllServices returns all deployed services in the model. 1754 func (st *State) AllServices() (services []*Service, err error) { 1755 servicesCollection, closer := st.getCollection(servicesC) 1756 defer closer() 1757 1758 sdocs := []serviceDoc{} 1759 err = servicesCollection.Find(bson.D{}).All(&sdocs) 1760 if err != nil { 1761 return nil, errors.Errorf("cannot get all services") 1762 } 1763 for _, v := range sdocs { 1764 services = append(services, newService(st, &v)) 1765 } 1766 return services, nil 1767 } 1768 1769 // docID generates a globally unique id value 1770 // where the model uuid is prefixed to the 1771 // localID. 1772 func (st *State) docID(localID string) string { 1773 return ensureModelUUID(st.ModelUUID(), localID) 1774 } 1775 1776 // localID returns the local id value by stripping 1777 // off the model uuid prefix if it is there. 1778 func (st *State) localID(ID string) string { 1779 modelUUID, localID, ok := splitDocID(ID) 1780 if !ok || modelUUID != st.ModelUUID() { 1781 return ID 1782 } 1783 return localID 1784 } 1785 1786 // strictLocalID returns the local id value by removing the 1787 // model UUID prefix. 1788 // 1789 // If there is no prefix matching the State's model, an error is 1790 // returned. 1791 func (st *State) strictLocalID(ID string) (string, error) { 1792 modelUUID, localID, ok := splitDocID(ID) 1793 if !ok || modelUUID != st.ModelUUID() { 1794 return "", errors.Errorf("unexpected id: %#v", ID) 1795 } 1796 return localID, nil 1797 } 1798 1799 // InferEndpoints returns the endpoints corresponding to the supplied names. 1800 // There must be 1 or 2 supplied names, of the form <service>[:<relation>]. 1801 // If the supplied names uniquely specify a possible relation, or if they 1802 // uniquely specify a possible relation once all implicit relations have been 1803 // filtered, the endpoints corresponding to that relation will be returned. 1804 func (st *State) InferEndpoints(names ...string) ([]Endpoint, error) { 1805 // Collect all possible sane endpoint lists. 1806 var candidates [][]Endpoint 1807 switch len(names) { 1808 case 1: 1809 eps, err := st.endpoints(names[0], isPeer) 1810 if err != nil { 1811 return nil, errors.Trace(err) 1812 } 1813 for _, ep := range eps { 1814 candidates = append(candidates, []Endpoint{ep}) 1815 } 1816 case 2: 1817 eps1, err := st.endpoints(names[0], notPeer) 1818 if err != nil { 1819 return nil, errors.Trace(err) 1820 } 1821 eps2, err := st.endpoints(names[1], notPeer) 1822 if err != nil { 1823 return nil, errors.Trace(err) 1824 } 1825 for _, ep1 := range eps1 { 1826 for _, ep2 := range eps2 { 1827 if ep1.CanRelateTo(ep2) && containerScopeOk(st, ep1, ep2) { 1828 candidates = append(candidates, []Endpoint{ep1, ep2}) 1829 } 1830 } 1831 } 1832 default: 1833 return nil, errors.Errorf("cannot relate %d endpoints", len(names)) 1834 } 1835 // If there's ambiguity, try discarding implicit relations. 1836 switch len(candidates) { 1837 case 0: 1838 return nil, errors.Errorf("no relations found") 1839 case 1: 1840 return candidates[0], nil 1841 } 1842 var filtered [][]Endpoint 1843 outer: 1844 for _, cand := range candidates { 1845 for _, ep := range cand { 1846 if ep.IsImplicit() { 1847 continue outer 1848 } 1849 } 1850 filtered = append(filtered, cand) 1851 } 1852 if len(filtered) == 1 { 1853 return filtered[0], nil 1854 } 1855 keys := []string{} 1856 for _, cand := range candidates { 1857 keys = append(keys, fmt.Sprintf("%q", relationKey(cand))) 1858 } 1859 sort.Strings(keys) 1860 return nil, errors.Errorf("ambiguous relation: %q could refer to %s", 1861 strings.Join(names, " "), strings.Join(keys, "; ")) 1862 } 1863 1864 func isPeer(ep Endpoint) bool { 1865 return ep.Role == charm.RolePeer 1866 } 1867 1868 func notPeer(ep Endpoint) bool { 1869 return ep.Role != charm.RolePeer 1870 } 1871 1872 func containerScopeOk(st *State, ep1, ep2 Endpoint) bool { 1873 if ep1.Scope != charm.ScopeContainer && ep2.Scope != charm.ScopeContainer { 1874 return true 1875 } 1876 var subordinateCount int 1877 for _, ep := range []Endpoint{ep1, ep2} { 1878 svc, err := st.Service(ep.ServiceName) 1879 if err != nil { 1880 return false 1881 } 1882 if svc.doc.Subordinate { 1883 subordinateCount++ 1884 } 1885 } 1886 return subordinateCount >= 1 1887 } 1888 1889 // endpoints returns all endpoints that could be intended by the 1890 // supplied endpoint name, and which cause the filter param to 1891 // return true. 1892 func (st *State) endpoints(name string, filter func(ep Endpoint) bool) ([]Endpoint, error) { 1893 var svcName, relName string 1894 if i := strings.Index(name, ":"); i == -1 { 1895 svcName = name 1896 } else if i != 0 && i != len(name)-1 { 1897 svcName = name[:i] 1898 relName = name[i+1:] 1899 } else { 1900 return nil, errors.Errorf("invalid endpoint %q", name) 1901 } 1902 svc, err := st.Service(svcName) 1903 if err != nil { 1904 return nil, errors.Trace(err) 1905 } 1906 eps := []Endpoint{} 1907 if relName != "" { 1908 ep, err := svc.Endpoint(relName) 1909 if err != nil { 1910 return nil, errors.Trace(err) 1911 } 1912 eps = append(eps, ep) 1913 } else { 1914 eps, err = svc.Endpoints() 1915 if err != nil { 1916 return nil, errors.Trace(err) 1917 } 1918 } 1919 final := []Endpoint{} 1920 for _, ep := range eps { 1921 if filter(ep) { 1922 final = append(final, ep) 1923 } 1924 } 1925 return final, nil 1926 } 1927 1928 // AddRelation creates a new relation with the given endpoints. 1929 func (st *State) AddRelation(eps ...Endpoint) (r *Relation, err error) { 1930 key := relationKey(eps) 1931 defer errors.DeferredAnnotatef(&err, "cannot add relation %q", key) 1932 // Enforce basic endpoint sanity. The epCount restrictions may be relaxed 1933 // in the future; if so, this method is likely to need significant rework. 1934 if len(eps) != 2 { 1935 return nil, errors.Errorf("relation must have two endpoints") 1936 } 1937 if !eps[0].CanRelateTo(eps[1]) { 1938 return nil, errors.Errorf("endpoints do not relate") 1939 } 1940 // If either endpoint has container scope, so must the other; and the 1941 // services's series must also match, because they'll be deployed to 1942 // the same machines. 1943 matchSeries := true 1944 if eps[0].Scope == charm.ScopeContainer { 1945 eps[1].Scope = charm.ScopeContainer 1946 } else if eps[1].Scope == charm.ScopeContainer { 1947 eps[0].Scope = charm.ScopeContainer 1948 } else { 1949 matchSeries = false 1950 } 1951 // We only get a unique relation id once, to save on roundtrips. If it's 1952 // -1, we haven't got it yet (we don't get it at this stage, because we 1953 // still don't know whether it's sane to even attempt creation). 1954 id := -1 1955 // If a service's charm is upgraded while we're trying to add a relation, 1956 // we'll need to re-validate service sanity. 1957 var doc *relationDoc 1958 buildTxn := func(attempt int) ([]txn.Op, error) { 1959 // Perform initial relation sanity check. 1960 if exists, err := isNotDead(st, relationsC, key); err != nil { 1961 return nil, errors.Trace(err) 1962 } else if exists { 1963 return nil, errors.Errorf("relation already exists") 1964 } 1965 // Collect per-service operations, checking sanity as we go. 1966 var ops []txn.Op 1967 var subordinateCount int 1968 series := map[string]bool{} 1969 for _, ep := range eps { 1970 svc, err := st.Service(ep.ServiceName) 1971 if errors.IsNotFound(err) { 1972 return nil, errors.Errorf("service %q does not exist", ep.ServiceName) 1973 } else if err != nil { 1974 return nil, errors.Trace(err) 1975 } else if svc.doc.Life != Alive { 1976 return nil, errors.Errorf("service %q is not alive", ep.ServiceName) 1977 } 1978 if svc.doc.Subordinate { 1979 subordinateCount++ 1980 } 1981 series[svc.doc.Series] = true 1982 ch, _, err := svc.Charm() 1983 if err != nil { 1984 return nil, errors.Trace(err) 1985 } 1986 if !ep.ImplementedBy(ch) { 1987 return nil, errors.Errorf("%q does not implement %q", ep.ServiceName, ep) 1988 } 1989 ops = append(ops, txn.Op{ 1990 C: servicesC, 1991 Id: st.docID(ep.ServiceName), 1992 Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}}, 1993 Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}}, 1994 }) 1995 } 1996 if matchSeries && len(series) != 1 { 1997 return nil, errors.Errorf("principal and subordinate services' series must match") 1998 } 1999 if eps[0].Scope == charm.ScopeContainer && subordinateCount < 1 { 2000 return nil, errors.Errorf("container scoped relation requires at least one subordinate service") 2001 } 2002 2003 // Create a new unique id if that has not already been done, and add 2004 // an operation to create the relation document. 2005 if id == -1 { 2006 var err error 2007 if id, err = st.sequence("relation"); err != nil { 2008 return nil, errors.Trace(err) 2009 } 2010 } 2011 docID := st.docID(key) 2012 doc = &relationDoc{ 2013 DocID: docID, 2014 Key: key, 2015 ModelUUID: st.ModelUUID(), 2016 Id: id, 2017 Endpoints: eps, 2018 Life: Alive, 2019 } 2020 ops = append(ops, txn.Op{ 2021 C: relationsC, 2022 Id: docID, 2023 Assert: txn.DocMissing, 2024 Insert: doc, 2025 }) 2026 return ops, nil 2027 } 2028 if err = st.run(buildTxn); err == nil { 2029 return &Relation{st, *doc}, nil 2030 } 2031 return nil, errors.Trace(err) 2032 } 2033 2034 // EndpointsRelation returns the existing relation with the given endpoints. 2035 func (st *State) EndpointsRelation(endpoints ...Endpoint) (*Relation, error) { 2036 return st.KeyRelation(relationKey(endpoints)) 2037 } 2038 2039 // KeyRelation returns the existing relation with the given key (which can 2040 // be derived unambiguously from the relation's endpoints). 2041 func (st *State) KeyRelation(key string) (*Relation, error) { 2042 relations, closer := st.getCollection(relationsC) 2043 defer closer() 2044 2045 doc := relationDoc{} 2046 err := relations.FindId(key).One(&doc) 2047 if err == mgo.ErrNotFound { 2048 return nil, errors.NotFoundf("relation %q", key) 2049 } 2050 if err != nil { 2051 return nil, errors.Annotatef(err, "cannot get relation %q", key) 2052 } 2053 return newRelation(st, &doc), nil 2054 } 2055 2056 // Relation returns the existing relation with the given id. 2057 func (st *State) Relation(id int) (*Relation, error) { 2058 relations, closer := st.getCollection(relationsC) 2059 defer closer() 2060 2061 doc := relationDoc{} 2062 err := relations.Find(bson.D{{"id", id}}).One(&doc) 2063 if err == mgo.ErrNotFound { 2064 return nil, errors.NotFoundf("relation %d", id) 2065 } 2066 if err != nil { 2067 return nil, errors.Annotatef(err, "cannot get relation %d", id) 2068 } 2069 return newRelation(st, &doc), nil 2070 } 2071 2072 // AllRelations returns all relations in the model ordered by id. 2073 func (st *State) AllRelations() (relations []*Relation, err error) { 2074 relationsCollection, closer := st.getCollection(relationsC) 2075 defer closer() 2076 2077 docs := relationDocSlice{} 2078 err = relationsCollection.Find(nil).All(&docs) 2079 if err != nil { 2080 return nil, errors.Annotate(err, "cannot get all relations") 2081 } 2082 sort.Sort(docs) 2083 for _, v := range docs { 2084 relations = append(relations, newRelation(st, &v)) 2085 } 2086 return 2087 } 2088 2089 type relationDocSlice []relationDoc 2090 2091 func (rdc relationDocSlice) Len() int { return len(rdc) } 2092 func (rdc relationDocSlice) Swap(i, j int) { rdc[i], rdc[j] = rdc[j], rdc[i] } 2093 func (rdc relationDocSlice) Less(i, j int) bool { 2094 return rdc[i].Id < rdc[j].Id 2095 } 2096 2097 // Unit returns a unit by name. 2098 func (st *State) Unit(name string) (*Unit, error) { 2099 if !names.IsValidUnit(name) { 2100 return nil, errors.Errorf("%q is not a valid unit name", name) 2101 } 2102 units, closer := st.getCollection(unitsC) 2103 defer closer() 2104 2105 doc := unitDoc{} 2106 err := units.FindId(name).One(&doc) 2107 if err == mgo.ErrNotFound { 2108 return nil, errors.NotFoundf("unit %q", name) 2109 } 2110 if err != nil { 2111 return nil, errors.Annotatef(err, "cannot get unit %q", name) 2112 } 2113 return newUnit(st, &doc), nil 2114 } 2115 2116 // UnitsFor returns the units placed in the given machine id. 2117 func (st *State) UnitsFor(machineId string) ([]*Unit, error) { 2118 if !names.IsValidMachine(machineId) { 2119 return nil, errors.Errorf("%q is not a valid machine id", machineId) 2120 } 2121 m := &Machine{ 2122 st: st, 2123 doc: machineDoc{ 2124 Id: machineId, 2125 }, 2126 } 2127 return m.Units() 2128 } 2129 2130 // AssignUnit places the unit on a machine. Depending on the policy, and the 2131 // state of the model, this may lead to new instances being launched 2132 // within the model. 2133 func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) { 2134 if !u.IsPrincipal() { 2135 return errors.Errorf("subordinate unit %q cannot be assigned directly to a machine", u) 2136 } 2137 defer errors.DeferredAnnotatef(&err, "cannot assign unit %q to machine", u) 2138 var m *Machine 2139 switch policy { 2140 case AssignLocal: 2141 m, err = st.Machine("0") 2142 if err != nil { 2143 return errors.Trace(err) 2144 } 2145 return u.AssignToMachine(m) 2146 case AssignClean: 2147 if _, err = u.AssignToCleanMachine(); err != noCleanMachines { 2148 return errors.Trace(err) 2149 } 2150 return u.AssignToNewMachineOrContainer() 2151 case AssignCleanEmpty: 2152 if _, err = u.AssignToCleanEmptyMachine(); err != noCleanMachines { 2153 return errors.Trace(err) 2154 } 2155 return u.AssignToNewMachineOrContainer() 2156 case AssignNew: 2157 return errors.Trace(u.AssignToNewMachine()) 2158 } 2159 return errors.Errorf("unknown unit assignment policy: %q", policy) 2160 } 2161 2162 // StartSync forces watchers to resynchronize their state with the 2163 // database immediately. This will happen periodically automatically. 2164 func (st *State) StartSync() { 2165 st.watcher.StartSync() 2166 st.pwatcher.Sync() 2167 } 2168 2169 // SetAdminMongoPassword sets the administrative password 2170 // to access the state. If the password is non-empty, 2171 // all subsequent attempts to access the state must 2172 // be authorized; otherwise no authorization is required. 2173 func (st *State) SetAdminMongoPassword(password string) error { 2174 err := mongo.SetAdminMongoPassword(st.session, mongo.AdminUser, password) 2175 return errors.Trace(err) 2176 } 2177 2178 type controllersDoc struct { 2179 Id string `bson:"_id"` 2180 ModelUUID string `bson:"model-uuid"` 2181 MachineIds []string 2182 VotingMachineIds []string 2183 MongoSpaceName string `bson:"mongo-space-name"` 2184 MongoSpaceState string `bson:"mongo-space-state"` 2185 } 2186 2187 // ControllerInfo holds information about currently 2188 // configured controller machines. 2189 type ControllerInfo struct { 2190 // ModelTag identifies the initial model. Only the initial 2191 // model is able to have machines that manage state. The initial 2192 // model is the model that is created when bootstrapping. 2193 ModelTag names.ModelTag 2194 2195 // MachineIds holds the ids of all machines configured 2196 // to run a controller. It includes all the machine 2197 // ids in VotingMachineIds. 2198 MachineIds []string 2199 2200 // VotingMachineIds holds the ids of all machines 2201 // configured to run a controller and to have a vote 2202 // in peer election. 2203 VotingMachineIds []string 2204 2205 // MongoSpaceName is the space that contains all Mongo servers. 2206 MongoSpaceName string 2207 2208 // MongoSpaceState records the state of the mongo space selection state machine. Valid states are: 2209 // * We haven't looked for a Mongo space yet (MongoSpaceUnknown) 2210 // * We have looked for a Mongo space, but we didn't find one (MongoSpaceInvalid) 2211 // * We have looked for and found a Mongo space (MongoSpaceValid) 2212 // * We didn't try to find a Mongo space because the provider doesn't support spaces (MongoSpaceUnsupported) 2213 MongoSpaceState MongoSpaceStates 2214 } 2215 2216 type MongoSpaceStates string 2217 2218 const ( 2219 MongoSpaceUnknown MongoSpaceStates = "" 2220 MongoSpaceValid MongoSpaceStates = "valid" 2221 MongoSpaceInvalid MongoSpaceStates = "invalid" 2222 MongoSpaceUnsupported MongoSpaceStates = "unsupported" 2223 ) 2224 2225 // ControllerInfo returns information about 2226 // the currently configured controller machines. 2227 func (st *State) ControllerInfo() (*ControllerInfo, error) { 2228 session := st.session.Copy() 2229 defer session.Close() 2230 return readRawControllerInfo(st.session) 2231 } 2232 2233 // readRawControllerInfo reads ControllerInfo direct from the supplied session, 2234 // falling back to the bootstrap model document to extract the UUID when 2235 // required. 2236 func readRawControllerInfo(session *mgo.Session) (*ControllerInfo, error) { 2237 db := session.DB(jujuDB) 2238 controllers := db.C(controllersC) 2239 2240 var doc controllersDoc 2241 err := controllers.Find(bson.D{{"_id", modelGlobalKey}}).One(&doc) 2242 if err != nil { 2243 return nil, errors.Annotatef(err, "cannot get controllers document") 2244 } 2245 2246 if doc.ModelUUID == "" { 2247 logger.Warningf("controllers info has no model UUID so retrieving it from model") 2248 2249 // This only happens when migrating from 1.20 to 1.21 before 2250 // upgrade steps have been run. Without this hack modelTag 2251 // on State ends up empty, breaking basic functionality needed 2252 // to run upgrade steps (a chicken-and-egg scenario). 2253 environments := db.C(modelsC) 2254 2255 var envDoc modelDoc 2256 query := environments.Find(nil) 2257 count, err := query.Count() 2258 if err != nil { 2259 return nil, errors.Annotate(err, "cannot get model document count") 2260 } 2261 if count != 1 { 2262 return nil, errors.New("expected just one model to get UUID from") 2263 } 2264 if err := query.One(&envDoc); err != nil { 2265 return nil, errors.Annotate(err, "cannot load model document") 2266 } 2267 doc.ModelUUID = envDoc.UUID 2268 } 2269 2270 return &ControllerInfo{ 2271 ModelTag: names.NewModelTag(doc.ModelUUID), 2272 MachineIds: doc.MachineIds, 2273 VotingMachineIds: doc.VotingMachineIds, 2274 MongoSpaceName: doc.MongoSpaceName, 2275 MongoSpaceState: MongoSpaceStates(doc.MongoSpaceState), 2276 }, nil 2277 } 2278 2279 const stateServingInfoKey = "stateServingInfo" 2280 2281 // StateServingInfo returns information for running a controller machine 2282 func (st *State) StateServingInfo() (StateServingInfo, error) { 2283 controllers, closer := st.getCollection(controllersC) 2284 defer closer() 2285 2286 var info StateServingInfo 2287 err := controllers.Find(bson.D{{"_id", stateServingInfoKey}}).One(&info) 2288 if err != nil { 2289 return info, errors.Trace(err) 2290 } 2291 if info.StatePort == 0 { 2292 return StateServingInfo{}, errors.NotFoundf("state serving info") 2293 } 2294 return info, nil 2295 } 2296 2297 // SetStateServingInfo stores information needed for running a controller 2298 func (st *State) SetStateServingInfo(info StateServingInfo) error { 2299 if info.StatePort == 0 || info.APIPort == 0 || 2300 info.Cert == "" || info.PrivateKey == "" { 2301 return errors.Errorf("incomplete state serving info set in state") 2302 } 2303 if info.CAPrivateKey == "" { 2304 // No CA certificate key means we can't generate new controller 2305 // certificates when needed to add to the certificate SANs. 2306 // Older Juju deployments discard the key because no one realised 2307 // the certificate was flawed, so at best we can log a warning 2308 // until an upgrade process is written. 2309 logger.Warningf("state serving info has no CA certificate key") 2310 } 2311 ops := []txn.Op{{ 2312 C: controllersC, 2313 Id: stateServingInfoKey, 2314 Update: bson.D{{"$set", info}}, 2315 }} 2316 if err := st.runTransaction(ops); err != nil { 2317 return errors.Annotatef(err, "cannot set state serving info") 2318 } 2319 return nil 2320 } 2321 2322 // SetSystemIdentity sets the system identity value in the database 2323 // if and only iff it is empty. 2324 func SetSystemIdentity(st *State, identity string) error { 2325 ops := []txn.Op{{ 2326 C: controllersC, 2327 Id: stateServingInfoKey, 2328 Assert: bson.D{{"systemidentity", ""}}, 2329 Update: bson.D{{"$set", bson.D{{"systemidentity", identity}}}}, 2330 }} 2331 2332 if err := st.runTransaction(ops); err != nil { 2333 return errors.Trace(err) 2334 } 2335 return nil 2336 } 2337 2338 // SetOrGetMongoSpaceName attempts to set the Mongo space or, if that fails, look 2339 // up the current Mongo space. Either way, it always returns what is in the 2340 // database by the end of the call. 2341 func (st *State) SetOrGetMongoSpaceName(mongoSpaceName network.SpaceName) (network.SpaceName, error) { 2342 err := st.setMongoSpaceName(mongoSpaceName) 2343 if err == txn.ErrAborted { 2344 // Failed to set the new space name. Return what is already stored in state. 2345 controllerInfo, err := st.ControllerInfo() 2346 if err != nil { 2347 return network.SpaceName(""), errors.Trace(err) 2348 } 2349 return network.SpaceName(controllerInfo.MongoSpaceName), nil 2350 } else if err != nil { 2351 return network.SpaceName(""), errors.Trace(err) 2352 } 2353 return mongoSpaceName, nil 2354 } 2355 2356 // SetMongoSpaceState attempts to set the Mongo space state or, if that fails, look 2357 // up the current Mongo state. Either way, it always returns what is in the 2358 // database by the end of the call. 2359 func (st *State) SetMongoSpaceState(mongoSpaceState MongoSpaceStates) error { 2360 2361 if mongoSpaceState != MongoSpaceUnknown && 2362 mongoSpaceState != MongoSpaceValid && 2363 mongoSpaceState != MongoSpaceInvalid && 2364 mongoSpaceState != MongoSpaceUnsupported { 2365 return errors.NotValidf("mongoSpaceState: %s", mongoSpaceState) 2366 } 2367 2368 err := st.setMongoSpaceState(mongoSpaceState) 2369 if err != nil { 2370 return errors.Trace(err) 2371 } 2372 return nil 2373 } 2374 2375 func (st *State) setMongoSpaceName(mongoSpaceName network.SpaceName) error { 2376 ops := []txn.Op{{ 2377 C: controllersC, 2378 Id: modelGlobalKey, 2379 Assert: bson.D{{"mongo-space-state", string(MongoSpaceUnknown)}}, 2380 Update: bson.D{{ 2381 "$set", 2382 bson.D{ 2383 {"mongo-space-name", string(mongoSpaceName)}, 2384 {"mongo-space-state", MongoSpaceValid}, 2385 }, 2386 }}, 2387 }} 2388 2389 return st.runTransaction(ops) 2390 } 2391 2392 func (st *State) setMongoSpaceState(mongoSpaceState MongoSpaceStates) error { 2393 ops := []txn.Op{{ 2394 C: controllersC, 2395 Id: modelGlobalKey, 2396 Update: bson.D{{"$set", bson.D{{"mongo-space-state", mongoSpaceState}}}}, 2397 }} 2398 2399 return st.runTransaction(ops) 2400 } 2401 2402 var tagPrefix = map[byte]string{ 2403 'm': names.MachineTagKind + "-", 2404 's': names.ServiceTagKind + "-", 2405 'u': names.UnitTagKind + "-", 2406 'e': names.ModelTagKind + "-", 2407 'r': names.RelationTagKind + "-", 2408 } 2409 2410 func tagForGlobalKey(key string) (string, bool) { 2411 if len(key) < 3 || key[1] != '#' { 2412 return "", false 2413 } 2414 p, ok := tagPrefix[key[0]] 2415 if !ok { 2416 return "", false 2417 } 2418 return p + key[2:], true 2419 }