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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller
     5  
     6  import (
     7  	"encoding/json"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/juju/names.v2"
    11  	"gopkg.in/macaroon.v1"
    12  
    13  	"github.com/juju/juju/api"
    14  	"github.com/juju/juju/api/base"
    15  	"github.com/juju/juju/api/common"
    16  	"github.com/juju/juju/api/common/cloudspec"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/permission"
    20  )
    21  
    22  // Client provides methods that the Juju client command uses to interact
    23  // with the Juju controller.
    24  type Client struct {
    25  	base.ClientFacade
    26  	facade base.FacadeCaller
    27  	*common.ControllerConfigAPI
    28  	*cloudspec.CloudSpecAPI
    29  }
    30  
    31  // NewClient creates a new `Client` based on an existing authenticated API
    32  // connection.
    33  func NewClient(st base.APICallCloser) *Client {
    34  	frontend, backend := base.NewClientFacade(st, "Controller")
    35  	return &Client{
    36  		ClientFacade:        frontend,
    37  		facade:              backend,
    38  		ControllerConfigAPI: common.NewControllerConfig(backend),
    39  		CloudSpecAPI:        cloudspec.NewCloudSpecAPI(backend),
    40  	}
    41  }
    42  
    43  // AllModels allows controller administrators to get the list of all the
    44  // models in the controller.
    45  func (c *Client) AllModels() ([]base.UserModel, error) {
    46  	var models params.UserModelList
    47  	err := c.facade.FacadeCall("AllModels", nil, &models)
    48  	if err != nil {
    49  		return nil, errors.Trace(err)
    50  	}
    51  	result := make([]base.UserModel, len(models.UserModels))
    52  	for i, model := range models.UserModels {
    53  		owner, err := names.ParseUserTag(model.OwnerTag)
    54  		if err != nil {
    55  			return nil, errors.Annotatef(err, "OwnerTag %q at position %d", model.OwnerTag, i)
    56  		}
    57  		result[i] = base.UserModel{
    58  			Name:           model.Name,
    59  			UUID:           model.UUID,
    60  			Owner:          owner.Canonical(),
    61  			LastConnection: model.LastConnection,
    62  		}
    63  	}
    64  	return result, nil
    65  }
    66  
    67  // ModelConfig returns all model settings for the
    68  // controller model.
    69  func (c *Client) ModelConfig() (map[string]interface{}, error) {
    70  	result := params.ModelConfigResults{}
    71  	err := c.facade.FacadeCall("ModelConfig", nil, &result)
    72  	values := make(map[string]interface{})
    73  	for name, val := range result.Config {
    74  		values[name] = val.Value
    75  	}
    76  	return values, err
    77  }
    78  
    79  // HostedConfig contains the model config and the cloud spec for that
    80  // model such that direct access to the provider can be used.
    81  type HostedConfig struct {
    82  	Name      string
    83  	Owner     names.UserTag
    84  	Config    map[string]interface{}
    85  	CloudSpec environs.CloudSpec
    86  	Error     error
    87  }
    88  
    89  // HostedModelsConfig returns all model settings for the
    90  // controller model.
    91  func (c *Client) HostedModelConfigs() ([]HostedConfig, error) {
    92  	result := params.HostedModelConfigsResults{}
    93  	err := c.facade.FacadeCall("HostedModelConfigs", nil, &result)
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	// If we get to here, we have some values. Each value may or
    98  	// may not have an error, but it should at least have a name
    99  	// and owner.
   100  	hostedConfigs := make([]HostedConfig, len(result.Models))
   101  	for i, modelConfig := range result.Models {
   102  		hostedConfigs[i].Name = modelConfig.Name
   103  		tag, err := names.ParseUserTag(modelConfig.OwnerTag)
   104  		if err != nil {
   105  			hostedConfigs[i].Error = errors.Trace(err)
   106  			continue
   107  		}
   108  		hostedConfigs[i].Owner = tag
   109  		if modelConfig.Error != nil {
   110  			hostedConfigs[i].Error = errors.Trace(modelConfig.Error)
   111  			continue
   112  		}
   113  		hostedConfigs[i].Config = modelConfig.Config
   114  		spec, err := c.MakeCloudSpec(modelConfig.CloudSpec)
   115  		if err != nil {
   116  			hostedConfigs[i].Error = errors.Trace(err)
   117  			continue
   118  		}
   119  		hostedConfigs[i].CloudSpec = spec
   120  	}
   121  	return hostedConfigs, err
   122  }
   123  
   124  // DestroyController puts the controller model into a "dying" state,
   125  // and removes all non-manager machine instances. Underlying DestroyModel
   126  // calls will fail if there are any manually-provisioned non-manager machines
   127  // in state.
   128  func (c *Client) DestroyController(destroyModels bool) error {
   129  	args := params.DestroyControllerArgs{
   130  		DestroyModels: destroyModels,
   131  	}
   132  	return c.facade.FacadeCall("DestroyController", args, nil)
   133  }
   134  
   135  // ListBlockedModels returns a list of all models within the controller
   136  // which have at least one block in place.
   137  func (c *Client) ListBlockedModels() ([]params.ModelBlockInfo, error) {
   138  	result := params.ModelBlockInfoList{}
   139  	err := c.facade.FacadeCall("ListBlockedModels", nil, &result)
   140  	return result.Models, err
   141  }
   142  
   143  // RemoveBlocks removes all the blocks in the controller.
   144  func (c *Client) RemoveBlocks() error {
   145  	args := params.RemoveBlocksArgs{All: true}
   146  	return c.facade.FacadeCall("RemoveBlocks", args, nil)
   147  }
   148  
   149  // WatchAllModels returns an AllWatcher, from which you can request
   150  // the Next collection of Deltas (for all models).
   151  func (c *Client) WatchAllModels() (*api.AllWatcher, error) {
   152  	var info params.AllWatcherId
   153  	if err := c.facade.FacadeCall("WatchAllModels", nil, &info); err != nil {
   154  		return nil, err
   155  	}
   156  	return api.NewAllModelWatcher(c.facade.RawAPICaller(), &info.AllWatcherId), nil
   157  }
   158  
   159  // ModelStatus returns a status summary for each model tag passed in.
   160  func (c *Client) ModelStatus(tags ...names.ModelTag) ([]base.ModelStatus, error) {
   161  	result := params.ModelStatusResults{}
   162  	models := make([]params.Entity, len(tags))
   163  	for i, tag := range tags {
   164  		models[i] = params.Entity{Tag: tag.String()}
   165  	}
   166  	req := params.Entities{
   167  		Entities: models,
   168  	}
   169  	if err := c.facade.FacadeCall("ModelStatus", req, &result); err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	results := make([]base.ModelStatus, len(result.Results))
   174  	for i, r := range result.Results {
   175  		model, err := names.ParseModelTag(r.ModelTag)
   176  		if err != nil {
   177  			return nil, errors.Annotatef(err, "ModelTag %q at position %d", r.ModelTag, i)
   178  		}
   179  		owner, err := names.ParseUserTag(r.OwnerTag)
   180  		if err != nil {
   181  			return nil, errors.Annotatef(err, "OwnerTag %q at position %d", r.OwnerTag, i)
   182  		}
   183  
   184  		results[i] = base.ModelStatus{
   185  			UUID:               model.Id(),
   186  			Life:               string(r.Life),
   187  			Owner:              owner.Canonical(),
   188  			HostedMachineCount: r.HostedMachineCount,
   189  			ServiceCount:       r.ApplicationCount,
   190  			TotalMachineCount:  len(r.Machines),
   191  		}
   192  		results[i].Machines = make([]base.Machine, len(r.Machines))
   193  		for j, mm := range r.Machines {
   194  			if mm.Hardware != nil && mm.Hardware.Cores != nil {
   195  				results[i].CoreCount += int(*mm.Hardware.Cores)
   196  			}
   197  			results[i].Machines[j] = base.Machine{
   198  				Id:         mm.Id,
   199  				InstanceId: mm.InstanceId,
   200  				HasVote:    mm.HasVote,
   201  				WantsVote:  mm.WantsVote,
   202  				Status:     mm.Status,
   203  			}
   204  		}
   205  	}
   206  	return results, nil
   207  }
   208  
   209  // GrantController grants a user access to the controller.
   210  func (c *Client) GrantController(user, access string) error {
   211  	return c.modifyControllerUser(params.GrantControllerAccess, user, access)
   212  }
   213  
   214  // RevokeController revokes a user's access to the controller.
   215  func (c *Client) RevokeController(user, access string) error {
   216  	return c.modifyControllerUser(params.RevokeControllerAccess, user, access)
   217  }
   218  
   219  func (c *Client) modifyControllerUser(action params.ControllerAction, user, access string) error {
   220  	var args params.ModifyControllerAccessRequest
   221  
   222  	if !names.IsValidUser(user) {
   223  		return errors.Errorf("invalid username: %q", user)
   224  	}
   225  	userTag := names.NewUserTag(user)
   226  
   227  	args.Changes = []params.ModifyControllerAccess{{
   228  		UserTag: userTag.String(),
   229  		Action:  action,
   230  		Access:  access,
   231  	}}
   232  
   233  	var result params.ErrorResults
   234  	err := c.facade.FacadeCall("ModifyControllerAccess", args, &result)
   235  	if err != nil {
   236  		return errors.Trace(err)
   237  	}
   238  	if len(result.Results) != len(args.Changes) {
   239  		return errors.Errorf("expected %d results, got %d", len(args.Changes), len(result.Results))
   240  	}
   241  
   242  	return result.Combine()
   243  }
   244  
   245  // GetControllerAccess returns the access level the user has on the controller.
   246  func (c *Client) GetControllerAccess(user string) (permission.Access, error) {
   247  	if !names.IsValidUser(user) {
   248  		return "", errors.Errorf("invalid username: %q", user)
   249  	}
   250  	entities := params.Entities{Entities: []params.Entity{{names.NewUserTag(user).String()}}}
   251  	var results params.UserAccessResults
   252  	err := c.facade.FacadeCall("GetControllerAccess", entities, &results)
   253  	if err != nil {
   254  		return "", errors.Trace(err)
   255  	}
   256  	if len(results.Results) != 1 {
   257  		return "", errors.Errorf("expected 1 result, got %d", len(results.Results))
   258  	}
   259  	if err := results.Results[0].Error; err != nil {
   260  		return "", errors.Trace(err)
   261  	}
   262  	return permission.Access(results.Results[0].Result.Access), nil
   263  }
   264  
   265  // MigrationSpec holds the details required to start the migration of
   266  // a single model.
   267  type MigrationSpec struct {
   268  	ModelUUID            string
   269  	TargetControllerUUID string
   270  	TargetAddrs          []string
   271  	TargetCACert         string
   272  	TargetUser           string
   273  	TargetPassword       string
   274  	TargetMacaroons      []macaroon.Slice
   275  	ExternalControl      bool
   276  	SkipInitialPrechecks bool
   277  }
   278  
   279  // Validate performs sanity checks on the migration configuration it
   280  // holds.
   281  func (s *MigrationSpec) Validate() error {
   282  	if !names.IsValidModel(s.ModelUUID) {
   283  		return errors.NotValidf("model UUID")
   284  	}
   285  	if !names.IsValidModel(s.TargetControllerUUID) {
   286  		return errors.NotValidf("controller UUID")
   287  	}
   288  	if len(s.TargetAddrs) < 1 {
   289  		return errors.NotValidf("empty target API addresses")
   290  	}
   291  	if s.TargetCACert == "" {
   292  		return errors.NotValidf("empty target CA cert")
   293  	}
   294  	if !names.IsValidUser(s.TargetUser) {
   295  		return errors.NotValidf("target user")
   296  	}
   297  	if s.TargetPassword == "" && len(s.TargetMacaroons) == 0 {
   298  		return errors.NotValidf("missing authentication secrets")
   299  	}
   300  	return nil
   301  }
   302  
   303  // InitiateMigration attempts to start a migration for the specified
   304  // model, returning the migration's ID.
   305  //
   306  // The API server supports starting multiple migrations in one request
   307  // but we don't need that at the client side yet (and may never) so
   308  // this call just supports starting one migration at a time.
   309  func (c *Client) InitiateMigration(spec MigrationSpec) (string, error) {
   310  	if err := spec.Validate(); err != nil {
   311  		return "", errors.Trace(err)
   312  	}
   313  
   314  	macsJSON, err := macaroonsToJSON(spec.TargetMacaroons)
   315  	if err != nil {
   316  		return "", errors.Trace(err)
   317  	}
   318  
   319  	args := params.InitiateMigrationArgs{
   320  		Specs: []params.MigrationSpec{{
   321  			ModelTag: names.NewModelTag(spec.ModelUUID).String(),
   322  			TargetInfo: params.MigrationTargetInfo{
   323  				ControllerTag: names.NewControllerTag(spec.TargetControllerUUID).String(),
   324  				Addrs:         spec.TargetAddrs,
   325  				CACert:        spec.TargetCACert,
   326  				AuthTag:       names.NewUserTag(spec.TargetUser).String(),
   327  				Password:      spec.TargetPassword,
   328  				Macaroons:     string(macsJSON),
   329  			},
   330  			ExternalControl:      spec.ExternalControl,
   331  			SkipInitialPrechecks: spec.SkipInitialPrechecks,
   332  		}},
   333  	}
   334  	response := params.InitiateMigrationResults{}
   335  	if err := c.facade.FacadeCall("InitiateMigration", args, &response); err != nil {
   336  		return "", errors.Trace(err)
   337  	}
   338  	if len(response.Results) != 1 {
   339  		return "", errors.New("unexpected number of results returned")
   340  	}
   341  	result := response.Results[0]
   342  	if result.Error != nil {
   343  		return "", errors.Trace(result.Error)
   344  	}
   345  	return result.MigrationId, nil
   346  }
   347  
   348  func macaroonsToJSON(macs []macaroon.Slice) (string, error) {
   349  	if len(macs) == 0 {
   350  		return "", nil
   351  	}
   352  	out, err := json.Marshal(macs)
   353  	if err != nil {
   354  		return "", errors.Annotate(err, "marshalling macaroons")
   355  	}
   356  	return string(out), nil
   357  }