Merge lp:~axwalk/juju-core/localstorage-to-httpstorage into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
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
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+185958@code.launchpad.net

Commit message

environs/localstorage -> environs/httpstorage

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/filestorage, which will be modified to
write to temporary storage and atomically move,
in the same way that sshstorage has been coded.

https://codereview.appspot.com/13736043/

Description of the change

environs/localstorage -> environs/httpstorage

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/filestorage, which will be modified to
write to temporary storage and atomically move,
in the same way that sshstorage has been coded.

https://codereview.appspot.com/13736043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+185958_code.launchpad.net,

Message:
Please take a look.

Description:
environs/localstorage -> environs/httpstorage

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/filestorage, which will be modified to
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):
   A [revision details]
   M cmd/jujud/machine.go
   M environs/bootstrap/bootstrap_test.go
   M environs/filestorage/filestorage.go
   M environs/httpstorage/backend.go
   M environs/httpstorage/backend_test.go
   M environs/httpstorage/storage.go
   M environs/httpstorage/storage_test.go
   M environs/testing/storage.go
   M provider/azure/environ_test.go
   M provider/local/environ.go
   M provider/local/storage/worker.go
   M provider/state_test.go

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Tim Penhey (thumper) wrote :

A direct test for the glob change, and a helper function to create the
storage are needed.

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go
File environs/httpstorage/backend.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode54
environs/httpstorage/backend.go:54: defer readcloser.Close()
Why defer? There is no harm in calling Close right now.

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode75
environs/httpstorage/backend.go:75: if req.ContentLength <= -1 {
isn't " < 0 " more readable and understandable?

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend_test.go
File environs/httpstorage/backend_test.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend_test.go#newcode109
environs/httpstorage/backend_test.go:109: // Get on a directory returns
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://codereview.appspot.com/13736043/diff/4001/environs/testing/storage.go
File environs/testing/storage.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/testing/storage.go#newcode33
environs/testing/storage.go:33: listener, err :=
httpstorage.Serve("localhost:0", underlying)
Given this is the second or third time I have seen these lines of code,
perhaps a testing helper function would be good.

https://codereview.appspot.com/13736043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go
File environs/httpstorage/backend.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode54
environs/httpstorage/backend.go:54: defer readcloser.Close()
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://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode75
environs/httpstorage/backend.go:75: if req.ContentLength <= -1 {
On 2013/09/18 04:26:28, thumper wrote:
> isn't " < 0 " more readable and understandable?

Done.

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend_test.go
File environs/httpstorage/backend_test.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend_test.go#newcode109
environs/httpstorage/backend_test.go:109: // Get on a directory returns
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://codereview.appspot.com/13736043/diff/4001/environs/testing/storage.go
File environs/testing/storage.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/testing/storage.go#newcode33
environs/testing/storage.go:33: listener, err :=
httpstorage.Serve("localhost:0", underlying)
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.

https://codereview.appspot.com/13736043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Tim Penhey (thumper) wrote :

LGTM - although I'd prefer with the added testing helper method, this
isn't essential.

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go
File environs/httpstorage/backend.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode54
environs/httpstorage/backend.go:54: defer readcloser.Close()
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.

https://codereview.appspot.com/13736043/

Revision history for this message
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://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go
> File environs/httpstorage/backend.go (right):

https://codereview.appspot.com/13736043/diff/4001/environs/httpstorage/backend.go#newcode54
> environs/httpstorage/backend.go:54: defer readcloser.Close()
> 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.

https://codereview.appspot.com/13736043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Go Bot (go-bot) wrote :
Download full text (7.8 KiB)

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.net/juju-core/agent 0.940s
ok launchpad.net/juju-core/agent/tools 0.304s
ok launchpad.net/juju-core/bzr 7.841s
ok launchpad.net/juju-core/cert 2.141s
ok launchpad.net/juju-core/charm 0.601s
? launchpad.net/juju-core/charm/hooks [no test files]
ok launchpad.net/juju-core/cloudinit 0.022s
ok launchpad.net/juju-core/cmd 0.207s
? launchpad.net/juju-core/cmd/builddb [no test files]
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 138.797s
ok launchpad.net/juju-core/cmd/jujud 40.019s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 1.869s
ok launchpad.net/juju-core/constraints 0.024s
ok launchpad.net/juju-core/container/lxc 0.394s
? launchpad.net/juju-core/container/lxc/mock [no test files]
ok launchpad.net/juju-core/downloader 5.282s
ok launchpad.net/juju-core/environs 1.976s
ok launchpad.net/juju-core/environs/bootstrap 5.172s
ok launchpad.net/juju-core/environs/cloudinit 0.451s
ok launchpad.net/juju-core/environs/config 0.796s
ok launchpad.net/juju-core/environs/configstore 0.066s
ok launchpad.net/juju-core/environs/filestorage 0.061s
ok launchpad.net/juju-core/environs/httpstorage 0.245s
ok launchpad.net/juju-core/environs/imagemetadata 0.535s
ok launchpad.net/juju-core/environs/instances 0.256s
ok launchpad.net/juju-core/environs/jujutest 0.226s
ok launchpad.net/juju-core/environs/manual 1.173s
ok launchpad.net/juju-core/environs/simplestreams 0.256s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 0.744s
ok launchpad.net/juju-core/environs/sync 26.682s
ok launchpad.net/juju-core/environs/testing 0.011s
ok launchpad.net/juju-core/environs/tools 10.007s
? launchpad.net/juju-core/environs/tools/testing [no test files]
? launchpad.net/juju-core/errors [no test files]
ok launchpad.net/juju-core/instance 0.020s
ok launchpad.net/juju-core/juju 17.497s
ok launchpad.net/juju-core/juju/osenv 0.219s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.015s
ok launchpad.net/juju-core/log/syslog 0.023s
ok launchpad.net/juju-core/names 0.037s
ok launchpad.net/juju-core/provider 0.259s
? launchpad.net/juju-core/provider/all [no test files]
ok launchpad.net/juju-core/provider/azure 6.380s
ok launchpad.net/juju-core/provider/dummy 20.464s
ok launchpad.net/juju-core/provider/ec2 5.396s
ok launchpad.net/juju-core/provider/ec2/httpstorage 0.049s
ok launchpad.net/juju-core/provider/local 1.800s
? launchpad.net/juju-core/provider/local/storage [no test files]
ok launchpad.net/juju-core/provider/maas 3.655s
ok launchpad.net/juju-core/provider/openstack 8.236s
ok launchpad.net/juju-core/rpc 0.264s
ok launchpad.net/juju-core/rpc/jsoncodec 0.270s
ok launchpad.net/juju-core/schema 0.018s

----------------------------------------------------------------------
PANIC: service_test.go:500: ServiceS...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'environs/bootstrap/bootstrap_test.go'
--- environs/bootstrap/bootstrap_test.go 2013-09-12 12:38:04 +0000
+++ environs/bootstrap/bootstrap_test.go 2013-09-18 06:28:38 +0000
@@ -13,7 +13,6 @@
13 "launchpad.net/juju-core/environs"13 "launchpad.net/juju-core/environs"
14 "launchpad.net/juju-core/environs/bootstrap"14 "launchpad.net/juju-core/environs/bootstrap"
15 "launchpad.net/juju-core/environs/config"15 "launchpad.net/juju-core/environs/config"
16 "launchpad.net/juju-core/environs/localstorage"
17 envtesting "launchpad.net/juju-core/environs/testing"16 envtesting "launchpad.net/juju-core/environs/testing"
18 "launchpad.net/juju-core/provider/dummy"17 "launchpad.net/juju-core/provider/dummy"
19 coretesting "launchpad.net/juju-core/testing"18 coretesting "launchpad.net/juju-core/testing"
@@ -52,8 +51,7 @@
5251
53func (s *bootstrapSuite) TestBootstrapNeedsSettings(c *gc.C) {52func (s *bootstrapSuite) TestBootstrapNeedsSettings(c *gc.C) {
54 env := newEnviron("bar", noKeysDefined)53 env := newEnviron("bar", noKeysDefined)
55 cleanup := setDummyStorage(c, env)54 s.setDummyStorage(c, env)
56 defer cleanup()
57 fixEnv := func(key string, value interface{}) {55 fixEnv := func(key string, value interface{}) {
58 cfg, err := env.Config().Apply(map[string]interface{}{56 cfg, err := env.Config().Apply(map[string]interface{}{
59 key: value,57 key: value,
@@ -87,8 +85,7 @@
8785
88func (s *bootstrapSuite) TestBootstrapEmptyConstraints(c *gc.C) {86func (s *bootstrapSuite) TestBootstrapEmptyConstraints(c *gc.C) {
89 env := newEnviron("foo", useDefaultKeys)87 env := newEnviron("foo", useDefaultKeys)
90 cleanup := setDummyStorage(c, env)88 s.setDummyStorage(c, env)
91 defer cleanup()
92 uploadTools(c, env)89 uploadTools(c, env)
93 err := bootstrap.Bootstrap(env, constraints.Value{})90 err := bootstrap.Bootstrap(env, constraints.Value{})
94 c.Assert(err, gc.IsNil)91 c.Assert(err, gc.IsNil)
@@ -98,8 +95,7 @@
9895
99func (s *bootstrapSuite) TestBootstrapSpecifiedConstraints(c *gc.C) {96func (s *bootstrapSuite) TestBootstrapSpecifiedConstraints(c *gc.C) {
100 env := newEnviron("foo", useDefaultKeys)97 env := newEnviron("foo", useDefaultKeys)
101 cleanup := setDummyStorage(c, env)98 s.setDummyStorage(c, env)
102 defer cleanup()
103 uploadTools(c, env)99 uploadTools(c, env)
104 cons := constraints.MustParse("cpu-cores=2 mem=4G")100 cons := constraints.MustParse("cpu-cores=2 mem=4G")
105 err := bootstrap.Bootstrap(env, cons)101 err := bootstrap.Bootstrap(env, cons)
@@ -188,8 +184,7 @@
188184
189func (s *bootstrapSuite) TestBootstrapNeedsTools(c *gc.C) {185func (s *bootstrapSuite) TestBootstrapNeedsTools(c *gc.C) {
190 env := newEnviron("foo", useDefaultKeys)186 env := newEnviron("foo", useDefaultKeys)
191 cleanup := setDummyStorage(c, env)187 s.setDummyStorage(c, env)
192 defer cleanup()
193 envtesting.RemoveFakeTools(c, env.Storage())188 envtesting.RemoveFakeTools(c, env.Storage())
194 err := bootstrap.Bootstrap(env, constraints.Value{})189 err := bootstrap.Bootstrap(env, constraints.Value{})
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")
@@ -228,13 +223,11 @@
228// setDummyStorage injects the local provider's fake storage implementation223// setDummyStorage injects the local provider's fake storage implementation
229// into the given environment, so that tests can manipulate storage as if it224// into the given environment, so that tests can manipulate storage as if it
230// were real.225// were real.
231// Returns a cleanup function that must be called when done with the storage.226func (s *bootstrapSuite) setDummyStorage(c *gc.C, env *bootstrapEnviron) {
232func setDummyStorage(c *gc.C, env *bootstrapEnviron) func() {227 closer, storage, _ := envtesting.CreateLocalTestStorage(c)
233 listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir())228 env.storage = storage
234 c.Assert(err, gc.IsNil)
235 env.storage = localstorage.Client(listener.Addr().String())
236 envtesting.UploadFakeTools(c, env.storage)229 envtesting.UploadFakeTools(c, env.storage)
237 return func() { listener.Close() }230 s.AddCleanup(func(c *gc.C) { closer.Close() })
238}231}
239232
240func (e *bootstrapEnviron) Name() string {233func (e *bootstrapEnviron) Name() string {
241234
=== modified file 'environs/filestorage/filestorage.go'
--- environs/filestorage/filestorage.go 2013-09-12 05:04:40 +0000
+++ environs/filestorage/filestorage.go 2013-09-18 06:28:38 +0000
@@ -10,8 +10,10 @@
10 "os"10 "os"
11 "path/filepath"11 "path/filepath"
12 "sort"12 "sort"
13 "strings"
1314
14 "launchpad.net/juju-core/environs"15 "launchpad.net/juju-core/environs"
16 coreerrors "launchpad.net/juju-core/errors"
15 "launchpad.net/juju-core/utils"17 "launchpad.net/juju-core/utils"
16)18)
1719
@@ -42,6 +44,15 @@
42// Get implements environs.StorageReader.Get.44// Get implements environs.StorageReader.Get.
43func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {45func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {
44 filename := f.fullPath(name)46 filename := f.fullPath(name)
47 fi, err := os.Stat(filename)
48 if err != nil {
49 if os.IsNotExist(err) {
50 err = coreerrors.NewNotFoundError(err, "")
51 }
52 return nil, err
53 } else if fi.IsDir() {
54 return nil, coreerrors.NotFoundf("no such file with name %q", name)
55 }
45 file, err := os.Open(filename)56 file, err := os.Open(filename)
46 if err != nil {57 if err != nil {
47 return nil, err58 return nil, err
@@ -51,26 +62,23 @@
5162
52// List implements environs.StorageReader.List.63// List implements environs.StorageReader.List.
53func (f *fileStorageReader) List(prefix string) ([]string, error) {64func (f *fileStorageReader) List(prefix string) ([]string, error) {
54 // Add one for the missing path separator.65 prefix = filepath.Join(f.path, prefix)
55 pathlen := len(f.path) + 166 dir := filepath.Dir(prefix)
56 pattern := filepath.Join(f.path, prefix+"*")67 var names []string
57 matches, err := filepath.Glob(pattern)68 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
58 if err != nil {
59 return nil, err
60 }
61 list := []string{}
62 for _, match := range matches {
63 fi, err := os.Stat(match)
64 if err != nil {69 if err != nil {
65 return nil, err70 return err
66 }71 }
67 if !fi.Mode().IsDir() {72 if !info.IsDir() && strings.HasPrefix(path, prefix) {
68 filename := match[pathlen:]73 names = append(names, path[len(f.path)+1:])
69 list = append(list, filename)74 }
70 }75 return nil
76 })
77 if err != nil && !os.IsNotExist(err) {
78 return nil, err
71 }79 }
72 sort.Strings(list)80 sort.Strings(names)
73 return list, nil81 return names, nil
74}82}
7583
76// URL implements environs.StorageReader.URL.84// URL implements environs.StorageReader.URL.
@@ -110,7 +118,11 @@
110118
111func (f *fileStorageWriter) Remove(name string) error {119func (f *fileStorageWriter) Remove(name string) error {
112 fullpath := f.fullPath(name)120 fullpath := f.fullPath(name)
113 return os.Remove(fullpath)121 err := os.Remove(fullpath)
122 if os.IsNotExist(err) {
123 err = nil
124 }
125 return err
114}126}
115127
116func (f *fileStorageWriter) RemoveAll() error {128func (f *fileStorageWriter) RemoveAll() error {
117129
=== modified file 'environs/filestorage/filestorage_test.go'
--- environs/filestorage/filestorage_test.go 2013-09-12 05:04:40 +0000
+++ environs/filestorage/filestorage_test.go 2013-09-18 06:28:38 +0000
@@ -10,6 +10,7 @@
10import (10import (
11 "bytes"11 "bytes"
12 "io/ioutil"12 "io/ioutil"
13 "os"
13 "path/filepath"14 "path/filepath"
14 "testing"15 "testing"
1516
@@ -17,6 +18,8 @@
1718
18 "launchpad.net/juju-core/environs"19 "launchpad.net/juju-core/environs"
19 "launchpad.net/juju-core/environs/filestorage"20 "launchpad.net/juju-core/environs/filestorage"
21 coreerrors "launchpad.net/juju-core/errors"
22 jc "launchpad.net/juju-core/testing/checkers"
20)23)
2124
22func TestPackage(t *testing.T) {25func TestPackage(t *testing.T) {
@@ -40,8 +43,10 @@
40 c.Assert(err, gc.IsNil)43 c.Assert(err, gc.IsNil)
41}44}
4245
43func (s *filestorageSuite) createFile(c *gc.C) (fullpath string, data []byte) {46func (s *filestorageSuite) createFile(c *gc.C, name string) (fullpath string, data []byte) {
44 fullpath = filepath.Join(s.dir, "test-file")47 fullpath = filepath.Join(s.dir, name)
48 dir := filepath.Dir(fullpath)
49 c.Assert(os.MkdirAll(dir, 0755), gc.IsNil)
45 data = []byte{1, 2, 3, 4, 5}50 data = []byte{1, 2, 3, 4, 5}
46 err := ioutil.WriteFile(fullpath, data, 0644)51 err := ioutil.WriteFile(fullpath, data, 0644)
47 c.Assert(err, gc.IsNil)52 c.Assert(err, gc.IsNil)
@@ -49,15 +54,35 @@
49}54}
5055
51func (s *filestorageSuite) TestList(c *gc.C) {56func (s *filestorageSuite) TestList(c *gc.C) {
52 expectedpath, _ := s.createFile(c)57 names := []string{
53 files, err := s.reader.List("test-")58 "a/b/c",
54 c.Assert(err, gc.IsNil)59 "a/bb",
55 _, file := filepath.Split(expectedpath)60 "a/c",
56 c.Assert(files, gc.DeepEquals, []string{file})61 "aa",
62 "b/c/d",
63 }
64 for _, name := range names {
65 s.createFile(c, name)
66 }
67 type test struct {
68 prefix string
69 expected []string
70 }
71 for i, test := range []test{
72 {"a", []string{"a/b/c", "a/bb", "a/c", "aa"}},
73 {"a/b", []string{"a/b/c", "a/bb"}},
74 {"a/b/c", []string{"a/b/c"}},
75 {"", names},
76 } {
77 c.Logf("test %d: prefix=%q", i, test.prefix)
78 files, err := s.reader.List(test.prefix)
79 c.Assert(err, gc.IsNil)
80 c.Assert(files, gc.DeepEquals, test.expected)
81 }
57}82}
5883
59func (s *filestorageSuite) TestURL(c *gc.C) {84func (s *filestorageSuite) TestURL(c *gc.C) {
60 expectedpath, _ := s.createFile(c)85 expectedpath, _ := s.createFile(c, "test-file")
61 _, file := filepath.Split(expectedpath)86 _, file := filepath.Split(expectedpath)
62 url, err := s.reader.URL(file)87 url, err := s.reader.URL(file)
63 c.Assert(err, gc.IsNil)88 c.Assert(err, gc.IsNil)
@@ -65,7 +90,7 @@
65}90}
6691
67func (s *filestorageSuite) TestGet(c *gc.C) {92func (s *filestorageSuite) TestGet(c *gc.C) {
68 expectedpath, data := s.createFile(c)93 expectedpath, data := s.createFile(c, "test-file")
69 _, file := filepath.Split(expectedpath)94 _, file := filepath.Split(expectedpath)
70 rc, err := s.reader.Get(file)95 rc, err := s.reader.Get(file)
71 c.Assert(err, gc.IsNil)96 c.Assert(err, gc.IsNil)
@@ -74,6 +99,15 @@
74 b, err := ioutil.ReadAll(rc)99 b, err := ioutil.ReadAll(rc)
75 c.Assert(err, gc.IsNil)100 c.Assert(err, gc.IsNil)
76 c.Assert(b, gc.DeepEquals, data)101 c.Assert(b, gc.DeepEquals, data)
102
103 // Get on a non-existant path returns NotFoundError
104 _, err = s.reader.Get("nowhere")
105 c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError)
106
107 // Get on a directory returns NotFoundError
108 s.createFile(c, "dir/file")
109 _, err = s.reader.Get("dir")
110 c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError)
77}111}
78112
79func (s *filestorageSuite) TestPut(c *gc.C) {113func (s *filestorageSuite) TestPut(c *gc.C) {
@@ -86,7 +120,7 @@
86}120}
87121
88func (s *filestorageSuite) TestRemove(c *gc.C) {122func (s *filestorageSuite) TestRemove(c *gc.C) {
89 expectedpath, _ := s.createFile(c)123 expectedpath, _ := s.createFile(c, "test-file")
90 _, file := filepath.Split(expectedpath)124 _, file := filepath.Split(expectedpath)
91 err := s.writer.Remove(file)125 err := s.writer.Remove(file)
92 c.Assert(err, gc.IsNil)126 c.Assert(err, gc.IsNil)
@@ -95,7 +129,7 @@
95}129}
96130
97func (s *filestorageSuite) TestRemoveAll(c *gc.C) {131func (s *filestorageSuite) TestRemoveAll(c *gc.C) {
98 expectedpath, _ := s.createFile(c)132 expectedpath, _ := s.createFile(c, "test-file")
99 err := s.writer.RemoveAll()133 err := s.writer.RemoveAll()
100 c.Assert(err, gc.IsNil)134 c.Assert(err, gc.IsNil)
101 _, err = ioutil.ReadFile(expectedpath)135 _, err = ioutil.ReadFile(expectedpath)
102136
=== renamed directory 'environs/localstorage' => 'environs/httpstorage'
=== modified file 'environs/httpstorage/backend.go'
--- environs/localstorage/backend.go 2013-07-03 00:15:19 +0000
+++ environs/httpstorage/backend.go 2013-09-18 06:28:38 +0000
@@ -1,26 +1,24 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package localstorage4package httpstorage
55
6import (6import (
7 "fmt"7 "fmt"
8 "io"
9 "io/ioutil"8 "io/ioutil"
10 "net"9 "net"
11 "net/http"10 "net/http"
12 "os"
13 "path/filepath"
14 "sort"
15 "strings"11 "strings"
12
13 "launchpad.net/juju-core/environs"
16)14)
1715
18// storageBackend provides HTTP access to a defined path. The local16// TODO(axw) 2013-09-16 bug #1225916
19// provider otimally would use a much simpler Storage, but this17// Implement authentication for modifying storage.
20// code may be useful in storage-free environs. Here it requires18
21// additional authentication work before it's viable.19// storageBackend provides HTTP access to a storage object.
22type storageBackend struct {20type storageBackend struct {
23 dir string21 backend environs.Storage
24}22}
2523
26// ServeHTTP handles the HTTP requests to the container.24// ServeHTTP handles the HTTP requests to the container.
@@ -43,9 +41,15 @@
4341
44// handleGet returns a storage file to the client.42// handleGet returns a storage file to the client.
45func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) {43func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) {
46 data, err := ioutil.ReadFile(filepath.Join(s.dir, req.URL.Path))44 readcloser, err := s.backend.Get(req.URL.Path[1:])
47 if err != nil {45 if err != nil {
48 http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)46 http.Error(w, fmt.Sprint(err), http.StatusNotFound)
47 return
48 }
49 defer readcloser.Close()
50 data, err := ioutil.ReadAll(readcloser)
51 if err != nil {
52 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
49 return53 return
50 }54 }
51 w.Header().Set("Content-Type", "application/octet-stream")55 w.Header().Set("Content-Type", "application/octet-stream")
@@ -54,62 +58,27 @@
5458
55// handleList returns the file names in the storage to the client.59// handleList returns the file names in the storage to the client.
56func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) {60func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) {
57 fp := filepath.Join(s.dir, req.URL.Path)61 prefix := req.URL.Path
58 dir, prefix := filepath.Split(fp)62 prefix = prefix[1 : len(prefix)-1] // drop the leading '/' and trailing '*'
59 names, err := readDirs(dir, prefix[:len(prefix)-1], len(s.dir)+1)63 names, err := s.backend.List(prefix)
60 if err != nil {64 if err != nil {
61 http.Error(w, fmt.Sprintf("404 %v", err), http.StatusNotFound)65 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
62 return66 return
63 }67 }
64 sort.Strings(names)
65 data := []byte(strings.Join(names, "\n"))68 data := []byte(strings.Join(names, "\n"))
66 w.Header().Set("Content-Type", "application/octet-stream")69 w.Header().Set("Content-Type", "application/octet-stream")
67 w.Write(data)70 w.Write(data)
68}71}
6972
70// readDirs reads the directory hierarchy and compares the found
71// names with the given prefix.
72func readDirs(dir, prefix string, start int) ([]string, error) {
73 names := []string{}
74 fis, err := ioutil.ReadDir(dir)
75 if err != nil {
76 return nil, err
77 }
78 for _, fi := range fis {
79 name := fi.Name()
80 if strings.HasPrefix(name, prefix) {
81 if fi.IsDir() {
82 dnames, err := readDirs(filepath.Join(dir, name), prefix, start)
83 if err != nil {
84 return nil, err
85 }
86 names = append(names, dnames...)
87 continue
88 }
89 fullname := filepath.Join(dir, name)[start:]
90 names = append(names, fullname)
91 }
92 }
93 return names, nil
94}
95
96// handlePut stores data from the client in the storage.73// handlePut stores data from the client in the storage.
97func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) {74func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) {
98 fp := filepath.Join(s.dir, req.URL.Path)75 if req.ContentLength < 0 {
99 dir, _ := filepath.Split(fp)76 http.Error(w, "missing or invalid Content-Length header", http.StatusInternalServerError)
100 err := os.MkdirAll(dir, 0777)77 return
101 if err != nil {78 }
102 http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)79 err := s.backend.Put(req.URL.Path[1:], req.Body, req.ContentLength)
103 return80 if err != nil {
104 }81 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
105 out, err := os.Create(fp)
106 if err != nil {
107 http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
108 return
109 }
110 defer out.Close()
111 if _, err := io.Copy(out, req.Body); err != nil {
112 http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)
113 return82 return
114 }83 }
115 w.WriteHeader(http.StatusCreated)84 w.WriteHeader(http.StatusCreated)
@@ -117,28 +86,19 @@
11786
118// handleDelete removes a file from the storage.87// handleDelete removes a file from the storage.
119func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) {88func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) {
120 fp := filepath.Join(s.dir, req.URL.Path)89 err := s.backend.Remove(req.URL.Path[1:])
121 if err := os.Remove(fp); err != nil && !os.IsNotExist(err) {90 if err != nil {
122 http.Error(w, fmt.Sprintf("500 %v", err), http.StatusInternalServerError)91 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
123 return92 return
124 }93 }
125 w.WriteHeader(http.StatusOK)94 w.WriteHeader(http.StatusOK)
126}95}
12796
128// Serve runs a storage server on the given network address, storing97// Serve runs a storage server on the given network address, relaying
129// data under the given directory. It returns the network listener.98// requests to the given storage implementation. It returns the network
130// This can then be attached to with Client.99// listener. This can then be attached to with Client.
131func Serve(addr, dir string) (net.Listener, error) {100func Serve(addr string, storage environs.Storage) (net.Listener, error) {
132 backend := &storageBackend{101 backend := &storageBackend{backend: storage}
133 dir: dir,
134 }
135 info, err := os.Stat(dir)
136 if err != nil {
137 return nil, fmt.Errorf("cannot stat directory: %v", err)
138 }
139 if !info.IsDir() {
140 return nil, fmt.Errorf("%q is not a directory", dir)
141 }
142 listener, err := net.Listen("tcp", addr)102 listener, err := net.Listen("tcp", addr)
143 if err != nil {103 if err != nil {
144 return nil, fmt.Errorf("cannot start listener: %v", err)104 return nil, fmt.Errorf("cannot start listener: %v", err)
145105
=== modified file 'environs/httpstorage/backend_test.go'
--- environs/localstorage/backend_test.go 2013-09-13 14:48:13 +0000
+++ environs/httpstorage/backend_test.go 2013-09-18 06:28:38 +0000
@@ -1,7 +1,7 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package localstorage_test4package httpstorage_test
55
6import (6import (
7 "bytes"7 "bytes"
@@ -16,7 +16,8 @@
1616
17 gc "launchpad.net/gocheck"17 gc "launchpad.net/gocheck"
1818
19 "launchpad.net/juju-core/environs/localstorage"19 "launchpad.net/juju-core/environs/filestorage"
20 "launchpad.net/juju-core/environs/httpstorage"
20 "launchpad.net/juju-core/testing"21 "launchpad.net/juju-core/testing"
21)22)
2223
@@ -35,7 +36,9 @@
35// a base URL for the server and the directory path.36// a base URL for the server and the directory path.
36func startServer(c *gc.C) (listener net.Listener, url, dataDir string) {37func startServer(c *gc.C) (listener net.Listener, url, dataDir string) {
37 dataDir = c.MkDir()38 dataDir = c.MkDir()
38 listener, err := localstorage.Serve("localhost:0", dataDir)39 embedded, err := filestorage.NewFileStorageWriter(dataDir)
40 c.Assert(err, gc.IsNil)
41 listener, err = httpstorage.Serve("localhost:0", embedded)
39 c.Assert(err, gc.IsNil)42 c.Assert(err, gc.IsNil)
40 return listener, fmt.Sprintf("http://%s/", listener.Addr()), dataDir43 return listener, fmt.Sprintf("http://%s/", listener.Addr()), dataDir
41}44}
4245
=== modified file 'environs/httpstorage/storage.go'
--- environs/localstorage/storage.go 2013-09-02 04:11:11 +0000
+++ environs/httpstorage/storage.go 2013-09-18 06:28:38 +0000
@@ -1,7 +1,7 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package localstorage4package httpstorage
55
6import (6import (
7 "fmt"7 "fmt"
@@ -112,6 +112,7 @@
112 return err112 return err
113 }113 }
114 req.Header.Set("Content-Type", "application/octet-stream")114 req.Header.Set("Content-Type", "application/octet-stream")
115 req.ContentLength = length
115 resp, err := http.DefaultClient.Do(req)116 resp, err := http.DefaultClient.Do(req)
116 if err != nil {117 if err != nil {
117 return err118 return err
118119
=== modified file 'environs/httpstorage/storage_test.go'
--- environs/localstorage/storage_test.go 2013-08-29 01:46:55 +0000
+++ environs/httpstorage/storage_test.go 2013-09-18 06:28:38 +0000
@@ -1,7 +1,7 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package localstorage_test4package httpstorage_test
55
6import (6import (
7 "bytes"7 "bytes"
@@ -13,7 +13,7 @@
13 gc "launchpad.net/gocheck"13 gc "launchpad.net/gocheck"
1414
15 "launchpad.net/juju-core/environs"15 "launchpad.net/juju-core/environs"
16 "launchpad.net/juju-core/environs/localstorage"16 "launchpad.net/juju-core/environs/httpstorage"
17 "launchpad.net/juju-core/errors"17 "launchpad.net/juju-core/errors"
18 jc "launchpad.net/juju-core/testing/checkers"18 jc "launchpad.net/juju-core/testing/checkers"
19)19)
@@ -25,7 +25,7 @@
25func (s *storageSuite) TestList(c *gc.C) {25func (s *storageSuite) TestList(c *gc.C) {
26 listener, _, _ := startServer(c)26 listener, _, _ := startServer(c)
27 defer listener.Close()27 defer listener.Close()
28 storage := localstorage.Client(listener.Addr().String())28 storage := httpstorage.Client(listener.Addr().String())
29 names, err := storage.List("a/b/c")29 names, err := storage.List("a/b/c")
30 c.Assert(err, gc.IsNil)30 c.Assert(err, gc.IsNil)
31 c.Assert(names, gc.HasLen, 0)31 c.Assert(names, gc.HasLen, 0)
@@ -37,7 +37,7 @@
37 listener, _, _ := startServer(c)37 listener, _, _ := startServer(c)
38 defer listener.Close()38 defer listener.Close()
3939
40 storage := localstorage.Client(listener.Addr().String())40 storage := httpstorage.Client(listener.Addr().String())
41 names := []string{41 names := []string{
42 "aa",42 "aa",
43 "zzz/aa",43 "zzz/aa",
@@ -51,7 +51,7 @@
51 checkList(c, storage, "a", []string{"aa"})51 checkList(c, storage, "a", []string{"aa"})
52 checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"})52 checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"})
5353
54 storage2 := localstorage.Client(listener.Addr().String())54 storage2 := httpstorage.Client(listener.Addr().String())
55 for _, name := range names {55 for _, name := range names {
56 checkFileHasContents(c, storage2, name, []byte(name))56 checkFileHasContents(c, storage2, name, []byte(name))
57 }57 }
5858
=== modified file 'environs/testing/storage.go'
--- environs/testing/storage.go 2013-09-17 05:29:35 +0000
+++ environs/testing/storage.go 2013-09-18 06:28:38 +0000
@@ -17,7 +17,8 @@
17 gc "launchpad.net/gocheck"17 gc "launchpad.net/gocheck"
1818
19 "launchpad.net/juju-core/environs"19 "launchpad.net/juju-core/environs"
20 "launchpad.net/juju-core/environs/localstorage"20 "launchpad.net/juju-core/environs/filestorage"
21 "launchpad.net/juju-core/environs/httpstorage"
21 "launchpad.net/juju-core/environs/tools"22 "launchpad.net/juju-core/environs/tools"
22 "launchpad.net/juju-core/version"23 "launchpad.net/juju-core/version"
23)24)
@@ -27,9 +28,11 @@
27// directory.28// directory.
28func CreateLocalTestStorage(c *gc.C) (closer io.Closer, storage environs.Storage, dataDir string) {29func CreateLocalTestStorage(c *gc.C) (closer io.Closer, storage environs.Storage, dataDir string) {
29 dataDir = c.MkDir()30 dataDir = c.MkDir()
30 listener, err := localstorage.Serve("localhost:0", dataDir)31 underlying, err := filestorage.NewFileStorageWriter(dataDir)
31 c.Assert(err, gc.IsNil)32 c.Assert(err, gc.IsNil)
32 storage = localstorage.Client(listener.Addr().String())33 listener, err := httpstorage.Serve("localhost:0", underlying)
34 c.Assert(err, gc.IsNil)
35 storage = httpstorage.Client(listener.Addr().String())
33 closer = listener36 closer = listener
34 return37 return
35}38}
3639
=== modified file 'provider/azure/environ_test.go'
--- provider/azure/environ_test.go 2013-09-12 14:16:48 +0000
+++ provider/azure/environ_test.go 2013-09-18 06:28:38 +0000
@@ -23,8 +23,8 @@
23 "launchpad.net/juju-core/environs"23 "launchpad.net/juju-core/environs"
24 "launchpad.net/juju-core/environs/config"24 "launchpad.net/juju-core/environs/config"
25 "launchpad.net/juju-core/environs/imagemetadata"25 "launchpad.net/juju-core/environs/imagemetadata"
26 "launchpad.net/juju-core/environs/localstorage"
27 "launchpad.net/juju-core/environs/simplestreams"26 "launchpad.net/juju-core/environs/simplestreams"
27 envtesting "launchpad.net/juju-core/environs/testing"
28 "launchpad.net/juju-core/environs/tools"28 "launchpad.net/juju-core/environs/tools"
29 "launchpad.net/juju-core/errors"29 "launchpad.net/juju-core/errors"
30 "launchpad.net/juju-core/instance"30 "launchpad.net/juju-core/instance"
@@ -54,12 +54,10 @@
54// setDummyStorage injects the local provider's fake storage implementation54// setDummyStorage injects the local provider's fake storage implementation
55// into the given environment, so that tests can manipulate storage as if it55// into the given environment, so that tests can manipulate storage as if it
56// were real.56// were real.
57// Returns a cleanup function that must be called when done with the storage.57func (s *environSuite) setDummyStorage(c *gc.C, env *azureEnviron) {
58func setDummyStorage(c *gc.C, env *azureEnviron) func() {58 closer, storage, _ := envtesting.CreateLocalTestStorage(c)
59 listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir())59 env.storage = storage
60 c.Assert(err, gc.IsNil)60 s.AddCleanup(func(c *gc.C) { closer.Close() })
61 env.storage = localstorage.Client(listener.Addr().String())
62 return func() { listener.Close() }
63}61}
6462
65func (*environSuite) TestGetSnapshot(c *gc.C) {63func (*environSuite) TestGetSnapshot(c *gc.C) {
@@ -469,22 +467,20 @@
469 c.Check(env.storageAccountKey, gc.Equals, "")467 c.Check(env.storageAccountKey, gc.Equals, "")
470}468}
471469
472func (*environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) {470func (s *environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) {
473 env := makeEnviron(c)471 env := makeEnviron(c)
474 cleanup := setDummyStorage(c, env)472 s.setDummyStorage(c, env)
475 defer cleanup()
476 _, _, err := env.StateInfo()473 _, _, err := env.StateInfo()
477 c.Check(err, jc.Satisfies, errors.IsNotBootstrapped)474 c.Check(err, jc.Satisfies, errors.IsNotBootstrapped)
478}475}
479476
480func (*environSuite) TestStateInfo(c *gc.C) {477func (s *environSuite) TestStateInfo(c *gc.C) {
481 instanceID := "my-instance"478 instanceID := "my-instance"
482 patchWithServiceListResponse(c, []gwacl.HostedServiceDescriptor{{479 patchWithServiceListResponse(c, []gwacl.HostedServiceDescriptor{{
483 ServiceName: instanceID,480 ServiceName: instanceID,
484 }})481 }})
485 env := makeEnviron(c)482 env := makeEnviron(c)
486 cleanup := setDummyStorage(c, env)483 s.setDummyStorage(c, env)
487 defer cleanup()
488 err := provider.SaveState(484 err := provider.SaveState(
489 env.Storage(),485 env.Storage(),
490 &provider.BootstrapState{StateInstances: []instance.Id{instance.Id(instanceID)}})486 &provider.BootstrapState{StateInstances: []instance.Id{instance.Id(instanceID)}})
@@ -705,15 +701,13 @@
705 return service1, service1Desc701 return service1, service1Desc
706}702}
707703
708func setServiceDeletionConcurrency(nbGoroutines int) func() {704func (s *environSuite) setServiceDeletionConcurrency(nbGoroutines int) {
709 oldMaxConcurrentDeletes := maxConcurrentDeletes705 restore := jc.Set(&maxConcurrentDeletes, nbGoroutines)
710 maxConcurrentDeletes = nbGoroutines706 s.AddCleanup(func(*gc.C) { restore() })
711 return func() { maxConcurrentDeletes = oldMaxConcurrentDeletes }
712}707}
713708
714func (*environSuite) TestStopInstancesDestroysMachines(c *gc.C) {709func (s *environSuite) TestStopInstancesDestroysMachines(c *gc.C) {
715 cleanup := setServiceDeletionConcurrency(3)710 s.setServiceDeletionConcurrency(3)
716 defer cleanup()
717 service1Name := "service1"711 service1Name := "service1"
718 service1, service1Desc := makeAzureService(service1Name)712 service1, service1Desc := makeAzureService(service1Name)
719 service2Name := "service2"713 service2Name := "service2"
@@ -739,9 +733,8 @@
739 assertOneRequestMatches(c, *requests, "DELETE", ".*"+service2Name+".*")733 assertOneRequestMatches(c, *requests, "DELETE", ".*"+service2Name+".*")
740}734}
741735
742func (*environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) {736func (s *environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) {
743 cleanup := setServiceDeletionConcurrency(3)737 s.setServiceDeletionConcurrency(3)
744 defer cleanup()
745 responses := []gwacl.DispatcherResponse{738 responses := []gwacl.DispatcherResponse{
746 gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil),739 gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil),
747 }740 }
@@ -767,9 +760,8 @@
767 assertOneRequestMatches(c, *requests, "DELETE", ".*")760 assertOneRequestMatches(c, *requests, "DELETE", ".*")
768}761}
769762
770func (*environSuite) TestStopInstancesWithLimitedConcurrency(c *gc.C) {763func (s *environSuite) TestStopInstancesWithLimitedConcurrency(c *gc.C) {
771 cleanup := setServiceDeletionConcurrency(3)764 s.setServiceDeletionConcurrency(3)
772 defer cleanup()
773 services := []*gwacl.HostedService{}765 services := []*gwacl.HostedService{}
774 serviceDescs := []gwacl.HostedServiceDescriptor{}766 serviceDescs := []gwacl.HostedServiceDescriptor{}
775 for i := 0; i < 10; i++ {767 for i := 0; i < 10; i++ {
@@ -788,9 +780,8 @@
788 c.Check(len(*requests), gc.Equals, len(services)*2)780 c.Check(len(*requests), gc.Equals, len(services)*2)
789}781}
790782
791func (*environSuite) TestStopInstancesWithZeroInstance(c *gc.C) {783func (s *environSuite) TestStopInstancesWithZeroInstance(c *gc.C) {
792 cleanup := setServiceDeletionConcurrency(3)784 s.setServiceDeletionConcurrency(3)
793 defer cleanup()
794 env := makeEnviron(c)785 env := makeEnviron(c)
795 instances := []instance.Instance{}786 instances := []instance.Instance{}
796787
@@ -818,10 +809,9 @@
818 return cleanupResponses809 return cleanupResponses
819}810}
820811
821func (*environSuite) TestDestroyDoesNotCleanStorageIfError(c *gc.C) {812func (s *environSuite) TestDestroyDoesNotCleanStorageIfError(c *gc.C) {
822 env := makeEnviron(c)813 env := makeEnviron(c)
823 cleanup := setDummyStorage(c, env)814 s.setDummyStorage(c, env)
824 defer cleanup()
825 // Populate storage.815 // Populate storage.
826 err := provider.SaveState(816 err := provider.SaveState(
827 env.Storage(),817 env.Storage(),
@@ -840,10 +830,9 @@
840 c.Check(files, gc.HasLen, 1)830 c.Check(files, gc.HasLen, 1)
841}831}
842832
843func (*environSuite) TestDestroyCleansUpStorage(c *gc.C) {833func (s *environSuite) TestDestroyCleansUpStorage(c *gc.C) {
844 env := makeEnviron(c)834 env := makeEnviron(c)
845 cleanup := setDummyStorage(c, env)835 s.setDummyStorage(c, env)
846 defer cleanup()
847 // Populate storage.836 // Populate storage.
848 err := provider.SaveState(837 err := provider.SaveState(
849 env.Storage(),838 env.Storage(),
@@ -864,10 +853,9 @@
864 c.Check(files, gc.HasLen, 0)853 c.Check(files, gc.HasLen, 0)
865}854}
866855
867func (*environSuite) TestDestroyDeletesVirtualNetworkAndAffinityGroup(c *gc.C) {856func (s *environSuite) TestDestroyDeletesVirtualNetworkAndAffinityGroup(c *gc.C) {
868 env := makeEnviron(c)857 env := makeEnviron(c)
869 cleanup := setDummyStorage(c, env)858 s.setDummyStorage(c, env)
870 defer cleanup()
871 services := []gwacl.HostedServiceDescriptor{}859 services := []gwacl.HostedServiceDescriptor{}
872 responses := getAzureServiceListResponse(c, services)860 responses := getAzureServiceListResponse(c, services)
873 // Prepare a configuration with a single virtual network.861 // Prepare a configuration with a single virtual network.
@@ -933,12 +921,10 @@
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))
934}922}
935923
936func (*environSuite) TestDestroyStopsAllInstances(c *gc.C) {924func (s *environSuite) TestDestroyStopsAllInstances(c *gc.C) {
937 cleanup1 := setServiceDeletionConcurrency(3)925 s.setServiceDeletionConcurrency(3)
938 defer cleanup1()
939 env := makeEnviron(c)926 env := makeEnviron(c)
940 cleanup2 := setDummyStorage(c, env)927 s.setDummyStorage(c, env)
941 defer cleanup2()
942928
943 // Simulate 2 instances corresponding to two Azure services.929 // Simulate 2 instances corresponding to two Azure services.
944 prefix := env.getEnvPrefix()930 prefix := env.getEnvPrefix()
@@ -1300,10 +1286,9 @@
1300 c.Assert(retrieved, gc.DeepEquals, content)1286 c.Assert(retrieved, gc.DeepEquals, content)
1301}1287}
13021288
1303func (*environSuite) TestGetImageMetadataSources(c *gc.C) {1289func (s *environSuite) TestGetImageMetadataSources(c *gc.C) {
1304 env := makeEnviron(c)1290 env := makeEnviron(c)
1305 cleanup := setDummyStorage(c, env)1291 s.setDummyStorage(c, env)
1306 defer cleanup()
13071292
1308 data := []byte{1, 2, 3, 4}1293 data := []byte{1, 2, 3, 4}
1309 env.Storage().Put("filename", bytes.NewReader(data), int64(len(data)))1294 env.Storage().Put("filename", bytes.NewReader(data), int64(len(data)))
@@ -1320,10 +1305,9 @@
1320 c.Assert(url, gc.Equals, imagemetadata.DefaultBaseURL+"/")1305 c.Assert(url, gc.Equals, imagemetadata.DefaultBaseURL+"/")
1321}1306}
13221307
1323func (*environSuite) TestGetToolsMetadataSources(c *gc.C) {1308func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) {
1324 env := makeEnviron(c)1309 env := makeEnviron(c)
1325 cleanup := setDummyStorage(c, env)1310 s.setDummyStorage(c, env)
1326 defer cleanup()
13271311
1328 data := []byte{1, 2, 3, 4}1312 data := []byte{1, 2, 3, 4}
1329 env.Storage().Put("tools/filename", bytes.NewReader(data), int64(len(data)))1313 env.Storage().Put("tools/filename", bytes.NewReader(data), int64(len(data)))
13301314
=== modified file 'provider/local/environ.go'
--- provider/local/environ.go 2013-09-16 22:02:24 +0000
+++ provider/local/environ.go 2013-09-18 06:28:38 +0000
@@ -21,7 +21,8 @@
21 "launchpad.net/juju-core/environs/bootstrap"21 "launchpad.net/juju-core/environs/bootstrap"
22 "launchpad.net/juju-core/environs/cloudinit"22 "launchpad.net/juju-core/environs/cloudinit"
23 "launchpad.net/juju-core/environs/config"23 "launchpad.net/juju-core/environs/config"
24 "launchpad.net/juju-core/environs/localstorage"24 "launchpad.net/juju-core/environs/filestorage"
25 "launchpad.net/juju-core/environs/httpstorage"
25 "launchpad.net/juju-core/instance"26 "launchpad.net/juju-core/instance"
26 "launchpad.net/juju-core/juju/osenv"27 "launchpad.net/juju-core/juju/osenv"
27 "launchpad.net/juju-core/names"28 "launchpad.net/juju-core/names"
@@ -164,7 +165,11 @@
164 } else if !info.Mode().IsDir() {165 } else if !info.Mode().IsDir() {
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)
166 }167 }
167 return localstorage.Serve(address, dir)168 storage, err := filestorage.NewFileStorageWriter(dir)
169 if err != nil {
170 return nil, err
171 }
172 return httpstorage.Serve(address, storage)
168}173}
169174
170// SetConfig is specified in the Environ interface.175// SetConfig is specified in the Environ interface.
@@ -319,12 +324,12 @@
319324
320// Storage is specified in the Environ interface.325// Storage is specified in the Environ interface.
321func (env *localEnviron) Storage() environs.Storage {326func (env *localEnviron) Storage() environs.Storage {
322 return localstorage.Client(env.config.storageAddr())327 return httpstorage.Client(env.config.storageAddr())
323}328}
324329
325// PublicStorage is specified in the Environ interface.330// PublicStorage is specified in the Environ interface.
326func (env *localEnviron) PublicStorage() environs.StorageReader {331func (env *localEnviron) PublicStorage() environs.StorageReader {
327 return localstorage.Client(env.config.sharedStorageAddr())332 return httpstorage.Client(env.config.sharedStorageAddr())
328}333}
329334
330// Destroy is specified in the Environ interface.335// Destroy is specified in the Environ interface.
331336
=== modified file 'provider/local/storage/worker.go'
--- provider/local/storage/worker.go 2013-08-30 04:07:04 +0000
+++ provider/local/storage/worker.go 2013-09-18 06:28:38 +0000
@@ -5,7 +5,8 @@
5 "launchpad.net/tomb"5 "launchpad.net/tomb"
66
7 "launchpad.net/juju-core/agent"7 "launchpad.net/juju-core/agent"
8 "launchpad.net/juju-core/environs/localstorage"8 "launchpad.net/juju-core/environs/filestorage"
9 "launchpad.net/juju-core/environs/httpstorage"
9 "launchpad.net/juju-core/worker"10 "launchpad.net/juju-core/worker"
10)11)
1112
@@ -40,7 +41,12 @@
40 storageAddr := s.config.Value(agent.StorageAddr)41 storageAddr := s.config.Value(agent.StorageAddr)
41 logger.Infof("serving %s on %s", storageDir, storageAddr)42 logger.Infof("serving %s on %s", storageDir, storageAddr)
4243
43 storageListener, err := localstorage.Serve(storageAddr, storageDir)44 storage, err := filestorage.NewFileStorageWriter(storageDir)
45 if err != nil {
46 logger.Errorf("error with local storage: %v", err)
47 return err
48 }
49 storageListener, err := httpstorage.Serve(storageAddr, storage)
44 if err != nil {50 if err != nil {
45 logger.Errorf("error with local storage: %v", err)51 logger.Errorf("error with local storage: %v", err)
46 return err52 return err
@@ -51,7 +57,12 @@
51 sharedStorageAddr := s.config.Value(agent.SharedStorageAddr)57 sharedStorageAddr := s.config.Value(agent.SharedStorageAddr)
52 logger.Infof("serving %s on %s", sharedStorageDir, sharedStorageAddr)58 logger.Infof("serving %s on %s", sharedStorageDir, sharedStorageAddr)
5359
54 sharedStorageListener, err := localstorage.Serve(sharedStorageAddr, sharedStorageDir)60 sharedStorage, err := filestorage.NewFileStorageWriter(sharedStorageDir)
61 if err != nil {
62 logger.Errorf("error with local storage: %v", err)
63 return err
64 }
65 sharedStorageListener, err := httpstorage.Serve(sharedStorageAddr, sharedStorage)
55 if err != nil {66 if err != nil {
56 logger.Errorf("error with local storage: %v", err)67 logger.Errorf("error with local storage: %v", err)
57 return err68 return err
5869
=== modified file 'provider/state_test.go'
--- provider/state_test.go 2013-09-12 12:38:04 +0000
+++ provider/state_test.go 2013-09-18 06:28:38 +0000
@@ -12,7 +12,7 @@
1212
13 "launchpad.net/juju-core/environs"13 "launchpad.net/juju-core/environs"
14 "launchpad.net/juju-core/environs/config"14 "launchpad.net/juju-core/environs/config"
15 "launchpad.net/juju-core/environs/localstorage"15 envtesting "launchpad.net/juju-core/environs/testing"
16 "launchpad.net/juju-core/errors"16 "launchpad.net/juju-core/errors"
17 "launchpad.net/juju-core/instance"17 "launchpad.net/juju-core/instance"
18 "launchpad.net/juju-core/provider"18 "launchpad.net/juju-core/provider"
@@ -20,23 +20,21 @@
20 jc "launchpad.net/juju-core/testing/checkers"20 jc "launchpad.net/juju-core/testing/checkers"
21)21)
2222
23type StateSuite struct{}23type StateSuite struct {
24 testing.LoggingSuite
25}
2426
25var _ = gc.Suite(&StateSuite{})27var _ = gc.Suite(&StateSuite{})
2628
27// makeDummyStorage creates a local storage.29// makeDummyStorage creates a local storage.
28// Returns a cleanup function that must be called when done with the storage.30func (suite *StateSuite) makeDummyStorage(c *gc.C) environs.Storage {
29func makeDummyStorage(c *gc.C) (environs.Storage, func()) {31 closer, storage, _ := envtesting.CreateLocalTestStorage(c)
30 listener, err := localstorage.Serve("127.0.0.1:0", c.MkDir())32 suite.AddCleanup(func(*gc.C) { closer.Close() })
31 c.Assert(err, gc.IsNil)33 return storage
32 storage := localstorage.Client(listener.Addr().String())
33 cleanup := func() { listener.Close() }
34 return storage, cleanup
35}34}
3635
37func (*StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) {36func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) {
38 storage, cleanup := makeDummyStorage(c)37 storage := suite.makeDummyStorage(c)
39 defer cleanup()
4038
41 url, err := provider.CreateStateFile(storage)39 url, err := provider.CreateStateFile(storage)
42 c.Assert(err, gc.IsNil)40 c.Assert(err, gc.IsNil)
@@ -53,8 +51,7 @@
53}51}
5452
55func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) {53func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) {
56 storage, cleanup := makeDummyStorage(c)54 storage := suite.makeDummyStorage(c)
57 defer cleanup()
58 arch := "amd64"55 arch := "amd64"
59 state := provider.BootstrapState{56 state := provider.BootstrapState{
60 StateInstances: []instance.Id{instance.Id("an-instance-id")},57 StateInstances: []instance.Id{instance.Id("an-instance-id")},
@@ -85,8 +82,7 @@
85}82}
8683
87func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) {84func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) {
88 storage, cleanup := makeDummyStorage(c)85 storage := suite.makeDummyStorage(c)
89 defer cleanup()
90 state := suite.setUpSavedState(c, storage)86 state := suite.setUpSavedState(c, storage)
91 storedState, err := provider.LoadState(storage)87 storedState, err := provider.LoadState(storage)
92 c.Assert(err, gc.IsNil)88 c.Assert(err, gc.IsNil)
@@ -94,8 +90,7 @@
94}90}
9591
96func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) {92func (suite *StateSuite) TestLoadStateFromURLReadsStateFile(c *gc.C) {
97 storage, cleanup := makeDummyStorage(c)93 storage := suite.makeDummyStorage(c)
98 defer cleanup()
99 state := suite.setUpSavedState(c, storage)94 state := suite.setUpSavedState(c, storage)
100 url, err := storage.URL(provider.StateFile)95 url, err := storage.URL(provider.StateFile)
101 c.Assert(err, gc.IsNil)96 c.Assert(err, gc.IsNil)
@@ -105,8 +100,7 @@
105}100}
106101
107func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) {102func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) {
108 storage, cleanup := makeDummyStorage(c)103 storage := suite.makeDummyStorage(c)
109 defer cleanup()
110104
111 _, err := provider.LoadState(storage)105 _, err := provider.LoadState(storage)
112106
@@ -114,8 +108,7 @@
114}108}
115109
116func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) {110func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) {
117 storage, cleanup := makeDummyStorage(c)111 storage := suite.makeDummyStorage(c)
118 defer cleanup()
119 arch := "amd64"112 arch := "amd64"
120 state := provider.BootstrapState{113 state := provider.BootstrapState{
121 StateInstances: []instance.Id{instance.Id("an-instance-id")},114 StateInstances: []instance.Id{instance.Id("an-instance-id")},

Subscribers

People subscribed via source and target branches

to status/vote changes: