Merge lp:~axwalk/juju-core/localstorage-to-httpstorage into lp:~go-bot/juju-core/trunk
- localstorage-to-httpstorage
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Andrew Wilkins |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1832 |
Proposed branch: | lp:~axwalk/juju-core/localstorage-to-httpstorage |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
953 lines (+211/-212) 12 files modified
environs/bootstrap/bootstrap_test.go (+8/-15) environs/filestorage/filestorage.go (+31/-19) environs/filestorage/filestorage_test.go (+45/-11) environs/httpstorage/backend.go (+36/-76) environs/httpstorage/backend_test.go (+6/-3) environs/httpstorage/storage.go (+2/-1) environs/httpstorage/storage_test.go (+5/-5) environs/testing/storage.go (+7/-4) provider/azure/environ_test.go (+33/-49) provider/local/environ.go (+9/-4) provider/local/storage/worker.go (+14/-3) provider/state_test.go (+15/-22) |
To merge this branch: | bzr merge lp:~axwalk/juju-core/localstorage-to-httpstorage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+185958@code.launchpad.net |
Commit message
environs/
localstorage becomes httpstorage; it no longer
operates on the disk, but instead delegates to
another environs.Storage object.
This work goes together with changes to
environs/
write to temporary storage and atomically move,
in the same way that sshstorage has been coded.
Description of the change
environs/
localstorage becomes httpstorage; it no longer
operates on the disk, but instead delegates to
another environs.Storage object.
This work goes together with changes to
environs/
write to temporary storage and atomically move,
in the same way that sshstorage has been coded.
Andrew Wilkins (axwalk) wrote : | # |
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
Tim Penhey (thumper) wrote : | # |
A direct test for the glob change, and a helper function to create the
storage are needed.
https:/
File environs/
https:/
environs/
Why defer? There is no harm in calling Close right now.
https:/
environs/
isn't " < 0 " more readable and understandable?
https:/
File environs/
https:/
environs/
a 500 as it is
I argue with this status code.
5xx means it is our fault. 4xx means it is your fault. This is
certainly a bad request.
https:/
File environs/
https:/
environs/
httpstorage.
Given this is the second or third time I have seen these lines of code,
perhaps a testing helper function would be good.
Andrew Wilkins (axwalk) wrote : | # |
https:/
File environs/
https:/
environs/
On 2013/09/18 04:26:28, thumper wrote:
> Why defer? There is no harm in calling Close right now.
Indeed. I'll keep the defer, but move it closer to Get/before ReadAll.
https:/
environs/
On 2013/09/18 04:26:28, thumper wrote:
> isn't " < 0 " more readable and understandable?
Done.
https:/
File environs/
https:/
environs/
a 500 as it is
On 2013/09/18 04:26:28, thumper wrote:
> I argue with this status code.
> 5xx means it is our fault. 4xx means it is your fault. This is
certainly a bad
> request.
Yeah, I'll change the implementation (of filestorage's Get) and put this
back. Thanks.
https:/
File environs/
https:/
environs/
httpstorage.
On 2013/09/18 04:26:28, thumper wrote:
> Given this is the second or third time I have seen these lines of
code, perhaps
> a testing helper function would be good.
This is a testing helper function ;)
I will modify the other code to use it where it makes sense.
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
Tim Penhey (thumper) wrote : | # |
LGTM - although I'd prefer with the added testing helper method, this
isn't essential.
https:/
File environs/
https:/
environs/
On 2013/09/18 05:33:28, axw1 wrote:
> On 2013/09/18 04:26:28, thumper wrote:
> > Why defer? There is no harm in calling Close right now.
> Indeed. I'll keep the defer, but move it closer to Get/before ReadAll.
Good call. I missed the err just above.
Andrew Wilkins (axwalk) wrote : | # |
On 2013/09/18 05:43:00, thumper wrote:
> LGTM - although I'd prefer with the added testing helper method, this
isn't
> essential.
Done. I've also updated the code to use AddCleanup.
https:/
> File environs/
https:/
> environs/
> On 2013/09/18 05:33:28, axw1 wrote:
> > On 2013/09/18 04:26:28, thumper wrote:
> > > Why defer? There is no harm in calling Close right now.
> >
> > Indeed. I'll keep the defer, but move it closer to Get/before
ReadAll.
> Good call. I missed the err just above.
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~axwalk/juju-core/localstorage-to-httpstorage into lp:juju-core failed. Below is the output from the failed tests.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
-------
PANIC: service_
Preview Diff
1 | === modified file 'environs/bootstrap/bootstrap_test.go' | |||
2 | --- environs/bootstrap/bootstrap_test.go 2013-09-12 12:38:04 +0000 | |||
3 | +++ environs/bootstrap/bootstrap_test.go 2013-09-18 06:28:38 +0000 | |||
4 | @@ -13,7 +13,6 @@ | |||
5 | 13 | "launchpad.net/juju-core/environs" | 13 | "launchpad.net/juju-core/environs" |
6 | 14 | "launchpad.net/juju-core/environs/bootstrap" | 14 | "launchpad.net/juju-core/environs/bootstrap" |
7 | 15 | "launchpad.net/juju-core/environs/config" | 15 | "launchpad.net/juju-core/environs/config" |
8 | 16 | "launchpad.net/juju-core/environs/localstorage" | ||
9 | 17 | envtesting "launchpad.net/juju-core/environs/testing" | 16 | envtesting "launchpad.net/juju-core/environs/testing" |
10 | 18 | "launchpad.net/juju-core/provider/dummy" | 17 | "launchpad.net/juju-core/provider/dummy" |
11 | 19 | coretesting "launchpad.net/juju-core/testing" | 18 | coretesting "launchpad.net/juju-core/testing" |
12 | @@ -52,8 +51,7 @@ | |||
13 | 52 | 51 | ||
14 | 53 | func (s *bootstrapSuite) TestBootstrapNeedsSettings(c *gc.C) { | 52 | func (s *bootstrapSuite) TestBootstrapNeedsSettings(c *gc.C) { |
15 | 54 | env := newEnviron("bar", noKeysDefined) | 53 | env := newEnviron("bar", noKeysDefined) |
18 | 55 | cleanup := setDummyStorage(c, env) | 54 | s.setDummyStorage(c, env) |
17 | 56 | defer cleanup() | ||
19 | 57 | fixEnv := func(key string, value interface{}) { | 55 | fixEnv := func(key string, value interface{}) { |
20 | 58 | cfg, err := env.Config().Apply(map[string]interface{}{ | 56 | cfg, err := env.Config().Apply(map[string]interface{}{ |
21 | 59 | key: value, | 57 | key: value, |
22 | @@ -87,8 +85,7 @@ | |||
23 | 87 | 85 | ||
24 | 88 | func (s *bootstrapSuite) TestBootstrapEmptyConstraints(c *gc.C) { | 86 | func (s *bootstrapSuite) TestBootstrapEmptyConstraints(c *gc.C) { |
25 | 89 | env := newEnviron("foo", useDefaultKeys) | 87 | env := newEnviron("foo", useDefaultKeys) |
28 | 90 | cleanup := setDummyStorage(c, env) | 88 | s.setDummyStorage(c, env) |
27 | 91 | defer cleanup() | ||
29 | 92 | uploadTools(c, env) | 89 | uploadTools(c, env) |
30 | 93 | err := bootstrap.Bootstrap(env, constraints.Value{}) | 90 | err := bootstrap.Bootstrap(env, constraints.Value{}) |
31 | 94 | c.Assert(err, gc.IsNil) | 91 | c.Assert(err, gc.IsNil) |
32 | @@ -98,8 +95,7 @@ | |||
33 | 98 | 95 | ||
34 | 99 | func (s *bootstrapSuite) TestBootstrapSpecifiedConstraints(c *gc.C) { | 96 | func (s *bootstrapSuite) TestBootstrapSpecifiedConstraints(c *gc.C) { |
35 | 100 | env := newEnviron("foo", useDefaultKeys) | 97 | env := newEnviron("foo", useDefaultKeys) |
38 | 101 | cleanup := setDummyStorage(c, env) | 98 | s.setDummyStorage(c, env) |
37 | 102 | defer cleanup() | ||
39 | 103 | uploadTools(c, env) | 99 | uploadTools(c, env) |
40 | 104 | cons := constraints.MustParse("cpu-cores=2 mem=4G") | 100 | cons := constraints.MustParse("cpu-cores=2 mem=4G") |
41 | 105 | err := bootstrap.Bootstrap(env, cons) | 101 | err := bootstrap.Bootstrap(env, cons) |
42 | @@ -188,8 +184,7 @@ | |||
43 | 188 | 184 | ||
44 | 189 | func (s *bootstrapSuite) TestBootstrapNeedsTools(c *gc.C) { | 185 | func (s *bootstrapSuite) TestBootstrapNeedsTools(c *gc.C) { |
45 | 190 | env := newEnviron("foo", useDefaultKeys) | 186 | env := newEnviron("foo", useDefaultKeys) |
48 | 191 | cleanup := setDummyStorage(c, env) | 187 | s.setDummyStorage(c, env) |
47 | 192 | defer cleanup() | ||
49 | 193 | envtesting.RemoveFakeTools(c, env.Storage()) | 188 | envtesting.RemoveFakeTools(c, env.Storage()) |
50 | 194 | err := bootstrap.Bootstrap(env, constraints.Value{}) | 189 | err := bootstrap.Bootstrap(env, constraints.Value{}) |
51 | 195 | c.Check(err, gc.ErrorMatches, "cannot find bootstrap tools: no tools available") | 190 | c.Check(err, gc.ErrorMatches, "cannot find bootstrap tools: no tools available") |
52 | @@ -228,13 +223,11 @@ | |||
53 | 228 | // setDummyStorage injects the local provider's fake storage implementation | 223 | // setDummyStorage injects the local provider's fake storage implementation |
54 | 229 | // into the given environment, so that tests can manipulate storage as if it | 224 | // into the given environment, so that tests can manipulate storage as if it |
55 | 230 | // were real. | 225 | // were real. |
61 | 231 | // Returns a cleanup function that must be called when done with the storage. | 226 | func (s *bootstrapSuite) setDummyStorage(c *gc.C, env *bootstrapEnviron) { |
62 | 232 | func setDummyStorage(c *gc.C, env *bootstrapEnviron) func() { | 227 | closer, storage, _ := envtesting.CreateLocalTestStorage(c) |
63 | 233 | listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir()) | 228 | env.storage = storage |
59 | 234 | c.Assert(err, gc.IsNil) | ||
60 | 235 | env.storage = localstorage.Client(listener.Addr().String()) | ||
64 | 236 | envtesting.UploadFakeTools(c, env.storage) | 229 | envtesting.UploadFakeTools(c, env.storage) |
66 | 237 | return func() { listener.Close() } | 230 | s.AddCleanup(func(c *gc.C) { closer.Close() }) |
67 | 238 | } | 231 | } |
68 | 239 | 232 | ||
69 | 240 | func (e *bootstrapEnviron) Name() string { | 233 | func (e *bootstrapEnviron) Name() string { |
70 | 241 | 234 | ||
71 | === modified file 'environs/filestorage/filestorage.go' | |||
72 | --- environs/filestorage/filestorage.go 2013-09-12 05:04:40 +0000 | |||
73 | +++ environs/filestorage/filestorage.go 2013-09-18 06:28:38 +0000 | |||
74 | @@ -10,8 +10,10 @@ | |||
75 | 10 | "os" | 10 | "os" |
76 | 11 | "path/filepath" | 11 | "path/filepath" |
77 | 12 | "sort" | 12 | "sort" |
78 | 13 | "strings" | ||
79 | 13 | 14 | ||
80 | 14 | "launchpad.net/juju-core/environs" | 15 | "launchpad.net/juju-core/environs" |
81 | 16 | coreerrors "launchpad.net/juju-core/errors" | ||
82 | 15 | "launchpad.net/juju-core/utils" | 17 | "launchpad.net/juju-core/utils" |
83 | 16 | ) | 18 | ) |
84 | 17 | 19 | ||
85 | @@ -42,6 +44,15 @@ | |||
86 | 42 | // Get implements environs.StorageReader.Get. | 44 | // Get implements environs.StorageReader.Get. |
87 | 43 | func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) { | 45 | func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) { |
88 | 44 | filename := f.fullPath(name) | 46 | filename := f.fullPath(name) |
89 | 47 | fi, err := os.Stat(filename) | ||
90 | 48 | if err != nil { | ||
91 | 49 | if os.IsNotExist(err) { | ||
92 | 50 | err = coreerrors.NewNotFoundError(err, "") | ||
93 | 51 | } | ||
94 | 52 | return nil, err | ||
95 | 53 | } else if fi.IsDir() { | ||
96 | 54 | return nil, coreerrors.NotFoundf("no such file with name %q", name) | ||
97 | 55 | } | ||
98 | 45 | file, err := os.Open(filename) | 56 | file, err := os.Open(filename) |
99 | 46 | if err != nil { | 57 | if err != nil { |
100 | 47 | return nil, err | 58 | return nil, err |
101 | @@ -51,26 +62,23 @@ | |||
102 | 51 | 62 | ||
103 | 52 | // List implements environs.StorageReader.List. | 63 | // List implements environs.StorageReader.List. |
104 | 53 | func (f *fileStorageReader) List(prefix string) ([]string, error) { | 64 | func (f *fileStorageReader) List(prefix string) ([]string, error) { |
115 | 54 | // Add one for the missing path separator. | 65 | prefix = filepath.Join(f.path, prefix) |
116 | 55 | pathlen := len(f.path) + 1 | 66 | dir := filepath.Dir(prefix) |
117 | 56 | pattern := filepath.Join(f.path, prefix+"*") | 67 | var names []string |
118 | 57 | matches, err := filepath.Glob(pattern) | 68 | err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { |
109 | 58 | if err != nil { | ||
110 | 59 | return nil, err | ||
111 | 60 | } | ||
112 | 61 | list := []string{} | ||
113 | 62 | for _, match := range matches { | ||
114 | 63 | fi, err := os.Stat(match) | ||
119 | 64 | if err != nil { | 69 | if err != nil { |
126 | 65 | return nil, err | 70 | return err |
127 | 66 | } | 71 | } |
128 | 67 | if !fi.Mode().IsDir() { | 72 | if !info.IsDir() && strings.HasPrefix(path, prefix) { |
129 | 68 | filename := match[pathlen:] | 73 | names = append(names, path[len(f.path)+1:]) |
130 | 69 | list = append(list, filename) | 74 | } |
131 | 70 | } | 75 | return nil |
132 | 76 | }) | ||
133 | 77 | if err != nil && !os.IsNotExist(err) { | ||
134 | 78 | return nil, err | ||
135 | 71 | } | 79 | } |
138 | 72 | sort.Strings(list) | 80 | sort.Strings(names) |
139 | 73 | return list, nil | 81 | return names, nil |
140 | 74 | } | 82 | } |
141 | 75 | 83 | ||
142 | 76 | // URL implements environs.StorageReader.URL. | 84 | // URL implements environs.StorageReader.URL. |
143 | @@ -110,7 +118,11 @@ | |||
144 | 110 | 118 | ||
145 | 111 | func (f *fileStorageWriter) Remove(name string) error { | 119 | func (f *fileStorageWriter) Remove(name string) error { |
146 | 112 | fullpath := f.fullPath(name) | 120 | fullpath := f.fullPath(name) |
148 | 113 | return os.Remove(fullpath) | 121 | err := os.Remove(fullpath) |
149 | 122 | if os.IsNotExist(err) { | ||
150 | 123 | err = nil | ||
151 | 124 | } | ||
152 | 125 | return err | ||
153 | 114 | } | 126 | } |
154 | 115 | 127 | ||
155 | 116 | func (f *fileStorageWriter) RemoveAll() error { | 128 | func (f *fileStorageWriter) RemoveAll() error { |
156 | 117 | 129 | ||
157 | === modified file 'environs/filestorage/filestorage_test.go' | |||
158 | --- environs/filestorage/filestorage_test.go 2013-09-12 05:04:40 +0000 | |||
159 | +++ environs/filestorage/filestorage_test.go 2013-09-18 06:28:38 +0000 | |||
160 | @@ -10,6 +10,7 @@ | |||
161 | 10 | import ( | 10 | import ( |
162 | 11 | "bytes" | 11 | "bytes" |
163 | 12 | "io/ioutil" | 12 | "io/ioutil" |
164 | 13 | "os" | ||
165 | 13 | "path/filepath" | 14 | "path/filepath" |
166 | 14 | "testing" | 15 | "testing" |
167 | 15 | 16 | ||
168 | @@ -17,6 +18,8 @@ | |||
169 | 17 | 18 | ||
170 | 18 | "launchpad.net/juju-core/environs" | 19 | "launchpad.net/juju-core/environs" |
171 | 19 | "launchpad.net/juju-core/environs/filestorage" | 20 | "launchpad.net/juju-core/environs/filestorage" |
172 | 21 | coreerrors "launchpad.net/juju-core/errors" | ||
173 | 22 | jc "launchpad.net/juju-core/testing/checkers" | ||
174 | 20 | ) | 23 | ) |
175 | 21 | 24 | ||
176 | 22 | func TestPackage(t *testing.T) { | 25 | func TestPackage(t *testing.T) { |
177 | @@ -40,8 +43,10 @@ | |||
178 | 40 | c.Assert(err, gc.IsNil) | 43 | c.Assert(err, gc.IsNil) |
179 | 41 | } | 44 | } |
180 | 42 | 45 | ||
183 | 43 | func (s *filestorageSuite) createFile(c *gc.C) (fullpath string, data []byte) { | 46 | func (s *filestorageSuite) createFile(c *gc.C, name string) (fullpath string, data []byte) { |
184 | 44 | fullpath = filepath.Join(s.dir, "test-file") | 47 | fullpath = filepath.Join(s.dir, name) |
185 | 48 | dir := filepath.Dir(fullpath) | ||
186 | 49 | c.Assert(os.MkdirAll(dir, 0755), gc.IsNil) | ||
187 | 45 | data = []byte{1, 2, 3, 4, 5} | 50 | data = []byte{1, 2, 3, 4, 5} |
188 | 46 | err := ioutil.WriteFile(fullpath, data, 0644) | 51 | err := ioutil.WriteFile(fullpath, data, 0644) |
189 | 47 | c.Assert(err, gc.IsNil) | 52 | c.Assert(err, gc.IsNil) |
190 | @@ -49,15 +54,35 @@ | |||
191 | 49 | } | 54 | } |
192 | 50 | 55 | ||
193 | 51 | func (s *filestorageSuite) TestList(c *gc.C) { | 56 | func (s *filestorageSuite) TestList(c *gc.C) { |
199 | 52 | expectedpath, _ := s.createFile(c) | 57 | names := []string{ |
200 | 53 | files, err := s.reader.List("test-") | 58 | "a/b/c", |
201 | 54 | c.Assert(err, gc.IsNil) | 59 | "a/bb", |
202 | 55 | _, file := filepath.Split(expectedpath) | 60 | "a/c", |
203 | 56 | c.Assert(files, gc.DeepEquals, []string{file}) | 61 | "aa", |
204 | 62 | "b/c/d", | ||
205 | 63 | } | ||
206 | 64 | for _, name := range names { | ||
207 | 65 | s.createFile(c, name) | ||
208 | 66 | } | ||
209 | 67 | type test struct { | ||
210 | 68 | prefix string | ||
211 | 69 | expected []string | ||
212 | 70 | } | ||
213 | 71 | for i, test := range []test{ | ||
214 | 72 | {"a", []string{"a/b/c", "a/bb", "a/c", "aa"}}, | ||
215 | 73 | {"a/b", []string{"a/b/c", "a/bb"}}, | ||
216 | 74 | {"a/b/c", []string{"a/b/c"}}, | ||
217 | 75 | {"", names}, | ||
218 | 76 | } { | ||
219 | 77 | c.Logf("test %d: prefix=%q", i, test.prefix) | ||
220 | 78 | files, err := s.reader.List(test.prefix) | ||
221 | 79 | c.Assert(err, gc.IsNil) | ||
222 | 80 | c.Assert(files, gc.DeepEquals, test.expected) | ||
223 | 81 | } | ||
224 | 57 | } | 82 | } |
225 | 58 | 83 | ||
226 | 59 | func (s *filestorageSuite) TestURL(c *gc.C) { | 84 | func (s *filestorageSuite) TestURL(c *gc.C) { |
228 | 60 | expectedpath, _ := s.createFile(c) | 85 | expectedpath, _ := s.createFile(c, "test-file") |
229 | 61 | _, file := filepath.Split(expectedpath) | 86 | _, file := filepath.Split(expectedpath) |
230 | 62 | url, err := s.reader.URL(file) | 87 | url, err := s.reader.URL(file) |
231 | 63 | c.Assert(err, gc.IsNil) | 88 | c.Assert(err, gc.IsNil) |
232 | @@ -65,7 +90,7 @@ | |||
233 | 65 | } | 90 | } |
234 | 66 | 91 | ||
235 | 67 | func (s *filestorageSuite) TestGet(c *gc.C) { | 92 | func (s *filestorageSuite) TestGet(c *gc.C) { |
237 | 68 | expectedpath, data := s.createFile(c) | 93 | expectedpath, data := s.createFile(c, "test-file") |
238 | 69 | _, file := filepath.Split(expectedpath) | 94 | _, file := filepath.Split(expectedpath) |
239 | 70 | rc, err := s.reader.Get(file) | 95 | rc, err := s.reader.Get(file) |
240 | 71 | c.Assert(err, gc.IsNil) | 96 | c.Assert(err, gc.IsNil) |
241 | @@ -74,6 +99,15 @@ | |||
242 | 74 | b, err := ioutil.ReadAll(rc) | 99 | b, err := ioutil.ReadAll(rc) |
243 | 75 | c.Assert(err, gc.IsNil) | 100 | c.Assert(err, gc.IsNil) |
244 | 76 | c.Assert(b, gc.DeepEquals, data) | 101 | c.Assert(b, gc.DeepEquals, data) |
245 | 102 | |||
246 | 103 | // Get on a non-existant path returns NotFoundError | ||
247 | 104 | _, err = s.reader.Get("nowhere") | ||
248 | 105 | c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) | ||
249 | 106 | |||
250 | 107 | // Get on a directory returns NotFoundError | ||
251 | 108 | s.createFile(c, "dir/file") | ||
252 | 109 | _, err = s.reader.Get("dir") | ||
253 | 110 | c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) | ||
254 | 77 | } | 111 | } |
255 | 78 | 112 | ||
256 | 79 | func (s *filestorageSuite) TestPut(c *gc.C) { | 113 | func (s *filestorageSuite) TestPut(c *gc.C) { |
257 | @@ -86,7 +120,7 @@ | |||
258 | 86 | } | 120 | } |
259 | 87 | 121 | ||
260 | 88 | func (s *filestorageSuite) TestRemove(c *gc.C) { | 122 | func (s *filestorageSuite) TestRemove(c *gc.C) { |
262 | 89 | expectedpath, _ := s.createFile(c) | 123 | expectedpath, _ := s.createFile(c, "test-file") |
263 | 90 | _, file := filepath.Split(expectedpath) | 124 | _, file := filepath.Split(expectedpath) |
264 | 91 | err := s.writer.Remove(file) | 125 | err := s.writer.Remove(file) |
265 | 92 | c.Assert(err, gc.IsNil) | 126 | c.Assert(err, gc.IsNil) |
266 | @@ -95,7 +129,7 @@ | |||
267 | 95 | } | 129 | } |
268 | 96 | 130 | ||
269 | 97 | func (s *filestorageSuite) TestRemoveAll(c *gc.C) { | 131 | func (s *filestorageSuite) TestRemoveAll(c *gc.C) { |
271 | 98 | expectedpath, _ := s.createFile(c) | 132 | expectedpath, _ := s.createFile(c, "test-file") |
272 | 99 | err := s.writer.RemoveAll() | 133 | err := s.writer.RemoveAll() |
273 | 100 | c.Assert(err, gc.IsNil) | 134 | c.Assert(err, gc.IsNil) |
274 | 101 | _, err = ioutil.ReadFile(expectedpath) | 135 | _, err = ioutil.ReadFile(expectedpath) |
275 | 102 | 136 | ||
276 | === renamed directory 'environs/localstorage' => 'environs/httpstorage' | |||
277 | === modified file 'environs/httpstorage/backend.go' | |||
278 | --- environs/localstorage/backend.go 2013-07-03 00:15:19 +0000 | |||
279 | +++ environs/httpstorage/backend.go 2013-09-18 06:28:38 +0000 | |||
280 | @@ -1,26 +1,24 @@ | |||
281 | 1 | // Copyright 2013 Canonical Ltd. | 1 | // Copyright 2013 Canonical Ltd. |
282 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | 2 | // Licensed under the AGPLv3, see LICENCE file for details. |
283 | 3 | 3 | ||
285 | 4 | package localstorage | 4 | package httpstorage |
286 | 5 | 5 | ||
287 | 6 | import ( | 6 | import ( |
288 | 7 | "fmt" | 7 | "fmt" |
289 | 8 | "io" | ||
290 | 9 | "io/ioutil" | 8 | "io/ioutil" |
291 | 10 | "net" | 9 | "net" |
292 | 11 | "net/http" | 10 | "net/http" |
293 | 12 | "os" | ||
294 | 13 | "path/filepath" | ||
295 | 14 | "sort" | ||
296 | 15 | "strings" | 11 | "strings" |
297 | 12 | |||
298 | 13 | "launchpad.net/juju-core/environs" | ||
299 | 16 | ) | 14 | ) |
300 | 17 | 15 | ||
305 | 18 | // storageBackend provides HTTP access to a defined path. The local | 16 | // TODO(axw) 2013-09-16 bug #1225916 |
306 | 19 | // provider otimally would use a much simpler Storage, but this | 17 | // Implement authentication for modifying storage. |
307 | 20 | // code may be useful in storage-free environs. Here it requires | 18 | |
308 | 21 | // additional authentication work before it's viable. | 19 | // storageBackend provides HTTP access to a storage object. |
309 | 22 | type storageBackend struct { | 20 | type storageBackend struct { |
311 | 23 | dir string | 21 | backend environs.Storage |
312 | 24 | } | 22 | } |
313 | 25 | 23 | ||
314 | 26 | // ServeHTTP handles the HTTP requests to the container. | 24 | // ServeHTTP handles the HTTP requests to the container. |
315 | @@ -43,9 +41,15 @@ | |||
316 | 43 | 41 | ||
317 | 44 | // handleGet returns a storage file to the client. | 42 | // handleGet returns a storage file to the client. |
318 | 45 | func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) { | 43 | func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) { |
322 | 46 | data, err := ioutil.ReadFile(filepath.Join(s.dir, req.URL.Path)) | 44 | readcloser, err := s.backend.Get(req.URL.Path[1:]) |
323 | 47 | if err != nil { | 45 | if err != nil { |
324 | 48 | http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound) | 46 | http.Error(w, fmt.Sprint(err), http.StatusNotFound) |
325 | 47 | return | ||
326 | 48 | } | ||
327 | 49 | defer readcloser.Close() | ||
328 | 50 | data, err := ioutil.ReadAll(readcloser) | ||
329 | 51 | if err != nil { | ||
330 | 52 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) | ||
331 | 49 | return | 53 | return |
332 | 50 | } | 54 | } |
333 | 51 | w.Header().Set("Content-Type", "application/octet-stream") | 55 | w.Header().Set("Content-Type", "application/octet-stream") |
334 | @@ -54,62 +58,27 @@ | |||
335 | 54 | 58 | ||
336 | 55 | // handleList returns the file names in the storage to the client. | 59 | // handleList returns the file names in the storage to the client. |
337 | 56 | func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) { | 60 | func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) { |
341 | 57 | fp := filepath.Join(s.dir, req.URL.Path) | 61 | prefix := req.URL.Path |
342 | 58 | dir, prefix := filepath.Split(fp) | 62 | prefix = prefix[1 : len(prefix)-1] // drop the leading '/' and trailing '*' |
343 | 59 | names, err := readDirs(dir, prefix[:len(prefix)-1], len(s.dir)+1) | 63 | names, err := s.backend.List(prefix) |
344 | 60 | if err != nil { | 64 | if err != nil { |
346 | 61 | http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound) | 65 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) |
347 | 62 | return | 66 | return |
348 | 63 | } | 67 | } |
349 | 64 | sort.Strings(names) | ||
350 | 65 | data := []byte(strings.Join(names, "\n")) | 68 | data := []byte(strings.Join(names, "\n")) |
351 | 66 | w.Header().Set("Content-Type", "application/octet-stream") | 69 | w.Header().Set("Content-Type", "application/octet-stream") |
352 | 67 | w.Write(data) | 70 | w.Write(data) |
353 | 68 | } | 71 | } |
354 | 69 | 72 | ||
355 | 70 | // readDirs reads the directory hierarchy and compares the found | ||
356 | 71 | // names with the given prefix. | ||
357 | 72 | func readDirs(dir, prefix string, start int) ([]string, error) { | ||
358 | 73 | names := []string{} | ||
359 | 74 | fis, err := ioutil.ReadDir(dir) | ||
360 | 75 | if err != nil { | ||
361 | 76 | return nil, err | ||
362 | 77 | } | ||
363 | 78 | for _, fi := range fis { | ||
364 | 79 | name := fi.Name() | ||
365 | 80 | if strings.HasPrefix(name, prefix) { | ||
366 | 81 | if fi.IsDir() { | ||
367 | 82 | dnames, err := readDirs(filepath.Join(dir, name), prefix, start) | ||
368 | 83 | if err != nil { | ||
369 | 84 | return nil, err | ||
370 | 85 | } | ||
371 | 86 | names = append(names, dnames...) | ||
372 | 87 | continue | ||
373 | 88 | } | ||
374 | 89 | fullname := filepath.Join(dir, name)[start:] | ||
375 | 90 | names = append(names, fullname) | ||
376 | 91 | } | ||
377 | 92 | } | ||
378 | 93 | return names, nil | ||
379 | 94 | } | ||
380 | 95 | |||
381 | 96 | // handlePut stores data from the client in the storage. | 73 | // handlePut stores data from the client in the storage. |
382 | 97 | func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) { | 74 | func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) { |
398 | 98 | fp := filepath.Join(s.dir, req.URL.Path) | 75 | if req.ContentLength < 0 { |
399 | 99 | dir, _ := filepath.Split(fp) | 76 | http.Error(w, "missing or invalid Content-Length header", http.StatusInternalServerError) |
400 | 100 | err := os.MkdirAll(dir, 0777) | 77 | return |
401 | 101 | if err != nil { | 78 | } |
402 | 102 | http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError) | 79 | err := s.backend.Put(req.URL.Path[1:], req.Body, req.ContentLength) |
403 | 103 | return | 80 | if err != nil { |
404 | 104 | } | 81 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) |
390 | 105 | out, err := os.Create(fp) | ||
391 | 106 | if err != nil { | ||
392 | 107 | http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError) | ||
393 | 108 | return | ||
394 | 109 | } | ||
395 | 110 | defer out.Close() | ||
396 | 111 | if _, err := io.Copy(out, req.Body); err != nil { | ||
397 | 112 | http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError) | ||
405 | 113 | return | 82 | return |
406 | 114 | } | 83 | } |
407 | 115 | w.WriteHeader(http.StatusCreated) | 84 | w.WriteHeader(http.StatusCreated) |
408 | @@ -117,28 +86,19 @@ | |||
409 | 117 | 86 | ||
410 | 118 | // handleDelete removes a file from the storage. | 87 | // handleDelete removes a file from the storage. |
411 | 119 | func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) { | 88 | func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) { |
415 | 120 | fp := filepath.Join(s.dir, req.URL.Path) | 89 | err := s.backend.Remove(req.URL.Path[1:]) |
416 | 121 | if err := os.Remove(fp); err != nil && !os.IsNotExist(err) { | 90 | if err != nil { |
417 | 122 | http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError) | 91 | http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) |
418 | 123 | return | 92 | return |
419 | 124 | } | 93 | } |
420 | 125 | w.WriteHeader(http.StatusOK) | 94 | w.WriteHeader(http.StatusOK) |
421 | 126 | } | 95 | } |
422 | 127 | 96 | ||
437 | 128 | // Serve runs a storage server on the given network address, storing | 97 | // Serve runs a storage server on the given network address, relaying |
438 | 129 | // data under the given directory. It returns the network listener. | 98 | // requests to the given storage implementation. It returns the network |
439 | 130 | // This can then be attached to with Client. | 99 | // listener. This can then be attached to with Client. |
440 | 131 | func Serve(addr, dir string) (net.Listener, error) { | 100 | func Serve(addr string, storage environs.Storage) (net.Listener, error) { |
441 | 132 | backend := &storageBackend{ | 101 | backend := &storageBackend{backend: storage} |
428 | 133 | dir: dir, | ||
429 | 134 | } | ||
430 | 135 | info, err := os.Stat(dir) | ||
431 | 136 | if err != nil { | ||
432 | 137 | return nil, fmt.Errorf("cannot stat directory: %v", err) | ||
433 | 138 | } | ||
434 | 139 | if !info.IsDir() { | ||
435 | 140 | return nil, fmt.Errorf("%q is not a directory", dir) | ||
436 | 141 | } | ||
442 | 142 | listener, err := net.Listen("tcp", addr) | 102 | listener, err := net.Listen("tcp", addr) |
443 | 143 | if err != nil { | 103 | if err != nil { |
444 | 144 | return nil, fmt.Errorf("cannot start listener: %v", err) | 104 | return nil, fmt.Errorf("cannot start listener: %v", err) |
445 | 145 | 105 | ||
446 | === modified file 'environs/httpstorage/backend_test.go' | |||
447 | --- environs/localstorage/backend_test.go 2013-09-13 14:48:13 +0000 | |||
448 | +++ environs/httpstorage/backend_test.go 2013-09-18 06:28:38 +0000 | |||
449 | @@ -1,7 +1,7 @@ | |||
450 | 1 | // Copyright 2013 Canonical Ltd. | 1 | // Copyright 2013 Canonical Ltd. |
451 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | 2 | // Licensed under the AGPLv3, see LICENCE file for details. |
452 | 3 | 3 | ||
454 | 4 | package localstorage_test | 4 | package httpstorage_test |
455 | 5 | 5 | ||
456 | 6 | import ( | 6 | import ( |
457 | 7 | "bytes" | 7 | "bytes" |
458 | @@ -16,7 +16,8 @@ | |||
459 | 16 | 16 | ||
460 | 17 | gc "launchpad.net/gocheck" | 17 | gc "launchpad.net/gocheck" |
461 | 18 | 18 | ||
463 | 19 | "launchpad.net/juju-core/environs/localstorage" | 19 | "launchpad.net/juju-core/environs/filestorage" |
464 | 20 | "launchpad.net/juju-core/environs/httpstorage" | ||
465 | 20 | "launchpad.net/juju-core/testing" | 21 | "launchpad.net/juju-core/testing" |
466 | 21 | ) | 22 | ) |
467 | 22 | 23 | ||
468 | @@ -35,7 +36,9 @@ | |||
469 | 35 | // a base URL for the server and the directory path. | 36 | // a base URL for the server and the directory path. |
470 | 36 | func startServer(c *gc.C) (listener net.Listener, url, dataDir string) { | 37 | func startServer(c *gc.C) (listener net.Listener, url, dataDir string) { |
471 | 37 | dataDir = c.MkDir() | 38 | dataDir = c.MkDir() |
473 | 38 | listener, err := localstorage.Serve("localhost:0", dataDir) | 39 | embedded, err := filestorage.NewFileStorageWriter(dataDir) |
474 | 40 | c.Assert(err, gc.IsNil) | ||
475 | 41 | listener, err = httpstorage.Serve("localhost:0", embedded) | ||
476 | 39 | c.Assert(err, gc.IsNil) | 42 | c.Assert(err, gc.IsNil) |
477 | 40 | return listener, fmt.Sprintf("http://%s/", listener.Addr()), dataDir | 43 | return listener, fmt.Sprintf("http://%s/", listener.Addr()), dataDir |
478 | 41 | } | 44 | } |
479 | 42 | 45 | ||
480 | === modified file 'environs/httpstorage/storage.go' | |||
481 | --- environs/localstorage/storage.go 2013-09-02 04:11:11 +0000 | |||
482 | +++ environs/httpstorage/storage.go 2013-09-18 06:28:38 +0000 | |||
483 | @@ -1,7 +1,7 @@ | |||
484 | 1 | // Copyright 2013 Canonical Ltd. | 1 | // Copyright 2013 Canonical Ltd. |
485 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | 2 | // Licensed under the AGPLv3, see LICENCE file for details. |
486 | 3 | 3 | ||
488 | 4 | package localstorage | 4 | package httpstorage |
489 | 5 | 5 | ||
490 | 6 | import ( | 6 | import ( |
491 | 7 | "fmt" | 7 | "fmt" |
492 | @@ -112,6 +112,7 @@ | |||
493 | 112 | return err | 112 | return err |
494 | 113 | } | 113 | } |
495 | 114 | req.Header.Set("Content-Type", "application/octet-stream") | 114 | req.Header.Set("Content-Type", "application/octet-stream") |
496 | 115 | req.ContentLength = length | ||
497 | 115 | resp, err := http.DefaultClient.Do(req) | 116 | resp, err := http.DefaultClient.Do(req) |
498 | 116 | if err != nil { | 117 | if err != nil { |
499 | 117 | return err | 118 | return err |
500 | 118 | 119 | ||
501 | === modified file 'environs/httpstorage/storage_test.go' | |||
502 | --- environs/localstorage/storage_test.go 2013-08-29 01:46:55 +0000 | |||
503 | +++ environs/httpstorage/storage_test.go 2013-09-18 06:28:38 +0000 | |||
504 | @@ -1,7 +1,7 @@ | |||
505 | 1 | // Copyright 2013 Canonical Ltd. | 1 | // Copyright 2013 Canonical Ltd. |
506 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | 2 | // Licensed under the AGPLv3, see LICENCE file for details. |
507 | 3 | 3 | ||
509 | 4 | package localstorage_test | 4 | package httpstorage_test |
510 | 5 | 5 | ||
511 | 6 | import ( | 6 | import ( |
512 | 7 | "bytes" | 7 | "bytes" |
513 | @@ -13,7 +13,7 @@ | |||
514 | 13 | gc "launchpad.net/gocheck" | 13 | gc "launchpad.net/gocheck" |
515 | 14 | 14 | ||
516 | 15 | "launchpad.net/juju-core/environs" | 15 | "launchpad.net/juju-core/environs" |
518 | 16 | "launchpad.net/juju-core/environs/localstorage" | 16 | "launchpad.net/juju-core/environs/httpstorage" |
519 | 17 | "launchpad.net/juju-core/errors" | 17 | "launchpad.net/juju-core/errors" |
520 | 18 | jc "launchpad.net/juju-core/testing/checkers" | 18 | jc "launchpad.net/juju-core/testing/checkers" |
521 | 19 | ) | 19 | ) |
522 | @@ -25,7 +25,7 @@ | |||
523 | 25 | func (s *storageSuite) TestList(c *gc.C) { | 25 | func (s *storageSuite) TestList(c *gc.C) { |
524 | 26 | listener, _, _ := startServer(c) | 26 | listener, _, _ := startServer(c) |
525 | 27 | defer listener.Close() | 27 | defer listener.Close() |
527 | 28 | storage := localstorage.Client(listener.Addr().String()) | 28 | storage := httpstorage.Client(listener.Addr().String()) |
528 | 29 | names, err := storage.List("a/b/c") | 29 | names, err := storage.List("a/b/c") |
529 | 30 | c.Assert(err, gc.IsNil) | 30 | c.Assert(err, gc.IsNil) |
530 | 31 | c.Assert(names, gc.HasLen, 0) | 31 | c.Assert(names, gc.HasLen, 0) |
531 | @@ -37,7 +37,7 @@ | |||
532 | 37 | listener, _, _ := startServer(c) | 37 | listener, _, _ := startServer(c) |
533 | 38 | defer listener.Close() | 38 | defer listener.Close() |
534 | 39 | 39 | ||
536 | 40 | storage := localstorage.Client(listener.Addr().String()) | 40 | storage := httpstorage.Client(listener.Addr().String()) |
537 | 41 | names := []string{ | 41 | names := []string{ |
538 | 42 | "aa", | 42 | "aa", |
539 | 43 | "zzz/aa", | 43 | "zzz/aa", |
540 | @@ -51,7 +51,7 @@ | |||
541 | 51 | checkList(c, storage, "a", []string{"aa"}) | 51 | checkList(c, storage, "a", []string{"aa"}) |
542 | 52 | checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"}) | 52 | checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"}) |
543 | 53 | 53 | ||
545 | 54 | storage2 := localstorage.Client(listener.Addr().String()) | 54 | storage2 := httpstorage.Client(listener.Addr().String()) |
546 | 55 | for _, name := range names { | 55 | for _, name := range names { |
547 | 56 | checkFileHasContents(c, storage2, name, []byte(name)) | 56 | checkFileHasContents(c, storage2, name, []byte(name)) |
548 | 57 | } | 57 | } |
549 | 58 | 58 | ||
550 | === modified file 'environs/testing/storage.go' | |||
551 | --- environs/testing/storage.go 2013-09-17 05:29:35 +0000 | |||
552 | +++ environs/testing/storage.go 2013-09-18 06:28:38 +0000 | |||
553 | @@ -17,7 +17,8 @@ | |||
554 | 17 | gc "launchpad.net/gocheck" | 17 | gc "launchpad.net/gocheck" |
555 | 18 | 18 | ||
556 | 19 | "launchpad.net/juju-core/environs" | 19 | "launchpad.net/juju-core/environs" |
558 | 20 | "launchpad.net/juju-core/environs/localstorage" | 20 | "launchpad.net/juju-core/environs/filestorage" |
559 | 21 | "launchpad.net/juju-core/environs/httpstorage" | ||
560 | 21 | "launchpad.net/juju-core/environs/tools" | 22 | "launchpad.net/juju-core/environs/tools" |
561 | 22 | "launchpad.net/juju-core/version" | 23 | "launchpad.net/juju-core/version" |
562 | 23 | ) | 24 | ) |
563 | @@ -27,9 +28,11 @@ | |||
564 | 27 | // directory. | 28 | // directory. |
565 | 28 | func CreateLocalTestStorage(c *gc.C) (closer io.Closer, storage environs.Storage, dataDir string) { | 29 | func CreateLocalTestStorage(c *gc.C) (closer io.Closer, storage environs.Storage, dataDir string) { |
566 | 29 | dataDir = c.MkDir() | 30 | dataDir = c.MkDir() |
570 | 30 | listener, err := localstorage.Serve("localhost:0", dataDir) | 31 | underlying, err := filestorage.NewFileStorageWriter(dataDir) |
571 | 31 | c.Assert(err, gc.IsNil) | 32 | c.Assert(err, gc.IsNil) |
572 | 32 | storage = localstorage.Client(listener.Addr().String()) | 33 | listener, err := httpstorage.Serve("localhost:0", underlying) |
573 | 34 | c.Assert(err, gc.IsNil) | ||
574 | 35 | storage = httpstorage.Client(listener.Addr().String()) | ||
575 | 33 | closer = listener | 36 | closer = listener |
576 | 34 | return | 37 | return |
577 | 35 | } | 38 | } |
578 | 36 | 39 | ||
579 | === modified file 'provider/azure/environ_test.go' | |||
580 | --- provider/azure/environ_test.go 2013-09-12 14:16:48 +0000 | |||
581 | +++ provider/azure/environ_test.go 2013-09-18 06:28:38 +0000 | |||
582 | @@ -23,8 +23,8 @@ | |||
583 | 23 | "launchpad.net/juju-core/environs" | 23 | "launchpad.net/juju-core/environs" |
584 | 24 | "launchpad.net/juju-core/environs/config" | 24 | "launchpad.net/juju-core/environs/config" |
585 | 25 | "launchpad.net/juju-core/environs/imagemetadata" | 25 | "launchpad.net/juju-core/environs/imagemetadata" |
586 | 26 | "launchpad.net/juju-core/environs/localstorage" | ||
587 | 27 | "launchpad.net/juju-core/environs/simplestreams" | 26 | "launchpad.net/juju-core/environs/simplestreams" |
588 | 27 | envtesting "launchpad.net/juju-core/environs/testing" | ||
589 | 28 | "launchpad.net/juju-core/environs/tools" | 28 | "launchpad.net/juju-core/environs/tools" |
590 | 29 | "launchpad.net/juju-core/errors" | 29 | "launchpad.net/juju-core/errors" |
591 | 30 | "launchpad.net/juju-core/instance" | 30 | "launchpad.net/juju-core/instance" |
592 | @@ -54,12 +54,10 @@ | |||
593 | 54 | // setDummyStorage injects the local provider's fake storage implementation | 54 | // setDummyStorage injects the local provider's fake storage implementation |
594 | 55 | // into the given environment, so that tests can manipulate storage as if it | 55 | // into the given environment, so that tests can manipulate storage as if it |
595 | 56 | // were real. | 56 | // were real. |
602 | 57 | // Returns a cleanup function that must be called when done with the storage. | 57 | func (s *environSuite) setDummyStorage(c *gc.C, env *azureEnviron) { |
603 | 58 | func setDummyStorage(c *gc.C, env *azureEnviron) func() { | 58 | closer, storage, _ := envtesting.CreateLocalTestStorage(c) |
604 | 59 | listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir()) | 59 | env.storage = storage |
605 | 60 | c.Assert(err, gc.IsNil) | 60 | s.AddCleanup(func(c *gc.C) { closer.Close() }) |
600 | 61 | env.storage = localstorage.Client(listener.Addr().String()) | ||
601 | 62 | return func() { listener.Close() } | ||
606 | 63 | } | 61 | } |
607 | 64 | 62 | ||
608 | 65 | func (*environSuite) TestGetSnapshot(c *gc.C) { | 63 | func (*environSuite) TestGetSnapshot(c *gc.C) { |
609 | @@ -469,22 +467,20 @@ | |||
610 | 469 | c.Check(env.storageAccountKey, gc.Equals, "") | 467 | c.Check(env.storageAccountKey, gc.Equals, "") |
611 | 470 | } | 468 | } |
612 | 471 | 469 | ||
614 | 472 | func (*environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) { | 470 | func (s *environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) { |
615 | 473 | env := makeEnviron(c) | 471 | env := makeEnviron(c) |
618 | 474 | cleanup := setDummyStorage(c, env) | 472 | s.setDummyStorage(c, env) |
617 | 475 | defer cleanup() | ||
619 | 476 | _, _, err := env.StateInfo() | 473 | _, _, err := env.StateInfo() |
620 | 477 | c.Check(err, jc.Satisfies, errors.IsNotBootstrapped) | 474 | c.Check(err, jc.Satisfies, errors.IsNotBootstrapped) |
621 | 478 | } | 475 | } |
622 | 479 | 476 | ||
624 | 480 | func (*environSuite) TestStateInfo(c *gc.C) { | 477 | func (s *environSuite) TestStateInfo(c *gc.C) { |
625 | 481 | instanceID := "my-instance" | 478 | instanceID := "my-instance" |
626 | 482 | patchWithServiceListResponse(c, []gwacl.HostedServiceDescriptor{{ | 479 | patchWithServiceListResponse(c, []gwacl.HostedServiceDescriptor{{ |
627 | 483 | ServiceName: instanceID, | 480 | ServiceName: instanceID, |
628 | 484 | }}) | 481 | }}) |
629 | 485 | env := makeEnviron(c) | 482 | env := makeEnviron(c) |
632 | 486 | cleanup := setDummyStorage(c, env) | 483 | s.setDummyStorage(c, env) |
631 | 487 | defer cleanup() | ||
633 | 488 | err := provider.SaveState( | 484 | err := provider.SaveState( |
634 | 489 | env.Storage(), | 485 | env.Storage(), |
635 | 490 | &provider.BootstrapState{StateInstances: []instance.Id{instance.Id(instanceID)}}) | 486 | &provider.BootstrapState{StateInstances: []instance.Id{instance.Id(instanceID)}}) |
636 | @@ -705,15 +701,13 @@ | |||
637 | 705 | return service1, service1Desc | 701 | return service1, service1Desc |
638 | 706 | } | 702 | } |
639 | 707 | 703 | ||
644 | 708 | func setServiceDeletionConcurrency(nbGoroutines int) func() { | 704 | func (s *environSuite) setServiceDeletionConcurrency(nbGoroutines int) { |
645 | 709 | oldMaxConcurrentDeletes := maxConcurrentDeletes | 705 | restore := jc.Set(&maxConcurrentDeletes, nbGoroutines) |
646 | 710 | maxConcurrentDeletes = nbGoroutines | 706 | s.AddCleanup(func(*gc.C) { restore() }) |
643 | 711 | return func() { maxConcurrentDeletes = oldMaxConcurrentDeletes } | ||
647 | 712 | } | 707 | } |
648 | 713 | 708 | ||
652 | 714 | func (*environSuite) TestStopInstancesDestroysMachines(c *gc.C) { | 709 | func (s *environSuite) TestStopInstancesDestroysMachines(c *gc.C) { |
653 | 715 | cleanup := setServiceDeletionConcurrency(3) | 710 | s.setServiceDeletionConcurrency(3) |
651 | 716 | defer cleanup() | ||
654 | 717 | service1Name := "service1" | 711 | service1Name := "service1" |
655 | 718 | service1, service1Desc := makeAzureService(service1Name) | 712 | service1, service1Desc := makeAzureService(service1Name) |
656 | 719 | service2Name := "service2" | 713 | service2Name := "service2" |
657 | @@ -739,9 +733,8 @@ | |||
658 | 739 | assertOneRequestMatches(c, *requests, "DELETE", ".*"+service2Name+".*") | 733 | assertOneRequestMatches(c, *requests, "DELETE", ".*"+service2Name+".*") |
659 | 740 | } | 734 | } |
660 | 741 | 735 | ||
664 | 742 | func (*environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) { | 736 | func (s *environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) { |
665 | 743 | cleanup := setServiceDeletionConcurrency(3) | 737 | s.setServiceDeletionConcurrency(3) |
663 | 744 | defer cleanup() | ||
666 | 745 | responses := []gwacl.DispatcherResponse{ | 738 | responses := []gwacl.DispatcherResponse{ |
667 | 746 | gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil), | 739 | gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil), |
668 | 747 | } | 740 | } |
669 | @@ -767,9 +760,8 @@ | |||
670 | 767 | assertOneRequestMatches(c, *requests, "DELETE", ".*") | 760 | assertOneRequestMatches(c, *requests, "DELETE", ".*") |
671 | 768 | } | 761 | } |
672 | 769 | 762 | ||
676 | 770 | func (*environSuite) TestStopInstancesWithLimitedConcurrency(c *gc.C) { | 763 | func (s *environSuite) TestStopInstancesWithLimitedConcurrency(c *gc.C) { |
677 | 771 | cleanup := setServiceDeletionConcurrency(3) | 764 | s.setServiceDeletionConcurrency(3) |
675 | 772 | defer cleanup() | ||
678 | 773 | services := []*gwacl.HostedService{} | 765 | services := []*gwacl.HostedService{} |
679 | 774 | serviceDescs := []gwacl.HostedServiceDescriptor{} | 766 | serviceDescs := []gwacl.HostedServiceDescriptor{} |
680 | 775 | for i := 0; i < 10; i++ { | 767 | for i := 0; i < 10; i++ { |
681 | @@ -788,9 +780,8 @@ | |||
682 | 788 | c.Check(len(*requests), gc.Equals, len(services)*2) | 780 | c.Check(len(*requests), gc.Equals, len(services)*2) |
683 | 789 | } | 781 | } |
684 | 790 | 782 | ||
688 | 791 | func (*environSuite) TestStopInstancesWithZeroInstance(c *gc.C) { | 783 | func (s *environSuite) TestStopInstancesWithZeroInstance(c *gc.C) { |
689 | 792 | cleanup := setServiceDeletionConcurrency(3) | 784 | s.setServiceDeletionConcurrency(3) |
687 | 793 | defer cleanup() | ||
690 | 794 | env := makeEnviron(c) | 785 | env := makeEnviron(c) |
691 | 795 | instances := []instance.Instance{} | 786 | instances := []instance.Instance{} |
692 | 796 | 787 | ||
693 | @@ -818,10 +809,9 @@ | |||
694 | 818 | return cleanupResponses | 809 | return cleanupResponses |
695 | 819 | } | 810 | } |
696 | 820 | 811 | ||
698 | 821 | func (*environSuite) TestDestroyDoesNotCleanStorageIfError(c *gc.C) { | 812 | func (s *environSuite) TestDestroyDoesNotCleanStorageIfError(c *gc.C) { |
699 | 822 | env := makeEnviron(c) | 813 | env := makeEnviron(c) |
702 | 823 | cleanup := setDummyStorage(c, env) | 814 | s.setDummyStorage(c, env) |
701 | 824 | defer cleanup() | ||
703 | 825 | // Populate storage. | 815 | // Populate storage. |
704 | 826 | err := provider.SaveState( | 816 | err := provider.SaveState( |
705 | 827 | env.Storage(), | 817 | env.Storage(), |
706 | @@ -840,10 +830,9 @@ | |||
707 | 840 | c.Check(files, gc.HasLen, 1) | 830 | c.Check(files, gc.HasLen, 1) |
708 | 841 | } | 831 | } |
709 | 842 | 832 | ||
711 | 843 | func (*environSuite) TestDestroyCleansUpStorage(c *gc.C) { | 833 | func (s *environSuite) TestDestroyCleansUpStorage(c *gc.C) { |
712 | 844 | env := makeEnviron(c) | 834 | env := makeEnviron(c) |
715 | 845 | cleanup := setDummyStorage(c, env) | 835 | s.setDummyStorage(c, env) |
714 | 846 | defer cleanup() | ||
716 | 847 | // Populate storage. | 836 | // Populate storage. |
717 | 848 | err := provider.SaveState( | 837 | err := provider.SaveState( |
718 | 849 | env.Storage(), | 838 | env.Storage(), |
719 | @@ -864,10 +853,9 @@ | |||
720 | 864 | c.Check(files, gc.HasLen, 0) | 853 | c.Check(files, gc.HasLen, 0) |
721 | 865 | } | 854 | } |
722 | 866 | 855 | ||
724 | 867 | func (*environSuite) TestDestroyDeletesVirtualNetworkAndAffinityGroup(c *gc.C) { | 856 | func (s *environSuite) TestDestroyDeletesVirtualNetworkAndAffinityGroup(c *gc.C) { |
725 | 868 | env := makeEnviron(c) | 857 | env := makeEnviron(c) |
728 | 869 | cleanup := setDummyStorage(c, env) | 858 | s.setDummyStorage(c, env) |
727 | 870 | defer cleanup() | ||
729 | 871 | services := []gwacl.HostedServiceDescriptor{} | 859 | services := []gwacl.HostedServiceDescriptor{} |
730 | 872 | responses := getAzureServiceListResponse(c, services) | 860 | responses := getAzureServiceListResponse(c, services) |
731 | 873 | // Prepare a configuration with a single virtual network. | 861 | // Prepare a configuration with a single virtual network. |
732 | @@ -933,12 +921,10 @@ | |||
733 | 933 | c.Error(fmt.Sprintf("none of the requests matches: Method=%v, URL pattern=%v", method, urlPattern)) | 921 | c.Error(fmt.Sprintf("none of the requests matches: Method=%v, URL pattern=%v", method, urlPattern)) |
734 | 934 | } | 922 | } |
735 | 935 | 923 | ||
739 | 936 | func (*environSuite) TestDestroyStopsAllInstances(c *gc.C) { | 924 | func (s *environSuite) TestDestroyStopsAllInstances(c *gc.C) { |
740 | 937 | cleanup1 := setServiceDeletionConcurrency(3) | 925 | s.setServiceDeletionConcurrency(3) |
738 | 938 | defer cleanup1() | ||
741 | 939 | env := makeEnviron(c) | 926 | env := makeEnviron(c) |
744 | 940 | cleanup2 := setDummyStorage(c, env) | 927 | s.setDummyStorage(c, env) |
743 | 941 | defer cleanup2() | ||
745 | 942 | 928 | ||
746 | 943 | // Simulate 2 instances corresponding to two Azure services. | 929 | // Simulate 2 instances corresponding to two Azure services. |
747 | 944 | prefix := env.getEnvPrefix() | 930 | prefix := env.getEnvPrefix() |
748 | @@ -1300,10 +1286,9 @@ | |||
749 | 1300 | c.Assert(retrieved, gc.DeepEquals, content) | 1286 | c.Assert(retrieved, gc.DeepEquals, content) |
750 | 1301 | } | 1287 | } |
751 | 1302 | 1288 | ||
753 | 1303 | func (*environSuite) TestGetImageMetadataSources(c *gc.C) { | 1289 | func (s *environSuite) TestGetImageMetadataSources(c *gc.C) { |
754 | 1304 | env := makeEnviron(c) | 1290 | env := makeEnviron(c) |
757 | 1305 | cleanup := setDummyStorage(c, env) | 1291 | s.setDummyStorage(c, env) |
756 | 1306 | defer cleanup() | ||
758 | 1307 | 1292 | ||
759 | 1308 | data := []byte{1, 2, 3, 4} | 1293 | data := []byte{1, 2, 3, 4} |
760 | 1309 | env.Storage().Put("filename", bytes.NewReader(data), int64(len(data))) | 1294 | env.Storage().Put("filename", bytes.NewReader(data), int64(len(data))) |
761 | @@ -1320,10 +1305,9 @@ | |||
762 | 1320 | c.Assert(url, gc.Equals, imagemetadata.DefaultBaseURL+"/") | 1305 | c.Assert(url, gc.Equals, imagemetadata.DefaultBaseURL+"/") |
763 | 1321 | } | 1306 | } |
764 | 1322 | 1307 | ||
766 | 1323 | func (*environSuite) TestGetToolsMetadataSources(c *gc.C) { | 1308 | func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) { |
767 | 1324 | env := makeEnviron(c) | 1309 | env := makeEnviron(c) |
770 | 1325 | cleanup := setDummyStorage(c, env) | 1310 | s.setDummyStorage(c, env) |
769 | 1326 | defer cleanup() | ||
771 | 1327 | 1311 | ||
772 | 1328 | data := []byte{1, 2, 3, 4} | 1312 | data := []byte{1, 2, 3, 4} |
773 | 1329 | env.Storage().Put("tools/filename", bytes.NewReader(data), int64(len(data))) | 1313 | env.Storage().Put("tools/filename", bytes.NewReader(data), int64(len(data))) |
774 | 1330 | 1314 | ||
775 | === modified file 'provider/local/environ.go' | |||
776 | --- provider/local/environ.go 2013-09-16 22:02:24 +0000 | |||
777 | +++ provider/local/environ.go 2013-09-18 06:28:38 +0000 | |||
778 | @@ -21,7 +21,8 @@ | |||
779 | 21 | "launchpad.net/juju-core/environs/bootstrap" | 21 | "launchpad.net/juju-core/environs/bootstrap" |
780 | 22 | "launchpad.net/juju-core/environs/cloudinit" | 22 | "launchpad.net/juju-core/environs/cloudinit" |
781 | 23 | "launchpad.net/juju-core/environs/config" | 23 | "launchpad.net/juju-core/environs/config" |
783 | 24 | "launchpad.net/juju-core/environs/localstorage" | 24 | "launchpad.net/juju-core/environs/filestorage" |
784 | 25 | "launchpad.net/juju-core/environs/httpstorage" | ||
785 | 25 | "launchpad.net/juju-core/instance" | 26 | "launchpad.net/juju-core/instance" |
786 | 26 | "launchpad.net/juju-core/juju/osenv" | 27 | "launchpad.net/juju-core/juju/osenv" |
787 | 27 | "launchpad.net/juju-core/names" | 28 | "launchpad.net/juju-core/names" |
788 | @@ -164,7 +165,11 @@ | |||
789 | 164 | } else if !info.Mode().IsDir() { | 165 | } else if !info.Mode().IsDir() { |
790 | 165 | return nil, fmt.Errorf("%q exists but is not a directory (and it needs to be)", dir) | 166 | return nil, fmt.Errorf("%q exists but is not a directory (and it needs to be)", dir) |
791 | 166 | } | 167 | } |
793 | 167 | return localstorage.Serve(address, dir) | 168 | storage, err := filestorage.NewFileStorageWriter(dir) |
794 | 169 | if err != nil { | ||
795 | 170 | return nil, err | ||
796 | 171 | } | ||
797 | 172 | return httpstorage.Serve(address, storage) | ||
798 | 168 | } | 173 | } |
799 | 169 | 174 | ||
800 | 170 | // SetConfig is specified in the Environ interface. | 175 | // SetConfig is specified in the Environ interface. |
801 | @@ -319,12 +324,12 @@ | |||
802 | 319 | 324 | ||
803 | 320 | // Storage is specified in the Environ interface. | 325 | // Storage is specified in the Environ interface. |
804 | 321 | func (env *localEnviron) Storage() environs.Storage { | 326 | func (env *localEnviron) Storage() environs.Storage { |
806 | 322 | return localstorage.Client(env.config.storageAddr()) | 327 | return httpstorage.Client(env.config.storageAddr()) |
807 | 323 | } | 328 | } |
808 | 324 | 329 | ||
809 | 325 | // PublicStorage is specified in the Environ interface. | 330 | // PublicStorage is specified in the Environ interface. |
810 | 326 | func (env *localEnviron) PublicStorage() environs.StorageReader { | 331 | func (env *localEnviron) PublicStorage() environs.StorageReader { |
812 | 327 | return localstorage.Client(env.config.sharedStorageAddr()) | 332 | return httpstorage.Client(env.config.sharedStorageAddr()) |
813 | 328 | } | 333 | } |
814 | 329 | 334 | ||
815 | 330 | // Destroy is specified in the Environ interface. | 335 | // Destroy is specified in the Environ interface. |
816 | 331 | 336 | ||
817 | === modified file 'provider/local/storage/worker.go' | |||
818 | --- provider/local/storage/worker.go 2013-08-30 04:07:04 +0000 | |||
819 | +++ provider/local/storage/worker.go 2013-09-18 06:28:38 +0000 | |||
820 | @@ -5,7 +5,8 @@ | |||
821 | 5 | "launchpad.net/tomb" | 5 | "launchpad.net/tomb" |
822 | 6 | 6 | ||
823 | 7 | "launchpad.net/juju-core/agent" | 7 | "launchpad.net/juju-core/agent" |
825 | 8 | "launchpad.net/juju-core/environs/localstorage" | 8 | "launchpad.net/juju-core/environs/filestorage" |
826 | 9 | "launchpad.net/juju-core/environs/httpstorage" | ||
827 | 9 | "launchpad.net/juju-core/worker" | 10 | "launchpad.net/juju-core/worker" |
828 | 10 | ) | 11 | ) |
829 | 11 | 12 | ||
830 | @@ -40,7 +41,12 @@ | |||
831 | 40 | storageAddr := s.config.Value(agent.StorageAddr) | 41 | storageAddr := s.config.Value(agent.StorageAddr) |
832 | 41 | logger.Infof("serving %s on %s", storageDir, storageAddr) | 42 | logger.Infof("serving %s on %s", storageDir, storageAddr) |
833 | 42 | 43 | ||
835 | 43 | storageListener, err := localstorage.Serve(storageAddr, storageDir) | 44 | storage, err := filestorage.NewFileStorageWriter(storageDir) |
836 | 45 | if err != nil { | ||
837 | 46 | logger.Errorf("error with local storage: %v", err) | ||
838 | 47 | return err | ||
839 | 48 | } | ||
840 | 49 | storageListener, err := httpstorage.Serve(storageAddr, storage) | ||
841 | 44 | if err != nil { | 50 | if err != nil { |
842 | 45 | logger.Errorf("error with local storage: %v", err) | 51 | logger.Errorf("error with local storage: %v", err) |
843 | 46 | return err | 52 | return err |
844 | @@ -51,7 +57,12 @@ | |||
845 | 51 | sharedStorageAddr := s.config.Value(agent.SharedStorageAddr) | 57 | sharedStorageAddr := s.config.Value(agent.SharedStorageAddr) |
846 | 52 | logger.Infof("serving %s on %s", sharedStorageDir, sharedStorageAddr) | 58 | logger.Infof("serving %s on %s", sharedStorageDir, sharedStorageAddr) |
847 | 53 | 59 | ||
849 | 54 | sharedStorageListener, err := localstorage.Serve(sharedStorageAddr, sharedStorageDir) | 60 | sharedStorage, err := filestorage.NewFileStorageWriter(sharedStorageDir) |
850 | 61 | if err != nil { | ||
851 | 62 | logger.Errorf("error with local storage: %v", err) | ||
852 | 63 | return err | ||
853 | 64 | } | ||
854 | 65 | sharedStorageListener, err := httpstorage.Serve(sharedStorageAddr, sharedStorage) | ||
855 | 55 | if err != nil { | 66 | if err != nil { |
856 | 56 | logger.Errorf("error with local storage: %v", err) | 67 | logger.Errorf("error with local storage: %v", err) |
857 | 57 | return err | 68 | return err |
858 | 58 | 69 | ||
859 | === modified file 'provider/state_test.go' | |||
860 | --- provider/state_test.go 2013-09-12 12:38:04 +0000 | |||
861 | +++ provider/state_test.go 2013-09-18 06:28:38 +0000 | |||
862 | @@ -12,7 +12,7 @@ | |||
863 | 12 | 12 | ||
864 | 13 | "launchpad.net/juju-core/environs" | 13 | "launchpad.net/juju-core/environs" |
865 | 14 | "launchpad.net/juju-core/environs/config" | 14 | "launchpad.net/juju-core/environs/config" |
867 | 15 | "launchpad.net/juju-core/environs/localstorage" | 15 | envtesting "launchpad.net/juju-core/environs/testing" |
868 | 16 | "launchpad.net/juju-core/errors" | 16 | "launchpad.net/juju-core/errors" |
869 | 17 | "launchpad.net/juju-core/instance" | 17 | "launchpad.net/juju-core/instance" |
870 | 18 | "launchpad.net/juju-core/provider" | 18 | "launchpad.net/juju-core/provider" |
871 | @@ -20,23 +20,21 @@ | |||
872 | 20 | jc "launchpad.net/juju-core/testing/checkers" | 20 | jc "launchpad.net/juju-core/testing/checkers" |
873 | 21 | ) | 21 | ) |
874 | 22 | 22 | ||
876 | 23 | type StateSuite struct{} | 23 | type StateSuite struct { |
877 | 24 | testing.LoggingSuite | ||
878 | 25 | } | ||
879 | 24 | 26 | ||
880 | 25 | var _ = gc.Suite(&StateSuite{}) | 27 | var _ = gc.Suite(&StateSuite{}) |
881 | 26 | 28 | ||
882 | 27 | // makeDummyStorage creates a local storage. | 29 | // makeDummyStorage creates a local storage. |
890 | 28 | // Returns a cleanup function that must be called when done with the storage. | 30 | func (suite *StateSuite) makeDummyStorage(c *gc.C) environs.Storage { |
891 | 29 | func makeDummyStorage(c *gc.C) (environs.Storage, func()) { | 31 | closer, storage, _ := envtesting.CreateLocalTestStorage(c) |
892 | 30 | listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir()) | 32 | suite.AddCleanup(func(*gc.C) { closer.Close() }) |
893 | 31 | c.Assert(err, gc.IsNil) | 33 | return storage |
887 | 32 | storage := localstorage.Client(listener.Addr().String()) | ||
888 | 33 | cleanup := func() { listener.Close() } | ||
889 | 34 | return storage, cleanup | ||
894 | 35 | } | 34 | } |
895 | 36 | 35 | ||
899 | 37 | func (*StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) { | 36 | func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) { |
900 | 38 | storage, cleanup := makeDummyStorage(c) | 37 | storage := suite.makeDummyStorage(c) |
898 | 39 | defer cleanup() | ||
901 | 40 | 38 | ||
902 | 41 | url, err := provider.CreateStateFile(storage) | 39 | url, err := provider.CreateStateFile(storage) |
903 | 42 | c.Assert(err, gc.IsNil) | 40 | c.Assert(err, gc.IsNil) |
904 | @@ -53,8 +51,7 @@ | |||
905 | 53 | } | 51 | } |
906 | 54 | 52 | ||
907 | 55 | func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) { | 53 | func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) { |
910 | 56 | storage, cleanup := makeDummyStorage(c) | 54 | storage := suite.makeDummyStorage(c) |
909 | 57 | defer cleanup() | ||
911 | 58 | arch := "amd64" | 55 | arch := "amd64" |
912 | 59 | state := provider.BootstrapState{ | 56 | state := provider.BootstrapState{ |
913 | 60 | StateInstances: []instance.Id{instance.Id("an-instance-id")}, | 57 | StateInstances: []instance.Id{instance.Id("an-instance-id")}, |
914 | @@ -85,8 +82,7 @@ | |||
915 | 85 | } | 82 | } |
916 | 86 | 83 | ||
917 | 87 | func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) { | 84 | func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) { |
920 | 88 | storage, cleanup := makeDummyStorage(c) | 85 | storage := suite.makeDummyStorage(c) |
919 | 89 | defer cleanup() | ||
921 | 90 | state := suite.setUpSavedState(c, storage) | 86 | state := suite.setUpSavedState(c, storage) |
922 | 91 | storedState, err := provider.LoadState(storage) | 87 | storedState, err := provider.LoadState(storage) |
923 | 92 | c.Assert(err, gc.IsNil) | 88 | c.Assert(err, gc.IsNil) |
924 | @@ -94,8 +90,7 @@ | |||
925 | 94 | } | 90 | } |
926 | 95 | 91 | ||
927 | 96 | func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) { | 92 | func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) { |
930 | 97 | storage, cleanup := makeDummyStorage(c) | 93 | storage := suite.makeDummyStorage(c) |
929 | 98 | defer cleanup() | ||
931 | 99 | state := suite.setUpSavedState(c, storage) | 94 | state := suite.setUpSavedState(c, storage) |
932 | 100 | url, err := storage.URL(provider.StateFile) | 95 | url, err := storage.URL(provider.StateFile) |
933 | 101 | c.Assert(err, gc.IsNil) | 96 | c.Assert(err, gc.IsNil) |
934 | @@ -105,8 +100,7 @@ | |||
935 | 105 | } | 100 | } |
936 | 106 | 101 | ||
937 | 107 | func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) { | 102 | func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) { |
940 | 108 | storage, cleanup := makeDummyStorage(c) | 103 | storage := suite.makeDummyStorage(c) |
939 | 109 | defer cleanup() | ||
941 | 110 | 104 | ||
942 | 111 | _, err := provider.LoadState(storage) | 105 | _, err := provider.LoadState(storage) |
943 | 112 | 106 | ||
944 | @@ -114,8 +108,7 @@ | |||
945 | 114 | } | 108 | } |
946 | 115 | 109 | ||
947 | 116 | func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) { | 110 | func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) { |
950 | 117 | storage, cleanup := makeDummyStorage(c) | 111 | storage := suite.makeDummyStorage(c) |
949 | 118 | defer cleanup() | ||
951 | 119 | arch := "amd64" | 112 | arch := "amd64" |
952 | 120 | state := provider.BootstrapState{ | 113 | state := provider.BootstrapState{ |
953 | 121 | StateInstances: []instance.Id{instance.Id("an-instance-id")}, | 114 | StateInstances: []instance.Id{instance.Id("an-instance-id")}, |
Reviewers: mp+185958_ code.launchpad. net,
Message:
Please take a look.
Description: localstorage -> environs/ httpstorage
environs/
localstorage becomes httpstorage; it no longer
operates on the disk, but instead delegates to
another environs.Storage object.
This work goes together with changes to filestorage, which will be modified to
environs/
write to temporary storage and atomically move,
in the same way that sshstorage has been coded.
https:/ /code.launchpad .net/~axwalk/ juju-core/ localstorage- to-httpstorage/ +merge/ 185958
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/13736043/
Affected files (+129, -133 lines): machine. go bootstrap/ bootstrap_ test.go filestorage/ filestorage. go httpstorage/ backend. go httpstorage/ backend_ test.go httpstorage/ storage. go httpstorage/ storage_ test.go testing/ storage. go azure/environ_ test.go local/environ. go local/storage/ worker. go state_test. go
A [revision details]
M cmd/jujud/
M environs/
M environs/
M environs/
M environs/
M environs/
M environs/
M environs/
M provider/
M provider/
M provider/
M provider/