github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/controller/controller.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // The controller package defines an API end point for functions dealing
     5  // with controllers as a whole.
     6  package controller
     7  
     8  import (
     9  	"encoding/json"
    10  	"sort"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/txn"
    15  	"github.com/juju/utils/set"
    16  	"gopkg.in/juju/names.v2"
    17  	"gopkg.in/macaroon.v1"
    18  
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/api/migrationtarget"
    21  	"github.com/juju/juju/apiserver/common"
    22  	"github.com/juju/juju/apiserver/common/cloudspec"
    23  	"github.com/juju/juju/apiserver/facade"
    24  	"github.com/juju/juju/apiserver/params"
    25  	coremigration "github.com/juju/juju/core/migration"
    26  	"github.com/juju/juju/migration"
    27  	"github.com/juju/juju/permission"
    28  	"github.com/juju/juju/state"
    29  	"github.com/juju/juju/state/stateenvirons"
    30  )
    31  
    32  var logger = loggo.GetLogger("juju.apiserver.controller")
    33  
    34  func init() {
    35  	common.RegisterStandardFacade("Controller", 3, NewControllerAPI)
    36  }
    37  
    38  // Controller defines the methods on the controller API end point.
    39  type Controller interface {
    40  	AllModels() (params.UserModelList, error)
    41  	DestroyController(args params.DestroyControllerArgs) error
    42  	ModelConfig() (params.ModelConfigResults, error)
    43  	HostedModelConfigs() (params.HostedModelConfigsResults, error)
    44  	GetControllerAccess(params.Entities) (params.UserAccessResults, error)
    45  	ControllerConfig() (params.ControllerConfigResult, error)
    46  	ListBlockedModels() (params.ModelBlockInfoList, error)
    47  	RemoveBlocks(args params.RemoveBlocksArgs) error
    48  	WatchAllModels() (params.AllWatcherId, error)
    49  	ModelStatus(params.Entities) (params.ModelStatusResults, error)
    50  	InitiateMigration(params.InitiateMigrationArgs) (params.InitiateMigrationResults, error)
    51  	ModifyControllerAccess(params.ModifyControllerAccessRequest) (params.ErrorResults, error)
    52  }
    53  
    54  // ControllerAPI implements the environment manager interface and is
    55  // the concrete implementation of the api end point.
    56  type ControllerAPI struct {
    57  	*common.ControllerConfigAPI
    58  	cloudspec.CloudSpecAPI
    59  
    60  	state      *state.State
    61  	authorizer facade.Authorizer
    62  	apiUser    names.UserTag
    63  	resources  facade.Resources
    64  }
    65  
    66  var _ Controller = (*ControllerAPI)(nil)
    67  
    68  // NewControllerAPI creates a new api server endpoint for managing
    69  // environments.
    70  func NewControllerAPI(
    71  	st *state.State,
    72  	resources facade.Resources,
    73  	authorizer facade.Authorizer,
    74  ) (*ControllerAPI, error) {
    75  	if !authorizer.AuthClient() {
    76  		return nil, errors.Trace(common.ErrPerm)
    77  	}
    78  
    79  	// Since we know this is a user tag (because AuthClient is true),
    80  	// we just do the type assertion to the UserTag.
    81  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
    82  
    83  	environConfigGetter := stateenvirons.EnvironConfigGetter{st}
    84  	return &ControllerAPI{
    85  		ControllerConfigAPI: common.NewControllerConfig(st),
    86  		CloudSpecAPI:        cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag())),
    87  		state:               st,
    88  		authorizer:          authorizer,
    89  		apiUser:             apiUser,
    90  		resources:           resources,
    91  	}, nil
    92  }
    93  
    94  func (s *ControllerAPI) checkHasAdmin() error {
    95  	isAdmin, err := s.authorizer.HasPermission(permission.SuperuserAccess, s.state.ControllerTag())
    96  	if err != nil {
    97  		return errors.Trace(err)
    98  	}
    99  	if !isAdmin {
   100  		return common.ServerError(common.ErrPerm)
   101  	}
   102  	return nil
   103  }
   104  
   105  // AllModels allows controller administrators to get the list of all the
   106  // environments in the controller.
   107  func (s *ControllerAPI) AllModels() (params.UserModelList, error) {
   108  	result := params.UserModelList{}
   109  	if err := s.checkHasAdmin(); err != nil {
   110  		return result, errors.Trace(err)
   111  	}
   112  
   113  	// Get all the environments that the authenticated user can see, and
   114  	// supplement that with the other environments that exist that the user
   115  	// cannot see. The reason we do this is to get the LastConnection time for
   116  	// the environments that the user is able to see, so we have consistent
   117  	// output when listing with or without --all when an admin user.
   118  	environments, err := s.state.ModelsForUser(s.apiUser)
   119  	if err != nil {
   120  		return result, errors.Trace(err)
   121  	}
   122  	visibleEnvironments := set.NewStrings()
   123  	for _, env := range environments {
   124  		lastConn, err := env.LastConnection()
   125  		if err != nil && !state.IsNeverConnectedError(err) {
   126  			return result, errors.Trace(err)
   127  		}
   128  		visibleEnvironments.Add(env.UUID())
   129  		result.UserModels = append(result.UserModels, params.UserModel{
   130  			Model: params.Model{
   131  				Name:     env.Name(),
   132  				UUID:     env.UUID(),
   133  				OwnerTag: env.Owner().String(),
   134  			},
   135  			LastConnection: &lastConn,
   136  		})
   137  	}
   138  
   139  	allEnvs, err := s.state.AllModels()
   140  	if err != nil {
   141  		return result, errors.Trace(err)
   142  	}
   143  
   144  	for _, env := range allEnvs {
   145  		if !visibleEnvironments.Contains(env.UUID()) {
   146  			result.UserModels = append(result.UserModels, params.UserModel{
   147  				Model: params.Model{
   148  					Name:     env.Name(),
   149  					UUID:     env.UUID(),
   150  					OwnerTag: env.Owner().String(),
   151  				},
   152  				// No LastConnection as this user hasn't.
   153  			})
   154  		}
   155  	}
   156  
   157  	// Sort the resulting sequence by environment name, then owner.
   158  	sort.Sort(orderedUserModels(result.UserModels))
   159  
   160  	return result, nil
   161  }
   162  
   163  // ListBlockedModels returns a list of all environments on the controller
   164  // which have a block in place.  The resulting slice is sorted by environment
   165  // name, then owner. Callers must be controller administrators to retrieve the
   166  // list.
   167  func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) {
   168  	results := params.ModelBlockInfoList{}
   169  	if err := s.checkHasAdmin(); err != nil {
   170  		return results, errors.Trace(err)
   171  	}
   172  	blocks, err := s.state.AllBlocksForController()
   173  	if err != nil {
   174  		return results, errors.Trace(err)
   175  	}
   176  
   177  	envBlocks := make(map[string][]string)
   178  	for _, block := range blocks {
   179  		uuid := block.ModelUUID()
   180  		types, ok := envBlocks[uuid]
   181  		if !ok {
   182  			types = []string{block.Type().String()}
   183  		} else {
   184  			types = append(types, block.Type().String())
   185  		}
   186  		envBlocks[uuid] = types
   187  	}
   188  
   189  	for uuid, blocks := range envBlocks {
   190  		envInfo, err := s.state.GetModel(names.NewModelTag(uuid))
   191  		if err != nil {
   192  			logger.Debugf("Unable to get name for model: %s", uuid)
   193  			continue
   194  		}
   195  		results.Models = append(results.Models, params.ModelBlockInfo{
   196  			UUID:     envInfo.UUID(),
   197  			Name:     envInfo.Name(),
   198  			OwnerTag: envInfo.Owner().String(),
   199  			Blocks:   blocks,
   200  		})
   201  	}
   202  
   203  	// Sort the resulting sequence by environment name, then owner.
   204  	sort.Sort(orderedBlockInfo(results.Models))
   205  
   206  	return results, nil
   207  }
   208  
   209  // ModelConfig returns the environment config for the controller
   210  // environment.  For information on the current environment, use
   211  // client.ModelGet
   212  func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) {
   213  	result := params.ModelConfigResults{}
   214  	if err := s.checkHasAdmin(); err != nil {
   215  		return result, errors.Trace(err)
   216  	}
   217  
   218  	controllerModel, err := s.state.ControllerModel()
   219  	if err != nil {
   220  		return result, errors.Trace(err)
   221  	}
   222  
   223  	cfg, err := controllerModel.Config()
   224  	if err != nil {
   225  		return result, errors.Trace(err)
   226  	}
   227  
   228  	result.Config = make(map[string]params.ConfigValue)
   229  	for name, val := range cfg.AllAttrs() {
   230  		result.Config[name] = params.ConfigValue{
   231  			Value: val,
   232  		}
   233  	}
   234  	return result, nil
   235  }
   236  
   237  // HostedModelConfigs returns all the information that the client needs in
   238  // order to connect directly with the host model's provider and destroy it
   239  // directly.
   240  func (s *ControllerAPI) HostedModelConfigs() (params.HostedModelConfigsResults, error) {
   241  	result := params.HostedModelConfigsResults{}
   242  	if err := s.checkHasAdmin(); err != nil {
   243  		return result, errors.Trace(err)
   244  	}
   245  
   246  	controllerModel, err := s.state.ControllerModel()
   247  	if err != nil {
   248  		return result, errors.Trace(err)
   249  	}
   250  
   251  	allModels, err := s.state.AllModels()
   252  	if err != nil {
   253  		return result, errors.Trace(err)
   254  	}
   255  
   256  	for _, model := range allModels {
   257  		if model.UUID() != controllerModel.UUID() {
   258  			config := params.HostedModelConfig{
   259  				Name:     model.Name(),
   260  				OwnerTag: model.Owner().String(),
   261  			}
   262  			modelConf, err := model.Config()
   263  			if err != nil {
   264  				config.Error = common.ServerError(err)
   265  			} else {
   266  				config.Config = modelConf.AllAttrs()
   267  			}
   268  			cloudSpec := s.GetCloudSpec(model.ModelTag())
   269  			if config.Error == nil {
   270  				config.CloudSpec = cloudSpec.Result
   271  				config.Error = cloudSpec.Error
   272  			}
   273  			result.Models = append(result.Models, config)
   274  		}
   275  	}
   276  
   277  	return result, nil
   278  }
   279  
   280  // RemoveBlocks removes all the blocks in the controller.
   281  func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error {
   282  	if err := s.checkHasAdmin(); err != nil {
   283  		return errors.Trace(err)
   284  	}
   285  
   286  	if !args.All {
   287  		return errors.New("not supported")
   288  	}
   289  	return errors.Trace(s.state.RemoveAllBlocksForController())
   290  }
   291  
   292  // WatchAllModels starts watching events for all models in the
   293  // controller. The returned AllWatcherId should be used with Next on the
   294  // AllModelWatcher endpoint to receive deltas.
   295  func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) {
   296  	if err := c.checkHasAdmin(); err != nil {
   297  		return params.AllWatcherId{}, errors.Trace(err)
   298  	}
   299  	w := c.state.WatchAllModels()
   300  	return params.AllWatcherId{
   301  		AllWatcherId: c.resources.Register(w),
   302  	}, nil
   303  }
   304  
   305  type orderedBlockInfo []params.ModelBlockInfo
   306  
   307  func (o orderedBlockInfo) Len() int {
   308  	return len(o)
   309  }
   310  
   311  func (o orderedBlockInfo) Less(i, j int) bool {
   312  	if o[i].Name < o[j].Name {
   313  		return true
   314  	}
   315  	if o[i].Name > o[j].Name {
   316  		return false
   317  	}
   318  
   319  	if o[i].OwnerTag < o[j].OwnerTag {
   320  		return true
   321  	}
   322  	if o[i].OwnerTag > o[j].OwnerTag {
   323  		return false
   324  	}
   325  
   326  	// Unreachable based on the rules of there not being duplicate
   327  	// environments of the same name for the same owner, but return false
   328  	// instead of panicing.
   329  	return false
   330  }
   331  
   332  // ModelStatus returns a summary of the environment.
   333  func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
   334  	models := req.Entities
   335  	results := params.ModelStatusResults{}
   336  	if err := c.checkHasAdmin(); err != nil {
   337  		return results, errors.Trace(err)
   338  	}
   339  
   340  	status := make([]params.ModelStatus, len(models))
   341  	for i, model := range models {
   342  		modelStatus, err := c.modelStatus(model.Tag)
   343  		if err != nil {
   344  			return results, errors.Trace(err)
   345  		}
   346  		status[i] = modelStatus
   347  	}
   348  	results.Results = status
   349  	return results, nil
   350  }
   351  
   352  // GetControllerAccess returns the level of access the specifed users
   353  // have on the controller.
   354  func (c *ControllerAPI) GetControllerAccess(req params.Entities) (params.UserAccessResults, error) {
   355  	results := params.UserAccessResults{}
   356  	isAdmin, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag())
   357  	if err != nil {
   358  		return results, errors.Trace(err)
   359  	}
   360  
   361  	users := req.Entities
   362  	results.Results = make([]params.UserAccessResult, len(users))
   363  	for i, user := range users {
   364  		userTag, err := names.ParseUserTag(user.Tag)
   365  		if err != nil {
   366  			results.Results[i].Error = common.ServerError(err)
   367  			continue
   368  		}
   369  		if !isAdmin && !c.authorizer.AuthOwner(userTag) {
   370  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   371  			continue
   372  		}
   373  		accessInfo, err := c.state.UserAccess(userTag, c.state.ControllerTag())
   374  		if err != nil {
   375  			results.Results[i].Error = common.ServerError(err)
   376  			continue
   377  		}
   378  		results.Results[i].Result = &params.UserAccess{
   379  			Access:  string(accessInfo.Access),
   380  			UserTag: userTag.String()}
   381  	}
   382  	return results, nil
   383  }
   384  
   385  // InitiateMigration attempts to begin the migration of one or
   386  // more models to other controllers.
   387  func (c *ControllerAPI) InitiateMigration(reqArgs params.InitiateMigrationArgs) (
   388  	params.InitiateMigrationResults, error,
   389  ) {
   390  	out := params.InitiateMigrationResults{
   391  		Results: make([]params.InitiateMigrationResult, len(reqArgs.Specs)),
   392  	}
   393  	if err := c.checkHasAdmin(); err != nil {
   394  		return out, errors.Trace(err)
   395  	}
   396  
   397  	for i, spec := range reqArgs.Specs {
   398  		result := &out.Results[i]
   399  		result.ModelTag = spec.ModelTag
   400  		id, err := c.initiateOneMigration(spec)
   401  		if err != nil {
   402  			result.Error = common.ServerError(err)
   403  		} else {
   404  			result.MigrationId = id
   405  		}
   406  	}
   407  	return out, nil
   408  }
   409  
   410  func (c *ControllerAPI) initiateOneMigration(spec params.MigrationSpec) (string, error) {
   411  	modelTag, err := names.ParseModelTag(spec.ModelTag)
   412  	if err != nil {
   413  		return "", errors.Annotate(err, "model tag")
   414  	}
   415  
   416  	// Ensure the model exists.
   417  	if _, err := c.state.GetModel(modelTag); err != nil {
   418  		return "", errors.Annotate(err, "unable to read model")
   419  	}
   420  
   421  	hostedState, err := c.state.ForModel(modelTag)
   422  	if err != nil {
   423  		return "", errors.Trace(err)
   424  	}
   425  	defer hostedState.Close()
   426  
   427  	// Construct target info.
   428  	specTarget := spec.TargetInfo
   429  	controllerTag, err := names.ParseControllerTag(specTarget.ControllerTag)
   430  	if err != nil {
   431  		return "", errors.Annotate(err, "controller tag")
   432  	}
   433  	authTag, err := names.ParseUserTag(specTarget.AuthTag)
   434  	if err != nil {
   435  		return "", errors.Annotate(err, "auth tag")
   436  	}
   437  	var macs []macaroon.Slice
   438  	if specTarget.Macaroons != "" {
   439  		if err := json.Unmarshal([]byte(specTarget.Macaroons), &macs); err != nil {
   440  			return "", errors.Annotate(err, "invalid macaroons")
   441  		}
   442  	}
   443  	targetInfo := coremigration.TargetInfo{
   444  		ControllerTag: controllerTag,
   445  		Addrs:         specTarget.Addrs,
   446  		CACert:        specTarget.CACert,
   447  		AuthTag:       authTag,
   448  		Password:      specTarget.Password,
   449  		Macaroons:     macs,
   450  	}
   451  
   452  	// Check if the migration is likely to succeed.
   453  	if !(spec.ExternalControl && spec.SkipInitialPrechecks) {
   454  		if err := runMigrationPrechecks(hostedState, targetInfo); err != nil {
   455  			return "", errors.Trace(err)
   456  		}
   457  	}
   458  
   459  	// Trigger the migration.
   460  	mig, err := hostedState.CreateMigration(state.MigrationSpec{
   461  		InitiatedBy:     c.apiUser,
   462  		TargetInfo:      targetInfo,
   463  		ExternalControl: spec.ExternalControl,
   464  	})
   465  	if err != nil {
   466  		return "", errors.Trace(err)
   467  	}
   468  	return mig.Id(), nil
   469  }
   470  
   471  func (c *ControllerAPI) modelStatus(tag string) (params.ModelStatus, error) {
   472  	var status params.ModelStatus
   473  	modelTag, err := names.ParseModelTag(tag)
   474  	if err != nil {
   475  		return status, errors.Trace(err)
   476  	}
   477  	st, err := c.state.ForModel(modelTag)
   478  	if err != nil {
   479  		return status, errors.Trace(err)
   480  	}
   481  	defer st.Close()
   482  
   483  	machines, err := st.AllMachines()
   484  	if err != nil {
   485  		return status, errors.Trace(err)
   486  	}
   487  
   488  	var hostedMachines []*state.Machine
   489  	for _, m := range machines {
   490  		if !m.IsManager() {
   491  			hostedMachines = append(hostedMachines, m)
   492  		}
   493  	}
   494  
   495  	applications, err := st.AllApplications()
   496  	if err != nil {
   497  		return status, errors.Trace(err)
   498  	}
   499  
   500  	model, err := st.Model()
   501  	if err != nil {
   502  		return status, errors.Trace(err)
   503  	}
   504  	if err != nil {
   505  		return status, errors.Trace(err)
   506  	}
   507  
   508  	modelMachines, err := common.ModelMachineInfo(common.NewModelManagerBackend(st))
   509  	if err != nil {
   510  		return status, errors.Trace(err)
   511  	}
   512  
   513  	return params.ModelStatus{
   514  		ModelTag:           tag,
   515  		OwnerTag:           model.Owner().String(),
   516  		Life:               params.Life(model.Life().String()),
   517  		HostedMachineCount: len(hostedMachines),
   518  		ApplicationCount:   len(applications),
   519  		Machines:           modelMachines,
   520  	}, nil
   521  }
   522  
   523  // ModifyControllerAccess changes the model access granted to users.
   524  func (c *ControllerAPI) ModifyControllerAccess(args params.ModifyControllerAccessRequest) (params.ErrorResults, error) {
   525  	result := params.ErrorResults{
   526  		Results: make([]params.ErrorResult, len(args.Changes)),
   527  	}
   528  	if len(args.Changes) == 0 {
   529  		return result, nil
   530  	}
   531  
   532  	hasPermission, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag())
   533  	if err != nil {
   534  		return result, errors.Trace(err)
   535  	}
   536  
   537  	for i, arg := range args.Changes {
   538  		if !hasPermission {
   539  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   540  			continue
   541  		}
   542  
   543  		controllerAccess := permission.Access(arg.Access)
   544  		if err := permission.ValidateControllerAccess(controllerAccess); err != nil {
   545  			result.Results[i].Error = common.ServerError(err)
   546  			continue
   547  		}
   548  
   549  		targetUserTag, err := names.ParseUserTag(arg.UserTag)
   550  		if err != nil {
   551  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify controller access"))
   552  			continue
   553  		}
   554  
   555  		result.Results[i].Error = common.ServerError(
   556  			ChangeControllerAccess(c.state, c.apiUser, targetUserTag, arg.Action, controllerAccess))
   557  	}
   558  	return result, nil
   559  }
   560  
   561  var runMigrationPrechecks = func(st *state.State, targetInfo coremigration.TargetInfo) error {
   562  	// Check model and source controller.
   563  	if err := migration.SourcePrecheck(migration.PrecheckShim(st)); err != nil {
   564  		return errors.Annotate(err, "source prechecks failed")
   565  	}
   566  
   567  	// Check target controller.
   568  	conn, err := api.Open(targetToAPIInfo(targetInfo), migration.ControllerDialOpts())
   569  	if err != nil {
   570  		return errors.Annotate(err, "connect to target controller")
   571  	}
   572  	defer conn.Close()
   573  	modelInfo, err := makeModelInfo(st)
   574  	if err != nil {
   575  		return errors.Trace(err)
   576  	}
   577  	err = migrationtarget.NewClient(conn).Prechecks(modelInfo)
   578  	return errors.Annotate(err, "target prechecks failed")
   579  }
   580  
   581  func makeModelInfo(st *state.State) (coremigration.ModelInfo, error) {
   582  	var empty coremigration.ModelInfo
   583  
   584  	model, err := st.Model()
   585  	if err != nil {
   586  		return empty, errors.Trace(err)
   587  	}
   588  	conf, err := st.ModelConfig()
   589  	if err != nil {
   590  		return empty, errors.Trace(err)
   591  	}
   592  	agentVersion, _ := conf.AgentVersion()
   593  	return coremigration.ModelInfo{
   594  		UUID:         model.UUID(),
   595  		Name:         model.Name(),
   596  		Owner:        model.Owner(),
   597  		AgentVersion: agentVersion,
   598  	}, nil
   599  }
   600  
   601  func targetToAPIInfo(ti coremigration.TargetInfo) *api.Info {
   602  	return &api.Info{
   603  		Addrs:     ti.Addrs,
   604  		CACert:    ti.CACert,
   605  		Tag:       ti.AuthTag,
   606  		Password:  ti.Password,
   607  		Macaroons: ti.Macaroons,
   608  	}
   609  }
   610  
   611  func grantControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error {
   612  	_, err := accessor.AddControllerUser(state.UserAccessSpec{User: targetUserTag, CreatedBy: apiUser, Access: access})
   613  	if errors.IsAlreadyExists(err) {
   614  		controllerTag := accessor.ControllerTag()
   615  		controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag)
   616  		if errors.IsNotFound(err) {
   617  			// Conflicts with prior check, must be inconsistent state.
   618  			err = txn.ErrExcessiveContention
   619  		}
   620  		if err != nil {
   621  			return errors.Annotate(err, "could not look up controller access for user")
   622  		}
   623  
   624  		// Only set access if greater access is being granted.
   625  		if controllerUser.Access.EqualOrGreaterControllerAccessThan(access) {
   626  			return errors.Errorf("user already has %q access or greater", access)
   627  		}
   628  		if _, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, access); err != nil {
   629  			return errors.Annotate(err, "could not set controller access for user")
   630  		}
   631  		return nil
   632  
   633  	}
   634  	if err != nil {
   635  		return errors.Trace(err)
   636  	}
   637  	return nil
   638  }
   639  
   640  func revokeControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error {
   641  	controllerTag := accessor.ControllerTag()
   642  	switch access {
   643  	case permission.LoginAccess:
   644  		// Revoking login access removes all access.
   645  		err := accessor.RemoveUserAccess(targetUserTag, controllerTag)
   646  		return errors.Annotate(err, "could not revoke controller access")
   647  	case permission.AddModelAccess:
   648  		// Revoking add-model access sets login.
   649  		controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag)
   650  		if err != nil {
   651  			return errors.Annotate(err, "could not look up controller access for user")
   652  		}
   653  		_, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, permission.LoginAccess)
   654  		return errors.Annotate(err, "could not set controller access to read-only")
   655  	case permission.SuperuserAccess:
   656  		// Revoking superuser sets add-model.
   657  		controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag)
   658  		if err != nil {
   659  			return errors.Annotate(err, "could not look up controller access for user")
   660  		}
   661  		_, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, permission.AddModelAccess)
   662  		return errors.Annotate(err, "could not set controller access to add-model")
   663  
   664  	default:
   665  		return errors.Errorf("don't know how to revoke %q access", access)
   666  	}
   667  
   668  }
   669  
   670  // ChangeControllerAccess performs the requested access grant or revoke action for the
   671  // specified user on the controller.
   672  func ChangeControllerAccess(accessor *state.State, apiUser, targetUserTag names.UserTag, action params.ControllerAction, access permission.Access) error {
   673  	switch action {
   674  	case params.GrantControllerAccess:
   675  		err := grantControllerAccess(accessor, targetUserTag, apiUser, access)
   676  		if err != nil {
   677  			return errors.Annotate(err, "could not grant controller access")
   678  		}
   679  		return nil
   680  	case params.RevokeControllerAccess:
   681  		return revokeControllerAccess(accessor, targetUserTag, apiUser, access)
   682  	default:
   683  		return errors.Errorf("unknown action %q", action)
   684  	}
   685  }
   686  
   687  func (o orderedBlockInfo) Swap(i, j int) {
   688  	o[i], o[j] = o[j], o[i]
   689  }
   690  
   691  type orderedUserModels []params.UserModel
   692  
   693  func (o orderedUserModels) Len() int {
   694  	return len(o)
   695  }
   696  
   697  func (o orderedUserModels) Less(i, j int) bool {
   698  	if o[i].Name < o[j].Name {
   699  		return true
   700  	}
   701  	if o[i].Name > o[j].Name {
   702  		return false
   703  	}
   704  
   705  	if o[i].OwnerTag < o[j].OwnerTag {
   706  		return true
   707  	}
   708  	if o[i].OwnerTag > o[j].OwnerTag {
   709  		return false
   710  	}
   711  
   712  	// Unreachable based on the rules of there not being duplicate
   713  	// environments of the same name for the same owner, but return false
   714  	// instead of panicing.
   715  	return false
   716  }
   717  
   718  func (o orderedUserModels) Swap(i, j int) {
   719  	o[i], o[j] = o[j], o[i]
   720  }