github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 "gopkg.in/mgo.v2" 10 "gopkg.in/mgo.v2/bson" 11 "gopkg.in/mgo.v2/txn" 12 13 "github.com/juju/juju/environs/config" 14 ) 15 16 // environGlobalKey is the key for the environment, its 17 // settings and constraints. 18 const environGlobalKey = "e" 19 20 // Environment represents the state of an environment. 21 type Environment struct { 22 st *State 23 doc environmentDoc 24 } 25 26 // environmentDoc represents the internal state of the environment in MongoDB. 27 type environmentDoc struct { 28 UUID string `bson:"_id"` 29 Name string 30 Life Life 31 Owner string `bson:"owner"` 32 ServerUUID string `bson:"server-uuid"` 33 } 34 35 // StateServerEnvironment returns the environment that was bootstrapped. 36 // This is the only environment that can have state server machines. 37 // The owner of this environment is also considered "special", in that 38 // they are the only user that is able to create other users (until we 39 // have more fine grained permissions), and they cannot be disabled. 40 func (st *State) StateServerEnvironment() (*Environment, error) { 41 ssinfo, err := st.StateServerInfo() 42 if err != nil { 43 return nil, errors.Annotate(err, "could not get state server info") 44 } 45 46 environments, closer := st.getCollection(environmentsC) 47 defer closer() 48 49 env := &Environment{st: st} 50 uuid := ssinfo.EnvironmentTag.Id() 51 if err := env.refresh(environments.FindId(uuid)); err != nil { 52 return nil, errors.Trace(err) 53 } 54 return env, nil 55 } 56 57 // Environment returns the environment entity. 58 func (st *State) Environment() (*Environment, error) { 59 environments, closer := st.getCollection(environmentsC) 60 defer closer() 61 62 env := &Environment{st: st} 63 uuid := st.environTag.Id() 64 if err := env.refresh(environments.FindId(uuid)); err != nil { 65 return nil, errors.Trace(err) 66 } 67 return env, nil 68 } 69 70 // GetEnvironment looks for the environment identified by the uuid passed in. 71 func (st *State) GetEnvironment(tag names.EnvironTag) (*Environment, error) { 72 environments, closer := st.getCollection(environmentsC) 73 defer closer() 74 75 env := &Environment{st: st} 76 if err := env.refresh(environments.FindId(tag.Id())); err != nil { 77 return nil, errors.Trace(err) 78 } 79 return env, nil 80 } 81 82 // NewEnvironment creates a new environment with its own UUID and 83 // prepares it for use. Environment and State instances for the new 84 // environment are returned. 85 // 86 // The state server environment's UUID is attached to the new 87 // environment's document. Having the server UUIDs stored with each 88 // environment document means that we have a way to represent external 89 // environments, perhaps for future use around cross environment 90 // relations. 91 func (st *State) NewEnvironment(cfg *config.Config, owner names.UserTag) (_ *Environment, _ *State, err error) { 92 if owner.IsLocal() { 93 if _, err := st.User(owner); err != nil { 94 return nil, nil, errors.Annotate(err, "cannot create environment") 95 } 96 } 97 98 ssEnv, err := st.StateServerEnvironment() 99 if err != nil { 100 return nil, nil, errors.Annotate(err, "could not load state server environment") 101 } 102 103 uuid, ok := cfg.UUID() 104 if !ok { 105 return nil, nil, errors.Errorf("environment uuid was not supplied") 106 } 107 newState, err := st.ForEnviron(names.NewEnvironTag(uuid)) 108 if err != nil { 109 return nil, nil, errors.Annotate(err, "could not create state for new environment") 110 } 111 defer func() { 112 if err != nil { 113 newState.Close() 114 } 115 }() 116 117 ops, err := newState.envSetupOps(cfg, uuid, ssEnv.UUID(), owner) 118 if err != nil { 119 return nil, nil, errors.Annotate(err, "failed to create new environment") 120 } 121 err = newState.runTransactionNoEnvAliveAssert(ops) 122 if err == txn.ErrAborted { 123 err = errors.New("environment already exists") 124 } 125 if err != nil { 126 return nil, nil, errors.Trace(err) 127 } 128 129 newEnv, err := newState.Environment() 130 if err != nil { 131 return nil, nil, errors.Annotate(err, "could not load new environment") 132 } 133 134 return newEnv, newState, nil 135 } 136 137 // Tag returns a name identifying the environment. 138 // The returned name will be different from other Tag values returned 139 // by any other entities from the same state. 140 func (e *Environment) Tag() names.Tag { 141 return e.EnvironTag() 142 } 143 144 // EnvironTag is the concrete environ tag for this environment. 145 func (e *Environment) EnvironTag() names.EnvironTag { 146 return names.NewEnvironTag(e.doc.UUID) 147 } 148 149 // ServerTag is the environ tag for the server that the environment is running 150 // within. 151 func (e *Environment) ServerTag() names.EnvironTag { 152 return names.NewEnvironTag(e.doc.ServerUUID) 153 } 154 155 // UUID returns the universally unique identifier of the environment. 156 func (e *Environment) UUID() string { 157 return e.doc.UUID 158 } 159 160 // Name returns the human friendly name of the environment. 161 func (e *Environment) Name() string { 162 return e.doc.Name 163 } 164 165 // Life returns whether the environment is Alive, Dying or Dead. 166 func (e *Environment) Life() Life { 167 return e.doc.Life 168 } 169 170 // Owner returns tag representing the owner of the environment. 171 // The owner is the user that created the environment. 172 func (e *Environment) Owner() names.UserTag { 173 return names.NewUserTag(e.doc.Owner) 174 } 175 176 // Config returns the config for the environment. 177 func (e *Environment) Config() (*config.Config, error) { 178 if e.st.environTag.Id() == e.UUID() { 179 return e.st.EnvironConfig() 180 } 181 // The active environment isn't the same as the environment 182 // we are querying. 183 envState, err := e.st.ForEnviron(e.ServerTag()) 184 if err != nil { 185 return nil, errors.Trace(err) 186 } 187 defer envState.Close() 188 return envState.EnvironConfig() 189 } 190 191 // globalKey returns the global database key for the environment. 192 func (e *Environment) globalKey() string { 193 return environGlobalKey 194 } 195 196 func (e *Environment) Refresh() error { 197 environments, closer := e.st.getCollection(environmentsC) 198 defer closer() 199 200 return e.refresh(environments.FindId(e.UUID())) 201 } 202 203 func (e *Environment) refresh(query *mgo.Query) error { 204 err := query.One(&e.doc) 205 if err == mgo.ErrNotFound { 206 return errors.NotFoundf("environment") 207 } 208 return err 209 } 210 211 // Destroy sets the environment's lifecycle to Dying, preventing 212 // addition of services or machines to state. 213 func (e *Environment) Destroy() error { 214 if e.Life() != Alive { 215 return nil 216 } 217 // TODO(axw) 2013-12-11 #1218688 218 // Resolve the race between checking for manual machines and 219 // destroying the environment. We can set Environment to Dying 220 // to resolve the race, but that puts the environment into an 221 // unusable state. 222 // 223 // We add a cleanup for services, but not for machines; 224 // machines are destroyed via the provider interface. The 225 // exception to this rule is manual machines; the API prevents 226 // destroy-environment from succeeding if any non-manager 227 // manual machines exist. 228 ops := []txn.Op{{ 229 C: environmentsC, 230 Id: e.doc.UUID, 231 Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, 232 Assert: isEnvAliveDoc, 233 }, e.st.newCleanupOp(cleanupServicesForDyingEnvironment, "")} 234 err := e.st.runTransaction(ops) 235 switch err { 236 case nil, txn.ErrAborted: 237 // If the transaction aborted, the environment is either 238 // Dying or Dead; neither case is an error. If it's Dead, 239 // reporting it as Dying is not incorrect; the user thought 240 // it was Alive, so we've progressed towards Dead. If the 241 // user then calls Refresh they'll get the true value. 242 e.doc.Life = Dying 243 } 244 return err 245 } 246 247 // createEnvironmentOp returns the operation needed to create 248 // an environment document with the given name and UUID. 249 func createEnvironmentOp(st *State, owner names.UserTag, name, uuid, server string) txn.Op { 250 doc := &environmentDoc{ 251 UUID: uuid, 252 Name: name, 253 Life: Alive, 254 Owner: owner.Username(), 255 ServerUUID: server, 256 } 257 return txn.Op{ 258 C: environmentsC, 259 Id: uuid, 260 Assert: txn.DocMissing, 261 Insert: doc, 262 } 263 } 264 265 // assertAliveOp returns a txn.Op that asserts the environment is alive. 266 func (e *Environment) assertAliveOp() txn.Op { 267 return txn.Op{ 268 C: environmentsC, 269 Id: e.UUID(), 270 Assert: isEnvAliveDoc, 271 } 272 } 273 274 // isEnvAlive is an Environment-specific versio nof isAliveDoc. 275 // 276 // Environment documents from versions of Juju prior to 1.17 277 // do not have the life field; if it does not exist, it should 278 // be considered to have the value Alive. 279 var isEnvAliveDoc = bson.D{ 280 {"life", bson.D{{"$in", []interface{}{Alive, nil}}}}, 281 }