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