github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/open.go (about) 1 // Copyright 2012, 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 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 "github.com/juju/utils" 14 "gopkg.in/mgo.v2" 15 "gopkg.in/mgo.v2/txn" 16 17 "github.com/juju/juju/constraints" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/mongo" 20 "github.com/juju/juju/state/watcher" 21 "github.com/juju/juju/status" 22 ) 23 24 // Open connects to the server described by the given 25 // info, waits for it to be initialized, and returns a new State 26 // representing the model connected to. 27 // 28 // A policy may be provided, which will be used to validate and 29 // modify behaviour of certain operations in state. A nil policy 30 // may be provided. 31 // 32 // Open returns unauthorizedError if access is unauthorized. 33 func Open(tag names.ModelTag, info *mongo.MongoInfo, opts mongo.DialOpts, policy Policy) (*State, error) { 34 st, err := open(tag, info, opts, policy) 35 if err != nil { 36 return nil, errors.Trace(err) 37 } 38 if _, err := st.Model(); err != nil { 39 if err := st.Close(); err != nil { 40 logger.Errorf("error closing state for unreadable model %s: %v", tag.Id(), err) 41 } 42 return nil, errors.Annotatef(err, "cannot read model %s", tag.Id()) 43 } 44 45 // State should only be Opened on behalf of a controller environ; all 46 // other *States should be created via ForEnviron. 47 if err := st.start(tag); err != nil { 48 return nil, errors.Trace(err) 49 } 50 return st, nil 51 } 52 53 func open(tag names.ModelTag, info *mongo.MongoInfo, opts mongo.DialOpts, policy Policy) (*State, error) { 54 logger.Infof("opening state, mongo addresses: %q; entity %v", info.Addrs, info.Tag) 55 logger.Debugf("dialing mongo") 56 session, err := mongo.DialWithInfo(info.Info, opts) 57 if err != nil { 58 return nil, maybeUnauthorized(err, "cannot connect to mongodb") 59 } 60 logger.Debugf("connection established") 61 62 err = mongodbLogin(session, info) 63 if err != nil { 64 session.Close() 65 return nil, errors.Trace(err) 66 } 67 logger.Debugf("mongodb login successful") 68 69 // In rare circumstances, we may be upgrading from pre-1.23, and not have the 70 // model UUID available. In that case we need to infer what it might be; 71 // we depend on the assumption that this is the only circumstance in which 72 // the the UUID might not be known. 73 if tag.Id() == "" { 74 logger.Warningf("creating state without model tag; inferring bootstrap model") 75 ssInfo, err := readRawControllerInfo(session) 76 if err != nil { 77 session.Close() 78 return nil, errors.Trace(err) 79 } 80 tag = ssInfo.ModelTag 81 } 82 83 st, err := newState(tag, session, info, policy) 84 if err != nil { 85 session.Close() 86 return nil, errors.Trace(err) 87 } 88 return st, nil 89 } 90 91 // mongodbLogin logs in to the mongodb admin database. 92 func mongodbLogin(session *mgo.Session, mongoInfo *mongo.MongoInfo) error { 93 admin := session.DB("admin") 94 if mongoInfo.Tag != nil { 95 if err := admin.Login(mongoInfo.Tag.String(), mongoInfo.Password); err != nil { 96 return maybeUnauthorized(err, fmt.Sprintf("cannot log in to admin database as %q", mongoInfo.Tag)) 97 } 98 } else if mongoInfo.Password != "" { 99 if err := admin.Login(mongo.AdminUser, mongoInfo.Password); err != nil { 100 return maybeUnauthorized(err, "cannot log in to admin database") 101 } 102 } 103 return nil 104 } 105 106 // Initialize sets up an initial empty state and returns it. 107 // This needs to be performed only once for the initial controller model. 108 // It returns unauthorizedError if access is unauthorized. 109 func Initialize(owner names.UserTag, info *mongo.MongoInfo, cfg *config.Config, opts mongo.DialOpts, policy Policy) (_ *State, err error) { 110 uuid := cfg.UUID() 111 modelTag := names.NewModelTag(uuid) 112 st, err := open(modelTag, info, opts, policy) 113 if err != nil { 114 return nil, errors.Trace(err) 115 } 116 defer func() { 117 if err != nil { 118 if closeErr := st.Close(); closeErr != nil { 119 logger.Errorf("error closing state while aborting Initialize: %v", closeErr) 120 } 121 } 122 }() 123 124 // A valid model is used as a signal that the 125 // state has already been initalized. If this is the case 126 // do nothing. 127 if _, err := st.Model(); err == nil { 128 return nil, errors.New("already initialized") 129 } else if !errors.IsNotFound(err) { 130 return nil, errors.Trace(err) 131 } 132 133 // When creating the controller model, the new model 134 // UUID is also used as the controller UUID. 135 logger.Infof("initializing controller model %s", uuid) 136 modelOps, err := st.modelSetupOps(cfg, uuid, uuid, owner, MigrationModeActive) 137 if err != nil { 138 return nil, errors.Trace(err) 139 } 140 salt, err := utils.RandomSalt() 141 if err != nil { 142 return nil, err 143 } 144 ops := []txn.Op{ 145 createInitialUserOp(st, owner, info.Password, salt), 146 txn.Op{ 147 C: controllersC, 148 Id: modelGlobalKey, 149 Assert: txn.DocMissing, 150 Insert: &controllersDoc{ 151 ModelUUID: st.ModelUUID(), 152 }, 153 }, 154 txn.Op{ 155 C: controllersC, 156 Id: apiHostPortsKey, 157 Assert: txn.DocMissing, 158 Insert: &apiHostPortsDoc{}, 159 }, 160 txn.Op{ 161 C: controllersC, 162 Id: stateServingInfoKey, 163 Assert: txn.DocMissing, 164 Insert: &StateServingInfo{}, 165 }, 166 txn.Op{ 167 C: controllersC, 168 Id: hostedModelCountKey, 169 Assert: txn.DocMissing, 170 Insert: &hostedModelCountDoc{}, 171 }, 172 } 173 ops = append(ops, modelOps...) 174 175 if err := st.runTransaction(ops); err != nil { 176 return nil, errors.Trace(err) 177 } 178 if err := st.start(modelTag); err != nil { 179 return nil, errors.Trace(err) 180 } 181 return st, nil 182 } 183 184 func (st *State) modelSetupOps(cfg *config.Config, modelUUID, serverUUID string, owner names.UserTag, mode MigrationMode) ([]txn.Op, error) { 185 if err := checkModelConfig(cfg); err != nil { 186 return nil, errors.Trace(err) 187 } 188 189 modelStatusDoc := statusDoc{ 190 ModelUUID: modelUUID, 191 // TODO(fwereade): 2016-03-17 lp:1558657 192 Updated: time.Now().UnixNano(), 193 // TODO(axw) 2016-04-13 lp:1569632 194 // We need to decide how we will 195 // represent migration in model status. 196 Status: status.StatusAvailable, 197 } 198 199 // When creating the controller model, the new model 200 // UUID is also used as the controller UUID. 201 if serverUUID == "" { 202 serverUUID = modelUUID 203 } 204 modelUserOp := createModelUserOp(modelUUID, owner, owner, owner.Name(), nowToTheSecond(), ModelAdminAccess) 205 ops := []txn.Op{ 206 createStatusOp(st, modelGlobalKey, modelStatusDoc), 207 createConstraintsOp(st, modelGlobalKey, constraints.Value{}), 208 createSettingsOp(modelGlobalKey, cfg.AllAttrs()), 209 } 210 if modelUUID != serverUUID { 211 ops = append(ops, incHostedModelCountOp()) 212 } 213 ops = append(ops, 214 createModelEntityRefsOp(st, modelUUID), 215 createModelOp(st, owner, cfg.Name(), modelUUID, serverUUID, mode), 216 createUniqueOwnerModelNameOp(owner, cfg.Name()), 217 modelUserOp, 218 ) 219 return ops, nil 220 } 221 222 func maybeUnauthorized(err error, msg string) error { 223 if err == nil { 224 return nil 225 } 226 if isUnauthorized(err) { 227 return errors.Unauthorizedf("%s: unauthorized mongo access: %v", msg, err) 228 } 229 return errors.Annotatef(err, msg) 230 } 231 232 func isUnauthorized(err error) bool { 233 if err == nil { 234 return false 235 } 236 // Some unauthorized access errors have no error code, 237 // just a simple error string; and some do have error codes 238 // but are not of consistent types (LastError/QueryError). 239 for _, prefix := range []string{"auth fail", "not authorized"} { 240 if strings.HasPrefix(err.Error(), prefix) { 241 return true 242 } 243 } 244 if err, ok := err.(*mgo.QueryError); ok { 245 return err.Code == 10057 || 246 err.Message == "need to login" || 247 err.Message == "unauthorized" 248 } 249 return false 250 } 251 252 // newState creates an incomplete *State, with a configured watcher but no 253 // pwatcher, leadershipManager, or controllerTag. You must start() the returned 254 // *State before it will function correctly. 255 func newState(modelTag names.ModelTag, session *mgo.Session, mongoInfo *mongo.MongoInfo, policy Policy) (_ *State, resultErr error) { 256 // Set up database. 257 rawDB := session.DB(jujuDB) 258 database, err := allCollections().Load(rawDB, modelTag.Id()) 259 if err != nil { 260 return nil, errors.Trace(err) 261 } 262 if err := InitDbLogs(session); err != nil { 263 return nil, errors.Trace(err) 264 } 265 266 // Create State. 267 return &State{ 268 modelTag: modelTag, 269 mongoInfo: mongoInfo, 270 session: session, 271 database: database, 272 policy: policy, 273 watcher: watcher.New(rawDB.C(txnLogC)), 274 }, nil 275 } 276 277 // MongoConnectionInfo returns information for connecting to mongo 278 func (st *State) MongoConnectionInfo() *mongo.MongoInfo { 279 return st.mongoInfo 280 } 281 282 // CACert returns the certificate used to validate the state connection. 283 func (st *State) CACert() string { 284 return st.mongoInfo.CACert 285 } 286 287 // Close the connection to the database. 288 func (st *State) Close() (err error) { 289 defer errors.DeferredAnnotatef(&err, "closing state failed") 290 291 // TODO(fwereade): we have no defence against these components failing 292 // and leaving other parts of state going. They should be managed by a 293 // dependency.Engine (or perhaps worker.Runner). 294 var errs []error 295 handle := func(name string, err error) { 296 if err != nil { 297 errs = append(errs, errors.Annotatef(err, "error stopping %s", name)) 298 } 299 } 300 301 handle("transaction watcher", st.watcher.Stop()) 302 if st.pwatcher != nil { 303 handle("presence watcher", st.pwatcher.Stop()) 304 } 305 if st.leadershipManager != nil { 306 st.leadershipManager.Kill() 307 handle("leadership manager", st.leadershipManager.Wait()) 308 } 309 if st.singularManager != nil { 310 st.singularManager.Kill() 311 handle("singular manager", st.singularManager.Wait()) 312 } 313 st.mu.Lock() 314 if st.allManager != nil { 315 handle("allwatcher manager", st.allManager.Stop()) 316 } 317 if st.allModelManager != nil { 318 handle("allModelWatcher manager", st.allModelManager.Stop()) 319 } 320 if st.allModelWatcherBacking != nil { 321 handle("allModelWatcher backing", st.allModelWatcherBacking.Release()) 322 } 323 st.session.Close() 324 st.mu.Unlock() 325 326 if len(errs) > 0 { 327 for _, err := range errs[1:] { 328 logger.Errorf("while closing state: %v", err) 329 } 330 return errs[0] 331 } 332 logger.Debugf("closed state without error") 333 return nil 334 }