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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasunitprovisioner
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/api/base"
    11  	apiwatcher "github.com/juju/juju/api/watcher"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/core/application"
    14  	"github.com/juju/juju/core/constraints"
    15  	"github.com/juju/juju/core/devices"
    16  	"github.com/juju/juju/core/life"
    17  	"github.com/juju/juju/core/status"
    18  	"github.com/juju/juju/core/watcher"
    19  	"github.com/juju/juju/storage"
    20  )
    21  
    22  // Client allows access to the CAAS unit provisioner API endpoint.
    23  type Client struct {
    24  	facade base.FacadeCaller
    25  }
    26  
    27  // NewClient returns a client used to access the CAAS unit provisioner API.
    28  func NewClient(caller base.APICaller) *Client {
    29  	facadeCaller := base.NewFacadeCaller(caller, "CAASUnitProvisioner")
    30  	return &Client{
    31  		facade: facadeCaller,
    32  	}
    33  }
    34  
    35  func applicationTag(application string) (names.ApplicationTag, error) {
    36  	if !names.IsValidApplication(application) {
    37  		return names.ApplicationTag{}, errors.NotValidf("application name %q", application)
    38  	}
    39  	return names.NewApplicationTag(application), nil
    40  }
    41  
    42  func entities(tags ...names.Tag) params.Entities {
    43  	entities := params.Entities{
    44  		Entities: make([]params.Entity, len(tags)),
    45  	}
    46  	for i, tag := range tags {
    47  		entities.Entities[i].Tag = tag.String()
    48  	}
    49  	return entities
    50  }
    51  
    52  // WatchApplications returns a StringsWatcher that notifies of
    53  // changes to the lifecycles of CAAS applications in the current model.
    54  func (c *Client) WatchApplications() (watcher.StringsWatcher, error) {
    55  	var result params.StringsWatchResult
    56  	if err := c.facade.FacadeCall("WatchApplications", nil, &result); err != nil {
    57  		return nil, err
    58  	}
    59  	if err := result.Error; err != nil {
    60  		return nil, result.Error
    61  	}
    62  	w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), result)
    63  	return w, nil
    64  }
    65  
    66  // ApplicationConfig returns the config for the specified application.
    67  func (c *Client) ApplicationConfig(applicationName string) (application.ConfigAttributes, error) {
    68  	var results params.ApplicationGetConfigResults
    69  	args := params.Entities{
    70  		Entities: []params.Entity{{Tag: names.NewApplicationTag(applicationName).String()}},
    71  	}
    72  	err := c.facade.FacadeCall("ApplicationsConfig", args, &results)
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	if len(results.Results) != len(args.Entities) {
    77  		return nil, errors.Errorf("expected %d result(s), got %d", len(args.Entities), len(results.Results))
    78  	}
    79  	return application.ConfigAttributes(results.Results[0].Config), nil
    80  }
    81  
    82  // WatchApplicationScale returns a NotifyWatcher that notifies of
    83  // changes to the lifecycles of units of the specified
    84  // CAAS application in the current model.
    85  func (c *Client) WatchApplicationScale(application string) (watcher.NotifyWatcher, error) {
    86  	applicationTag, err := applicationTag(application)
    87  	if err != nil {
    88  		return nil, errors.Trace(err)
    89  	}
    90  	args := entities(applicationTag)
    91  
    92  	var results params.NotifyWatchResults
    93  	if err := c.facade.FacadeCall("WatchApplicationsScale", args, &results); err != nil {
    94  		return nil, err
    95  	}
    96  	if n := len(results.Results); n != 1 {
    97  		return nil, errors.Errorf("expected 1 result, got %d", n)
    98  	}
    99  	if err := results.Results[0].Error; err != nil {
   100  		return nil, errors.Trace(err)
   101  	}
   102  	w := apiwatcher.NewNotifyWatcher(c.facade.RawAPICaller(), results.Results[0])
   103  	return w, nil
   104  }
   105  
   106  // ApplicationScale returns the scale for the specified application.
   107  func (c *Client) ApplicationScale(applicationName string) (int, error) {
   108  	var results params.IntResults
   109  	args := params.Entities{
   110  		Entities: []params.Entity{{Tag: names.NewApplicationTag(applicationName).String()}},
   111  	}
   112  	err := c.facade.FacadeCall("ApplicationsScale", args, &results)
   113  	if err != nil {
   114  		return 0, errors.Trace(err)
   115  	}
   116  	if len(results.Results) != len(args.Entities) {
   117  		return 0, errors.Errorf("expected %d result(s), got %d", len(args.Entities), len(results.Results))
   118  	}
   119  	return results.Results[0].Result, nil
   120  }
   121  
   122  // WatchPodSpec returns a NotifyWatcher that notifies of
   123  // changes to the pod spec of the specified CAAS application in
   124  // the current model.
   125  func (c *Client) WatchPodSpec(application string) (watcher.NotifyWatcher, error) {
   126  	appTag, err := applicationTag(application)
   127  	if err != nil {
   128  		return nil, errors.Trace(err)
   129  	}
   130  	args := entities(appTag)
   131  
   132  	var results params.NotifyWatchResults
   133  	if err := c.facade.FacadeCall("WatchPodSpec", args, &results); err != nil {
   134  		return nil, err
   135  	}
   136  	if n := len(results.Results); n != 1 {
   137  		return nil, errors.Errorf("expected 1 result, got %d", n)
   138  	}
   139  	if err := results.Results[0].Error; err != nil {
   140  		return nil, errors.Trace(err)
   141  	}
   142  	w := apiwatcher.NewNotifyWatcher(c.facade.RawAPICaller(), results.Results[0])
   143  	return w, nil
   144  }
   145  
   146  // ProvisioningInfo holds unit provisioning info.
   147  type ProvisioningInfo struct {
   148  	PodSpec     string
   149  	Placement   string
   150  	Constraints constraints.Value
   151  	Filesystems []storage.KubernetesFilesystemParams
   152  	Devices     []devices.KubernetesDeviceParams
   153  	Tags        map[string]string
   154  }
   155  
   156  // ErrNoUnits is returned when trying to provision a caas app but
   157  // there are no units defined in the model.
   158  var ErrNoUnits = errors.New("no units to provision")
   159  
   160  // ProvisioningInfo returns the provisioning info for the specified CAAS
   161  // application in the current model.
   162  func (c *Client) ProvisioningInfo(appName string) (*ProvisioningInfo, error) {
   163  	appTag, err := applicationTag(appName)
   164  	if err != nil {
   165  		return nil, errors.Trace(err)
   166  	}
   167  	args := entities(appTag)
   168  
   169  	var results params.KubernetesProvisioningInfoResults
   170  	if err := c.facade.FacadeCall("ProvisioningInfo", args, &results); err != nil {
   171  		return nil, err
   172  	}
   173  	if n := len(results.Results); n != 1 {
   174  		return nil, errors.Errorf("expected 1 result, got %d", n)
   175  	}
   176  	if err := results.Results[0].Error; err != nil {
   177  		return nil, maybeNotFound(err)
   178  	}
   179  	result := results.Results[0].Result
   180  	if result == nil {
   181  		return nil, ErrNoUnits
   182  	}
   183  	info := &ProvisioningInfo{
   184  		PodSpec:     result.PodSpec,
   185  		Placement:   result.Placement,
   186  		Constraints: result.Constraints,
   187  		Tags:        result.Tags,
   188  	}
   189  
   190  	for _, fs := range result.Filesystems {
   191  		fsInfo, err := filesystemFromParams(fs)
   192  		if err != nil {
   193  			return nil, errors.Trace(err)
   194  		}
   195  		info.Filesystems = append(info.Filesystems, *fsInfo)
   196  	}
   197  
   198  	var devs []devices.KubernetesDeviceParams
   199  	for _, device := range result.Devices {
   200  		devs = append(devs, devices.KubernetesDeviceParams{
   201  			Type:       devices.DeviceType(device.Type),
   202  			Count:      device.Count,
   203  			Attributes: device.Attributes,
   204  		})
   205  	}
   206  	info.Devices = devs
   207  	return info, nil
   208  }
   209  
   210  func filesystemFromParams(in params.KubernetesFilesystemParams) (*storage.KubernetesFilesystemParams, error) {
   211  	var attachment *storage.KubernetesFilesystemAttachmentParams
   212  	if in.Attachment != nil {
   213  		var err error
   214  		attachment, err = filesystemAttachmentFromParams(*in.Attachment)
   215  		if err != nil {
   216  			return nil, errors.Trace(err)
   217  		}
   218  	}
   219  	return &storage.KubernetesFilesystemParams{
   220  		StorageName:  in.StorageName,
   221  		Provider:     storage.ProviderType(in.Provider),
   222  		Size:         in.Size,
   223  		Attributes:   in.Attributes,
   224  		ResourceTags: in.Tags,
   225  		Attachment:   attachment,
   226  	}, nil
   227  }
   228  
   229  func filesystemAttachmentFromParams(in params.KubernetesFilesystemAttachmentParams) (*storage.KubernetesFilesystemAttachmentParams, error) {
   230  	return &storage.KubernetesFilesystemAttachmentParams{
   231  		AttachmentParams: storage.AttachmentParams{
   232  			Provider: storage.ProviderType(in.Provider),
   233  			ReadOnly: in.ReadOnly,
   234  		},
   235  		Path: in.MountPoint,
   236  	}, nil
   237  }
   238  
   239  // Life returns the lifecycle state for the specified CAAS application
   240  // or unit in the current model.
   241  func (c *Client) Life(entityName string) (life.Value, error) {
   242  	var tag names.Tag
   243  	switch {
   244  	case names.IsValidApplication(entityName):
   245  		tag = names.NewApplicationTag(entityName)
   246  	case names.IsValidUnit(entityName):
   247  		tag = names.NewUnitTag(entityName)
   248  	default:
   249  		return "", errors.NotValidf("application or unit name %q", entityName)
   250  	}
   251  	args := entities(tag)
   252  
   253  	var results params.LifeResults
   254  	if err := c.facade.FacadeCall("Life", args, &results); err != nil {
   255  		return "", err
   256  	}
   257  	if n := len(results.Results); n != 1 {
   258  		return "", errors.Errorf("expected 1 result, got %d", n)
   259  	}
   260  	if err := results.Results[0].Error; err != nil {
   261  		return "", maybeNotFound(err)
   262  	}
   263  	return life.Value(results.Results[0].Life), nil
   264  }
   265  
   266  // maybeNotFound returns an error satisfying errors.IsNotFound
   267  // if the supplied error has a CodeNotFound error.
   268  func maybeNotFound(err *params.Error) error {
   269  	if err == nil || !params.IsCodeNotFound(err) {
   270  		return err
   271  	}
   272  	return errors.NewNotFound(err, "")
   273  }
   274  
   275  // UpdateUnits updates the state model to reflect the state of the units
   276  // as reported by the cloud.
   277  func (c *Client) UpdateUnits(arg params.UpdateApplicationUnits) error {
   278  	var result params.ErrorResults
   279  	args := params.UpdateApplicationUnitArgs{Args: []params.UpdateApplicationUnits{arg}}
   280  	err := c.facade.FacadeCall("UpdateApplicationsUnits", args, &result)
   281  	if err != nil {
   282  		return errors.Trace(err)
   283  	}
   284  	if len(result.Results) != len(args.Args) {
   285  		return errors.Errorf("expected %d result(s), got %d", len(args.Args), len(result.Results))
   286  	}
   287  	if result.Results[0].Error == nil {
   288  		return nil
   289  	}
   290  	return maybeNotFound(result.Results[0].Error)
   291  }
   292  
   293  // UpdateApplicationService updates the state model to reflect the state of the application's
   294  // service as reported by the cloud.
   295  func (c *Client) UpdateApplicationService(arg params.UpdateApplicationServiceArg) error {
   296  	var result params.ErrorResults
   297  	args := params.UpdateApplicationServiceArgs{Args: []params.UpdateApplicationServiceArg{arg}}
   298  	if err := c.facade.FacadeCall("UpdateApplicationsService", args, &result); err != nil {
   299  		return errors.Trace(err)
   300  	}
   301  	if len(result.Results) != len(args.Args) {
   302  		return errors.Errorf("expected %d result(s), got %d", len(args.Args), len(result.Results))
   303  	}
   304  	if result.Results[0].Error == nil {
   305  		return nil
   306  	}
   307  	return maybeNotFound(result.Results[0].Error)
   308  }
   309  
   310  // SetOperatorStatus updates the provisioning status of an operator.
   311  func (c *Client) SetOperatorStatus(appName string, status status.Status, message string, data map[string]interface{}) error {
   312  	var result params.ErrorResults
   313  	args := params.SetStatus{Entities: []params.EntityStatusArgs{
   314  		{Tag: names.NewApplicationTag(appName).String(), Status: status.String(), Info: message, Data: data},
   315  	}}
   316  	err := c.facade.FacadeCall("SetOperatorStatus", args, &result)
   317  	if err != nil {
   318  		return err
   319  	}
   320  	return result.OneError()
   321  }