github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/utils"
    12  	"github.com/juju/utils/clock"
    13  	"gopkg.in/juju/names.v2"
    14  	"gopkg.in/mgo.v2"
    15  	"gopkg.in/mgo.v2/txn"
    16  
    17  	"github.com/juju/juju/cloud"
    18  	"github.com/juju/juju/controller"
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/environs/config"
    21  	"github.com/juju/juju/mongo"
    22  	"github.com/juju/juju/permission"
    23  	"github.com/juju/juju/status"
    24  	"github.com/juju/juju/storage"
    25  	"github.com/juju/juju/storage/poolmanager"
    26  	"github.com/juju/juju/worker"
    27  )
    28  
    29  // Open connects to the server described by the given
    30  // info, waits for it to be initialized, and returns a new State
    31  // representing the model connected to.
    32  //
    33  // A policy may be provided, which will be used to validate and
    34  // modify behaviour of certain operations in state. A nil policy
    35  // may be provided.
    36  //
    37  // Open returns unauthorizedError if access is unauthorized.
    38  func Open(
    39  	controllerModelTag names.ModelTag,
    40  	controllerTag names.ControllerTag,
    41  	info *mongo.MongoInfo, opts mongo.DialOpts,
    42  	newPolicy NewPolicyFunc,
    43  ) (*State, error) {
    44  	st, err := open(controllerModelTag, info, opts, newPolicy, clock.WallClock)
    45  	if err != nil {
    46  		return nil, errors.Trace(err)
    47  	}
    48  	if _, err := st.Model(); err != nil {
    49  		if err := st.Close(); err != nil {
    50  			logger.Errorf("closing State for %s: %v", controllerModelTag, err)
    51  		}
    52  		return nil, errors.Annotatef(err, "cannot read model %s", controllerModelTag.Id())
    53  	}
    54  
    55  	// State should only be Opened on behalf of a controller environ; all
    56  	// other *States should be created via ForModel.
    57  	if err := st.start(controllerTag); err != nil {
    58  		return nil, errors.Trace(err)
    59  	}
    60  	return st, nil
    61  }
    62  
    63  func open(
    64  	controllerModelTag names.ModelTag,
    65  	info *mongo.MongoInfo, opts mongo.DialOpts,
    66  	newPolicy NewPolicyFunc,
    67  	clock clock.Clock,
    68  ) (*State, error) {
    69  	logger.Infof("opening state, mongo addresses: %q; entity %v", info.Addrs, info.Tag)
    70  	logger.Debugf("dialing mongo")
    71  	session, err := mongo.DialWithInfo(info.Info, opts)
    72  	if err != nil {
    73  		return nil, maybeUnauthorized(err, "cannot connect to mongodb")
    74  	}
    75  	logger.Debugf("connection established")
    76  
    77  	err = mongodbLogin(session, info)
    78  	if err != nil {
    79  		session.Close()
    80  		return nil, errors.Trace(err)
    81  	}
    82  	logger.Debugf("mongodb login successful")
    83  
    84  	st, err := newState(controllerModelTag, controllerModelTag, session, info, newPolicy, clock)
    85  	if err != nil {
    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  // InitializeParams contains the parameters for initializing the state database.
   107  type InitializeParams struct {
   108  	// Clock wraps all calls time. Real uses use clock.WallClock,
   109  	// tests may override with a testing clock.
   110  	Clock clock.Clock
   111  
   112  	// ControllerModelArgs contains the arguments for creating
   113  	// the controller model.
   114  	ControllerModelArgs ModelArgs
   115  
   116  	// CloudName is the name of the cloud that the controller
   117  	// runs in.
   118  	CloudName string
   119  
   120  	// Cloud contains the properties of the cloud that the
   121  	// controller runs in.
   122  	Cloud cloud.Cloud
   123  
   124  	// CloudCredentials contains the credentials for the owner of
   125  	// the controller model to store in the controller.
   126  	CloudCredentials map[names.CloudCredentialTag]cloud.Credential
   127  
   128  	// ControllerConfig contains config attributes for
   129  	// the controller.
   130  	ControllerConfig controller.Config
   131  
   132  	// ControllerInheritedConfig contains default config attributes for
   133  	// models on the specified cloud.
   134  	ControllerInheritedConfig map[string]interface{}
   135  
   136  	// RegionInheritedConfig contains region specific configuration for
   137  	// models running on specific cloud regions.
   138  	RegionInheritedConfig cloud.RegionConfig
   139  
   140  	// NewPolicy is a function that returns the set of state policies
   141  	// to apply.
   142  	NewPolicy NewPolicyFunc
   143  
   144  	// MongoInfo contains the information required to address and
   145  	// authenticate with Mongo.
   146  	MongoInfo *mongo.MongoInfo
   147  
   148  	// MongoDialOpts contains the dial options for connecting to
   149  	// Mongo.
   150  	MongoDialOpts mongo.DialOpts
   151  }
   152  
   153  // Validate checks that the state initialization parameters are valid.
   154  func (p InitializeParams) Validate() error {
   155  	if p.Clock == nil {
   156  		return errors.NotValidf("missing clock")
   157  	}
   158  	if err := p.ControllerModelArgs.Validate(); err != nil {
   159  		return errors.Trace(err)
   160  	}
   161  	if p.ControllerModelArgs.MigrationMode != MigrationModeNone {
   162  		return errors.NotValidf("migration mode %q", p.ControllerModelArgs.MigrationMode)
   163  	}
   164  	uuid := p.ControllerModelArgs.Config.UUID()
   165  	controllerUUID := p.ControllerConfig.ControllerUUID()
   166  	if uuid == controllerUUID {
   167  		return errors.NotValidf("same controller model uuid (%v) and controller-uuid (%v)", uuid, controllerUUID)
   168  	}
   169  	if p.MongoInfo == nil {
   170  		return errors.NotValidf("nil MongoInfo")
   171  	}
   172  	if p.CloudName == "" {
   173  		return errors.NotValidf("empty CloudName")
   174  	}
   175  	if p.Cloud.Type == "" {
   176  		return errors.NotValidf("empty Cloud")
   177  	}
   178  	if err := validateCloud(p.Cloud); err != nil {
   179  		return errors.Annotate(err, "validating cloud")
   180  	}
   181  	if _, err := validateCloudRegion(p.Cloud, p.CloudName, p.ControllerModelArgs.CloudRegion); err != nil {
   182  		return errors.Annotate(err, "validating controller model cloud region")
   183  	}
   184  	if _, err := validateCloudCredentials(p.Cloud, p.CloudName, p.CloudCredentials); err != nil {
   185  		return errors.Annotate(err, "validating cloud credentials")
   186  	}
   187  	creds := make(map[string]cloud.Credential, len(p.CloudCredentials))
   188  	for tag, cred := range p.CloudCredentials {
   189  		creds[tag.Canonical()] = cred
   190  	}
   191  	if _, err := validateCloudCredential(
   192  		p.Cloud,
   193  		p.CloudName,
   194  		creds,
   195  		p.ControllerModelArgs.CloudCredential,
   196  	); err != nil {
   197  		return errors.Annotate(err, "validating controller model cloud credential")
   198  	}
   199  	return nil
   200  }
   201  
   202  // Initialize sets up an initial empty state and returns it.
   203  // This needs to be performed only once for the initial controller model.
   204  // It returns unauthorizedError if access is unauthorized.
   205  func Initialize(args InitializeParams) (_ *State, err error) {
   206  	if err := args.Validate(); err != nil {
   207  		return nil, errors.Annotate(err, "validating initialization args")
   208  	}
   209  
   210  	// When creating the controller model, the new model
   211  	// UUID is also used as the controller UUID.
   212  	modelTag := names.NewModelTag(args.ControllerModelArgs.Config.UUID())
   213  	st, err := open(modelTag, args.MongoInfo, args.MongoDialOpts, args.NewPolicy, args.Clock)
   214  	if err != nil {
   215  		return nil, errors.Trace(err)
   216  	}
   217  	defer func() {
   218  		if err != nil {
   219  			if closeErr := st.Close(); closeErr != nil {
   220  				logger.Errorf("error closing state while aborting Initialize: %v", closeErr)
   221  			}
   222  		}
   223  	}()
   224  	st.controllerModelTag = modelTag
   225  
   226  	// A valid model is used as a signal that the
   227  	// state has already been initalized. If this is the case
   228  	// do nothing.
   229  	if _, err := st.Model(); err == nil {
   230  		return nil, errors.New("already initialized")
   231  	} else if !errors.IsNotFound(err) {
   232  		return nil, errors.Trace(err)
   233  	}
   234  
   235  	logger.Infof("initializing controller model %s", modelTag.Id())
   236  
   237  	modelOps, err := st.modelSetupOps(
   238  		args.ControllerConfig.ControllerUUID(),
   239  		args.ControllerModelArgs,
   240  		&lineage{
   241  			ControllerConfig: args.ControllerInheritedConfig,
   242  			RegionConfig:     args.RegionInheritedConfig,
   243  		})
   244  	if err != nil {
   245  		return nil, errors.Trace(err)
   246  	}
   247  	salt, err := utils.RandomSalt()
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	dateCreated := st.NowToTheSecond()
   253  	ops := createInitialUserOps(
   254  		args.ControllerConfig.ControllerUUID(),
   255  		args.ControllerModelArgs.Owner,
   256  		args.MongoInfo.Password,
   257  		salt,
   258  		dateCreated,
   259  	)
   260  	ops = append(ops,
   261  
   262  		txn.Op{
   263  			C:      controllersC,
   264  			Id:     modelGlobalKey,
   265  			Assert: txn.DocMissing,
   266  			Insert: &controllersDoc{
   267  				CloudName: args.CloudName,
   268  				ModelUUID: st.ModelUUID(),
   269  			},
   270  		},
   271  		createCloudOp(args.Cloud, args.CloudName),
   272  		txn.Op{
   273  			C:      controllersC,
   274  			Id:     apiHostPortsKey,
   275  			Assert: txn.DocMissing,
   276  			Insert: &apiHostPortsDoc{},
   277  		},
   278  		txn.Op{
   279  			C:      controllersC,
   280  			Id:     stateServingInfoKey,
   281  			Assert: txn.DocMissing,
   282  			Insert: &StateServingInfo{},
   283  		},
   284  		txn.Op{
   285  			C:      controllersC,
   286  			Id:     hostedModelCountKey,
   287  			Assert: txn.DocMissing,
   288  			Insert: &hostedModelCountDoc{},
   289  		},
   290  		createSettingsOp(controllersC, controllerSettingsGlobalKey, args.ControllerConfig),
   291  		createSettingsOp(globalSettingsC, controllerInheritedSettingsGlobalKey, args.ControllerInheritedConfig),
   292  	)
   293  	for k, v := range args.Cloud.RegionConfig {
   294  		// Create an entry keyed on cloudname#<key>, value for each region in
   295  		// region-config. The values here are themselves
   296  		// map[string]interface{}.
   297  		ops = append(ops, createSettingsOp(globalSettingsC, regionSettingsGlobalKey(args.CloudName, k), v))
   298  	}
   299  
   300  	for tag, cred := range args.CloudCredentials {
   301  		ops = append(ops, createCloudCredentialOp(tag, cred))
   302  	}
   303  	ops = append(ops, modelOps...)
   304  
   305  	if err := st.runTransaction(ops); err != nil {
   306  		return nil, errors.Trace(err)
   307  	}
   308  	controllerTag := names.NewControllerTag(args.ControllerConfig.ControllerUUID())
   309  	if err := st.start(controllerTag); err != nil {
   310  		return nil, errors.Trace(err)
   311  	}
   312  	return st, nil
   313  }
   314  
   315  // lineage is a composite of inheritable properties for the extent of
   316  // passing them into modelSetupOps.
   317  type lineage struct {
   318  	ControllerConfig map[string]interface{}
   319  	RegionConfig     cloud.RegionConfig
   320  }
   321  
   322  // modelSetupOps returns the transactions necessary to set up a model.
   323  func (st *State) modelSetupOps(controllerUUID string, args ModelArgs, inherited *lineage) ([]txn.Op, error) {
   324  	if inherited != nil {
   325  		if err := checkControllerInheritedConfig(inherited.ControllerConfig); err != nil {
   326  			return nil, errors.Trace(err)
   327  		}
   328  	}
   329  	if err := checkModelConfig(args.Config); err != nil {
   330  		return nil, errors.Trace(err)
   331  	}
   332  
   333  	controllerModelUUID := st.controllerModelTag.Id()
   334  	modelUUID := args.Config.UUID()
   335  	modelStatusDoc := statusDoc{
   336  		ModelUUID: modelUUID,
   337  		Updated:   st.clock.Now().UnixNano(),
   338  		Status:    status.Available,
   339  	}
   340  
   341  	modelUserOps := createModelUserOps(
   342  		modelUUID, args.Owner, args.Owner, args.Owner.Name(), st.NowToTheSecond(), permission.AdminAccess,
   343  	)
   344  	ops := []txn.Op{
   345  		createStatusOp(st, modelGlobalKey, modelStatusDoc),
   346  		createConstraintsOp(st, modelGlobalKey, args.Constraints),
   347  	}
   348  	// Inc ref count for hosted models.
   349  	if controllerModelUUID != modelUUID {
   350  		ops = append(ops, incHostedModelCountOp())
   351  	}
   352  
   353  	// Create the default storage pools for the model.
   354  	defaultStoragePoolsOps, err := st.createDefaultStoragePoolsOps(args.StorageProviderRegistry)
   355  	if err != nil {
   356  		return nil, errors.Trace(err)
   357  	}
   358  	ops = append(ops, defaultStoragePoolsOps...)
   359  
   360  	// Create the final map of config attributes for the model.
   361  	// If we have ControllerInheritedConfig passed in, that means state
   362  	// is being initialised and there won't be any config sources
   363  	// in state.
   364  	var configSources []modelConfigSource
   365  	if inherited != nil {
   366  		configSources = []modelConfigSource{
   367  			{
   368  				name: config.JujuDefaultSource,
   369  				sourceFunc: modelConfigSourceFunc(func() (attrValues, error) {
   370  					return config.ConfigDefaults(), nil
   371  				})},
   372  			{
   373  				name: config.JujuControllerSource,
   374  				sourceFunc: modelConfigSourceFunc(func() (attrValues, error) {
   375  					return inherited.ControllerConfig, nil
   376  				})},
   377  			{
   378  				name: config.JujuRegionSource,
   379  				sourceFunc: modelConfigSourceFunc(func() (attrValues, error) {
   380  					// We return the values specific to this region for this model.
   381  					return attrValues(inherited.RegionConfig[args.CloudRegion]), nil
   382  				})},
   383  		}
   384  	} else {
   385  		rspec := &environs.RegionSpec{Cloud: args.CloudName, Region: args.CloudRegion}
   386  		configSources = modelConfigSources(st, rspec)
   387  	}
   388  	modelCfg, err := composeModelConfigAttributes(args.Config.AllAttrs(), configSources...)
   389  	if err != nil {
   390  		return nil, errors.Trace(err)
   391  	}
   392  	// Some values require marshalling before storage.
   393  	modelCfg = config.CoerceForStorage(modelCfg)
   394  	ops = append(ops,
   395  		createSettingsOp(settingsC, modelGlobalKey, modelCfg),
   396  		createModelEntityRefsOp(modelUUID),
   397  		createModelOp(
   398  			args.Owner,
   399  			args.Config.Name(),
   400  			modelUUID, controllerUUID,
   401  			args.CloudName, args.CloudRegion, args.CloudCredential,
   402  			args.MigrationMode,
   403  		),
   404  		createUniqueOwnerModelNameOp(args.Owner, args.Config.Name()),
   405  	)
   406  	ops = append(ops, modelUserOps...)
   407  	return ops, nil
   408  }
   409  
   410  func (st *State) createDefaultStoragePoolsOps(registry storage.ProviderRegistry) ([]txn.Op, error) {
   411  	m := poolmanager.MemSettings{make(map[string]map[string]interface{})}
   412  	pm := poolmanager.New(m, registry)
   413  	providerTypes, err := registry.StorageProviderTypes()
   414  	if err != nil {
   415  		return nil, errors.Trace(err)
   416  	}
   417  	for _, providerType := range providerTypes {
   418  		p, err := registry.StorageProvider(providerType)
   419  		if err != nil {
   420  			return nil, errors.Trace(err)
   421  		}
   422  		if err := poolmanager.AddDefaultStoragePools(p, pm); err != nil {
   423  			return nil, errors.Annotatef(
   424  				err, "adding default storage pools for %q", providerType,
   425  			)
   426  		}
   427  	}
   428  
   429  	var ops []txn.Op
   430  	for key, settings := range m.Settings {
   431  		ops = append(ops, createSettingsOp(settingsC, key, settings))
   432  	}
   433  	return ops, nil
   434  }
   435  
   436  func maybeUnauthorized(err error, msg string) error {
   437  	if err == nil {
   438  		return nil
   439  	}
   440  	if isUnauthorized(err) {
   441  		return errors.Unauthorizedf("%s: unauthorized mongo access: %v", msg, err)
   442  	}
   443  	return errors.Annotatef(err, msg)
   444  }
   445  
   446  func isUnauthorized(err error) bool {
   447  	if err == nil {
   448  		return false
   449  	}
   450  	// Some unauthorized access errors have no error code,
   451  	// just a simple error string; and some do have error codes
   452  	// but are not of consistent types (LastError/QueryError).
   453  	for _, prefix := range []string{"auth fail", "not authorized", "server returned error on SASL authentication step: Authentication failed."} {
   454  		if strings.HasPrefix(err.Error(), prefix) {
   455  			return true
   456  		}
   457  	}
   458  	if err, ok := err.(*mgo.QueryError); ok {
   459  		return err.Code == 10057 ||
   460  			err.Message == "need to login" ||
   461  			err.Message == "unauthorized"
   462  	}
   463  	return false
   464  }
   465  
   466  // newState creates an incomplete *State, with no running workers or
   467  // controllerTag. You must start() the returned *State before it will
   468  // function correctly.
   469  //
   470  // newState takes responsibility for the supplied *mgo.Session, and will
   471  // close it if it cannot be returned under the aegis of a *State.
   472  func newState(
   473  	modelTag, controllerModelTag names.ModelTag,
   474  	session *mgo.Session, mongoInfo *mongo.MongoInfo,
   475  	newPolicy NewPolicyFunc,
   476  	clock clock.Clock,
   477  ) (_ *State, err error) {
   478  
   479  	defer func() {
   480  		if err != nil {
   481  			session.Close()
   482  		}
   483  	}()
   484  
   485  	// Set up database.
   486  	rawDB := session.DB(jujuDB)
   487  	database, err := allCollections().Load(rawDB, modelTag.Id())
   488  	if err != nil {
   489  		return nil, errors.Trace(err)
   490  	}
   491  	if err := InitDbLogs(session); err != nil {
   492  		return nil, errors.Trace(err)
   493  	}
   494  
   495  	// Create State.
   496  	st := &State{
   497  		clock:              clock,
   498  		modelTag:           modelTag,
   499  		controllerModelTag: controllerModelTag,
   500  		mongoInfo:          mongoInfo,
   501  		session:            session,
   502  		database:           database,
   503  		newPolicy:          newPolicy,
   504  	}
   505  	if newPolicy != nil {
   506  		st.policy = newPolicy(st)
   507  	}
   508  	return st, nil
   509  }
   510  
   511  // MongoConnectionInfo returns information for connecting to mongo
   512  func (st *State) MongoConnectionInfo() *mongo.MongoInfo {
   513  	return st.mongoInfo
   514  }
   515  
   516  // CACert returns the certificate used to validate the state connection.
   517  func (st *State) CACert() string {
   518  	return st.mongoInfo.CACert
   519  }
   520  
   521  // Close the connection to the database.
   522  func (st *State) Close() (err error) {
   523  	defer errors.DeferredAnnotatef(&err, "closing state failed")
   524  
   525  	var errs []error
   526  	handle := func(name string, err error) {
   527  		if err != nil {
   528  			errs = append(errs, errors.Annotatef(err, "error stopping %s", name))
   529  		}
   530  	}
   531  	if st.workers != nil {
   532  		handle("standard workers", worker.Stop(st.workers))
   533  	}
   534  
   535  	st.mu.Lock()
   536  	if st.allManager != nil {
   537  		handle("allwatcher manager", st.allManager.Stop())
   538  	}
   539  	if st.allModelManager != nil {
   540  		handle("allModelWatcher manager", st.allModelManager.Stop())
   541  	}
   542  	if st.allModelWatcherBacking != nil {
   543  		handle("allModelWatcher backing", st.allModelWatcherBacking.Release())
   544  	}
   545  	st.session.Close()
   546  	st.mu.Unlock()
   547  
   548  	if len(errs) > 0 {
   549  		for _, err := range errs[1:] {
   550  			logger.Errorf("while closing state: %v", err)
   551  		}
   552  		return errs[0]
   553  	}
   554  	logger.Debugf("closed state without error")
   555  	return nil
   556  }