Merge lp:~themue/juju-core/033-sync-tools-source into lp:~go-bot/juju-core/trunk
- 033-sync-tools-source
- Merge into trunk
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 |
Related bugs: |
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.
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.
Frank Mueller (themue) wrote : | # |
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:/
File cmd/juju/
https:/
cmd/juju/
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:/
cmd/juju/
StorageReader accessing the local
fileStorageReader implements StorageReader backed by the local
filesystem
https:/
cmd/juju/
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:/
File cmd/juju/
https:/
cmd/juju/
version.Binary) {
can you s/s/storagePath/ to be clear what this string is?
Frank Mueller (themue) wrote : | # |
Please take a look.
https:/
File cmd/juju/
https:/
cmd/juju/
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:/
cmd/juju/
StorageReader accessing the local
On 2013/07/10 10:59:09, jameinel wrote:
> fileStorageReader implements StorageReader backed by the local
filesystem
Done.
https:/
cmd/juju/
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 fileStorageRead
https:/
File cmd/juju/
https:/
cmd/juju/
version.Binary) {
On 2013/07/10 10:59:09, jameinel wrote:
> can you s/s/storagePath/ to be clear what this string is?
Done.
Dimiter Naydenov (dimitern) wrote : | # |
A few thoughts.
https:/
File cmd/juju/
https:/
cmd/juju/
local cloud.
please add an example of the two kinds of --source options: file path
and url.
https:/
cmd/juju/
"chose a location on the file system or via http as source")
s/"default"/""/ (see below)
https:/
cmd/juju/
== "default" {
"default" seems icky - how's this different from "" ? why do we need it?
https:/
cmd/juju/
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:/
cmd/juju/
maybe len(f.path) + len("/") is less ambiguous.
https:/
cmd/juju/
why are you sorting it?
https:/
cmd/juju/
in this case it should be filepath.Join, I think
https:/
File cmd/juju/
https:/
cmd/juju/
s/tool/tools/
https:/
cmd/juju/
shouldn't you be testing for specific contents of ctx instead?
https:/
cmd/juju/
isn't dir the same as storagePath after this, use that instead? why the
split? why not Base or Dir?
https:/
cmd/juju/
why not ioutil.WriteFile instead of most of the block below?
Frank Mueller (themue) wrote : | # |
Please take a look.
https:/
File cmd/juju/
https:/
cmd/juju/
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:/
cmd/juju/
"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:/
cmd/juju/
== "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:/
cmd/juju/
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:/
cmd/juju/
On 2013/07/11 20:40:40, dimitern wrote:
> maybe len(f.path) + len("/") is less ambiguous.
Simply added a comment. ;)
https:/
cmd/juju/
On 2013/07/11 20:40:40, dimitern wrote:
> why are you sorting it?
See interface specification: "... in alphabetical order ...".
https:/
cmd/juju/
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:/
File cmd/juju/
https:/
cmd/juju/
On 2013/07/11 20:40:40, dimitern wrote:
> s/tool/tools/
Done.
https:/
cmd/juju/
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...
Dimiter Naydenov (dimitern) wrote : | # |
Thanks for the changes, LGTM.
Preview Diff
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 | +} |
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: synctools. go synctools_ test.go
A [revision details]
M cmd/juju/
M cmd/juju/