1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
"github.com/juju/errors"
13
"github.com/juju/utils"
16
// A DataSource retrieves simplestreams metadata.
17
type DataSource interface {
18
// Description describes the origin of this datasource.
19
// eg agent-metadata-url, cloud storage, keystone catalog etc.
22
// Fetch loads the data at the specified relative path. It returns a reader from which
23
// the data can be retrieved as well as the full URL of the file. The full URL is typically
24
// used in log messages to help diagnose issues accessing the data.
25
Fetch(path string) (io.ReadCloser, string, error)
27
// URL returns the full URL of the path, as applicable to this datasource.
28
// This method is used primarily for logging purposes.
29
URL(path string) (string, error)
31
// PublicSigningKey returns the public key used to validate signed metadata.
32
PublicSigningKey() string
34
// SetAllowRetry sets the flag which determines if the datasource will retry fetching the metadata
35
// if it is not immediately available.
36
SetAllowRetry(allow bool)
37
// Priority is an importance factor for Data Source. Higher number means higher priority.
38
// This is will allow to sort data sources in order of importance.
40
// RequireSigned indicates whether this data source requires signed data.
45
// These values used as priority factors for sorting data source data.
47
// EXISTING_CLOUD_DATA is the lowest in priority.
48
// It is mostly used in merge functions
49
// where existing data does not need to be ranked.
50
EXISTING_CLOUD_DATA = 0
52
// DEFAULT_CLOUD_DATA is used for common cloud data that
53
// is shared an is publically available.
54
DEFAULT_CLOUD_DATA = 10
56
// SPECIFIC_CLOUD_DATA is used to rank cloud specific data
57
// above commonly available.
58
// For e.g., openstack's "keystone catalogue".
59
SPECIFIC_CLOUD_DATA = 20
61
// CUSTOM_CLOUD_DATA is the highest available ranking and
62
// is given to custom data.
63
CUSTOM_CLOUD_DATA = 50
66
// A urlDataSource retrieves data from an HTTP URL.
67
type urlDataSource struct {
70
hostnameVerification utils.SSLHostnameVerification
71
publicSigningKey string
76
// NewURLDataSource returns a new datasource reading from the specified baseURL.
77
func NewURLDataSource(description, baseURL string, hostnameVerification utils.SSLHostnameVerification, priority int, requireSigned bool) DataSource {
78
return &urlDataSource{
79
description: description,
81
hostnameVerification: hostnameVerification,
83
requireSigned: requireSigned,
87
// NewURLSignedDataSource returns a new datasource for signed metadata reading from the specified baseURL.
88
func NewURLSignedDataSource(description, baseURL, publicKey string, hostnameVerification utils.SSLHostnameVerification, priority int, requireSigned bool) DataSource {
89
return &urlDataSource{
90
description: description,
92
publicSigningKey: publicKey,
93
hostnameVerification: hostnameVerification,
95
requireSigned: requireSigned,
99
// Description is defined in simplestreams.DataSource.
100
func (u *urlDataSource) Description() string {
104
func (u *urlDataSource) GoString() string {
105
return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL)
108
// urlJoin returns baseURL + relpath making sure to have a '/' inbetween them
109
// This doesn't try to do anything fancy with URL query or parameter bits
110
// It also doesn't use path.Join because that normalizes slashes, and you need
111
// to keep both slashes in 'http://'.
112
func urlJoin(baseURL, relpath string) string {
113
if strings.HasSuffix(baseURL, "/") {
114
return baseURL + relpath
116
return baseURL + "/" + relpath
119
// Fetch is defined in simplestreams.DataSource.
120
func (h *urlDataSource) Fetch(path string) (io.ReadCloser, string, error) {
121
dataURL := urlJoin(h.baseURL, path)
122
client := utils.GetHTTPClient(h.hostnameVerification)
123
// dataURL can be http:// or file://
124
// MakeFileURL will only modify the URL if it's a file URL
125
dataURL = utils.MakeFileURL(dataURL)
126
resp, err := client.Get(dataURL)
128
logger.Tracef("Got error requesting %q: %v", dataURL, err)
129
return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL)
131
if resp.StatusCode != http.StatusOK {
133
switch resp.StatusCode {
134
case http.StatusNotFound:
135
return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL)
136
case http.StatusUnauthorized:
137
return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL)
139
return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status)
141
return resp.Body, dataURL, nil
144
// URL is defined in simplestreams.DataSource.
145
func (h *urlDataSource) URL(path string) (string, error) {
146
return utils.MakeFileURL(urlJoin(h.baseURL, path)), nil
149
// PublicSigningKey is defined in simplestreams.DataSource.
150
func (u *urlDataSource) PublicSigningKey() string {
151
return u.publicSigningKey
154
// SetAllowRetry is defined in simplestreams.DataSource.
155
func (h *urlDataSource) SetAllowRetry(allow bool) {
156
// This is a NOOP for url datasources.
159
// Priority is defined in simplestreams.DataSource.
160
func (h *urlDataSource) Priority() int {
164
// RequireSigned is defined in simplestreams.DataSource.
165
func (h *urlDataSource) RequireSigned() bool {
166
return h.requireSigned