github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/caasoperator/client.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperator
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/version"
     9  	"gopkg.in/juju/charm.v6"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/api/base"
    13  	"github.com/juju/juju/api/common"
    14  	apiwatcher "github.com/juju/juju/api/watcher"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/life"
    17  	"github.com/juju/juju/core/model"
    18  	"github.com/juju/juju/core/status"
    19  	"github.com/juju/juju/core/watcher"
    20  )
    21  
    22  // Client allows access to the CAAS operator API endpoint.
    23  type Client struct {
    24  	facade base.FacadeCaller
    25  	*common.APIAddresser
    26  }
    27  
    28  // NewClient returns a client used to access the CAAS Operator API.
    29  func NewClient(caller base.APICaller) *Client {
    30  	facadeCaller := base.NewFacadeCaller(caller, "CAASOperator")
    31  	return &Client{
    32  		facade:       facadeCaller,
    33  		APIAddresser: common.NewAPIAddresser(facadeCaller),
    34  	}
    35  }
    36  
    37  func (c *Client) appTag(application string) (names.ApplicationTag, error) {
    38  	if !names.IsValidApplication(application) {
    39  		return names.ApplicationTag{}, errors.NotValidf("application name %q", application)
    40  	}
    41  	return names.NewApplicationTag(application), nil
    42  }
    43  
    44  // Model returns the model entity.
    45  func (c *Client) Model() (*model.Model, error) {
    46  	var result params.ModelResult
    47  	err := c.facade.FacadeCall("CurrentModel", nil, &result)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	if err := result.Error; err != nil {
    52  		return nil, err
    53  	}
    54  	modelType := model.ModelType(result.Type)
    55  	if modelType == "" {
    56  		modelType = model.IAAS
    57  	}
    58  	return &model.Model{
    59  		Name:      result.Name,
    60  		UUID:      result.UUID,
    61  		ModelType: modelType,
    62  	}, nil
    63  }
    64  
    65  // SetStatus sets the status of the specified application.
    66  func (c *Client) SetStatus(
    67  	application string,
    68  	status status.Status,
    69  	info string,
    70  	data map[string]interface{},
    71  ) error {
    72  	tag, err := c.appTag(application)
    73  	if err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	var result params.ErrorResults
    77  	args := params.SetStatus{
    78  		Entities: []params.EntityStatusArgs{{
    79  			Tag:    tag.String(),
    80  			Status: status.String(),
    81  			Info:   info,
    82  			Data:   data,
    83  		}},
    84  	}
    85  	if err := c.facade.FacadeCall("SetStatus", args, &result); err != nil {
    86  		return errors.Trace(err)
    87  	}
    88  	return result.OneError()
    89  }
    90  
    91  // Charm returns information about the charm currently assigned
    92  // to the application, including url, force upgrade and sha etc.
    93  func (c *Client) Charm(application string) (_ *charm.URL, forceUpgrade bool, sha256 string, vers int, _ error) {
    94  	tag, err := c.appTag(application)
    95  	if err != nil {
    96  		return nil, false, "", 0, errors.Trace(err)
    97  	}
    98  	var results params.ApplicationCharmResults
    99  	args := params.Entities{
   100  		Entities: []params.Entity{{Tag: tag.String()}},
   101  	}
   102  	if err := c.facade.FacadeCall("Charm", args, &results); err != nil {
   103  		return nil, false, "", 0, errors.Trace(err)
   104  	}
   105  	if n := len(results.Results); n != 1 {
   106  		return nil, false, "", 0, errors.Errorf("expected 1 result, got %d", n)
   107  	}
   108  	if err := results.Results[0].Error; err != nil {
   109  		return nil, false, "", 0, errors.Trace(err)
   110  	}
   111  	result := results.Results[0].Result
   112  	curl, err := charm.ParseURL(result.URL)
   113  	if err != nil {
   114  		return nil, false, "", 0, errors.Trace(err)
   115  	}
   116  	return curl, result.ForceUpgrade, result.SHA256, result.CharmModifiedVersion, nil
   117  }
   118  
   119  // SetPodSpec sets the pod spec of the specified application.
   120  func (c *Client) SetPodSpec(appName string, spec string) error {
   121  	tag, err := applicationTag(appName)
   122  	if err != nil {
   123  		return errors.Trace(err)
   124  	}
   125  	var result params.ErrorResults
   126  	args := params.SetPodSpecParams{
   127  		Specs: []params.EntityString{{
   128  			Tag:   tag.String(),
   129  			Value: spec,
   130  		}},
   131  	}
   132  	if err := c.facade.FacadeCall("SetPodSpec", args, &result); err != nil {
   133  		return errors.Trace(err)
   134  	}
   135  	return result.OneError()
   136  }
   137  
   138  func applicationTag(application string) (names.ApplicationTag, error) {
   139  	if !names.IsValidApplication(application) {
   140  		return names.ApplicationTag{}, errors.NotValidf("application name %q", application)
   141  	}
   142  	return names.NewApplicationTag(application), nil
   143  }
   144  
   145  // Watch returns a watcher for observing changes to an application.
   146  func (c *Client) Watch(application string) (watcher.NotifyWatcher, error) {
   147  	tag, err := c.appTag(application)
   148  	if err != nil {
   149  		return nil, errors.Trace(err)
   150  	}
   151  	return common.Watch(c.facade, "Watch", tag)
   152  }
   153  
   154  func entities(tags ...names.Tag) params.Entities {
   155  	entities := params.Entities{
   156  		Entities: make([]params.Entity, len(tags)),
   157  	}
   158  	for i, tag := range tags {
   159  		entities.Entities[i].Tag = tag.String()
   160  	}
   161  	return entities
   162  }
   163  
   164  // WatchUnits returns a StringsWatcher that notifies of
   165  // changes to the lifecycles of units of the specified
   166  // CAAS application in the current model.
   167  func (c *Client) WatchUnits(application string) (watcher.StringsWatcher, error) {
   168  	applicationTag, err := applicationTag(application)
   169  	if err != nil {
   170  		return nil, errors.Trace(err)
   171  	}
   172  	args := entities(applicationTag)
   173  
   174  	var results params.StringsWatchResults
   175  	if err := c.facade.FacadeCall("WatchUnits", args, &results); err != nil {
   176  		return nil, err
   177  	}
   178  	if n := len(results.Results); n != 1 {
   179  		return nil, errors.Errorf("expected 1 result, got %d", n)
   180  	}
   181  	if err := results.Results[0].Error; err != nil {
   182  		return nil, errors.Trace(err)
   183  	}
   184  	w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), results.Results[0])
   185  	return w, nil
   186  }
   187  
   188  // RemoveUnit removes the specified unit from the current model.
   189  func (c *Client) RemoveUnit(unitName string) error {
   190  	if !names.IsValidUnit(unitName) {
   191  		return errors.NotValidf("unit name %q", unitName)
   192  	}
   193  	var result params.ErrorResults
   194  	args := params.Entities{
   195  		Entities: []params.Entity{{Tag: names.NewUnitTag(unitName).String()}},
   196  	}
   197  	err := c.facade.FacadeCall("Remove", args, &result)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	return result.OneError()
   202  }
   203  
   204  // Life returns the lifecycle state for the specified CAAS application
   205  // or unit in the current model.
   206  func (c *Client) Life(entityName string) (life.Value, error) {
   207  	var tag names.Tag
   208  	switch {
   209  	case names.IsValidApplication(entityName):
   210  		tag = names.NewApplicationTag(entityName)
   211  	case names.IsValidUnit(entityName):
   212  		tag = names.NewUnitTag(entityName)
   213  	default:
   214  		return "", errors.NotValidf("application or unit name %q", entityName)
   215  	}
   216  	args := entities(tag)
   217  
   218  	var results params.LifeResults
   219  	if err := c.facade.FacadeCall("Life", args, &results); err != nil {
   220  		return "", err
   221  	}
   222  	if n := len(results.Results); n != 1 {
   223  		return "", errors.Errorf("expected 1 result, got %d", n)
   224  	}
   225  	if err := results.Results[0].Error; err != nil {
   226  		return "", maybeNotFound(err)
   227  	}
   228  	return life.Value(results.Results[0].Life), nil
   229  }
   230  
   231  // maybeNotFound returns an error satisfying errors.IsNotFound
   232  // if the supplied error has a CodeNotFound error.
   233  func maybeNotFound(err *params.Error) error {
   234  	if err == nil || !params.IsCodeNotFound(err) {
   235  		return err
   236  	}
   237  	return errors.NewNotFound(err, "")
   238  }
   239  
   240  // SetVersion sets the tools version associated with
   241  // the given application.
   242  func (c *Client) SetVersion(appName string, v version.Binary) error {
   243  	if !names.IsValidApplication(appName) {
   244  		return errors.NotValidf("application name %q", appName)
   245  	}
   246  	var results params.ErrorResults
   247  	args := params.EntitiesVersion{
   248  		AgentTools: []params.EntityVersion{{
   249  			Tag:   names.NewApplicationTag(appName).String(),
   250  			Tools: &params.Version{v},
   251  		}},
   252  	}
   253  	err := c.facade.FacadeCall("SetTools", args, &results)
   254  	if err != nil {
   255  		return errors.Trace(err)
   256  	}
   257  	return results.OneError()
   258  }