github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelmanager/modelmanager.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package modelmanager defines an API end point for functions dealing with
     5  // models.  Creating, listing and sharing models. This facade is available at
     6  // the root of the controller API, and as such, there is no implicit Model
     7  // assocated.
     8  package modelmanager
     9  
    10  import (
    11  	"fmt"
    12  	"sort"
    13  	"time"
    14  
    15  	"github.com/juju/collections/set"
    16  	"github.com/juju/description"
    17  	"github.com/juju/errors"
    18  	"github.com/juju/loggo"
    19  	"github.com/juju/txn"
    20  	"github.com/juju/utils"
    21  	"github.com/juju/version"
    22  	"gopkg.in/juju/names.v2"
    23  	"gopkg.in/yaml.v2"
    24  
    25  	"github.com/juju/juju/apiserver/common"
    26  	"github.com/juju/juju/apiserver/facade"
    27  	"github.com/juju/juju/apiserver/params"
    28  	"github.com/juju/juju/caas"
    29  	jujucloud "github.com/juju/juju/cloud"
    30  	"github.com/juju/juju/controller/modelmanager"
    31  	"github.com/juju/juju/environs"
    32  	"github.com/juju/juju/environs/config"
    33  	"github.com/juju/juju/environs/context"
    34  	"github.com/juju/juju/permission"
    35  	"github.com/juju/juju/state"
    36  	"github.com/juju/juju/state/stateenvirons"
    37  	"github.com/juju/juju/tools"
    38  	jujuversion "github.com/juju/juju/version"
    39  )
    40  
    41  var logger = loggo.GetLogger("juju.apiserver.modelmanager")
    42  
    43  // ModelManagerV5 defines the methods on the version 5 facade for the
    44  // modelmanager API endpoint.
    45  type ModelManagerV5 interface {
    46  	CreateModel(args params.ModelCreateArgs) (params.ModelInfo, error)
    47  	DumpModels(args params.DumpModelRequest) params.StringResults
    48  	DumpModelsDB(args params.Entities) params.MapResults
    49  	ListModelSummaries(request params.ModelSummariesRequest) (params.ModelSummaryResults, error)
    50  	ListModels(user params.Entity) (params.UserModelList, error)
    51  	DestroyModels(args params.DestroyModelsParams) (params.ErrorResults, error)
    52  	ModelInfo(args params.Entities) (params.ModelInfoResults, error)
    53  	ModelStatus(req params.Entities) (params.ModelStatusResults, error)
    54  	ChangeModelCredential(args params.ChangeModelCredentialsParams) (params.ErrorResults, error)
    55  }
    56  
    57  // ModelManagerV4 defines the methods on the version 4 facade for the
    58  // modelmanager API endpoint.
    59  type ModelManagerV4 interface {
    60  	CreateModel(args params.ModelCreateArgs) (params.ModelInfo, error)
    61  	DumpModels(args params.DumpModelRequest) params.StringResults
    62  	DumpModelsDB(args params.Entities) params.MapResults
    63  	ListModelSummaries(request params.ModelSummariesRequest) (params.ModelSummaryResults, error)
    64  	ListModels(user params.Entity) (params.UserModelList, error)
    65  	DestroyModels(args params.DestroyModelsParams) (params.ErrorResults, error)
    66  	ModelInfo(args params.Entities) (params.ModelInfoResults, error)
    67  	ModelStatus(req params.Entities) (params.ModelStatusResults, error)
    68  }
    69  
    70  // ModelManagerV3 defines the methods on the version 3 facade for the
    71  // modelmanager API endpoint.
    72  type ModelManagerV3 interface {
    73  	CreateModel(args params.ModelCreateArgs) (params.ModelInfo, error)
    74  	DumpModels(args params.DumpModelRequest) params.StringResults
    75  	DumpModelsDB(args params.Entities) params.MapResults
    76  	ListModels(user params.Entity) (params.UserModelList, error)
    77  	DestroyModels(args params.Entities) (params.ErrorResults, error)
    78  	ModelInfo(args params.Entities) (params.ModelInfoResults, error)
    79  	ModelStatus(req params.Entities) (params.ModelStatusResults, error)
    80  }
    81  
    82  // ModelManagerV2 defines the methods on the version 2 facade for the
    83  // modelmanager API endpoint.
    84  type ModelManagerV2 interface {
    85  	CreateModel(args params.ModelCreateArgs) (params.ModelInfo, error)
    86  	DumpModels(args params.Entities) params.MapResults
    87  	DumpModelsDB(args params.Entities) params.MapResults
    88  	ListModels(user params.Entity) (params.UserModelList, error)
    89  	DestroyModels(args params.Entities) (params.ErrorResults, error)
    90  	ModelStatus(req params.Entities) (params.ModelStatusResults, error)
    91  }
    92  
    93  type newCaasBrokerFunc func(args environs.OpenParams) (caas.Broker, error)
    94  
    95  // ModelManagerAPI implements the model manager interface and is
    96  // the concrete implementation of the api end point.
    97  type ModelManagerAPI struct {
    98  	*common.ModelStatusAPI
    99  	state       common.ModelManagerBackend
   100  	ctlrState   common.ModelManagerBackend
   101  	check       *common.BlockChecker
   102  	authorizer  facade.Authorizer
   103  	toolsFinder *common.ToolsFinder
   104  	apiUser     names.UserTag
   105  	isAdmin     bool
   106  	model       common.Model
   107  	getBroker   newCaasBrokerFunc
   108  	callContext context.ProviderCallContext
   109  }
   110  
   111  // ModelManagerAPIV4 provides a way to wrap the different calls between
   112  // version 4 and version 5 of the model manager API
   113  type ModelManagerAPIV4 struct {
   114  	*ModelManagerAPI
   115  }
   116  
   117  // ModelManagerAPIV3 provides a way to wrap the different calls between
   118  // version 3 and version 4 of the model manager API
   119  type ModelManagerAPIV3 struct {
   120  	*ModelManagerAPIV4
   121  }
   122  
   123  // ModelManagerAPIV2 provides a way to wrap the different calls between
   124  // version 2 and version 3 of the model manager API
   125  type ModelManagerAPIV2 struct {
   126  	*ModelManagerAPIV3
   127  }
   128  
   129  var (
   130  	_ ModelManagerV5 = (*ModelManagerAPI)(nil)
   131  	_ ModelManagerV4 = (*ModelManagerAPIV4)(nil)
   132  	_ ModelManagerV3 = (*ModelManagerAPIV3)(nil)
   133  	_ ModelManagerV2 = (*ModelManagerAPIV2)(nil)
   134  )
   135  
   136  // NewFacadeV5 is used for API registration.
   137  func NewFacadeV5(ctx facade.Context) (*ModelManagerAPI, error) {
   138  	st := ctx.State()
   139  	pool := ctx.StatePool()
   140  	ctlrSt := pool.SystemState()
   141  	auth := ctx.Auth()
   142  
   143  	var err error
   144  	model, err := st.Model()
   145  	if err != nil {
   146  		return nil, errors.Trace(err)
   147  	}
   148  
   149  	configGetter := stateenvirons.EnvironConfigGetter{st, model}
   150  
   151  	ctrlModel, err := ctlrSt.Model()
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	return NewModelManagerAPI(
   157  		common.NewModelManagerBackend(model, pool),
   158  		common.NewModelManagerBackend(ctrlModel, pool),
   159  		configGetter,
   160  		caas.New,
   161  		auth,
   162  		model,
   163  		state.CallContext(st),
   164  	)
   165  }
   166  
   167  // NewFacadeV4 is used for API registration.
   168  func NewFacadeV4(ctx facade.Context) (*ModelManagerAPIV4, error) {
   169  	v5, err := NewFacadeV5(ctx)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	return &ModelManagerAPIV4{v5}, nil
   174  }
   175  
   176  // NewFacadeV3 is used for API registration.
   177  func NewFacadeV3(ctx facade.Context) (*ModelManagerAPIV3, error) {
   178  	v4, err := NewFacadeV4(ctx)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return &ModelManagerAPIV3{v4}, nil
   183  }
   184  
   185  // NewFacade is used for API registration.
   186  func NewFacadeV2(ctx facade.Context) (*ModelManagerAPIV2, error) {
   187  	v3, err := NewFacadeV3(ctx)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	return &ModelManagerAPIV2{v3}, nil
   192  }
   193  
   194  // NewModelManagerAPI creates a new api server endpoint for managing
   195  // models.
   196  func NewModelManagerAPI(
   197  	st common.ModelManagerBackend,
   198  	ctlrSt common.ModelManagerBackend,
   199  	configGetter environs.EnvironConfigGetter,
   200  	getBroker newCaasBrokerFunc,
   201  	authorizer facade.Authorizer,
   202  	m common.Model,
   203  	callCtx context.ProviderCallContext,
   204  ) (*ModelManagerAPI, error) {
   205  	if !authorizer.AuthClient() {
   206  		return nil, common.ErrPerm
   207  	}
   208  	// Since we know this is a user tag (because AuthClient is true),
   209  	// we just do the type assertion to the UserTag.
   210  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
   211  	// Pretty much all of the user manager methods have special casing for admin
   212  	// users, so look once when we start and remember if the user is an admin.
   213  	isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, st.ControllerTag())
   214  	if err != nil {
   215  		return nil, errors.Trace(err)
   216  	}
   217  	urlGetter := common.NewToolsURLGetter(st.ModelUUID(), st)
   218  	return &ModelManagerAPI{
   219  		ModelStatusAPI: common.NewModelStatusAPI(st, authorizer, apiUser),
   220  		state:          st,
   221  		ctlrState:      ctlrSt,
   222  		getBroker:      getBroker,
   223  		check:          common.NewBlockChecker(st),
   224  		authorizer:     authorizer,
   225  		toolsFinder:    common.NewToolsFinder(configGetter, st, urlGetter),
   226  		apiUser:        apiUser,
   227  		isAdmin:        isAdmin,
   228  		model:          m,
   229  		callContext:    callCtx,
   230  	}, nil
   231  }
   232  
   233  // authCheck checks if the user is acting on their own behalf, or if they
   234  // are an administrator acting on behalf of another user.
   235  func (m *ModelManagerAPI) authCheck(user names.UserTag) error {
   236  	if m.isAdmin {
   237  		logger.Tracef("%q is a controller admin", m.apiUser.Id())
   238  		return nil
   239  	}
   240  
   241  	// We can't just compare the UserTags themselves as the provider part
   242  	// may be unset, and gets replaced with 'local'. We must compare against
   243  	// the Canonical value of the user tag.
   244  	if m.apiUser == user {
   245  		return nil
   246  	}
   247  	return common.ErrPerm
   248  }
   249  
   250  func (m *ModelManagerAPI) hasWriteAccess(modelTag names.ModelTag) (bool, error) {
   251  	canWrite, err := m.authorizer.HasPermission(permission.WriteAccess, modelTag)
   252  	if errors.IsNotFound(err) {
   253  		return false, nil
   254  	}
   255  	return canWrite, err
   256  }
   257  
   258  // ConfigSource describes a type that is able to provide config.
   259  // Abstracted primarily for testing.
   260  type ConfigSource interface {
   261  	Config() (*config.Config, error)
   262  }
   263  
   264  func (m *ModelManagerAPI) newModelConfig(
   265  	cloudSpec environs.CloudSpec,
   266  	args params.ModelCreateArgs,
   267  	source ConfigSource,
   268  ) (*config.Config, error) {
   269  	// For now, we just smash to the two maps together as we store
   270  	// the account values and the model config together in the
   271  	// *config.Config instance.
   272  	joint := make(map[string]interface{})
   273  	for key, value := range args.Config {
   274  		joint[key] = value
   275  	}
   276  	if _, ok := joint["uuid"]; ok {
   277  		return nil, errors.New("uuid is generated, you cannot specify one")
   278  	}
   279  	if args.Name == "" {
   280  		return nil, errors.NewNotValid(nil, "Name must be specified")
   281  	}
   282  	if _, ok := joint[config.NameKey]; ok {
   283  		return nil, errors.New("name must not be specified in config")
   284  	}
   285  	joint[config.NameKey] = args.Name
   286  
   287  	baseConfig, err := source.Config()
   288  	if err != nil {
   289  		return nil, errors.Trace(err)
   290  	}
   291  
   292  	regionSpec := &environs.RegionSpec{Cloud: cloudSpec.Name, Region: cloudSpec.Region}
   293  	if joint, err = m.state.ComposeNewModelConfig(joint, regionSpec); err != nil {
   294  		return nil, errors.Trace(err)
   295  	}
   296  
   297  	creator := modelmanager.ModelConfigCreator{
   298  		Provider: environs.Provider,
   299  		FindTools: func(n version.Number) (tools.List, error) {
   300  			result, err := m.toolsFinder.FindTools(params.FindToolsParams{
   301  				Number: n,
   302  			})
   303  			if err != nil {
   304  				return nil, errors.Trace(err)
   305  			}
   306  			return result.List, nil
   307  		},
   308  	}
   309  	return creator.NewModelConfig(cloudSpec, baseConfig, joint)
   310  }
   311  
   312  func (m *ModelManagerAPI) newCAASModelConfig(
   313  	cloudSpec environs.CloudSpec,
   314  	args params.ModelCreateArgs,
   315  ) (*config.Config, error) {
   316  	uuid, err := utils.NewUUID()
   317  	if err != nil {
   318  		return nil, errors.Trace(err)
   319  	}
   320  	if args.Name == "" {
   321  		return nil, errors.NewNotValid(nil, "Name must be specified")
   322  	}
   323  
   324  	attrs := map[string]interface{}{
   325  		config.NameKey:         args.Name,
   326  		config.TypeKey:         cloudSpec.Type,
   327  		config.UUIDKey:         uuid.String(),
   328  		config.AgentVersionKey: jujuversion.Current.String(),
   329  	}
   330  
   331  	cfg, err := config.New(config.UseDefaults, attrs)
   332  	if err != nil {
   333  		return nil, errors.Annotate(err, "creating config from values failed")
   334  	}
   335  
   336  	return cfg, nil
   337  }
   338  
   339  func (m *ModelManagerAPI) checkAddModelPermission(cloud string, userTag names.UserTag) (bool, error) {
   340  	perm, err := m.ctlrState.GetCloudAccess(cloud, userTag)
   341  	if err != nil && !errors.IsNotFound(err) {
   342  		return false, errors.Trace(err)
   343  	}
   344  	if !perm.EqualOrGreaterCloudAccessThan(permission.AddModelAccess) {
   345  		return false, nil
   346  	}
   347  	return true, nil
   348  }
   349  
   350  // CreateModel creates a new model using the account and
   351  // model config specified in the args.
   352  func (m *ModelManagerAPI) CreateModel(args params.ModelCreateArgs) (params.ModelInfo, error) {
   353  	result := params.ModelInfo{}
   354  
   355  	// Get the controller model first. We need it both for the state
   356  	// server owner and the ability to get the config.
   357  	controllerModel, err := m.ctlrState.Model()
   358  	if err != nil {
   359  		return result, errors.Trace(err)
   360  	}
   361  
   362  	var cloudTag names.CloudTag
   363  	cloudRegionName := args.CloudRegion
   364  	if args.CloudTag != "" {
   365  		var err error
   366  		cloudTag, err = names.ParseCloudTag(args.CloudTag)
   367  		if err != nil {
   368  			return result, errors.Trace(err)
   369  		}
   370  	} else {
   371  		cloudTag = names.NewCloudTag(controllerModel.Cloud())
   372  	}
   373  	if cloudRegionName == "" && cloudTag.Id() == controllerModel.Cloud() {
   374  		cloudRegionName = controllerModel.CloudRegion()
   375  	}
   376  
   377  	isAdmin, err := m.authorizer.HasPermission(permission.SuperuserAccess, m.state.ControllerTag())
   378  	if err != nil {
   379  		return result, errors.Trace(err)
   380  	}
   381  	if !isAdmin {
   382  		canAddModel, err := m.checkAddModelPermission(cloudTag.Id(), m.apiUser)
   383  		if err != nil {
   384  			return result, errors.Trace(err)
   385  		}
   386  		if !canAddModel {
   387  			return result, common.ErrPerm
   388  		}
   389  	}
   390  
   391  	ownerTag, err := names.ParseUserTag(args.OwnerTag)
   392  	if err != nil {
   393  		return result, errors.Trace(err)
   394  	}
   395  
   396  	// a special case of ErrPerm will happen if the user has add-model permission but is trying to
   397  	// create a model for another person, which is not yet supported.
   398  	if !m.isAdmin && ownerTag != m.apiUser {
   399  		return result, errors.Annotatef(common.ErrPerm, "%q permission does not permit creation of models for different owners", permission.AddModelAccess)
   400  	}
   401  
   402  	cloud, err := m.state.Cloud(cloudTag.Id())
   403  	if err != nil {
   404  		if errors.IsNotFound(err) && args.CloudTag != "" {
   405  			// A cloud was specified, and it was not found.
   406  			// Annotate the error with the supported clouds.
   407  			clouds, err := m.state.Clouds()
   408  			if err != nil {
   409  				return result, errors.Trace(err)
   410  			}
   411  			cloudNames := make([]string, 0, len(clouds))
   412  			for tag := range clouds {
   413  				cloudNames = append(cloudNames, tag.Id())
   414  			}
   415  			sort.Strings(cloudNames)
   416  			return result, errors.NewNotFound(err, fmt.Sprintf(
   417  				"cloud %q not found, expected one of %q",
   418  				cloudTag.Id(), cloudNames,
   419  			))
   420  		}
   421  		return result, errors.Annotate(err, "getting cloud definition")
   422  	}
   423  
   424  	var cloudCredentialTag names.CloudCredentialTag
   425  	if args.CloudCredentialTag != "" {
   426  		var err error
   427  		cloudCredentialTag, err = names.ParseCloudCredentialTag(args.CloudCredentialTag)
   428  		if err != nil {
   429  			return result, errors.Trace(err)
   430  		}
   431  	} else {
   432  		if ownerTag == controllerModel.Owner() {
   433  			cloudCredentialTag, _ = controllerModel.CloudCredential()
   434  		} else {
   435  			// TODO(axw) check if the user has one and only one
   436  			// cloud credential, and if so, use it? For now, we
   437  			// require the user to specify a credential unless
   438  			// the cloud does not require one.
   439  			var hasEmpty bool
   440  			for _, authType := range cloud.AuthTypes {
   441  				if authType != jujucloud.EmptyAuthType {
   442  					continue
   443  				}
   444  				hasEmpty = true
   445  				break
   446  			}
   447  			if !hasEmpty {
   448  				return result, errors.NewNotValid(nil, "no credential specified")
   449  			}
   450  		}
   451  	}
   452  
   453  	var credential *jujucloud.Credential
   454  	if cloudCredentialTag != (names.CloudCredentialTag{}) {
   455  		credentialValue, err := m.state.CloudCredential(cloudCredentialTag)
   456  		if err != nil {
   457  			return result, errors.Annotate(err, "getting credential")
   458  		}
   459  		cloudCredential := jujucloud.NewNamedCredential(
   460  			credentialValue.Name,
   461  			jujucloud.AuthType(credentialValue.AuthType),
   462  			credentialValue.Attributes,
   463  			credentialValue.Revoked,
   464  		)
   465  		credential = &cloudCredential
   466  	}
   467  
   468  	cloudSpec, err := environs.MakeCloudSpec(cloud, cloudRegionName, credential)
   469  	if err != nil {
   470  		return result, errors.Trace(err)
   471  	}
   472  
   473  	var model common.Model
   474  
   475  	if jujucloud.CloudIsCAAS(cloud) {
   476  		model, err = m.newCAASModel(
   477  			cloudSpec,
   478  			args,
   479  			cloudTag,
   480  			cloudCredentialTag,
   481  			ownerTag)
   482  	} else {
   483  		model, err = m.newModel(
   484  			cloudSpec,
   485  			args,
   486  			controllerModel,
   487  			cloudTag,
   488  			cloudRegionName,
   489  			cloudCredentialTag,
   490  			ownerTag)
   491  	}
   492  	if err != nil {
   493  		return result, errors.Trace(err)
   494  	}
   495  	return m.getModelInfo(model.ModelTag())
   496  }
   497  
   498  func (m *ModelManagerAPI) newCAASModel(cloudSpec environs.CloudSpec,
   499  	createArgs params.ModelCreateArgs,
   500  	cloudTag names.CloudTag,
   501  	cloudCredentialTag names.CloudCredentialTag,
   502  	ownerTag names.UserTag,
   503  ) (common.Model, error) {
   504  	newConfig, err := m.newCAASModelConfig(cloudSpec, createArgs)
   505  	if err != nil {
   506  		return nil, errors.Annotate(err, "failed to create config")
   507  	}
   508  
   509  	broker, err := m.getBroker(environs.OpenParams{
   510  		Cloud:  cloudSpec,
   511  		Config: newConfig,
   512  	})
   513  	if err != nil {
   514  		return nil, errors.Annotate(err, "failed to open kubernetes client")
   515  	}
   516  
   517  	// CAAS models exist in a namespace which must be unique.
   518  	namespaces, err := broker.Namespaces()
   519  	if err != nil {
   520  		return nil, errors.Annotate(err, "failed to list namespaces")
   521  	}
   522  	nsSet := set.NewStrings(namespaces...)
   523  	if nsSet.Contains(createArgs.Name) {
   524  		return nil, errors.NewAlreadyExists(nil, fmt.Sprintf("namespace called %q already exists, would clash with model name", createArgs.Name))
   525  	}
   526  
   527  	storageProviderRegistry := stateenvirons.NewStorageProviderRegistry(broker)
   528  
   529  	model, st, err := m.state.NewModel(state.ModelArgs{
   530  		Type:                    state.ModelTypeCAAS,
   531  		CloudName:               cloudTag.Id(),
   532  		CloudCredential:         cloudCredentialTag,
   533  		Config:                  newConfig,
   534  		Owner:                   ownerTag,
   535  		StorageProviderRegistry: storageProviderRegistry,
   536  	})
   537  	if err != nil {
   538  		return nil, errors.Annotate(err, "failed to create new model")
   539  	}
   540  	defer st.Close()
   541  
   542  	return model, nil
   543  }
   544  
   545  func (m *ModelManagerAPI) newModel(
   546  	cloudSpec environs.CloudSpec,
   547  	createArgs params.ModelCreateArgs,
   548  	controllerModel common.Model,
   549  	cloudTag names.CloudTag,
   550  	cloudRegionName string,
   551  	cloudCredentialTag names.CloudCredentialTag,
   552  	ownerTag names.UserTag,
   553  ) (common.Model, error) {
   554  	newConfig, err := m.newModelConfig(cloudSpec, createArgs, controllerModel)
   555  	if err != nil {
   556  		return nil, errors.Annotate(err, "failed to create config")
   557  	}
   558  
   559  	// Create the Environ.
   560  	env, err := environs.New(environs.OpenParams{
   561  		Cloud:  cloudSpec,
   562  		Config: newConfig,
   563  	})
   564  	if err != nil {
   565  		return nil, errors.Annotate(err, "failed to open environ")
   566  	}
   567  
   568  	controllerCfg, err := m.state.ControllerConfig()
   569  	if err != nil {
   570  		return nil, errors.Trace(err)
   571  	}
   572  
   573  	err = env.Create(
   574  		m.callContext,
   575  		environs.CreateParams{
   576  			ControllerUUID: controllerCfg.ControllerUUID(),
   577  		},
   578  	)
   579  	if err != nil {
   580  		return nil, errors.Annotate(err, "failed to create environ")
   581  	}
   582  	storageProviderRegistry := stateenvirons.NewStorageProviderRegistry(env)
   583  
   584  	// NOTE: check the agent-version of the config, and if it is > the current
   585  	// version, it is not supported, also check existing tools, and if we don't
   586  	// have tools for that version, also die.
   587  	model, st, err := m.state.NewModel(state.ModelArgs{
   588  		Type:                    state.ModelTypeIAAS,
   589  		CloudName:               cloudTag.Id(),
   590  		CloudRegion:             cloudRegionName,
   591  		CloudCredential:         cloudCredentialTag,
   592  		Config:                  newConfig,
   593  		Owner:                   ownerTag,
   594  		StorageProviderRegistry: storageProviderRegistry,
   595  		EnvironVersion:          env.Provider().Version(),
   596  	})
   597  	if err != nil {
   598  		return nil, errors.Annotate(err, "failed to create new model")
   599  	}
   600  	defer st.Close()
   601  
   602  	if err = model.AutoConfigureContainerNetworking(env); err != nil {
   603  		if errors.IsNotSupported(err) {
   604  			logger.Debugf("Not performing container networking autoconfiguration on a non-networking environment")
   605  		} else {
   606  			return nil, errors.Annotate(err, "Failed to perform container networking autoconfiguration")
   607  		}
   608  	}
   609  	if err = st.ReloadSpaces(env); err != nil {
   610  		if errors.IsNotSupported(err) {
   611  			logger.Debugf("Not performing spaces load on a non-networking environment")
   612  		} else {
   613  			return nil, errors.Annotate(err, "Failed to perform spaces discovery")
   614  		}
   615  	}
   616  	return model, nil
   617  }
   618  
   619  func (m *ModelManagerAPI) dumpModel(args params.Entity, simplified bool) ([]byte, error) {
   620  	modelTag, err := names.ParseModelTag(args.Tag)
   621  	if err != nil {
   622  		return nil, errors.Trace(err)
   623  	}
   624  
   625  	isModelAdmin, err := m.authorizer.HasPermission(permission.AdminAccess, modelTag)
   626  	if err != nil {
   627  		return nil, errors.Trace(err)
   628  	}
   629  	if !isModelAdmin && !m.isAdmin {
   630  		return nil, common.ErrPerm
   631  	}
   632  
   633  	st, release, err := m.state.GetBackend(modelTag.Id())
   634  	if err != nil {
   635  		if errors.IsNotFound(err) {
   636  			return nil, errors.Trace(common.ErrBadId)
   637  		}
   638  		return nil, errors.Trace(err)
   639  	}
   640  	defer release()
   641  
   642  	var exportConfig state.ExportConfig
   643  	if simplified {
   644  		exportConfig.SkipActions = true
   645  		exportConfig.SkipAnnotations = true
   646  		exportConfig.SkipCloudImageMetadata = true
   647  		exportConfig.SkipCredentials = true
   648  		exportConfig.SkipIPAddresses = true
   649  		exportConfig.SkipSettings = true
   650  		exportConfig.SkipSSHHostKeys = true
   651  		exportConfig.SkipStatusHistory = true
   652  		exportConfig.SkipLinkLayerDevices = true
   653  	}
   654  
   655  	model, err := st.ExportPartial(exportConfig)
   656  	if err != nil {
   657  		return nil, errors.Trace(err)
   658  	}
   659  	bytes, err := description.Serialize(model)
   660  	if err != nil {
   661  		return nil, errors.Trace(err)
   662  	}
   663  	return bytes, nil
   664  }
   665  
   666  func (m *ModelManagerAPIV2) dumpModel(args params.Entity) (map[string]interface{}, error) {
   667  	bytes, err := m.ModelManagerAPI.dumpModel(args, false)
   668  	if err != nil {
   669  		return nil, errors.Trace(err)
   670  	}
   671  	// Now read it back into a map.
   672  	var asMap map[string]interface{}
   673  	err = yaml.Unmarshal(bytes, &asMap)
   674  	if err != nil {
   675  		return nil, errors.Trace(err)
   676  	}
   677  	// In order to serialize the map through JSON, we need to make sure
   678  	// that all the embedded maps are map[string]interface{}, not
   679  	// map[interface{}]interface{} which is what YAML gives by default.
   680  	out, err := utils.ConformYAML(asMap)
   681  	if err != nil {
   682  		return nil, errors.Trace(err)
   683  	}
   684  	return out.(map[string]interface{}), nil
   685  }
   686  
   687  func (m *ModelManagerAPI) dumpModelDB(args params.Entity) (map[string]interface{}, error) {
   688  	modelTag, err := names.ParseModelTag(args.Tag)
   689  	if err != nil {
   690  		return nil, errors.Trace(err)
   691  	}
   692  
   693  	isModelAdmin, err := m.authorizer.HasPermission(permission.AdminAccess, modelTag)
   694  	if err != nil {
   695  		return nil, errors.Trace(err)
   696  	}
   697  	if !isModelAdmin && !m.isAdmin {
   698  		return nil, common.ErrPerm
   699  	}
   700  
   701  	st := m.state
   702  	if st.ModelTag() != modelTag {
   703  		newSt, release, err := m.state.GetBackend(modelTag.Id())
   704  		if errors.IsNotFound(err) {
   705  			return nil, errors.Trace(common.ErrBadId)
   706  		} else if err != nil {
   707  			return nil, errors.Trace(err)
   708  		}
   709  		defer release()
   710  		st = newSt
   711  	}
   712  
   713  	return st.DumpAll()
   714  }
   715  
   716  // DumpModels will export the models into the database agnostic
   717  // representation. The user needs to either be a controller admin, or have
   718  // admin privileges on the model itself.
   719  func (m *ModelManagerAPI) DumpModels(args params.DumpModelRequest) params.StringResults {
   720  	results := params.StringResults{
   721  		Results: make([]params.StringResult, len(args.Entities)),
   722  	}
   723  	for i, entity := range args.Entities {
   724  		bytes, err := m.dumpModel(entity, args.Simplified)
   725  		if err != nil {
   726  			results.Results[i].Error = common.ServerError(err)
   727  			continue
   728  		}
   729  		// We know here that the bytes are valid YAML.
   730  		results.Results[i].Result = string(bytes)
   731  	}
   732  	return results
   733  }
   734  
   735  // DumpModels will export the models into the database agnostic
   736  // representation. The user needs to either be a controller admin, or have
   737  // admin privileges on the model itself.
   738  func (m *ModelManagerAPIV2) DumpModels(args params.Entities) params.MapResults {
   739  	results := params.MapResults{
   740  		Results: make([]params.MapResult, len(args.Entities)),
   741  	}
   742  	for i, entity := range args.Entities {
   743  		dumped, err := m.dumpModel(entity)
   744  		if err != nil {
   745  			results.Results[i].Error = common.ServerError(err)
   746  			continue
   747  		}
   748  		results.Results[i].Result = dumped
   749  	}
   750  	return results
   751  }
   752  
   753  // DumpModelsDB will gather all documents from all model collections
   754  // for the specified model. The map result contains a map of collection
   755  // names to lists of documents represented as maps.
   756  func (m *ModelManagerAPI) DumpModelsDB(args params.Entities) params.MapResults {
   757  	results := params.MapResults{
   758  		Results: make([]params.MapResult, len(args.Entities)),
   759  	}
   760  	for i, entity := range args.Entities {
   761  		dumped, err := m.dumpModelDB(entity)
   762  		if err != nil {
   763  			results.Results[i].Error = common.ServerError(err)
   764  			continue
   765  		}
   766  		results.Results[i].Result = dumped
   767  	}
   768  	return results
   769  }
   770  
   771  // ListModelSummaries returns models that the specified user
   772  // has access to in the current server.  Controller admins (superuser)
   773  // can list models for any user.  Other users
   774  // can only ask about their own models.
   775  func (m *ModelManagerAPI) ListModelSummaries(req params.ModelSummariesRequest) (params.ModelSummaryResults, error) {
   776  	result := params.ModelSummaryResults{}
   777  
   778  	userTag, err := names.ParseUserTag(req.UserTag)
   779  	if err != nil {
   780  		return result, errors.Trace(err)
   781  	}
   782  
   783  	err = m.authCheck(userTag)
   784  	if err != nil {
   785  		return result, errors.Trace(err)
   786  	}
   787  
   788  	modelInfos, err := m.state.ModelSummariesForUser(userTag, req.All)
   789  	if err != nil {
   790  		return result, errors.Trace(err)
   791  	}
   792  
   793  	for _, mi := range modelInfos {
   794  		summary := &params.ModelSummary{
   795  			Name:           mi.Name,
   796  			UUID:           mi.UUID,
   797  			Type:           string(mi.Type),
   798  			OwnerTag:       names.NewUserTag(mi.Owner).String(),
   799  			ControllerUUID: mi.ControllerUUID,
   800  			IsController:   mi.IsController,
   801  			Life:           params.Life(mi.Life.String()),
   802  
   803  			CloudTag:    mi.CloudTag,
   804  			CloudRegion: mi.CloudRegion,
   805  
   806  			CloudCredentialTag: mi.CloudCredentialTag,
   807  
   808  			SLA: &params.ModelSLAInfo{
   809  				Level: mi.SLALevel,
   810  				Owner: mi.Owner,
   811  			},
   812  
   813  			DefaultSeries: mi.DefaultSeries,
   814  			ProviderType:  mi.ProviderType,
   815  			AgentVersion:  mi.AgentVersion,
   816  
   817  			Status:             common.EntityStatusFromState(mi.Status),
   818  			Counts:             []params.ModelEntityCount{},
   819  			UserLastConnection: mi.UserLastConnection,
   820  		}
   821  
   822  		if mi.MachineCount > 0 {
   823  			summary.Counts = append(summary.Counts, params.ModelEntityCount{params.Machines, mi.MachineCount})
   824  		}
   825  
   826  		if mi.CoreCount > 0 {
   827  			summary.Counts = append(summary.Counts, params.ModelEntityCount{params.Cores, mi.CoreCount})
   828  		}
   829  
   830  		access, err := common.StateToParamsUserAccessPermission(mi.Access)
   831  		if err == nil {
   832  			summary.UserAccess = access
   833  		}
   834  		if mi.Migration != nil {
   835  			migration := mi.Migration
   836  			startTime := migration.StartTime()
   837  			endTime := new(time.Time)
   838  			*endTime = migration.EndTime()
   839  			var zero time.Time
   840  			if *endTime == zero {
   841  				endTime = nil
   842  			}
   843  
   844  			summary.Migration = &params.ModelMigrationStatus{
   845  				Status: migration.StatusMessage(),
   846  				Start:  &startTime,
   847  				End:    endTime,
   848  			}
   849  		}
   850  
   851  		result.Results = append(result.Results, params.ModelSummaryResult{Result: summary})
   852  	}
   853  	return result, nil
   854  }
   855  
   856  // ListModels returns the models that the specified user
   857  // has access to in the current server.  Controller admins (superuser)
   858  // can list models for any user.  Other users
   859  // can only ask about their own models.
   860  func (m *ModelManagerAPI) ListModels(user params.Entity) (params.UserModelList, error) {
   861  	result := params.UserModelList{}
   862  
   863  	userTag, err := names.ParseUserTag(user.Tag)
   864  	if err != nil {
   865  		return result, errors.Trace(err)
   866  	}
   867  
   868  	err = m.authCheck(userTag)
   869  	if err != nil {
   870  		return result, errors.Trace(err)
   871  	}
   872  
   873  	modelInfos, err := m.state.ModelBasicInfoForUser(userTag)
   874  	if err != nil {
   875  		return result, errors.Trace(err)
   876  	}
   877  
   878  	for _, mi := range modelInfos {
   879  		var ownerTag names.UserTag
   880  		if names.IsValidUser(mi.Owner) {
   881  			ownerTag = names.NewUserTag(mi.Owner)
   882  		} else {
   883  			// no reason to fail the request here, as it wasn't the users fault
   884  			logger.Warningf("for model %v, got an invalid owner: %q", mi.UUID, mi.Owner)
   885  		}
   886  		result.UserModels = append(result.UserModels, params.UserModel{
   887  			Model: params.Model{
   888  				Name:     mi.Name,
   889  				UUID:     mi.UUID,
   890  				Type:     string(mi.Type),
   891  				OwnerTag: ownerTag.String(),
   892  			},
   893  			LastConnection: &mi.LastConnection,
   894  		})
   895  	}
   896  
   897  	return result, nil
   898  }
   899  
   900  // DestroyModels will try to destroy the specified models.
   901  // If there is a block on destruction, this method will return an error.
   902  func (m *ModelManagerAPIV3) DestroyModels(args params.Entities) (params.ErrorResults, error) {
   903  	// v3 DestroyModels is implemented in terms of v4:
   904  	// storage is unconditionally destroyed, as was the
   905  	// old behaviour.
   906  	destroyStorage := true
   907  	v4Args := params.DestroyModelsParams{
   908  		Models: make([]params.DestroyModelParams, len(args.Entities)),
   909  	}
   910  	for i, arg := range args.Entities {
   911  		v4Args.Models[i] = params.DestroyModelParams{
   912  			ModelTag:       arg.Tag,
   913  			DestroyStorage: &destroyStorage,
   914  		}
   915  	}
   916  	return m.ModelManagerAPI.DestroyModels(v4Args)
   917  }
   918  
   919  // DestroyModels will try to destroy the specified models.
   920  // If there is a block on destruction, this method will return an error.
   921  func (m *ModelManagerAPI) DestroyModels(args params.DestroyModelsParams) (params.ErrorResults, error) {
   922  	results := params.ErrorResults{
   923  		Results: make([]params.ErrorResult, len(args.Models)),
   924  	}
   925  
   926  	destroyModel := func(modelUUID string, destroyStorage *bool) error {
   927  		st, releaseSt, err := m.state.GetBackend(modelUUID)
   928  		if err != nil {
   929  			return errors.Trace(err)
   930  		}
   931  		defer releaseSt()
   932  
   933  		model, err := st.Model()
   934  		if err != nil {
   935  			return errors.Trace(err)
   936  		}
   937  		if !m.isAdmin {
   938  			hasAdmin, err := m.authorizer.HasPermission(permission.AdminAccess, model.ModelTag())
   939  			if err != nil {
   940  				return errors.Trace(err)
   941  			}
   942  			if !hasAdmin {
   943  				return errors.Trace(common.ErrPerm)
   944  			}
   945  		}
   946  
   947  		return errors.Trace(common.DestroyModel(st, destroyStorage))
   948  	}
   949  
   950  	for i, arg := range args.Models {
   951  		tag, err := names.ParseModelTag(arg.ModelTag)
   952  		if err != nil {
   953  			results.Results[i].Error = common.ServerError(err)
   954  			continue
   955  		}
   956  		if err := destroyModel(tag.Id(), arg.DestroyStorage); err != nil {
   957  			results.Results[i].Error = common.ServerError(err)
   958  			continue
   959  		}
   960  	}
   961  	return results, nil
   962  }
   963  
   964  // ModelInfo returns information about the specified models.
   965  func (m *ModelManagerAPI) ModelInfo(args params.Entities) (params.ModelInfoResults, error) {
   966  	results := params.ModelInfoResults{
   967  		Results: make([]params.ModelInfoResult, len(args.Entities)),
   968  	}
   969  
   970  	getModelInfo := func(arg params.Entity) (params.ModelInfo, error) {
   971  		tag, err := names.ParseModelTag(arg.Tag)
   972  		if err != nil {
   973  			return params.ModelInfo{}, errors.Trace(err)
   974  		}
   975  		return m.getModelInfo(tag)
   976  	}
   977  
   978  	for i, arg := range args.Entities {
   979  		modelInfo, err := getModelInfo(arg)
   980  		if err != nil {
   981  			results.Results[i].Error = common.ServerError(err)
   982  			continue
   983  		}
   984  		results.Results[i].Result = &modelInfo
   985  	}
   986  	return results, nil
   987  }
   988  
   989  func (m *ModelManagerAPI) getModelInfo(tag names.ModelTag) (params.ModelInfo, error) {
   990  	st, release, err := m.state.GetBackend(tag.Id())
   991  	if errors.IsNotFound(err) {
   992  		return params.ModelInfo{}, errors.Trace(common.ErrPerm)
   993  	} else if err != nil {
   994  		return params.ModelInfo{}, errors.Trace(err)
   995  	}
   996  	defer release()
   997  
   998  	model, err := st.Model()
   999  	if errors.IsNotFound(err) {
  1000  		return params.ModelInfo{}, errors.Trace(common.ErrPerm)
  1001  	} else if err != nil {
  1002  		return params.ModelInfo{}, errors.Trace(err)
  1003  	}
  1004  
  1005  	info := params.ModelInfo{
  1006  		Name:           model.Name(),
  1007  		Type:           string(model.Type()),
  1008  		UUID:           model.UUID(),
  1009  		ControllerUUID: model.ControllerUUID(),
  1010  		IsController:   st.IsController(),
  1011  		OwnerTag:       model.Owner().String(),
  1012  		Life:           params.Life(model.Life().String()),
  1013  		CloudTag:       names.NewCloudTag(model.Cloud()).String(),
  1014  		CloudRegion:    model.CloudRegion(),
  1015  	}
  1016  
  1017  	if cloudCredentialTag, ok := model.CloudCredential(); ok {
  1018  		info.CloudCredentialTag = cloudCredentialTag.String()
  1019  	}
  1020  
  1021  	// All users with access to the model can see the SLA information.
  1022  	info.SLA = &params.ModelSLAInfo{
  1023  		Level: model.SLALevel(),
  1024  		Owner: model.SLAOwner(),
  1025  	}
  1026  
  1027  	// If model is not alive - dying or dead - or if it is being imported,
  1028  	// there is no guarantee that the rest of the call will succeed.
  1029  	// For these models we can ignore NotFound errors coming from persistence layer.
  1030  	// However, for Alive models, these errors are genuine and cannot be ignored.
  1031  	ignoreNotFoundError := model.Life() != state.Alive || model.MigrationMode() == state.MigrationModeImporting
  1032  
  1033  	// If we received an an error and cannot ignore it, we should consider it fatal and surface it.
  1034  	// We should do the same if we can ignore NotFound errors but the given error is of some other type.
  1035  	shouldErr := func(thisErr error) bool {
  1036  		if thisErr == nil {
  1037  			return false
  1038  		}
  1039  		return !ignoreNotFoundError || !errors.IsNotFound(thisErr)
  1040  	}
  1041  	cfg, err := model.Config()
  1042  	if shouldErr(err) {
  1043  		return params.ModelInfo{}, errors.Trace(err)
  1044  	}
  1045  	if err == nil {
  1046  		info.ProviderType = cfg.Type()
  1047  		info.DefaultSeries = config.PreferredSeries(cfg)
  1048  		if agentVersion, exists := cfg.AgentVersion(); exists {
  1049  			info.AgentVersion = &agentVersion
  1050  		}
  1051  	}
  1052  
  1053  	status, err := model.Status()
  1054  	if shouldErr(err) {
  1055  		return params.ModelInfo{}, errors.Trace(err)
  1056  	}
  1057  	if err == nil {
  1058  		entityStatus := common.EntityStatusFromState(status)
  1059  		info.Status = entityStatus
  1060  	}
  1061  
  1062  	// If the user is a controller superuser, they are considered a model
  1063  	// admin.
  1064  	modelAdmin := m.isAdmin
  1065  	if !m.isAdmin {
  1066  		modelAdmin, err = m.authorizer.HasPermission(permission.AdminAccess, model.ModelTag())
  1067  		if err != nil {
  1068  			modelAdmin = false
  1069  		}
  1070  	}
  1071  
  1072  	users, err := model.Users()
  1073  	if shouldErr(err) {
  1074  		return params.ModelInfo{}, errors.Trace(err)
  1075  	}
  1076  	if err == nil {
  1077  		for _, user := range users {
  1078  			if !modelAdmin && m.authCheck(user.UserTag) != nil {
  1079  				// The authenticated user is neither the a controller
  1080  				// superuser, a model administrator, nor the model user, so
  1081  				// has no business knowing about the model user.
  1082  				continue
  1083  			}
  1084  
  1085  			userInfo, err := common.ModelUserInfo(user, model)
  1086  			if err != nil {
  1087  				return params.ModelInfo{}, errors.Trace(err)
  1088  			}
  1089  			info.Users = append(info.Users, userInfo)
  1090  		}
  1091  
  1092  		if len(info.Users) == 0 {
  1093  			// No users, which means the authenticated user doesn't
  1094  			// have access to the model.
  1095  			return params.ModelInfo{}, errors.Trace(common.ErrPerm)
  1096  		}
  1097  	}
  1098  
  1099  	canSeeMachines := modelAdmin
  1100  	if !canSeeMachines {
  1101  		if canSeeMachines, err = m.hasWriteAccess(tag); err != nil {
  1102  			return params.ModelInfo{}, errors.Trace(err)
  1103  		}
  1104  	}
  1105  	if canSeeMachines {
  1106  		if info.Machines, err = common.ModelMachineInfo(st); shouldErr(err) {
  1107  			return params.ModelInfo{}, err
  1108  		}
  1109  	}
  1110  
  1111  	migration, err := st.LatestMigration()
  1112  	if err != nil && !errors.IsNotFound(err) {
  1113  		return params.ModelInfo{}, errors.Trace(err)
  1114  	}
  1115  	if err == nil {
  1116  		startTime := migration.StartTime()
  1117  		endTime := new(time.Time)
  1118  		*endTime = migration.EndTime()
  1119  		var zero time.Time
  1120  		if *endTime == zero {
  1121  			endTime = nil
  1122  		}
  1123  		info.Migration = &params.ModelMigrationStatus{
  1124  			Status: migration.StatusMessage(),
  1125  			Start:  &startTime,
  1126  			End:    endTime,
  1127  		}
  1128  	}
  1129  	return info, nil
  1130  }
  1131  
  1132  // ModifyModelAccess changes the model access granted to users.
  1133  func (m *ModelManagerAPI) ModifyModelAccess(args params.ModifyModelAccessRequest) (result params.ErrorResults, _ error) {
  1134  	result = params.ErrorResults{
  1135  		Results: make([]params.ErrorResult, len(args.Changes)),
  1136  	}
  1137  
  1138  	canModifyController, err := m.authorizer.HasPermission(permission.SuperuserAccess, m.state.ControllerTag())
  1139  	if err != nil {
  1140  		return result, errors.Trace(err)
  1141  	}
  1142  	if len(args.Changes) == 0 {
  1143  		return result, nil
  1144  	}
  1145  
  1146  	for i, arg := range args.Changes {
  1147  		modelAccess := permission.Access(arg.Access)
  1148  		if err := permission.ValidateModelAccess(modelAccess); err != nil {
  1149  			err = errors.Annotate(err, "could not modify model access")
  1150  			result.Results[i].Error = common.ServerError(err)
  1151  			continue
  1152  		}
  1153  
  1154  		modelTag, err := names.ParseModelTag(arg.ModelTag)
  1155  		if err != nil {
  1156  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access"))
  1157  			continue
  1158  		}
  1159  		canModifyModel, err := m.authorizer.HasPermission(permission.AdminAccess, modelTag)
  1160  		if err != nil {
  1161  			return result, errors.Trace(err)
  1162  		}
  1163  		canModify := canModifyController || canModifyModel
  1164  
  1165  		if !canModify {
  1166  			result.Results[i].Error = common.ServerError(common.ErrPerm)
  1167  			continue
  1168  		}
  1169  
  1170  		targetUserTag, err := names.ParseUserTag(arg.UserTag)
  1171  		if err != nil {
  1172  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access"))
  1173  			continue
  1174  		}
  1175  
  1176  		result.Results[i].Error = common.ServerError(
  1177  			changeModelAccess(m.state, modelTag, m.apiUser, targetUserTag, arg.Action, modelAccess, m.isAdmin))
  1178  	}
  1179  	return result, nil
  1180  }
  1181  
  1182  func userAuthorizedToChangeAccess(st common.ModelManagerBackend, userIsAdmin bool, userTag names.UserTag) error {
  1183  	if userIsAdmin {
  1184  		// Just confirm that the model that has been given is a valid model.
  1185  		_, err := st.Model()
  1186  		if err != nil {
  1187  			return errors.Trace(err)
  1188  		}
  1189  		return nil
  1190  	}
  1191  
  1192  	// Get the current user's ModelUser for the Model to see if the user has
  1193  	// permission to grant or revoke permissions on the model.
  1194  	currentUser, err := st.UserAccess(userTag, st.ModelTag())
  1195  	if err != nil {
  1196  		if errors.IsNotFound(err) {
  1197  			// No, this user doesn't have permission.
  1198  			return common.ErrPerm
  1199  		}
  1200  		return errors.Annotate(err, "could not retrieve user")
  1201  	}
  1202  	if currentUser.Access != permission.AdminAccess {
  1203  		return common.ErrPerm
  1204  	}
  1205  	return nil
  1206  }
  1207  
  1208  // changeModelAccess performs the requested access grant or revoke action for the
  1209  // specified user on the specified model.
  1210  func changeModelAccess(accessor common.ModelManagerBackend, modelTag names.ModelTag, apiUser, targetUserTag names.UserTag, action params.ModelAction, access permission.Access, userIsAdmin bool) error {
  1211  	st, release, err := accessor.GetBackend(modelTag.Id())
  1212  	if err != nil {
  1213  		return errors.Annotate(err, "could not lookup model")
  1214  	}
  1215  	defer release()
  1216  
  1217  	if err := userAuthorizedToChangeAccess(st, userIsAdmin, apiUser); err != nil {
  1218  		return errors.Trace(err)
  1219  	}
  1220  
  1221  	model, err := st.Model()
  1222  	if err != nil {
  1223  		return errors.Trace(err)
  1224  	}
  1225  
  1226  	switch action {
  1227  	case params.GrantModelAccess:
  1228  		_, err = model.AddUser(state.UserAccessSpec{User: targetUserTag, CreatedBy: apiUser, Access: access})
  1229  		if errors.IsAlreadyExists(err) {
  1230  			modelUser, err := st.UserAccess(targetUserTag, modelTag)
  1231  			if errors.IsNotFound(err) {
  1232  				// Conflicts with prior check, must be inconsistent state.
  1233  				err = txn.ErrExcessiveContention
  1234  			}
  1235  			if err != nil {
  1236  				return errors.Annotate(err, "could not look up model access for user")
  1237  			}
  1238  
  1239  			// Only set access if greater access is being granted.
  1240  			if modelUser.Access.EqualOrGreaterModelAccessThan(access) {
  1241  				return errors.Errorf("user already has %q access or greater", access)
  1242  			}
  1243  			if _, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, access); err != nil {
  1244  				return errors.Annotate(err, "could not set model access for user")
  1245  			}
  1246  			return nil
  1247  		}
  1248  		return errors.Annotate(err, "could not grant model access")
  1249  
  1250  	case params.RevokeModelAccess:
  1251  		switch access {
  1252  		case permission.ReadAccess:
  1253  			// Revoking read access removes all access.
  1254  			err := st.RemoveUserAccess(targetUserTag, modelTag)
  1255  			return errors.Annotate(err, "could not revoke model access")
  1256  		case permission.WriteAccess:
  1257  			// Revoking write access sets read-only.
  1258  			modelUser, err := st.UserAccess(targetUserTag, modelTag)
  1259  			if err != nil {
  1260  				return errors.Annotate(err, "could not look up model access for user")
  1261  			}
  1262  			_, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, permission.ReadAccess)
  1263  			return errors.Annotate(err, "could not set model access to read-only")
  1264  		case permission.AdminAccess:
  1265  			// Revoking admin access sets read-write.
  1266  			modelUser, err := st.UserAccess(targetUserTag, modelTag)
  1267  			if err != nil {
  1268  				return errors.Annotate(err, "could not look up model access for user")
  1269  			}
  1270  			_, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, permission.WriteAccess)
  1271  			return errors.Annotate(err, "could not set model access to read-write")
  1272  
  1273  		default:
  1274  			return errors.Errorf("don't know how to revoke %q access", access)
  1275  		}
  1276  
  1277  	default:
  1278  		return errors.Errorf("unknown action %q", action)
  1279  	}
  1280  }
  1281  
  1282  // ModelDefaults returns the default config values used when creating a new model.
  1283  func (m *ModelManagerAPI) ModelDefaults() (params.ModelDefaultsResult, error) {
  1284  	result := params.ModelDefaultsResult{}
  1285  	if !m.isAdmin {
  1286  		return result, common.ErrPerm
  1287  	}
  1288  
  1289  	values, err := m.model.ModelConfigDefaultValues()
  1290  	if err != nil {
  1291  		return result, errors.Trace(err)
  1292  	}
  1293  	result.Config = make(map[string]params.ModelDefaults)
  1294  	for attr, val := range values {
  1295  		settings := params.ModelDefaults{
  1296  			Controller: val.Controller,
  1297  			Default:    val.Default,
  1298  		}
  1299  		for _, v := range val.Regions {
  1300  			settings.Regions = append(
  1301  				settings.Regions, params.RegionDefaults{
  1302  					RegionName: v.Name,
  1303  					Value:      v.Value})
  1304  		}
  1305  		result.Config[attr] = settings
  1306  	}
  1307  	return result, nil
  1308  }
  1309  
  1310  // SetModelDefaults writes new values for the specified default model settings.
  1311  func (m *ModelManagerAPI) SetModelDefaults(args params.SetModelDefaults) (params.ErrorResults, error) {
  1312  	results := params.ErrorResults{Results: make([]params.ErrorResult, len(args.Config))}
  1313  	if err := m.check.ChangeAllowed(); err != nil {
  1314  		return results, errors.Trace(err)
  1315  	}
  1316  	for i, arg := range args.Config {
  1317  		results.Results[i].Error = common.ServerError(
  1318  			m.setModelDefaults(arg),
  1319  		)
  1320  	}
  1321  	return results, nil
  1322  }
  1323  
  1324  func (m *ModelManagerAPI) setModelDefaults(args params.ModelDefaultValues) error {
  1325  	if !m.isAdmin {
  1326  		return common.ErrPerm
  1327  	}
  1328  
  1329  	if err := m.check.ChangeAllowed(); err != nil {
  1330  		return errors.Trace(err)
  1331  	}
  1332  	// Make sure we don't allow changing agent-version.
  1333  	if _, found := args.Config["agent-version"]; found {
  1334  		return errors.New("agent-version cannot have a default value")
  1335  	}
  1336  
  1337  	var rspec *environs.RegionSpec
  1338  	if args.CloudRegion != "" {
  1339  		spec, err := m.makeRegionSpec(args.CloudTag, args.CloudRegion)
  1340  		if err != nil {
  1341  			return errors.Trace(err)
  1342  		}
  1343  		rspec = spec
  1344  	}
  1345  	return m.state.UpdateModelConfigDefaultValues(args.Config, nil, rspec)
  1346  }
  1347  
  1348  // UnsetModelDefaults removes the specified default model settings.
  1349  func (m *ModelManagerAPI) UnsetModelDefaults(args params.UnsetModelDefaults) (params.ErrorResults, error) {
  1350  	results := params.ErrorResults{Results: make([]params.ErrorResult, len(args.Keys))}
  1351  	if !m.isAdmin {
  1352  		return results, common.ErrPerm
  1353  	}
  1354  
  1355  	if err := m.check.ChangeAllowed(); err != nil {
  1356  		return results, errors.Trace(err)
  1357  	}
  1358  
  1359  	for i, arg := range args.Keys {
  1360  		var rspec *environs.RegionSpec
  1361  		if arg.CloudRegion != "" {
  1362  			spec, err := m.makeRegionSpec(arg.CloudTag, arg.CloudRegion)
  1363  			if err != nil {
  1364  				results.Results[i].Error = common.ServerError(
  1365  					errors.Trace(err))
  1366  				continue
  1367  			}
  1368  			rspec = spec
  1369  		}
  1370  		results.Results[i].Error = common.ServerError(
  1371  			m.state.UpdateModelConfigDefaultValues(nil, arg.Keys, rspec),
  1372  		)
  1373  	}
  1374  	return results, nil
  1375  }
  1376  
  1377  // makeRegionSpec is a helper method for methods that call
  1378  // state.UpdateModelConfigDefaultValues.
  1379  func (m *ModelManagerAPI) makeRegionSpec(cloudTag, r string) (*environs.RegionSpec, error) {
  1380  	cTag, err := names.ParseCloudTag(cloudTag)
  1381  	if err != nil {
  1382  		return nil, errors.Trace(err)
  1383  	}
  1384  	rspec, err := environs.NewRegionSpec(cTag.Id(), r)
  1385  	if err != nil {
  1386  		return nil, errors.Trace(err)
  1387  	}
  1388  	return rspec, nil
  1389  }
  1390  
  1391  // ModelStatus is a legacy method call to ensure that we preserve
  1392  // backward compatibility.
  1393  // TODO (anastasiamac 2017-10-26) This should be made obsolete/removed.
  1394  func (s *ModelManagerAPIV2) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
  1395  	return s.ModelManagerAPI.oldModelStatus(req)
  1396  }
  1397  
  1398  // ModelStatus is a legacy method call to ensure that we preserve
  1399  // backward compatibility.
  1400  // TODO (anastasiamac 2017-10-26) This should be made obsolete/removed.
  1401  func (s *ModelManagerAPIV3) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
  1402  	return s.ModelManagerAPI.oldModelStatus(req)
  1403  }
  1404  
  1405  // ModelStatus is a legacy method call to ensure that we preserve
  1406  // backward compatibility.
  1407  // TODO (anastasiamac 2017-10-26) This should be made obsolete/removed.
  1408  func (s *ModelManagerAPI) oldModelStatus(req params.Entities) (params.ModelStatusResults, error) {
  1409  	results, err := s.ModelStatusAPI.ModelStatus(req)
  1410  	if err != nil {
  1411  		return params.ModelStatusResults{}, err
  1412  	}
  1413  	for _, r := range results.Results {
  1414  		if r.Error != nil {
  1415  			return params.ModelStatusResults{Results: make([]params.ModelStatus, len(req.Entities))}, errors.Trace(r.Error)
  1416  		}
  1417  	}
  1418  	return results, nil
  1419  }
  1420  
  1421  // ChangeModelCredentials changes cloud credential reference for models.
  1422  // These new cloud credentials must already exist on the controller.
  1423  func (m *ModelManagerAPI) ChangeModelCredential(args params.ChangeModelCredentialsParams) (params.ErrorResults, error) {
  1424  	if err := m.check.ChangeAllowed(); err != nil {
  1425  		return params.ErrorResults{}, errors.Trace(err)
  1426  	}
  1427  
  1428  	controllerAdmin, err := m.authorizer.HasPermission(permission.SuperuserAccess, m.state.ControllerTag())
  1429  	if err != nil {
  1430  		return params.ErrorResults{}, errors.Trace(err)
  1431  	}
  1432  	// Only controller or model admin can change cloud credential on a model.
  1433  	checkModelAccess := func(tag names.ModelTag) error {
  1434  		if controllerAdmin {
  1435  			return nil
  1436  		}
  1437  		modelAdmin, err := m.authorizer.HasPermission(permission.AdminAccess, tag)
  1438  		if err != nil {
  1439  			return errors.Trace(err)
  1440  		}
  1441  		if modelAdmin {
  1442  			return nil
  1443  		}
  1444  		return common.ErrPerm
  1445  	}
  1446  
  1447  	replaceModelCredential := func(arg params.ChangeModelCredentialParams) error {
  1448  		modelTag, err := names.ParseModelTag(arg.ModelTag)
  1449  		if err != nil {
  1450  			return errors.Trace(err)
  1451  		}
  1452  		if err := checkModelAccess(modelTag); err != nil {
  1453  			return errors.Trace(err)
  1454  		}
  1455  		credentialTag, err := names.ParseCloudCredentialTag(arg.CloudCredentialTag)
  1456  		if err != nil {
  1457  			return errors.Trace(err)
  1458  		}
  1459  		model, releaser, err := m.state.GetModel(modelTag.Id())
  1460  		if err != nil {
  1461  			return errors.Trace(err)
  1462  		}
  1463  		defer releaser()
  1464  
  1465  		updated, err := model.SetCloudCredential(credentialTag)
  1466  		if err != nil {
  1467  			return errors.Trace(err)
  1468  		}
  1469  		if !updated {
  1470  			return errors.Errorf("model %v already uses credential %v", modelTag.Id(), credentialTag.Id())
  1471  		}
  1472  		return nil
  1473  	}
  1474  
  1475  	results := make([]params.ErrorResult, len(args.Models))
  1476  	for i, arg := range args.Models {
  1477  		if err := replaceModelCredential(arg); err != nil {
  1478  			results[i].Error = common.ServerError(err)
  1479  		}
  1480  	}
  1481  	return params.ErrorResults{results}, nil
  1482  }
  1483  
  1484  // Mask out new methods from the old API versions. The API reflection
  1485  // code in rpc/rpcreflect/type.go:newMethod skips 2-argument methods,
  1486  // so this removes the method as far as the RPC machinery is concerned.
  1487  //
  1488  // ChangeModelCredential did not exist prior to v5.
  1489  func (*ModelManagerAPIV4) ChangeModelCredential(_, _ struct{}) {}