github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/model.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 jujutxn "github.com/juju/txn" 13 "gopkg.in/mgo.v2" 14 "gopkg.in/mgo.v2/bson" 15 "gopkg.in/mgo.v2/txn" 16 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/mongo" 19 "github.com/juju/juju/status" 20 "github.com/juju/version" 21 ) 22 23 // modelGlobalKey is the key for the model, its 24 // settings and constraints. 25 const modelGlobalKey = "e" 26 27 // MigrationMode specifies where the Model is with respect to migration. 28 type MigrationMode string 29 30 const ( 31 // MigrationModeActive is the default mode for a model and reflects 32 // a model that is active within its controller. 33 MigrationModeActive MigrationMode = "" 34 35 // MigrationModeExporting reflects a model that is in the process of being 36 // exported from one controller to another. 37 MigrationModeExporting MigrationMode = "exporting" 38 39 // MigrationModeImporting reflects a model that is being imported into a 40 // controller, but is not yet fully active. 41 MigrationModeImporting MigrationMode = "importing" 42 ) 43 44 // Model represents the state of a model. 45 type Model struct { 46 // st is not necessarily the state of this model. Though it is 47 // usually safe to assume that it is. The only times it isn't is when we 48 // get models other than the current one - which is mostly in 49 // controller api endpoints. 50 st *State 51 doc modelDoc 52 } 53 54 // modelDoc represents the internal state of the model in MongoDB. 55 type modelDoc struct { 56 UUID string `bson:"_id"` 57 Name string 58 Life Life 59 Owner string `bson:"owner"` 60 ServerUUID string `bson:"server-uuid"` 61 MigrationMode MigrationMode `bson:"migration-mode"` 62 63 // LatestAvailableTools is a string representing the newest version 64 // found while checking streams for new versions. 65 LatestAvailableTools string `bson:"available-tools,omitempty"` 66 } 67 68 // modelEntityRefsDoc records references to the top-level entities 69 // in the model. 70 type modelEntityRefsDoc struct { 71 UUID string `bson:"_id"` 72 73 // Machines contains the names of the top-level machines in the model. 74 Machines []string `bson:"machines"` 75 76 // Services contains the names of the services in the model. 77 Services []string `bson:"services"` 78 } 79 80 // ControllerModel returns the model that was bootstrapped. 81 // This is the only model that can have controller machines. 82 // The owner of this model is also considered "special", in that 83 // they are the only user that is able to create other users (until we 84 // have more fine grained permissions), and they cannot be disabled. 85 func (st *State) ControllerModel() (*Model, error) { 86 ssinfo, err := st.ControllerInfo() 87 if err != nil { 88 return nil, errors.Annotate(err, "could not get controller info") 89 } 90 91 models, closer := st.getCollection(modelsC) 92 defer closer() 93 94 env := &Model{st: st} 95 uuid := ssinfo.ModelTag.Id() 96 if err := env.refresh(models.FindId(uuid)); err != nil { 97 return nil, errors.Trace(err) 98 } 99 return env, nil 100 } 101 102 // Model returns the model entity. 103 func (st *State) Model() (*Model, error) { 104 models, closer := st.getCollection(modelsC) 105 defer closer() 106 107 env := &Model{st: st} 108 uuid := st.modelTag.Id() 109 if err := env.refresh(models.FindId(uuid)); err != nil { 110 return nil, errors.Trace(err) 111 } 112 return env, nil 113 } 114 115 // GetModel looks for the model identified by the uuid passed in. 116 func (st *State) GetModel(tag names.ModelTag) (*Model, error) { 117 models, closer := st.getCollection(modelsC) 118 defer closer() 119 120 env := &Model{st: st} 121 if err := env.refresh(models.FindId(tag.Id())); err != nil { 122 return nil, errors.Trace(err) 123 } 124 return env, nil 125 } 126 127 // AllModels returns all the models in the system. 128 func (st *State) AllModels() ([]*Model, error) { 129 models, closer := st.getCollection(modelsC) 130 defer closer() 131 132 var envDocs []modelDoc 133 err := models.Find(nil).All(&envDocs) 134 if err != nil { 135 return nil, err 136 } 137 138 result := make([]*Model, len(envDocs)) 139 for i, doc := range envDocs { 140 result[i] = &Model{st: st, doc: doc} 141 } 142 143 return result, nil 144 } 145 146 // ModelArgs is a params struct for creating a new model. 147 type ModelArgs struct { 148 Config *config.Config 149 Owner names.UserTag 150 MigrationMode MigrationMode 151 } 152 153 // NewModel creates a new model with its own UUID and 154 // prepares it for use. Model and State instances for the new 155 // model are returned. 156 // 157 // The controller model's UUID is attached to the new 158 // model's document. Having the server UUIDs stored with each 159 // model document means that we have a way to represent external 160 // models, perhaps for future use around cross model 161 // relations. 162 func (st *State) NewModel(args ModelArgs) (_ *Model, _ *State, err error) { 163 owner := args.Owner 164 if owner.IsLocal() { 165 if _, err := st.User(owner); err != nil { 166 return nil, nil, errors.Annotate(err, "cannot create model") 167 } 168 } 169 170 ssEnv, err := st.ControllerModel() 171 if err != nil { 172 return nil, nil, errors.Annotate(err, "could not load controller model") 173 } 174 175 uuid := args.Config.UUID() 176 newState, err := st.ForModel(names.NewModelTag(uuid)) 177 if err != nil { 178 return nil, nil, errors.Annotate(err, "could not create state for new model") 179 } 180 defer func() { 181 if err != nil { 182 newState.Close() 183 } 184 }() 185 186 ops, err := newState.modelSetupOps(args.Config, uuid, ssEnv.UUID(), owner, args.MigrationMode) 187 if err != nil { 188 return nil, nil, errors.Annotate(err, "failed to create new model") 189 } 190 err = newState.runTransaction(ops) 191 if err == txn.ErrAborted { 192 193 // We have a unique key restriction on the "owner" and "name" fields, 194 // which will cause the insert to fail if there is another record with 195 // the same "owner" and "name" in the collection. If the txn is 196 // aborted, check if it is due to the unique key restriction. 197 name := args.Config.Name() 198 models, closer := st.getCollection(modelsC) 199 defer closer() 200 envCount, countErr := models.Find(bson.D{ 201 {"owner", owner.Canonical()}, 202 {"name", name}}, 203 ).Count() 204 if countErr != nil { 205 err = errors.Trace(countErr) 206 } else if envCount > 0 { 207 err = errors.AlreadyExistsf("model %q for %s", name, owner.Canonical()) 208 } else { 209 err = errors.New("model already exists") 210 } 211 } 212 if err != nil { 213 return nil, nil, errors.Trace(err) 214 } 215 216 newEnv, err := newState.Model() 217 if err != nil { 218 return nil, nil, errors.Trace(err) 219 } 220 221 return newEnv, newState, nil 222 } 223 224 // Tag returns a name identifying the model. 225 // The returned name will be different from other Tag values returned 226 // by any other entities from the same state. 227 func (m *Model) Tag() names.Tag { 228 return m.ModelTag() 229 } 230 231 // ModelTag is the concrete model tag for this model. 232 func (m *Model) ModelTag() names.ModelTag { 233 return names.NewModelTag(m.doc.UUID) 234 } 235 236 // ControllerTag is the model tag for the controller that the model is 237 // running within. 238 func (m *Model) ControllerTag() names.ModelTag { 239 return names.NewModelTag(m.doc.ServerUUID) 240 } 241 242 // UUID returns the universally unique identifier of the model. 243 func (m *Model) UUID() string { 244 return m.doc.UUID 245 } 246 247 // ControllerUUID returns the universally unique identifier of the controller 248 // in which the model is running. 249 func (m *Model) ControllerUUID() string { 250 return m.doc.ServerUUID 251 } 252 253 // Name returns the human friendly name of the model. 254 func (m *Model) Name() string { 255 return m.doc.Name 256 } 257 258 // MigrationMode returns whether the model is active or being migrated. 259 func (m *Model) MigrationMode() MigrationMode { 260 return m.doc.MigrationMode 261 } 262 263 // SetMigrationMode updates the migration mode of the model. 264 func (m *Model) SetMigrationMode(mode MigrationMode) error { 265 st, closeState, err := m.getState() 266 if err != nil { 267 return errors.Trace(err) 268 } 269 defer closeState() 270 271 ops := []txn.Op{{ 272 C: modelsC, 273 Id: m.doc.UUID, 274 Assert: txn.DocExists, 275 Update: bson.D{{"$set", bson.D{{"migration-mode", mode}}}}, 276 }} 277 if err := st.runTransaction(ops); err != nil { 278 return errors.Trace(err) 279 } 280 return m.Refresh() 281 } 282 283 // Life returns whether the model is Alive, Dying or Dead. 284 func (m *Model) Life() Life { 285 return m.doc.Life 286 } 287 288 // Owner returns tag representing the owner of the model. 289 // The owner is the user that created the model. 290 func (m *Model) Owner() names.UserTag { 291 return names.NewUserTag(m.doc.Owner) 292 } 293 294 // Status returns the status of the model. 295 func (m *Model) Status() (status.StatusInfo, error) { 296 st, closeState, err := m.getState() 297 if err != nil { 298 return status.StatusInfo{}, errors.Trace(err) 299 } 300 defer closeState() 301 status, err := getStatus(st, m.globalKey(), "model") 302 if err != nil { 303 return status, err 304 } 305 return status, nil 306 } 307 308 // SetStatus sets the status of the model. 309 func (m *Model) SetStatus(modelStatus status.Status, info string, data map[string]interface{}) error { 310 st, closeState, err := m.getState() 311 if err != nil { 312 return errors.Trace(err) 313 } 314 defer closeState() 315 if !status.ValidModelStatus(modelStatus) { 316 return errors.Errorf("cannot set invalid status %q", modelStatus) 317 } 318 return setStatus(st, setStatusParams{ 319 badge: "model", 320 globalKey: m.globalKey(), 321 status: modelStatus, 322 message: info, 323 rawData: data, 324 }) 325 } 326 327 // Config returns the config for the model. 328 func (m *Model) Config() (*config.Config, error) { 329 st, closeState, err := m.getState() 330 if err != nil { 331 return nil, errors.Trace(err) 332 } 333 defer closeState() 334 return st.ModelConfig() 335 } 336 337 // UpdateLatestToolsVersion looks up for the latest available version of 338 // juju tools and updates environementDoc with it. 339 func (m *Model) UpdateLatestToolsVersion(ver version.Number) error { 340 v := ver.String() 341 // TODO(perrito666): I need to assert here that there isn't a newer 342 // version in place. 343 ops := []txn.Op{{ 344 C: modelsC, 345 Id: m.doc.UUID, 346 Update: bson.D{{"$set", bson.D{{"available-tools", v}}}}, 347 }} 348 err := m.st.runTransaction(ops) 349 if err != nil { 350 return errors.Trace(err) 351 } 352 return m.Refresh() 353 } 354 355 // LatestToolsVersion returns the newest version found in the last 356 // check in the streams. 357 // Bear in mind that the check was performed filtering only 358 // new patches for the current major.minor. (major.minor.patch) 359 func (m *Model) LatestToolsVersion() version.Number { 360 ver := m.doc.LatestAvailableTools 361 if ver == "" { 362 return version.Zero 363 } 364 v, err := version.Parse(ver) 365 if err != nil { 366 // This is being stored from a valid version but 367 // in case this data would beacame corrupt It is not 368 // worth to fail because of it. 369 return version.Zero 370 } 371 return v 372 } 373 374 // globalKey returns the global database key for the model. 375 func (m *Model) globalKey() string { 376 return modelGlobalKey 377 } 378 379 func (m *Model) Refresh() error { 380 models, closer := m.st.getCollection(modelsC) 381 defer closer() 382 return m.refresh(models.FindId(m.UUID())) 383 } 384 385 func (m *Model) refresh(query mongo.Query) error { 386 err := query.One(&m.doc) 387 if err == mgo.ErrNotFound { 388 return errors.NotFoundf("model") 389 } 390 return err 391 } 392 393 // Users returns a slice of all users for this model. 394 func (m *Model) Users() ([]*ModelUser, error) { 395 if m.st.ModelUUID() != m.UUID() { 396 return nil, errors.New("cannot lookup model users outside the current model") 397 } 398 coll, closer := m.st.getCollection(modelUsersC) 399 defer closer() 400 401 var userDocs []modelUserDoc 402 err := coll.Find(nil).All(&userDocs) 403 if err != nil { 404 return nil, errors.Trace(err) 405 } 406 407 var modelUsers []*ModelUser 408 for _, doc := range userDocs { 409 modelUsers = append(modelUsers, &ModelUser{ 410 st: m.st, 411 doc: doc, 412 }) 413 } 414 415 return modelUsers, nil 416 } 417 418 // Destroy sets the models's lifecycle to Dying, preventing 419 // addition of services or machines to state. If called on 420 // an empty hosted model, the lifecycle will be advanced 421 // straight to Dead. 422 // 423 // If called on a controller model, and that controller is 424 // hosting any non-Dead models, this method will return an 425 // error satisfying IsHasHostedsError. 426 func (m *Model) Destroy() (err error) { 427 ensureNoHostedModels := false 428 if m.doc.UUID == m.doc.ServerUUID { 429 ensureNoHostedModels = true 430 } 431 return m.destroy(ensureNoHostedModels) 432 } 433 434 // DestroyIncludingHosted sets the model's lifecycle to Dying, preventing 435 // addition of services or machines to state. If this model is a controller 436 // hosting other models, they will also be destroyed. 437 func (m *Model) DestroyIncludingHosted() error { 438 ensureNoHostedModels := false 439 return m.destroy(ensureNoHostedModels) 440 } 441 442 func (m *Model) destroy(ensureNoHostedModels bool) (err error) { 443 defer errors.DeferredAnnotatef(&err, "failed to destroy model") 444 445 st, closeState, err := m.getState() 446 if err != nil { 447 return errors.Trace(err) 448 } 449 defer closeState() 450 451 buildTxn := func(attempt int) ([]txn.Op, error) { 452 // On the first attempt, we assume memory state is recent 453 // enough to try using... 454 if attempt != 0 { 455 // ...but on subsequent attempts, we read fresh environ 456 // state from the DB. Note that we do *not* refresh `e` 457 // itself, as detailed in doc/hacking-state.txt 458 if m, err = st.Model(); err != nil { 459 return nil, errors.Trace(err) 460 } 461 } 462 463 ops, err := m.destroyOps(ensureNoHostedModels, false) 464 if err == errModelNotAlive { 465 return nil, jujutxn.ErrNoOperations 466 } else if err != nil { 467 return nil, errors.Trace(err) 468 } 469 470 return ops, nil 471 } 472 473 return st.run(buildTxn) 474 } 475 476 // errModelNotAlive is a signal emitted from destroyOps to indicate 477 // that model destruction is already underway. 478 var errModelNotAlive = errors.New("model is no longer alive") 479 480 type hasHostedModelsError int 481 482 func (e hasHostedModelsError) Error() string { 483 return fmt.Sprintf("hosting %d other models", e) 484 } 485 486 func IsHasHostedModelsError(err error) bool { 487 _, ok := errors.Cause(err).(hasHostedModelsError) 488 return ok 489 } 490 491 // destroyOps returns the txn operations necessary to begin model 492 // destruction, or an error indicating why it can't. 493 // 494 // If ensureNoHostedModels is true, then destroyOps will 495 // fail if there are any non-Dead hosted models 496 func (m *Model) destroyOps(ensureNoHostedModels, ensureEmpty bool) ([]txn.Op, error) { 497 if m.Life() != Alive { 498 return nil, errModelNotAlive 499 } 500 501 // Ensure we're using the model's state. 502 st, closeState, err := m.getState() 503 if err != nil { 504 return nil, errors.Trace(err) 505 } 506 defer closeState() 507 508 if err := ensureDestroyable(st); err != nil { 509 return nil, errors.Trace(err) 510 } 511 512 // Check if the model is empty. If it is, we can advance the model's 513 // lifecycle state directly to Dead. 514 var prereqOps []txn.Op 515 checkEmptyErr := m.checkEmpty() 516 isEmpty := checkEmptyErr == nil 517 uuid := m.UUID() 518 if ensureEmpty && !isEmpty { 519 return nil, errors.Trace(checkEmptyErr) 520 } 521 if isEmpty { 522 prereqOps = append(prereqOps, txn.Op{ 523 C: modelEntityRefsC, 524 Id: uuid, 525 Assert: bson.D{ 526 {"machines", bson.D{{"$size", 0}}}, 527 {"services", bson.D{{"$size", 0}}}, 528 }, 529 }) 530 } 531 532 if ensureNoHostedModels { 533 // Check for any Dying or alive but non-empty models. If there 534 // are any, we return an error indicating that there are hosted 535 // models. 536 models, err := st.AllModels() 537 if err != nil { 538 return nil, errors.Trace(err) 539 } 540 var aliveEmpty, aliveNonEmpty, dying, dead int 541 for _, model := range models { 542 if model.UUID() == m.UUID() { 543 // Ignore the controller model. 544 continue 545 } 546 if model.Life() == Dead { 547 // Dead hosted models don't affect 548 // whether the controller can be 549 // destroyed or not, but they are 550 // still counted in the hosted models. 551 dead++ 552 continue 553 } 554 // See if the model is empty, and if it is, 555 // get the ops required to destroy it. 556 ops, err := model.destroyOps(false, true) 557 switch err { 558 case errModelNotAlive: 559 dying++ 560 case nil: 561 prereqOps = append(prereqOps, ops...) 562 aliveEmpty++ 563 default: 564 aliveNonEmpty++ 565 } 566 } 567 if dying > 0 || aliveNonEmpty > 0 { 568 // There are Dying, or Alive but non-empty models. 569 // We cannot destroy the controller without first 570 // destroying the models and waiting for them to 571 // become Dead. 572 return nil, errors.Trace( 573 hasHostedModelsError(dying + aliveNonEmpty + aliveEmpty), 574 ) 575 } 576 // Ensure that the number of active models has not changed 577 // between the query and when the transaction is applied. 578 // 579 // Note that we assert that each empty model that we intend 580 // move to Dead is still Alive, so we're protected from an 581 // ABA style problem where an empty model is concurrently 582 // removed, and replaced with a non-empty model. 583 prereqOps = append(prereqOps, assertHostedModelsOp(aliveEmpty+dead)) 584 } 585 586 life := Dying 587 if isEmpty && uuid != m.doc.ServerUUID { 588 // The model is empty, and is not the admin 589 // model, so we can move it straight to Dead. 590 life = Dead 591 } 592 timeOfDying := nowToTheSecond() 593 modelUpdateValues := bson.D{ 594 {"life", life}, 595 {"time-of-dying", timeOfDying}, 596 } 597 if life == Dead { 598 modelUpdateValues = append(modelUpdateValues, bson.DocElem{ 599 "time-of-death", timeOfDying, 600 }) 601 } 602 603 ops := []txn.Op{{ 604 C: modelsC, 605 Id: uuid, 606 Assert: isAliveDoc, 607 Update: bson.D{{"$set", modelUpdateValues}}, 608 }} 609 610 // Because txn operations execute in order, and may encounter 611 // arbitrarily long delays, we need to make sure every op 612 // causes a state change that's still consistent; so we make 613 // sure the cleanup ops are the last thing that will execute. 614 if uuid == m.doc.ServerUUID { 615 cleanupOp := st.newCleanupOp(cleanupModelsForDyingController, uuid) 616 ops = append(ops, cleanupOp) 617 } 618 if !isEmpty { 619 // We only need to destroy machines and models if the model is 620 // non-empty. It wouldn't normally be harmful to enqueue the 621 // cleanups otherwise, except for when we're destroying an empty 622 // hosted model in the course of destroying the controller. In 623 // that case we'll get errors if we try to enqueue hosted-model 624 // cleanups, because the cleanups collection is non-global. 625 cleanupMachinesOp := st.newCleanupOp(cleanupMachinesForDyingModel, uuid) 626 ops = append(ops, cleanupMachinesOp) 627 cleanupServicesOp := st.newCleanupOp(cleanupServicesForDyingModel, uuid) 628 ops = append(ops, cleanupServicesOp) 629 } 630 return append(prereqOps, ops...), nil 631 } 632 633 // checkEmpty checks that the machine is empty of any entities that may 634 // require external resource cleanup. If the machine is not empty, then 635 // an error will be returned. 636 func (m *Model) checkEmpty() error { 637 st, closeState, err := m.getState() 638 if err != nil { 639 return errors.Trace(err) 640 } 641 defer closeState() 642 643 modelEntityRefs, closer := st.getCollection(modelEntityRefsC) 644 defer closer() 645 646 var doc modelEntityRefsDoc 647 if err := modelEntityRefs.FindId(m.UUID()).One(&doc); err != nil { 648 if err == mgo.ErrNotFound { 649 return errors.NotFoundf("entity references doc for model %s", m.UUID()) 650 } 651 return errors.Annotatef(err, "getting entity references for model %s", m.UUID()) 652 } 653 if n := len(doc.Machines); n > 0 { 654 return errors.Errorf("model not empty, found %d machine(s)", n) 655 } 656 if n := len(doc.Services); n > 0 { 657 return errors.Errorf("model not empty, found %d services(s)", n) 658 } 659 return nil 660 } 661 662 func addModelMachineRefOp(st *State, machineId string) txn.Op { 663 return txn.Op{ 664 C: modelEntityRefsC, 665 Id: st.ModelUUID(), 666 Assert: txn.DocExists, 667 Update: bson.D{{"$addToSet", bson.D{{"machines", machineId}}}}, 668 } 669 } 670 671 func removeModelMachineRefOp(st *State, machineId string) txn.Op { 672 return txn.Op{ 673 C: modelEntityRefsC, 674 Id: st.ModelUUID(), 675 Update: bson.D{{"$pull", bson.D{{"machines", machineId}}}}, 676 } 677 } 678 679 func addModelServiceRefOp(st *State, serviceName string) txn.Op { 680 return txn.Op{ 681 C: modelEntityRefsC, 682 Id: st.ModelUUID(), 683 Assert: txn.DocExists, 684 Update: bson.D{{"$addToSet", bson.D{{"services", serviceName}}}}, 685 } 686 } 687 688 func removeModelServiceRefOp(st *State, serviceName string) txn.Op { 689 return txn.Op{ 690 C: modelEntityRefsC, 691 Id: st.ModelUUID(), 692 Update: bson.D{{"$pull", bson.D{{"services", serviceName}}}}, 693 } 694 } 695 696 // checkManualMachines checks if any of the machines in the slice were 697 // manually provisioned, and are non-manager machines. These machines 698 // must (currently) be manually destroyed via destroy-machine before 699 // destroy-model can successfully complete. 700 func checkManualMachines(machines []*Machine) error { 701 var ids []string 702 for _, m := range machines { 703 if m.IsManager() { 704 continue 705 } 706 manual, err := m.IsManual() 707 if err != nil { 708 return errors.Trace(err) 709 } 710 if manual { 711 ids = append(ids, m.Id()) 712 } 713 } 714 if len(ids) > 0 { 715 return errors.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " ")) 716 } 717 return nil 718 } 719 720 // ensureDestroyable an error if any manual machines or persistent volumes are 721 // found. 722 func ensureDestroyable(st *State) error { 723 724 // TODO(waigani) bug #1475212: Model destroy can miss manual 725 // machines. We need to be able to assert the absence of these as 726 // part of the destroy txn, but in order to do this manual machines 727 // need to add refcounts to their models. 728 729 // Check for manual machines. We bail out if there are any, 730 // to stop the user from prematurely hobbling the model. 731 machines, err := st.AllMachines() 732 if err != nil { 733 return errors.Trace(err) 734 } 735 736 if err := checkManualMachines(machines); err != nil { 737 return errors.Trace(err) 738 } 739 740 return nil 741 } 742 743 // createModelOp returns the operation needed to create 744 // an model document with the given name and UUID. 745 func createModelOp(st *State, owner names.UserTag, name, uuid, server string, mode MigrationMode) txn.Op { 746 doc := &modelDoc{ 747 UUID: uuid, 748 Name: name, 749 Life: Alive, 750 Owner: owner.Canonical(), 751 ServerUUID: server, 752 MigrationMode: mode, 753 } 754 return txn.Op{ 755 C: modelsC, 756 Id: uuid, 757 Assert: txn.DocMissing, 758 Insert: doc, 759 } 760 } 761 762 func createModelEntityRefsOp(st *State, uuid string) txn.Op { 763 return txn.Op{ 764 C: modelEntityRefsC, 765 Id: uuid, 766 Assert: txn.DocMissing, 767 Insert: &modelEntityRefsDoc{UUID: uuid}, 768 } 769 } 770 771 const hostedModelCountKey = "hostedModelCount" 772 773 type hostedModelCountDoc struct { 774 // RefCount is the number of models in the Juju system. 775 // We do not count the system model. 776 RefCount int `bson:"refcount"` 777 } 778 779 func assertNoHostedModelsOp() txn.Op { 780 return assertHostedModelsOp(0) 781 } 782 783 func assertHostedModelsOp(n int) txn.Op { 784 return txn.Op{ 785 C: controllersC, 786 Id: hostedModelCountKey, 787 Assert: bson.D{{"refcount", n}}, 788 } 789 } 790 791 func incHostedModelCountOp() txn.Op { 792 return HostedModelCountOp(1) 793 } 794 795 func decHostedModelCountOp() txn.Op { 796 return HostedModelCountOp(-1) 797 } 798 799 func HostedModelCountOp(amount int) txn.Op { 800 return txn.Op{ 801 C: controllersC, 802 Id: hostedModelCountKey, 803 Assert: txn.DocExists, 804 Update: bson.M{ 805 "$inc": bson.M{"refcount": amount}, 806 }, 807 } 808 } 809 810 func hostedModelCount(st *State) (int, error) { 811 var doc hostedModelCountDoc 812 controllers, closer := st.getCollection(controllersC) 813 defer closer() 814 815 if err := controllers.Find(bson.D{{"_id", hostedModelCountKey}}).One(&doc); err != nil { 816 return 0, errors.Trace(err) 817 } 818 return doc.RefCount, nil 819 } 820 821 // createUniqueOwnerModelNameOp returns the operation needed to create 822 // an usermodelnameC document with the given owner and model name. 823 func createUniqueOwnerModelNameOp(owner names.UserTag, envName string) txn.Op { 824 return txn.Op{ 825 C: usermodelnameC, 826 Id: userModelNameIndex(owner.Canonical(), envName), 827 Assert: txn.DocMissing, 828 Insert: bson.M{}, 829 } 830 } 831 832 // assertAliveOp returns a txn.Op that asserts the model is alive. 833 func (m *Model) assertActiveOp() txn.Op { 834 return assertModelActiveOp(m.UUID()) 835 } 836 837 // getState returns the State for the model, and a function to close 838 // it when done. If m.st is the model-specific state, then it will 839 // be returned and the close function will be a no-op. 840 // 841 // TODO(axw) 2016-04-14 #1570269 842 // find a way to guarantee that every Model is associated with the 843 // appropriate State. The current work-around is too easy to get wrong. 844 func (m *Model) getState() (*State, func(), error) { 845 if m.st.modelTag == m.ModelTag() { 846 return m.st, func() {}, nil 847 } 848 st, err := m.st.ForModel(m.ModelTag()) 849 if err != nil { 850 return nil, nil, errors.Trace(err) 851 } 852 uuid := st.ModelUUID() 853 return st, func() { 854 if err := st.Close(); err != nil { 855 logger.Errorf("closing temporary state for model %s", uuid) 856 } 857 }, nil 858 } 859 860 // assertModelActiveOp returns a txn.Op that asserts the given 861 // model UUID refers to an Alive model. 862 func assertModelActiveOp(modelUUID string) txn.Op { 863 return txn.Op{ 864 C: modelsC, 865 Id: modelUUID, 866 Assert: append(isAliveDoc, bson.DocElem{"migration-mode", MigrationModeActive}), 867 } 868 } 869 870 func checkModelActive(st *State) error { 871 model, err := st.Model() 872 if (err == nil && model.Life() != Alive) || errors.IsNotFound(err) { 873 return errors.Errorf("model %q is no longer alive", model.Name()) 874 } else if err != nil { 875 return errors.Annotate(err, "unable to read model") 876 } else if mode := model.MigrationMode(); mode != MigrationModeActive { 877 return errors.Errorf("model %q is being migrated", model.Name()) 878 } 879 return nil 880 }