github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/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  	"gopkg.in/juju/names.v2"
    16  	"gopkg.in/macaroon.v2-unstable"
    17  
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/api/migrationtarget"
    20  	"github.com/juju/juju/apiserver/common"
    21  	"github.com/juju/juju/apiserver/common/cloudspec"
    22  	"github.com/juju/juju/apiserver/facade"
    23  	"github.com/juju/juju/apiserver/params"
    24  	corecontroller "github.com/juju/juju/controller"
    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/pubsub/controller"
    29  	"github.com/juju/juju/state"
    30  )
    31  
    32  var logger = loggo.GetLogger("juju.apiserver.controller")
    33  
    34  // ControllerAPI provides the Controller API.
    35  type ControllerAPI struct {
    36  	*common.ControllerConfigAPI
    37  	*common.ModelStatusAPI
    38  	cloudspec.CloudSpecAPI
    39  
    40  	state      *state.State
    41  	statePool  *state.StatePool
    42  	authorizer facade.Authorizer
    43  	apiUser    names.UserTag
    44  	resources  facade.Resources
    45  	presence   facade.Presence
    46  	hub        facade.Hub
    47  }
    48  
    49  // ControllerAPIv6 provides the v6 Controller API. The only difference
    50  // between this and v7 is that v6 doesn't have the IdentityProviderURL method.
    51  type ControllerAPIv6 struct {
    52  	*ControllerAPI
    53  }
    54  
    55  // ControllerAPIv5 provides the v5 Controller API. The only difference
    56  // between this and v6 is that v5 doesn't have the MongoVersion method.
    57  type ControllerAPIv5 struct {
    58  	*ControllerAPIv6
    59  }
    60  
    61  // ControllerAPIv4 provides the v4 Controller API. The only difference
    62  // between this and v5 is that v4 doesn't have the
    63  // UpdateControllerConfig method.
    64  type ControllerAPIv4 struct {
    65  	*ControllerAPIv5
    66  }
    67  
    68  // ControllerAPIv3 provides the v3 Controller API.
    69  type ControllerAPIv3 struct {
    70  	*ControllerAPIv4
    71  }
    72  
    73  // NewControllerAPIv7 creates a new ControllerAPIv7.
    74  func NewControllerAPIv7(ctx facade.Context) (*ControllerAPI, error) {
    75  	st := ctx.State()
    76  	authorizer := ctx.Auth()
    77  	pool := ctx.StatePool()
    78  	resources := ctx.Resources()
    79  	presence := ctx.Presence()
    80  	hub := ctx.Hub()
    81  
    82  	return NewControllerAPI(
    83  		st,
    84  		pool,
    85  		authorizer,
    86  		resources,
    87  		presence,
    88  		hub,
    89  	)
    90  }
    91  
    92  // NewControllerAPIv6 creates a new ControllerAPIv6.
    93  func NewControllerAPIv6(ctx facade.Context) (*ControllerAPIv6, error) {
    94  	v7, err := NewControllerAPIv7(ctx)
    95  	if err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  	return &ControllerAPIv6{v7}, nil
    99  }
   100  
   101  // NewControllerAPIv5 creates a new ControllerAPIv5.
   102  func NewControllerAPIv5(ctx facade.Context) (*ControllerAPIv5, error) {
   103  	v6, err := NewControllerAPIv6(ctx)
   104  	if err != nil {
   105  		return nil, errors.Trace(err)
   106  	}
   107  	return &ControllerAPIv5{v6}, nil
   108  }
   109  
   110  // NewControllerAPIv4 creates a new ControllerAPIv4.
   111  func NewControllerAPIv4(ctx facade.Context) (*ControllerAPIv4, error) {
   112  	v5, err := NewControllerAPIv5(ctx)
   113  	if err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	return &ControllerAPIv4{v5}, nil
   117  }
   118  
   119  // NewControllerAPIv3 creates a new ControllerAPIv3.
   120  func NewControllerAPIv3(ctx facade.Context) (*ControllerAPIv3, error) {
   121  	v4, err := NewControllerAPIv4(ctx)
   122  	if err != nil {
   123  		return nil, errors.Trace(err)
   124  	}
   125  	return &ControllerAPIv3{v4}, nil
   126  }
   127  
   128  // NewControllerAPI creates a new api server endpoint for operations
   129  // on a controller.
   130  func NewControllerAPI(
   131  	st *state.State,
   132  	pool *state.StatePool,
   133  	authorizer facade.Authorizer,
   134  	resources facade.Resources,
   135  	presence facade.Presence,
   136  	hub facade.Hub,
   137  ) (*ControllerAPI, error) {
   138  	if !authorizer.AuthClient() {
   139  		return nil, errors.Trace(common.ErrPerm)
   140  	}
   141  
   142  	// Since we know this is a user tag (because AuthClient is true),
   143  	// we just do the type assertion to the UserTag.
   144  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
   145  
   146  	model, err := st.Model()
   147  	if err != nil {
   148  		return nil, errors.Trace(err)
   149  	}
   150  	return &ControllerAPI{
   151  		ControllerConfigAPI: common.NewStateControllerConfig(st),
   152  		ModelStatusAPI: common.NewModelStatusAPI(
   153  			common.NewModelManagerBackend(model, pool),
   154  			authorizer,
   155  			apiUser,
   156  		),
   157  		CloudSpecAPI: cloudspec.NewCloudSpec(
   158  			cloudspec.MakeCloudSpecGetter(pool),
   159  			common.AuthFuncForTag(model.ModelTag()),
   160  		),
   161  		state:      st,
   162  		statePool:  pool,
   163  		authorizer: authorizer,
   164  		apiUser:    apiUser,
   165  		resources:  resources,
   166  		presence:   presence,
   167  		hub:        hub,
   168  	}, nil
   169  }
   170  
   171  func (c *ControllerAPI) checkHasAdmin() error {
   172  	isAdmin, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag())
   173  	if err != nil {
   174  		return errors.Trace(err)
   175  	}
   176  	if !isAdmin {
   177  		return common.ServerError(common.ErrPerm)
   178  	}
   179  	return nil
   180  }
   181  
   182  // IdentityProviderURL isn't on the v6 API.
   183  func (c *ControllerAPIv6) IdentityProviderURL() {}
   184  
   185  // IdentityProviderURL returns the URL of the configured external identity
   186  // provider for this controller or an empty string if no external identity
   187  // provider has been configured when the controller was bootstrapped.
   188  //
   189  // NOTE: the implementation intentionally does not check for SuperuserAccess
   190  // as the URL is known even to users with login access.
   191  func (c *ControllerAPI) IdentityProviderURL() (params.StringResult, error) {
   192  	var result params.StringResult
   193  
   194  	cfgRes, err := c.ControllerConfig()
   195  	if err != nil {
   196  		return result, errors.Trace(err)
   197  	}
   198  
   199  	if cfgRes.Config != nil {
   200  		result.Result = corecontroller.Config(cfgRes.Config).IdentityURL()
   201  	}
   202  	return result, nil
   203  }
   204  
   205  // ModelStatus is a legacy method call to ensure that we preserve
   206  // backward compatibility.
   207  // TODO (anastasiamac 2017-10-26) This should be made obsolete/removed.
   208  func (c *ControllerAPIv3) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
   209  	results, err := c.ModelStatusAPI.ModelStatus(req)
   210  	if err != nil {
   211  		return params.ModelStatusResults{}, err
   212  	}
   213  
   214  	for _, r := range results.Results {
   215  		if r.Error != nil {
   216  			return params.ModelStatusResults{Results: make([]params.ModelStatus, len(req.Entities))}, errors.Trace(r.Error)
   217  		}
   218  	}
   219  	return results, nil
   220  }
   221  
   222  // MongoVersion isn't on the v5 API.
   223  func (c *ControllerAPIv5) MongoVersion() {}
   224  
   225  // MongoVersion allows the introspection of the mongo version per controller
   226  func (c *ControllerAPI) MongoVersion() (params.StringResult, error) {
   227  	result := params.StringResult{}
   228  	if err := c.checkHasAdmin(); err != nil {
   229  		return result, errors.Trace(err)
   230  	}
   231  	version, err := c.state.MongoVersion()
   232  	if err != nil {
   233  		return result, errors.Trace(err)
   234  	}
   235  	result.Result = version
   236  	return result, nil
   237  }
   238  
   239  // AllModels allows controller administrators to get the list of all the
   240  // models in the controller.
   241  func (c *ControllerAPI) AllModels() (params.UserModelList, error) {
   242  	result := params.UserModelList{}
   243  	if err := c.checkHasAdmin(); err != nil {
   244  		return result, errors.Trace(err)
   245  	}
   246  
   247  	modelUUIDs, err := c.state.AllModelUUIDs()
   248  	if err != nil {
   249  		return result, errors.Trace(err)
   250  	}
   251  	for _, modelUUID := range modelUUIDs {
   252  		st, err := c.statePool.Get(modelUUID)
   253  		if err != nil {
   254  			// This model could have been removed.
   255  			if errors.IsNotFound(err) {
   256  				continue
   257  			}
   258  			return result, errors.Trace(err)
   259  		}
   260  		defer st.Release()
   261  
   262  		model, err := st.Model()
   263  		if err != nil {
   264  			return result, errors.Trace(err)
   265  		}
   266  
   267  		userModel := params.UserModel{
   268  			Model: params.Model{
   269  				Name:     model.Name(),
   270  				UUID:     model.UUID(),
   271  				Type:     string(model.Type()),
   272  				OwnerTag: model.Owner().String(),
   273  			},
   274  		}
   275  
   276  		lastConn, err := model.LastModelConnection(c.apiUser)
   277  		if err != nil {
   278  			if !state.IsNeverConnectedError(err) {
   279  				return result, errors.Trace(err)
   280  			}
   281  		} else {
   282  			userModel.LastConnection = &lastConn
   283  		}
   284  
   285  		result.UserModels = append(result.UserModels, userModel)
   286  	}
   287  
   288  	return result, nil
   289  }
   290  
   291  // ListBlockedModels returns a list of all models on the controller
   292  // which have a block in place.  The resulting slice is sorted by model
   293  // name, then owner. Callers must be controller administrators to retrieve the
   294  // list.
   295  func (c *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) {
   296  	results := params.ModelBlockInfoList{}
   297  	if err := c.checkHasAdmin(); err != nil {
   298  		return results, errors.Trace(err)
   299  	}
   300  	blocks, err := c.state.AllBlocksForController()
   301  	if err != nil {
   302  		return results, errors.Trace(err)
   303  	}
   304  
   305  	modelBlocks := make(map[string][]string)
   306  	for _, block := range blocks {
   307  		uuid := block.ModelUUID()
   308  		types, ok := modelBlocks[uuid]
   309  		if !ok {
   310  			types = []string{block.Type().String()}
   311  		} else {
   312  			types = append(types, block.Type().String())
   313  		}
   314  		modelBlocks[uuid] = types
   315  	}
   316  
   317  	for uuid, blocks := range modelBlocks {
   318  		model, ph, err := c.statePool.GetModel(uuid)
   319  		if err != nil {
   320  			logger.Debugf("unable to retrieve model %s: %v", uuid, err)
   321  			continue
   322  		}
   323  		results.Models = append(results.Models, params.ModelBlockInfo{
   324  			UUID:     model.UUID(),
   325  			Name:     model.Name(),
   326  			OwnerTag: model.Owner().String(),
   327  			Blocks:   blocks,
   328  		})
   329  		ph.Release()
   330  	}
   331  
   332  	// Sort the resulting sequence by model name, then owner.
   333  	sort.Sort(orderedBlockInfo(results.Models))
   334  	return results, nil
   335  }
   336  
   337  // ModelConfig returns the model config for the controller
   338  // model.  For information on the current model, use
   339  // client.ModelGet
   340  func (c *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) {
   341  	result := params.ModelConfigResults{}
   342  	if err := c.checkHasAdmin(); err != nil {
   343  		return result, errors.Trace(err)
   344  	}
   345  
   346  	controllerState := c.statePool.SystemState()
   347  	controllerModel, err := controllerState.Model()
   348  	if err != nil {
   349  		return result, errors.Trace(err)
   350  	}
   351  	cfg, err := controllerModel.Config()
   352  	if err != nil {
   353  		return result, errors.Trace(err)
   354  	}
   355  
   356  	result.Config = make(map[string]params.ConfigValue)
   357  	for name, val := range cfg.AllAttrs() {
   358  		result.Config[name] = params.ConfigValue{
   359  			Value: val,
   360  		}
   361  	}
   362  	return result, nil
   363  }
   364  
   365  // HostedModelConfigs returns all the information that the client needs in
   366  // order to connect directly with the host model's provider and destroy it
   367  // directly.
   368  func (c *ControllerAPI) HostedModelConfigs() (params.HostedModelConfigsResults, error) {
   369  	result := params.HostedModelConfigsResults{}
   370  	if err := c.checkHasAdmin(); err != nil {
   371  		return result, errors.Trace(err)
   372  	}
   373  
   374  	modelUUIDs, err := c.state.AllModelUUIDs()
   375  	if err != nil {
   376  		return result, errors.Trace(err)
   377  	}
   378  
   379  	for _, modelUUID := range modelUUIDs {
   380  		if modelUUID == c.state.ControllerModelUUID() {
   381  			continue
   382  		}
   383  		st, err := c.statePool.Get(modelUUID)
   384  		if err != nil {
   385  			// This model could have been removed.
   386  			if errors.IsNotFound(err) {
   387  				continue
   388  			}
   389  			return result, errors.Trace(err)
   390  		}
   391  		defer st.Release()
   392  		model, err := st.Model()
   393  		if err != nil {
   394  			return result, errors.Trace(err)
   395  		}
   396  
   397  		config := params.HostedModelConfig{
   398  			Name:     model.Name(),
   399  			OwnerTag: model.Owner().String(),
   400  		}
   401  		modelConf, err := model.Config()
   402  		if err != nil {
   403  			config.Error = common.ServerError(err)
   404  		} else {
   405  			config.Config = modelConf.AllAttrs()
   406  		}
   407  		cloudSpec := c.GetCloudSpec(model.ModelTag())
   408  		if config.Error == nil {
   409  			config.CloudSpec = cloudSpec.Result
   410  			config.Error = cloudSpec.Error
   411  		}
   412  		result.Models = append(result.Models, config)
   413  	}
   414  
   415  	return result, nil
   416  }
   417  
   418  // RemoveBlocks removes all the blocks in the controller.
   419  func (c *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error {
   420  	if err := c.checkHasAdmin(); err != nil {
   421  		return errors.Trace(err)
   422  	}
   423  
   424  	if !args.All {
   425  		return errors.New("not supported")
   426  	}
   427  	return errors.Trace(c.state.RemoveAllBlocksForController())
   428  }
   429  
   430  // WatchAllModels starts watching events for all models in the
   431  // controller. The returned AllWatcherId should be used with Next on the
   432  // AllModelWatcher endpoint to receive deltas.
   433  func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) {
   434  	if err := c.checkHasAdmin(); err != nil {
   435  		return params.AllWatcherId{}, errors.Trace(err)
   436  	}
   437  	w := c.state.WatchAllModels(c.statePool)
   438  	return params.AllWatcherId{
   439  		AllWatcherId: c.resources.Register(w),
   440  	}, nil
   441  }
   442  
   443  // GetControllerAccess returns the level of access the specified users
   444  // have on the controller.
   445  func (c *ControllerAPI) GetControllerAccess(req params.Entities) (params.UserAccessResults, error) {
   446  	results := params.UserAccessResults{}
   447  	isAdmin, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag())
   448  	if err != nil {
   449  		return results, errors.Trace(err)
   450  	}
   451  
   452  	users := req.Entities
   453  	results.Results = make([]params.UserAccessResult, len(users))
   454  	for i, user := range users {
   455  		userTag, err := names.ParseUserTag(user.Tag)
   456  		if err != nil {
   457  			results.Results[i].Error = common.ServerError(err)
   458  			continue
   459  		}
   460  		if !isAdmin && !c.authorizer.AuthOwner(userTag) {
   461  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   462  			continue
   463  		}
   464  		access, err := c.state.UserPermission(userTag, c.state.ControllerTag())
   465  		if err != nil {
   466  			results.Results[i].Error = common.ServerError(err)
   467  			continue
   468  		}
   469  		results.Results[i].Result = &params.UserAccess{
   470  			Access:  string(access),
   471  			UserTag: userTag.String()}
   472  	}
   473  	return results, nil
   474  }
   475  
   476  // InitiateMigration attempts to begin the migration of one or
   477  // more models to other controllers.
   478  func (c *ControllerAPI) InitiateMigration(reqArgs params.InitiateMigrationArgs) (
   479  	params.InitiateMigrationResults, error,
   480  ) {
   481  	out := params.InitiateMigrationResults{
   482  		Results: make([]params.InitiateMigrationResult, len(reqArgs.Specs)),
   483  	}
   484  	if err := c.checkHasAdmin(); err != nil {
   485  		return out, errors.Trace(err)
   486  	}
   487  
   488  	for i, spec := range reqArgs.Specs {
   489  		result := &out.Results[i]
   490  		result.ModelTag = spec.ModelTag
   491  		id, err := c.initiateOneMigration(spec)
   492  		if err != nil {
   493  			result.Error = common.ServerError(err)
   494  		} else {
   495  			result.MigrationId = id
   496  		}
   497  	}
   498  	return out, nil
   499  }
   500  
   501  func (c *ControllerAPI) initiateOneMigration(spec params.MigrationSpec) (string, error) {
   502  	modelTag, err := names.ParseModelTag(spec.ModelTag)
   503  	if err != nil {
   504  		return "", errors.Annotate(err, "model tag")
   505  	}
   506  
   507  	// Ensure the model exists.
   508  	if modelExists, err := c.state.ModelExists(modelTag.Id()); err != nil {
   509  		return "", errors.Annotate(err, "reading model")
   510  	} else if !modelExists {
   511  		return "", errors.NotFoundf("model")
   512  	}
   513  
   514  	hostedState, err := c.statePool.Get(modelTag.Id())
   515  	if err != nil {
   516  		return "", errors.Trace(err)
   517  	}
   518  	defer hostedState.Release()
   519  
   520  	// Construct target info.
   521  	specTarget := spec.TargetInfo
   522  	controllerTag, err := names.ParseControllerTag(specTarget.ControllerTag)
   523  	if err != nil {
   524  		return "", errors.Annotate(err, "controller tag")
   525  	}
   526  	authTag, err := names.ParseUserTag(specTarget.AuthTag)
   527  	if err != nil {
   528  		return "", errors.Annotate(err, "auth tag")
   529  	}
   530  	var macs []macaroon.Slice
   531  	if specTarget.Macaroons != "" {
   532  		if err := json.Unmarshal([]byte(specTarget.Macaroons), &macs); err != nil {
   533  			return "", errors.Annotate(err, "invalid macaroons")
   534  		}
   535  	}
   536  	targetInfo := coremigration.TargetInfo{
   537  		ControllerTag: controllerTag,
   538  		Addrs:         specTarget.Addrs,
   539  		CACert:        specTarget.CACert,
   540  		AuthTag:       authTag,
   541  		Password:      specTarget.Password,
   542  		Macaroons:     macs,
   543  	}
   544  
   545  	// Check if the migration is likely to succeed.
   546  	if err := runMigrationPrechecks(hostedState.State, c.statePool.SystemState(), &targetInfo, c.presence); err != nil {
   547  		return "", errors.Trace(err)
   548  	}
   549  
   550  	// Trigger the migration.
   551  	mig, err := hostedState.CreateMigration(state.MigrationSpec{
   552  		InitiatedBy: c.apiUser,
   553  		TargetInfo:  targetInfo,
   554  	})
   555  	if err != nil {
   556  		return "", errors.Trace(err)
   557  	}
   558  	return mig.Id(), nil
   559  }
   560  
   561  // ModifyControllerAccess changes the model access granted to users.
   562  func (c *ControllerAPI) ModifyControllerAccess(args params.ModifyControllerAccessRequest) (params.ErrorResults, error) {
   563  	result := params.ErrorResults{
   564  		Results: make([]params.ErrorResult, len(args.Changes)),
   565  	}
   566  	if len(args.Changes) == 0 {
   567  		return result, nil
   568  	}
   569  
   570  	hasPermission, err := c.authorizer.HasPermission(permission.SuperuserAccess, c.state.ControllerTag())
   571  	if err != nil {
   572  		return result, errors.Trace(err)
   573  	}
   574  
   575  	for i, arg := range args.Changes {
   576  		if !hasPermission {
   577  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   578  			continue
   579  		}
   580  
   581  		controllerAccess := permission.Access(arg.Access)
   582  		if err := permission.ValidateControllerAccess(controllerAccess); err != nil {
   583  			// TODO(wallyworld) - remove in Juju 3.0
   584  			// Backwards compatibility requires us to accept add-model.
   585  			if controllerAccess != permission.AddModelAccess {
   586  				result.Results[i].Error = common.ServerError(err)
   587  				continue
   588  			}
   589  		}
   590  
   591  		targetUserTag, err := names.ParseUserTag(arg.UserTag)
   592  		if err != nil {
   593  			result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify controller access"))
   594  			continue
   595  		}
   596  
   597  		result.Results[i].Error = common.ServerError(
   598  			ChangeControllerAccess(c.state, c.apiUser, targetUserTag, arg.Action, controllerAccess))
   599  	}
   600  	return result, nil
   601  }
   602  
   603  // ConfigSet changes the value of specified controller configuration
   604  // settings. Only some settings can be changed after bootstrap.
   605  // Settings that aren't specified in the params are left unchanged.
   606  func (c *ControllerAPI) ConfigSet(args params.ControllerConfigSet) error {
   607  	if err := c.checkHasAdmin(); err != nil {
   608  		return errors.Trace(err)
   609  	}
   610  	if err := c.state.UpdateControllerConfig(args.Config, nil); err != nil {
   611  		return errors.Trace(err)
   612  	}
   613  	// TODO(thumper): add a version to controller config to allow for
   614  	// simultaneous updates and races in publishing, potentially across
   615  	// HA servers.
   616  	cfg, err := c.state.ControllerConfig()
   617  	if err != nil {
   618  		return errors.Trace(err)
   619  	}
   620  	if _, err := c.hub.Publish(
   621  		controller.ConfigChanged,
   622  		controller.ConfigChangedMessage{cfg}); err != nil {
   623  		return errors.Trace(err)
   624  	}
   625  	return nil
   626  }
   627  
   628  // Mask the ConfigSet method from the v4 API. The API reflection code
   629  // in rpc/rpcreflect/type.go:newMethod skips 2-argument methods, so
   630  // this removes the method as far as the RPC machinery is concerned.
   631  
   632  // ConfigSet isn't on the v4 API.
   633  func (c *ControllerAPIv4) ConfigSet(_, _ struct{}) {}
   634  
   635  // runMigrationPrechecks runs prechecks on the migration and updates
   636  // information in targetInfo as needed based on information
   637  // retrieved from the target controller.
   638  var runMigrationPrechecks = func(st, ctlrSt *state.State, targetInfo *coremigration.TargetInfo, presence facade.Presence) error {
   639  	// Check model and source controller.
   640  	backend, err := migration.PrecheckShim(st, ctlrSt)
   641  	if err != nil {
   642  		return errors.Annotate(err, "creating backend")
   643  	}
   644  	modelPresence := presence.ModelPresence(st.ModelUUID())
   645  	controllerPresence := presence.ModelPresence(ctlrSt.ModelUUID())
   646  	if err := migration.SourcePrecheck(backend, modelPresence, controllerPresence); err != nil {
   647  		return errors.Annotate(err, "source prechecks failed")
   648  	}
   649  
   650  	// Check target controller.
   651  	conn, err := api.Open(targetToAPIInfo(targetInfo), migration.ControllerDialOpts())
   652  	if err != nil {
   653  		return errors.Annotate(err, "connect to target controller")
   654  	}
   655  	defer conn.Close()
   656  	modelInfo, err := makeModelInfo(st, ctlrSt)
   657  	if err != nil {
   658  		return errors.Trace(err)
   659  	}
   660  	client := migrationtarget.NewClient(conn)
   661  	if targetInfo.CACert == "" {
   662  		targetInfo.CACert, err = client.CACert()
   663  		if err != nil {
   664  			if !params.IsCodeNotImplemented(err) {
   665  				return errors.Annotatef(err, "cannot retrieve CA certificate")
   666  			}
   667  			// If the call's not implemented, it indicates an earlier version
   668  			// of the controller, which we can't migrate to.
   669  			return errors.New("controller API version is too old")
   670  		}
   671  	}
   672  	err = client.Prechecks(modelInfo)
   673  	return errors.Annotate(err, "target prechecks failed")
   674  }
   675  
   676  func makeModelInfo(st, ctlrSt *state.State) (coremigration.ModelInfo, error) {
   677  	var empty coremigration.ModelInfo
   678  
   679  	model, err := st.Model()
   680  	if err != nil {
   681  		return empty, errors.Trace(err)
   682  	}
   683  
   684  	// Retrieve agent version for the model.
   685  	conf, err := model.ModelConfig()
   686  	if err != nil {
   687  		return empty, errors.Trace(err)
   688  	}
   689  	agentVersion, _ := conf.AgentVersion()
   690  
   691  	// Retrieve agent version for the controller.
   692  	controllerModel, err := ctlrSt.Model()
   693  	if err != nil {
   694  		return empty, errors.Trace(err)
   695  	}
   696  	controllerConfig, err := controllerModel.Config()
   697  	if err != nil {
   698  		return empty, errors.Trace(err)
   699  	}
   700  	controllerVersion, _ := controllerConfig.AgentVersion()
   701  
   702  	return coremigration.ModelInfo{
   703  		UUID:                   model.UUID(),
   704  		Name:                   model.Name(),
   705  		Owner:                  model.Owner(),
   706  		AgentVersion:           agentVersion,
   707  		ControllerAgentVersion: controllerVersion,
   708  	}, nil
   709  }
   710  
   711  func targetToAPIInfo(ti *coremigration.TargetInfo) *api.Info {
   712  	return &api.Info{
   713  		Addrs:     ti.Addrs,
   714  		CACert:    ti.CACert,
   715  		Tag:       ti.AuthTag,
   716  		Password:  ti.Password,
   717  		Macaroons: ti.Macaroons,
   718  	}
   719  }
   720  
   721  // grantControllerCloudAccess exists for backwards compatibility since older clients
   722  // still set add-model on the controller rather than the controller cloud.
   723  func grantControllerCloudAccess(accessor *state.State, targetUserTag names.UserTag, access permission.Access) error {
   724  	controllerInfo, err := accessor.ControllerInfo()
   725  	if err != nil {
   726  		return errors.Trace(err)
   727  	}
   728  	cloud := controllerInfo.CloudName
   729  	err = accessor.CreateCloudAccess(cloud, targetUserTag, access)
   730  	if errors.IsAlreadyExists(err) {
   731  		cloudAccess, err := accessor.GetCloudAccess(cloud, targetUserTag)
   732  		if errors.IsNotFound(err) {
   733  			// Conflicts with prior check, must be inconsistent state.
   734  			err = txn.ErrExcessiveContention
   735  		}
   736  		if err != nil {
   737  			return errors.Annotate(err, "could not look up cloud access for user")
   738  		}
   739  
   740  		// Only set access if greater access is being granted.
   741  		if cloudAccess.EqualOrGreaterCloudAccessThan(access) {
   742  			return errors.Errorf("user already has %q access or greater", access)
   743  		}
   744  		if _, err = accessor.SetUserAccess(targetUserTag, names.NewCloudTag(cloud), access); err != nil {
   745  			return errors.Annotate(err, "could not set cloud access for user")
   746  		}
   747  		return nil
   748  
   749  	}
   750  	if err != nil {
   751  		return errors.Trace(err)
   752  	}
   753  	return nil
   754  }
   755  
   756  func grantControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error {
   757  	// TODO(wallyworld) - remove in Juju 3.0
   758  	// Older clients still use the controller facade to manage add-model access.
   759  	if access == permission.AddModelAccess {
   760  		return grantControllerCloudAccess(accessor, targetUserTag, access)
   761  	}
   762  
   763  	_, err := accessor.AddControllerUser(state.UserAccessSpec{User: targetUserTag, CreatedBy: apiUser, Access: access})
   764  	if errors.IsAlreadyExists(err) {
   765  		controllerTag := accessor.ControllerTag()
   766  		controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag)
   767  		if errors.IsNotFound(err) {
   768  			// Conflicts with prior check, must be inconsistent state.
   769  			err = txn.ErrExcessiveContention
   770  		}
   771  		if err != nil {
   772  			return errors.Annotate(err, "could not look up controller access for user")
   773  		}
   774  
   775  		// Only set access if greater access is being granted.
   776  		if controllerUser.Access.EqualOrGreaterControllerAccessThan(access) {
   777  			return errors.Errorf("user already has %q access or greater", access)
   778  		}
   779  		if _, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, access); err != nil {
   780  			return errors.Annotate(err, "could not set controller access for user")
   781  		}
   782  		return nil
   783  
   784  	}
   785  	if err != nil {
   786  		return errors.Trace(err)
   787  	}
   788  	return nil
   789  }
   790  
   791  func revokeControllerAccess(accessor *state.State, targetUserTag, apiUser names.UserTag, access permission.Access) error {
   792  	// TODO(wallyworld) - remove in Juju 3.0
   793  	// Older clients still use the controller facade to manage add-model access.
   794  	if access == permission.AddModelAccess {
   795  		controllerInfo, err := accessor.ControllerInfo()
   796  		if err != nil {
   797  			return errors.Trace(err)
   798  		}
   799  		return accessor.RemoveCloudAccess(controllerInfo.CloudName, targetUserTag)
   800  	}
   801  
   802  	controllerTag := accessor.ControllerTag()
   803  	switch access {
   804  	case permission.LoginAccess:
   805  		// Revoking login access removes all access.
   806  		err := accessor.RemoveUserAccess(targetUserTag, controllerTag)
   807  		return errors.Annotate(err, "could not revoke controller access")
   808  	case permission.SuperuserAccess:
   809  		// Revoking superuser sets login.
   810  		controllerUser, err := accessor.UserAccess(targetUserTag, controllerTag)
   811  		if err != nil {
   812  			return errors.Annotate(err, "could not look up controller access for user")
   813  		}
   814  		_, err = accessor.SetUserAccess(controllerUser.UserTag, controllerUser.Object, permission.LoginAccess)
   815  		return errors.Annotate(err, "could not set controller access to login")
   816  
   817  	default:
   818  		return errors.Errorf("don't know how to revoke %q access", access)
   819  	}
   820  }
   821  
   822  // ChangeControllerAccess performs the requested access grant or revoke action for the
   823  // specified user on the controller.
   824  func ChangeControllerAccess(accessor *state.State, apiUser, targetUserTag names.UserTag, action params.ControllerAction, access permission.Access) error {
   825  	switch action {
   826  	case params.GrantControllerAccess:
   827  		err := grantControllerAccess(accessor, targetUserTag, apiUser, access)
   828  		if err != nil {
   829  			return errors.Annotate(err, "could not grant controller access")
   830  		}
   831  		return nil
   832  	case params.RevokeControllerAccess:
   833  		return revokeControllerAccess(accessor, targetUserTag, apiUser, access)
   834  	default:
   835  		return errors.Errorf("unknown action %q", action)
   836  	}
   837  }
   838  
   839  type orderedBlockInfo []params.ModelBlockInfo
   840  
   841  func (o orderedBlockInfo) Len() int {
   842  	return len(o)
   843  }
   844  
   845  func (o orderedBlockInfo) Less(i, j int) bool {
   846  	if o[i].Name < o[j].Name {
   847  		return true
   848  	}
   849  	if o[i].Name > o[j].Name {
   850  		return false
   851  	}
   852  
   853  	if o[i].OwnerTag < o[j].OwnerTag {
   854  		return true
   855  	}
   856  	if o[i].OwnerTag > o[j].OwnerTag {
   857  		return false
   858  	}
   859  
   860  	// Unreachable based on the rules of there not being duplicate
   861  	// models of the same name for the same owner, but return false
   862  	// instead of panicing.
   863  	return false
   864  }
   865  
   866  func (o orderedBlockInfo) Swap(i, j int) {
   867  	o[i], o[j] = o[j], o[i]
   868  }