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 }