Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(351)

Delta Between Two Patch Sets: environs/tools.go

Issue 6198064: environs: upload juju tools to cloud.
Left Patch Set: environs: upload juju tools to cloud. Created 12 years, 10 months ago
Right Patch Set: environs: upload juju tools to cloud. Created 12 years, 9 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « environs/jujutest/test.go ('k') | environs/tools_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 package environs 1 package environs
2 2
3 import ( 3 import (
4 "archive/tar" 4 "archive/tar"
5 "compress/gzip" 5 "compress/gzip"
6 "fmt" 6 "fmt"
7 "io" 7 "io"
8 "io/ioutil" 8 "io/ioutil"
9 "launchpad.net/juju/go/log" 9 "launchpad.net/juju/go/log"
10 "launchpad.net/juju/go/version" 10 "launchpad.net/juju/go/version"
11 "os" 11 "os"
12 "os/exec" 12 "os/exec"
13 "path/filepath" 13 "path/filepath"
14 "regexp" 14 "regexp"
15 "runtime" 15 "runtime"
16 "strings" 16 "strings"
17 ) 17 )
18 18
19 var CurrentUbuntuRelease = "precise" // TODO find out actual vers ion. 19 // TODO find out actual architecture and Ubuntu release.
20 var CurrentArch = ubuntuArch(runtime.GOARCH) // TODO better than this 20 var CurrentSeries = "precise" // current Ubuntu release name.·····
21 var CurrentArch = ubuntuArch(runtime.GOARCH)
21 22
22 func ubuntuArch(arch string) string { 23 func ubuntuArch(arch string) string {
23 if arch == "386" { 24 if arch == "386" {
24 arch = "i386" 25 arch = "i386"
25 } 26 }
26 return arch 27 return arch
27 } 28 }
28 29
29 var toolPrefix = "tools/juju-" 30 var toolPrefix = "tools/juju-"
30 31
31 var toolFilePat = regexp.MustCompile(`^`+toolPrefix+`(\d+\.\d+\.\d+)-([^-]+)-([^ -]+)\.tgz$`) 32 var toolFilePat = regexp.MustCompile(`^` + toolPrefix + `(\d+\.\d+\.\d+)-([^-]+) -([^-]+)\.tgz$`)
32 33
33 // toolsPathForVersion returns a path for the juju tools with the 34 // toolsPathForVersion returns a path for the juju tools with the
34 // given version, OS and architecture. 35 // given version, OS and architecture.
35 func toolsPathForVersion(v version.Version, series, arch string) string { 36 func toolsPathForVersion(v version.Version, series, arch string) string {
36 return fmt.Sprintf(toolPrefix+"%v-%s-%s.tgz", v, series, arch) 37 return fmt.Sprintf(toolPrefix+"%v-%s-%s.tgz", v, series, arch)
37 } 38 }
38 39
39 // ToolsPath gives the path for the current juju tools, as expected 40 // ToolsPath gives the path for the current juju tools, as expected
40 // by environs.Environ.PutFile, for example. 41 // by environs.Environ.PutFile, for example.
41 var toolsPath = toolsPathForVersion(version.Current, CurrentUbuntuRelease, Curre ntArch) 42 var toolsPath = toolsPathForVersion(version.Current, CurrentSeries, CurrentArch)
42 43
43 // PutTools uploads the current version of the juju tools 44 // PutTools uploads the current version of the juju tools
44 // executables to the given storage. 45 // executables to the given storage.
45 // TODO find binaries from $PATH when go dev environment not available. 46 // TODO find binaries from $PATH when not using a development
47 // version of juju within a $GOPATH.
46 func PutTools(storage StorageWriter) error { 48 func PutTools(storage StorageWriter) error {
47 // We create the entire archive before asking the environment to 49 // We create the entire archive before asking the environment to
48 // start uploading so that we can be sure we have archived 50 // start uploading so that we can be sure we have archived
49 // correctly. 51 // correctly.
50 f, err := ioutil.TempFile("", "juju-tgz") 52 f, err := ioutil.TempFile("", "juju-tgz")
51 if err != nil { 53 if err != nil {
52 return err 54 return err
53 } 55 }
54 defer f.Close() 56 defer f.Close()
55 defer os.Remove(f.Name()) 57 defer os.Remove(f.Name())
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 if !isExecutable(ent) { 90 if !isExecutable(ent) {
89 return fmt.Errorf("archive: found non-executable file %q ", filepath.Join(dir, ent.Name())) 91 return fmt.Errorf("archive: found non-executable file %q ", filepath.Join(dir, ent.Name()))
90 } 92 }
91 h := tarHeader(ent) 93 h := tarHeader(ent)
92 // ignore local umask 94 // ignore local umask
93 h.Mode = 0755 95 h.Mode = 0755
94 err := tarw.WriteHeader(h) 96 err := tarw.WriteHeader(h)
95 if err != nil { 97 if err != nil {
96 return err 98 return err
97 } 99 }
98 » » if err := readFile(tarw, filepath.Join(dir, ent.Name())); err != nil { 100 » » if err := copyFile(tarw, filepath.Join(dir, ent.Name())); err != nil {
99 return err 101 return err
100 } 102 }
101 } 103 }
102 return nil 104 return nil
103 } 105 }
104 106
105 // readFile writes the contents of the given file to w. 107 // copyFile writes the contents of the given file to w.
106 func readFile(w io.Writer, file string) error { 108 func copyFile(w io.Writer, file string) error {
107 f, err := os.Open(file) 109 f, err := os.Open(file)
108 if err != nil { 110 if err != nil {
109 return err 111 return err
110 } 112 }
111 defer f.Close() 113 defer f.Close()
112 _, err = io.Copy(w, f) 114 _, err = io.Copy(w, f)
113 return err 115 return err
114 } 116 }
115 117
116 // tarHeader returns a tar file header given the file's stat 118 // tarHeader returns a tar file header given the file's stat
(...skipping 20 matching lines...) Expand all
137 139
138 // closeErrorCheck means that we can ensure that 140 // closeErrorCheck means that we can ensure that
139 // Close errors do not get lost even when we defer them, 141 // Close errors do not get lost even when we defer them,
140 func closeErrorCheck(errp *error, c io.Closer) { 142 func closeErrorCheck(errp *error, c io.Closer) {
141 err := c.Close() 143 err := c.Close()
142 if *errp == nil { 144 if *errp == nil {
143 *errp = err 145 *errp = err
144 } 146 }
145 } 147 }
146 148
147 // FindTools tries to find a set of tools appropriate for the current 149 type toolsSpec struct {
148 // version and platform and returns a URL that can be used to access 150 » vers version.Version
149 // them in gzipped tar archive format. 151 » series string
150 func FindTools(env Environ) (url string, err error) { 152 » arch string
151 » storage, path, err := findTools(env) 153 }
154
155 // FindTools tries to find a set of tools appropriate for the given
156 // version, Ubuntu series and architecture, and returns a URL that can
157 // be used to access them in gzipped tar archive format.
158 func FindTools(env Environ, vers version.Version, series, arch string) (url stri ng, err error) {
159 » storage, path, err := findTools(env, toolsSpec{vers, series, arch})
152 if err != nil { 160 if err != nil {
153 return "", err 161 return "", err
154 } 162 }
155 return storage.URL(path) 163 return storage.URL(path)
156 } 164 }
157 165
158 // GetTools finds the latest compatible version of the juju tools 166 // GetTools finds the latest compatible version of the juju tools
159 // and downloads them into the given directory. 167 // and downloads them into the given directory.
160 func GetTools(env Environ, dir string) error { 168 func GetTools(env Environ, dir string) error {
161 » storage, path, err := findTools(env) 169 » storage, path, err := findTools(env, toolsSpec{version.Current, CurrentS eries, CurrentArch})
162 if err != nil { 170 if err != nil {
163 return err 171 return err
164 } 172 }
165 r, err := storage.Get(path) 173 r, err := storage.Get(path)
166 if err != nil { 174 if err != nil {
167 return err 175 return err
168 } 176 }
169 defer r.Close() 177 defer r.Close()
170 178
171 r, err = gzip.NewReader(r) 179 r, err = gzip.NewReader(r)
(...skipping 17 matching lines...) Expand all
189 197
190 name := filepath.Join(dir, hdr.Name) 198 name := filepath.Join(dir, hdr.Name)
191 if err := writeFile(name, os.FileMode(hdr.Mode&0777), tr); err ! = nil { 199 if err := writeFile(name, os.FileMode(hdr.Mode&0777), tr); err ! = nil {
192 return fmt.Errorf("tar extract %q failed: %v", name, err ) 200 return fmt.Errorf("tar extract %q failed: %v", name, err )
193 } 201 }
194 } 202 }
195 panic("not reached") 203 panic("not reached")
196 } 204 }
197 205
198 // findToolsPath is an internal version of FindTools that returns the 206 // findToolsPath is an internal version of FindTools that returns the
199 // found StorageReader and the path within that storage. 207 // storage in which the tools have been found, and the path within that storage.
200 func findTools(env Environ) (storage StorageReader, path string, err error) { 208 func findTools(env Environ, spec toolsSpec) (storage StorageReader, path string, err error) {
201 storage = env.Storage() 209 storage = env.Storage()
202 » path, err = findToolsPath(storage) 210 » path, err = findToolsPath(storage, spec)
203 if _, ok := err.(*NotFoundError); ok { 211 if _, ok := err.(*NotFoundError); ok {
204 storage = env.PublicStorage() 212 storage = env.PublicStorage()
205 » » path, err = findToolsPath(storage) 213 » » path, err = findToolsPath(storage, spec)
206 } 214 }
207 if err != nil { 215 if err != nil {
208 return nil, "", err 216 return nil, "", err
209 } 217 }
210 return 218 return
211 } 219 }
212 220
213 // findToolsPath looks for the tools in the given storage. 221 // findToolsPath looks for the tools in the given storage.
214 func findToolsPath(store StorageReader) (path string, err error) { 222 func findToolsPath(store StorageReader, spec toolsSpec) (path string, err error) {
215 » names, err := store.List(fmt.Sprintf("%s%d.", toolPrefix, version.Curren t.Major)) 223 » names, err := store.List(fmt.Sprintf("%s%d.", toolPrefix, spec.vers.Majo r))
216 if err != nil { 224 if err != nil {
217 return "", err 225 return "", err
218 } 226 }
219 if len(names) == 0 { 227 if len(names) == 0 {
220 return "", &NotFoundError{fmt.Errorf("no compatible tools found" )} 228 return "", &NotFoundError{fmt.Errorf("no compatible tools found" )}
221 } 229 }
222 bestVersion := version.Version{Major: -1} 230 bestVersion := version.Version{Major: -1}
223 bestName := "" 231 bestName := ""
224 for _, name := range names { 232 for _, name := range names {
225 m := toolFilePat.FindStringSubmatch(name) 233 m := toolFilePat.FindStringSubmatch(name)
226 if m == nil { 234 if m == nil {
227 log.Printf("unexpected tools file found %q", name) 235 log.Printf("unexpected tools file found %q", name)
228 continue 236 continue
229 } 237 }
230 vers, err := version.Parse(m[1]) 238 vers, err := version.Parse(m[1])
231 if err != nil { 239 if err != nil {
232 log.Printf("failed to parse version %q: %v", name, err) 240 log.Printf("failed to parse version %q: %v", name, err)
233 continue 241 continue
234 } 242 }
235 » » if m[2] != CurrentUbuntuRelease { 243 » » if m[2] != spec.series {
236 continue 244 continue
237 } 245 }
238 // TODO allow different architectures. 246 // TODO allow different architectures.
239 » » if m[3] != CurrentArch { 247 » » if m[3] != spec.arch {
240 » » » continue 248 » » » continue
241 » » } 249 » » }
242 » » if vers.Major != version.Current.Major { 250 » » if vers.Major != spec.vers.Major {
243 continue 251 continue
244 } 252 }
245 if bestVersion.Less(vers) { 253 if bestVersion.Less(vers) {
246 bestVersion = vers 254 bestVersion = vers
247 bestName = name 255 bestName = name
248 } 256 }
249 } 257 }
250 if bestVersion.Major < 0 { 258 if bestVersion.Major < 0 {
251 return "", &NotFoundError{fmt.Errorf("no compatible tools found" )} 259 return "", &NotFoundError{fmt.Errorf("no compatible tools found" )}
252 } 260 }
(...skipping 24 matching lines...) Expand all
277 func writeFile(name string, mode os.FileMode, r io.Reader) error { 285 func writeFile(name string, mode os.FileMode, r io.Reader) error {
278 f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) 286 f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
279 if err != nil { 287 if err != nil {
280 return err 288 return err
281 } 289 }
282 defer f.Close() 290 defer f.Close()
283 _, err = io.Copy(f, r) 291 _, err = io.Copy(f, r)
284 return err 292 return err
285 } 293 }
286 294
287 // EmptyStorage holds a StorageReader object 295 // EmptyStorage holds a StorageReader object that contains nothing.
288 // that contains nothing.
289 var EmptyStorage StorageReader = emptyStorage{} 296 var EmptyStorage StorageReader = emptyStorage{}
290 297
291 type emptyStorage struct{} 298 type emptyStorage struct{}
292 299
293 func (s emptyStorage) Get(name string) (io.ReadCloser, error) { 300 func (s emptyStorage) Get(name string) (io.ReadCloser, error) {
294 return nil, &NotFoundError{fmt.Errorf("file %q not found in empty storag e", name)} 301 return nil, &NotFoundError{fmt.Errorf("file %q not found in empty storag e", name)}
295 } 302 }
296 303
297 func (s emptyStorage) URL(string) (string, error) { 304 func (s emptyStorage) URL(string) (string, error) {
298 return "", fmt.Errorf("empty storage has no URLs") 305 return "", fmt.Errorf("empty storage has no URLs")
299 } 306 }
300 307
301 func (s emptyStorage) List(prefix string) ([]string, error) { 308 func (s emptyStorage) List(prefix string) ([]string, error) {
302 return nil, nil 309 return nil, nil
303 } 310 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b