17
11
"launchpad.net/gnuflag"
18
12
"launchpad.net/juju-core/cmd"
19
13
"launchpad.net/juju-core/environs"
14
"launchpad.net/juju-core/environs/ec2"
20
15
"launchpad.net/juju-core/environs/tools"
21
"launchpad.net/juju-core/errors"
22
16
"launchpad.net/juju-core/log"
23
17
"launchpad.net/juju-core/state"
24
18
"launchpad.net/juju-core/version"
21
// defaultToolsUrl leads to the juju distribution on S3.
22
var defaultToolsLocation string = "https://juju-dist.s3.amazonaws.com/"
27
24
// SyncToolsCommand copies all the tools from the us-east-1 bucket to the local
29
26
type SyncToolsCommand struct {
65
62
return cmd.CheckEmpty(args)
68
var officialBucketAttrs = map[string]interface{}{
69
"name": "juju-public",
71
"control-bucket": "juju-dist",
74
"authorized-keys": "not-really", // We shouldn't need ssh access
78
66
tool *state.Tools, source environs.StorageReader,
79
67
target environs.Storage, ctx *cmd.Context,
182
170
fmt.Fprintf(ctx.Stderr, "copied %d tools\n", len(missing))
186
// defaultToolsUrl leads to the juju distribution on S3.
187
var defaultToolsLocation string = "https://juju-dist.s3.amazonaws.com/"
189
// listBucketResult is the top level XML element of the storage index.
190
type listBucketResult struct {
191
XMLName xml.Name `xml: "ListBucketResult"`
200
// content describes one entry of the storage index.
201
type contents struct {
202
XMLName xml.Name `xml: "Contents"`
204
LastModified time.Time
210
// httpToolsReader implements the environs.StorageReader interface by
211
// accessing the juju-core public store simply using http.
212
type httpToolsReader struct {
216
// newHttpToolsReader creates a storage reader for the http
217
// access to the juju-core public store.
218
func newHttpToolsReader() environs.StorageReader {
219
return &httpToolsReader{defaultToolsLocation}
222
// Get opens the given storage file and returns a ReadCloser
223
// that can be used to read its contents.
224
func (h *httpToolsReader) Get(name string) (io.ReadCloser, error) {
225
locationName, err := h.URL(name)
229
resp, err := http.Get(locationName)
230
if err != nil && resp.StatusCode == http.StatusNotFound {
231
return nil, &errors.NotFoundError{err, ""}
233
return resp.Body, nil
236
// List lists all names in the storage with the given prefix.
237
func (h *httpToolsReader) List(prefix string) ([]string, error) {
238
lbr, err := h.getListBucketResult()
243
for _, c := range lbr.Contents {
244
if strings.HasPrefix(c.Key, prefix) {
245
names = append(names, c.Key)
252
// URL returns a URL that can be used to access the given storage file.
253
func (h *httpToolsReader) URL(name string) (string, error) {
254
if strings.HasSuffix(h.location, "/") {
255
return h.location + name, nil
257
return h.location + "/" + name, nil
260
// getListBucketResult retrieves the index of the storage,
261
func (h *httpToolsReader) getListBucketResult() (*listBucketResult, error) {
262
resp, err := http.Get(h.location)
266
defer resp.Body.Close()
267
buf, err := ioutil.ReadAll(resp.Body)
271
var lbr listBucketResult
272
err = xml.Unmarshal(buf, &lbr)