github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/resource/api/private/client/client.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client
     5  
     6  import (
     7  	"io"
     8  	"net/http"
     9  	"path"
    10  
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/resource"
    15  	"github.com/juju/juju/resource/api"
    16  	"github.com/juju/juju/resource/api/private"
    17  )
    18  
    19  // FacadeCaller exposes the raw API caller functionality needed here.
    20  type FacadeCaller interface {
    21  	// FacadeCall makes an API request.
    22  	FacadeCall(request string, params, response interface{}) error
    23  }
    24  
    25  // HTTPClient exposes the raw API HTTP caller functionality needed here.
    26  type HTTPClient interface {
    27  	// Do sends the HTTP request/body and unpacks the response into
    28  	// the provided "resp". If that is a **http.Response then it is
    29  	// unpacked as-is. Otherwise it is unmarshaled from JSON.
    30  	Do(req *http.Request, body io.ReadSeeker, resp interface{}) error
    31  }
    32  
    33  // UnitHTTPClient exposes the raw API HTTP caller functionality needed here.
    34  type UnitHTTPClient interface {
    35  	HTTPClient
    36  
    37  	// Unit Returns the name of the unit for this client.
    38  	Unit() string
    39  }
    40  
    41  // NewUnitFacadeClient creates a new API client for the resources
    42  // portion of the uniter facade.
    43  func NewUnitFacadeClient(facadeCaller FacadeCaller, httpClient UnitHTTPClient) *UnitFacadeClient {
    44  	return &UnitFacadeClient{
    45  		FacadeCaller: facadeCaller,
    46  		HTTPClient:   httpClient,
    47  	}
    48  }
    49  
    50  // UnitFacadeClient is an API client for the resources portion
    51  // of the uniter facade.
    52  type UnitFacadeClient struct {
    53  	FacadeCaller
    54  	HTTPClient
    55  }
    56  
    57  // GetResource opens the resource (metadata/blob), if it exists, via
    58  // the HTTP API and returns it. If it does not exist or hasn't been
    59  // uploaded yet then errors.NotFound is returned.
    60  func (c *UnitFacadeClient) GetResource(resourceName string) (resource.Resource, io.ReadCloser, error) {
    61  	var response *http.Response
    62  	req, err := api.NewHTTPDownloadRequest(resourceName)
    63  	if err != nil {
    64  		return resource.Resource{}, nil, errors.Annotate(err, "failed to build API request")
    65  	}
    66  	if err := c.Do(req, nil, &response); err != nil {
    67  		return resource.Resource{}, nil, errors.Annotate(err, "HTTP request failed")
    68  	}
    69  
    70  	// HACK(katco): Combine this into one request?
    71  	resourceInfo, err := c.getResourceInfo(resourceName)
    72  	if err != nil {
    73  		return resource.Resource{}, nil, errors.Trace(err)
    74  	}
    75  
    76  	// TODO(katco): Check headers against resource info
    77  	// TODO(katco): Check in on all the response headers
    78  	return resourceInfo, response.Body, nil
    79  }
    80  
    81  func (c *UnitFacadeClient) getResourceInfo(resourceName string) (resource.Resource, error) {
    82  	var response private.ResourcesResult
    83  
    84  	args := private.ListResourcesArgs{
    85  		ResourceNames: []string{resourceName},
    86  	}
    87  	if err := c.FacadeCall("GetResourceInfo", &args, &response); err != nil {
    88  		return resource.Resource{}, errors.Annotate(err, "could not get resource info")
    89  	}
    90  	if response.Error != nil {
    91  		err := common.RestoreError(response.Error)
    92  		return resource.Resource{}, errors.Annotate(err, "request failed on server")
    93  	}
    94  
    95  	if len(response.Resources) != 1 {
    96  		return resource.Resource{}, errors.New("got bad response from API server")
    97  	}
    98  	if response.Resources[0].Error != nil {
    99  		err := common.RestoreError(response.Error)
   100  		return resource.Resource{}, errors.Annotate(err, "request failed for resource")
   101  	}
   102  	res, err := api.API2Resource(response.Resources[0].Resource)
   103  	if err != nil {
   104  		return resource.Resource{}, errors.Annotate(err, "got bad data from API server")
   105  	}
   106  	return res, nil
   107  }
   108  
   109  type unitHTTPClient struct {
   110  	HTTPClient
   111  	unitName string
   112  }
   113  
   114  // NewUnitHTTPClient wraps an HTTP client (a la httprequest.Client)
   115  // with unit information. This allows rewriting of the URL to match
   116  // the relevant unit.
   117  func NewUnitHTTPClient(client HTTPClient, unitName string) UnitHTTPClient {
   118  	return &unitHTTPClient{
   119  		HTTPClient: client,
   120  		unitName:   unitName,
   121  	}
   122  }
   123  
   124  // Unit returns the name of the unit.
   125  func (uhc unitHTTPClient) Unit() string {
   126  	return uhc.unitName
   127  }
   128  
   129  // Do implements httprequest.Doer.
   130  func (uhc *unitHTTPClient) Do(req *http.Request, body io.ReadSeeker, response interface{}) error {
   131  	req.URL.Path = path.Join("/units", uhc.unitName, req.URL.Path)
   132  	return uhc.HTTPClient.Do(req, body, response)
   133  }