github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/state.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/juju/charm/v12" 15 "github.com/juju/clock" 16 "github.com/juju/collections/set" 17 "github.com/juju/errors" 18 "github.com/juju/loggo" 19 "github.com/juju/mgo/v3" 20 "github.com/juju/mgo/v3/bson" 21 "github.com/juju/mgo/v3/txn" 22 "github.com/juju/names/v5" 23 "github.com/juju/pubsub/v2" 24 jujutxn "github.com/juju/txn/v3" 25 "github.com/juju/utils/v3" 26 "github.com/juju/version/v2" 27 28 corebase "github.com/juju/juju/core/base" 29 corecharm "github.com/juju/juju/core/charm" 30 "github.com/juju/juju/core/config" 31 "github.com/juju/juju/core/constraints" 32 "github.com/juju/juju/core/instance" 33 corenetwork "github.com/juju/juju/core/network" 34 "github.com/juju/juju/core/permission" 35 "github.com/juju/juju/core/status" 36 "github.com/juju/juju/mongo" 37 "github.com/juju/juju/state/cloudimagemetadata" 38 stateerrors "github.com/juju/juju/state/errors" 39 "github.com/juju/juju/state/watcher" 40 "github.com/juju/juju/storage" 41 jujuversion "github.com/juju/juju/version" 42 ) 43 44 var logger = loggo.GetLogger("juju.state") 45 46 const ( 47 // jujuDB is the name of the main juju database. 48 jujuDB = "juju" 49 50 // blobstoreDB is the name of the blobstore GridFS database. 51 blobstoreDB = "blobstore" 52 ) 53 54 type providerIdDoc struct { 55 ID string `bson:"_id"` // format: "<model-uuid>:<global-key>:<provider-id>" 56 } 57 58 // State represents the state of an model 59 // managed by juju. 60 type State struct { 61 stateClock clock.Clock 62 modelTag names.ModelTag 63 controllerModelTag names.ModelTag 64 controllerTag names.ControllerTag 65 session *mgo.Session 66 database Database 67 policy Policy 68 newPolicy NewPolicyFunc 69 runTransactionObserver RunTransactionObserverFunc 70 maxTxnAttempts int 71 72 // workers is responsible for keeping the various sub-workers 73 // available by starting new ones as they fail. It doesn't do 74 // that yet, but having a type that collects them together is the 75 // first step. 76 workers *workers 77 78 // TODO(anastasiamac 2015-07-16) As state gets broken up, remove this. 79 CloudImageMetadataStorage cloudimagemetadata.Storage 80 } 81 82 func (st *State) newStateNoWorkers(modelUUID string) (*State, error) { 83 session := st.session.Copy() 84 newSt, err := newState( 85 st.controllerTag, 86 names.NewModelTag(modelUUID), 87 st.controllerModelTag, 88 session, 89 st.newPolicy, 90 st.stateClock, 91 st.runTransactionObserver, 92 st.maxTxnAttempts, 93 ) 94 // We explicitly don't start the workers. 95 if err != nil { 96 session.Close() 97 return nil, errors.Trace(err) 98 } 99 return newSt, nil 100 } 101 102 // IsController returns true if this state instance has the bootstrap 103 // model UUID. 104 func (st *State) IsController() bool { 105 return st.modelTag == st.controllerModelTag 106 } 107 108 // ControllerUUID returns the UUID for the controller 109 // of this state instance. 110 func (st *State) ControllerUUID() string { 111 return st.controllerTag.Id() 112 } 113 114 // ControllerTag returns the tag form of the ControllerUUID. 115 func (st *State) ControllerTag() names.ControllerTag { 116 return st.controllerTag 117 } 118 119 // ControllerTimestamp returns the current timestamp of the backend 120 // controller. 121 func (st *State) ControllerTimestamp() (*time.Time, error) { 122 now := st.clock().Now() 123 return &now, nil 124 } 125 126 // ControllerModelUUID returns the UUID of the model that was 127 // bootstrapped. This is the only model that can have controller 128 // machines. The owner of this model is also considered "special", in 129 // that they are the only user that is able to create other users 130 // (until we have more fine grained permissions), and they cannot be 131 // disabled. 132 func (st *State) ControllerModelUUID() string { 133 return st.controllerModelTag.Id() 134 } 135 136 // ControllerModelTag returns the tag form the return value of 137 // ControllerModelUUID. 138 func (st *State) ControllerModelTag() names.ModelTag { 139 return st.controllerModelTag 140 } 141 142 // ControllerOwner returns the owner of the controller model. 143 func (st *State) ControllerOwner() (names.UserTag, error) { 144 models, closer := st.db().GetCollection(modelsC) 145 defer closer() 146 var doc map[string]string 147 err := models.FindId(st.ControllerModelUUID()).Select(bson.M{"owner": 1}).One(&doc) 148 if err != nil { 149 return names.UserTag{}, errors.Annotate(err, "loading controller model") 150 } 151 owner := doc["owner"] 152 if owner == "" { 153 return names.UserTag{}, errors.New("model owner missing") 154 } 155 return names.NewUserTag(owner), nil 156 } 157 158 func ControllerAccess(st *State, tag names.Tag) (permission.UserAccess, error) { 159 return st.UserAccess(tag.(names.UserTag), st.controllerTag) 160 } 161 162 // setDyingModelToDead sets current dying model to dead. 163 func (st *State) setDyingModelToDead() error { 164 buildTxn := func(attempt int) ([]txn.Op, error) { 165 model, err := st.Model() 166 if err != nil { 167 return nil, errors.Trace(err) 168 } 169 if model.Life() != Dying { 170 return nil, errors.Trace(ErrModelNotDying) 171 } 172 ops := []txn.Op{{ 173 C: modelsC, 174 Id: st.ModelUUID(), 175 Assert: isDyingDoc, 176 Update: bson.M{"$set": bson.M{ 177 "life": Dead, 178 "time-of-death": st.nowToTheSecond(), 179 }}, 180 }, { 181 // Cleanup the owner:modelName unique key. 182 C: usermodelnameC, 183 Id: model.uniqueIndexID(), 184 Remove: true, 185 }} 186 return ops, nil 187 } 188 if err := st.db().Run(buildTxn); err != nil { 189 return errors.Trace(err) 190 } 191 return nil 192 } 193 194 // RemoveDyingModel sets current model to dead then removes all documents from 195 // multi-model collections. 196 func (st *State) RemoveDyingModel() error { 197 model, err := st.Model() 198 if err != nil { 199 return errors.Trace(err) 200 } 201 if model.Life() == Alive { 202 return errors.Errorf("can't remove model: model still alive") 203 } 204 if model.Life() == Dying { 205 // set model to dead if it's dying. 206 if err = st.setDyingModelToDead(); err != nil { 207 return errors.Trace(err) 208 } 209 } 210 err = st.removeAllModelDocs(bson.D{{"life", Dead}}) 211 if errors.Cause(err) == txn.ErrAborted { 212 return errors.Wrap(err, errors.New("can't remove model: model not dead")) 213 } 214 return errors.Trace(err) 215 } 216 217 // RemoveImportingModelDocs removes all documents from multi-model collections 218 // for the current model. This method asserts that the model's migration mode 219 // is "importing". 220 func (st *State) RemoveImportingModelDocs() error { 221 err := st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeImporting}}) 222 if errors.Cause(err) == txn.ErrAborted { 223 return errors.New("can't remove model: model not being imported for migration") 224 } 225 return errors.Trace(err) 226 } 227 228 // RemoveExportingModelDocs removes all documents from multi-model collections 229 // for the current model. This method asserts that the model's migration mode 230 // is "exporting". 231 func (st *State) RemoveExportingModelDocs() error { 232 err := st.removeAllModelDocs(bson.D{{"migration-mode", MigrationModeExporting}}) 233 if errors.Cause(err) == txn.ErrAborted { 234 return errors.Wrap(err, errors.New("can't remove model: model not being exported for migration")) 235 } 236 return errors.Trace(err) 237 } 238 239 func cleanupSecretBackendRefCountAfterModelMigrationDone(st *State) error { 240 col, closer := st.db().GetCollection(secretRevisionsC) 241 defer closer() 242 pipe := col.Pipe([]bson.M{ 243 { 244 "$match": bson.M{ 245 "value-reference.backend-id": bson.M{ 246 "$exists": true, 247 "$ne": "", 248 }, 249 }, 250 }, 251 { 252 "$group": bson.M{ 253 "_id": "$value-reference.backend-id", "count": bson.M{"$sum": 1}, 254 }, 255 }, 256 }) 257 var result []struct { 258 ID string `bson:"_id"` 259 Count int `bson:"count"` 260 } 261 if err := pipe.All(&result); err != nil { 262 return errors.Trace(err) 263 } 264 if len(result) == 0 { 265 return nil 266 } 267 268 var ops []txn.Op 269 for _, r := range result { 270 for i := r.Count; i > 0; i-- { 271 refOps, err := st.decSecretBackendRefCountOp(r.ID) 272 if err != nil { 273 return errors.Trace(err) 274 } 275 ops = append(ops, refOps...) 276 } 277 } 278 return st.db().RunTransaction(ops) 279 } 280 281 func (st *State) removeAllModelDocs(modelAssertion bson.D) error { 282 // Remove permissions first, because we potentially 283 // remove parent documents in the following stage. 284 if err := st.removeAllModelPermissions(); err != nil { 285 return errors.Annotate(err, "removing permissions") 286 } 287 288 if err := cleanupSecretBackendRefCountAfterModelMigrationDone(st); err != nil { 289 // We have to do this before secrets get removed. 290 return errors.Trace(err) 291 } 292 293 // Remove each collection in its own transaction. 294 modelUUID := st.ModelUUID() 295 for name, info := range st.database.Schema() { 296 if info.global || info.rawAccess { 297 continue 298 } 299 300 ops, err := st.removeAllInCollectionOps(name) 301 if err != nil { 302 return errors.Trace(err) 303 } 304 if len(ops) == 0 { 305 // Nothing to delete. 306 continue 307 } 308 // Make sure we gate everything on the model assertion. 309 ops = append([]txn.Op{{ 310 C: modelsC, 311 Id: modelUUID, 312 Assert: modelAssertion, 313 }}, ops...) 314 err = st.db().RunTransaction(ops) 315 if err != nil { 316 return errors.Annotatef(err, "removing from collection %q", name) 317 } 318 } 319 320 // Remove from the raw (non-transactional) collections. 321 for name, info := range st.database.Schema() { 322 if !info.global && info.rawAccess { 323 if err := st.removeAllInCollectionRaw(name); err != nil { 324 return errors.Trace(err) 325 } 326 } 327 } 328 329 // Logs are in a separate database so don't get caught by that loop. 330 _ = removeModelLogs(st.MongoSession(), modelUUID) 331 332 // Now remove the model. 333 model, err := st.Model() 334 if err != nil { 335 return errors.Trace(err) 336 } 337 ops := []txn.Op{{ 338 // Cleanup the owner:envName unique key. 339 C: usermodelnameC, 340 Id: model.uniqueIndexID(), 341 Remove: true, 342 }, { 343 C: modelEntityRefsC, 344 Id: modelUUID, 345 Remove: true, 346 }, { 347 C: modelsC, 348 Id: modelUUID, 349 Assert: modelAssertion, 350 Remove: true, 351 }} 352 353 // Decrement the model count for the cloud to which this model belongs. 354 decCloudRefOp, err := decCloudModelRefOp(st, model.CloudName()) 355 if err != nil { 356 return errors.Trace(err) 357 } 358 ops = append(ops, decCloudRefOp) 359 360 if !st.IsController() { 361 ops = append(ops, decHostedModelCountOp()) 362 } 363 return errors.Trace(st.db().RunTransaction(ops)) 364 } 365 366 // removeAllModelPermissions removes all direct permissions documents for 367 // this model, and all permissions for offers hosted by this model. 368 func (st *State) removeAllModelPermissions() error { 369 var permOps []txn.Op 370 permPattern := bson.M{ 371 "_id": bson.M{"$regex": "^" + permissionID(modelKey(st.ModelUUID()), "")}, 372 } 373 ops, err := st.removeInCollectionOps(permissionsC, permPattern) 374 if err != nil { 375 return errors.Trace(err) 376 } 377 permOps = append(permOps, ops...) 378 379 applicationOffersCollection, closer := st.db().GetCollection(applicationOffersC) 380 defer closer() 381 382 var offerDocs []applicationOfferDoc 383 if err := applicationOffersCollection.Find(bson.D{}).All(&offerDocs); err != nil { 384 return errors.Annotate(err, "getting application offer documents") 385 } 386 387 for _, offer := range offerDocs { 388 permPattern = bson.M{ 389 "_id": bson.M{"$regex": "^" + permissionID(applicationOfferKey(offer.OfferUUID), "")}, 390 } 391 ops, err = st.removeInCollectionOps(permissionsC, permPattern) 392 if err != nil { 393 return errors.Trace(err) 394 } 395 permOps = append(permOps, ops...) 396 } 397 err = st.db().RunTransaction(permOps) 398 return errors.Trace(err) 399 } 400 401 // removeAllInCollectionRaw removes all the documents from the given 402 // named collection. 403 func (st *State) removeAllInCollectionRaw(name string) error { 404 coll, closer := st.db().GetCollection(name) 405 defer closer() 406 _, err := coll.Writeable().RemoveAll(nil) 407 return errors.Trace(err) 408 } 409 410 // removeAllInCollectionOps appends to ops operations to 411 // remove all the documents in the given named collection. 412 func (st *State) removeAllInCollectionOps(name string) ([]txn.Op, error) { 413 return st.removeInCollectionOps(name, nil) 414 } 415 416 // removeInCollectionOps generates operations to remove all documents 417 // from the named collection matching a specific selector. 418 func (st *State) removeInCollectionOps(name string, sel interface{}) ([]txn.Op, error) { 419 coll, closer := st.db().GetCollection(name) 420 defer closer() 421 422 var ids []bson.M 423 err := coll.Find(sel).Select(bson.D{{"_id", 1}}).All(&ids) 424 if err != nil { 425 return nil, errors.Trace(err) 426 } 427 var ops []txn.Op 428 for _, id := range ids { 429 ops = append(ops, txn.Op{ 430 C: name, 431 Id: id["_id"], 432 Remove: true, 433 }) 434 } 435 return ops, nil 436 } 437 438 // startWorkers starts the worker backends on the *State 439 // - txn log watcher 440 // - txn log pruner 441 // 442 // startWorkers will close the *State if it fails. 443 func (st *State) startWorkers(hub *pubsub.SimpleHub) (err error) { 444 defer func() { 445 if err == nil { 446 return 447 } 448 if err2 := st.Close(); err2 != nil { 449 logger.Errorf("closing State for %s: %v", st.modelTag, err2) 450 } 451 }() 452 453 logger.Infof("starting standard state workers") 454 workers, err := newWorkers(st, hub) 455 if err != nil { 456 return errors.Trace(err) 457 } 458 st.workers = workers 459 logger.Infof("started state workers for %s successfully", st.modelTag) 460 return nil 461 } 462 463 // ModelUUID returns the model UUID for the model 464 // controlled by this state instance. 465 func (st *State) ModelUUID() string { 466 return st.modelTag.Id() 467 } 468 469 // userModelNameIndex returns a string to be used as a usermodelnameC unique index. 470 func userModelNameIndex(username, modelName string) string { 471 return strings.ToLower(username) + ":" + modelName 472 } 473 474 // EnsureModelRemoved returns an error if any multi-model 475 // documents for this model are found. It is intended only to be used in 476 // tests and exported so it can be used in the tests of other packages. 477 func (st *State) EnsureModelRemoved() error { 478 found := map[string]int{} 479 var foundOrdered []string 480 for name, info := range st.database.Schema() { 481 if info.global { 482 continue 483 } 484 485 if err := func(name string, info CollectionInfo) error { 486 coll, closer := st.db().GetCollection(name) 487 defer closer() 488 489 n, err := coll.Find(nil).Count() 490 if err != nil { 491 return errors.Trace(err) 492 } 493 494 if n != 0 { 495 found[name] = n 496 foundOrdered = append(foundOrdered, name) 497 } 498 return nil 499 }(name, info); err != nil { 500 return errors.Trace(err) 501 } 502 } 503 504 if len(found) != 0 { 505 errMessage := fmt.Sprintf("found documents for model with uuid %s:", st.ModelUUID()) 506 sort.Strings(foundOrdered) 507 for _, name := range foundOrdered { 508 number := found[name] 509 errMessage += fmt.Sprintf(" %d %s doc,", number, name) 510 } 511 // Remove trailing comma. 512 errMessage = errMessage[:len(errMessage)-1] 513 return errors.New(errMessage) 514 } 515 return nil 516 } 517 518 // newDB returns a database connection using a new session, along with 519 // a closer function for the session. This is useful where you need to work 520 // with various collections in a single session, so don't want to call 521 // getCollection multiple times. 522 func (st *State) newDB() (Database, func()) { 523 return st.database.Copy() 524 } 525 526 // db returns the Database instance used by the State. It is part of 527 // the modelBackend interface. 528 func (st *State) db() Database { 529 return st.database 530 } 531 532 // txnLogWatcher returns the TxnLogWatcher for the State. It is part 533 // of the modelBackend interface. 534 func (st *State) txnLogWatcher() watcher.BaseWatcher { 535 return st.workers.txnLogWatcher() 536 } 537 538 // Ping probes the state's database connection to ensure 539 // that it is still alive. 540 func (st *State) Ping() error { 541 return st.session.Ping() 542 } 543 544 // MongoVersion return the string repre 545 func (st *State) MongoVersion() (string, error) { 546 binfo, err := st.session.BuildInfo() 547 if err != nil { 548 return "", errors.Annotate(err, "cannot obtain mongo build info") 549 } 550 return binfo.Version, nil 551 } 552 553 // MongoSession returns the underlying mongodb session 554 // used by the state. It is exposed so that external code 555 // can maintain the mongo replica set and should not 556 // otherwise be used. 557 func (st *State) MongoSession() *mgo.Session { 558 return st.session 559 } 560 561 // WatchParams defines config to control which 562 // entites are included when watching a model. 563 type WatchParams struct { 564 // IncludeOffers controls whether application offers should be watched. 565 IncludeOffers bool 566 } 567 568 func (st *State) checkCanUpgradeCAAS(currentVersion, newVersion string) error { 569 // TODO(caas) 570 return nil 571 } 572 573 func (st *State) checkCanUpgradeIAAS(currentVersion, newVersion string) error { 574 matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-" 575 matchNew := "^" + regexp.QuoteMeta(newVersion) + "-" 576 // Get all machines and units with a different or empty version. 577 sel := bson.D{{"$or", []bson.D{ 578 {{"tools", bson.D{{"$exists", false}}}}, 579 {{"$and", []bson.D{ 580 {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}}, 581 {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}}, 582 }}}, 583 }}} 584 var agentTags []string 585 for _, name := range []string{machinesC, unitsC} { 586 collection, closer := st.db().GetCollection(name) 587 defer closer() 588 var doc struct { 589 DocID string `bson:"_id"` 590 } 591 iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter() 592 defer iter.Close() 593 for iter.Next(&doc) { 594 localID, err := st.strictLocalID(doc.DocID) 595 if err != nil { 596 return errors.Trace(err) 597 } 598 switch name { 599 case machinesC: 600 agentTags = append(agentTags, names.NewMachineTag(localID).String()) 601 case unitsC: 602 agentTags = append(agentTags, names.NewUnitTag(localID).String()) 603 } 604 } 605 if err := iter.Close(); err != nil { 606 return errors.Trace(err) 607 } 608 } 609 if len(agentTags) > 0 { 610 err := newVersionInconsistentError(version.MustParse(currentVersion), agentTags) 611 return errors.Trace(err) 612 } 613 return nil 614 } 615 616 // SetModelAgentVersion changes the agent version for the model to the 617 // given version, only if the model is in a stable state (all agents are 618 // running the current version). If this is a hosted model, newVersion 619 // cannot be higher than the controller version. 620 func (st *State) SetModelAgentVersion(newVersion version.Number, stream *string, ignoreAgentVersions bool) (err error) { 621 if newVersion.Compare(jujuversion.Current) > 0 && !st.IsController() { 622 return errors.Errorf("model cannot be upgraded to %s while the controller is %s: upgrade 'controller' model first", 623 newVersion.String(), 624 jujuversion.Current, 625 ) 626 } 627 628 model, err := st.Model() 629 if err != nil { 630 return errors.Trace(err) 631 } 632 isCAAS := model.Type() == ModelTypeCAAS 633 buildTxn := func(attempt int) ([]txn.Op, error) { 634 settings, err := readSettings(st.db(), settingsC, modelGlobalKey) 635 if err != nil { 636 return nil, errors.Annotatef(err, "model %q", st.modelTag.Id()) 637 } 638 agentVersion, ok := settings.Get("agent-version") 639 if !ok { 640 return nil, errors.Errorf("no agent version set in the model") 641 } 642 currentVersion, ok := agentVersion.(string) 643 if !ok { 644 return nil, errors.Errorf("invalid agent version format: expected string, got %v", agentVersion) 645 } 646 if newVersion.String() == currentVersion { 647 // Nothing to do. 648 return nil, jujutxn.ErrNoOperations 649 } 650 agentStream, _ := settings.Get("agent-stream") 651 currentStream, _ := agentStream.(string) 652 newStream := currentStream 653 if stream != nil { 654 if (*stream != "" || currentStream != "released") && (*stream != "released" || currentStream != "") { 655 newStream = *stream 656 } 657 } 658 659 if !ignoreAgentVersions { 660 if isCAAS { 661 if err := st.checkCanUpgradeCAAS(currentVersion, newVersion.String()); err != nil { 662 return nil, errors.Trace(err) 663 } 664 } else { 665 if err := st.checkCanUpgradeIAAS(currentVersion, newVersion.String()); err != nil { 666 return nil, errors.Trace(err) 667 } 668 } 669 } 670 671 ops := []txn.Op{ 672 // Can't set agent-version if there's an active upgradeInfo doc. 673 { 674 C: upgradeInfoC, 675 Id: currentUpgradeId, 676 Assert: txn.DocMissing, 677 }, { 678 C: settingsC, 679 Id: st.docID(modelGlobalKey), 680 Assert: bson.D{{"version", settings.version}}, 681 Update: bson.D{ 682 {"$set", bson.D{ 683 {"settings.agent-version", newVersion.String()}, 684 {"settings.agent-stream", newStream}, 685 }}, 686 }, 687 }, 688 } 689 return ops, nil 690 } 691 if err = st.db().Run(buildTxn); err == jujutxn.ErrExcessiveContention { 692 // Although there is a small chance of a race here, try to 693 // return a more helpful error message in the case of an 694 // active upgradeInfo document being in place. 695 if upgrading, _ := st.IsUpgrading(); upgrading { 696 err = stateerrors.ErrUpgradeInProgress 697 } else { 698 err = errors.Annotate(err, "cannot set agent version") 699 } 700 } 701 return errors.Trace(err) 702 } 703 704 // ModelConstraints returns the current model constraints. 705 func (st *State) ModelConstraints() (constraints.Value, error) { 706 cons, err := readConstraints(st, modelGlobalKey) 707 return cons, errors.Trace(err) 708 } 709 710 // SetModelConstraints replaces the current model constraints. 711 func (st *State) SetModelConstraints(cons constraints.Value) error { 712 unsupported, err := st.validateConstraints(cons) 713 if len(unsupported) > 0 { 714 logger.Warningf( 715 "setting model constraints: unsupported constraints: %v", strings.Join(unsupported, ",")) 716 } else if err != nil { 717 return errors.Trace(err) 718 } 719 return writeConstraints(st, modelGlobalKey, cons) 720 } 721 722 func (st *State) allMachines(machinesCollection mongo.Collection) ([]*Machine, error) { 723 mdocs := machineDocSlice{} 724 err := machinesCollection.Find(nil).All(&mdocs) 725 if err != nil { 726 return nil, errors.Annotatef(err, "cannot get all machines") 727 } 728 sort.Sort(mdocs) 729 machines := make([]*Machine, len(mdocs)) 730 for i, doc := range mdocs { 731 machines[i] = newMachine(st, &doc) 732 } 733 return machines, nil 734 } 735 736 // AllMachines returns all machines in the model 737 // ordered by id. 738 func (st *State) AllMachines() ([]*Machine, error) { 739 machinesCollection, closer := st.db().GetCollection(machinesC) 740 defer closer() 741 return st.allMachines(machinesCollection) 742 } 743 744 // MachineCountForBase counts the machines for the provided bases in the model. 745 // The bases must all be for the one os. 746 func (st *State) MachineCountForBase(base ...Base) (map[string]int, error) { 747 machinesCollection, closer := st.db().GetCollection(machinesC) 748 defer closer() 749 750 var ( 751 os string 752 channels []string 753 ) 754 for _, b := range base { 755 if os != "" && os != b.OS { 756 return nil, errors.New("bases must all be for the same OS") 757 } 758 os = b.OS 759 channels = append(channels, b.Normalise().Channel) 760 } 761 762 var docs []machineDoc 763 err := machinesCollection.Find(bson.D{ 764 {"base.channel", bson.D{{"$in", channels}}}, 765 {"base.os", os}, 766 }).Select(bson.M{"base": 1}).All(&docs) 767 if err != nil { 768 return nil, errors.Trace(err) 769 } 770 result := make(map[string]int) 771 for _, m := range docs { 772 b := m.Base.DisplayString() 773 result[b] = result[b] + 1 774 } 775 return result, nil 776 } 777 778 type machineDocSlice []machineDoc 779 780 func (ms machineDocSlice) Len() int { return len(ms) } 781 func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } 782 func (ms machineDocSlice) Less(i, j int) bool { 783 return machineIdLessThan(ms[i].Id, ms[j].Id) 784 } 785 786 // machineIdLessThan returns true if id1 < id2, false otherwise. 787 // Machine ids may include "/" separators if they are for a container so 788 // the comparison is done by comparing the id component values from 789 // left to right (most significant part to least significant). Ids for 790 // host machines are always less than ids for their containers. 791 func machineIdLessThan(id1, id2 string) bool { 792 // Most times, we are dealing with host machines and not containers, so we will 793 // try interpreting the ids as ints - this will be faster than dealing with the 794 // container ids below. 795 mint1, err1 := strconv.Atoi(id1) 796 mint2, err2 := strconv.Atoi(id2) 797 if err1 == nil && err2 == nil { 798 return mint1 < mint2 799 } 800 // We have at least one container id so it gets complicated. 801 idParts1 := strings.Split(id1, "/") 802 idParts2 := strings.Split(id2, "/") 803 nrParts1 := len(idParts1) 804 nrParts2 := len(idParts2) 805 minLen := nrParts1 806 if nrParts2 < minLen { 807 minLen = nrParts2 808 } 809 for x := 0; x < minLen; x++ { 810 m1 := idParts1[x] 811 m2 := idParts2[x] 812 if m1 == m2 { 813 continue 814 } 815 // See if the id part is a container type, and if so compare directly. 816 if x%2 == 1 { 817 return m1 < m2 818 } 819 // Compare the integer ids. 820 // There's nothing we can do with errors at this point. 821 mint1, _ := strconv.Atoi(m1) 822 mint2, _ := strconv.Atoi(m2) 823 return mint1 < mint2 824 } 825 return nrParts1 < nrParts2 826 } 827 828 // Machine returns the machine with the given id. 829 func (st *State) Machine(id string) (*Machine, error) { 830 mdoc, err := st.getMachineDoc(id) 831 if err != nil { 832 return nil, err 833 } 834 return newMachine(st, mdoc), nil 835 } 836 837 func (st *State) getMachineDoc(id string) (*machineDoc, error) { 838 machinesCollection, closer := st.db().GetCollection(machinesC) 839 defer closer() 840 841 var err error 842 mdoc := &machineDoc{} 843 err = machinesCollection.FindId(id).One(mdoc) 844 845 switch err { 846 case nil: 847 return mdoc, nil 848 case mgo.ErrNotFound: 849 return nil, errors.NotFoundf("machine %s", id) 850 default: 851 return nil, errors.Annotatef(err, "cannot get machine %s", id) 852 } 853 } 854 855 // FindEntity returns the entity with the given tag. 856 // 857 // The returned value can be of type *Machine, *Unit, 858 // *User, *Application, *Model, or *Action, depending 859 // on the tag. 860 func (st *State) FindEntity(tag names.Tag) (Entity, error) { 861 id := tag.Id() 862 switch tag := tag.(type) { 863 case names.ControllerAgentTag: 864 return st.ControllerNode(id) 865 case names.MachineTag: 866 return st.Machine(id) 867 case names.UnitTag: 868 return st.Unit(id) 869 case names.UserTag: 870 return st.User(tag) 871 case names.ApplicationTag: 872 return st.Application(id) 873 case names.ModelTag: 874 model, err := st.Model() 875 if err != nil { 876 return nil, errors.Trace(err) 877 } 878 // Return an invalid entity error if the requested model is not 879 // the current one. 880 if id != model.UUID() { 881 if utils.IsValidUUIDString(id) { 882 return nil, errors.NotFoundf("model %q", id) 883 } 884 // TODO(axw) 2013-12-04 #1257587 885 // We should not accept model tags that do not match the 886 // model's UUID. We accept anything for now, to cater 887 // both for past usage, and for potentially supporting aliases. 888 logger.Warningf("model-tag does not match current model UUID: %q != %q", id, model.UUID()) 889 conf, err := model.ModelConfig() 890 if err != nil { 891 logger.Warningf("ModelConfig failed: %v", err) 892 } else if id != conf.Name() { 893 logger.Warningf("model-tag does not match current model name: %q != %q", id, conf.Name()) 894 } 895 } 896 return model, nil 897 case names.RelationTag: 898 return st.KeyRelation(id) 899 case names.ActionTag: 900 model, err := st.Model() 901 if err != nil { 902 return nil, errors.Trace(err) 903 } 904 return model.ActionByTag(tag) 905 case names.OperationTag: 906 model, err := st.Model() 907 if err != nil { 908 return nil, errors.Trace(err) 909 } 910 return model.Operation(tag.Id()) 911 case names.CharmTag: 912 return st.Charm(id) 913 case names.VolumeTag: 914 sb, err := NewStorageBackend(st) 915 if err != nil { 916 return nil, errors.Trace(err) 917 } 918 return sb.Volume(tag) 919 case names.FilesystemTag: 920 sb, err := NewStorageBackend(st) 921 if err != nil { 922 return nil, errors.Trace(err) 923 } 924 return sb.Filesystem(tag) 925 default: 926 return nil, errors.Errorf("unsupported tag %T", tag) 927 } 928 } 929 930 // tagToCollectionAndId, given an entity tag, returns the collection name and id 931 // of the entity document. 932 func (st *State) tagToCollectionAndId(tag names.Tag) (string, interface{}, error) { 933 if tag == nil { 934 return "", nil, errors.Errorf("tag is nil") 935 } 936 coll := "" 937 id := tag.Id() 938 switch tag := tag.(type) { 939 case names.MachineTag: 940 coll = machinesC 941 id = st.docID(id) 942 case names.ApplicationTag: 943 coll = applicationsC 944 id = st.docID(id) 945 case names.UnitTag: 946 coll = unitsC 947 id = st.docID(id) 948 case names.UserTag: 949 coll = usersC 950 if !tag.IsLocal() { 951 return "", nil, fmt.Errorf("%q is not a local user", tag.Id()) 952 } 953 id = tag.Name() 954 case names.RelationTag: 955 coll = relationsC 956 id = st.docID(id) 957 case names.ModelTag: 958 coll = modelsC 959 case names.ActionTag: 960 coll = actionsC 961 id = tag.Id() 962 case names.CharmTag: 963 coll = charmsC 964 id = tag.Id() 965 default: 966 return "", nil, errors.Errorf("%q is not a valid collection tag", tag) 967 } 968 return coll, id, nil 969 } 970 971 // removeStalePeerRelationsOps returns the operations necessary to remove any 972 // stale peer relation docs that may have been left behind after switching to 973 // a different charm. 974 func (st *State) removeStalePeerRelationsOps(applicationName string, relations []*Relation, newCharmMeta *charm.Meta) ([]txn.Op, error) { 975 if len(relations) == 0 { 976 return nil, nil // nothing to do 977 } 978 979 // Construct set of keys for existing peer relations. 980 oldPeerRelKeySet := set.NewStrings() 981 nextRel: 982 for _, rel := range relations { 983 for _, ep := range rel.Endpoints() { 984 if ep.Role == charm.RolePeer { 985 oldPeerRelKeySet.Add(ep.String()) 986 continue nextRel 987 } 988 } 989 } 990 991 // Construct set of keys for any peer relations defined by the new charm. 992 newPeerRelKeySet := set.NewStrings() 993 for _, rel := range newCharmMeta.Peers { 994 newPeerRelKeySet.Add( 995 relationKey( 996 []Endpoint{{ 997 ApplicationName: applicationName, 998 Relation: rel, 999 }}, 1000 ), 1001 ) 1002 } 1003 1004 // Remove any stale peer relation docs 1005 var ops []txn.Op 1006 for peerRelKey := range oldPeerRelKeySet.Difference(newPeerRelKeySet) { 1007 ops = append(ops, 1008 txn.Op{ 1009 C: relationsC, 1010 Id: st.docID(peerRelKey), 1011 Assert: txn.DocExists, 1012 Remove: true, 1013 }, 1014 ) 1015 } 1016 1017 // If any peer relation docs are to be removed, we need to adjust the 1018 // relationcount field for the application document accordingly. 1019 if removals := len(ops); removals > 0 { 1020 ops = append(ops, 1021 txn.Op{ 1022 C: applicationsC, 1023 Id: st.docID(applicationName), 1024 Assert: txn.DocExists, 1025 Update: bson.M{ 1026 "$inc": bson.M{ 1027 "relationcount": -removals, 1028 }, 1029 }, 1030 }, 1031 ) 1032 } 1033 1034 return ops, nil 1035 } 1036 1037 // addPeerRelationsOps returns the operations necessary to add the 1038 // specified application peer relations to the state. 1039 func (st *State) addPeerRelationsOps(applicationName string, peers map[string]charm.Relation) ([]txn.Op, error) { 1040 now := st.clock().Now() 1041 var ops []txn.Op 1042 for _, rel := range peers { 1043 relId, err := sequence(st, "relation") 1044 if err != nil { 1045 return nil, errors.Trace(err) 1046 } 1047 eps := []Endpoint{{ 1048 ApplicationName: applicationName, 1049 Relation: rel, 1050 }} 1051 relKey := relationKey(eps) 1052 relDoc := &relationDoc{ 1053 DocID: st.docID(relKey), 1054 Key: relKey, 1055 ModelUUID: st.ModelUUID(), 1056 Id: relId, 1057 Endpoints: eps, 1058 Life: Alive, 1059 } 1060 relationStatusDoc := statusDoc{ 1061 Status: status.Joining, 1062 ModelUUID: st.ModelUUID(), 1063 Updated: now.UnixNano(), 1064 } 1065 ops = append(ops, 1066 txn.Op{ 1067 C: relationsC, 1068 Id: relDoc.DocID, 1069 Assert: txn.DocMissing, 1070 Insert: relDoc, 1071 }, 1072 createStatusOp(st, relationGlobalScope(relId), relationStatusDoc), 1073 createSettingsOp( 1074 settingsC, 1075 relationApplicationSettingsKey(relId, eps[0].ApplicationName), 1076 map[string]interface{}{}, 1077 ), 1078 ) 1079 } 1080 return ops, nil 1081 } 1082 1083 var ( 1084 errSameNameRemoteApplicationExists = errors.Errorf("saas application with same name already exists") 1085 errLocalApplicationExists = errors.Errorf("application already exists") 1086 ) 1087 1088 // SaveCloudServiceArgs defines the arguments for SaveCloudService method. 1089 type SaveCloudServiceArgs struct { 1090 // Id will be the application Name if it's a part of application, 1091 // and will be controller UUID for k8s a controller(controller does not have an application), 1092 // then is wrapped with applicationGlobalKey. 1093 Id string 1094 ProviderId string 1095 Addresses corenetwork.SpaceAddresses 1096 1097 Generation int64 1098 DesiredScaleProtected bool 1099 } 1100 1101 // SaveCloudService creates a cloud service. 1102 func (st *State) SaveCloudService(args SaveCloudServiceArgs) (_ *CloudService, err error) { 1103 defer errors.DeferredAnnotatef(&err, "cannot add cloud service %q", args.ProviderId) 1104 1105 doc := cloudServiceDoc{ 1106 DocID: applicationGlobalKey(args.Id), 1107 ProviderId: args.ProviderId, 1108 Addresses: fromNetworkAddresses(args.Addresses, corenetwork.OriginProvider), 1109 Generation: args.Generation, 1110 DesiredScaleProtected: args.DesiredScaleProtected, 1111 } 1112 buildTxn := func(int) ([]txn.Op, error) { 1113 return buildCloudServiceOps(st, doc) 1114 } 1115 1116 if err := st.db().Run(buildTxn); err != nil { 1117 return nil, errors.Annotate(err, "failed to save cloud service") 1118 } 1119 // refresh then return updated CloudService. 1120 return newCloudService(st, &doc).CloudService() 1121 } 1122 1123 // CloudService returns a cloud service state by Id. 1124 func (st *State) CloudService(id string) (*CloudService, error) { 1125 svc := newCloudService(st, &cloudServiceDoc{DocID: st.docID(applicationGlobalKey(id))}) 1126 return svc.CloudService() 1127 } 1128 1129 type AddApplicationArgs struct { 1130 Name string 1131 Charm *Charm 1132 CharmOrigin *CharmOrigin 1133 Storage map[string]StorageConstraints 1134 Devices map[string]DeviceConstraints 1135 AttachStorage []names.StorageTag 1136 EndpointBindings map[string]string 1137 ApplicationConfig *config.Config 1138 CharmConfig charm.Settings 1139 NumUnits int 1140 Placement []*instance.Placement 1141 Constraints constraints.Value 1142 Resources map[string]string 1143 } 1144 1145 // AddApplication creates a new application, running the supplied charm, with the 1146 // supplied name (which must be unique). If the charm defines peer relations, 1147 // they will be created automatically. 1148 func (st *State) AddApplication(args AddApplicationArgs) (_ *Application, err error) { 1149 defer errors.DeferredAnnotatef(&err, "cannot add application %q", args.Name) 1150 1151 // Sanity checks. 1152 if !names.IsValidApplication(args.Name) { 1153 return nil, errors.Errorf("invalid name") 1154 } 1155 if args.Charm == nil { 1156 return nil, errors.Errorf("charm is nil") 1157 } 1158 if args.CharmOrigin == nil { 1159 return nil, errors.Errorf("charm origin is nil") 1160 } 1161 if args.CharmOrigin.Platform == nil { 1162 return nil, errors.Errorf("charm origin platform is nil") 1163 } 1164 1165 // If either the charm origin ID or Hash is set before a charm is 1166 // downloaded, charm download will fail for charms with a forced series. 1167 // The logic (refreshConfig) in sending the correct request to charmhub 1168 // will break. 1169 if (args.CharmOrigin.ID != "" && args.CharmOrigin.Hash == "") || 1170 (args.CharmOrigin.ID == "" && args.CharmOrigin.Hash != "") { 1171 return nil, errors.BadRequestf("programming error, AddApplication, neither CharmOrigin ID nor Hash can be set before a charm is downloaded. See CharmHubRepository GetDownloadURL.") 1172 } 1173 1174 model, err := st.Model() 1175 if err != nil { 1176 return nil, errors.Trace(err) 1177 } 1178 1179 // CAAS charms don't support volume/block storage yet. 1180 if model.Type() == ModelTypeCAAS { 1181 for name, charmStorage := range args.Charm.Meta().Storage { 1182 if storageKind(charmStorage.Type) != storage.StorageKindBlock { 1183 continue 1184 } 1185 var count uint64 1186 if arg, ok := args.Storage[name]; ok { 1187 count = arg.Count 1188 } 1189 if charmStorage.CountMin > 0 || count > 0 { 1190 return nil, errors.NotSupportedf("block storage on a container model") 1191 } 1192 } 1193 } 1194 1195 if len(args.AttachStorage) > 0 && args.NumUnits != 1 { 1196 return nil, errors.Errorf("AttachStorage is non-empty but NumUnits is %d, must be 1", args.NumUnits) 1197 } 1198 1199 if err := jujuversion.CheckJujuMinVersion(args.Charm.Meta().MinJujuVersion, jujuversion.Current); err != nil { 1200 return nil, errors.Trace(err) 1201 } 1202 1203 if exists, err := isNotDead(st, applicationsC, args.Name); err != nil { 1204 return nil, errors.Trace(err) 1205 } else if exists { 1206 return nil, errors.AlreadyExistsf("application") 1207 } 1208 if err := checkModelActive(st); err != nil { 1209 return nil, errors.Trace(err) 1210 } 1211 1212 // ensure storage 1213 if args.Storage == nil { 1214 args.Storage = make(map[string]StorageConstraints) 1215 } 1216 sb, err := NewStorageBackend(st) 1217 if err != nil { 1218 return nil, errors.Trace(err) 1219 } 1220 if err := addDefaultStorageConstraints(sb, args.Storage, args.Charm.Meta()); err != nil { 1221 return nil, errors.Trace(err) 1222 } 1223 if err := validateStorageConstraints(sb, args.Storage, args.Charm.Meta()); err != nil { 1224 return nil, errors.Trace(err) 1225 } 1226 storagePools := make(set.Strings) 1227 for _, storageParams := range args.Storage { 1228 storagePools.Add(storageParams.Pool) 1229 } 1230 1231 // ensure Devices 1232 if args.Devices == nil { 1233 args.Devices = make(map[string]DeviceConstraints) 1234 } 1235 deviceb, err := NewDeviceBackend(st) 1236 if err != nil { 1237 return nil, errors.Trace(err) 1238 } 1239 1240 if err := validateDeviceConstraints(deviceb, args.Devices, args.Charm.Meta()); err != nil { 1241 return nil, errors.Trace(err) 1242 } 1243 1244 // Always ensure that we snapshot the application architecture when adding 1245 // the application. If no architecture in the constraints, then look at 1246 // the model constraints. If no architecture is found in the model, use the 1247 // default architecture (amd64). 1248 var ( 1249 cons = args.Constraints 1250 subordinate = args.Charm.Meta().Subordinate 1251 ) 1252 if !subordinate && !cons.HasArch() { 1253 modelConstraints, err := st.ModelConstraints() 1254 if err != nil { 1255 return nil, errors.Trace(err) 1256 } 1257 a := constraints.ArchOrDefault(cons, &modelConstraints) 1258 cons.Arch = &a 1259 args.Constraints = cons 1260 } 1261 1262 // Perform model specific arg processing. 1263 var ( 1264 scale int 1265 placement string 1266 hasResources bool 1267 operatorStatusDoc *statusDoc 1268 ) 1269 nowNano := st.clock().Now().UnixNano() 1270 switch model.Type() { 1271 case ModelTypeIAAS: 1272 if err := st.processIAASModelApplicationArgs(&args); err != nil { 1273 return nil, errors.Trace(err) 1274 } 1275 case ModelTypeCAAS: 1276 hasResources = true // all k8s apps start with the assumption of resources 1277 if err := st.processCAASModelApplicationArgs(&args); err != nil { 1278 return nil, errors.Trace(err) 1279 } 1280 scale = args.NumUnits 1281 if len(args.Placement) == 1 { 1282 placement = args.Placement[0].Directive 1283 } 1284 operatorStatusDoc = &statusDoc{ 1285 ModelUUID: st.ModelUUID(), 1286 Status: status.Waiting, 1287 StatusInfo: status.MessageWaitForContainer, 1288 Updated: nowNano, 1289 } 1290 } 1291 1292 applicationID := st.docID(args.Name) 1293 1294 // Create the application addition operations. 1295 peers := args.Charm.Meta().Peers 1296 1297 // The doc defaults to CharmModifiedVersion = 0, which is correct, since it 1298 // has, by definition, at its initial state. 1299 cURL := args.Charm.URL() 1300 appDoc := &applicationDoc{ 1301 DocID: applicationID, 1302 Name: args.Name, 1303 ModelUUID: st.ModelUUID(), 1304 Subordinate: subordinate, 1305 CharmURL: &cURL, 1306 CharmOrigin: *args.CharmOrigin, 1307 RelationCount: len(peers), 1308 Life: Alive, 1309 UnitCount: args.NumUnits, 1310 1311 // CAAS 1312 DesiredScale: scale, 1313 Placement: placement, 1314 HasResources: hasResources, 1315 } 1316 1317 app := newApplication(st, appDoc) 1318 1319 // The app has no existing bindings yet. 1320 b, err := app.bindingsForOps(nil) 1321 if err != nil { 1322 return nil, errors.Trace(err) 1323 } 1324 endpointBindingsOp, err := b.createOp( 1325 args.EndpointBindings, 1326 args.Charm.Meta(), 1327 ) 1328 if err != nil { 1329 return nil, errors.Trace(err) 1330 } 1331 1332 statusDoc := statusDoc{ 1333 ModelUUID: st.ModelUUID(), 1334 Status: status.Unset, 1335 Updated: nowNano, 1336 } 1337 1338 if err := args.ApplicationConfig.Validate(); err != nil { 1339 return nil, errors.Trace(err) 1340 } 1341 appConfigAttrs := args.ApplicationConfig.Attributes() 1342 1343 // When creating the settings, we ignore nils. In other circumstances, nil 1344 // means to delete the value (reset to default), so creating with nil should 1345 // mean to use the default, i.e. don't set the value. 1346 removeNils(args.CharmConfig) 1347 removeNils(appConfigAttrs) 1348 1349 buildTxn := func(attempt int) ([]txn.Op, error) { 1350 // If we've tried once already and failed, check that 1351 // model may have been destroyed. 1352 if attempt > 0 { 1353 if err := checkModelActive(st); err != nil { 1354 return nil, errors.Trace(err) 1355 } 1356 // Ensure a local application with the same name doesn't exist. 1357 if exists, err := isNotDead(st, applicationsC, args.Name); err != nil { 1358 return nil, errors.Trace(err) 1359 } else if exists { 1360 return nil, errLocalApplicationExists 1361 } 1362 // Ensure a remote application with the same name doesn't exist. 1363 if remoteExists, err := isNotDead(st, remoteApplicationsC, args.Name); err != nil { 1364 return nil, errors.Trace(err) 1365 } else if remoteExists { 1366 return nil, errSameNameRemoteApplicationExists 1367 } 1368 } 1369 // The addApplicationOps does not include the model alive assertion, 1370 // so we add it here. 1371 ops := []txn.Op{ 1372 assertModelActiveOp(st.ModelUUID()), 1373 endpointBindingsOp, 1374 } 1375 addOps, err := addApplicationOps(st, app, addApplicationOpsArgs{ 1376 applicationDoc: appDoc, 1377 statusDoc: statusDoc, 1378 operatorStatus: operatorStatusDoc, 1379 constraints: args.Constraints, 1380 storage: args.Storage, 1381 devices: args.Devices, 1382 applicationConfig: appConfigAttrs, 1383 charmConfig: args.CharmConfig, 1384 }) 1385 if err != nil { 1386 return nil, errors.Trace(err) 1387 } 1388 ops = append(ops, addOps...) 1389 1390 // Collect peer relation addition operations. 1391 // 1392 // TODO(dimitern): Ensure each st.Endpoint has a space name associated in a 1393 // follow-up. 1394 addPeerOps, err := st.addPeerRelationsOps(args.Name, peers) 1395 if err != nil { 1396 return nil, errors.Trace(err) 1397 } 1398 ops = append(ops, addPeerOps...) 1399 1400 if len(args.Resources) > 0 { 1401 // Collect pending resource resolution operations. 1402 resources := st.resources() 1403 resOps, err := resources.resolveApplicationPendingResourcesOps(args.Name, args.Resources) 1404 if err != nil { 1405 return nil, errors.Trace(err) 1406 } 1407 ops = append(ops, resOps...) 1408 } 1409 1410 isSidecar, err := app.IsSidecar() 1411 if err != nil { 1412 return nil, errors.Trace(err) 1413 } 1414 if isSidecar { 1415 if err := resetSequence(st, app.Tag().String()); err != nil { 1416 return nil, errors.Trace(err) 1417 } 1418 } 1419 1420 // Collect unit-adding operations. 1421 for x := 0; x < args.NumUnits; x++ { 1422 unitName, unitOps, err := app.addUnitOpsWithCons(applicationAddUnitOpsArgs{ 1423 cons: args.Constraints, 1424 storageCons: args.Storage, 1425 attachStorage: args.AttachStorage, 1426 }) 1427 if err != nil { 1428 return nil, errors.Trace(err) 1429 } 1430 ops = append(ops, unitOps...) 1431 if model.Type() != ModelTypeCAAS { 1432 placement := instance.Placement{} 1433 if x < len(args.Placement) { 1434 placement = *args.Placement[x] 1435 } 1436 ops = append(ops, assignUnitOps(unitName, placement)...) 1437 } 1438 } 1439 return ops, nil 1440 } 1441 // At the last moment before inserting the application, prime status history. 1442 _, _ = probablyUpdateStatusHistory(st.db(), app.globalKey(), statusDoc) 1443 1444 if err = st.db().Run(buildTxn); err == nil { 1445 // Refresh to pick the txn-revno. 1446 if err = app.Refresh(); err != nil { 1447 return nil, errors.Trace(err) 1448 } 1449 return app, nil 1450 } 1451 return nil, errors.Trace(err) 1452 } 1453 1454 func (st *State) processCommonModelApplicationArgs(args *AddApplicationArgs) (Base, error) { 1455 // User has specified base. Overriding supported bases is 1456 // handled by the client, so args.Release is not necessarily 1457 // one of the charm's supported bases. We require that the 1458 // specified base is of the same operating system as one of 1459 // the supported bases. 1460 appBase, err := corebase.ParseBase(args.CharmOrigin.Platform.OS, args.CharmOrigin.Platform.Channel) 1461 if err != nil { 1462 return Base{}, errors.Trace(err) 1463 } 1464 1465 err = corecharm.OSIsCompatibleWithCharm(appBase.OS, args.Charm) 1466 if err != nil { 1467 return Base{}, errors.Trace(err) 1468 } 1469 1470 // Ignore constraints that result from this call as 1471 // these would be accumulation of model and application constraints 1472 // but we only want application constraints to be persisted here. 1473 cons, err := st.ResolveConstraints(args.Constraints) 1474 if err != nil { 1475 return Base{}, errors.Trace(err) 1476 } 1477 unsupported, err := st.validateConstraints(cons) 1478 if len(unsupported) > 0 { 1479 logger.Warningf( 1480 "deploying %q: unsupported constraints: %v", args.Name, strings.Join(unsupported, ",")) 1481 } 1482 return Base{appBase.OS, appBase.Channel.String()}, errors.Trace(err) 1483 } 1484 1485 func (st *State) processIAASModelApplicationArgs(args *AddApplicationArgs) error { 1486 appBase, err := st.processCommonModelApplicationArgs(args) 1487 if err != nil { 1488 return errors.Trace(err) 1489 } 1490 1491 storagePools := make(set.Strings) 1492 for _, storageParams := range args.Storage { 1493 storagePools.Add(storageParams.Pool) 1494 } 1495 1496 // Obtain volume attachment params corresponding to storage being 1497 // attached. We need to pass them along to precheckInstance, in 1498 // case the volumes cannot be attached to a machine with the given 1499 // placement directive. 1500 sb, err := NewStorageBackend(st) 1501 if err != nil { 1502 return errors.Trace(err) 1503 } 1504 volumeAttachments := make([]storage.VolumeAttachmentParams, 0, len(args.AttachStorage)) 1505 for _, storageTag := range args.AttachStorage { 1506 v, err := sb.StorageInstanceVolume(storageTag) 1507 if errors.IsNotFound(err) { 1508 continue 1509 } else if err != nil { 1510 return errors.Trace(err) 1511 } 1512 volumeInfo, err := v.Info() 1513 if err != nil { 1514 // Volume has not been provisioned yet, 1515 // so it cannot be attached. 1516 continue 1517 } 1518 providerType, _, _, err := poolStorageProvider(sb, volumeInfo.Pool) 1519 if err != nil { 1520 return errors.Annotatef(err, "cannot attach %s", names.ReadableString(storageTag)) 1521 } 1522 storageName, _ := names.StorageName(storageTag.Id()) 1523 volumeAttachments = append(volumeAttachments, storage.VolumeAttachmentParams{ 1524 AttachmentParams: storage.AttachmentParams{ 1525 Provider: providerType, 1526 ReadOnly: args.Charm.Meta().Storage[storageName].ReadOnly, 1527 }, 1528 Volume: v.VolumeTag(), 1529 VolumeId: volumeInfo.VolumeId, 1530 }) 1531 } 1532 1533 // Collect distinct placements that need to be checked. 1534 for _, placement := range args.Placement { 1535 data, err := st.parsePlacement(placement) 1536 if err != nil { 1537 return errors.Trace(err) 1538 } 1539 switch data.placementType() { 1540 case machinePlacement: 1541 // Ensure that the machine and charm series match. 1542 m, err := st.Machine(data.machineId) 1543 if err != nil { 1544 return errors.Trace(err) 1545 } 1546 subordinate := args.Charm.Meta().Subordinate 1547 if err := validateUnitMachineAssignment( 1548 m, appBase, subordinate, storagePools, 1549 ); err != nil { 1550 return errors.Annotatef( 1551 err, "cannot deploy to machine %s", m, 1552 ) 1553 } 1554 // This placement directive indicates that we're putting a 1555 // unit on a pre-existing machine. There's no need to 1556 // precheck the args since we're not starting an instance. 1557 1558 case directivePlacement: 1559 if err := st.precheckInstance( 1560 appBase, 1561 args.Constraints, 1562 data.directive, 1563 volumeAttachments, 1564 ); err != nil { 1565 return errors.Trace(err) 1566 } 1567 } 1568 } 1569 // We want to check the constraints if there's no placement at all. 1570 if len(args.Placement) == 0 { 1571 if err := st.precheckInstance( 1572 appBase, 1573 args.Constraints, 1574 "", 1575 volumeAttachments, 1576 ); err != nil { 1577 return errors.Trace(err) 1578 } 1579 } 1580 1581 return nil 1582 } 1583 1584 func (st *State) processCAASModelApplicationArgs(args *AddApplicationArgs) error { 1585 appSeries, err := st.processCommonModelApplicationArgs(args) 1586 if err != nil { 1587 return errors.Trace(err) 1588 } 1589 if len(args.Placement) > 0 { 1590 return errors.NotValidf("placement directives on k8s models") 1591 } 1592 return st.precheckInstance( 1593 appSeries, 1594 args.Constraints, 1595 "", 1596 nil, 1597 ) 1598 } 1599 1600 // removeNils removes any keys with nil values from the given map. 1601 func removeNils(m map[string]interface{}) { 1602 for k, v := range m { 1603 if v == nil { 1604 delete(m, k) 1605 } 1606 } 1607 } 1608 1609 // assignUnitOps returns the db ops to save unit assignment for use by the 1610 // UnitAssigner worker. 1611 func assignUnitOps(unitName string, placement instance.Placement) []txn.Op { 1612 udoc := assignUnitDoc{ 1613 DocId: unitName, 1614 Scope: placement.Scope, 1615 Directive: placement.Directive, 1616 } 1617 return []txn.Op{{ 1618 C: assignUnitC, 1619 Id: udoc.DocId, 1620 Assert: txn.DocMissing, 1621 Insert: udoc, 1622 }} 1623 } 1624 1625 // AssignStagedUnits gets called by the UnitAssigner worker, and runs the given 1626 // assignments. 1627 func (st *State) AssignStagedUnits(ids []string) ([]UnitAssignmentResult, error) { 1628 query := bson.D{{"_id", bson.D{{"$in", ids}}}} 1629 unitAssignments, err := st.unitAssignments(query) 1630 if err != nil { 1631 return nil, errors.Annotate(err, "getting staged unit assignments") 1632 } 1633 results := make([]UnitAssignmentResult, len(unitAssignments)) 1634 for i, a := range unitAssignments { 1635 err := st.assignStagedUnit(a) 1636 results[i].Unit = a.Unit 1637 results[i].Error = err 1638 } 1639 return results, nil 1640 } 1641 1642 // AllUnitAssignments returns all staged unit assignments in the model. 1643 func (st *State) AllUnitAssignments() ([]UnitAssignment, error) { 1644 return st.unitAssignments(nil) 1645 } 1646 1647 func (st *State) unitAssignments(query bson.D) ([]UnitAssignment, error) { 1648 col, closer := st.db().GetCollection(assignUnitC) 1649 defer closer() 1650 1651 var docs []assignUnitDoc 1652 if err := col.Find(query).All(&docs); err != nil { 1653 return nil, errors.Annotatef(err, "cannot get unit assignment docs") 1654 } 1655 results := make([]UnitAssignment, len(docs)) 1656 for i, doc := range docs { 1657 results[i] = UnitAssignment{ 1658 st.localID(doc.DocId), 1659 doc.Scope, 1660 doc.Directive, 1661 } 1662 } 1663 return results, nil 1664 } 1665 1666 func removeStagedAssignmentOp(id string) txn.Op { 1667 return txn.Op{ 1668 C: assignUnitC, 1669 Id: id, 1670 Remove: true, 1671 } 1672 } 1673 1674 func (st *State) assignStagedUnit(a UnitAssignment) error { 1675 u, err := st.Unit(a.Unit) 1676 if err != nil { 1677 return errors.Trace(err) 1678 } 1679 if a.Scope == "" && a.Directive == "" { 1680 return errors.Trace(st.AssignUnit(u, AssignCleanEmpty)) 1681 } 1682 1683 placement := &instance.Placement{Scope: a.Scope, Directive: a.Directive} 1684 1685 return errors.Trace(st.AssignUnitWithPlacement(u, placement)) 1686 } 1687 1688 // AssignUnitWithPlacement chooses a machine using the given placement directive 1689 // and then assigns the unit to it. 1690 func (st *State) AssignUnitWithPlacement(unit *Unit, placement *instance.Placement) error { 1691 // TODO(natefinch) this should be done as a single transaction, not two. 1692 // Mark https://launchpad.net/bugs/1506994 fixed when done. 1693 1694 data, err := st.parsePlacement(placement) 1695 if err != nil { 1696 return errors.Trace(err) 1697 } 1698 if data.placementType() == directivePlacement { 1699 return unit.assignToNewMachine(data.directive) 1700 } 1701 1702 m, err := st.addMachineWithPlacement(unit, data) 1703 if err != nil { 1704 return errors.Trace(err) 1705 } 1706 return unit.AssignToMachine(m) 1707 } 1708 1709 // placementData is a helper type that encodes some of the logic behind how an 1710 // instance.Placement gets translated into a placement directive the providers 1711 // understand. 1712 type placementData struct { 1713 machineId string 1714 directive string 1715 containerType instance.ContainerType 1716 } 1717 1718 type placementType int 1719 1720 const ( 1721 containerPlacement placementType = iota 1722 directivePlacement 1723 machinePlacement 1724 ) 1725 1726 // placementType returns the type of placement that this data represents. 1727 func (p placementData) placementType() placementType { 1728 if p.containerType != "" { 1729 return containerPlacement 1730 } 1731 if p.directive != "" { 1732 return directivePlacement 1733 } 1734 return machinePlacement 1735 } 1736 1737 func (st *State) parsePlacement(placement *instance.Placement) (*placementData, error) { 1738 // Extract container type and parent from container placement directives. 1739 if container, err := instance.ParseContainerType(placement.Scope); err == nil { 1740 return &placementData{ 1741 containerType: container, 1742 machineId: placement.Directive, 1743 }, nil 1744 } 1745 switch placement.Scope { 1746 case st.ModelUUID(): 1747 return &placementData{directive: placement.Directive}, nil 1748 case instance.MachineScope: 1749 return &placementData{machineId: placement.Directive}, nil 1750 default: 1751 return nil, errors.Errorf("placement scope: invalid model UUID %q", placement.Scope) 1752 } 1753 } 1754 1755 // addMachineWithPlacement finds a machine that matches the given 1756 // placement directive for the given unit. 1757 func (st *State) addMachineWithPlacement(unit *Unit, data *placementData) (*Machine, error) { 1758 unitCons, err := unit.Constraints() 1759 if err != nil { 1760 return nil, errors.Trace(err) 1761 } 1762 1763 // Turn any endpoint bindings for the unit's application into machine 1764 // constraints. This prevents a possible race condition where the 1765 // provisioner can act on a newly created container before the unit is 1766 // assigned to it, missing the required spaces for bridging based on 1767 // endpoint bindings. 1768 // TODO (manadart 2019-10-08): This step is not necessary when a single 1769 // transaction is used based on the comment below. 1770 app, err := unit.Application() 1771 if err != nil { 1772 return nil, errors.Trace(err) 1773 } 1774 bindings, err := app.EndpointBindings() 1775 if err != nil { 1776 return nil, errors.Trace(err) 1777 } 1778 1779 lookup, err := st.AllSpaceInfos() 1780 if err != nil { 1781 return nil, errors.Trace(err) 1782 } 1783 1784 // Space constraints must be space name format as they are 1785 // used by the providers directly. 1786 bindingsNameMap, err := bindings.MapWithSpaceNames(lookup) 1787 if err != nil { 1788 return nil, errors.Trace(err) 1789 } 1790 spaces := set.NewStrings() 1791 for _, name := range bindingsNameMap { 1792 // TODO (manadart 2019-10-08): "alpha" is not guaranteed to have 1793 // subnets, which the provisioner expects, so can not be used as 1794 // a constraint. This also preserves behavior from when the 1795 // AlphaSpaceName was "". This condition will be removed with 1796 // the institution of universal mutable spaces. 1797 if name != corenetwork.AlphaSpaceName { 1798 spaces.Add(name) 1799 } 1800 } 1801 1802 // Merging constraints returns an error if any spaces are already set, 1803 // so we "move" any existing constraints over to the bind spaces before 1804 // parsing and merging. 1805 if unitCons.Spaces != nil { 1806 for _, sp := range *unitCons.Spaces { 1807 spaces.Add(sp) 1808 } 1809 unitCons.Spaces = nil 1810 } 1811 spaceCons := constraints.MustParse("spaces=" + strings.Join(spaces.Values(), ",")) 1812 1813 cons, err := constraints.Merge(*unitCons, spaceCons) 1814 if err != nil { 1815 return nil, errors.Trace(err) 1816 } 1817 1818 // Create any new machine marked as dirty so that 1819 // nothing else will grab it before we assign the unit to it. 1820 // TODO(natefinch) fix this when we put assignment in the same 1821 // transaction as adding a machine. See bug 1822 // https://launchpad.net/bugs/1506994 1823 1824 mId := data.machineId 1825 var machine *Machine 1826 if data.machineId != "" { 1827 machine, err = st.Machine(mId) 1828 if err != nil { 1829 return nil, errors.Trace(err) 1830 } 1831 1832 // Check if an upgrade-series lock is present for the requested 1833 // machine or its parent. 1834 // If one exists, return an error to prevent deployment. 1835 locked, err := machine.IsLockedForSeriesUpgrade() 1836 if err != nil { 1837 return nil, errors.Trace(err) 1838 } 1839 if locked { 1840 return nil, errors.Errorf("machine %q is locked for series upgrade", mId) 1841 } 1842 locked, err = machine.IsParentLockedForSeriesUpgrade() 1843 if err != nil { 1844 return nil, errors.Trace(err) 1845 } 1846 if locked { 1847 return nil, errors.Errorf("machine hosting %q is locked for series upgrade", mId) 1848 } 1849 } 1850 1851 switch data.placementType() { 1852 case containerPlacement: 1853 // If a container is to be used, create it. 1854 template := MachineTemplate{ 1855 Base: unit.doc.Base, 1856 Jobs: []MachineJob{JobHostUnits}, 1857 Dirty: true, 1858 Constraints: cons, 1859 } 1860 if mId != "" { 1861 return st.AddMachineInsideMachine(template, mId, data.containerType) 1862 } 1863 return st.AddMachineInsideNewMachine(template, template, data.containerType) 1864 case directivePlacement: 1865 return nil, errors.NotSupportedf( 1866 "programming error: directly adding a machine for %s with a non-machine placement directive", unit.Name()) 1867 default: 1868 return machine, nil 1869 } 1870 } 1871 1872 // Application returns an application state by name. 1873 func (st *State) Application(name string) (_ *Application, err error) { 1874 applications, closer := st.db().GetCollection(applicationsC) 1875 defer closer() 1876 1877 if !names.IsValidApplication(name) { 1878 return nil, errors.Errorf("%q is not a valid application name", name) 1879 } 1880 sdoc := &applicationDoc{} 1881 err = applications.FindId(name).One(sdoc) 1882 if err == mgo.ErrNotFound { 1883 return nil, errors.NotFoundf("application %q", name) 1884 } 1885 if err != nil { 1886 return nil, errors.Annotatef(err, "cannot get application %q", name) 1887 } 1888 return newApplication(st, sdoc), nil 1889 } 1890 1891 // AllApplications returns all deployed applications in the model. 1892 func (st *State) AllApplications() (applications []*Application, err error) { 1893 applicationsCollection, closer := st.db().GetCollection(applicationsC) 1894 defer closer() 1895 1896 sdocs := []applicationDoc{} 1897 err = applicationsCollection.Find(bson.D{}).All(&sdocs) 1898 if err != nil { 1899 return nil, errors.Errorf("cannot get all applications") 1900 } 1901 for _, v := range sdocs { 1902 applications = append(applications, newApplication(st, &v)) 1903 } 1904 return applications, nil 1905 } 1906 1907 // InferActiveRelation returns the relation corresponding to the supplied 1908 // application or endpoint name(s), assuming such a relation exists and is unique. 1909 // There must be 1 or 2 supplied names, of the form <application>[:<relation>]. 1910 func (st *State) InferActiveRelation(names ...string) (*Relation, error) { 1911 candidates, err := matchingRelations(st, names...) 1912 if err != nil { 1913 return nil, err 1914 } 1915 1916 relationQuery := strings.Join(names, " ") 1917 if len(candidates) == 0 { 1918 return nil, errors.NotFoundf("relation matching %q", relationQuery) 1919 } 1920 if len(candidates) == 1 { 1921 return candidates[0], nil 1922 } 1923 1924 keys := make([]string, len(candidates)) 1925 for i, relation := range candidates { 1926 keys[i] = fmt.Sprintf("%q", relation.String()) 1927 } 1928 return nil, errors.Errorf("ambiguous relation: %q could refer to %s", 1929 relationQuery, strings.Join(keys, "; "), 1930 ) 1931 } 1932 1933 // InferEndpoints returns the endpoints corresponding to the supplied names. 1934 // There must be 1 or 2 supplied names, of the form <application>[:<relation>]. 1935 // If the supplied names uniquely specify a possible relation, or if they 1936 // uniquely specify a possible relation once all implicit relations have been 1937 // filtered, the endpoints corresponding to that relation will be returned. 1938 func (st *State) InferEndpoints(names ...string) ([]Endpoint, error) { 1939 // Collect all possible sane endpoint lists. 1940 var candidates [][]Endpoint 1941 switch len(names) { 1942 // Implicitly assume this is a peer relation, as they have only one endpoint 1943 case 1: 1944 eps, err := st.endpoints(names[0], isPeer) 1945 if err != nil { 1946 return nil, errors.Trace(err) 1947 } 1948 for _, ep := range eps { 1949 candidates = append(candidates, []Endpoint{ep}) 1950 } 1951 // All other relations are between two endpoints 1952 case 2: 1953 eps1, err := st.endpoints(names[0], notPeer) 1954 if err != nil { 1955 return nil, errors.Trace(err) 1956 } 1957 eps2, err := st.endpoints(names[1], notPeer) 1958 if err != nil { 1959 return nil, errors.Trace(err) 1960 } 1961 for _, ep1 := range eps1 { 1962 for _, ep2 := range eps2 { 1963 scopeOk, err := containerScopeOk(st, ep1, ep2) 1964 if err != nil { 1965 return nil, errors.Trace(err) 1966 } 1967 if ep1.CanRelateTo(ep2) && scopeOk { 1968 candidates = append(candidates, []Endpoint{ep1, ep2}) 1969 } 1970 } 1971 } 1972 default: 1973 return nil, errors.Errorf("cannot relate %d endpoints", len(names)) 1974 } 1975 switch len(candidates) { 1976 case 0: 1977 return nil, errors.Errorf("no relations found") 1978 case 1: 1979 return candidates[0], nil 1980 } 1981 // If there's ambiguity, try discarding implicit relations. 1982 var filtered [][]Endpoint 1983 outer: 1984 for _, cand := range candidates { 1985 for _, ep := range cand { 1986 if ep.IsImplicit() { 1987 continue outer 1988 } 1989 } 1990 filtered = append(filtered, cand) 1991 } 1992 if len(filtered) == 1 { 1993 return filtered[0], nil 1994 } 1995 keys := []string{} 1996 for _, cand := range candidates { 1997 keys = append(keys, fmt.Sprintf("%q", relationKey(cand))) 1998 } 1999 sort.Strings(keys) 2000 return nil, errors.Errorf("ambiguous relation: %q could refer to %s", 2001 strings.Join(names, " "), strings.Join(keys, "; ")) 2002 } 2003 2004 func isPeer(ep Endpoint) bool { 2005 return ep.Role == charm.RolePeer 2006 } 2007 2008 func notPeer(ep Endpoint) bool { 2009 return ep.Role != charm.RolePeer 2010 } 2011 2012 func containerScopeOk(st *State, ep1, ep2 Endpoint) (bool, error) { 2013 if ep1.Scope != charm.ScopeContainer && ep2.Scope != charm.ScopeContainer { 2014 return true, nil 2015 } 2016 var subordinateCount int 2017 for _, ep := range []Endpoint{ep1, ep2} { 2018 app, err := applicationByName(st, ep.ApplicationName) 2019 if err != nil { 2020 return false, err 2021 } 2022 // Container scoped relations are not allowed for remote applications. 2023 if app.IsRemote() { 2024 return false, nil 2025 } 2026 if app.(*Application).doc.Subordinate { 2027 subordinateCount++ 2028 } 2029 } 2030 return subordinateCount >= 1, nil 2031 } 2032 2033 func splitEndpointName(name string) (string, string, error) { 2034 if i := strings.Index(name, ":"); i == -1 { 2035 return name, "", nil 2036 } else if i != 0 && i != len(name)-1 { 2037 return name[:i], name[i+1:], nil 2038 } else { 2039 return "", "", errors.Errorf("invalid endpoint %q", name) 2040 } 2041 } 2042 2043 func applicationByName(st *State, name string) (ApplicationEntity, error) { 2044 app, err := st.Application(name) 2045 if err == nil { 2046 return app, nil 2047 } else if err != nil && !errors.IsNotFound(err) { 2048 return nil, err 2049 } 2050 remoteApp, remoteErr := st.RemoteApplication(name) 2051 if errors.IsNotFound(remoteErr) { 2052 // We can't find either an application or a remote application 2053 // by that name. Report the missing application, since that's 2054 // probably what was intended (and still indicates the problem 2055 // for remote applications). 2056 return nil, err 2057 } 2058 return remoteApp, remoteErr 2059 } 2060 2061 // endpoints returns all endpoints that could be intended by the 2062 // supplied endpoint name, and which cause the filter param to 2063 // return true. 2064 func (st *State) endpoints(name string, filter func(ep Endpoint) bool) ([]Endpoint, error) { 2065 appName, relName, err := splitEndpointName(name) 2066 if err != nil { 2067 return nil, err 2068 } 2069 app, err := applicationByName(st, appName) 2070 if err != nil { 2071 return nil, errors.Trace(err) 2072 } 2073 eps := []Endpoint{} 2074 if relName != "" { 2075 ep, err := app.Endpoint(relName) 2076 if err != nil { 2077 return nil, errors.Trace(err) 2078 } 2079 eps = append(eps, ep) 2080 } else { 2081 eps, err = app.Endpoints() 2082 if err != nil { 2083 return nil, errors.Trace(err) 2084 } 2085 } 2086 final := []Endpoint{} 2087 for _, ep := range eps { 2088 if filter(ep) { 2089 final = append(final, ep) 2090 } 2091 } 2092 return final, nil 2093 } 2094 2095 // AddRelation creates a new relation with the given endpoints. 2096 func (st *State) AddRelation(eps ...Endpoint) (r *Relation, err error) { 2097 key := relationKey(eps) 2098 defer errors.DeferredAnnotatef(&err, "cannot add relation %q", key) 2099 2100 // Enforce basic endpoint sanity. The epCount restrictions may be relaxed 2101 // in the future; if so, this method is likely to need significant rework. 2102 if len(eps) != 2 { 2103 return nil, errors.Errorf("relation must have two endpoints") 2104 } 2105 if !eps[0].CanRelateTo(eps[1]) { 2106 return nil, errors.Errorf("endpoints do not relate") 2107 } 2108 2109 // Check applications are alive and do checks if one is remote. 2110 app1, err := aliveApplication(st, eps[0].ApplicationName) 2111 if err != nil { 2112 return nil, err 2113 } 2114 app2, err := aliveApplication(st, eps[1].ApplicationName) 2115 if err != nil { 2116 return nil, err 2117 } 2118 if app1.IsRemote() && app2.IsRemote() { 2119 return nil, errors.Errorf("cannot add relation between saas applications %q and %q", eps[0].ApplicationName, eps[1].ApplicationName) 2120 } 2121 remoteRelation := app1.IsRemote() || app2.IsRemote() 2122 ep0ok := app1.IsRemote() || eps[0].Scope == charm.ScopeGlobal 2123 ep1ok := app2.IsRemote() || eps[1].Scope == charm.ScopeGlobal 2124 if remoteRelation && (!ep0ok || !ep1ok) { 2125 return nil, errors.Errorf("local endpoint must be globally scoped for remote relations") 2126 } 2127 2128 // If either endpoint has container scope, so must the other; and the 2129 // application's base must also match, because they'll be deployed to 2130 // the same machines. 2131 compatibleBases := true 2132 if eps[0].Scope == charm.ScopeContainer { 2133 eps[1].Scope = charm.ScopeContainer 2134 } else if eps[1].Scope == charm.ScopeContainer { 2135 eps[0].Scope = charm.ScopeContainer 2136 } else { 2137 compatibleBases = false 2138 } 2139 2140 // We only get a unique relation id once, to save on roundtrips. If it's 2141 // -1, we haven't got it yet (we don't get it at this stage, because we 2142 // still don't know whether it's sane to even attempt creation). 2143 id := -1 2144 2145 // If a application's charm is upgraded while we're trying to add a relation, 2146 // we'll need to re-validate application sanity. 2147 var doc *relationDoc 2148 now := st.clock().Now() 2149 buildTxn := func(attempt int) ([]txn.Op, error) { 2150 // Perform initial relation sanity check. 2151 if exists, err := isNotDead(st, relationsC, key); err != nil { 2152 return nil, errors.Trace(err) 2153 } else if exists { 2154 // Ignore the error here, if there is an error, we know that dying 2155 // will be false and can fall through to error message below. 2156 if dying, _ := isDying(st, relationsC, key); dying { 2157 return nil, errors.NewAlreadyExists(nil, fmt.Sprintf("relation %v is dying, but not yet removed", key)) 2158 } 2159 return nil, errors.NewAlreadyExists(nil, fmt.Sprintf("relation %v", key)) 2160 } 2161 2162 // Collect per-application operations, checking sanity as we go. 2163 var ops []txn.Op 2164 var subordinateCount int 2165 appBases := map[string][]corebase.Base{} 2166 for _, ep := range eps { 2167 app, err := aliveApplication(st, ep.ApplicationName) 2168 if err != nil { 2169 return nil, err 2170 } 2171 if app.IsRemote() { 2172 // If the remote application is known to be terminated, we don't 2173 // allow a relation to it. 2174 statusInfo, err := app.Status() 2175 if err != nil && !errors.IsNotFound(err) { 2176 return nil, errors.Trace(err) 2177 } 2178 if err == nil && statusInfo.Status == status.Terminated { 2179 return nil, errors.Errorf("remote offer %s is terminated", ep.ApplicationName) 2180 } 2181 ops = append(ops, txn.Op{ 2182 C: remoteApplicationsC, 2183 Id: st.docID(ep.ApplicationName), 2184 Assert: bson.D{{"life", Alive}}, 2185 Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}}, 2186 }) 2187 } else { 2188 localApp := app.(*Application) 2189 if localApp.doc.Subordinate { 2190 subordinateCount++ 2191 } 2192 ch, _, err := localApp.Charm() 2193 if err != nil { 2194 return nil, errors.Trace(err) 2195 } 2196 if !ep.ImplementedBy(ch) { 2197 return nil, errors.Errorf("%q does not implement %q", ep.ApplicationName, ep) 2198 } 2199 charmBases, err := corecharm.ComputedBases(ch) 2200 if err != nil { 2201 return nil, errors.Trace(err) 2202 } 2203 if len(charmBases) == 0 { 2204 return nil, errors.NotValidf("charm %q does not support any bases", ch.Meta().Name) 2205 } 2206 appBases[localApp.doc.Name] = charmBases 2207 ops = append(ops, txn.Op{ 2208 C: applicationsC, 2209 Id: st.docID(ep.ApplicationName), 2210 Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}}, 2211 Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}}, 2212 }) 2213 } 2214 2215 // Enforce max-relation limits for the app:ep combination 2216 if err := enforceMaxRelationLimit(app, ep); err != nil { 2217 return nil, errors.Trace(err) 2218 } 2219 } 2220 // We need to ensure that there's intersection between the supported 2221 // bases of both applications' charms. 2222 if compatibleBases && len(appBases) > 1 && !compatibleSupportedBases(appBases[eps[0].ApplicationName], appBases[eps[1].ApplicationName]) { 2223 return nil, errors.Errorf("principal and subordinate applications' bases must match") 2224 } 2225 if eps[0].Scope == charm.ScopeContainer && subordinateCount < 1 { 2226 return nil, errors.Errorf("container scoped relation requires at least one subordinate application") 2227 } 2228 2229 // Create a new unique id if that has not already been done, and add 2230 // an operation to create the relation document. 2231 if id == -1 { 2232 var err error 2233 if id, err = sequence(st, "relation"); err != nil { 2234 return nil, errors.Trace(err) 2235 } 2236 } 2237 docID := st.docID(key) 2238 doc = &relationDoc{ 2239 DocID: docID, 2240 Key: key, 2241 ModelUUID: st.ModelUUID(), 2242 Id: id, 2243 Endpoints: eps, 2244 Life: Alive, 2245 } 2246 relationStatusDoc := statusDoc{ 2247 Status: status.Joining, 2248 ModelUUID: st.ModelUUID(), 2249 Updated: now.UnixNano(), 2250 } 2251 ops = append(ops, txn.Op{ 2252 C: relationsC, 2253 Id: docID, 2254 Assert: txn.DocMissing, 2255 Insert: doc, 2256 }, createStatusOp(st, relationGlobalScope(id), relationStatusDoc)) 2257 2258 for _, ep := range eps { 2259 key := relationApplicationSettingsKey(id, ep.ApplicationName) 2260 settingsOp := createSettingsOp(settingsC, key, map[string]interface{}{}) 2261 ops = append(ops, settingsOp) 2262 } 2263 return ops, nil 2264 } 2265 if err = st.db().Run(buildTxn); err == nil { 2266 return &Relation{st, *doc}, nil 2267 } 2268 return nil, errors.Trace(err) 2269 } 2270 2271 func compatibleSupportedBases(b1s, b2s []corebase.Base) bool { 2272 for _, b1 := range b1s { 2273 for _, b2 := range b2s { 2274 if b1.IsCompatible(b2) { 2275 return true 2276 } 2277 } 2278 } 2279 return false 2280 } 2281 2282 // enforceMaxRelationLimit returns an error if adding an additional relation 2283 // from app:ep exceeds the maximum allowed relation limit as specified in the 2284 // charm metadata. 2285 func enforceMaxRelationLimit(app ApplicationEntity, ep Endpoint) error { 2286 // No limit defined 2287 if ep.Relation.Limit == 0 { 2288 return nil 2289 } 2290 2291 // Count the number of already established relations for this app:endpoint 2292 existingRels, err := app.Relations() 2293 if err != nil { 2294 return errors.Trace(err) 2295 } 2296 2297 // Adding a new relation would bump the already established limit by 1 2298 establishedCount := establishedRelationCount(existingRels, ep.ApplicationName, ep.Relation) 2299 if establishedCount+1 > ep.Relation.Limit { 2300 return errors.QuotaLimitExceededf("establishing a new relation for %s:%s would exceed its maximum relation limit of %d", ep.ApplicationName, ep.Relation.Name, ep.Relation.Limit) 2301 } 2302 2303 return nil 2304 } 2305 2306 func aliveApplication(st *State, name string) (ApplicationEntity, error) { 2307 app, err := applicationByName(st, name) 2308 if errors.IsNotFound(err) { 2309 return nil, errors.Errorf("application %q does not exist", name) 2310 } else if err != nil { 2311 return nil, errors.Trace(err) 2312 } else if app.Life() != Alive { 2313 return nil, errors.Errorf("application %q is not alive", name) 2314 } 2315 return app, err 2316 } 2317 2318 // EndpointsRelation returns the existing relation with the given endpoints. 2319 func (st *State) EndpointsRelation(endpoints ...Endpoint) (*Relation, error) { 2320 return st.KeyRelation(relationKey(endpoints)) 2321 } 2322 2323 // KeyRelation returns the existing relation with the given key (which can 2324 // be derived unambiguously from the relation's endpoints). 2325 func (st *State) KeyRelation(key string) (*Relation, error) { 2326 relations, closer := st.db().GetCollection(relationsC) 2327 defer closer() 2328 2329 doc := relationDoc{} 2330 err := relations.FindId(key).One(&doc) 2331 if err == mgo.ErrNotFound { 2332 return nil, errors.NotFoundf("relation %q", key) 2333 } 2334 if err != nil { 2335 return nil, errors.Annotatef(err, "cannot get relation %q", key) 2336 } 2337 return newRelation(st, &doc), nil 2338 } 2339 2340 // Relation returns the existing relation with the given id. 2341 func (st *State) Relation(id int) (*Relation, error) { 2342 relations, closer := st.db().GetCollection(relationsC) 2343 defer closer() 2344 2345 doc := relationDoc{} 2346 err := relations.Find(bson.D{{"id", id}}).One(&doc) 2347 if err == mgo.ErrNotFound { 2348 return nil, errors.NotFoundf("relation %d", id) 2349 } 2350 if err != nil { 2351 return nil, errors.Annotatef(err, "cannot get relation %d", id) 2352 } 2353 return newRelation(st, &doc), nil 2354 } 2355 2356 // AllRelations returns all relations in the model ordered by id. 2357 func (st *State) AllRelations() (relations []*Relation, err error) { 2358 relationsCollection, closer := st.db().GetCollection(relationsC) 2359 defer closer() 2360 2361 docs := relationDocSlice{} 2362 err = relationsCollection.Find(nil).All(&docs) 2363 if err != nil { 2364 return nil, errors.Annotate(err, "cannot get all relations") 2365 } 2366 sort.Sort(docs) 2367 for _, v := range docs { 2368 relations = append(relations, newRelation(st, &v)) 2369 } 2370 return 2371 } 2372 2373 // AliveRelationKeys returns the relation keys of all live relations in 2374 // the model. Used in charmhub metrics collection. 2375 func (st *State) AliveRelationKeys() []string { 2376 relationsCollection, closer := st.db().GetCollection(relationsC) 2377 defer closer() 2378 var doc struct { 2379 Key string `bson:"key"` 2380 } 2381 2382 var keys []string 2383 iter := relationsCollection.Find(isAliveDoc).Iter() 2384 defer func() { _ = iter.Close() }() 2385 for iter.Next(&doc) { 2386 key := doc.Key 2387 keys = append(keys, key) 2388 } 2389 return keys 2390 } 2391 2392 // Report conforms to the Dependency Engine Report() interface, giving an opportunity to introspect 2393 // what is going on at runtime. 2394 func (st *State) Report() map[string]interface{} { 2395 if st.workers == nil { 2396 return nil 2397 } 2398 return st.workers.Report() 2399 } 2400 2401 type relationDocSlice []relationDoc 2402 2403 func (rdc relationDocSlice) Len() int { return len(rdc) } 2404 func (rdc relationDocSlice) Swap(i, j int) { rdc[i], rdc[j] = rdc[j], rdc[i] } 2405 func (rdc relationDocSlice) Less(i, j int) bool { 2406 return rdc[i].Id < rdc[j].Id 2407 } 2408 2409 // Unit returns a unit by name. 2410 func (st *State) Unit(name string) (*Unit, error) { 2411 if !names.IsValidUnit(name) { 2412 return nil, errors.Errorf("%q is not a valid unit name", name) 2413 } 2414 units, closer := st.db().GetCollection(unitsC) 2415 defer closer() 2416 2417 doc := unitDoc{} 2418 err := units.FindId(name).One(&doc) 2419 if err == mgo.ErrNotFound { 2420 return nil, errors.NotFoundf("unit %q", name) 2421 } 2422 if err != nil { 2423 return nil, errors.Annotatef(err, "cannot get unit %q", name) 2424 } 2425 model, err := st.Model() 2426 if err != nil { 2427 return nil, errors.Trace(err) 2428 } 2429 return newUnit(st, model.Type(), &doc), nil 2430 } 2431 2432 // UnitsFor returns the units placed in the given machine id. 2433 func (st *State) UnitsFor(machineId string) ([]*Unit, error) { 2434 if !names.IsValidMachine(machineId) { 2435 return nil, errors.Errorf("%q is not a valid machine id", machineId) 2436 } 2437 m := &Machine{ 2438 st: st, 2439 doc: machineDoc{ 2440 Id: machineId, 2441 }, 2442 } 2443 return m.Units() 2444 } 2445 2446 // UnitsInError returns the units which have an agent status of Error. 2447 func (st *State) UnitsInError() ([]*Unit, error) { 2448 // First, find the agents in error state. 2449 agentGlobalKeys, err := getEntityKeysForStatus(st, "u", status.Error) 2450 if err != nil { 2451 return nil, errors.Trace(err) 2452 } 2453 // Extract the unit names. 2454 unitNames := make([]string, len(agentGlobalKeys)) 2455 for i, key := range agentGlobalKeys { 2456 // agent key prefix is "u#" 2457 if !strings.HasPrefix(key, "u#") { 2458 return nil, errors.NotValidf("unit agent global key %q", key) 2459 } 2460 unitNames[i] = key[2:] 2461 } 2462 2463 // Query the units with the names of units in error. 2464 units, closer := st.db().GetCollection(unitsC) 2465 defer closer() 2466 2467 var docs []unitDoc 2468 err = units.Find(bson.D{{"name", bson.D{{"$in", unitNames}}}}).All(&docs) 2469 if err != nil { 2470 return nil, errors.Trace(err) 2471 } 2472 model, err := st.Model() 2473 if err != nil { 2474 return nil, errors.Trace(err) 2475 } 2476 2477 result := make([]*Unit, len(docs)) 2478 for i, doc := range docs { 2479 result[i] = &Unit{st: st, doc: doc, modelType: model.Type()} 2480 } 2481 return result, nil 2482 } 2483 2484 // AssignUnit places the unit on a machine. Depending on the policy, and the 2485 // state of the model, this may lead to new instances being launched 2486 // within the model. 2487 func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) { 2488 if !u.IsPrincipal() { 2489 return errors.Errorf("subordinate unit %q cannot be assigned directly to a machine", u) 2490 } 2491 defer errors.DeferredAnnotatef(&err, "cannot assign unit %q to machine", u) 2492 var m *Machine 2493 switch policy { 2494 case AssignLocal: 2495 m, err = st.Machine("0") 2496 if err != nil { 2497 return errors.Trace(err) 2498 } 2499 return u.AssignToMachine(m) 2500 case AssignClean: 2501 if _, err = u.AssignToCleanMachine(); errors.Cause(err) != noCleanMachines { 2502 return errors.Trace(err) 2503 } 2504 return u.AssignToNewMachineOrContainer() 2505 case AssignCleanEmpty: 2506 if _, err = u.AssignToCleanEmptyMachine(); errors.Cause(err) != noCleanMachines { 2507 return errors.Trace(err) 2508 } 2509 return u.AssignToNewMachineOrContainer() 2510 case AssignNew: 2511 return errors.Trace(u.AssignToNewMachine()) 2512 } 2513 return errors.Errorf("unknown unit assignment policy: %q", policy) 2514 } 2515 2516 // SetAdminMongoPassword sets the administrative password 2517 // to access the state. If the password is non-empty, 2518 // all subsequent attempts to access the state must 2519 // be authorized; otherwise no authorization is required. 2520 func (st *State) SetAdminMongoPassword(password string) error { 2521 err := mongo.SetAdminMongoPassword(st.session, mongo.AdminUser, password) 2522 return errors.Trace(err) 2523 } 2524 2525 func (st *State) networkEntityGlobalKeyOp(globalKey string, providerId corenetwork.Id) txn.Op { 2526 key := st.networkEntityGlobalKey(globalKey, providerId) 2527 return txn.Op{ 2528 C: providerIDsC, 2529 Id: key, 2530 Assert: txn.DocMissing, 2531 Insert: providerIdDoc{ID: key}, 2532 } 2533 } 2534 2535 func (st *State) networkEntityGlobalKeyRemoveOp(globalKey string, providerId corenetwork.Id) txn.Op { 2536 key := st.networkEntityGlobalKey(globalKey, providerId) 2537 return txn.Op{ 2538 C: providerIDsC, 2539 Id: key, 2540 Remove: true, 2541 } 2542 } 2543 2544 func (st *State) networkEntityGlobalKeyExists(globalKey string, providerId corenetwork.Id) (bool, error) { 2545 col, closer := st.db().GetCollection(providerIDsC) 2546 defer closer() 2547 2548 key := st.networkEntityGlobalKey(globalKey, providerId) 2549 var doc providerIdDoc 2550 err := col.FindId(key).One(&doc) 2551 2552 switch err { 2553 case nil: 2554 return true, nil 2555 case mgo.ErrNotFound: 2556 return false, nil 2557 default: 2558 return false, errors.Annotatef(err, "reading provider ID %q", key) 2559 } 2560 } 2561 2562 func (st *State) networkEntityGlobalKey(globalKey string, providerId corenetwork.Id) string { 2563 return st.docID(globalKey + ":" + string(providerId)) 2564 } 2565 2566 // SetSLA sets the SLA on the current connected model. 2567 func (st *State) SetSLA(level, owner string, credentials []byte) error { 2568 model, err := st.Model() 2569 if err != nil { 2570 return errors.Trace(err) 2571 } 2572 return model.SetSLA(level, owner, credentials) 2573 } 2574 2575 // SetModelMeterStatus sets the meter status for the current connected model. 2576 func (st *State) SetModelMeterStatus(status, info string) error { 2577 model, err := st.Model() 2578 if err != nil { 2579 return errors.Trace(err) 2580 } 2581 return model.SetMeterStatus(status, info) 2582 } 2583 2584 // ModelMeterStatus returns the meter status for the current connected model. 2585 func (st *State) ModelMeterStatus() (MeterStatus, error) { 2586 model, err := st.Model() 2587 if err != nil { 2588 return MeterStatus{MeterNotAvailable, ""}, errors.Trace(err) 2589 } 2590 return model.MeterStatus(), nil 2591 } 2592 2593 // SLALevel returns the SLA level of the current connected model. 2594 func (st *State) SLALevel() (string, error) { 2595 model, err := st.Model() 2596 if err != nil { 2597 return "", errors.Trace(err) 2598 } 2599 return model.SLALevel(), nil 2600 } 2601 2602 // SLACredential returns the SLA credential of the current connected model. 2603 func (st *State) SLACredential() ([]byte, error) { 2604 model, err := st.Model() 2605 if err != nil { 2606 return []byte{}, errors.Trace(err) 2607 } 2608 return model.SLACredential(), nil 2609 } 2610 2611 var tagPrefix = map[byte]string{ 2612 'm': names.MachineTagKind + "-", 2613 'a': names.ApplicationTagKind + "-", 2614 'u': names.UnitTagKind + "-", 2615 'e': names.ModelTagKind + "-", 2616 'r': names.RelationTagKind + "-", 2617 } 2618 2619 func tagForGlobalKey(key string) (string, bool) { 2620 if len(key) < 3 || key[1] != '#' { 2621 return "", false 2622 } 2623 p, ok := tagPrefix[key[0]] 2624 if !ok { 2625 return "", false 2626 } 2627 return p + key[2:], true 2628 } 2629 2630 // TagFromDocID tries attempts to extract an entity-identifying tag from a 2631 // Mongo document ID. 2632 // For example "c9741ea1-0c2a-444d-82f5-787583a48557:a#mediawiki" would yield 2633 // an application tag for "mediawiki" 2634 func TagFromDocID(docID string) names.Tag { 2635 _, localID, _ := splitDocID(docID) 2636 switch { 2637 case strings.HasPrefix(localID, "a#"): 2638 return names.NewApplicationTag(localID[2:]) 2639 case strings.HasPrefix(localID, "m#"): 2640 return names.NewMachineTag(localID[2:]) 2641 case strings.HasPrefix(localID, "u#"): 2642 return names.NewUnitTag(localID[2:]) 2643 case strings.HasPrefix(localID, "e"): 2644 return names.NewModelTag(docID) 2645 default: 2646 return nil 2647 } 2648 }