github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/environs/simplestreams/datasource.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package simplestreams 5 6 import ( 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/juju/errors" 13 "github.com/juju/utils" 14 ) 15 16 // A DataSource retrieves simplestreams metadata. 17 type DataSource interface { 18 // Description describes the origin of this datasource. 19 // eg tools-metadata-url, cloud storage, keystone catalog etc. 20 Description() string 21 // Fetch loads the data at the specified relative path. It returns a reader from which 22 // the data can be retrieved as well as the full URL of the file. The full URL is typically 23 // used in log messages to help diagnose issues accessing the data. 24 Fetch(path string) (io.ReadCloser, string, error) 25 // URL returns the full URL of the path, as applicable to this datasource. 26 // This method is used primarily for logging purposes. 27 URL(path string) (string, error) 28 // SetAllowRetry sets the flag which determines if the datasource will retry fetching the metadata 29 // if it is not immediately available. 30 SetAllowRetry(allow bool) 31 } 32 33 // A urlDataSource retrieves data from an HTTP URL. 34 type urlDataSource struct { 35 description string 36 baseURL string 37 hostnameVerification utils.SSLHostnameVerification 38 } 39 40 // NewURLDataSource returns a new datasource reading from the specified baseURL. 41 func NewURLDataSource(description, baseURL string, hostnameVerification utils.SSLHostnameVerification) DataSource { 42 return &urlDataSource{ 43 description: description, 44 baseURL: baseURL, 45 hostnameVerification: hostnameVerification, 46 } 47 } 48 49 // Description is defined in simplestreams.DataSource. 50 func (u *urlDataSource) Description() string { 51 return u.description 52 } 53 54 func (u *urlDataSource) GoString() string { 55 return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL) 56 } 57 58 // urlJoin returns baseURL + relpath making sure to have a '/' inbetween them 59 // This doesn't try to do anything fancy with URL query or parameter bits 60 // It also doesn't use path.Join because that normalizes slashes, and you need 61 // to keep both slashes in 'http://'. 62 func urlJoin(baseURL, relpath string) string { 63 if strings.HasSuffix(baseURL, "/") { 64 return baseURL + relpath 65 } 66 return baseURL + "/" + relpath 67 } 68 69 // Fetch is defined in simplestreams.DataSource. 70 func (h *urlDataSource) Fetch(path string) (io.ReadCloser, string, error) { 71 dataURL := urlJoin(h.baseURL, path) 72 client := utils.GetHTTPClient(h.hostnameVerification) 73 // dataURL can be http:// or file:// 74 resp, err := client.Get(dataURL) 75 if err != nil { 76 logger.Debugf("Got error requesting %q: %v", dataURL, err) 77 return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL) 78 } 79 if resp.StatusCode == http.StatusNotFound { 80 return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL) 81 } 82 if resp.StatusCode == http.StatusUnauthorized { 83 return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL) 84 } 85 if resp.StatusCode != http.StatusOK { 86 return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status) 87 } 88 return resp.Body, dataURL, nil 89 } 90 91 // URL is defined in simplestreams.DataSource. 92 func (h *urlDataSource) URL(path string) (string, error) { 93 return urlJoin(h.baseURL, path), nil 94 } 95 96 // SetAllowRetry is defined in simplestreams.DataSource. 97 func (h *urlDataSource) SetAllowRetry(allow bool) { 98 // This is a NOOP for url datasources. 99 }