github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "launchpad.net/juju-core/errors" 13 "launchpad.net/juju-core/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 // SSLHostnameVerification is used as a switch for when a given provider might 34 // use self-signed credentials and we should not try to verify the hostname on 35 // the TLS/SSL certificates 36 type SSLHostnameVerification bool 37 38 const ( 39 // VerifySSLHostnames ensures we verify the hostname on the certificate 40 // matches the host we are connecting and is signed 41 VerifySSLHostnames = SSLHostnameVerification(true) 42 // NoVerifySSLHostnames informs us to skip verifying the hostname 43 // matches a valid certificate 44 NoVerifySSLHostnames = SSLHostnameVerification(false) 45 ) 46 47 // A urlDataSource retrieves data from an HTTP URL. 48 type urlDataSource struct { 49 description string 50 baseURL string 51 hostnameVerification SSLHostnameVerification 52 } 53 54 // NewURLDataSource returns a new datasource reading from the specified baseURL. 55 func NewURLDataSource(description, baseURL string, verify SSLHostnameVerification) DataSource { 56 return &urlDataSource{ 57 description: description, 58 baseURL: baseURL, 59 hostnameVerification: verify, 60 } 61 } 62 63 // Description is defined in simplestreams.DataSource. 64 func (u *urlDataSource) Description() string { 65 return u.description 66 } 67 68 func (u *urlDataSource) GoString() string { 69 return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL) 70 } 71 72 // urlJoin returns baseURL + relpath making sure to have a '/' inbetween them 73 // This doesn't try to do anything fancy with URL query or parameter bits 74 // It also doesn't use path.Join because that normalizes slashes, and you need 75 // to keep both slashes in 'http://'. 76 func urlJoin(baseURL, relpath string) string { 77 if strings.HasSuffix(baseURL, "/") { 78 return baseURL + relpath 79 } 80 return baseURL + "/" + relpath 81 } 82 83 // Fetch is defined in simplestreams.DataSource. 84 func (h *urlDataSource) Fetch(path string) (io.ReadCloser, string, error) { 85 dataURL := urlJoin(h.baseURL, path) 86 // dataURL can be http:// or file:// 87 client := http.DefaultClient 88 if !h.hostnameVerification { 89 client = utils.GetNonValidatingHTTPClient() 90 } 91 resp, err := client.Get(dataURL) 92 if err != nil { 93 logger.Debugf("Got error requesting %q: %v", dataURL, err) 94 return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL) 95 } 96 if resp.StatusCode == http.StatusNotFound { 97 return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL) 98 } 99 if resp.StatusCode == http.StatusUnauthorized { 100 return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL) 101 } 102 if resp.StatusCode != http.StatusOK { 103 return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status) 104 } 105 return resp.Body, dataURL, nil 106 } 107 108 // URL is defined in simplestreams.DataSource. 109 func (h *urlDataSource) URL(path string) (string, error) { 110 return urlJoin(h.baseURL, path), nil 111 } 112 113 // SetAllowRetry is defined in simplestreams.DataSource. 114 func (h *urlDataSource) SetAllowRetry(allow bool) { 115 // This is a NOOP for url datasources. 116 }