Merge lp:~themue/juju-core/033-sync-tools-source into lp:~go-bot/juju-core/trunk

Proposed by Frank Mueller
Status: Merged
Approved by: Frank Mueller
Approved revision: no longer in the source branch.
Merged at revision: 1440
Proposed branch: lp:~themue/juju-core/033-sync-tools-source
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 211 lines (+123/-5)
2 files modified
cmd/juju/synctools.go (+86/-4)
cmd/juju/synctools_test.go (+37/-1)
To merge this branch: bzr merge lp:~themue/juju-core/033-sync-tools-source
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+173884@code.launchpad.net

Commit message

cmd/juju: added source flag to sync-tools

The sync-tools command now allows to specify
a local directory as the source for the tools
to synchronize. If not specified otherwise
the standard EC2 HTTP source is taken.

https://codereview.appspot.com/11103043/

Description of the change

cmd/juju: added source flag to sync-tools

The sync-tools command now allows to specify
a local directory as the source for the tools
to synchronize. If not specified otherwise
the standard EC2 HTTP source is taken.

https://codereview.appspot.com/11103043/

To post a comment you must log in.
Revision history for this message
Frank Mueller (themue) wrote :

Reviewers: mp+173884_code.launchpad.net,

Message:
Please take a look.

Description:
cmd/juju: added source flag to sync-tools

The sync-tools command now allows to specify
a local directory as the source for the tools
to synchronize. If not specified otherwise
the standard EC2 HTTP source is taken.

https://code.launchpad.net/~themue/juju-core/033-sync-tools-source/+merge/173884

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/11103043/

Affected files:
   A [revision details]
   M cmd/juju/synctools.go
   M cmd/juju/synctools_test.go

Revision history for this message
John A Meinel (jameinel) wrote :

we probably need to think carefully about whether we need 'path' instead
of 'filepath' (if something is called URL I would be surprised to see
'\' in the string.)

otherwise LGTM
just minor stuff.

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go
File cmd/juju/synctools.go (right):

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode185
cmd/juju/synctools.go:185: if sourceFlagValue == "default" {
I would have actually expected "" to mean default, but I can see people
doing: --source=default as a way to be explicit.

Even so, I would probably like to have:

if sourceFlagValue == "" || sourceFlagValue == "default" {
}

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode191
cmd/juju/synctools.go:191: // fileStorageReader implements the
StorageReader accessing the local
fileStorageReader implements StorageReader backed by the local
filesystem

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode250
cmd/juju/synctools.go:250: }
I'm a little concerned about path.Join vs filepath.Join

On Windows, filepath.Join is going to return "\" paths, which is
inappropriate for something called a URL.

Since you can still access files via '/' paths, I'd rather see us just
use "path.Join" in here.

though I'm not sure what filepath.Clean does to know whether it munges
all of our '/' characters.

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools_test.go
File cmd/juju/synctools_test.go (right):

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools_test.go#newcode209
cmd/juju/synctools_test.go:209: func putBinary(c *C, s string, v
version.Binary) {
can you s/s/storagePath/ to be clear what this string is?

https://codereview.appspot.com/11103043/

Revision history for this message
Frank Mueller (themue) wrote :

Please take a look.

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go
File cmd/juju/synctools.go (right):

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode185
cmd/juju/synctools.go:185: if sourceFlagValue == "default" {
On 2013/07/10 10:59:09, jameinel wrote:
> I would have actually expected "" to mean default, but I can see
people doing:
> --source=default as a way to be explicit.

> Even so, I would probably like to have:

> if sourceFlagValue == "" || sourceFlagValue == "default" {
> }

Done.

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode191
cmd/juju/synctools.go:191: // fileStorageReader implements the
StorageReader accessing the local
On 2013/07/10 10:59:09, jameinel wrote:
> fileStorageReader implements StorageReader backed by the local
filesystem

Done.

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools.go#newcode250
cmd/juju/synctools.go:250: }
On 2013/07/10 10:59:09, jameinel wrote:
> I'm a little concerned about path.Join vs filepath.Join

> On Windows, filepath.Join is going to return "\" paths, which is
inappropriate
> for something called a URL.

> Since you can still access files via '/' paths, I'd rather see us just
use
> "path.Join" in here.

> though I'm not sure what filepath.Clean does to know whether it munges
all of
> our '/' characters.

Done. In our case it's relative unimportant as the method is only to
implement the interface and used internally in fileStorageReader.Get().

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools_test.go
File cmd/juju/synctools_test.go (right):

https://codereview.appspot.com/11103043/diff/1/cmd/juju/synctools_test.go#newcode209
cmd/juju/synctools_test.go:209: func putBinary(c *C, s string, v
version.Binary) {
On 2013/07/10 10:59:09, jameinel wrote:
> can you s/s/storagePath/ to be clear what this string is?

Done.

https://codereview.appspot.com/11103043/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

A few thoughts.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go
File cmd/juju/synctools.go (right):

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode50
cmd/juju/synctools.go:50: to avoid having to access data outside of the
local cloud.
please add an example of the two kinds of --source options: file path
and url.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode61
cmd/juju/synctools.go:61: f.StringVar(&c.source, "source", "default",
"chose a location on the file system or via http as source")
s/"default"/""/ (see below)

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode186
cmd/juju/synctools.go:186: if sourceFlagValue == "" || sourceFlagValue
== "default" {
"default" seems icky - how's this different from "" ? why do we need it?

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode206
cmd/juju/synctools.go:206: return nil, fmt.Errorf("specified source path
is no directory: %s", p)
s/no/not a/, also if you're saying "specified", please pass `path`
instead of `p`, since that what was passed before Clean (less confusing
for the user)

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode226
cmd/juju/synctools.go:226: pathlen := len(f.path) + 1
maybe len(f.path) + len("/") is less ambiguous.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode243
cmd/juju/synctools.go:243: sort.Strings(list)
why are you sorting it?

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode249
cmd/juju/synctools.go:249: return path.Join(f.path, name), nil
in this case it should be filepath.Join, I think

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go
File cmd/juju/synctools_test.go (right):

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode53
cmd/juju/synctools_test.go:53: // Create a local tool directory.
s/tool/tools/

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode106
cmd/juju/synctools_test.go:106: c.Assert(ctx, NotNil)
shouldn't you be testing for specific contents of ctx instead?

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode213
cmd/juju/synctools_test.go:213: dir, _ := filepath.Split(path)
isn't dir the same as storagePath after this, use that instead? why the
split? why not Base or Dir?

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode216
cmd/juju/synctools_test.go:216: file, err := os.Create(path)
why not ioutil.WriteFile instead of most of the block below?

https://codereview.appspot.com/11103043/

Revision history for this message
Frank Mueller (themue) wrote :
Download full text (4.0 KiB)

Please take a look.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go
File cmd/juju/synctools.go (right):

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode50
cmd/juju/synctools.go:50: to avoid having to access data outside of the
local cloud.
On 2013/07/11 20:40:40, dimitern wrote:
> please add an example of the two kinds of --source options: file path
and url.

Done, but only added a hint to the option to specify a source directory.
The idea of alternatively pass a HTTP location has been postponed.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode61
cmd/juju/synctools.go:61: f.StringVar(&c.source, "source", "default",
"chose a location on the file system or via http as source")
On 2013/07/11 20:40:40, dimitern wrote:
> s/"default"/""/ (see below)

Done, also changed wrong help text.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode186
cmd/juju/synctools.go:186: if sourceFlagValue == "" || sourceFlagValue
== "default" {
On 2013/07/11 20:40:40, dimitern wrote:
> "default" seems icky - how's this different from "" ? why do we need
it?

Idea has been to give better chance to make it explicit. But you're
right, it's not needed, so removed it.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode206
cmd/juju/synctools.go:206: return nil, fmt.Errorf("specified source path
is no directory: %s", p)
On 2013/07/11 20:40:40, dimitern wrote:
> s/no/not a/, also if you're saying "specified", please pass `path`
instead of
> `p`, since that what was passed before Clean (less confusing for the
user)

Done.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode226
cmd/juju/synctools.go:226: pathlen := len(f.path) + 1
On 2013/07/11 20:40:40, dimitern wrote:
> maybe len(f.path) + len("/") is less ambiguous.

Simply added a comment. ;)

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode243
cmd/juju/synctools.go:243: sort.Strings(list)
On 2013/07/11 20:40:40, dimitern wrote:
> why are you sorting it?

See interface specification: "... in alphabetical order ...".

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools.go#newcode249
cmd/juju/synctools.go:249: return path.Join(f.path, name), nil
On 2013/07/11 20:40:40, dimitern wrote:
> in this case it should be filepath.Join, I think

Had it before, but see discussion with John.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go
File cmd/juju/synctools_test.go (right):

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode53
cmd/juju/synctools_test.go:53: // Create a local tool directory.
On 2013/07/11 20:40:40, dimitern wrote:
> s/tool/tools/

Done.

https://codereview.appspot.com/11103043/diff/6001/cmd/juju/synctools_test.go#newcode106
cmd/juju/synctools_test.go:106: c.Assert(ctx, NotNil)
On 2013/07/11 20:40:40, dimitern wrote:
> shouldn't you be testing for specific contents of ctx instead?

See tests below, simply done it the same way. Don't know the original
intention, but this way nobody should ask why the test differs from
those below.

https...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Thanks for the changes, LGTM.

https://codereview.appspot.com/11103043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/synctools.go'
2--- cmd/juju/synctools.go 2013-06-20 06:42:25 +0000
3+++ cmd/juju/synctools.go 2013-07-12 08:35:41 +0000
4@@ -7,6 +7,10 @@
5 "bytes"
6 "fmt"
7 "io"
8+ "os"
9+ "path"
10+ "path/filepath"
11+ "sort"
12
13 "launchpad.net/gnuflag"
14 "launchpad.net/juju-core/cmd"
15@@ -29,6 +33,7 @@
16 dryRun bool
17 publicBucket bool
18 dev bool
19+ source string
20 }
21
22 var _ cmd.Command = (*SyncToolsCommand)(nil)
23@@ -40,9 +45,12 @@
24 Doc: `
25 This copies the Juju tools tarball from the official bucket into
26 your environment. This is generally done when you want Juju to be able
27-to run without having to access Amazon. Sometimes this is because the
28-environment does not have public access, and sometimes you just want
29-to avoid having to access data outside of the local cloud.
30+to run without having to access Amazon. Alternatively you can specify
31+a local directory as source.
32+
33+Sometimes this is because the environment does not have public access,
34+and sometimes you just want to avoid having to access data outside of
35+the local cloud.
36 `,
37 }
38 }
39@@ -53,6 +61,7 @@
40 f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied")
41 f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones")
42 f.BoolVar(&c.publicBucket, "public", false, "write to the public-bucket of the account, instead of the bucket private to the environment.")
43+ f.StringVar(&c.source, "source", "", "chose a location on the file system as source")
44
45 // BUG(lp:1163164) jam 2013-04-2 we would like to add a "source"
46 // location, rather than only copying from us-east-1
47@@ -106,7 +115,11 @@
48 }
49
50 func (c *SyncToolsCommand) Run(ctx *cmd.Context) error {
51- sourceStorage := ec2.NewHTTPStorageReader(defaultToolsLocation)
52+ sourceStorage, err := selectSourceStorage(c.source)
53+ if err != nil {
54+ log.Errorf("unable to select source: %v", err)
55+ return err
56+ }
57 targetEnv, err := environs.NewFromName(c.EnvName)
58 if err != nil {
59 log.Errorf("unable to read %q from environment", c.EnvName)
60@@ -170,3 +183,72 @@
61 fmt.Fprintf(ctx.Stderr, "copied %d tools\n", len(missing))
62 return nil
63 }
64+
65+// selectSourceStorage returns a storage reader based on the passed source flag.
66+func selectSourceStorage(sourceFlagValue string) (environs.StorageReader, error) {
67+ if sourceFlagValue == "" {
68+ return ec2.NewHTTPStorageReader(defaultToolsLocation), nil
69+ }
70+ return newFileStorageReader(sourceFlagValue)
71+}
72+
73+// fileStorageReader implements StorageReader backed by the local filesystem.
74+type fileStorageReader struct {
75+ path string
76+}
77+
78+// newFileStorageReader return a new storage reader for
79+// a directory inside the local file system.
80+func newFileStorageReader(path string) (environs.StorageReader, error) {
81+ p := filepath.Clean(path)
82+ fi, err := os.Stat(p)
83+ if err != nil {
84+ return nil, err
85+ }
86+ if !fi.Mode().IsDir() {
87+ return nil, fmt.Errorf("specified source path is not a directory: %s", path)
88+ }
89+ return &fileStorageReader{p}, nil
90+}
91+
92+// Get implements environs.StorageReader.Get.
93+func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {
94+ filename, err := f.URL(name)
95+ if err != nil {
96+ return nil, err
97+ }
98+ file, err := os.Open(filename)
99+ if err != nil {
100+ return nil, err
101+ }
102+ return file, nil
103+}
104+
105+// List implements environs.StorageReader.List.
106+func (f *fileStorageReader) List(prefix string) ([]string, error) {
107+ // Add one for the missing path separator.
108+ pathlen := len(f.path) + 1
109+ pattern := filepath.Join(f.path, prefix+"*")
110+ matches, err := filepath.Glob(pattern)
111+ if err != nil {
112+ return nil, err
113+ }
114+ list := []string{}
115+ for _, match := range matches {
116+ fi, err := os.Stat(match)
117+ if err != nil {
118+ return nil, err
119+ }
120+ if !fi.Mode().IsDir() {
121+ filename := match[pathlen:]
122+ list = append(list, filename)
123+ }
124+ }
125+ sort.Strings(list)
126+ return list, nil
127+}
128+
129+// URL implements environs.StorageReader.URL.
130+func (f *fileStorageReader) URL(name string) (string, error) {
131+ return path.Join(f.path, name), nil
132+}
133
134=== modified file 'cmd/juju/synctools_test.go'
135--- cmd/juju/synctools_test.go 2013-06-20 13:42:24 +0000
136+++ cmd/juju/synctools_test.go 2013-07-12 08:35:41 +0000
137@@ -4,6 +4,10 @@
138 package main
139
140 import (
141+ "io/ioutil"
142+ "os"
143+ "path/filepath"
144+
145 . "launchpad.net/gocheck"
146 "launchpad.net/juju-core/cmd"
147 "launchpad.net/juju-core/environs"
148@@ -21,6 +25,7 @@
149 origVersion version.Binary
150 origLocation string
151 storage *envtesting.EC2HTTPTestStorage
152+ localStorage string
153 }
154
155 func (s *syncToolsSuite) SetUpTest(c *C) {
156@@ -42,12 +47,17 @@
157 c.Assert(err, IsNil)
158 envtesting.RemoveAllTools(c, s.targetEnv)
159
160- // Create a source environment and populate its public tools.
161+ // Create a source storage.
162 s.storage, err = envtesting.NewEC2HTTPTestStorage("127.0.0.1")
163 c.Assert(err, IsNil)
164
165+ // Create a local tools directory.
166+ s.localStorage = c.MkDir()
167+
168+ // Populate both with the public tools.
169 for _, vers := range vAll {
170 s.storage.PutBinary(vers)
171+ putBinary(c, s.localStorage, vers)
172 }
173
174 s.origLocation = defaultToolsLocation
175@@ -91,6 +101,20 @@
176 c.Assert(err, Equals, tools.ErrNoTools)
177 }
178
179+func (s *syncToolsSuite) TestCopyNewestFromFilesystem(c *C) {
180+ ctx, err := runSyncToolsCommand(c, "-e", "test-target", "--source", s.localStorage)
181+ c.Assert(err, IsNil)
182+ c.Assert(ctx, NotNil)
183+
184+ // Newest released v1 tools made available to target env.
185+ targetTools, err := environs.FindAvailableTools(s.targetEnv, 1)
186+ c.Assert(err, IsNil)
187+ assertToolsList(c, targetTools, v100all)
188+
189+ // Public bucket was not touched.
190+ assertEmpty(c, s.targetEnv.PublicStorage())
191+}
192+
193 func (s *syncToolsSuite) TestCopyNewestFromDummy(c *C) {
194 ctx, err := runSyncToolsCommand(c, "-e", "test-target")
195 c.Assert(err, IsNil)
196@@ -181,3 +205,15 @@
197 v200p64 = version.MustParseBinary("2.0.0-precise-amd64")
198 vAll = append(v1all, v200p64)
199 )
200+
201+// putBinary stores a faked binary in the test directory.
202+func putBinary(c *C, storagePath string, v version.Binary) {
203+ data := v.String()
204+ name := tools.StorageName(v)
205+ filename := filepath.Join(storagePath, name)
206+ dir := filepath.Dir(filename)
207+ err := os.MkdirAll(dir, 0755)
208+ c.Assert(err, IsNil)
209+ err = ioutil.WriteFile(filename, []byte(data), 0666)
210+ c.Assert(err, IsNil)
211+}

Subscribers

People subscribed via source and target branches

to status/vote changes: