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