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  }