~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/environs/simplestreams/datasource.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 agent-metadata-url, cloud storage, keystone catalog etc.
 
20
        Description() string
 
21
 
 
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)
 
26
 
 
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)
 
30
 
 
31
        // PublicSigningKey returns the public key used to validate signed metadata.
 
32
        PublicSigningKey() string
 
33
 
 
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.
 
39
        Priority() int
 
40
        // RequireSigned indicates whether this data source requires signed data.
 
41
        RequireSigned() bool
 
42
}
 
43
 
 
44
const (
 
45
        // These values used as priority factors for sorting data source data.
 
46
 
 
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
 
51
 
 
52
        // DEFAULT_CLOUD_DATA is used for common cloud data that
 
53
        // is shared an is publically available.
 
54
        DEFAULT_CLOUD_DATA = 10
 
55
 
 
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
 
60
 
 
61
        // CUSTOM_CLOUD_DATA is the highest available ranking and
 
62
        // is given to custom data.
 
63
        CUSTOM_CLOUD_DATA = 50
 
64
)
 
65
 
 
66
// A urlDataSource retrieves data from an HTTP URL.
 
67
type urlDataSource struct {
 
68
        description          string
 
69
        baseURL              string
 
70
        hostnameVerification utils.SSLHostnameVerification
 
71
        publicSigningKey     string
 
72
        priority             int
 
73
        requireSigned        bool
 
74
}
 
75
 
 
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,
 
80
                baseURL:              baseURL,
 
81
                hostnameVerification: hostnameVerification,
 
82
                priority:             priority,
 
83
                requireSigned:        requireSigned,
 
84
        }
 
85
}
 
86
 
 
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,
 
91
                baseURL:              baseURL,
 
92
                publicSigningKey:     publicKey,
 
93
                hostnameVerification: hostnameVerification,
 
94
                priority:             priority,
 
95
                requireSigned:        requireSigned,
 
96
        }
 
97
}
 
98
 
 
99
// Description is defined in simplestreams.DataSource.
 
100
func (u *urlDataSource) Description() string {
 
101
        return u.description
 
102
}
 
103
 
 
104
func (u *urlDataSource) GoString() string {
 
105
        return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL)
 
106
}
 
107
 
 
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
 
115
        }
 
116
        return baseURL + "/" + relpath
 
117
}
 
118
 
 
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)
 
127
        if err != nil {
 
128
                logger.Tracef("Got error requesting %q: %v", dataURL, err)
 
129
                return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL)
 
130
        }
 
131
        if resp.StatusCode != http.StatusOK {
 
132
                resp.Body.Close()
 
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)
 
138
                }
 
139
                return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status)
 
140
        }
 
141
        return resp.Body, dataURL, nil
 
142
}
 
143
 
 
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
 
147
}
 
148
 
 
149
// PublicSigningKey is defined in simplestreams.DataSource.
 
150
func (u *urlDataSource) PublicSigningKey() string {
 
151
        return u.publicSigningKey
 
152
}
 
153
 
 
154
// SetAllowRetry is defined in simplestreams.DataSource.
 
155
func (h *urlDataSource) SetAllowRetry(allow bool) {
 
156
        // This is a NOOP for url datasources.
 
157
}
 
158
 
 
159
// Priority is defined in simplestreams.DataSource.
 
160
func (h *urlDataSource) Priority() int {
 
161
        return h.priority
 
162
}
 
163
 
 
164
// RequireSigned is defined in simplestreams.DataSource.
 
165
func (h *urlDataSource) RequireSigned() bool {
 
166
        return h.requireSigned
 
167
}