github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"sort"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names"
    14  	"github.com/juju/utils/set"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/core/migration"
    19  	"github.com/juju/juju/state"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.apiserver.controller")
    23  
    24  func init() {
    25  	common.RegisterStandardFacade("Controller", 2, NewControllerAPI)
    26  }
    27  
    28  // Controller defines the methods on the controller API end point.
    29  type Controller interface {
    30  	AllModels() (params.UserModelList, error)
    31  	DestroyController(args params.DestroyControllerArgs) error
    32  	ModelConfig() (params.ModelConfigResults, error)
    33  	ListBlockedModels() (params.ModelBlockInfoList, error)
    34  	RemoveBlocks(args params.RemoveBlocksArgs) error
    35  	WatchAllModels() (params.AllWatcherId, error)
    36  	ModelStatus(req params.Entities) (params.ModelStatusResults, error)
    37  	InitiateModelMigration(params.InitiateModelMigrationArgs) (params.InitiateModelMigrationResults, error)
    38  }
    39  
    40  // ControllerAPI implements the environment manager interface and is
    41  // the concrete implementation of the api end point.
    42  type ControllerAPI struct {
    43  	state      *state.State
    44  	authorizer common.Authorizer
    45  	apiUser    names.UserTag
    46  	resources  *common.Resources
    47  }
    48  
    49  var _ Controller = (*ControllerAPI)(nil)
    50  
    51  // NewControllerAPI creates a new api server endpoint for managing
    52  // environments.
    53  func NewControllerAPI(
    54  	st *state.State,
    55  	resources *common.Resources,
    56  	authorizer common.Authorizer,
    57  ) (*ControllerAPI, error) {
    58  	if !authorizer.AuthClient() {
    59  		return nil, errors.Trace(common.ErrPerm)
    60  	}
    61  
    62  	// Since we know this is a user tag (because AuthClient is true),
    63  	// we just do the type assertion to the UserTag.
    64  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
    65  	isAdmin, err := st.IsControllerAdministrator(apiUser)
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	// The entire end point is only accessible to controller administrators.
    70  	if !isAdmin {
    71  		return nil, errors.Trace(common.ErrPerm)
    72  	}
    73  
    74  	return &ControllerAPI{
    75  		state:      st,
    76  		authorizer: authorizer,
    77  		apiUser:    apiUser,
    78  		resources:  resources,
    79  	}, nil
    80  }
    81  
    82  // AllModels allows controller administrators to get the list of all the
    83  // environments in the controller.
    84  func (s *ControllerAPI) AllModels() (params.UserModelList, error) {
    85  	result := params.UserModelList{}
    86  
    87  	// Get all the environments that the authenticated user can see, and
    88  	// supplement that with the other environments that exist that the user
    89  	// cannot see. The reason we do this is to get the LastConnection time for
    90  	// the environments that the user is able to see, so we have consistent
    91  	// output when listing with or without --all when an admin user.
    92  	environments, err := s.state.ModelsForUser(s.apiUser)
    93  	if err != nil {
    94  		return result, errors.Trace(err)
    95  	}
    96  	visibleEnvironments := set.NewStrings()
    97  	for _, env := range environments {
    98  		lastConn, err := env.LastConnection()
    99  		if err != nil && !state.IsNeverConnectedError(err) {
   100  			return result, errors.Trace(err)
   101  		}
   102  		visibleEnvironments.Add(env.UUID())
   103  		result.UserModels = append(result.UserModels, params.UserModel{
   104  			Model: params.Model{
   105  				Name:     env.Name(),
   106  				UUID:     env.UUID(),
   107  				OwnerTag: env.Owner().String(),
   108  			},
   109  			LastConnection: &lastConn,
   110  		})
   111  	}
   112  
   113  	allEnvs, err := s.state.AllModels()
   114  	if err != nil {
   115  		return result, errors.Trace(err)
   116  	}
   117  
   118  	for _, env := range allEnvs {
   119  		if !visibleEnvironments.Contains(env.UUID()) {
   120  			result.UserModels = append(result.UserModels, params.UserModel{
   121  				Model: params.Model{
   122  					Name:     env.Name(),
   123  					UUID:     env.UUID(),
   124  					OwnerTag: env.Owner().String(),
   125  				},
   126  				// No LastConnection as this user hasn't.
   127  			})
   128  		}
   129  	}
   130  
   131  	// Sort the resulting sequence by environment name, then owner.
   132  	sort.Sort(orderedUserModels(result.UserModels))
   133  
   134  	return result, nil
   135  }
   136  
   137  // ListBlockedModels returns a list of all environments on the controller
   138  // which have a block in place.  The resulting slice is sorted by environment
   139  // name, then owner. Callers must be controller administrators to retrieve the
   140  // list.
   141  func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) {
   142  	results := params.ModelBlockInfoList{}
   143  
   144  	blocks, err := s.state.AllBlocksForController()
   145  	if err != nil {
   146  		return results, errors.Trace(err)
   147  	}
   148  
   149  	envBlocks := make(map[string][]string)
   150  	for _, block := range blocks {
   151  		uuid := block.ModelUUID()
   152  		types, ok := envBlocks[uuid]
   153  		if !ok {
   154  			types = []string{block.Type().String()}
   155  		} else {
   156  			types = append(types, block.Type().String())
   157  		}
   158  		envBlocks[uuid] = types
   159  	}
   160  
   161  	for uuid, blocks := range envBlocks {
   162  		envInfo, err := s.state.GetModel(names.NewModelTag(uuid))
   163  		if err != nil {
   164  			logger.Debugf("Unable to get name for model: %s", uuid)
   165  			continue
   166  		}
   167  		results.Models = append(results.Models, params.ModelBlockInfo{
   168  			UUID:     envInfo.UUID(),
   169  			Name:     envInfo.Name(),
   170  			OwnerTag: envInfo.Owner().String(),
   171  			Blocks:   blocks,
   172  		})
   173  	}
   174  
   175  	// Sort the resulting sequence by environment name, then owner.
   176  	sort.Sort(orderedBlockInfo(results.Models))
   177  
   178  	return results, nil
   179  }
   180  
   181  // ModelConfig returns the environment config for the controller
   182  // environment.  For information on the current environment, use
   183  // client.ModelGet
   184  func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) {
   185  	result := params.ModelConfigResults{}
   186  
   187  	controllerEnv, err := s.state.ControllerModel()
   188  	if err != nil {
   189  		return result, errors.Trace(err)
   190  	}
   191  
   192  	config, err := controllerEnv.Config()
   193  	if err != nil {
   194  		return result, errors.Trace(err)
   195  	}
   196  
   197  	result.Config = config.AllAttrs()
   198  	return result, nil
   199  }
   200  
   201  // RemoveBlocks removes all the blocks in the controller.
   202  func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error {
   203  	if !args.All {
   204  		return errors.New("not supported")
   205  	}
   206  	return errors.Trace(s.state.RemoveAllBlocksForController())
   207  }
   208  
   209  // WatchAllModels starts watching events for all models in the
   210  // controller. The returned AllWatcherId should be used with Next on the
   211  // AllModelWatcher endpoint to receive deltas.
   212  func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) {
   213  	w := c.state.WatchAllModels()
   214  	return params.AllWatcherId{
   215  		AllWatcherId: c.resources.Register(w),
   216  	}, nil
   217  }
   218  
   219  type orderedBlockInfo []params.ModelBlockInfo
   220  
   221  func (o orderedBlockInfo) Len() int {
   222  	return len(o)
   223  }
   224  
   225  func (o orderedBlockInfo) Less(i, j int) bool {
   226  	if o[i].Name < o[j].Name {
   227  		return true
   228  	}
   229  	if o[i].Name > o[j].Name {
   230  		return false
   231  	}
   232  
   233  	if o[i].OwnerTag < o[j].OwnerTag {
   234  		return true
   235  	}
   236  	if o[i].OwnerTag > o[j].OwnerTag {
   237  		return false
   238  	}
   239  
   240  	// Unreachable based on the rules of there not being duplicate
   241  	// environments of the same name for the same owner, but return false
   242  	// instead of panicing.
   243  	return false
   244  }
   245  
   246  // ModelStatus returns a summary of the environment.
   247  func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) {
   248  	envs := req.Entities
   249  	results := params.ModelStatusResults{}
   250  	status := make([]params.ModelStatus, len(envs))
   251  	for i, env := range envs {
   252  		envStatus, err := c.environStatus(env.Tag)
   253  		if err != nil {
   254  			return results, errors.Trace(err)
   255  		}
   256  		status[i] = envStatus
   257  	}
   258  	results.Results = status
   259  	return results, nil
   260  }
   261  
   262  // InitiateModelMigration attempts to begin the migration of one or
   263  // more models to other controllers.
   264  func (c *ControllerAPI) InitiateModelMigration(reqArgs params.InitiateModelMigrationArgs) (
   265  	params.InitiateModelMigrationResults, error,
   266  ) {
   267  	out := params.InitiateModelMigrationResults{
   268  		Results: make([]params.InitiateModelMigrationResult, len(reqArgs.Specs)),
   269  	}
   270  	for i, spec := range reqArgs.Specs {
   271  		result := &out.Results[i]
   272  		result.ModelTag = spec.ModelTag
   273  		id, err := c.initiateOneModelMigration(spec)
   274  		if err != nil {
   275  			result.Error = common.ServerError(err)
   276  		} else {
   277  			result.Id = id
   278  		}
   279  	}
   280  	return out, nil
   281  }
   282  
   283  func (c *ControllerAPI) initiateOneModelMigration(spec params.ModelMigrationSpec) (string, error) {
   284  	modelTag, err := names.ParseModelTag(spec.ModelTag)
   285  	if err != nil {
   286  		return "", errors.Annotate(err, "model tag")
   287  	}
   288  
   289  	// Ensure the model exists.
   290  	if _, err := c.state.GetModel(modelTag); err != nil {
   291  		return "", errors.Annotate(err, "unable to read model")
   292  	}
   293  
   294  	// Get State for model.
   295  	hostedState, err := c.state.ForModel(modelTag)
   296  	if err != nil {
   297  		return "", errors.Trace(err)
   298  	}
   299  	defer hostedState.Close()
   300  
   301  	// Start the migration.
   302  	targetInfo := spec.TargetInfo
   303  
   304  	controllerTag, err := names.ParseModelTag(targetInfo.ControllerTag)
   305  	if err != nil {
   306  		return "", errors.Annotate(err, "controller tag")
   307  	}
   308  	authTag, err := names.ParseUserTag(targetInfo.AuthTag)
   309  	if err != nil {
   310  		return "", errors.Annotate(err, "auth tag")
   311  	}
   312  
   313  	args := state.ModelMigrationSpec{
   314  		InitiatedBy: c.apiUser,
   315  		TargetInfo: migration.TargetInfo{
   316  			ControllerTag: controllerTag,
   317  			Addrs:         targetInfo.Addrs,
   318  			CACert:        targetInfo.CACert,
   319  			AuthTag:       authTag,
   320  			Password:      targetInfo.Password,
   321  		},
   322  	}
   323  	mig, err := hostedState.CreateModelMigration(args)
   324  	if err != nil {
   325  		return "", errors.Trace(err)
   326  	}
   327  	return mig.Id(), nil
   328  }
   329  
   330  func (c *ControllerAPI) environStatus(tag string) (params.ModelStatus, error) {
   331  	var status params.ModelStatus
   332  	modelTag, err := names.ParseModelTag(tag)
   333  	if err != nil {
   334  		return status, errors.Trace(err)
   335  	}
   336  	st, err := c.state.ForModel(modelTag)
   337  	if err != nil {
   338  		return status, errors.Trace(err)
   339  	}
   340  	defer st.Close()
   341  
   342  	machines, err := st.AllMachines()
   343  	if err != nil {
   344  		return status, errors.Trace(err)
   345  	}
   346  
   347  	var hostedMachines []*state.Machine
   348  	for _, m := range machines {
   349  		if !m.IsManager() {
   350  			hostedMachines = append(hostedMachines, m)
   351  		}
   352  	}
   353  
   354  	services, err := st.AllServices()
   355  	if err != nil {
   356  		return status, errors.Trace(err)
   357  	}
   358  
   359  	env, err := st.Model()
   360  	if err != nil {
   361  		return status, errors.Trace(err)
   362  	}
   363  	if err != nil {
   364  		return status, errors.Trace(err)
   365  	}
   366  
   367  	return params.ModelStatus{
   368  		ModelTag:           tag,
   369  		OwnerTag:           env.Owner().String(),
   370  		Life:               params.Life(env.Life().String()),
   371  		HostedMachineCount: len(hostedMachines),
   372  		ServiceCount:       len(services),
   373  	}, nil
   374  }
   375  
   376  func (o orderedBlockInfo) Swap(i, j int) {
   377  	o[i], o[j] = o[j], o[i]
   378  }
   379  
   380  type orderedUserModels []params.UserModel
   381  
   382  func (o orderedUserModels) Len() int {
   383  	return len(o)
   384  }
   385  
   386  func (o orderedUserModels) Less(i, j int) bool {
   387  	if o[i].Name < o[j].Name {
   388  		return true
   389  	}
   390  	if o[i].Name > o[j].Name {
   391  		return false
   392  	}
   393  
   394  	if o[i].OwnerTag < o[j].OwnerTag {
   395  		return true
   396  	}
   397  	if o[i].OwnerTag > o[j].OwnerTag {
   398  		return false
   399  	}
   400  
   401  	// Unreachable based on the rules of there not being duplicate
   402  	// environments of the same name for the same owner, but return false
   403  	// instead of panicing.
   404  	return false
   405  }
   406  
   407  func (o orderedUserModels) Swap(i, j int) {
   408  	o[i], o[j] = o[j], o[i]
   409  }