github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 9 "github.com/juju/errors" 10 jujutxn "github.com/juju/txn" 11 "github.com/juju/version" 12 "gopkg.in/juju/names.v2" 13 "gopkg.in/mgo.v2" 14 "gopkg.in/mgo.v2/bson" 15 "gopkg.in/mgo.v2/txn" 16 17 jujucloud "github.com/juju/juju/cloud" 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/mongo" 21 "github.com/juju/juju/permission" 22 "github.com/juju/juju/status" 23 "github.com/juju/juju/storage" 24 ) 25 26 // modelGlobalKey is the key for the model, its 27 // settings and constraints. 28 const modelGlobalKey = "e" 29 30 // modelKey will create the kei for a given model using the modelGlobalKey. 31 func modelKey(modelUUID string) string { 32 return fmt.Sprintf("%s#%s", modelGlobalKey, modelUUID) 33 } 34 35 // MigrationMode specifies where the Model is with respect to migration. 36 type MigrationMode string 37 38 const ( 39 // MigrationModeNone is the default mode for a model and reflects 40 // that it isn't involved with a model migration. 41 MigrationModeNone MigrationMode = "" 42 43 // MigrationModeExporting reflects a model that is in the process of being 44 // exported from one controller to another. 45 MigrationModeExporting MigrationMode = "exporting" 46 47 // MigrationModeImporting reflects a model that is being imported into a 48 // controller, but is not yet fully active. 49 MigrationModeImporting MigrationMode = "importing" 50 ) 51 52 // Model represents the state of a model. 53 type Model struct { 54 // st is not necessarily the state of this model. Though it is 55 // usually safe to assume that it is. The only times it isn't is when we 56 // get models other than the current one - which is mostly in 57 // controller api endpoints. 58 st *State 59 doc modelDoc 60 } 61 62 // modelDoc represents the internal state of the model in MongoDB. 63 type modelDoc struct { 64 UUID string `bson:"_id"` 65 Name string 66 Life Life 67 Owner string `bson:"owner"` 68 ControllerUUID string `bson:"controller-uuid"` 69 MigrationMode MigrationMode `bson:"migration-mode"` 70 71 // Cloud is the name of the cloud to which the model is deployed. 72 Cloud string `bson:"cloud"` 73 74 // CloudRegion is the name of the cloud region to which the model is 75 // deployed. This will be empty for clouds that do not support regions. 76 CloudRegion string `bson:"cloud-region,omitempty"` 77 78 // CloudCredential is the ID of the cloud credential that is used 79 // for managing cloud resources for this model. This will be empty 80 // for clouds that do not require credentials. 81 CloudCredential string `bson:"cloud-credential,omitempty"` 82 83 // LatestAvailableTools is a string representing the newest version 84 // found while checking streams for new versions. 85 LatestAvailableTools string `bson:"available-tools,omitempty"` 86 } 87 88 // modelEntityRefsDoc records references to the top-level entities 89 // in the model. 90 type modelEntityRefsDoc struct { 91 UUID string `bson:"_id"` 92 93 // Machines contains the names of the top-level machines in the model. 94 Machines []string `bson:"machines"` 95 96 // Applicatons contains the names of the applications in the model. 97 Applications []string `bson:"applications"` 98 } 99 100 // ControllerModel returns the model that was bootstrapped. 101 // This is the only model that can have controller machines. 102 // The owner of this model is also considered "special", in that 103 // they are the only user that is able to create other users (until we 104 // have more fine grained permissions), and they cannot be disabled. 105 func (st *State) ControllerModel() (*Model, error) { 106 ssinfo, err := st.ControllerInfo() 107 if err != nil { 108 return nil, errors.Annotate(err, "could not get controller info") 109 } 110 111 models, closer := st.getCollection(modelsC) 112 defer closer() 113 114 env := &Model{st: st} 115 uuid := ssinfo.ModelTag.Id() 116 if err := env.refresh(models.FindId(uuid)); err != nil { 117 return nil, errors.Trace(err) 118 } 119 return env, nil 120 } 121 122 // Model returns the model entity. 123 func (st *State) Model() (*Model, error) { 124 return st.GetModel(st.modelTag) 125 } 126 127 // GetModel looks for the model identified by the uuid passed in. 128 func (st *State) GetModel(tag names.ModelTag) (*Model, error) { 129 models, closer := st.getCollection(modelsC) 130 defer closer() 131 132 model := &Model{st: st} 133 if err := model.refresh(models.FindId(tag.Id())); err != nil { 134 return nil, errors.Trace(err) 135 } 136 return model, nil 137 } 138 139 // AllModels returns all the models in the system. 140 func (st *State) AllModels() ([]*Model, error) { 141 models, closer := st.getCollection(modelsC) 142 defer closer() 143 144 var modelDocs []modelDoc 145 err := models.Find(nil).Sort("name", "owner").All(&modelDocs) 146 if err != nil { 147 return nil, err 148 } 149 150 result := make([]*Model, len(modelDocs)) 151 for i, doc := range modelDocs { 152 result[i] = &Model{st: st, doc: doc} 153 } 154 155 return result, nil 156 } 157 158 // ModelArgs is a params struct for creating a new model. 159 type ModelArgs struct { 160 // CloudName is the name of the cloud to which the model is deployed. 161 CloudName string 162 163 // CloudRegion is the name of the cloud region to which the model is 164 // deployed. This will be empty for clouds that do not support regions. 165 CloudRegion string 166 167 // CloudCredential is the tag of the cloud credential that will be 168 // used for managing cloud resources for this model. This will be 169 // empty for clouds that do not require credentials. 170 CloudCredential names.CloudCredentialTag 171 172 // Config is the model config. 173 Config *config.Config 174 175 // Constraints contains the initial constraints for the model. 176 Constraints constraints.Value 177 178 // StorageProviderRegistry is used to determine and store the 179 // details of the default storage pools. 180 StorageProviderRegistry storage.ProviderRegistry 181 182 // Owner is the user that owns the model. 183 Owner names.UserTag 184 185 // MigrationMode is the initial migration mode of the model. 186 MigrationMode MigrationMode 187 } 188 189 // Validate validates the ModelArgs. 190 func (m ModelArgs) Validate() error { 191 if m.Config == nil { 192 return errors.NotValidf("nil Config") 193 } 194 if !names.IsValidCloud(m.CloudName) { 195 return errors.NotValidf("Cloud Name %q", m.CloudName) 196 } 197 if m.Owner == (names.UserTag{}) { 198 return errors.NotValidf("empty Owner") 199 } 200 if m.StorageProviderRegistry == nil { 201 return errors.NotValidf("nil StorageProviderRegistry") 202 } 203 switch m.MigrationMode { 204 case MigrationModeNone, MigrationModeImporting: 205 default: 206 return errors.NotValidf("initial migration mode %q", m.MigrationMode) 207 } 208 return nil 209 } 210 211 // NewModel creates a new model with its own UUID and 212 // prepares it for use. Model and State instances for the new 213 // model are returned. 214 // 215 // The controller model's UUID is attached to the new 216 // model's document. Having the server UUIDs stored with each 217 // model document means that we have a way to represent external 218 // models, perhaps for future use around cross model 219 // relations. 220 func (st *State) NewModel(args ModelArgs) (_ *Model, _ *State, err error) { 221 if err := args.Validate(); err != nil { 222 return nil, nil, errors.Trace(err) 223 } 224 // For now, the model cloud must be the same as the controller cloud. 225 controllerInfo, err := st.ControllerInfo() 226 if err != nil { 227 return nil, nil, errors.Trace(err) 228 } 229 if controllerInfo.CloudName != args.CloudName { 230 return nil, nil, errors.NewNotValid( 231 nil, fmt.Sprintf("controller cloud %s does not match model cloud %s", controllerInfo.CloudName, args.CloudName)) 232 } 233 234 // Ensure that the cloud region is valid, or if one is not specified, 235 // that the cloud does not support regions. 236 controllerCloud, err := st.Cloud(args.CloudName) 237 if err != nil { 238 return nil, nil, errors.Trace(err) 239 } 240 assertCloudRegionOp, err := validateCloudRegion(controllerCloud, args.CloudName, args.CloudRegion) 241 if err != nil { 242 return nil, nil, errors.Trace(err) 243 } 244 245 // Ensure that the cloud credential is valid, or if one is not 246 // specified, that the cloud supports the "empty" authentication 247 // type. 248 owner := args.Owner 249 cloudCredentials, err := st.CloudCredentials(owner, args.CloudName) 250 if err != nil { 251 return nil, nil, errors.Trace(err) 252 } 253 assertCloudCredentialOp, err := validateCloudCredential( 254 controllerCloud, args.CloudName, cloudCredentials, args.CloudCredential, 255 ) 256 if err != nil { 257 return nil, nil, errors.Trace(err) 258 } 259 260 if owner.IsLocal() { 261 if _, err := st.User(owner); err != nil { 262 return nil, nil, errors.Annotate(err, "cannot create model") 263 } 264 } 265 266 uuid := args.Config.UUID() 267 session := st.session.Copy() 268 newSt, err := newState(names.NewModelTag(uuid), controllerInfo.ModelTag, session, st.mongoInfo, st.newPolicy, st.clock) 269 if err != nil { 270 return nil, nil, errors.Annotate(err, "could not create state for new model") 271 } 272 defer func() { 273 if err != nil { 274 newSt.Close() 275 } 276 }() 277 newSt.controllerModelTag = st.controllerModelTag 278 279 modelOps, err := newSt.modelSetupOps(st.controllerTag.Id(), args, nil) 280 if err != nil { 281 return nil, nil, errors.Annotate(err, "failed to create new model") 282 } 283 284 prereqOps := []txn.Op{ 285 assertCloudRegionOp, 286 assertCloudCredentialOp, 287 } 288 ops := append(prereqOps, modelOps...) 289 err = newSt.runTransaction(ops) 290 if err == txn.ErrAborted { 291 292 // We have a unique key restriction on the "owner" and "name" fields, 293 // which will cause the insert to fail if there is another record with 294 // the same "owner" and "name" in the collection. If the txn is 295 // aborted, check if it is due to the unique key restriction. 296 name := args.Config.Name() 297 models, closer := st.getCollection(modelsC) 298 defer closer() 299 envCount, countErr := models.Find(bson.D{ 300 {"owner", owner.Canonical()}, 301 {"name", name}}, 302 ).Count() 303 if countErr != nil { 304 err = errors.Trace(countErr) 305 } else if envCount > 0 { 306 err = errors.AlreadyExistsf("model %q for %s", name, owner.Canonical()) 307 } else { 308 err = errors.New("model already exists") 309 } 310 } 311 if err != nil { 312 return nil, nil, errors.Trace(err) 313 } 314 315 err = newSt.start(st.controllerTag) 316 if err != nil { 317 return nil, nil, errors.Annotate(err, "could not start state for new model") 318 } 319 320 newModel, err := newSt.Model() 321 if err != nil { 322 return nil, nil, errors.Trace(err) 323 } 324 _, err = newSt.SetUserAccess(newModel.Owner(), newModel.ModelTag(), permission.AdminAccess) 325 if err != nil { 326 return nil, nil, errors.Annotate(err, "granting admin permission to the owner") 327 } 328 329 return newModel, newSt, nil 330 } 331 332 // validateCloudRegion validates the given region name against the 333 // provided Cloud definition, and returns a txn.Op to include in a 334 // transaction to assert the same. 335 func validateCloudRegion(cloud jujucloud.Cloud, cloudName, regionName string) (txn.Op, error) { 336 // Ensure that the cloud region is valid, or if one is not specified, 337 // that the cloud does not support regions. 338 assertCloudRegionOp := txn.Op{ 339 C: cloudsC, 340 Id: cloudName, 341 } 342 if regionName != "" { 343 region, err := jujucloud.RegionByName(cloud.Regions, regionName) 344 if err != nil { 345 return txn.Op{}, errors.Trace(err) 346 } 347 assertCloudRegionOp.Assert = bson.D{ 348 {"regions." + region.Name, bson.D{{"$exists", true}}}, 349 } 350 } else { 351 if len(cloud.Regions) > 0 { 352 return txn.Op{}, errors.NotValidf("missing CloudRegion") 353 } 354 assertCloudRegionOp.Assert = bson.D{ 355 {"regions", bson.D{{"$exists", false}}}, 356 } 357 } 358 return assertCloudRegionOp, nil 359 } 360 361 // validateCloudCredential validates the given cloud credential 362 // name against the provided cloud definition and credentials, 363 // and returns a txn.Op to include in a transaction to assert the 364 // same. A user is supplied, for which access to the credential 365 // will be asserted. 366 func validateCloudCredential( 367 cloud jujucloud.Cloud, 368 cloudName string, 369 cloudCredentials map[string]jujucloud.Credential, 370 cloudCredential names.CloudCredentialTag, 371 ) (txn.Op, error) { 372 if cloudCredential != (names.CloudCredentialTag{}) { 373 if cloudCredential.Cloud().Id() != cloudName { 374 return txn.Op{}, errors.NotValidf("credential %q", cloudCredential.Id()) 375 } 376 var found bool 377 for tag := range cloudCredentials { 378 if tag == cloudCredential.Canonical() { 379 found = true 380 break 381 } 382 } 383 if !found { 384 return txn.Op{}, errors.NotFoundf("credential %q", cloudCredential.Id()) 385 } 386 // NOTE(axw) if we add ACLs for credentials, 387 // we'll need to check access here. The map 388 // we check above contains only the credentials 389 // that the model owner has access to. 390 return txn.Op{ 391 C: cloudCredentialsC, 392 Id: cloudCredentialDocID(cloudCredential), 393 Assert: txn.DocExists, 394 }, nil 395 } 396 var hasEmptyAuth bool 397 for _, authType := range cloud.AuthTypes { 398 if authType != jujucloud.EmptyAuthType { 399 continue 400 } 401 hasEmptyAuth = true 402 break 403 } 404 if !hasEmptyAuth { 405 return txn.Op{}, errors.NotValidf("missing CloudCredential") 406 } 407 return txn.Op{ 408 C: cloudsC, 409 Id: cloudName, 410 Assert: bson.D{{"auth-types", string(jujucloud.EmptyAuthType)}}, 411 }, nil 412 } 413 414 // Tag returns a name identifying the model. 415 // The returned name will be different from other Tag values returned 416 // by any other entities from the same state. 417 func (m *Model) Tag() names.Tag { 418 return m.ModelTag() 419 } 420 421 // ModelTag is the concrete model tag for this model. 422 func (m *Model) ModelTag() names.ModelTag { 423 return names.NewModelTag(m.doc.UUID) 424 } 425 426 // ControllerTag is the tag for the controller that the model is 427 // running within. 428 func (m *Model) ControllerTag() names.ControllerTag { 429 return names.NewControllerTag(m.doc.ControllerUUID) 430 } 431 432 // UUID returns the universally unique identifier of the model. 433 func (m *Model) UUID() string { 434 return m.doc.UUID 435 } 436 437 // ControllerUUID returns the universally unique identifier of the controller 438 // in which the model is running. 439 func (m *Model) ControllerUUID() string { 440 return m.doc.ControllerUUID 441 } 442 443 // Name returns the human friendly name of the model. 444 func (m *Model) Name() string { 445 return m.doc.Name 446 } 447 448 // Cloud returns the name of the cloud to which the model is deployed. 449 func (m *Model) Cloud() string { 450 return m.doc.Cloud 451 } 452 453 // CloudRegion returns the name of the cloud region to which the model is deployed. 454 func (m *Model) CloudRegion() string { 455 return m.doc.CloudRegion 456 } 457 458 // CloudCredential returns the tag of the cloud credential used for managing the 459 // model's cloud resources, and a boolean indicating whether a credential is set. 460 func (m *Model) CloudCredential() (names.CloudCredentialTag, bool) { 461 if names.IsValidCloudCredential(m.doc.CloudCredential) { 462 return names.NewCloudCredentialTag(m.doc.CloudCredential), true 463 } 464 return names.CloudCredentialTag{}, false 465 } 466 467 // MigrationMode returns whether the model is active or being migrated. 468 func (m *Model) MigrationMode() MigrationMode { 469 return m.doc.MigrationMode 470 } 471 472 // SetMigrationMode updates the migration mode of the model. 473 func (m *Model) SetMigrationMode(mode MigrationMode) error { 474 st, closeState, err := m.getState() 475 if err != nil { 476 return errors.Trace(err) 477 } 478 defer closeState() 479 480 ops := []txn.Op{{ 481 C: modelsC, 482 Id: m.doc.UUID, 483 Assert: txn.DocExists, 484 Update: bson.D{{"$set", bson.D{{"migration-mode", mode}}}}, 485 }} 486 if err := st.runTransaction(ops); err != nil { 487 return errors.Trace(err) 488 } 489 return m.Refresh() 490 } 491 492 // Life returns whether the model is Alive, Dying or Dead. 493 func (m *Model) Life() Life { 494 return m.doc.Life 495 } 496 497 // Owner returns tag representing the owner of the model. 498 // The owner is the user that created the model. 499 func (m *Model) Owner() names.UserTag { 500 return names.NewUserTag(m.doc.Owner) 501 } 502 503 // Status returns the status of the model. 504 func (m *Model) Status() (status.StatusInfo, error) { 505 st, closeState, err := m.getState() 506 if err != nil { 507 return status.StatusInfo{}, errors.Trace(err) 508 } 509 defer closeState() 510 status, err := getStatus(st, m.globalKey(), "model") 511 if err != nil { 512 return status, err 513 } 514 return status, nil 515 } 516 517 // SetStatus sets the status of the model. 518 func (m *Model) SetStatus(sInfo status.StatusInfo) error { 519 st, closeState, err := m.getState() 520 if err != nil { 521 return errors.Trace(err) 522 } 523 defer closeState() 524 if !status.ValidModelStatus(sInfo.Status) { 525 return errors.Errorf("cannot set invalid status %q", sInfo.Status) 526 } 527 return setStatus(st, setStatusParams{ 528 badge: "model", 529 globalKey: m.globalKey(), 530 status: sInfo.Status, 531 message: sInfo.Message, 532 rawData: sInfo.Data, 533 updated: sInfo.Since, 534 }) 535 } 536 537 // Config returns the config for the model. 538 func (m *Model) Config() (*config.Config, error) { 539 st, closeState, err := m.getState() 540 if err != nil { 541 return nil, errors.Trace(err) 542 } 543 defer closeState() 544 return st.ModelConfig() 545 } 546 547 // ConfigValues returns the config values for the model. 548 func (m *Model) ConfigValues() (config.ConfigValues, error) { 549 st, closeState, err := m.getState() 550 if err != nil { 551 return nil, errors.Trace(err) 552 } 553 defer closeState() 554 return st.ModelConfigValues() 555 } 556 557 // UpdateLatestToolsVersion looks up for the latest available version of 558 // juju tools and updates environementDoc with it. 559 func (m *Model) UpdateLatestToolsVersion(ver version.Number) error { 560 v := ver.String() 561 // TODO(perrito666): I need to assert here that there isn't a newer 562 // version in place. 563 ops := []txn.Op{{ 564 C: modelsC, 565 Id: m.doc.UUID, 566 Update: bson.D{{"$set", bson.D{{"available-tools", v}}}}, 567 }} 568 err := m.st.runTransaction(ops) 569 if err != nil { 570 return errors.Trace(err) 571 } 572 return m.Refresh() 573 } 574 575 // LatestToolsVersion returns the newest version found in the last 576 // check in the streams. 577 // Bear in mind that the check was performed filtering only 578 // new patches for the current major.minor. (major.minor.patch) 579 func (m *Model) LatestToolsVersion() version.Number { 580 ver := m.doc.LatestAvailableTools 581 if ver == "" { 582 return version.Zero 583 } 584 v, err := version.Parse(ver) 585 if err != nil { 586 // This is being stored from a valid version but 587 // in case this data would beacame corrupt It is not 588 // worth to fail because of it. 589 return version.Zero 590 } 591 return v 592 } 593 594 // globalKey returns the global database key for the model. 595 func (m *Model) globalKey() string { 596 return modelGlobalKey 597 } 598 599 func (m *Model) Refresh() error { 600 models, closer := m.st.getCollection(modelsC) 601 defer closer() 602 return m.refresh(models.FindId(m.UUID())) 603 } 604 605 func (m *Model) refresh(query mongo.Query) error { 606 err := query.One(&m.doc) 607 if err == mgo.ErrNotFound { 608 return errors.NotFoundf("model") 609 } 610 return err 611 } 612 613 // Users returns a slice of all users for this model. 614 func (m *Model) Users() ([]permission.UserAccess, error) { 615 if m.st.ModelUUID() != m.UUID() { 616 return nil, errors.New("cannot lookup model users outside the current model") 617 } 618 coll, closer := m.st.getCollection(modelUsersC) 619 defer closer() 620 621 var userDocs []userAccessDoc 622 err := coll.Find(nil).All(&userDocs) 623 if err != nil { 624 return nil, errors.Trace(err) 625 } 626 627 var modelUsers []permission.UserAccess 628 for _, doc := range userDocs { 629 // check if the User belonging to this model user has 630 // been deleted, in this case we should not return it. 631 userTag := names.NewUserTag(doc.UserName) 632 if userTag.IsLocal() { 633 _, err := m.st.User(userTag) 634 if errors.IsUserNotFound(err) { 635 continue 636 } 637 if err != nil { 638 return nil, errors.Trace(err) 639 } 640 } 641 mu, err := NewModelUserAccess(m.st, doc) 642 if err != nil { 643 return nil, errors.Trace(err) 644 } 645 modelUsers = append(modelUsers, mu) 646 } 647 648 return modelUsers, nil 649 } 650 651 func (m *Model) isControllerModel() bool { 652 return m.st.controllerModelTag.Id() == m.doc.UUID 653 } 654 655 // Destroy sets the models's lifecycle to Dying, preventing 656 // addition of services or machines to state. If called on 657 // an empty hosted model, the lifecycle will be advanced 658 // straight to Dead. 659 // 660 // If called on a controller model, and that controller is 661 // hosting any non-Dead models, this method will return an 662 // error satisfying IsHasHostedsError. 663 func (m *Model) Destroy() error { 664 ensureNoHostedModels := false 665 if m.isControllerModel() { 666 ensureNoHostedModels = true 667 } 668 return m.destroy(ensureNoHostedModels) 669 } 670 671 // DestroyIncludingHosted sets the model's lifecycle to Dying, preventing 672 // addition of services or machines to state. If this model is a controller 673 // hosting other models, they will also be destroyed. 674 func (m *Model) DestroyIncludingHosted() error { 675 ensureNoHostedModels := false 676 return m.destroy(ensureNoHostedModels) 677 } 678 679 func (m *Model) destroy(ensureNoHostedModels bool) (err error) { 680 defer errors.DeferredAnnotatef(&err, "failed to destroy model") 681 682 st, closeState, err := m.getState() 683 if err != nil { 684 return errors.Trace(err) 685 } 686 defer closeState() 687 688 buildTxn := func(attempt int) ([]txn.Op, error) { 689 // On the first attempt, we assume memory state is recent 690 // enough to try using... 691 if attempt != 0 { 692 // ...but on subsequent attempts, we read fresh environ 693 // state from the DB. Note that we do *not* refresh `e` 694 // itself, as detailed in doc/hacking-state.txt 695 if m, err = st.Model(); err != nil { 696 return nil, errors.Trace(err) 697 } 698 } 699 700 ops, err := m.destroyOps(ensureNoHostedModels, false) 701 if err == errModelNotAlive { 702 return nil, jujutxn.ErrNoOperations 703 } else if err != nil { 704 return nil, errors.Trace(err) 705 } 706 707 return ops, nil 708 } 709 710 return st.run(buildTxn) 711 } 712 713 // errModelNotAlive is a signal emitted from destroyOps to indicate 714 // that model destruction is already underway. 715 var errModelNotAlive = errors.New("model is no longer alive") 716 717 type hasHostedModelsError int 718 719 func (e hasHostedModelsError) Error() string { 720 return fmt.Sprintf("hosting %d other models", e) 721 } 722 723 func IsHasHostedModelsError(err error) bool { 724 _, ok := errors.Cause(err).(hasHostedModelsError) 725 return ok 726 } 727 728 // destroyOps returns the txn operations necessary to begin model 729 // destruction, or an error indicating why it can't. 730 // 731 // If ensureNoHostedModels is true, then destroyOps will 732 // fail if there are any non-Dead hosted models 733 func (m *Model) destroyOps(ensureNoHostedModels, ensureEmpty bool) ([]txn.Op, error) { 734 if m.Life() != Alive { 735 return nil, errModelNotAlive 736 } 737 738 // Ensure we're using the model's state. 739 st, closeState, err := m.getState() 740 if err != nil { 741 return nil, errors.Trace(err) 742 } 743 defer closeState() 744 745 // Check if the model is empty. If it is, we can advance the model's 746 // lifecycle state directly to Dead. 747 checkEmptyErr := m.checkEmpty() 748 isEmpty := checkEmptyErr == nil 749 if ensureEmpty && !isEmpty { 750 return nil, errors.Trace(checkEmptyErr) 751 } 752 753 modelUUID := m.UUID() 754 nextLife := Dying 755 var prereqOps []txn.Op 756 if isEmpty { 757 prereqOps = []txn.Op{{ 758 C: modelEntityRefsC, 759 Id: modelUUID, 760 Assert: bson.D{ 761 {"machines", bson.D{{"$size", 0}}}, 762 {"applications", bson.D{{"$size", 0}}}, 763 }, 764 }} 765 if !m.isControllerModel() { 766 // The model is empty, and is not the controller 767 // model, so we can move it straight to Dead. 768 nextLife = Dead 769 } 770 } 771 772 if ensureNoHostedModels { 773 // Check for any Dying or alive but non-empty models. If there 774 // are any, we return an error indicating that there are hosted 775 // models. 776 models, err := st.AllModels() 777 if err != nil { 778 return nil, errors.Trace(err) 779 } 780 var aliveEmpty, aliveNonEmpty, dying, dead int 781 for _, model := range models { 782 if model.UUID() == m.UUID() { 783 // Ignore the controller model. 784 continue 785 } 786 if model.Life() == Dead { 787 // Dead hosted models don't affect 788 // whether the controller can be 789 // destroyed or not, but they are 790 // still counted in the hosted models. 791 dead++ 792 continue 793 } 794 // See if the model is empty, and if it is, 795 // get the ops required to destroy it. 796 ops, err := model.destroyOps(false, true) 797 switch err { 798 case errModelNotAlive: 799 dying++ 800 case nil: 801 prereqOps = append(prereqOps, ops...) 802 aliveEmpty++ 803 default: 804 aliveNonEmpty++ 805 } 806 } 807 if dying > 0 || aliveNonEmpty > 0 { 808 // There are Dying, or Alive but non-empty models. 809 // We cannot destroy the controller without first 810 // destroying the models and waiting for them to 811 // become Dead. 812 return nil, errors.Trace( 813 hasHostedModelsError(dying + aliveNonEmpty + aliveEmpty), 814 ) 815 } 816 // Ensure that the number of active models has not changed 817 // between the query and when the transaction is applied. 818 // 819 // Note that we assert that each empty model that we intend 820 // move to Dead is still Alive, so we're protected from an 821 // ABA style problem where an empty model is concurrently 822 // removed, and replaced with a non-empty model. 823 prereqOps = append(prereqOps, assertHostedModelsOp(aliveEmpty+dead)) 824 } 825 826 timeOfDying := st.NowToTheSecond() 827 modelUpdateValues := bson.D{ 828 {"life", nextLife}, 829 {"time-of-dying", timeOfDying}, 830 } 831 if nextLife == Dead { 832 modelUpdateValues = append(modelUpdateValues, bson.DocElem{ 833 "time-of-death", timeOfDying, 834 }) 835 } 836 837 ops := []txn.Op{{ 838 C: modelsC, 839 Id: modelUUID, 840 Assert: isAliveDoc, 841 Update: bson.D{{"$set", modelUpdateValues}}, 842 }} 843 844 // Because txn operations execute in order, and may encounter 845 // arbitrarily long delays, we need to make sure every op 846 // causes a state change that's still consistent; so we make 847 // sure the cleanup ops are the last thing that will execute. 848 if m.isControllerModel() { 849 cleanupOp := newCleanupOp(cleanupModelsForDyingController, modelUUID) 850 ops = append(ops, cleanupOp) 851 } 852 if !isEmpty { 853 // We only need to destroy machines and models if the model is 854 // non-empty. It wouldn't normally be harmful to enqueue the 855 // cleanups otherwise, except for when we're destroying an empty 856 // hosted model in the course of destroying the controller. In 857 // that case we'll get errors if we try to enqueue hosted-model 858 // cleanups, because the cleanups collection is non-global. 859 cleanupMachinesOp := newCleanupOp(cleanupMachinesForDyingModel, modelUUID) 860 cleanupServicesOp := newCleanupOp(cleanupServicesForDyingModel, modelUUID) 861 ops = append(ops, cleanupMachinesOp, cleanupServicesOp) 862 } 863 return append(prereqOps, ops...), nil 864 } 865 866 // checkEmpty checks that the machine is empty of any entities that may 867 // require external resource cleanup. If the model is not empty, then 868 // an error will be returned. 869 func (m *Model) checkEmpty() error { 870 st, closeState, err := m.getState() 871 if err != nil { 872 return errors.Trace(err) 873 } 874 defer closeState() 875 876 modelEntityRefs, closer := st.getCollection(modelEntityRefsC) 877 defer closer() 878 879 var doc modelEntityRefsDoc 880 if err := modelEntityRefs.FindId(m.UUID()).One(&doc); err != nil { 881 if err == mgo.ErrNotFound { 882 return errors.NotFoundf("entity references doc for model %s", m.UUID()) 883 } 884 return errors.Annotatef(err, "getting entity references for model %s", m.UUID()) 885 } 886 if n := len(doc.Machines); n > 0 { 887 return errors.Errorf("model not empty, found %d machine(s)", n) 888 } 889 if n := len(doc.Applications); n > 0 { 890 return errors.Errorf("model not empty, found %d applications(s)", n) 891 } 892 return nil 893 } 894 895 func addModelMachineRefOp(st *State, machineId string) txn.Op { 896 return txn.Op{ 897 C: modelEntityRefsC, 898 Id: st.ModelUUID(), 899 Assert: txn.DocExists, 900 Update: bson.D{{"$addToSet", bson.D{{"machines", machineId}}}}, 901 } 902 } 903 904 func removeModelMachineRefOp(st *State, machineId string) txn.Op { 905 return txn.Op{ 906 C: modelEntityRefsC, 907 Id: st.ModelUUID(), 908 Update: bson.D{{"$pull", bson.D{{"machines", machineId}}}}, 909 } 910 } 911 912 func addModelServiceRefOp(st *State, applicationname string) txn.Op { 913 return txn.Op{ 914 C: modelEntityRefsC, 915 Id: st.ModelUUID(), 916 Assert: txn.DocExists, 917 Update: bson.D{{"$addToSet", bson.D{{"applications", applicationname}}}}, 918 } 919 } 920 921 func removeModelServiceRefOp(st *State, applicationname string) txn.Op { 922 return txn.Op{ 923 C: modelEntityRefsC, 924 Id: st.ModelUUID(), 925 Update: bson.D{{"$pull", bson.D{{"applications", applicationname}}}}, 926 } 927 } 928 929 // createModelOp returns the operation needed to create 930 // an model document with the given name and UUID. 931 func createModelOp( 932 owner names.UserTag, 933 name, uuid, controllerUUID, cloudName, cloudRegion string, 934 cloudCredential names.CloudCredentialTag, 935 migrationMode MigrationMode, 936 ) txn.Op { 937 doc := &modelDoc{ 938 UUID: uuid, 939 Name: name, 940 Life: Alive, 941 Owner: owner.Canonical(), 942 ControllerUUID: controllerUUID, 943 MigrationMode: migrationMode, 944 Cloud: cloudName, 945 CloudRegion: cloudRegion, 946 CloudCredential: cloudCredential.Canonical(), 947 } 948 return txn.Op{ 949 C: modelsC, 950 Id: uuid, 951 Assert: txn.DocMissing, 952 Insert: doc, 953 } 954 } 955 956 func createModelEntityRefsOp(uuid string) txn.Op { 957 return txn.Op{ 958 C: modelEntityRefsC, 959 Id: uuid, 960 Assert: txn.DocMissing, 961 Insert: &modelEntityRefsDoc{UUID: uuid}, 962 } 963 } 964 965 const hostedModelCountKey = "hostedModelCount" 966 967 type hostedModelCountDoc struct { 968 // RefCount is the number of models in the Juju system. 969 // We do not count the system model. 970 RefCount int `bson:"refcount"` 971 } 972 973 func assertHostedModelsOp(n int) txn.Op { 974 return txn.Op{ 975 C: controllersC, 976 Id: hostedModelCountKey, 977 Assert: bson.D{{"refcount", n}}, 978 } 979 } 980 981 func incHostedModelCountOp() txn.Op { 982 return HostedModelCountOp(1) 983 } 984 985 func decHostedModelCountOp() txn.Op { 986 return HostedModelCountOp(-1) 987 } 988 989 func HostedModelCountOp(amount int) txn.Op { 990 return txn.Op{ 991 C: controllersC, 992 Id: hostedModelCountKey, 993 Assert: txn.DocExists, 994 Update: bson.M{ 995 "$inc": bson.M{"refcount": amount}, 996 }, 997 } 998 } 999 1000 func hostedModelCount(st *State) (int, error) { 1001 var doc hostedModelCountDoc 1002 controllers, closer := st.getCollection(controllersC) 1003 defer closer() 1004 1005 if err := controllers.Find(bson.D{{"_id", hostedModelCountKey}}).One(&doc); err != nil { 1006 return 0, errors.Trace(err) 1007 } 1008 return doc.RefCount, nil 1009 } 1010 1011 // createUniqueOwnerModelNameOp returns the operation needed to create 1012 // an usermodelnameC document with the given owner and model name. 1013 func createUniqueOwnerModelNameOp(owner names.UserTag, envName string) txn.Op { 1014 return txn.Op{ 1015 C: usermodelnameC, 1016 Id: userModelNameIndex(owner.Canonical(), envName), 1017 Assert: txn.DocMissing, 1018 Insert: bson.M{}, 1019 } 1020 } 1021 1022 // assertAliveOp returns a txn.Op that asserts the model is alive. 1023 func (m *Model) assertActiveOp() txn.Op { 1024 return assertModelActiveOp(m.UUID()) 1025 } 1026 1027 // getState returns the State for the model, and a function to close 1028 // it when done. If m.st is the model-specific state, then it will 1029 // be returned and the close function will be a no-op. 1030 // 1031 // TODO(axw) 2016-04-14 #1570269 1032 // find a way to guarantee that every Model is associated with the 1033 // appropriate State. The current work-around is too easy to get wrong. 1034 func (m *Model) getState() (*State, func(), error) { 1035 if m.st.modelTag == m.ModelTag() { 1036 return m.st, func() {}, nil 1037 } 1038 st, err := m.st.ForModel(m.ModelTag()) 1039 if err != nil { 1040 return nil, nil, errors.Trace(err) 1041 } 1042 uuid := st.ModelUUID() 1043 return st, func() { 1044 if err := st.Close(); err != nil { 1045 logger.Errorf("closing temporary state for model %s", uuid) 1046 } 1047 }, nil 1048 } 1049 1050 // assertModelActiveOp returns a txn.Op that asserts the given 1051 // model UUID refers to an Alive model. 1052 func assertModelActiveOp(modelUUID string) txn.Op { 1053 return txn.Op{ 1054 C: modelsC, 1055 Id: modelUUID, 1056 Assert: append(isAliveDoc, bson.DocElem{"migration-mode", MigrationModeNone}), 1057 } 1058 } 1059 1060 func checkModelActive(st *State) error { 1061 model, err := st.Model() 1062 if (err == nil && model.Life() != Alive) || errors.IsNotFound(err) { 1063 return errors.Errorf("model %q is no longer alive", model.Name()) 1064 } else if err != nil { 1065 return errors.Annotate(err, "unable to read model") 1066 } else if mode := model.MigrationMode(); mode != MigrationModeNone { 1067 return errors.Errorf("model %q is being migrated", model.Name()) 1068 } 1069 return nil 1070 }