Merge lp:~gz/juju-core/tests_os_ssh_isolation_1144704 into lp:~juju/juju-core/trunk

Proposed by Martin Packman
Status: Merged
Merged at revision: 1063
Proposed branch: lp:~gz/juju-core/tests_os_ssh_isolation_1144704
Merge into: lp:~juju/juju-core/trunk
Diff against target: 364 lines (+89/-70)
8 files modified
environs/dummy/environs_test.go (+2/-2)
environs/ec2/live_test.go (+3/-2)
environs/ec2/local_test.go (+3/-3)
environs/jujutest/livetests.go (+4/-4)
environs/jujutest/tests.go (+23/-4)
environs/openstack/config_test.go (+6/-3)
environs/openstack/live_test.go (+26/-26)
environs/openstack/local_test.go (+22/-26)
To merge this branch: bzr merge lp:~gz/juju-core/tests_os_ssh_isolation_1144704
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+154667@code.launchpad.net

Description of the change

Make openstack tests work with no ssh public key

The juju tests are not run in a well isolated manner, particularly they will
examime the user's home directory and environment variables. This branch fixes
the issue of openstack client setup complaining of no ssh public key is found in
~/.ssh even in tests that are just using the local fake server implementations.

Following the example of how this was fixed for the EC2 tests, this is done in
an ad hoc manner by adding the 'authorized-keys' config with a fake value to
tests that hit this condition.

The complication is for live tests, we really do want to use the user's ssh key,
and the test cases and setup code are used in both cases. Some shuffling of how
suite and test case setup is done was needed to make that work.

https://codereview.appspot.com/7943044/

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Reviewers: mp+154667_code.launchpad.net,

Message:
Please take a look.

Description:
Make openstack tests work with no ssh public key

The juju tests are not run in a well isolated manner, particularly they
will
examime the user's home directory and environment variables. This branch
fixes
the issue of openstack client setup complaining of no ssh public key is
found in
~/.ssh even in tests that are just using the local fake server
implementations.

Following the example of how this was fixed for the EC2 tests, this is
done in
an ad hoc manner by adding the 'authorized-keys' config with a fake
value to
tests that hit this condition.

The complication is for live tests, we really do want to use the user's
ssh key,
and the test cases and setup code are used in both cases. Some shuffling
of how
suite and test case setup is done was needed to make that work.

https://code.launchpad.net/~gz/juju-core/tests_os_ssh_isolation_1144704/+merge/154667

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M environs/openstack/config_test.go
   M environs/openstack/live_test.go
   M environs/openstack/local_test.go

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

LGTM

Could use a comment about how the copy preserves test isolation, but
otherwise yay.

https://codereview.appspot.com/7943044/diff/1/environs/openstack/live_test.go
File environs/openstack/live_test.go (left):

https://codereview.appspot.com/7943044/diff/1/environs/openstack/live_test.go#oldcode104
environs/openstack/live_test.go:104: }
I think we should add a comment here that we are copying t.Config to
ensure tests stay isolated from eachother.

https://codereview.appspot.com/7943044/diff/1/environs/openstack/local_test.go
File environs/openstack/local_test.go (left):

https://codereview.appspot.com/7943044/diff/1/environs/openstack/local_test.go#oldcode205
environs/openstack/local_test.go:205: }
And probably another comment here.

The reason for the comment is because even if we didn't have config to
change here, we would need the copy, because otherwise the Config map
gets updated as a side effect of other bootstrap type operations. (And
then tests are coupled and very sad.)

https://codereview.appspot.com/7943044/

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

One half-baked idea for your consideration. Let me know what you think.

https://codereview.appspot.com/7943044/diff/1/environs/openstack/live_test.go
File environs/openstack/live_test.go (right):

https://codereview.appspot.com/7943044/diff/1/environs/openstack/live_test.go#newcode142
environs/openstack/live_test.go:142: // Config and neither includes the
other so it would need to be copied in both.
I suspect you could embed one of these in each suite that previously had
a plain Config and use it to do all the updating consistently without
needing to change the code that just reads from .Config:

type TestConfig struct {
     Config map[string]interface{}
}

func (cfg *TestConfig) UpdateConfig(kv map[string]interface{}) {
     // ...as in updatedTestConfig, but:
     cfg.Config = newConfig
}

You'd want different creation funcs in different test packages, but the
openstack one would look pretty familiar:

func makeTestConfig(cred *identity.Credentials) jujutest.TestConfig {
     return jujutest.TestConfig{map[string]interface{}{
         // as in makeTestConfig
     }}
}

And would be pretty convenient to use:

// ...
     LiveTests: jujutest.LiveTests{
         TestConfig: makeTestConfig(cred),
// ...

// ...
config := makeTestConfig(cred)
// ...
config.UpdateConfig(map[string]interface{}{
     "default-image-id": ...
})
// ...

Sane? On reflection, it might be nicer to stick the map[string]interface
somewhere private and have a Config() method that copies it; that feels
like it might be a firmer guard against accidental pollution.

https://codereview.appspot.com/7943044/

Revision history for this message
Martin Packman (gz) wrote :
Revision history for this message
Martin Packman (gz) wrote :

On 2013/03/25 23:51:39, dfc wrote:
> LGTM I guess. I'm uneasy that in every test case we use the
authorised-keys key,
> but in practice that configuration item is always unset, defaulting to
the value
> of authorised-key-path, which defaults to $HOME/.ssh/...

I'm not to worried about that, it's easy to test config in isolation and
retesting it in local server tests doesn't add anything. Also, if we
break it, everyone will notice.

> Understanding it is more work, I'd be happier if this mocked
$HOME/.ssh

I'd be happier if all tests that may look at local disk or environment
variables ran inside a test-isolated home, but that was proposed by
Roger previously and rejected. We might want to revist it.

> environs/ec2/live_test.go:52: TestConfig:
jujutest.TestConfig{attrs},
> Why rename this field ?

It's no longer the same thing, I prefer renaming things when there are
semantic changes, helps prevent confusion, bogus merges, and such like.

https://codereview.appspot.com/7943044/

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

I certainly think it is better than what we have. I'd like to have a bit
more isolation between tests (.Config() as a method that copies the
internal map, rather than .Config an attribute that exposes it.) But
otherwise:
LGTM

https://codereview.appspot.com/7943044/

Revision history for this message
Martin Packman (gz) wrote :

*** Submitted:

Make openstack tests work with no ssh public key

The juju tests are not run in a well isolated manner, particularly they
will
examime the user's home directory and environment variables. This branch
fixes
the issue of openstack client setup complaining of no ssh public key is
found in
~/.ssh even in tests that are just using the local fake server
implementations.

Following the example of how this was fixed for the EC2 tests, this is
done in
an ad hoc manner by adding the 'authorized-keys' config with a fake
value to
tests that hit this condition.

The complication is for live tests, we really do want to use the user's
ssh key,
and the test cases and setup code are used in both cases. Some shuffling
of how
suite and test case setup is done was needed to make that work.

R=jameinel, fwereade, dfc
CC=
https://codereview.appspot.com/7943044

https://codereview.appspot.com/7943044/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'environs/dummy/environs_test.go'
2--- environs/dummy/environs_test.go 2012-11-27 09:19:59 +0000
3+++ environs/dummy/environs_test.go 2013-03-26 12:52:19 +0000
4@@ -20,12 +20,12 @@
5 "ca-private-key": testing.CAKey,
6 }
7 Suite(&jujutest.LiveTests{
8- Config: attrs,
9+ TestConfig: jujutest.TestConfig{attrs},
10 CanOpenState: true,
11 HasProvisioner: false,
12 })
13 Suite(&jujutest.Tests{
14- Config: attrs,
15+ TestConfig: jujutest.TestConfig{attrs},
16 })
17 }
18
19
20=== modified file 'environs/ec2/live_test.go'
21--- environs/ec2/live_test.go 2013-03-20 21:35:46 +0000
22+++ environs/ec2/live_test.go 2013-03-26 12:52:19 +0000
23@@ -50,7 +50,7 @@
24 }
25 Suite(&LiveTests{
26 LiveTests: jujutest.LiveTests{
27- Config: attrs,
28+ TestConfig: jujutest.TestConfig{attrs},
29 Attempt: *ec2.ShortAttempt,
30 CanOpenState: true,
31 HasProvisioner: true,
32@@ -68,7 +68,8 @@
33
34 func (t *LiveTests) SetUpSuite(c *C) {
35 t.LoggingSuite.SetUpSuite(c)
36- e, err := environs.NewFromAttrs(t.Config)
37+ // TODO: Share code from jujutest.LiveTests for creating environment
38+ e, err := environs.NewFromAttrs(t.TestConfig.Config)
39 c.Assert(err, IsNil)
40
41 // Environ.PublicStorage() is read only.
42
43=== modified file 'environs/ec2/local_test.go'
44--- environs/ec2/local_test.go 2013-03-25 17:11:21 +0000
45+++ environs/ec2/local_test.go 2013-03-26 12:52:19 +0000
46@@ -100,19 +100,19 @@
47
48 Suite(&localServerSuite{
49 Tests: jujutest.Tests{
50- Config: attrs,
51+ TestConfig: jujutest.TestConfig{attrs},
52 },
53 })
54 Suite(&localLiveSuite{
55 LiveTests: LiveTests{
56 LiveTests: jujutest.LiveTests{
57- Config: attrs,
58+ TestConfig: jujutest.TestConfig{attrs},
59 },
60 },
61 })
62 Suite(&localNonUSEastSuite{
63 tests: jujutest.Tests{
64- Config: attrs,
65+ TestConfig: jujutest.TestConfig{attrs},
66 },
67 srv: localServer{
68 config: &s3test.Config{
69
70=== modified file 'environs/jujutest/livetests.go'
71--- environs/jujutest/livetests.go 2013-03-21 04:04:27 +0000
72+++ environs/jujutest/livetests.go 2013-03-26 12:52:19 +0000
73@@ -24,8 +24,8 @@
74 type LiveTests struct {
75 coretesting.LoggingSuite
76
77- // Config holds the configuration attributes for opening an environment.
78- Config map[string]interface{}
79+ // TestConfig contains the configuration attributes for opening an environment.
80+ TestConfig TestConfig
81
82 // Env holds the currently opened environment.
83 Env environs.Environ
84@@ -47,8 +47,8 @@
85
86 func (t *LiveTests) SetUpSuite(c *C) {
87 t.LoggingSuite.SetUpSuite(c)
88- e, err := environs.NewFromAttrs(t.Config)
89- c.Assert(err, IsNil, Commentf("opening environ %#v", t.Config))
90+ e, err := environs.NewFromAttrs(t.TestConfig.Config)
91+ c.Assert(err, IsNil, Commentf("opening environ %#v", t.TestConfig.Config))
92 c.Assert(e, NotNil)
93 t.Env = e
94 c.Logf("environment configuration: %#v", publicAttrs(e))
95
96=== modified file 'environs/jujutest/tests.go'
97--- environs/jujutest/tests.go 2013-03-20 21:19:46 +0000
98+++ environs/jujutest/tests.go 2013-03-26 12:52:19 +0000
99@@ -15,6 +15,25 @@
100 "sort"
101 )
102
103+// TestConfig contains the configuration for the environment
104+// This is a is an indirection to make it harder for tests to accidentally
105+// share the underlying map.
106+type TestConfig struct {
107+ Config map[string]interface{}
108+}
109+
110+// UpdateConfig modifies the configuration safely by creating a new map
111+func (testConfig *TestConfig) UpdateConfig(update map[string]interface{}) {
112+ newConfig := map[string]interface{}{}
113+ for key, val := range testConfig.Config {
114+ newConfig[key] = val
115+ }
116+ for key, val := range update {
117+ newConfig[key] = val
118+ }
119+ testConfig.Config = newConfig
120+}
121+
122 // Tests is a gocheck suite containing tests verifying juju functionality
123 // against the environment with the given configuration. The
124 // tests are not designed to be run against a live server - the Environ
125@@ -22,14 +41,14 @@
126 // may be executed.
127 type Tests struct {
128 coretesting.LoggingSuite
129- Config map[string]interface{}
130- Env environs.Environ
131+ TestConfig TestConfig
132+ Env environs.Environ
133 }
134
135 // Open opens an instance of the testing environment.
136 func (t *Tests) Open(c *C) environs.Environ {
137- e, err := environs.NewFromAttrs(t.Config)
138- c.Assert(err, IsNil, Commentf("opening environ %#v", t.Config))
139+ e, err := environs.NewFromAttrs(t.TestConfig.Config)
140+ c.Assert(err, IsNil, Commentf("opening environ %#v", t.TestConfig.Config))
141 c.Assert(e, NotNil)
142 return e
143 }
144
145=== modified file 'environs/openstack/config_test.go'
146--- environs/openstack/config_test.go 2013-03-08 20:31:57 +0000
147+++ environs/openstack/config_test.go 2013-03-26 12:52:19 +0000
148@@ -57,7 +57,8 @@
149 envs := attrs{
150 "environments": attrs{
151 "testenv": attrs{
152- "type": "openstack",
153+ "type": "openstack",
154+ "authorized-keys": "fakekey",
155 },
156 },
157 }
158@@ -372,7 +373,8 @@
159 envs := attrs{
160 "environments": attrs{
161 "testenv": attrs{
162- "type": "openstack",
163+ "type": "openstack",
164+ "authorized-keys": "fakekey",
165 },
166 },
167 }
168@@ -397,7 +399,8 @@
169 envs := attrs{
170 "environments": attrs{
171 "testenv": attrs{
172- "type": "openstack",
173+ "type": "openstack",
174+ "authorized-keys": "fakekey",
175 },
176 },
177 }
178
179=== modified file 'environs/openstack/live_test.go'
180--- environs/openstack/live_test.go 2013-02-27 10:24:35 +0000
181+++ environs/openstack/live_test.go 2013-03-26 12:52:19 +0000
182@@ -26,7 +26,7 @@
183 return fmt.Sprintf("%x", buf)
184 }
185
186-func makeTestConfig() map[string]interface{} {
187+func makeTestConfig(cred *identity.Credentials) map[string]interface{} {
188 // The following attributes hold the environment configuration
189 // for running the OpenStack integration tests.
190 //
191@@ -36,22 +36,33 @@
192 // secret-key: $OS_PASSWORD
193 //
194 attrs := map[string]interface{}{
195- "name": "sample-" + randomName(),
196- "type": "openstack",
197- "auth-mode": "userpass",
198- "control-bucket": "juju-test-" + randomName(),
199- "ca-cert": coretesting.CACert,
200- "ca-private-key": coretesting.CAKey,
201+ "name": "sample-" + randomName(),
202+ "type": "openstack",
203+ "auth-mode": "userpass",
204+ "control-bucket": "juju-test-" + randomName(),
205+ "ca-cert": coretesting.CACert,
206+ "ca-private-key": coretesting.CAKey,
207+ "authorized-keys": "fakekey",
208+ "admin-secret": "secret",
209+ "username": cred.User,
210+ "password": cred.Secrets,
211+ "region": cred.Region,
212+ "auth-url": cred.URL,
213+ "tenant-name": cred.TenantName,
214 }
215 return attrs
216 }
217
218 // Register tests to run against a real Openstack instance.
219 func registerLiveTests(cred *identity.Credentials, testImageDetails openstack.ImageDetails) {
220+ config := makeTestConfig(cred)
221+ config["default-image-id"] = testImageDetails.ImageId
222+ config["default-instance-type"] = testImageDetails.Flavor
223 Suite(&LiveTests{
224 cred: cred,
225 LiveTests: jujutest.LiveTests{
226- Attempt: *openstack.ShortAttempt,
227+ TestConfig: jujutest.TestConfig{config},
228+ Attempt: *openstack.ShortAttempt,
229 // TODO: Bug #1133263, once the infrastructure is set up,
230 // enable The state tests on openstack
231 CanOpenState: false,
232@@ -79,29 +90,18 @@
233
234 func (t *LiveTests) SetUpSuite(c *C) {
235 t.LoggingSuite.SetUpSuite(c)
236- // Get an authenticated Goose client to extract some configuration parameters for the test environment.
237+ // Update some Config items now that we have services running.
238+ // This is setting the public-bucket-url and auth-url because that
239+ // information is set during startup of the localLiveSuite
240 cl := client.NewClient(t.cred, identity.AuthUserPass, nil)
241 err := cl.Authenticate()
242 c.Assert(err, IsNil)
243 publicBucketURL, err := cl.MakeServiceURL("object-store", nil)
244 c.Assert(err, IsNil)
245- attrs := makeTestConfig()
246- attrs["admin-secret"] = "secret"
247- attrs["username"] = t.cred.User
248- attrs["password"] = t.cred.Secrets
249- attrs["region"] = t.cred.Region
250- attrs["auth-url"] = t.cred.URL
251- attrs["tenant-name"] = t.cred.TenantName
252- attrs["public-bucket-url"] = publicBucketURL
253- attrs["default-image-id"] = t.testImageId
254- attrs["default-instance-type"] = t.testFlavor
255- t.Config = attrs
256- t.LiveTests = jujutest.LiveTests{
257- Config: attrs,
258- Attempt: *openstack.ShortAttempt,
259- CanOpenState: false, // no state; local tests (unless -live is passed)
260- HasProvisioner: false, // don't deploy anything
261- }
262+ t.TestConfig.UpdateConfig(map[string]interface{}{
263+ "public-bucket-url": publicBucketURL,
264+ "auth-url": t.cred.URL,
265+ })
266 t.LiveTests.SetUpSuite(c)
267 // Environ.PublicStorage() is read only.
268 // For testing, we create a specific storage instance which is authorised to write to
269
270=== modified file 'environs/openstack/local_test.go'
271--- environs/openstack/local_test.go 2013-03-20 21:19:46 +0000
272+++ environs/openstack/local_test.go 2013-03-26 12:52:19 +0000
273@@ -92,13 +92,25 @@
274 Region: "some region",
275 TenantName: "some tenant",
276 }
277+ config := makeTestConfig(cred)
278+ config["authorized-keys"] = "fakekey"
279+ config["default-image-id"] = "1"
280+ config["default-instance-type"] = "m1.small"
281 Suite(&localLiveSuite{
282 LiveTests: LiveTests{
283- cred: cred,
284+ cred: cred,
285+ testImageId: "1",
286+ testFlavor: "m1.small",
287+ LiveTests: jujutest.LiveTests{
288+ TestConfig: jujutest.TestConfig{config},
289+ },
290 },
291 })
292 Suite(&localServerSuite{
293 cred: cred,
294+ Tests: jujutest.Tests{
295+ TestConfig: jujutest.TestConfig{config},
296+ },
297 })
298 }
299
300@@ -117,6 +129,7 @@
301 s.Mux = http.NewServeMux()
302 s.Server.Config.Handler = s.Mux
303 cred.URL = s.Server.URL
304+ c.Logf("Started service at: %v", s.Server.URL)
305 s.Service = openstackservice.New(cred)
306 s.Service.SetupHTTP(s.Mux)
307 openstack.ShortTimeouts(true)
308@@ -139,9 +152,6 @@
309 func (s *localLiveSuite) SetUpSuite(c *C) {
310 s.LoggingSuite.SetUpSuite(c)
311 c.Logf("Running live tests using openstack service test double")
312-
313- s.testImageId = "1"
314- s.testFlavor = "m1.small"
315 s.srv.start(c, s.cred)
316 s.LiveTests.SetUpSuite(c)
317 }
318@@ -185,25 +195,12 @@
319 s.LoggingSuite.TearDownSuite(c)
320 }
321
322-func testConfig(cred *identity.Credentials) map[string]interface{} {
323- attrs := makeTestConfig()
324- attrs["admin-secret"] = "secret"
325- attrs["username"] = cred.User
326- attrs["password"] = cred.Secrets
327- attrs["region"] = cred.Region
328- attrs["auth-url"] = cred.URL
329- attrs["tenant-name"] = cred.TenantName
330- attrs["default-image-id"] = "1"
331- attrs["default-instance-type"] = "m1.small"
332- return attrs
333-}
334-
335 func (s *localServerSuite) SetUpTest(c *C) {
336 s.LoggingSuite.SetUpTest(c)
337 s.srv.start(c, s.cred)
338- s.Tests = jujutest.Tests{
339- Config: testConfig(s.cred),
340- }
341+ s.TestConfig.UpdateConfig(map[string]interface{}{
342+ "auth-url": s.cred.URL,
343+ })
344 s.Tests.SetUpTest(c)
345 writeablePublicStorage := openstack.WritablePublicStorage(s.Env)
346 putFakeTools(c, writeablePublicStorage)
347@@ -227,12 +224,11 @@
348 )
349 defer cleanup()
350 // Create a config that matches s.Config but with use-floating-ip set to true
351- newconfig := make(map[string]interface{}, len(s.Config))
352- for k, v := range s.Config {
353- newconfig[k] = v
354- }
355- newconfig["use-floating-ip"] = true
356- env, err := environs.NewFromAttrs(newconfig)
357+ s.TestConfig.UpdateConfig(map[string]interface{}{
358+ "use-floating-ip": true,
359+ })
360+ // TODO: Just share jujutest.Tests.Open rather than accessing .Config
361+ env, err := environs.NewFromAttrs(s.TestConfig.Config)
362 c.Assert(err, IsNil)
363 err = environs.Bootstrap(env, constraints.Value{})
364 c.Assert(err, ErrorMatches, ".*cannot allocate a public IP as needed.*")

Subscribers

People subscribed via source and target branches