Merge lp:~axwalk/juju-core/lp1262764-manual-storage-setconfig 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: 2308
Proposed branch: lp:~axwalk/juju-core/lp1262764-manual-storage-setconfig
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 388 lines (+125/-66)
8 files modified
provider/manual/config.go (+10/-0)
provider/manual/config_test.go (+17/-0)
provider/manual/environ.go (+46/-41)
provider/manual/environ_test.go (+8/-23)
provider/manual/export_test.go (+1/-0)
provider/manual/provider.go (+14/-2)
provider/manual/provider_test.go (+22/-0)
provider/manual/suite_test.go (+7/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1262764-manual-storage-setconfig
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+205530@code.launchpad.net

Commit message

provider/manual: set storage in SetConfig

We currently rely on EnableBootstrapStorage to
specify the storage mechanism to use for bootstrap
and sync-tools. This is insufficient in practice,
causing panics such as those in lp:1262764.

Instead, we inject a boolean attribute into the
environment config that is stored in state; this
attribute is false in the .jenv file, and true
in state. We use its value to decide whether to
use sshstorage (external) or httpstorage (internal).

A followup will eliminate BootstrapStorager.

Fixes lp:1262764

https://codereview.appspot.com/61480043/

Description of the change

provider/manual: set storage in SetConfig

We currently rely on EnableBootstrapStorage to
specify the storage mechanism to use for bootstrap
and sync-tools. This is insufficient in practice,
causing panics such as those in lp:1262764.

Instead, we inject a boolean attribute into the
environment config that is stored in state; this
attribute is false in the .jenv file, and true
in state. We use its value to decide whether to
use sshstorage (external) or httpstorage (internal).

A followup will eliminate BootstrapStorager.

Fixes lp:1262764

https://codereview.appspot.com/61480043/

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

Reviewers: mp+205530_code.launchpad.net,

Message:
Please take a look.

Description:
provider/manual: set storage in SetConfig

We currently rely on EnableBootstrapStorage to
specify the storage mechanism to use for bootstrap
and sync-tools. This is insufficient in practice,
causing panics such as those in lp:1262764.

Instead, we inject a boolean attribute into the
environment config that is stored in state; this
attribute is false in the .jenv file, and true
in state. We use its value to decide whether to
use sshstorage (external) or httpstorage (internal).

A followup will eliminate BootstrapStorager.

Fixes lp:1262764

https://code.launchpad.net/~axwalk/juju-core/lp1262764-manual-storage-setconfig/+merge/205530

(do not edit description out of merge proposal)

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

Affected files (+128, -66 lines):
   A [revision details]
   M provider/manual/config.go
   M provider/manual/config_test.go
   M provider/manual/environ.go
   M provider/manual/environ_test.go
   M provider/manual/export_test.go
   M provider/manual/provider.go
   M provider/manual/provider_test.go
   M provider/manual/suite_test.go

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

I'm not so sure about the flag name vs. what's its meaning. See below.

Other than that, it looks solid.

https://codereview.appspot.com/61480043/diff/1/provider/manual/config.go
File provider/manual/config.go (right):

https://codereview.appspot.com/61480043/diff/1/provider/manual/config.go#newcode22
provider/manual/config.go:22: "bootstrapped": schema.Bool(),
This looks a bit dubious - if the purpose of this flag (as seen later in
tests) is to make a decision which storage to use, then why not call it
"use-httpstorage" or something? That way, for older environments it will
be "false" by default, and we get the same logic, don't we?

https://codereview.appspot.com/61480043/

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

On 2014/02/10 09:28:06, dimitern wrote:
> I'm not so sure about the flag name vs. what's its meaning. See below.

> Other than that, it looks solid.

https://codereview.appspot.com/61480043/diff/1/provider/manual/config.go
> File provider/manual/config.go (right):

https://codereview.appspot.com/61480043/diff/1/provider/manual/config.go#newcode22
> provider/manual/config.go:22: "bootstrapped": schema.Bool(),
> This looks a bit dubious - if the purpose of this flag (as seen later
in tests)
> is to make a decision which storage to use, then why not call it
> "use-httpstorage" or something? That way, for older environments it
will be
> "false" by default, and we get the same logic, don't we?

Good idea, thanks. I'll update that tomorrow.

https://codereview.appspot.com/61480043/

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'provider/manual/config.go'
2--- provider/manual/config.go 2014-01-29 09:18:45 +0000
3+++ provider/manual/config.go 2014-02-10 12:33:23 +0000
4@@ -19,11 +19,13 @@
5 "storage-listen-ip": schema.String(),
6 "storage-port": schema.Int(),
7 "storage-auth-key": schema.String(),
8+ "use-sshstorage": schema.Bool(),
9 }
10 configDefaults = schema.Defaults{
11 "bootstrap-user": "",
12 "storage-listen-ip": "",
13 "storage-port": defaultStoragePort,
14+ "use-sshstorage": true,
15 }
16 )
17
18@@ -36,6 +38,14 @@
19 return &environConfig{Config: config, attrs: attrs}
20 }
21
22+func (c *environConfig) useSSHStorage() bool {
23+ // Prior to 1.17.3, the use-sshstorage attribute
24+ // did not exist. We take non-existence to be
25+ // equivalent to false.
26+ useSSHStorage, _ := c.attrs["use-sshstorage"].(bool)
27+ return useSSHStorage
28+}
29+
30 func (c *environConfig) bootstrapHost() string {
31 return c.attrs["bootstrap-host"].(string)
32 }
33
34=== modified file 'provider/manual/config_test.go'
35--- provider/manual/config_test.go 2014-01-31 05:38:42 +0000
36+++ provider/manual/config_test.go 2014-02-10 12:33:23 +0000
37@@ -11,6 +11,7 @@
38
39 "launchpad.net/juju-core/environs/config"
40 coretesting "launchpad.net/juju-core/testing"
41+ jc "launchpad.net/juju-core/testing/checkers"
42 "launchpad.net/juju-core/testing/testbase"
43 )
44
45@@ -26,6 +27,9 @@
46 "type": "manual",
47 "bootstrap-host": "hostname",
48 "storage-auth-key": "whatever",
49+ // Not strictly necessary, but simplifies testing by disabling
50+ // ssh storage by default.
51+ "use-sshstorage": false,
52 // While the ca-cert bits aren't entirely minimal, they avoid the need
53 // to set up a fake home.
54 "ca-cert": coretesting.CACert,
55@@ -120,3 +124,16 @@
56 c.Assert(testConfig.storageAddr(), gc.Equals, "hostname:1234")
57 c.Assert(testConfig.storageListenAddr(), gc.Equals, "10.0.0.123:1234")
58 }
59+
60+func (s *configSuite) TestStorageCompat(c *gc.C) {
61+ // Older environment configurations will not have the
62+ // use-sshstorage attribute. We treat them as if they
63+ // have use-sshstorage=false.
64+ values := MinimalConfigValues()
65+ delete(values, "use-sshstorage")
66+ cfg, err := config.New(config.UseDefaults, values)
67+ c.Assert(err, gc.IsNil)
68+ envConfig := newEnvironConfig(cfg, values)
69+ c.Assert(err, gc.IsNil)
70+ c.Assert(envConfig.useSSHStorage(), jc.IsFalse)
71+}
72
73=== modified file 'provider/manual/environ.go'
74--- provider/manual/environ.go 2014-01-29 09:18:45 +0000
75+++ provider/manual/environ.go 2014-02-10 12:33:23 +0000
76@@ -51,12 +51,11 @@
77 var logger = loggo.GetLogger("juju.provider.manual")
78
79 type manualEnviron struct {
80- cfg *environConfig
81- cfgmutex sync.Mutex
82- bootstrapStorage storage.Storage
83- bootstrapStorageMutex sync.Mutex
84- ubuntuUserInited bool
85- ubuntuUserInitMutex sync.Mutex
86+ cfg *environConfig
87+ cfgmutex sync.Mutex
88+ storage storage.Storage
89+ ubuntuUserInited bool
90+ ubuntuUserInitMutex sync.Mutex
91 }
92
93 var _ environs.BootstrapStorager = (*manualEnviron)(nil)
94@@ -115,6 +114,14 @@
95 if err := e.ensureBootstrapUbuntuUser(ctx); err != nil {
96 return err
97 }
98+ // Set "use-sshstorage" to false, so agents know not to use sshstorage.
99+ cfg, err := e.Config().Apply(map[string]interface{}{"use-sshstorage": false})
100+ if err != nil {
101+ return err
102+ }
103+ if err := e.SetConfig(cfg); err != nil {
104+ return err
105+ }
106 envConfig := e.envConfig()
107 host := envConfig.bootstrapHost()
108 hc, series, err := manual.DetectSeriesAndHardwareCharacteristics(host)
109@@ -147,6 +154,35 @@
110 if err != nil {
111 return err
112 }
113+ // Set storage. If "use-sshstorage" is true then use the SSH storage.
114+ // Otherwise, use HTTP storage.
115+ //
116+ // We don't change storage once it's been set. Storage parameters
117+ // are fixed at bootstrap time, and it is not possible to change
118+ // them.
119+ if e.storage == nil {
120+ var stor storage.Storage
121+ if envConfig.useSSHStorage() {
122+ storageDir := e.StorageDir()
123+ storageTmpdir := path.Join(dataDir, storageTmpSubdir)
124+ stor, err = newSSHStorage("ubuntu@"+e.cfg.bootstrapHost(), storageDir, storageTmpdir)
125+ if err != nil {
126+ return fmt.Errorf("initialising SSH storage failed: %v", err)
127+ }
128+ } else {
129+ caCertPEM, ok := envConfig.CACert()
130+ if !ok {
131+ // should not be possible to validate base config
132+ return fmt.Errorf("ca-cert not set")
133+ }
134+ authkey := envConfig.storageAuthKey()
135+ stor, err = httpstorage.ClientTLS(envConfig.storageAddr(), caCertPEM, authkey)
136+ if err != nil {
137+ return fmt.Errorf("initialising HTTPS storage failed: %v", err)
138+ }
139+ }
140+ e.storage = stor
141+ }
142 e.cfg = envConfig
143 return nil
144 }
145@@ -184,23 +220,7 @@
146
147 // Implements environs.BootstrapStorager.
148 func (e *manualEnviron) EnableBootstrapStorage(ctx environs.BootstrapContext) error {
149- e.bootstrapStorageMutex.Lock()
150- defer e.bootstrapStorageMutex.Unlock()
151- if e.bootstrapStorage != nil {
152- return nil
153- }
154- if err := e.ensureBootstrapUbuntuUser(ctx); err != nil {
155- return err
156- }
157- cfg := e.envConfig()
158- storageDir := e.StorageDir()
159- storageTmpdir := path.Join(dataDir, storageTmpSubdir)
160- bootstrapStorage, err := newSSHStorage("ubuntu@"+cfg.bootstrapHost(), storageDir, storageTmpdir)
161- if err != nil {
162- return err
163- }
164- e.bootstrapStorage = bootstrapStorage
165- return nil
166+ return e.ensureBootstrapUbuntuUser(ctx)
167 }
168
169 // GetToolsSources returns a list of sources which are
170@@ -213,24 +233,9 @@
171 }
172
173 func (e *manualEnviron) Storage() storage.Storage {
174- e.bootstrapStorageMutex.Lock()
175- defer e.bootstrapStorageMutex.Unlock()
176- if e.bootstrapStorage != nil {
177- return e.bootstrapStorage
178- }
179- caCertPEM, authkey := e.StorageCACert(), e.StorageAuthKey()
180- if caCertPEM != nil && authkey != "" {
181- storage, err := httpstorage.ClientTLS(e.envConfig().storageAddr(), caCertPEM, authkey)
182- if err != nil {
183- // Should be impossible, since ca-cert will always be validated.
184- logger.Errorf("initialising HTTPS storage failed: %v", err)
185- } else {
186- return storage
187- }
188- } else {
189- logger.Errorf("missing CA cert or auth-key")
190- }
191- return nil
192+ e.cfgmutex.Lock()
193+ defer e.cfgmutex.Unlock()
194+ return e.storage
195 }
196
197 var runSSHCommand = func(host string, command []string) (stderr string, err error) {
198
199=== modified file 'provider/manual/environ_test.go'
200--- provider/manual/environ_test.go 2014-01-31 05:38:42 +0000
201+++ provider/manual/environ_test.go 2014-02-10 12:33:23 +0000
202@@ -26,11 +26,16 @@
203 env *manualEnviron
204 }
205
206+type dummyStorage struct {
207+ storage.Storage
208+}
209+
210 var _ = gc.Suite(&environSuite{})
211
212 func (s *environSuite) SetUpTest(c *gc.C) {
213- envConfig := getEnvironConfig(c, MinimalConfigValues())
214- s.env = &manualEnviron{cfg: envConfig}
215+ env, err := manualProvider{}.Open(MinimalConfig(c))
216+ c.Assert(err, gc.IsNil)
217+ s.env = env.(*manualEnviron)
218 }
219
220 func (s *environSuite) TestSetConfig(c *gc.C) {
221@@ -128,19 +133,7 @@
222 c.Assert(strings.Contains(url, "/tools"), jc.IsTrue)
223 }
224
225-type dummyStorage struct {
226- storage.Storage
227-}
228-
229 func (s *environSuite) TestEnvironBootstrapStorager(c *gc.C) {
230- var newSSHStorageResult = struct {
231- stor storage.Storage
232- err error
233- }{dummyStorage{}, errors.New("failed to get SSH storage")}
234- s.PatchValue(&newSSHStorage, func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {
235- return newSSHStorageResult.stor, newSSHStorageResult.err
236- })
237-
238 var initUbuntuResult error
239 s.PatchValue(&initUbuntuUser, func(host, user, authorizedKeys string, stdin io.Reader, stdout io.Writer) error {
240 return initUbuntuResult
241@@ -150,17 +143,9 @@
242 initUbuntuResult = errors.New("failed to initialise ubuntu user")
243 c.Assert(s.env.EnableBootstrapStorage(ctx), gc.Equals, initUbuntuResult)
244 initUbuntuResult = nil
245- c.Assert(s.env.EnableBootstrapStorage(ctx), gc.Equals, newSSHStorageResult.err)
246+ c.Assert(s.env.EnableBootstrapStorage(ctx), gc.IsNil)
247 // after the user is initialised once successfully,
248 // another attempt will not be made.
249 initUbuntuResult = errors.New("failed to initialise ubuntu user")
250- c.Assert(s.env.EnableBootstrapStorage(ctx), gc.Equals, newSSHStorageResult.err)
251-
252- // after the bootstrap storage is initialised once successfully,
253- // another attempt will not be made.
254- backup := newSSHStorageResult.err
255- newSSHStorageResult.err = nil
256- c.Assert(s.env.EnableBootstrapStorage(ctx), gc.IsNil)
257- newSSHStorageResult.err = backup
258 c.Assert(s.env.EnableBootstrapStorage(ctx), gc.IsNil)
259 }
260
261=== modified file 'provider/manual/export_test.go'
262--- provider/manual/export_test.go 2014-01-31 05:38:42 +0000
263+++ provider/manual/export_test.go 2014-02-10 12:33:23 +0000
264@@ -5,4 +5,5 @@
265
266 var (
267 ProviderInstance = manualProvider{}
268+ NewSSHStorage = &newSSHStorage
269 )
270
271=== modified file 'provider/manual/provider.go'
272--- provider/manual/provider.go 2014-02-10 03:29:45 +0000
273+++ provider/manual/provider.go 2014-02-10 12:33:23 +0000
274@@ -22,7 +22,7 @@
275 var errNoBootstrapHost = errors.New("bootstrap-host must be specified")
276
277 func (p manualProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
278- if _, ok := cfg.UnknownAttrs()["storage-auth-key"].(string); !ok {
279+ if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok {
280 uuid, err := utils.NewUUID()
281 if err != nil {
282 return nil, err
283@@ -34,6 +34,9 @@
284 return nil, err
285 }
286 }
287+ if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use {
288+ return nil, fmt.Errorf("use-sshstorage must not be specified")
289+ }
290 return p.Open(cfg)
291 }
292
293@@ -46,7 +49,12 @@
294 }
295
296 func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) {
297- return &manualEnviron{cfg: cfg}, nil
298+ env := &manualEnviron{cfg: cfg}
299+ // Need to call SetConfig to initialise storage.
300+ if err := env.SetConfig(cfg.Config); err != nil {
301+ return nil, err
302+ }
303+ return env, nil
304 }
305
306 func checkImmutableString(cfg, old *environConfig, key string) error {
307@@ -88,6 +96,10 @@
308 if oldPort != newPort {
309 return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort)
310 }
311+ oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage()
312+ if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true {
313+ return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage)
314+ }
315 }
316 return envConfig, nil
317 }
318
319=== modified file 'provider/manual/provider_test.go'
320--- provider/manual/provider_test.go 2014-02-10 10:05:26 +0000
321+++ provider/manual/provider_test.go 2014-02-10 12:33:23 +0000
322@@ -4,10 +4,13 @@
323 package manual_test
324
325 import (
326+ "fmt"
327+
328 gc "launchpad.net/gocheck"
329
330 "launchpad.net/juju-core/environs"
331 "launchpad.net/juju-core/environs/config"
332+ "launchpad.net/juju-core/environs/storage"
333 "launchpad.net/juju-core/provider/manual"
334 jc "launchpad.net/juju-core/testing/checkers"
335 "launchpad.net/juju-core/testing/testbase"
336@@ -22,6 +25,7 @@
337
338 func (s *providerSuite) TestPrepare(c *gc.C) {
339 minimal := manual.MinimalConfigValues()
340+ minimal["use-sshstorage"] = true
341 delete(minimal, "storage-auth-key")
342 testConfig, err := config.New(config.UseDefaults, minimal)
343 c.Assert(err, gc.IsNil)
344@@ -32,6 +36,24 @@
345 c.Assert(key, jc.Satisfies, utils.IsValidUUIDString)
346 }
347
348+func (s *providerSuite) TestPrepareUseSSHStorage(c *gc.C) {
349+ minimal := manual.MinimalConfigValues()
350+ minimal["use-sshstorage"] = false
351+ testConfig, err := config.New(config.UseDefaults, minimal)
352+ c.Assert(err, gc.IsNil)
353+ _, err = manual.ProviderInstance.Prepare(testConfig)
354+ c.Assert(err, gc.ErrorMatches, "use-sshstorage must not be specified")
355+
356+ s.PatchValue(manual.NewSSHStorage, func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {
357+ return nil, fmt.Errorf("newSSHStorage failed")
358+ })
359+ minimal["use-sshstorage"] = true
360+ testConfig, err = config.New(config.UseDefaults, minimal)
361+ c.Assert(err, gc.IsNil)
362+ _, err = manual.ProviderInstance.Prepare(testConfig)
363+ c.Assert(err, gc.ErrorMatches, "initialising SSH storage failed: newSSHStorage failed")
364+}
365+
366 func (s *providerSuite) TestNullAlias(c *gc.C) {
367 p, err := environs.Provider("manual")
368 c.Assert(p, gc.NotNil)
369
370=== modified file 'provider/manual/suite_test.go'
371--- provider/manual/suite_test.go 2014-01-31 05:38:42 +0000
372+++ provider/manual/suite_test.go 2014-02-10 12:33:23 +0000
373@@ -7,8 +7,15 @@
374 "testing"
375
376 gc "launchpad.net/gocheck"
377+
378+ "launchpad.net/juju-core/environs/storage"
379+ "launchpad.net/juju-core/provider/manual"
380 )
381
382 func Test(t *testing.T) {
383+ // Prevent any use of ssh for storage.
384+ *manual.NewSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {
385+ return nil, nil
386+ }
387 gc.TestingT(t)
388 }

Subscribers

People subscribed via source and target branches

to status/vote changes: