Merge lp:~axwalk/juju-core/local-provider-testability 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: 2316
Proposed branch: lp:~axwalk/juju-core/local-provider-testability
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 498 lines (+100/-169)
9 files modified
provider/local/config.go (+5/-11)
provider/local/config_test.go (+5/-66)
provider/local/environ.go (+49/-69)
provider/local/environ_test.go (+5/-0)
provider/local/environprovider.go (+13/-2)
provider/local/environprovider_test.go (+4/-1)
provider/local/export_test.go (+7/-10)
provider/local/instance.go (+2/-2)
provider/local/local_test.go (+10/-8)
To merge this branch: bzr merge lp:~axwalk/juju-core/local-provider-testability
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+204169@code.launchpad.net

Commit message

provider/local: always use filestorage on CLI

The local provider is modified such that all
storage interactions from the CLI go directly
to disk, and the provider code no longer starts
an httpstorage listener. To achieve this, we
no longer store bootstrap-ip in the .jenv, but
instead add it to the environment config during
bootstrap. Thus, we can use the presence of the
attribute to decide whether or not to use the
file or http storage. BootstrapStorager no longer
needs to be implemented, so EnableBootsrapStorage
has been eliminated from the implementation.

For old environments, bootstrap-ip will be set
in the .jenv and thus httpstorage is used;
this is fine, as long as the machine agent is
running. There's an edge case that will no longer
work, where the environment is bootstrapped but
machine-0 agent is not running. In this case,
storage is inaccessible.

There are some other drive-by fixes to tests
that improve isolation, and some redundant tests
relating to the old sudo behaviour are removed.
All tests now pass with or without a local provider
running on the machine.

Fixes lp:1271672

https://codereview.appspot.com/49640050/

Description of the change

provider/local: always use filestorage on CLI

The local provider is modified such that all
storage interactions from the CLI go directly
to disk, and the provider code no longer starts
an httpstorage listener. To achieve this, we
no longer store bootstrap-ip in the .jenv, but
instead add it to the environment config during
bootstrap. Thus, we can use the presence of the
attribute to decide whether or not to use the
file or http storage. BootstrapStorager no longer
needs to be implemented, so EnableBootsrapStorage
has been eliminated from the implementation.

For old environments, bootstrap-ip will be set
in the .jenv and thus httpstorage is used;
this is fine, as long as the machine agent is
running. There's an edge case that will no longer
work, where the environment is bootstrapped but
machine-0 agent is not running. In this case,
storage is inaccessible.

There are some other drive-by fixes to tests
that improve isolation, and some redundant tests
relating to the old sudo behaviour are removed.
All tests now pass with or without a local provider
running on the machine.

Fixes lp:1271672

https://codereview.appspot.com/49640050/

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

Reviewers: mp+204169_code.launchpad.net,

Message:
Please take a look.

Description:
provider/local: always use filestorage on CLI

The local provider is modified such that all
storage interactions from the CLI go directly
to disk, and the provider code no longer starts
an httpstorage listener. To achieve this, we
no longer store bootstrap-ip in the .jenv, but
instead add it to the environment config during
bootstrap. Thus, we can use the presence of the
attribute to decide whether or not to use the
file or http storage. BootstrapStorager no longer
needs to be implemented, so EnableBootsrapStorage
has been eliminated from the implementation.

For old environments, bootstrap-ip will be set
in the .jenv and thus httpstorage is used;
this is fine, as long as the machine agent is
running. There's an edge case that will no longer
work, where the environment is bootstrapped but
machine-0 agent is not running. In this case,
storage is inaccessible.

There are some other drive-by fixes to tests
that improve isolation, and some redundant tests
relating to the old sudo behaviour are removed.
All tests now pass with or without a local provider
running on the machine.

Fixes lp:1271672

https://code.launchpad.net/~axwalk/juju-core/local-provider-testability/+merge/204169

(do not edit description out of merge proposal)

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

Affected files (+95, -169 lines):
   A [revision details]
   M provider/local/config.go
   M provider/local/config_test.go
   M provider/local/environ.go
   M provider/local/environ_test.go
   M provider/local/environprovider.go
   M provider/local/environprovider_test.go
   M provider/local/export_test.go
   M provider/local/instance.go
   M provider/local/local_test.go

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM although I'd like a second opinion from someone that's more
familiar with local provider issues.

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

https://codereview.appspot.com/49640050/diff/1/provider/local/environ.go#newcode235
provider/local/environ.go:235: // continue on to set up things prior to
bootstrap.
This comment doesn't read easily to me.

How about something like this:

// When the localEnviron value is created on the client
// side, the bootstrap-ip attribute will not exist,
// because it is only set *within* the running
// environment, not in the configuration created
// by Prepare.
// Thus when bootstrapIPAddress returns a non-empty
// string, we know we are running server-side and
// thus must use the network API to access the storage.

?

https://codereview.appspot.com/49640050/diff/1/provider/local/environprovider_test.go
File provider/local/environprovider_test.go (left):

https://codereview.appspot.com/49640050/diff/1/provider/local/environprovider_test.go#oldcode59
provider/local/environprovider_test.go:59: c.Skip("fails if local
provider running already")
I presume you've checked that this doesn't still
fail if a local environment is currently running.

https://codereview.appspot.com/49640050/diff/1/provider/local/export_test.go
File provider/local/export_test.go (right):

https://codereview.appspot.com/49640050/diff/1/provider/local/export_test.go#newcode54
provider/local/export_test.go:54: func MockAddressForInterface() func()
{
This might be easier just as:

PatchValue(local.GetAddressForInterface, func(name string) (string,
error) etc)

but perhaps that's a change for a future CL.

https://codereview.appspot.com/49640050/

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

Please take a look.

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

https://codereview.appspot.com/49640050/diff/1/provider/local/environ.go#newcode235
provider/local/environ.go:235: // continue on to set up things prior to
bootstrap.
On 2014/02/03 09:37:40, rog wrote:
> This comment doesn't read easily to me.

> How about something like this:

> // When the localEnviron value is created on the client
> // side, the bootstrap-ip attribute will not exist,
> // because it is only set *within* the running
> // environment, not in the configuration created
> // by Prepare.
> // Thus when bootstrapIPAddress returns a non-empty
> // string, we know we are running server-side and
> // thus must use the network API to access the storage.

> ?

Done.

https://codereview.appspot.com/49640050/diff/1/provider/local/environprovider_test.go
File provider/local/environprovider_test.go (left):

https://codereview.appspot.com/49640050/diff/1/provider/local/environprovider_test.go#oldcode59
provider/local/environprovider_test.go:59: c.Skip("fails if local
provider running already")
On 2014/02/03 09:37:40, rog wrote:
> I presume you've checked that this doesn't still
> fail if a local environment is currently running.

Correct.

https://codereview.appspot.com/49640050/diff/1/provider/local/export_test.go
File provider/local/export_test.go (right):

https://codereview.appspot.com/49640050/diff/1/provider/local/export_test.go#newcode54
provider/local/export_test.go:54: func MockAddressForInterface() func()
{
On 2014/02/03 09:37:40, rog wrote:
> This might be easier just as:

> PatchValue(local.GetAddressForInterface, func(name string) (string,
error) etc)

> but perhaps that's a change for a future CL.

I've changed it to use PatchValue here. Another CL in the future can
move that up the stack if it's deemed desirable.

https://codereview.appspot.com/49640050/

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

LGTM

https://codereview.appspot.com/49640050/diff/20001/provider/local/environprovider_test.go
File provider/local/environprovider_test.go (left):

https://codereview.appspot.com/49640050/diff/20001/provider/local/environprovider_test.go#oldcode59
provider/local/environprovider_test.go:59: c.Skip("fails if local
provider running already")
\o/

https://codereview.appspot.com/49640050/

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (9.4 KiB)

The attempt to merge lp:~axwalk/juju-core/local-provider-testability into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core 0.016s
ok launchpad.net/juju-core/agent 1.186s
ok launchpad.net/juju-core/agent/tools 0.230s
ok launchpad.net/juju-core/bzr 6.706s
ok launchpad.net/juju-core/cert 3.436s
ok launchpad.net/juju-core/charm 0.610s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.037s
ok launchpad.net/juju-core/cloudinit/sshinit 1.205s
ok launchpad.net/juju-core/cmd 0.247s
ok launchpad.net/juju-core/cmd/charm-admin 0.845s
? 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 228.578s
ok launchpad.net/juju-core/cmd/jujud 60.449s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 12.696s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/constraints 0.038s
ok launchpad.net/juju-core/container 0.053s
ok launchpad.net/juju-core/container/factory 0.055s
ok launchpad.net/juju-core/container/kvm 0.272s
ok launchpad.net/juju-core/container/kvm/mock 0.048s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 0.250s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.268s
ok launchpad.net/juju-core/environs 3.155s
ok launchpad.net/juju-core/environs/bootstrap 4.535s
ok launchpad.net/juju-core/environs/cloudinit 0.615s
ok launchpad.net/juju-core/environs/config 3.416s
ok launchpad.net/juju-core/environs/configstore 0.039s
ok launchpad.net/juju-core/environs/filestorage 0.031s
ok launchpad.net/juju-core/environs/httpstorage 0.968s
ok launchpad.net/juju-core/environs/imagemetadata 0.655s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.059s
ok launchpad.net/juju-core/environs/jujutest 0.245s
ok launchpad.net/juju-core/environs/manual 9.377s
ok launchpad.net/juju-core/environs/simplestreams 0.359s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 1.249s
ok launchpad.net/juju-core/environs/storage 1.233s
ok launchpad.net/juju-core/environs/sync 33.641s
ok launchpad.net/juju-core/environs/testing 0.201s
ok launchpad.net/juju-core/environs/tools 6.914s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.015s
ok launchpad.net/juju-core/instance 0.023s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 22.728s
ok launchpad.net/juju-core/juju/osenv 0.019s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.014s
ok launchpad.net/juju-core/log/syslog 0.022s
ok launchpad.net/juju-co...

Read more...

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

I think I've identified the problem in replicaset_test.go, will propose a fix later.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'provider/local/config.go'
2--- provider/local/config.go 2014-02-04 19:12:08 +0000
3+++ provider/local/config.go 2014-02-12 02:56:43 +0000
4@@ -83,18 +83,12 @@
5 return filepath.Join(c.rootDir(), "log")
6 }
7
8-// A config is bootstrapped if the bootstrap-ip address has been set.
9-func (c *environConfig) bootstrapped() bool {
10- _, found := c.attrs["bootstrap-ip"]
11- return found
12-}
13-
14+// bootstrapIPAddress returns the IP address of the bootstrap machine.
15+// As of 1.18 this is only set inside the environment, and not in the
16+// .jenv file.
17 func (c *environConfig) bootstrapIPAddress() string {
18- addr, found := c.attrs["bootstrap-ip"]
19- if found {
20- return addr.(string)
21- }
22- return ""
23+ addr, _ := c.attrs["bootstrap-ip"].(string)
24+ return addr
25 }
26
27 func (c *environConfig) storagePort() int {
28
29=== modified file 'provider/local/config_test.go'
30--- provider/local/config_test.go 2014-01-23 05:21:21 +0000
31+++ provider/local/config_test.go 2014-02-12 02:56:43 +0000
32@@ -4,14 +4,13 @@
33 package local_test
34
35 import (
36- "os"
37 "path/filepath"
38- "runtime"
39- "syscall"
40
41 gc "launchpad.net/gocheck"
42
43+ "launchpad.net/juju-core/constraints"
44 "launchpad.net/juju-core/environs/config"
45+ envtesting "launchpad.net/juju-core/environs/testing"
46 "launchpad.net/juju-core/juju/osenv"
47 "launchpad.net/juju-core/provider"
48 "launchpad.net/juju-core/provider/local"
49@@ -24,10 +23,6 @@
50
51 var _ = gc.Suite(&configSuite{})
52
53-func (s *configSuite) SetUpTest(c *gc.C) {
54- s.baseProviderSuite.SetUpTest(c)
55-}
56-
57 func minimalConfigValues() map[string]interface{} {
58 return testing.FakeConfig().Merge(testing.Attrs{
59 "name": "test",
60@@ -114,64 +109,8 @@
61 func (s *configSuite) TestBootstrapAsRoot(c *gc.C) {
62 restore := local.SetRootCheckFunction(func() bool { return true })
63 defer restore()
64- _, err := local.Provider.Prepare(minimalConfig(c))
65+ env, err := local.Provider.Prepare(minimalConfig(c))
66+ c.Assert(err, gc.IsNil)
67+ err = env.Bootstrap(envtesting.NewBootstrapContext(testing.Context(c)), constraints.Value{})
68 c.Assert(err, gc.ErrorMatches, "bootstrapping a local environment must not be done as root")
69 }
70-
71-type configRootSuite struct {
72- baseProviderSuite
73-}
74-
75-var _ = gc.Suite(&configRootSuite{})
76-
77-func (s *configRootSuite) SetUpTest(c *gc.C) {
78- s.baseProviderSuite.SetUpTest(c)
79- // Skip if not linux
80- if runtime.GOOS != "linux" {
81- c.Skip("not running linux")
82- }
83- // Skip if not running as root.
84- if os.Getuid() != 0 {
85- c.Skip("not running as root")
86- }
87-}
88-
89-func (s *configRootSuite) TestCreateDirsNoUserJustRoot(c *gc.C) {
90- defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
91- defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
92-
93- os.Setenv("SUDO_UID", "")
94- os.Setenv("SUDO_GID", "")
95-
96- testConfig := minimalConfig(c)
97- err := local.CreateDirs(c, testConfig)
98- c.Assert(err, gc.IsNil)
99- // Check that the dirs are owned by root.
100- for _, dir := range local.CheckDirs(c, testConfig) {
101- info, err := os.Stat(dir)
102- c.Assert(err, gc.IsNil)
103- // This call is linux specific, but then so is sudo
104- c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(0))
105- c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(0))
106- }
107-}
108-
109-func (s *configRootSuite) TestCreateDirsAsUser(c *gc.C) {
110- defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
111- defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
112-
113- os.Setenv("SUDO_UID", "1000")
114- os.Setenv("SUDO_GID", "1000")
115-
116- testConfig := minimalConfig(c)
117- err := local.CreateDirs(c, testConfig)
118- c.Assert(err, gc.IsNil)
119- // Check that the dirs are owned by the UID/GID set above..
120- for _, dir := range local.CheckDirs(c, testConfig) {
121- info, err := os.Stat(dir)
122- c.Assert(err, gc.IsNil)
123- // This call is linux specific, but then so is sudo
124- c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(1000))
125- c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(1000))
126- }
127-}
128
129=== modified file 'provider/local/environ.go'
130--- provider/local/environ.go 2014-02-05 08:01:22 +0000
131+++ provider/local/environ.go 2014-02-12 02:56:43 +0000
132@@ -51,6 +51,8 @@
133 localMutex sync.Mutex
134 config *environConfig
135 name string
136+ bridgeAddress string
137+ localStorage storage.Storage
138 storageListener net.Listener
139 containerManager container.Manager
140 }
141@@ -127,6 +129,18 @@
142 return err
143 }
144
145+ // Record the bootstrap IP, so the containers know where to go for storage.
146+ cfg, err := env.Config().Apply(map[string]interface{}{
147+ "bootstrap-ip": env.bridgeAddress,
148+ })
149+ if err == nil {
150+ err = env.SetConfig(cfg)
151+ }
152+ if err != nil {
153+ logger.Errorf("failed to apply bootstrap-ip to config: %v", err)
154+ return err
155+ }
156+
157 bootstrapJobs, err := agent.MarshalBootstrapJobs(state.JobManageEnviron)
158 if err != nil {
159 return err
160@@ -147,7 +161,7 @@
161 agent.StorageAddr: env.config.storageAddr(),
162 agent.BootstrapJobs: bootstrapJobs,
163 }
164- if err := environs.FinishMachineConfig(mcfg, env.Config(), cons); err != nil {
165+ if err := environs.FinishMachineConfig(mcfg, cfg, cons); err != nil {
166 return err
167 }
168 // don't write proxy settings for local machine
169@@ -197,22 +211,6 @@
170 return env.config.Config
171 }
172
173-func createLocalStorageListener(dir, address string) (net.Listener, error) {
174- info, err := os.Stat(dir)
175- if os.IsNotExist(err) {
176- return nil, fmt.Errorf("storage directory %q does not exist, bootstrap first", dir)
177- } else if err != nil {
178- return nil, err
179- } else if !info.Mode().IsDir() {
180- return nil, fmt.Errorf("%q exists but is not a directory (and it needs to be)", dir)
181- }
182- storage, err := filestorage.NewFileStorageWriter(dir, filestorage.UseDefaultTmpDir)
183- if err != nil {
184- return nil, err
185- }
186- return httpstorage.Serve(address, storage)
187-}
188-
189 // SetConfig is specified in the Environ interface.
190 func (env *localEnviron) SetConfig(cfg *config.Config) error {
191 ecfg, err := providerInstance.newConfig(cfg)
192@@ -235,19 +233,19 @@
193 return err
194 }
195
196- // Here is the end of normal config setting.
197- if ecfg.bootstrapped() {
198+ // When the localEnviron value is created on the client
199+ // side, the bootstrap-ip attribute will not exist,
200+ // because it is only set *within* the running
201+ // environment, not in the configuration created by
202+ // Prepare.
203+ //
204+ // When bootstrapIPAddress returns a non-empty string,
205+ // we know we are running server-side and thus must use
206+ // httpstorage.
207+ if addr := ecfg.bootstrapIPAddress(); addr != "" {
208+ env.bridgeAddress = addr
209 return nil
210 }
211- if err := ensureNotRoot(); err != nil {
212- return err
213- }
214- return env.bootstrapAddressAndStorage(cfg)
215-}
216-
217-// bootstrapAddressAndStorage finishes up the setup of the environment in
218-// situations where there is no machine agent running yet.
219-func (env *localEnviron) bootstrapAddressAndStorage(cfg *config.Config) error {
220 // If we get to here, it is because we haven't yet bootstrapped an
221 // environment, and saved the config in it, or we are running a command
222 // from the command line, so it is ok to work on the assumption that we
223@@ -255,7 +253,16 @@
224 if err := env.config.createDirs(); err != nil {
225 return err
226 }
227+ // Record the network bridge address and create a filestorage.
228+ if err := env.resolveBridgeAddress(cfg); err != nil {
229+ return err
230+ }
231+ return env.setLocalStorage()
232+}
233
234+// resolveBridgeAddress finishes up the setup of the environment in
235+// situations where there is no machine agent running yet.
236+func (env *localEnviron) resolveBridgeAddress(cfg *config.Config) error {
237 // We need the provider config to get the network bridge.
238 config, err := providerInstance.newConfig(cfg)
239 if err != nil {
240@@ -266,48 +273,22 @@
241 bridgeAddress, err := getAddressForInterface(networkBridge)
242 if err != nil {
243 logger.Infof("configure a different bridge using 'network-bridge' in the config file")
244- return fmt.Errorf("cannot find address of network-bridge: %q", networkBridge)
245+ return fmt.Errorf("cannot find address of network-bridge: %q: %v", networkBridge, err)
246 }
247 logger.Debugf("found %q as address for %q", bridgeAddress, networkBridge)
248- cfg, err = cfg.Apply(map[string]interface{}{
249- "bootstrap-ip": bridgeAddress,
250- })
251- if err != nil {
252- logger.Errorf("failed to apply new addresses to config: %v", err)
253- return err
254- }
255- // Now recreate the config based on the settings with the bootstrap id.
256- config, err = providerInstance.newConfig(cfg)
257- if err != nil {
258- logger.Errorf("failed to create new environ config: %v", err)
259- return err
260- }
261- env.config = config
262-
263- return env.setupLocalStorage()
264+ env.bridgeAddress = bridgeAddress
265+ return nil
266 }
267
268-// setupLocalStorage looks to see if there is someone listening on the storage
269-// address port. If there is we assume that it is ours and all is good. If
270-// there is no one listening on that port, create listeners for storage
271-// for the duration of the commands execution.
272-func (env *localEnviron) setupLocalStorage() error {
273- // Try to listen to the storageAddress.
274- logger.Debugf("checking %s to see if machine agent running storage listener", env.config.storageAddr())
275- connection, err := net.Dial("tcp", env.config.storageAddr())
276+// setLocalStorage creates a filestorage so tools can
277+// be synced and so forth without having a machine agent
278+// running.
279+func (env *localEnviron) setLocalStorage() error {
280+ storage, err := filestorage.NewFileStorageWriter(env.config.storageDir(), filestorage.UseDefaultTmpDir)
281 if err != nil {
282- logger.Debugf("nope, start some")
283- // These listeners are part of the environment structure so as to remain
284- // referenced for the duration of the open environment. This is only for
285- // environs that have been created due to a user command.
286- env.storageListener, err = createLocalStorageListener(env.config.storageDir(), env.config.storageAddr())
287- if err != nil {
288- return err
289- }
290- } else {
291- logger.Debugf("yes, don't start local storage listeners")
292- connection.Close()
293+ return err
294 }
295+ env.localStorage = storage
296 return nil
297 }
298
299@@ -396,14 +377,13 @@
300
301 // Storage is specified in the Environ interface.
302 func (env *localEnviron) Storage() storage.Storage {
303+ // localStorage is non-nil if we're running from the CLI
304+ if env.localStorage != nil {
305+ return env.localStorage
306+ }
307 return httpstorage.Client(env.config.storageAddr())
308 }
309
310-// Implements environs.BootstrapStorager.
311-func (env *localEnviron) EnableBootstrapStorage() error {
312- return env.setupLocalStorage()
313-}
314-
315 // Destroy is specified in the Environ interface.
316 func (env *localEnviron) Destroy() error {
317 // Kill all running instances. This must be done as
318
319=== modified file 'provider/local/environ_test.go'
320--- provider/local/environ_test.go 2014-02-05 08:01:22 +0000
321+++ provider/local/environ_test.go 2014-02-12 02:56:43 +0000
322@@ -39,6 +39,11 @@
323 s.ToolsFixture.SetUpTest(c)
324 }
325
326+func (s *environSuite) TearDownTest(c *gc.C) {
327+ s.ToolsFixture.TearDownTest(c)
328+ s.baseProviderSuite.TearDownTest(c)
329+}
330+
331 func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) {
332 testConfig := minimalConfig(c)
333 testConfig, err := testConfig.Apply(map[string]interface{}{
334
335=== modified file 'provider/local/environprovider.go'
336--- provider/local/environprovider.go 2014-02-04 19:12:08 +0000
337+++ provider/local/environprovider.go 2014-02-12 02:56:43 +0000
338@@ -7,6 +7,7 @@
339 "fmt"
340 "net"
341 "os"
342+ "os/user"
343 "syscall"
344
345 "github.com/loggo/loggo"
346@@ -32,6 +33,8 @@
347 environs.RegisterProvider(provider.Local, providerInstance)
348 }
349
350+var userCurrent = user.Current
351+
352 // Open implements environs.EnvironProvider.Open.
353 func (environProvider) Open(cfg *config.Config) (environs.Environ, error) {
354 logger.Infof("opening environment %q", cfg.Name())
355@@ -72,8 +75,16 @@
356 return environ, nil
357 }
358
359+var detectAptProxies = utils.DetectAptProxies
360+
361 // Prepare implements environs.EnvironProvider.Prepare.
362 func (p environProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
363+ // The user must not set bootstrap-ip; this is determined by the provider,
364+ // and its presence used to determine whether the environment has yet been
365+ // bootstrapped.
366+ if _, ok := cfg.UnknownAttrs()["bootstrap-ip"]; ok {
367+ return nil, fmt.Errorf("bootstrap-ip must not be specified")
368+ }
369 err := checkLocalPort(cfg.StatePort(), "state port")
370 if err != nil {
371 return nil, err
372@@ -103,7 +114,7 @@
373 if cfg.AptHttpProxy() == "" &&
374 cfg.AptHttpsProxy() == "" &&
375 cfg.AptFtpProxy() == "" {
376- proxy, err := utils.DetectAptProxies()
377+ proxy, err := detectAptProxies()
378 if err != nil {
379 return nil, err
380 }
381@@ -122,7 +133,7 @@
382 }
383
384 // checkLocalPort checks that the passed port is not used so far.
385-func checkLocalPort(port int, description string) error {
386+var checkLocalPort = func(port int, description string) error {
387 logger.Infof("checking %s", description)
388 // Try to connect the port on localhost.
389 address := fmt.Sprintf("localhost:%d", port)
390
391=== modified file 'provider/local/environprovider_test.go'
392--- provider/local/environprovider_test.go 2014-01-29 04:00:15 +0000
393+++ provider/local/environprovider_test.go 2014-02-12 02:56:43 +0000
394@@ -53,10 +53,13 @@
395 s.PatchEnvironment("ftp_proxy", "")
396 s.PatchEnvironment("FTP_PROXY", "")
397 s.HookCommandOutput(&utils.AptCommandOutput, nil, nil)
398+ restore := testbase.PatchValue(local.CheckLocalPort, func(port int, desc string) error {
399+ return nil
400+ })
401+ s.AddSuiteCleanup(func(*gc.C) { restore() })
402 }
403
404 func (s *prepareSuite) TestPrepareCapturesEnvironment(c *gc.C) {
405- c.Skip("fails if local provider running already")
406 baseConfig, err := config.New(config.UseDefaults, map[string]interface{}{
407 "type": provider.Local,
408 "name": "test",
409
410=== modified file 'provider/local/export_test.go'
411--- provider/local/export_test.go 2014-02-04 19:12:08 +0000
412+++ provider/local/export_test.go 2014-02-12 02:56:43 +0000
413@@ -6,12 +6,14 @@
414 gc "launchpad.net/gocheck"
415
416 "launchpad.net/juju-core/environs/config"
417- "launchpad.net/juju-core/utils"
418+ "launchpad.net/juju-core/testing/testbase"
419 )
420
421 var (
422- Provider = providerInstance
423- FinishBootstrap = &finishBootstrap
424+ Provider = providerInstance
425+ FinishBootstrap = &finishBootstrap
426+ CheckLocalPort = &checkLocalPort
427+ DetectAptProxies = &detectAptProxies
428 )
429
430 // SetRootCheckFunction allows tests to override the check for a root user.
431@@ -50,13 +52,8 @@
432 // MockAddressForInterface replaces the getAddressForInterface with a function
433 // that returns a constant localhost ip address.
434 func MockAddressForInterface() func() {
435- getAddressForInterface = func(name string) (string, error) {
436+ return testbase.PatchValue(&getAddressForInterface, func(name string) (string, error) {
437 logger.Debugf("getAddressForInterface called for %s", name)
438 return "127.0.0.1", nil
439- }
440- return func() {
441- getAddressForInterface = utils.GetAddressForInterface
442- }
443+ })
444 }
445-
446-var CheckLocalPort = checkLocalPort
447
448=== modified file 'provider/local/instance.go'
449--- provider/local/instance.go 2014-01-23 05:21:21 +0000
450+++ provider/local/instance.go 2014-02-12 02:56:43 +0000
451@@ -41,7 +41,7 @@
452 }, {
453 NetworkScope: instance.NetworkCloudLocal,
454 Type: instance.Ipv4Address,
455- Value: inst.env.config.bootstrapIPAddress(),
456+ Value: inst.env.bridgeAddress,
457 }}
458 return addrs, nil
459 }
460@@ -51,7 +51,7 @@
461 // DNSName implements instance.Instance.DNSName.
462 func (inst *localInstance) DNSName() (string, error) {
463 if inst.id == bootstrapInstanceId {
464- return inst.env.config.bootstrapIPAddress(), nil
465+ return inst.env.bridgeAddress, nil
466 }
467 // Get the IPv4 address from eth0
468 return getAddressForInterface("eth0")
469
470=== modified file 'provider/local/local_test.go'
471--- provider/local/local_test.go 2013-10-14 14:47:52 +0000
472+++ provider/local/local_test.go 2014-02-12 02:56:43 +0000
473@@ -34,15 +34,17 @@
474 }
475
476 func (*localSuite) TestCheckLocalPort(c *gc.C) {
477- // Block a ports
478- addr := fmt.Sprintf(":%d", 65501)
479- ln, err := net.Listen("tcp", addr)
480+ // Listen on a random port.
481+ ln, err := net.Listen("tcp", ":0")
482 c.Assert(err, gc.IsNil)
483 defer ln.Close()
484-
485- err = local.CheckLocalPort(65501, "test port")
486- c.Assert(err, gc.ErrorMatches, "cannot use 65501 as test port, already in use")
487-
488- err = local.CheckLocalPort(65502, "another test port")
489+ port := ln.Addr().(*net.TCPAddr).Port
490+
491+ checkLocalPort := *local.CheckLocalPort
492+ err = checkLocalPort(port, "test port")
493+ c.Assert(err, gc.ErrorMatches, fmt.Sprintf("cannot use %d as test port, already in use", port))
494+
495+ ln.Close()
496+ err = checkLocalPort(port, "test port, no longer in use")
497 c.Assert(err, gc.IsNil)
498 }

Subscribers

People subscribed via source and target branches

to status/vote changes: