github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/environ.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 "github.com/juju/errors" 8 "github.com/juju/names" 9 "labix.org/v2/mgo" 10 "labix.org/v2/mgo/bson" 11 "labix.org/v2/mgo/txn" 12 ) 13 14 // environGlobalKey is the key for the environment, its 15 // settings and constraints. 16 const environGlobalKey = "e" 17 18 // Environment represents the state of an environment. 19 type Environment struct { 20 st *State 21 doc environmentDoc 22 annotator 23 } 24 25 // environmentDoc represents the internal state of the environment in MongoDB. 26 type environmentDoc struct { 27 UUID string `bson:"_id"` 28 Name string 29 Life Life 30 } 31 32 // Environment returns the environment entity. 33 func (st *State) Environment() (*Environment, error) { 34 env := &Environment{st: st} 35 if err := env.refresh(st.environments.Find(nil)); err != nil { 36 return nil, err 37 } 38 env.annotator = annotator{ 39 globalKey: env.globalKey(), 40 tag: env.Tag(), 41 st: st, 42 } 43 return env, nil 44 } 45 46 // Tag returns a name identifying the environment. 47 // The returned name will be different from other Tag values returned 48 // by any other entities from the same state. 49 func (e *Environment) Tag() string { 50 return names.EnvironTag(e.doc.UUID) 51 } 52 53 // UUID returns the universally unique identifier of the environment. 54 func (e *Environment) UUID() string { 55 return e.doc.UUID 56 } 57 58 // Name returns the human friendly name of the environment. 59 func (e *Environment) Name() string { 60 return e.doc.Name 61 } 62 63 // Life returns whether the environment is Alive, Dying or Dead. 64 func (e *Environment) Life() Life { 65 return e.doc.Life 66 } 67 68 // globalKey returns the global database key for the environment. 69 func (e *Environment) globalKey() string { 70 return environGlobalKey 71 } 72 73 func (e *Environment) Refresh() error { 74 return e.refresh(e.st.environments.FindId(e.UUID())) 75 } 76 77 func (e *Environment) refresh(query *mgo.Query) error { 78 err := query.One(&e.doc) 79 if err == mgo.ErrNotFound { 80 return errors.NotFoundf("environment") 81 } 82 return err 83 } 84 85 // Destroy sets the environment's lifecycle to Dying, preventing 86 // addition of services or machines to state. 87 func (e *Environment) Destroy() error { 88 if e.Life() != Alive { 89 return nil 90 } 91 // TODO(axw) 2013-12-11 #1218688 92 // Resolve the race between checking for manual machines and 93 // destroying the environment. We can set Environment to Dying 94 // to resolve the race, but that puts the environment into an 95 // unusable state. 96 // 97 // We add a cleanup for services, but not for machines; 98 // machines are destroyed via the provider interface. The 99 // exception to this rule is manual machines; the API prevents 100 // destroy-environment from succeeding if any non-manager 101 // manual machines exist. 102 ops := []txn.Op{{ 103 C: e.st.environments.Name, 104 Id: e.doc.UUID, 105 Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, 106 Assert: isEnvAliveDoc, 107 }, e.st.newCleanupOp(cleanupServicesForDyingEnvironment, "")} 108 err := e.st.runTransaction(ops) 109 switch err { 110 case nil, txn.ErrAborted: 111 // If the transaction aborted, the environment is either 112 // Dying or Dead; neither case is an error. If it's Dead, 113 // reporting it as Dying is not incorrect; the user thought 114 // it was Alive, so we've progressed towards Dead. If the 115 // user then calls Refresh they'll get the true value. 116 e.doc.Life = Dying 117 } 118 return err 119 } 120 121 // createEnvironmentOp returns the operation needed to create 122 // an environment document with the given name and UUID. 123 func createEnvironmentOp(st *State, name, uuid string) txn.Op { 124 doc := &environmentDoc{uuid, name, Alive} 125 return txn.Op{ 126 C: st.environments.Name, 127 Id: uuid, 128 Assert: txn.DocMissing, 129 Insert: doc, 130 } 131 } 132 133 // assertAliveOp returns a txn.Op that asserts the environment is alive. 134 func (e *Environment) assertAliveOp() txn.Op { 135 return txn.Op{ 136 C: e.st.environments.Name, 137 Id: e.UUID(), 138 Assert: isEnvAliveDoc, 139 } 140 } 141 142 // isEnvAlive is an Environment-specific versio nof isAliveDoc. 143 // 144 // Environment documents from versions of Juju prior to 1.17 145 // do not have the life field; if it does not exist, it should 146 // be considered to have the value Alive. 147 var isEnvAliveDoc = bson.D{ 148 {"life", bson.D{{"$in", []interface{}{Alive, nil}}}}, 149 }