github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/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  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/api/base"
    12  	"github.com/juju/juju/api/common"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/permission"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.api.modelmanager")
    20  
    21  // Client provides methods that the Juju client command uses to interact
    22  // with models stored in the Juju Server.
    23  type Client struct {
    24  	base.ClientFacade
    25  	facade base.FacadeCaller
    26  	*common.ModelStatusAPI
    27  }
    28  
    29  // NewClient creates a new `Client` based on an existing authenticated API
    30  // connection.
    31  func NewClient(st base.APICallCloser) *Client {
    32  	frontend, backend := base.NewClientFacade(st, "ModelManager")
    33  	return &Client{
    34  		ClientFacade:   frontend,
    35  		facade:         backend,
    36  		ModelStatusAPI: common.NewModelStatusAPI(backend),
    37  	}
    38  }
    39  
    40  // Close closes the api connection.
    41  func (c *Client) Close() error {
    42  	return c.ClientFacade.Close()
    43  }
    44  
    45  // CreateModel creates a new model using the model config,
    46  // cloud region and credential specified in the args.
    47  func (c *Client) CreateModel(
    48  	name, owner, cloud, cloudRegion string,
    49  	cloudCredential names.CloudCredentialTag,
    50  	config map[string]interface{},
    51  ) (base.ModelInfo, error) {
    52  	var result base.ModelInfo
    53  	if !names.IsValidUser(owner) {
    54  		return result, errors.Errorf("invalid owner name %q", owner)
    55  	}
    56  	var cloudTag string
    57  	if cloud != "" {
    58  		if !names.IsValidCloud(cloud) {
    59  			return result, errors.Errorf("invalid cloud name %q", cloud)
    60  		}
    61  		cloudTag = names.NewCloudTag(cloud).String()
    62  	}
    63  	var cloudCredentialTag string
    64  	if cloudCredential != (names.CloudCredentialTag{}) {
    65  		cloudCredentialTag = cloudCredential.String()
    66  	}
    67  	createArgs := params.ModelCreateArgs{
    68  		Name:               name,
    69  		OwnerTag:           names.NewUserTag(owner).String(),
    70  		Config:             config,
    71  		CloudTag:           cloudTag,
    72  		CloudRegion:        cloudRegion,
    73  		CloudCredentialTag: cloudCredentialTag,
    74  	}
    75  	var modelInfo params.ModelInfo
    76  	err := c.facade.FacadeCall("CreateModel", createArgs, &modelInfo)
    77  	if err != nil {
    78  		return result, errors.Trace(err)
    79  	}
    80  	return convertParamsModelInfo(modelInfo)
    81  }
    82  
    83  func convertParamsModelInfo(modelInfo params.ModelInfo) (base.ModelInfo, error) {
    84  	cloud, err := names.ParseCloudTag(modelInfo.CloudTag)
    85  	if err != nil {
    86  		return base.ModelInfo{}, err
    87  	}
    88  	var credential string
    89  	if modelInfo.CloudCredentialTag != "" {
    90  		credTag, err := names.ParseCloudCredentialTag(modelInfo.CloudCredentialTag)
    91  		if err != nil {
    92  			return base.ModelInfo{}, err
    93  		}
    94  		credential = credTag.Id()
    95  	}
    96  	ownerTag, err := names.ParseUserTag(modelInfo.OwnerTag)
    97  	if err != nil {
    98  		return base.ModelInfo{}, err
    99  	}
   100  	result := base.ModelInfo{
   101  		Name:            modelInfo.Name,
   102  		UUID:            modelInfo.UUID,
   103  		ControllerUUID:  modelInfo.ControllerUUID,
   104  		ProviderType:    modelInfo.ProviderType,
   105  		DefaultSeries:   modelInfo.DefaultSeries,
   106  		Cloud:           cloud.Id(),
   107  		CloudRegion:     modelInfo.CloudRegion,
   108  		CloudCredential: credential,
   109  		Owner:           ownerTag.Id(),
   110  		Life:            string(modelInfo.Life),
   111  	}
   112  	result.Status = base.Status{
   113  		Status: modelInfo.Status.Status,
   114  		Info:   modelInfo.Status.Info,
   115  		Data:   make(map[string]interface{}),
   116  		Since:  modelInfo.Status.Since,
   117  	}
   118  	for k, v := range modelInfo.Status.Data {
   119  		result.Status.Data[k] = v
   120  	}
   121  	result.Users = make([]base.UserInfo, len(modelInfo.Users))
   122  	for i, u := range modelInfo.Users {
   123  		result.Users[i] = base.UserInfo{
   124  			UserName:       u.UserName,
   125  			DisplayName:    u.DisplayName,
   126  			Access:         string(u.Access),
   127  			LastConnection: u.LastConnection,
   128  		}
   129  	}
   130  	result.Machines = make([]base.Machine, len(modelInfo.Machines))
   131  	for i, m := range modelInfo.Machines {
   132  		machine := base.Machine{
   133  			Id:         m.Id,
   134  			InstanceId: m.InstanceId,
   135  			HasVote:    m.HasVote,
   136  			WantsVote:  m.WantsVote,
   137  			Status:     m.Status,
   138  		}
   139  		if m.Hardware != nil {
   140  			machine.Hardware = &instance.HardwareCharacteristics{
   141  				Arch:             m.Hardware.Arch,
   142  				Mem:              m.Hardware.Mem,
   143  				RootDisk:         m.Hardware.RootDisk,
   144  				CpuCores:         m.Hardware.Cores,
   145  				CpuPower:         m.Hardware.CpuPower,
   146  				Tags:             m.Hardware.Tags,
   147  				AvailabilityZone: m.Hardware.AvailabilityZone,
   148  			}
   149  		}
   150  		result.Machines[i] = machine
   151  	}
   152  	return result, nil
   153  }
   154  
   155  // ListModels returns the models that the specified user
   156  // has access to in the current server.  Only that controller owner
   157  // can list models for any user (at this stage).  Other users
   158  // can only ask about their own models.
   159  func (c *Client) ListModels(user string) ([]base.UserModel, error) {
   160  	var models params.UserModelList
   161  	if !names.IsValidUser(user) {
   162  		return nil, errors.Errorf("invalid user name %q", user)
   163  	}
   164  	entity := params.Entity{names.NewUserTag(user).String()}
   165  	err := c.facade.FacadeCall("ListModels", entity, &models)
   166  	if err != nil {
   167  		return nil, errors.Trace(err)
   168  	}
   169  	result := make([]base.UserModel, len(models.UserModels))
   170  	for i, model := range models.UserModels {
   171  		owner, err := names.ParseUserTag(model.OwnerTag)
   172  		if err != nil {
   173  			return nil, errors.Annotatef(err, "OwnerTag %q at position %d", model.OwnerTag, i)
   174  		}
   175  		result[i] = base.UserModel{
   176  			Name:           model.Name,
   177  			UUID:           model.UUID,
   178  			Owner:          owner.Id(),
   179  			LastConnection: model.LastConnection,
   180  		}
   181  	}
   182  	return result, nil
   183  }
   184  
   185  func (c *Client) ModelInfo(tags []names.ModelTag) ([]params.ModelInfoResult, error) {
   186  	entities := params.Entities{
   187  		Entities: make([]params.Entity, len(tags)),
   188  	}
   189  	for i, tag := range tags {
   190  		entities.Entities[i].Tag = tag.String()
   191  	}
   192  	var results params.ModelInfoResults
   193  	err := c.facade.FacadeCall("ModelInfo", entities, &results)
   194  	if err != nil {
   195  		return nil, errors.Trace(err)
   196  	}
   197  	if len(results.Results) != len(tags) {
   198  		return nil, errors.Errorf("expected %d result(s), got %d", len(tags), len(results.Results))
   199  	}
   200  	return results.Results, nil
   201  }
   202  
   203  // DumpModel returns the serialized database agnostic model representation.
   204  func (c *Client) DumpModel(model names.ModelTag) (map[string]interface{}, error) {
   205  	var results params.MapResults
   206  	entities := params.Entities{
   207  		Entities: []params.Entity{{Tag: model.String()}},
   208  	}
   209  
   210  	err := c.facade.FacadeCall("DumpModels", entities, &results)
   211  	if err != nil {
   212  		return nil, errors.Trace(err)
   213  	}
   214  	if count := len(results.Results); count != 1 {
   215  		return nil, errors.Errorf("unexpected result count: %d", count)
   216  	}
   217  	result := results.Results[0]
   218  	if result.Error != nil {
   219  		return nil, result.Error
   220  	}
   221  	return result.Result, nil
   222  }
   223  
   224  // DumpModelDB returns all relevant mongo documents for the model.
   225  func (c *Client) DumpModelDB(model names.ModelTag) (map[string]interface{}, error) {
   226  	var results params.MapResults
   227  	entities := params.Entities{
   228  		Entities: []params.Entity{{Tag: model.String()}},
   229  	}
   230  
   231  	err := c.facade.FacadeCall("DumpModelsDB", entities, &results)
   232  	if err != nil {
   233  		return nil, errors.Trace(err)
   234  	}
   235  	if count := len(results.Results); count != 1 {
   236  		return nil, errors.Errorf("unexpected result count: %d", count)
   237  	}
   238  	result := results.Results[0]
   239  	if result.Error != nil {
   240  		return nil, result.Error
   241  	}
   242  	return result.Result, nil
   243  }
   244  
   245  // DestroyModel puts the specified model into a "dying" state, which will
   246  // cause the model's resources to be cleaned up, after which the model will
   247  // be removed.
   248  func (c *Client) DestroyModel(tag names.ModelTag) error {
   249  	var results params.ErrorResults
   250  	entities := params.Entities{
   251  		Entities: []params.Entity{{Tag: tag.String()}},
   252  	}
   253  	if err := c.facade.FacadeCall("DestroyModels", entities, &results); err != nil {
   254  		return errors.Trace(err)
   255  	}
   256  	if n := len(results.Results); n != 1 {
   257  		return errors.Errorf("expected 1 result, got %d", n)
   258  	}
   259  	if err := results.Results[0].Error; err != nil {
   260  		return errors.Trace(err)
   261  	}
   262  	return nil
   263  }
   264  
   265  // GrantModel grants a user access to the specified models.
   266  func (c *Client) GrantModel(user, access string, modelUUIDs ...string) error {
   267  	return c.modifyModelUser(params.GrantModelAccess, user, access, modelUUIDs)
   268  }
   269  
   270  // RevokeModel revokes a user's access to the specified models.
   271  func (c *Client) RevokeModel(user, access string, modelUUIDs ...string) error {
   272  	return c.modifyModelUser(params.RevokeModelAccess, user, access, modelUUIDs)
   273  }
   274  
   275  func (c *Client) modifyModelUser(action params.ModelAction, user, access string, modelUUIDs []string) error {
   276  	var args params.ModifyModelAccessRequest
   277  
   278  	if !names.IsValidUser(user) {
   279  		return errors.Errorf("invalid username: %q", user)
   280  	}
   281  	userTag := names.NewUserTag(user)
   282  
   283  	modelAccess := permission.Access(access)
   284  	if err := permission.ValidateModelAccess(modelAccess); err != nil {
   285  		return errors.Trace(err)
   286  	}
   287  	for _, model := range modelUUIDs {
   288  		if !names.IsValidModel(model) {
   289  			return errors.Errorf("invalid model: %q", model)
   290  		}
   291  		modelTag := names.NewModelTag(model)
   292  		args.Changes = append(args.Changes, params.ModifyModelAccess{
   293  			UserTag:  userTag.String(),
   294  			Action:   action,
   295  			Access:   params.UserAccessPermission(modelAccess),
   296  			ModelTag: modelTag.String(),
   297  		})
   298  	}
   299  
   300  	var result params.ErrorResults
   301  	err := c.facade.FacadeCall("ModifyModelAccess", args, &result)
   302  	if err != nil {
   303  		return errors.Trace(err)
   304  	}
   305  	if len(result.Results) != len(args.Changes) {
   306  		return errors.Errorf("expected %d results, got %d", len(args.Changes), len(result.Results))
   307  	}
   308  
   309  	for i, r := range result.Results {
   310  		if r.Error != nil && r.Error.Code == params.CodeAlreadyExists {
   311  			logger.Warningf("model %q is already shared with %q", modelUUIDs[i], userTag.Id())
   312  			result.Results[i].Error = nil
   313  		}
   314  	}
   315  	return result.Combine()
   316  }
   317  
   318  // ModelDefaults returns the default values for various sources used when
   319  // creating a new model.
   320  func (c *Client) ModelDefaults() (config.ModelDefaultAttributes, error) {
   321  	result := params.ModelDefaultsResult{}
   322  	err := c.facade.FacadeCall("ModelDefaults", nil, &result)
   323  	if err != nil {
   324  		return nil, errors.Trace(err)
   325  	}
   326  	values := make(config.ModelDefaultAttributes)
   327  	for name, val := range result.Config {
   328  		setting := config.AttributeDefaultValues{
   329  			Default:    val.Default,
   330  			Controller: val.Controller,
   331  		}
   332  		for _, region := range val.Regions {
   333  			setting.Regions = append(setting.Regions, config.RegionDefaultValue{
   334  				Name:  region.RegionName,
   335  				Value: region.Value})
   336  		}
   337  		values[name] = setting
   338  	}
   339  	return values, nil
   340  }
   341  
   342  // SetModelDefaults updates the specified default model config values.
   343  func (c *Client) SetModelDefaults(cloud, region string, config map[string]interface{}) error {
   344  	var cloudTag string
   345  	if cloud != "" {
   346  		cloudTag = names.NewCloudTag(cloud).String()
   347  	}
   348  	args := params.SetModelDefaults{
   349  		Config: []params.ModelDefaultValues{{
   350  			Config:      config,
   351  			CloudTag:    cloudTag,
   352  			CloudRegion: region,
   353  		}},
   354  	}
   355  	var result params.ErrorResults
   356  	err := c.facade.FacadeCall("SetModelDefaults", args, &result)
   357  	if err != nil {
   358  		return err
   359  	}
   360  	return result.OneError()
   361  }
   362  
   363  // UnsetModelDefaults removes the specified default model config values.
   364  func (c *Client) UnsetModelDefaults(cloud, region string, keys ...string) error {
   365  	var cloudTag string
   366  	if cloud != "" {
   367  		cloudTag = names.NewCloudTag(cloud).String()
   368  	}
   369  	args := params.UnsetModelDefaults{
   370  		Keys: []params.ModelUnsetKeys{{
   371  			Keys:        keys,
   372  			CloudTag:    cloudTag,
   373  			CloudRegion: region,
   374  		}},
   375  	}
   376  	var result params.ErrorResults
   377  	err := c.facade.FacadeCall("UnsetModelDefaults", args, &result)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	return result.OneError()
   382  }