~ubuntu-branches/ubuntu/saucy/juju-core/saucy

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/environs/tools/storage.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 16:02:16 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20130820160216-5yu1llasa2e2youn
Tags: 1.13.1-0ubuntu1
* New upstream release.
  - Build and install juju metadata plugin.
  - d/NEWS: Add some guidance on upgrading environments from 1.11.x
    to 1.13.x.
* d/NEWS: Add details about lack of upgrade path from juju < 1.11
  and how to interact with older juju environments.

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 tools
5
 
 
6
 
import (
7
 
        "errors"
8
 
        "fmt"
9
 
        "io"
10
 
        "io/ioutil"
11
 
        "os"
12
 
        "strings"
13
 
 
14
 
        "launchpad.net/juju-core/state"
15
 
        "launchpad.net/juju-core/version"
16
 
)
17
 
 
18
 
var ErrNoTools = errors.New("no tools available")
19
 
var ErrNoMatches = errors.New("no matching tools available")
20
 
 
21
 
const (
22
 
        DefaultToolPrefix = "tools/juju-"
23
 
        toolSuffix        = ".tgz"
24
 
)
25
 
 
26
 
var toolPrefix string = DefaultToolPrefix
27
 
 
28
 
// SetToolPrefix changes the prefix used to compose the tools tarball file name.
29
 
func SetToolPrefix(prefix string) {
30
 
        toolPrefix = prefix
31
 
}
32
 
 
33
 
// StorageName returns the name that is used to store and retrieve the
34
 
// given version of the juju tools.
35
 
func StorageName(vers version.Binary) string {
36
 
        return toolPrefix + vers.String() + toolSuffix
37
 
}
38
 
 
39
 
// URLLister exposes to ReadList the relevant capabilities of an
40
 
// environs.Storage; it exists to foil an import cycle.
41
 
type URLLister interface {
42
 
        URL(name string) (string, error)
43
 
        List(prefix string) ([]string, error)
44
 
}
45
 
 
46
 
// ReadList returns a List of the tools in store with the given major version.
47
 
// If store contains no such tools, it returns ErrNoMatches.
48
 
func ReadList(storage URLLister, majorVersion int) (List, error) {
49
 
        logger.Debugf("reading v%d.* tools", majorVersion)
50
 
        names, err := storage.List(toolPrefix)
51
 
        if err != nil {
52
 
                return nil, err
53
 
        }
54
 
        var list List
55
 
        var foundAnyTools bool
56
 
        for _, name := range names {
57
 
                if !strings.HasPrefix(name, toolPrefix) || !strings.HasSuffix(name, toolSuffix) {
58
 
                        continue
59
 
                }
60
 
                var t state.Tools
61
 
                vers := name[len(toolPrefix) : len(name)-len(toolSuffix)]
62
 
                if t.Binary, err = version.ParseBinary(vers); err != nil {
63
 
                        continue
64
 
                }
65
 
                foundAnyTools = true
66
 
                if t.Major != majorVersion {
67
 
                        continue
68
 
                }
69
 
                logger.Debugf("found %s", vers)
70
 
                if t.URL, err = storage.URL(name); err != nil {
71
 
                        return nil, err
72
 
                }
73
 
                list = append(list, &t)
74
 
        }
75
 
        if len(list) == 0 {
76
 
                if foundAnyTools {
77
 
                        return nil, ErrNoMatches
78
 
                }
79
 
                return nil, ErrNoTools
80
 
        }
81
 
        return list, nil
82
 
}
83
 
 
84
 
// URLPutter exposes to Upload the relevant capabilities of an
85
 
// environs.Storage; it exists to foil an import cycle.
86
 
type URLPutter interface {
87
 
        URL(name string) (string, error)
88
 
        Put(name string, r io.Reader, length int64) error
89
 
}
90
 
 
91
 
// Upload builds whatever version of launchpad.net/juju-core is in $GOPATH,
92
 
// uploads it to the given storage, and returns a Tools instance describing
93
 
// them. If forceVersion is not nil, the uploaded tools bundle will report
94
 
// the given version number; if any fakeSeries are supplied, additional copies
95
 
// of the built tools will be uploaded for use by machines of those series.
96
 
// Juju tools built for one series do not necessarily run on another, but this
97
 
// func exists only for development use cases.
98
 
func Upload(storage URLPutter, forceVersion *version.Number, fakeSeries ...string) (*state.Tools, error) {
99
 
        // TODO(rog) find binaries from $PATH when not using a development
100
 
        // version of juju within a $GOPATH.
101
 
 
102
 
        logger.Debugf("Uploading tools for %v", fakeSeries)
103
 
        // We create the entire archive before asking the environment to
104
 
        // start uploading so that we can be sure we have archived
105
 
        // correctly.
106
 
        f, err := ioutil.TempFile("", "juju-tgz")
107
 
        if err != nil {
108
 
                return nil, err
109
 
        }
110
 
        defer f.Close()
111
 
        defer os.Remove(f.Name())
112
 
        toolsVersion, err := bundleTools(f, forceVersion)
113
 
        if err != nil {
114
 
                return nil, err
115
 
        }
116
 
        fileInfo, err := f.Stat()
117
 
        if err != nil {
118
 
                return nil, fmt.Errorf("cannot stat newly made tools archive: %v", err)
119
 
        }
120
 
        size := fileInfo.Size()
121
 
        logger.Infof("built %v (%dkB)", toolsVersion, (size+512)/1024)
122
 
        putTools := func(vers version.Binary) (string, error) {
123
 
                if _, err := f.Seek(0, 0); err != nil {
124
 
                        return "", fmt.Errorf("cannot seek to start of tools archive: %v", err)
125
 
                }
126
 
                name := StorageName(vers)
127
 
                logger.Infof("uploading %s", vers)
128
 
                if err := storage.Put(name, f, size); err != nil {
129
 
                        return "", err
130
 
                }
131
 
                return name, nil
132
 
        }
133
 
        for _, series := range fakeSeries {
134
 
                if series != toolsVersion.Series {
135
 
                        fakeVersion := toolsVersion
136
 
                        fakeVersion.Series = series
137
 
                        if _, err := putTools(fakeVersion); err != nil {
138
 
                                return nil, err
139
 
                        }
140
 
                }
141
 
        }
142
 
        name, err := putTools(toolsVersion)
143
 
        if err != nil {
144
 
                return nil, err
145
 
        }
146
 
        url, err := storage.URL(name)
147
 
        if err != nil {
148
 
                return nil, err
149
 
        }
150
 
        return &state.Tools{toolsVersion, url}, nil
151
 
}