Merge lp:~thumper/juju-core/local-config into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 1374
Proposed branch: lp:~thumper/juju-core/local-config
Merge into: lp:~go-bot/juju-core/trunk
Prerequisite: lp:~thumper/juju-core/local-provider-skeleton
Diff against target: 502 lines (+382/-8)
10 files modified
environs/local/config.go (+53/-0)
environs/local/config_test.go (+89/-0)
environs/local/environ.go (+16/-3)
environs/local/environ_test.go (+25/-0)
environs/local/environprovider.go (+89/-5)
environs/local/environprovider_test.go (+35/-0)
environs/local/export_test.go (+6/-0)
environs/local/local_test.go (+1/-0)
utils/file.go (+19/-0)
utils/file_test.go (+49/-0)
To merge this branch: bzr merge lp:~thumper/juju-core/local-config
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+171956@code.launchpad.net

Commit message

Add initial local provider config.

Right now, just looking at the directories for storing
the files used by public and private storage. The branch
also adds in the Validation and setting/getting methods
for the config.

https://codereview.appspot.com/10734043/

Description of the change

Add initial local provider config.

Right now, just looking at the directories for storing
the files used by public and private storage. The branch
also adds in the Validation and setting/getting methods
for the config.

https://codereview.appspot.com/10734043/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+171956_code.launchpad.net,

Message:
Please take a look.

Description:
Add initial local provider config.

Right now, just looking at the directories for storing
the files used by public and private storage. The branch
also adds in the Validation and setting/getting methods
for the config.

https://code.launchpad.net/~thumper/juju-core/local-config/+merge/171956

Requires:
https://code.launchpad.net/~thumper/juju-core/local-provider-skeleton/+merge/171951

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   A environs/local/config.go
   M environs/local/environ-provider.go
   A environs/local/environ-provider_test.go
   M environs/local/environ.go
   A environs/local/environ_test.go
   A environs/local/export_test.go

Revision history for this message
Ian Booth (wallyworld) wrote :

A few comments inline.
It would be great also if the config tests could be structured a little
like the ones used for the other providers, where and array of test
scenarios for things like default value handling etc is tested.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go
File environs/local/environ-provider.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode30
environs/local/environ-provider.go:30: defaultPrivateStorageDir =
"/var/lib/juju/local/%s/private"
Perhaps a stupid question - do we really want to make these in a
location where the user needs privileges or access granted? Why not
somewhere in their home dir? That would also allow many users on the one
machine to run juju with a local provider without interfering with each
other/

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode45
environs/local/environ-provider.go:45: func ensureDirExists(path string)
error {
Just curious - do we already have this function somewhere? If not, could
we consider putting it in a utils package or something. I'm a bit
perplexed the std libs don't have this built in.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go
File environs/local/environ-provider_test.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode61
environs/local/environ-provider_test.go:61: func (s *providerSuite)
TestValidateConfig(c *C) {
Could we follow the other providers and put config tests in a
config_test.go file?

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go
File environs/local/environ.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go#newcode22
environs/local/environ.go:22: localMutex sync.Mutex
Other providers need more than one mutex. The one protecting config is
called cfgMutex. Perhaps we can follow that convention? Or do we think
there will only be the one mutex required?

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go#newcode43
environs/local/environ.go:43: func (env *localEnviron) Config()
*config.Config {
Other providers use an intermediate ecfg() method. I'm not sure why.
Perhaps we could find out if that approach is for a good reason and
adopt it if required here too?

https://codereview.appspot.com/10734043/

Revision history for this message
William Reade (fwereade) wrote :

LGTM, I think, modulo quibbles below:

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go
File environs/local/environ-provider.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode30
environs/local/environ-provider.go:30: defaultPrivateStorageDir =
"/var/lib/juju/local/%s/private"
On 2013/06/28 06:22:57, wallyworld wrote:
> Perhaps a stupid question - do we really want to make these in a
location where
> the user needs privileges or access granted? Why not somewhere in
their home
> dir? That would also allow many users on the one machine to run juju
with a
> local provider without interfering with each other/

This is a good point and a noble goal. Somewhat implies badging things
like containers with username as well as environment name; probably a
good thing.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode45
environs/local/environ-provider.go:45: func ensureDirExists(path string)
error {
On 2013/06/28 06:22:57, wallyworld wrote:
> Just curious - do we already have this function somewhere? If not,
could we
> consider putting it in a utils package or something. I'm a bit
perplexed the std
> libs don't have this built in.

os.MkdirAll

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode91
environs/local/environ-provider.go:91: }
We need to check the storage dirs don't change, I think.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go
File environs/local/environ.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go#newcode22
environs/local/environ.go:22: localMutex sync.Mutex
On 2013/06/28 06:22:57, wallyworld wrote:
> Other providers need more than one mutex. The one protecting config is
called
> cfgMutex. Perhaps we can follow that convention? Or do we think there
will only
> be the one mutex required?

I'm ok with this as it is... I don't think this is going to be an
overwhelmingly complex provider. I hope.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go#newcode46
environs/local/environ.go:46: return env.config.Config
These are immutable, right?

https://codereview.appspot.com/10734043/diff/1/environs/local/environ_test.go
File environs/local/environ_test.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ_test.go#newcode12
environs/local/environ_test.go:12: providerSuite
This'll run all the tests in providerSuite a second time, won't it?

https://codereview.appspot.com/10734043/

Revision history for this message
Tim Penhey (thumper) wrote :
Download full text (4.1 KiB)

Please take a look.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go
File environs/local/environ-provider.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode30
environs/local/environ-provider.go:30: defaultPrivateStorageDir =
"/var/lib/juju/local/%s/private"
On 2013/06/28 06:22:57, wallyworld wrote:
> Perhaps a stupid question - do we really want to make these in a
location where
> the user needs privileges or access granted? Why not somewhere in
their home
> dir? That would also allow many users on the one machine to run juju
with a
> local provider without interfering with each other/

Interesting point. But there is a bigger problem here. lxc containers
(at least in precise now) require the containers to be in /var/lib/lxc.
This means that while also namespacing the storage area, we should also
make sure that we namespace the containers with the user id.

I really didn't consider multiple users using a local provider on one
machine as this was a little outside the scope of the local provider.
However we should probably support it.

With respect to the privileged location, this isn't an issue as the user
will need to 'sudo juju bootstrap' the local provider as we need to do
stuff with lxc containers, which requires root.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode45
environs/local/environ-provider.go:45: func ensureDirExists(path string)
error {
On 2013/06/28 14:43:50, fwereade wrote:
> On 2013/06/28 06:22:57, wallyworld wrote:
> > Just curious - do we already have this function somewhere? If not,
could we
> > consider putting it in a utils package or something. I'm a bit
perplexed the
> std
> > libs don't have this built in.

> os.MkdirAll

Hmm... will just simplify to use os.MkdirAll.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode91
environs/local/environ-provider.go:91: }
On 2013/06/28 14:43:50, fwereade wrote:
> We need to check the storage dirs don't change, I think.

OK

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go
File environs/local/environ-provider_test.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode61
environs/local/environ-provider_test.go:61: func (s *providerSuite)
TestValidateConfig(c *C) {
On 2013/06/28 06:22:57, wallyworld wrote:
> Could we follow the other providers and put config tests in a
config_test.go
> file?

OK.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go
File environs/local/environ.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ.go#newcode43
environs/local/environ.go:43: func (env *localEnviron) Config()
*config.Config {
On 2013/06/28 06:22:57, wallyworld wrote:
> Other providers use an intermediate ecfg() method. I'm not sure why.
Perhaps we
> could find out if that approach is for a good reason and adopt it if
required
> here too?

I may do this if we decide that it is necessary. I'm not convinced that
it is necessary yet though.

https://codereview.appspot.com/10734043/dif...

Read more...

Revision history for this message
Ian Booth (wallyworld) wrote :

LGTM. I think I'd prefer the config tests to use a similar structure as
those for other providers wrt testing default value handling etc but
won't block on that.

https://codereview.appspot.com/10734043/

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

LGTM

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go
File environs/local/environ-provider.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode30
environs/local/environ-provider.go:30: defaultPrivateStorageDir =
"/var/lib/juju/local/%s/private"

> With respect to the privileged location, this isn't an issue as the
user will
> need to 'sudo juju bootstrap' the local provider as we need to do
stuff with lxc
> containers, which requires root.

Since you need sudo access, I think just badging the instances with the
environment gets you enough. You don't really need to also add the
username.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode106
environs/local/environ-provider.go:106: `[1:]
You don't have to add them here, but wouldn't you want to document the 2
fields that you just added (private-storage and public-storage).

Also, I think you don't support 'default-series' yet, so it doesn't make
sense to have it commented out, since people would think they could just
uncomment it and change it. (Though that is probably where you are
heading.)

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go
File environs/local/environ-provider_test.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode2
environs/local/environ-provider_test.go:2: // Licensed under the AGPLv3,
see LICENCE file for details.
I'm a bit surprised the file is named 'environ-provider_test.go'. (It
isn't valid as a go identifier, I believe, though only the package name
must match)

I don't think we have any other filenames with '-' in them, though.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode15
environs/local/environ-provider_test.go:15: "launchpad.net/loggo"
I've been following more of a:

stdlib

3rd-party-libs

juju-core libs

For imports. Does that make sense, or is it splitting them up too much?

It would look like:
import (
   "fmt"
   "path/filepath"

   . "launchpad.net/gocheck"
   "launchpad.net/loggo"

   "launchpad.net/juju-core/environs/config"
   "launchpad.net/juju-core/environs/local"
   "launchpad.net/juju-core/testing"
   . "launchpad.net/juju-core/testing/checkers"
)

https://codereview.appspot.com/10734043/

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

On 2013/07/01 02:45:43, wallyworld wrote:
> LGTM. I think I'd prefer the config tests to use a similar structure
as those
> for other providers wrt testing default value handling etc but won't
block on
> that.

Actually I did try that, but there is too much state to deal with in the
tests
for this to be useful.

https://codereview.appspot.com/10734043/

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

On 2013/07/01 10:34:04, jameinel wrote:
> LGTM

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go
> File environs/local/environ-provider.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode30
> environs/local/environ-provider.go:30: defaultPrivateStorageDir =
> "/var/lib/juju/local/%s/private"

> >
> > With respect to the privileged location, this isn't an issue as the
user will
> > need to 'sudo juju bootstrap' the local provider as we need to do
stuff with
> lxc
> > containers, which requires root.

> Since you need sudo access, I think just badging the instances with
the
> environment gets you enough. You don't really need to also add the
username.

Not if multiple people use the default environment name called "local".

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider.go#newcode106
> environs/local/environ-provider.go:106: `[1:]
> You don't have to add them here, but wouldn't you want to document the
2 fields
> that you just added (private-storage and public-storage).

I did in patch 2.

> Also, I think you don't support 'default-series' yet, so it doesn't
make sense
> to have it commented out, since people would think they could just
uncomment it
> and change it. (Though that is probably where you are heading.)

Removed it.

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go
> File environs/local/environ-provider_test.go (right):

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode2
> environs/local/environ-provider_test.go:2: // Licensed under the
AGPLv3, see
> LICENCE file for details.
> I'm a bit surprised the file is named 'environ-provider_test.go'. (It
isn't
> valid as a go identifier, I believe, though only the package name must
match)

> I don't think we have any other filenames with '-' in them, though.

We do, but I may have added them too. I've renamed it
environprovider.go and environprovider_test.go

https://codereview.appspot.com/10734043/diff/1/environs/local/environ-provider_test.go#newcode15
> environs/local/environ-provider_test.go:15: "launchpad.net/loggo"
> I've been following more of a:

> stdlib

> 3rd-party-libs

> juju-core libs

> For imports. Does that make sense, or is it splitting them up too
much?

> It would look like:
> import (
> "fmt"
> "path/filepath"

> . "launchpad.net/gocheck"
> "launchpad.net/loggo"

> "launchpad.net/juju-core/environs/config"
> "launchpad.net/juju-core/environs/local"
> "launchpad.net/juju-core/testing"
> . "launchpad.net/juju-core/testing/checkers"
> )

Actually I like that even more, and fixed the code to follow this.

https://codereview.appspot.com/10734043/

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

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

...

>
>> Since you need sudo access, I think just badging the instances
>> with
> the
>> environment gets you enough. You don't really need to also add
>> the
> username.
>
> Not if multiple people use the default environment name called
> "local".
>

That is true, but it is also true if you use shared credentials on
Openstack or EC2 or anywhere else.

I think if people want to share a local system they can use different
names for their environments. (A lot of people run the user 'ubuntu'
anyway, right?)

We *can* add user, I just don't think it actually adds much, and it
isn't something we do anywhere else.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (Cygwin)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlHSkCIACgkQJdeBCYSNAAOTfgCfchBlt/YjayfD0DSQnu1S0SGx
UBIAoIebSwSIn7Vtfo1+Feu4n8u6vtG+
=NnXm
-----END PGP SIGNATURE-----

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'environs/local/config.go'
2--- environs/local/config.go 1970-01-01 00:00:00 +0000
3+++ environs/local/config.go 2013-07-02 04:37:23 +0000
4@@ -0,0 +1,53 @@
5+// Copyright 2013 Canonical Ltd.
6+// Licensed under the AGPLv3, see LICENCE file for details.
7+
8+package local
9+
10+import (
11+ "fmt"
12+ "os"
13+
14+ "launchpad.net/juju-core/environs/config"
15+ "launchpad.net/juju-core/schema"
16+)
17+
18+var configChecker = schema.StrictFieldMap(
19+ schema.Fields{
20+ "shared-storage": schema.String(),
21+ "storage": schema.String(),
22+ },
23+ schema.Defaults{
24+ "shared-storage": "",
25+ "storage": "",
26+ },
27+)
28+
29+type environConfig struct {
30+ *config.Config
31+ user string
32+ attrs map[string]interface{}
33+}
34+
35+func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {
36+ user := os.Getenv("USER")
37+ return &environConfig{
38+ Config: config,
39+ user: user,
40+ attrs: attrs,
41+ }
42+}
43+
44+// Since it is technically possible for two different users on one machine to
45+// have the same local provider name, we need to have a simple way to
46+// namespace the file locations, but more importantly the lxc containers.
47+func (c *environConfig) namespace() string {
48+ return fmt.Sprintf("%s-%s", c.user, c.Name())
49+}
50+
51+func (c *environConfig) publicStorageDir() string {
52+ return c.attrs["shared-storage"].(string)
53+}
54+
55+func (c *environConfig) privateStorageDir() string {
56+ return c.attrs["storage"].(string)
57+}
58
59=== added file 'environs/local/config_test.go'
60--- environs/local/config_test.go 1970-01-01 00:00:00 +0000
61+++ environs/local/config_test.go 2013-07-02 04:37:23 +0000
62@@ -0,0 +1,89 @@
63+// Copyright 2013 Canonical Ltd.
64+// Licensed under the AGPLv3, see LICENCE file for details.
65+
66+package local_test
67+
68+import (
69+ "fmt"
70+ "os"
71+ "path/filepath"
72+
73+ gc "launchpad.net/gocheck"
74+ "launchpad.net/juju-core/environs/config"
75+ "launchpad.net/juju-core/environs/local"
76+ "launchpad.net/juju-core/testing"
77+ jc "launchpad.net/juju-core/testing/checkers"
78+)
79+
80+type configSuite struct {
81+ baseProviderSuite
82+ oldUser string
83+}
84+
85+var _ = gc.Suite(&configSuite{})
86+
87+func (s *configSuite) SetUpTest(c *gc.C) {
88+ s.baseProviderSuite.SetUpTest(c)
89+ s.oldUser = os.Getenv("USER")
90+ err := os.Setenv("USER", "tester")
91+ c.Assert(err, gc.IsNil)
92+ c.Assert(err, gc.IsNil)
93+}
94+
95+func (s *configSuite) TearDownTest(c *gc.C) {
96+ os.Setenv("USER", s.oldUser)
97+ s.baseProviderSuite.TearDownTest(c)
98+}
99+
100+func minimalConfigValues() map[string]interface{} {
101+ return map[string]interface{}{
102+ "name": "test",
103+ "type": "local",
104+ // While the ca-cert bits aren't entirely minimal, they avoid the need
105+ // to set up a fake home.
106+ "ca-cert": testing.CACert,
107+ "ca-private-key": testing.CAKey,
108+ }
109+}
110+
111+func minimalConfig(c *gc.C) *config.Config {
112+ minimal := minimalConfigValues()
113+ testConfig, err := config.New(minimal)
114+ c.Assert(err, gc.IsNil)
115+ return testConfig
116+}
117+
118+func (s *configSuite) TestValidateConfig(c *gc.C) {
119+ testConfig := minimalConfig(c)
120+
121+ valid, err := local.Provider.Validate(testConfig, nil)
122+ c.Assert(err, gc.IsNil)
123+ unknownAttrs := valid.UnknownAttrs()
124+
125+ publicDir := fmt.Sprintf(s.public, "tester-test")
126+ c.Assert(publicDir, jc.IsDirectory)
127+ c.Assert(unknownAttrs["shared-storage"], gc.Equals, publicDir)
128+
129+ privateDir := fmt.Sprintf(s.private, "tester-test")
130+ c.Assert(privateDir, jc.IsDirectory)
131+ c.Assert(unknownAttrs["storage"], gc.Equals, privateDir)
132+}
133+
134+func (s *configSuite) TestValidateConfigWithStorage(c *gc.C) {
135+ values := minimalConfigValues()
136+ public := filepath.Join(c.MkDir(), "public", "storage")
137+ private := filepath.Join(c.MkDir(), "private", "storage")
138+ values["shared-storage"] = public
139+ values["storage"] = private
140+ testConfig, err := config.New(values)
141+ c.Assert(err, gc.IsNil)
142+
143+ valid, err := local.Provider.Validate(testConfig, nil)
144+ c.Assert(err, gc.IsNil)
145+ unknownAttrs := valid.UnknownAttrs()
146+
147+ c.Assert(public, jc.IsDirectory)
148+ c.Assert(unknownAttrs["shared-storage"], gc.Equals, public)
149+ c.Assert(private, jc.IsDirectory)
150+ c.Assert(unknownAttrs["storage"], gc.Equals, private)
151+}
152
153=== modified file 'environs/local/environ.go'
154--- environs/local/environ.go 2013-06-28 02:35:59 +0000
155+++ environs/local/environ.go 2013-07-02 04:37:23 +0000
156@@ -5,6 +5,7 @@
157
158 import (
159 "fmt"
160+ "sync"
161
162 "launchpad.net/juju-core/constraints"
163 "launchpad.net/juju-core/environs"
164@@ -18,7 +19,9 @@
165 var _ environs.Environ = (*localEnviron)(nil)
166
167 type localEnviron struct {
168- name string
169+ localMutex sync.Mutex
170+ config *environConfig
171+ name string
172 }
173
174 // Name is specified in the Environ interface.
175@@ -38,12 +41,22 @@
176
177 // Config is specified in the Environ interface.
178 func (env *localEnviron) Config() *config.Config {
179- panic("unimplemented")
180+ env.localMutex.Lock()
181+ defer env.localMutex.Unlock()
182+ return env.config.Config
183 }
184
185 // SetConfig is specified in the Environ interface.
186 func (env *localEnviron) SetConfig(cfg *config.Config) error {
187- return fmt.Errorf("not implemented")
188+ config, err := provider.newConfig(cfg)
189+ if err != nil {
190+ logger.Errorf("failed to create new environ config: %v", err)
191+ return err
192+ }
193+ env.localMutex.Lock()
194+ defer env.localMutex.Unlock()
195+ env.config = config
196+ return nil
197 }
198
199 // StartInstance is specified in the Environ interface.
200
201=== added file 'environs/local/environ_test.go'
202--- environs/local/environ_test.go 1970-01-01 00:00:00 +0000
203+++ environs/local/environ_test.go 2013-07-02 04:37:23 +0000
204@@ -0,0 +1,25 @@
205+// Copyright 2013 Canonical Ltd.
206+// Licensed under the AGPLv3, see LICENCE file for details.
207+
208+package local_test
209+
210+import (
211+ gc "launchpad.net/gocheck"
212+
213+ "launchpad.net/juju-core/environs/local"
214+)
215+
216+type environSuite struct {
217+ baseProviderSuite
218+}
219+
220+var _ = gc.Suite(&environSuite{})
221+
222+func (*environSuite) TestName(c *gc.C) {
223+ testConfig := minimalConfig(c)
224+
225+ environ, err := local.Provider.Open(testConfig)
226+ c.Assert(err, gc.IsNil)
227+
228+ c.Assert(environ.Name(), gc.Equals, "test")
229+}
230
231=== renamed file 'environs/local/environ-provider.go' => 'environs/local/environprovider.go'
232--- environs/local/environ-provider.go 2013-06-28 01:58:45 +0000
233+++ environs/local/environprovider.go 2013-07-02 04:37:23 +0000
234@@ -5,11 +5,14 @@
235
236 import (
237 "fmt"
238+ "os"
239+
240+ "launchpad.net/loggo"
241
242 "launchpad.net/juju-core/environs"
243 "launchpad.net/juju-core/environs/config"
244 "launchpad.net/juju-core/instance"
245- "launchpad.net/loggo"
246+ "launchpad.net/juju-core/utils"
247 )
248
249 var logger = loggo.GetLogger("juju.environs.local")
250@@ -24,19 +27,92 @@
251 environs.RegisterProvider("local", &environProvider{})
252 }
253
254+var (
255+ defaultPublicStorageDir = "/var/lib/juju/local/%s/public"
256+ defaultPrivateStorageDir = "/var/lib/juju/local/%s/private"
257+)
258+
259 // Open implements environs.EnvironProvider.Open.
260 func (environProvider) Open(cfg *config.Config) (environs.Environ, error) {
261- return nil, fmt.Errorf("not implemented")
262+ logger.Infof("opening environment %q", cfg.Name())
263+ environ := &localEnviron{name: cfg.Name()}
264+ err := environ.SetConfig(cfg)
265+ if err != nil {
266+ logger.Errorf("failure setting config: %v", err)
267+ return nil, err
268+ }
269+ return environ, nil
270 }
271
272 // Validate implements environs.EnvironProvider.Validate.
273-func (environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
274- return nil, fmt.Errorf("not implemented")
275+func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
276+ // Check for valid changes for the base config values.
277+ if err := config.Validate(cfg, old); err != nil {
278+ return nil, err
279+ }
280+ v, err := configChecker.Coerce(cfg.UnknownAttrs(), nil)
281+ if err != nil {
282+ return nil, err
283+ }
284+ localConfig := newEnvironConfig(cfg, v.(map[string]interface{}))
285+ // Before potentially creating directories, make sure that the
286+ // public storage and private storage values have not changed.
287+ if old != nil {
288+ oldLocalConfig, err := provider.newConfig(old)
289+ if err != nil {
290+ return nil, fmt.Errorf("old config is not a valid local config: %v", old)
291+ }
292+ if localConfig.publicStorageDir() != oldLocalConfig.publicStorageDir() {
293+ return nil, fmt.Errorf("cannot change shared-storage from %q to %q",
294+ oldLocalConfig.publicStorageDir(),
295+ localConfig.publicStorageDir())
296+ }
297+ if localConfig.privateStorageDir() != oldLocalConfig.privateStorageDir() {
298+ return nil, fmt.Errorf("cannot change storage from %q to %q",
299+ oldLocalConfig.privateStorageDir(),
300+ localConfig.privateStorageDir())
301+ }
302+ }
303+ dir := utils.NormalizePath(localConfig.publicStorageDir())
304+ if dir == "." {
305+ dir = fmt.Sprintf(defaultPublicStorageDir, localConfig.namespace())
306+ localConfig.attrs["shared-storage"] = dir
307+ }
308+ logger.Tracef("ensure shared-storage dir %s exists", dir)
309+ if err := os.MkdirAll(dir, 0755); err != nil {
310+ logger.Errorf("failed to make directory for shared storage at %s: %v", dir, err)
311+ return nil, err
312+ }
313+
314+ dir = utils.NormalizePath(localConfig.privateStorageDir())
315+ if dir == "." {
316+ dir = fmt.Sprintf(defaultPrivateStorageDir, localConfig.namespace())
317+ localConfig.attrs["storage"] = dir
318+ }
319+ logger.Tracef("ensure storage dir %s exists", dir)
320+ if err := os.MkdirAll(dir, 0755); err != nil {
321+ logger.Errorf("failed to make directory for storage at %s: %v", dir, err)
322+ return nil, err
323+ }
324+
325+ // Apply the coerced unknown values back into the config.
326+ return cfg.Apply(localConfig.attrs)
327 }
328
329 // BoilerplateConfig implements environs.EnvironProvider.BoilerplateConfig.
330 func (environProvider) BoilerplateConfig() string {
331- return "not implemented"
332+ return `
333+## https://juju.ubuntu.com/get-started/local/
334+local:
335+ type: local
336+ # Override the storage location to store the private files for this environment in the
337+ # specified location. The default location is /var/lib/juju/local/<USER>-<ENV>/private
338+ # storage: ~/.juju/local/private
339+ # Override the shared-storage location to store the public files for this environment in the
340+ # specified location. The default location is /var/lib/juju/local/<USER>-<ENV>/public
341+ # shared-storage: ~/.juju/local/public
342+
343+`[1:]
344 }
345
346 // SecretAttrs implements environs.EnvironProvider.SecretAttrs.
347@@ -63,3 +139,11 @@
348 func (environProvider) InstanceId() (instance.Id, error) {
349 return "", fmt.Errorf("not implemented")
350 }
351+
352+func (environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
353+ valid, err := provider.Validate(cfg, nil)
354+ if err != nil {
355+ return nil, err
356+ }
357+ return newEnvironConfig(valid, valid.UnknownAttrs()), nil
358+}
359
360=== added file 'environs/local/environprovider_test.go'
361--- environs/local/environprovider_test.go 1970-01-01 00:00:00 +0000
362+++ environs/local/environprovider_test.go 2013-07-02 04:37:23 +0000
363@@ -0,0 +1,35 @@
364+// Copyright 2013 Canonical Ltd.
365+// Licensed under the AGPLv3, see LICENCE file for details.
366+
367+package local_test
368+
369+import (
370+ "path/filepath"
371+
372+ gc "launchpad.net/gocheck"
373+ "launchpad.net/loggo"
374+
375+ "launchpad.net/juju-core/environs/local"
376+ "launchpad.net/juju-core/testing"
377+)
378+
379+type baseProviderSuite struct {
380+ testing.LoggingSuite
381+ public string
382+ private string
383+ oldPublic string
384+ oldPrivate string
385+}
386+
387+func (s *baseProviderSuite) SetUpTest(c *gc.C) {
388+ s.LoggingSuite.SetUpTest(c)
389+ loggo.GetLogger("juju.environs.local").SetLogLevel(loggo.TRACE)
390+ s.public = filepath.Join(c.MkDir(), "%s", "public")
391+ s.private = filepath.Join(c.MkDir(), "%s", "private")
392+ s.oldPublic, s.oldPrivate = local.SetDefaultStorageDirs(s.public, s.private)
393+}
394+
395+func (s *baseProviderSuite) TearDownTest(c *gc.C) {
396+ local.SetDefaultStorageDirs(s.oldPublic, s.oldPrivate)
397+ s.LoggingSuite.TearDownTest(c)
398+}
399
400=== modified file 'environs/local/export_test.go'
401--- environs/local/export_test.go 2013-06-30 22:58:37 +0000
402+++ environs/local/export_test.go 2013-07-02 04:37:23 +0000
403@@ -4,3 +4,9 @@
404 package local
405
406 var Provider = provider
407+
408+func SetDefaultStorageDirs(public, private string) (oldPublic, oldPrivate string) {
409+ oldPublic, defaultPublicStorageDir = defaultPublicStorageDir, public
410+ oldPrivate, defaultPrivateStorageDir = defaultPrivateStorageDir, private
411+ return
412+}
413
414=== modified file 'environs/local/local_test.go'
415--- environs/local/local_test.go 2013-06-30 22:58:37 +0000
416+++ environs/local/local_test.go 2013-07-02 04:37:23 +0000
417@@ -7,6 +7,7 @@
418 stdtesting "testing"
419
420 . "launchpad.net/gocheck"
421+
422 "launchpad.net/juju-core/environs"
423 "launchpad.net/juju-core/environs/local"
424 "launchpad.net/juju-core/testing"
425
426=== added file 'utils/file.go'
427--- utils/file.go 1970-01-01 00:00:00 +0000
428+++ utils/file.go 2013-07-02 04:37:23 +0000
429@@ -0,0 +1,19 @@
430+// Copyright 2013 Canonical Ltd.
431+// Licensed under the AGPLv3, see LICENCE file for details.
432+
433+package utils
434+
435+import (
436+ "os"
437+ "path/filepath"
438+ "strings"
439+)
440+
441+// NormalizePath replaces a leading ~ with $HOME, and removes any .. or . path
442+// elements.
443+func NormalizePath(dir string) string {
444+ if strings.HasPrefix(dir, "~/") {
445+ dir = filepath.Join(os.Getenv("HOME"), dir[2:])
446+ }
447+ return filepath.Clean(dir)
448+}
449
450=== added file 'utils/file_test.go'
451--- utils/file_test.go 1970-01-01 00:00:00 +0000
452+++ utils/file_test.go 2013-07-02 04:37:23 +0000
453@@ -0,0 +1,49 @@
454+// Copyright 2013 Canonical Ltd.
455+// Licensed under the AGPLv3, see LICENCE file for details.
456+
457+package utils_test
458+
459+import (
460+ "os"
461+
462+ gc "launchpad.net/gocheck"
463+ "launchpad.net/juju-core/utils"
464+)
465+
466+type fileSuite struct {
467+ oldHome string
468+}
469+
470+var _ = gc.Suite(&fileSuite{})
471+
472+func (s *fileSuite) SetUpTest(c *gc.C) {
473+ s.oldHome = os.Getenv("HOME")
474+ err := os.Setenv("HOME", "/home/test-user")
475+ c.Assert(err, gc.IsNil)
476+}
477+
478+func (s *fileSuite) TearDownTest(c *gc.C) {
479+ err := os.Setenv("HOME", s.oldHome)
480+ c.Assert(err, gc.IsNil)
481+}
482+
483+func (*fileSuite) TestNormalizePath(c *gc.C) {
484+ for _, test := range []struct {
485+ path string
486+ expected string
487+ }{{
488+ path: "/var/lib/juju",
489+ expected: "/var/lib/juju",
490+ }, {
491+ path: "~/foo",
492+ expected: "/home/test-user/foo",
493+ }, {
494+ path: "~/foo//../bar",
495+ expected: "/home/test-user/bar",
496+ }, {
497+ path: "~bob/foo",
498+ expected: "~bob/foo",
499+ }} {
500+ c.Assert(utils.NormalizePath(test.path), gc.Equals, test.expected)
501+ }
502+}

Subscribers

People subscribed via source and target branches

to status/vote changes: