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  }