Merge lp:~dave-cheney/juju-core/080-conn-update-secrets-only-if-needed into lp:~juju/juju-core/trunk

Proposed by Dave Cheney
Status: Rejected
Rejected by: Dave Cheney
Proposed branch: lp:~dave-cheney/juju-core/080-conn-update-secrets-only-if-needed
Merge into: lp:~juju/juju-core/trunk
Diff against target: 1047 lines (+354/-195) (has conflicts)
14 files modified
cmd/juju/cmd_test.go (+1/-1)
cmd/juju/main_test.go (+2/-2)
environs/cloudinit/cloudinit.go (+8/-8)
environs/cloudinit/cloudinit_test.go (+36/-16)
environs/config.go (+19/-0)
environs/config/config.go (+3/-1)
environs/dummy/environs.go (+28/-7)
environs/ec2/ec2.go (+11/-24)
environs/jujutest/test.go (+14/-19)
environs/tools.go (+35/-14)
environs/tools_test.go (+152/-101)
juju/conn.go (+20/-0)
juju/conn_test.go (+24/-1)
state/machine.go (+1/-1)
Text conflict in juju/conn.go
To merge this branch: bzr merge lp:~dave-cheney/juju-core/080-conn-update-secrets-only-if-needed
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+121979@code.launchpad.net

Description of the change

juju: update unknown config attrs once

Additional UnknownAttrs from the local environment
will only be applied if they do not already exist in
the state.

https://codereview.appspot.com/6497057/

To post a comment you must log in.
Revision history for this message
Dave Cheney (dave-cheney) wrote :

Please hold off on reviewing this proposal. Tests pass, but an actual deployment fails because the common attributes of the config are not being populated.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

http://codereview.appspot.com/6497057/diff/3001/juju/conn.go
File juju/conn.go (right):

http://codereview.appspot.com/6497057/diff/3001/juju/conn.go#newcode92
juju/conn.go:92: env.Set(k, v)
Aug 30 22:15:25 <niemeyer> I suspect it's a bit more involved than
that
Aug 30 22:15:52 <niemeyer> As the comment suggests, we should only
push secrets when they are not yet set, and should push *only* secrets

http://codereview.appspot.com/6497057/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Cool, I added some comments for the record, and.. well, not sure if we
should close this one?

http://codereview.appspot.com/6497057/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Ah, nevermind.. it looks already rejected. I probably looked at
out-of-date information, sorry.

http://codereview.appspot.com/6497057/

Unmerged revisions

452. By Dave Cheney

merge from prereq

451. By Dave Cheney

wip

450. By Dave Cheney

only update non existing UnknownAttrs

449. By Dave Cheney

merge from trunk

448. By Dave Cheney

wip

447. By Dave Cheney

environs/dummy: add delay

This proposal implements a delay in most dummy operations. The delay
can be set test wide by setting JUJU_DUMMY_DELAY to a time.Duration
parsable value.

R=niemeyer
CC=
https://codereview.appspot.com/6482081

446. By Dave Cheney

environs/ec2: log open/close port details

https://bugs.launchpad.net/juju-core/+bug/1042073

Added logging of security group name to successful
open/close port operation.

R=niemeyer
CC=
https://codereview.appspot.com/6490049

445. By Dave Cheney

environs/cloudinit: deploy mongo debs

Install mongo debs alongside zookeeper ones.

R=niemeyer
CC=
https://codereview.appspot.com/6501054

444. By Dave Cheney

environs/cloudinit: ZooKeeper -> StateServer

This proposal is part of a set to add support for deploying
an environment on MongoDB in parallel with ZooKeeper.

R=niemeyer
CC=
https://codereview.appspot.com/6497051

443. By Roger Peppe

testing: define and use all fixture methods.

When a helper suite defines only a subset of the available
fixture methods, it invites problems if that subset changes.
By making all helper suites define all fixture methods,
we make it easy to mechanically check that
a helper suite is being used correctly without going
to look at the source for that suite.

R=niemeyer
CC=
https://codereview.appspot.com/6472053

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/cmd_test.go'
2--- cmd/juju/cmd_test.go 2012-08-28 17:33:59 +0000
3+++ cmd/juju/cmd_test.go 2012-08-31 02:04:18 +0000
4@@ -149,7 +149,7 @@
5 }()
6 environs.VarDir = c.MkDir()
7
8- tools, err := environs.FindTools(env, version.Current)
9+ tools, err := environs.FindTools(env, version.Current, environs.CompatVersion)
10 c.Assert(err, IsNil)
11 resp, err := http.Get(tools.URL)
12 c.Assert(err, IsNil)
13
14=== modified file 'cmd/juju/main_test.go'
15--- cmd/juju/main_test.go 2012-08-09 01:37:27 +0000
16+++ cmd/juju/main_test.go 2012-08-31 02:04:18 +0000
17@@ -121,7 +121,7 @@
18 c.Assert(out, Equals, "error: "+msg+"\n")
19 content, err := ioutil.ReadFile(logpath)
20 c.Assert(err, IsNil)
21- fullmsg := fmt.Sprintf(`.* JUJU:DEBUG juju bootstrap command failed: %s\n`, msg)
22+ fullmsg := fmt.Sprintf(`(.|\n)*JUJU:DEBUG juju bootstrap command failed: %s\n`, msg)
23 c.Assert(string(content), Matches, fullmsg)
24 }
25
26@@ -134,7 +134,7 @@
27 c.Assert(out, Equals, "error: "+msg+"\n")
28 content, err := ioutil.ReadFile(logpath)
29 c.Assert(err, IsNil)
30- fullmsg := fmt.Sprintf(`.* JUJU:DEBUG juju bootstrap command failed: %s\n`, msg)
31+ fullmsg := fmt.Sprintf(`(.|\n)*JUJU:DEBUG juju bootstrap command failed: %s\n`, msg)
32 c.Assert(string(content), Matches, fullmsg)
33 }
34
35
36=== modified file 'environs/cloudinit/cloudinit.go'
37--- environs/cloudinit/cloudinit.go 2012-08-29 22:46:46 +0000
38+++ environs/cloudinit/cloudinit.go 2012-08-31 02:04:18 +0000
39@@ -6,7 +6,7 @@
40 "launchpad.net/goyaml"
41 "launchpad.net/juju-core/cloudinit"
42 "launchpad.net/juju-core/environs"
43- "launchpad.net/juju-core/log"
44+ "launchpad.net/juju-core/environs/config"
45 "launchpad.net/juju-core/state"
46 "launchpad.net/juju-core/upstart"
47 "path"
48@@ -56,9 +56,8 @@
49 // commands cannot work.
50 AuthorizedKeys string
51
52- // Config is map that is provided to juju bootstrap-state's --env-config
53- // option for initializing the environment configuration.
54- Config map[string]interface{}
55+ // Config holds the initial environment configuration.
56+ Config *config.Config
57 }
58
59 type requiresError string
60@@ -73,8 +72,8 @@
61 }
62 }
63
64-func base64yaml(m map[string]interface{}) string {
65- data, err := goyaml.Marshal(m)
66+func base64yaml(m *config.Config) string {
67+ data, err := goyaml.Marshal(m.AllAttrs())
68 if err != nil {
69 // can't happen, these values have been validated a number of times
70 panic(err)
71@@ -119,9 +118,7 @@
72 )
73
74 debugFlag := ""
75- if log.Debug {
76 debugFlag = " --debug"
77- }
78
79 if cfg.StateServer {
80 // zookeeper scripts
81@@ -223,6 +220,9 @@
82 if cfg.InstanceIdAccessor == "" {
83 return requiresError("instance id accessor")
84 }
85+ if cfg.Config == nil {
86+ return requiresError("environment configuration")
87+ }
88 } else {
89 if cfg.StateInfo == nil || len(cfg.StateInfo.Addrs) == 0 {
90 return requiresError("zookeeper hosts")
91
92=== modified file 'environs/cloudinit/cloudinit_test.go'
93--- environs/cloudinit/cloudinit_test.go 2012-08-29 22:50:49 +0000
94+++ environs/cloudinit/cloudinit_test.go 2012-08-31 02:04:18 +0000
95@@ -4,6 +4,7 @@
96 "encoding/base64"
97 . "launchpad.net/gocheck"
98 "launchpad.net/goyaml"
99+ "launchpad.net/juju-core/environs/config"
100 "launchpad.net/juju-core/environs"
101 "launchpad.net/juju-core/environs/cloudinit"
102 "launchpad.net/juju-core/state"
103@@ -18,6 +19,21 @@
104
105 var _ = Suite(cloudinitSuite{})
106
107+var envConfig = mustNewConfig(map[string] interface{} {
108+ "type": "ec2",
109+ "name": "foo",
110+ "default-series": "series",
111+ "authorized-keys": "keys",
112+ })
113+
114+func mustNewConfig(m map[string] interface{}) *config.Config {
115+ cfg, err := config.New(m)
116+ if err != nil {
117+ panic(err)
118+ }
119+ return cfg
120+}
121+
122 // Each test gives a cloudinit config - we check the
123 // output to see if it looks correct.
124 var cloudinitTests = []cloudinit.MachineConfig{
125@@ -29,7 +45,7 @@
126 AuthorizedKeys: "sshkey1",
127 Tools: newSimpleTools("1.2.3-linux-amd64"),
128 StateServer: true,
129- Config: map[string]interface{}{"name": "foo", "zookeeper": true},
130+ Config: envConfig,
131 },
132 {
133 MachineId: 99,
134@@ -65,15 +81,16 @@
135 if t.cfg.StateServer {
136 // TODO(dfc) remove this after the switch to mstate
137 t.checkPackage(c, "zookeeperd")
138-
139 t.checkPackage(c, "mongodb-server")
140 t.checkScripts(c, "jujud bootstrap-state")
141- t.checkEnvConfig(c)
142 t.checkScripts(c, regexp.QuoteMeta(t.cfg.InstanceIdAccessor))
143 t.checkScripts(c, "JUJU_ZOOKEEPER='localhost"+cloudinit.ZkPortSuffix+"'")
144 } else {
145 t.checkScripts(c, "JUJU_ZOOKEEPER='"+strings.Join(t.cfg.StateInfo.Addrs, ",")+"'")
146 }
147+ if t.cfg.Config != nil {
148+ t.checkEnvConfig(c)
149+ }
150 t.checkPackage(c, "libzookeeper-mt2")
151 t.checkScripts(c, "JUJU_MACHINE_ID=[0-9]+")
152
153@@ -96,20 +113,20 @@
154 return
155 }
156 scripts := scripts0.([]interface{})
157- re := regexp.MustCompile(`--env-config '[\w,=]+'`)
158+ re := regexp.MustCompile(`--env-config '([\w,=]+)'`)
159 found := false
160 for _, s0 := range scripts {
161- if s := re.FindString(s0.(string)); len(s) > 0 {
162- found = true
163- v := strings.Split(s, `'`)
164- c.Assert(len(v), Equals, 3)
165- buf, err := base64.StdEncoding.DecodeString(v[1])
166- c.Assert(err, IsNil)
167- actual := make(map[string]interface{})
168- err = goyaml.Unmarshal(buf, &actual)
169- c.Assert(err, IsNil)
170- c.Assert(t.cfg.Config, DeepEquals, actual)
171+ m := re.FindStringSubmatch(s0.(string))
172+ if m == nil {
173+ continue
174 }
175+ found = true
176+ buf, err := base64.StdEncoding.DecodeString(m[1])
177+ c.Assert(err, IsNil)
178+ var actual map[string]interface{}
179+ err = goyaml.Unmarshal(buf, &actual)
180+ c.Assert(err, IsNil)
181+ c.Assert(t.cfg.Config.AllAttrs(), DeepEquals, actual)
182 }
183 c.Assert(found, Equals, true)
184 }
185@@ -182,7 +199,7 @@
186 // in cloudinitTests is well formed.
187 func (cloudinitSuite) TestCloudInit(c *C) {
188 for i, cfg := range cloudinitTests {
189- c.Logf("check %d", i)
190+ c.Logf("test %d, config %v, envConfig %v", i, cfg.Config, envConfig)
191 ci, err := cloudinit.New(&cfg)
192 c.Assert(err, IsNil)
193 c.Check(ci, NotNil)
194@@ -217,9 +234,11 @@
195 {"negative machine id", func(cfg *cloudinit.MachineConfig) { cfg.MachineId = -1 }},
196 {"missing provider type", func(cfg *cloudinit.MachineConfig) { cfg.ProviderType = "" }},
197 {"missing instance id accessor", func(cfg *cloudinit.MachineConfig) {
198- cfg.StateServer = true
199 cfg.InstanceIdAccessor = ""
200 }},
201+ {"missing environment configuration", func(cfg *cloudinit.MachineConfig) {
202+ cfg.Config = nil
203+ }},
204 {"missing zookeeper hosts", func(cfg *cloudinit.MachineConfig) {
205 cfg.StateServer = false
206 cfg.StateInfo = nil
207@@ -250,6 +269,7 @@
208 Tools: newSimpleTools("9.9.9-linux-arble"),
209 AuthorizedKeys: "sshkey1",
210 StateInfo: &state.Info{Addrs: []string{"zkhost"}},
211+ Config: envConfig,
212 }
213 // check that the base configuration does not give an error
214 _, err := cloudinit.New(cfg)
215
216=== modified file 'environs/config.go'
217--- environs/config.go 2012-07-26 13:37:51 +0000
218+++ environs/config.go 2012-08-31 02:04:18 +0000
219@@ -6,6 +6,7 @@
220 "io/ioutil"
221 "launchpad.net/goyaml"
222 "launchpad.net/juju-core/environs/config"
223+ "launchpad.net/juju-core/state"
224 "os"
225 "path/filepath"
226 )
227@@ -138,3 +139,21 @@
228 }
229 return e, nil
230 }
231+
232+// BootstrapConfig returns an environment configuration suitable for
233+// priming the juju state using the given provider, configuration and
234+// tools.
235+//
236+// The returned configuration contains no secret attributes.
237+func BootstrapConfig(p EnvironProvider, cfg *config.Config, tools *state.Tools) (*config.Config, error) {
238+ secrets, err := p.SecretAttrs(cfg)
239+ if err != nil {
240+ return nil, err
241+ }
242+ m := cfg.AllAttrs()
243+ for k, _ := range secrets {
244+ delete(m, k)
245+ }
246+ m["agent-version"] = tools.Number.String()
247+ return config.New(m)
248+}
249
250=== modified file 'environs/config/config.go'
251--- environs/config/config.go 2012-08-30 12:21:22 +0000
252+++ environs/config/config.go 2012-08-31 02:04:18 +0000
253@@ -15,6 +15,8 @@
254 // Fields that are common to all environment providers are verified,
255 // and authorized-keys-path is also translated into authorized-keys
256 // by loading the content from respective file.
257+//
258+// The required keys are: "name", "type", "default-series" and "authorized-keys".
259 func New(attrs map[string]interface{}) (*Config, error) {
260 m, err := checker.Coerce(attrs, nil)
261 if err != nil {
262@@ -63,7 +65,7 @@
263 return c, nil
264 }
265
266-// Type returns the enviornment type.
267+// Type returns the environment type.
268 func (c *Config) Type() string {
269 return c.m["type"].(string)
270 }
271
272=== modified file 'environs/dummy/environs.go'
273--- environs/dummy/environs.go 2012-08-30 02:34:58 +0000
274+++ environs/dummy/environs.go 2012-08-31 02:04:18 +0000
275@@ -29,6 +29,7 @@
276 "launchpad.net/juju-core/schema"
277 "launchpad.net/juju-core/state"
278 "launchpad.net/juju-core/testing"
279+ "launchpad.net/juju-core/version"
280 "net"
281 "net/http"
282 "os"
283@@ -179,10 +180,24 @@
284 }
285 s.storage = newStorage(s, "/"+name+"/private")
286 s.publicStorage = newStorage(s, "/"+name+"/public")
287+ putFakeTools(s.publicStorage)
288 s.listen()
289 return s
290 }
291
292+// putFakeTools writes something
293+// that looks like a tools archive so Bootstrap can
294+// find some tools and initialise the state correctly.
295+func putFakeTools(s environs.StorageWriter) {
296+ log.Printf("putting fake tools")
297+ path := environs.ToolsStoragePath(version.Current)
298+ toolsContents := "tools archive, honest guv"
299+ err := s.Put(path, strings.NewReader(toolsContents), int64(len(toolsContents)))
300+ if err != nil {
301+ panic(err)
302+ }
303+}
304+
305 // listen starts a network listener listening for http
306 // requests to retrieve files in the state's storage.
307 func (s *environState) listen() {
308@@ -340,8 +355,16 @@
309 if err := e.checkBroken("Bootstrap"); err != nil {
310 return err
311 }
312+ var tools *state.Tools
313+ var err error
314 if uploadTools {
315- _, err := environs.PutTools(e.Storage(), nil)
316+ tools, err = environs.PutTools(e.Storage(), nil)
317+ if err != nil {
318+ return err
319+ }
320+ } else {
321+ flags := environs.HighestVersion|environs.CompatVersion
322+ tools, err = environs.FindTools(e, version.Current, flags)
323 if err != nil {
324 return err
325 }
326@@ -354,13 +377,11 @@
327 }
328 if e.ecfg().zookeeper() {
329 info := stateInfo()
330- config := map[string]interface{}{
331- "type": "dummy",
332- "zookeeper": true,
333- "name": e.ecfg().Name(),
334- "authorized-keys": e.ecfg().AuthorizedKeys(),
335+ cfg, err := environs.BootstrapConfig(&providerInstance, e.ecfg().Config, tools)
336+ if err != nil {
337+ return err
338 }
339- st, err := state.Initialize(info, config)
340+ st, err := state.Initialize(info, cfg.AllAttrs())
341 if err != nil {
342 panic(err)
343 }
344
345=== modified file 'environs/ec2/ec2.go'
346--- environs/ec2/ec2.go 2012-08-29 23:08:25 +0000
347+++ environs/ec2/ec2.go 2012-08-31 02:04:18 +0000
348@@ -123,25 +123,6 @@
349 return m, nil
350 }
351
352-func (environProvider) publicAttrs(cfg *config.Config) (map[string]interface{}, error) {
353- m := make(map[string]interface{})
354- ecfg, err := providerInstance.newConfig(cfg)
355- if err != nil {
356- return nil, err
357- }
358- for k, v := range ecfg.UnknownAttrs() {
359- m[k] = v
360- }
361- secret, err := providerInstance.SecretAttrs(cfg)
362- if err != nil {
363- return nil, err
364- }
365- for k, _ := range secret {
366- delete(m, k)
367- }
368- return m, nil
369-}
370-
371 func (environProvider) PublicAddress() (string, error) {
372 return fetchMetadata("public-hostname")
373 }
374@@ -240,9 +221,14 @@
375 if err != nil {
376 return fmt.Errorf("cannot upload tools: %v", err)
377 }
378+ } else {
379+ flags := environs.HighestVersion|environs.CompatVersion
380+ tools, err = environs.FindTools(e, version.Current, flags)
381+ if err != nil {
382+ return fmt.Errorf("cannot find tools: %v", err)
383+ }
384 }
385-
386- config, err := providerInstance.publicAttrs(e.Config())
387+ config, err := environs.BootstrapConfig(providerInstance, e.Config(), tools)
388 if err != nil {
389 return fmt.Errorf("unable to determine inital configuration: %v", err)
390 }
391@@ -311,7 +297,7 @@
392 return e.startInstance(machineId, info, tools, false, nil)
393 }
394
395-func (e *environ) userData(machineId int, info *state.Info, tools *state.Tools, master bool, config map[string]interface{}) ([]byte, error) {
396+func (e *environ) userData(machineId int, info *state.Info, tools *state.Tools, master bool, config *config.Config) ([]byte, error) {
397 cfg := &cloudinit.MachineConfig{
398 Provisioner: master,
399 StateServer: master,
400@@ -333,10 +319,11 @@
401 // startInstance is the internal version of StartInstance, used by Bootstrap
402 // as well as via StartInstance itself. If master is true, a bootstrap
403 // instance will be started.
404-func (e *environ) startInstance(machineId int, info *state.Info, tools *state.Tools, master bool, config map[string]interface{}) (environs.Instance, error) {
405+func (e *environ) startInstance(machineId int, info *state.Info, tools *state.Tools, master bool, config *config.Config) (environs.Instance, error) {
406 if tools == nil {
407 var err error
408- tools, err = environs.FindTools(e, version.Current)
409+ flags := environs.HighestVersion|environs.CompatVersion
410+ tools, err = environs.FindTools(e, version.Current, flags)
411 if err != nil {
412 return nil, err
413 }
414
415=== modified file 'environs/jujutest/test.go'
416--- environs/jujutest/test.go 2012-07-06 05:32:25 +0000
417+++ environs/jujutest/test.go 2012-08-31 02:04:18 +0000
418@@ -4,6 +4,7 @@
419 . "launchpad.net/gocheck"
420 "launchpad.net/juju-core/environs"
421 "launchpad.net/juju-core/state"
422+ "launchpad.net/juju-core/testing"
423 "time"
424 )
425
426@@ -19,6 +20,7 @@
427 // is opened once for each test, and some potentially expensive operations
428 // may be executed.
429 type Tests struct {
430+ testing.LoggingSuite
431 Environs *environs.Environs
432 Name string
433 Env environs.Environ
434@@ -32,25 +34,22 @@
435 return e
436 }
437
438-func (t *Tests) SetUpSuite(*C) {
439-}
440-
441-func (t *Tests) TearDownSuite(*C) {
442-}
443-
444 func (t *Tests) SetUpTest(c *C) {
445+ t.LoggingSuite.SetUpTest(c)
446 t.Env = t.Open(c)
447 }
448
449-func (t *Tests) TearDownTest(*C) {
450+func (t *Tests) TearDownTest(c *C) {
451 t.Env.Destroy(nil)
452 t.Env = nil
453+ t.LoggingSuite.TearDownTest(c)
454 }
455
456 // LiveTests contains tests that are designed to run against a live server
457 // (e.g. Amazon EC2). The Environ is opened once only for all the tests
458 // in the suite, stored in Env, and Destroyed after the suite has completed.
459 type LiveTests struct {
460+ testing.LoggingSuite
461 Environs *environs.Environs
462 Name string
463 Env environs.Environ
464@@ -70,12 +69,20 @@
465 }
466
467 func (t *LiveTests) SetUpSuite(c *C) {
468+ t.LoggingSuite.SetUpSuite(c)
469 e, err := t.Environs.Open(t.Name)
470 c.Assert(err, IsNil, Commentf("opening environ %q", t.Name))
471 c.Assert(e, NotNil)
472 t.Env = e
473 }
474
475+func (t *LiveTests) TearDownSuite(c *C) {
476+ err := t.Env.Destroy(nil)
477+ c.Check(err, IsNil)
478+ t.Env = nil
479+ t.LoggingSuite.TearDownSuite(c)
480+}
481+
482 func (t *LiveTests) BootstrapOnce(c *C) {
483 if t.bootstrapped {
484 return
485@@ -90,15 +97,3 @@
486 c.Assert(err, IsNil)
487 t.bootstrapped = false
488 }
489-
490-func (t *LiveTests) TearDownSuite(c *C) {
491- err := t.Env.Destroy(nil)
492- c.Check(err, IsNil)
493- t.Env = nil
494-}
495-
496-func (t *LiveTests) SetUpTest(*C) {
497-}
498-
499-func (t *LiveTests) TearDownTest(*C) {
500-}
501
502=== modified file 'environs/tools.go'
503--- environs/tools.go 2012-08-29 16:54:01 +0000
504+++ environs/tools.go 2012-08-31 02:04:18 +0000
505@@ -204,27 +204,29 @@
506
507 // BestTools returns the most recent version
508 // from the set of tools in the ToolsList that are
509-// compatible with the given version, and with a version
510-// number <= vers.Number, or nil if no such tools are found.
511-// If dev is true, it will consider development versions of the tools
512-// even if vers is not a development version.
513-func BestTools(list *ToolsList, vers version.Binary, dev bool) *state.Tools {
514- if tools := bestTools(list.Private, vers, dev); tools != nil {
515+// compatible with the given version, using flags
516+// to determine possible candidates.
517+// It returns nil if no such tools are found.
518+func BestTools(list *ToolsList, vers version.Binary, flags ToolsSearchFlags) *state.Tools {
519+ if flags&CompatVersion == 0 {
520+ panic("CompatVersion not implemented")
521+ }
522+ if tools := bestTools(list.Private, vers, flags); tools != nil {
523 return tools
524 }
525- return bestTools(list.Public, vers, dev)
526+ return bestTools(list.Public, vers, flags)
527 }
528
529 // bestTools is like BestTools but operates on a single list of tools.
530-func bestTools(toolsList []*state.Tools, vers version.Binary, dev bool) *state.Tools {
531+func bestTools(toolsList []*state.Tools, vers version.Binary, flags ToolsSearchFlags) *state.Tools {
532 var bestTools *state.Tools
533- allowDev := vers.IsDev() || dev
534+ allowDev := vers.IsDev() || flags&DevVersion != 0
535 for _, t := range toolsList {
536 if t.Major != vers.Major ||
537 t.Series != vers.Series ||
538 t.Arch != vers.Arch ||
539 !allowDev && t.IsDev() ||
540- vers.Number.Less(t.Number) {
541+ flags&HighestVersion == 0 && vers.Number.Less(t.Number) {
542 continue
543 }
544 if bestTools == nil || bestTools.Number.Less(t.Number) {
545@@ -379,19 +381,38 @@
546 return path.Join(VarDir, "tools", agentName)
547 }
548
549+// ToolsSearchFlags gives options when searching
550+// for tools.
551+type ToolsSearchFlags int
552+const (
553+ // HighestVersion specifies that the highest (minor, patch) version number should
554+ // be chosen. The default is to choose a version no higher than specified.
555+ HighestVersion ToolsSearchFlags = 1<<iota
556+
557+ // DevVersion specifies "dev mode" - it allows a development version to
558+ // be chosen even when the specified version is not a development
559+ // version.
560+ DevVersion
561+
562+ // CompatVersion specifies that the major version number
563+ // must be the same as specified. It is not yet implemented.
564+ CompatVersion
565+)
566+
567 // FindTools tries to find a set of tools compatible with the given
568-// version from the given environment. The latest version found with a
569-// number <= vers.Number will be used.
570+// version from the given environment, using flags to determine
571+// possible candidates.
572 //
573 // If no tools are found and there's no other error, a NotFoundError is
574 // returned. If there's anything compatible in the environ's Storage,
575 // it gets precedence over anything in its PublicStorage.
576-func FindTools(env Environ, vers version.Binary) (*state.Tools, error) {
577+func FindTools(env Environ, vers version.Binary, flags ToolsSearchFlags) (*state.Tools, error) {
578 toolsList, err := ListTools(env, vers.Major)
579 if err != nil {
580 return nil, err
581 }
582- tools := BestTools(toolsList, vers, false)
583+ log.Printf("findTools got tools list %v", toolsList)
584+ tools := BestTools(toolsList, vers, flags)
585 if tools == nil {
586 return tools, &NotFoundError{fmt.Errorf("no compatible tools found")}
587 }
588
589=== modified file 'environs/tools_test.go'
590--- environs/tools_test.go 2012-08-29 16:54:01 +0000
591+++ environs/tools_test.go 2012-08-31 02:04:18 +0000
592@@ -384,6 +384,7 @@
593
594 var findToolsTests = []struct {
595 version version.Number // version to assume is current for the test.
596+ flags environs.ToolsSearchFlags
597 contents []string // names in private storage.
598 publicContents []string // names in public storage.
599 expect string // the name we expect to find (if no error).
600@@ -391,35 +392,34 @@
601 }{{
602 // current version should be satisfied by current tools path.
603 version: version.Current.Number,
604+ flags: environs.CompatVersion,
605 contents: []string{environs.ToolsStoragePath(version.Current)},
606 expect: environs.ToolsStoragePath(version.Current),
607 }, {
608- // major versions don't match.
609- version: version.MustParse("1.0.0"),
610+ // highest version of tools is chosen.
611+ version: version.MustParse("0.0.0"),
612+ flags: environs.HighestVersion|environs.DevVersion|environs.CompatVersion,
613 contents: []string{
614 toolsStoragePath("0.0.9"),
615- },
616- err: "no compatible tools found",
617-}, {
618- // major versions don't match.
619- version: version.MustParse("1.0.0"),
620- contents: []string{
621- toolsStoragePath("2.0.9"),
622- },
623- err: "no compatible tools found",
624+ toolsStoragePath("0.1.9"),
625+ },
626+ expect: toolsStoragePath("0.1.9"),
627 }, {
628 // fall back to public storage when nothing found in private.
629- version: version.MustParse("1.0.0"),
630+ version: version.MustParse("1.0.2"),
631+ flags: environs.DevVersion|environs.CompatVersion,
632 contents: []string{
633 toolsStoragePath("0.0.9"),
634 },
635 publicContents: []string{
636 toolsStoragePath("1.0.0"),
637+ toolsStoragePath("1.0.1"),
638 },
639- expect: "public-" + toolsStoragePath("1.0.0"),
640+ expect: "public-" + toolsStoragePath("1.0.1"),
641 }, {
642 // always use private storage in preference to public storage.
643 version: version.MustParse("1.9.0"),
644+ flags: environs.DevVersion|environs.CompatVersion,
645 contents: []string{
646 toolsStoragePath("1.0.2"),
647 },
648@@ -428,43 +428,9 @@
649 },
650 expect: toolsStoragePath("1.0.2"),
651 }, {
652- // we'll use an earlier version if the major version number matches.
653- version: version.MustParse("1.99.99"),
654- contents: []string{
655- toolsStoragePath("1.0.0"),
656- },
657- expect: toolsStoragePath("1.0.0"),
658-}, {
659- // check that version comparing is numeric, not alphabetical.
660- version: version.MustParse("1.0.99"),
661- contents: []string{
662- toolsStoragePath("1.0.9"),
663- toolsStoragePath("1.0.10"),
664- toolsStoragePath("1.0.11"),
665- },
666- expect: toolsStoragePath("1.0.11"),
667-}, {
668- // minor version wins over patch version.
669- version: version.MustParse("1.99.99"),
670- contents: []string{
671- toolsStoragePath("1.9.11"),
672- toolsStoragePath("1.10.10"),
673- toolsStoragePath("1.11.9"),
674- },
675- expect: toolsStoragePath("1.11.9"),
676-}, {
677- // only earlier versions are chosen.
678- version: version.MustParse("1.10.9"),
679- contents: []string{
680- toolsStoragePath("1.9.10"),
681- toolsStoragePath("1.9.11"),
682- toolsStoragePath("1.10.10"),
683- toolsStoragePath("1.11.9"),
684- },
685- expect: toolsStoragePath("1.9.11"),
686-}, {
687 // mismatching series or architecture is ignored.
688 version: version.MustParse("1.0.0"),
689+ flags: environs.CompatVersion,
690 contents: []string{
691 environs.ToolsStoragePath(version.Binary{
692 Number: version.MustParse("1.9.9"),
693@@ -508,10 +474,11 @@
694 Series: version.Current.Series,
695 Arch: version.Current.Arch,
696 }
697- tools, err := environs.FindTools(t.env, vers)
698+ tools, err := environs.FindTools(t.env, vers, tt.flags)
699 if tt.err != "" {
700 c.Assert(err, ErrorMatches, tt.err)
701 } else {
702+ c.Assert(err, IsNil)
703 assertURLContents(c, tools.URL, tt.expect)
704 }
705 t.env.Destroy(nil)
706@@ -613,15 +580,53 @@
707 var bestToolsTests = []struct {
708 list *environs.ToolsList
709 vers version.Binary
710- dev bool
711+ flags environs.ToolsSearchFlags
712 expect *state.Tools
713+ expectDev *state.Tools
714+ expectHighest *state.Tools
715+ expectDevHighest *state.Tools
716 }{{
717- &environs.ToolsList{},
718- binaryVersion("1.2.3-precise-amd64"),
719- true,
720- nil,
721-}, {
722- &environs.ToolsList{
723+ // 0. Check that we don't get anything from an empty list.
724+ list: &environs.ToolsList{},
725+ vers: binaryVersion("1.2.3-precise-amd64"),
726+ flags: environs.DevVersion|environs.CompatVersion,
727+ expect: nil,
728+}, {
729+ // 1. Check that we can choose the same development version.
730+ list: &environs.ToolsList{
731+ Private: []*state.Tools{
732+ newTools("1.0.0-precise-amd64", ""),
733+ },
734+ },
735+ vers: binaryVersion("1.0.0-precise-amd64"),
736+ expect: newTools("1.0.0-precise-amd64", ""),
737+ expectDev: newTools("1.0.0-precise-amd64", ""),
738+ expectHighest: newTools("1.0.0-precise-amd64", ""),
739+ expectDevHighest: newTools("1.0.0-precise-amd64", ""),
740+}, {
741+ // 2. Check that major versions need to match.
742+ list: &environs.ToolsList{
743+ Private: []*state.Tools{
744+ newTools("2.0.0-precise-amd64", ""),
745+ newTools("6.0.0-precise-amd64", ""),
746+ },
747+ },
748+ vers: binaryVersion("4.0.0-precise-amd64"),
749+}, {
750+ // 3. Check that we can choose the same released version.
751+ list: &environs.ToolsList{
752+ Private: []*state.Tools{
753+ newTools("2.0.0-precise-amd64", ""),
754+ },
755+ },
756+ vers: binaryVersion("2.0.0-precise-amd64"),
757+ expect: newTools("2.0.0-precise-amd64", ""),
758+ expectDev: newTools("2.0.0-precise-amd64", ""),
759+ expectHighest: newTools("2.0.0-precise-amd64", ""),
760+ expectDevHighest: newTools("2.0.0-precise-amd64", ""),
761+}, {
762+ // 4. Check that different arch/series are ignored.
763+ list: &environs.ToolsList{
764 Private: []*state.Tools{
765 newTools("1.2.3-precise-amd64", ""),
766 newTools("1.2.4-precise-amd64", ""),
767@@ -631,29 +636,31 @@
768 newTools("2.2.3-precise-amd64", ""),
769 },
770 },
771- binaryVersion("1.9.4-precise-amd64"),
772- true,
773- newTools("1.3.4-precise-amd64", ""),
774+ vers: binaryVersion("1.9.4-precise-amd64"),
775+ expect: newTools("1.3.4-precise-amd64", ""),
776+ expectDev: newTools("1.3.4-precise-amd64", ""),
777+ expectHighest: newTools("1.3.4-precise-amd64", ""),
778+ expectDevHighest: newTools("1.3.4-precise-amd64", ""),
779 }, {
780- // Check that we can't upgrade to a dev version from
781+ // 5. Check that we can't upgrade to a dev version from
782 // a release version if dev is false.
783- &environs.ToolsList{
784+ list: &environs.ToolsList{
785 Private: []*state.Tools{
786 newTools("2.2.3-precise-amd64", ""),
787 newTools("2.2.4-precise-amd64", ""),
788 newTools("2.3.4-precise-amd64", ""),
789- newTools("2.4.4-precise-i386", ""),
790- newTools("2.4.5-quantal-i386", ""),
791 newTools("3.2.3-precise-amd64", ""),
792 },
793 },
794- binaryVersion("2.8.8-precise-amd64"),
795- false,
796- newTools("2.2.4-precise-amd64", ""),
797+ vers: binaryVersion("2.8.8-precise-amd64"),
798+ expect: newTools("2.2.4-precise-amd64", ""),
799+ expectDev: newTools("2.3.4-precise-amd64", ""),
800+ expectHighest: newTools("2.2.4-precise-amd64", ""),
801+ expectDevHighest: newTools("2.3.4-precise-amd64", ""),
802 }, {
803- // Check that we can upgrade to a release version from
804+ // 6. Check that we can upgrade to a release version from
805 // a dev version if dev is false.
806- &environs.ToolsList{
807+ list: &environs.ToolsList{
808 Private: []*state.Tools{
809 newTools("2.2.3-precise-amd64", ""),
810 newTools("2.2.4-precise-amd64", ""),
811@@ -662,12 +669,14 @@
812 newTools("3.2.3-precise-amd64", ""),
813 },
814 },
815- binaryVersion("2.8.8-precise-amd64"),
816- false,
817- newTools("2.4.4-precise-amd64", ""),
818+ vers: binaryVersion("2.8.8-precise-amd64"),
819+ expect: newTools("2.4.4-precise-amd64", ""),
820+ expectDev: newTools("2.4.4-precise-amd64", ""),
821+ expectHighest: newTools("2.4.4-precise-amd64", ""),
822+ expectDevHighest: newTools("2.4.4-precise-amd64", ""),
823 }, {
824- // Check that a different major version works ok.
825- &environs.ToolsList{
826+ // 7. Check that a different minor version works ok.
827+ list: &environs.ToolsList{
828 Private: []*state.Tools{
829 newTools("1.2.3-precise-amd64", ""),
830 newTools("1.2.4-precise-amd64", ""),
831@@ -677,54 +686,96 @@
832 newTools("2.2.3-precise-amd64", ""),
833 },
834 },
835- binaryVersion("2.8.8-precise-amd64"),
836- true,
837- newTools("2.2.3-precise-amd64", ""),
838+ vers: binaryVersion("2.8.8-precise-amd64"),
839+ expect: nil,
840+ expectDev: newTools("2.2.3-precise-amd64", ""),
841+ expectHighest: nil,
842+ expectDevHighest: newTools("2.2.3-precise-amd64", ""),
843 }, {
844- // Check that the private tools are chosen even though
845+ // 8. Check that the private tools are chosen even though
846 // they have a lower version number.
847- &environs.ToolsList{
848+ list: &environs.ToolsList{
849 Private: []*state.Tools{
850- newTools("1.2.3-precise-amd64", ""),
851+ newTools("1.2.2-precise-amd64", ""),
852 },
853 Public: []*state.Tools{
854 newTools("1.2.4-precise-amd64", ""),
855 },
856 },
857- binaryVersion("1.8.8-precise-amd64"),
858- true,
859- newTools("1.2.3-precise-amd64", ""),
860+ vers: binaryVersion("1.8.8-precise-amd64"),
861+ expect: newTools("1.2.2-precise-amd64", ""),
862+ expectDev: newTools("1.2.2-precise-amd64", ""),
863+ expectHighest: newTools("1.2.2-precise-amd64", ""),
864+ expectDevHighest: newTools("1.2.2-precise-amd64", ""),
865 }, {
866- // Check that the public tools can be chosen when
867+ // 9. Check that the public tools can be chosen when
868 // there are no private tools.
869- &environs.ToolsList{
870+ list: &environs.ToolsList{
871 Public: []*state.Tools{
872 newTools("1.2.4-precise-amd64", ""),
873 },
874 },
875- binaryVersion("1.8.9-precise-amd64"),
876- true,
877- newTools("1.2.4-precise-amd64", ""),
878-}, {
879- // Check that we don't choose a version later
880- // than requested.
881- &environs.ToolsList{
882- Public: []*state.Tools{
883- newTools("1.2.2-precise-amd64", ""),
884- newTools("1.2.3-precise-amd64", ""),
885- newTools("1.3.4-precise-amd64", ""),
886- },
887- },
888- binaryVersion("1.3.3-precise-amd64"),
889- true,
890- newTools("1.2.3-precise-amd64", ""),
891+ vers: binaryVersion("1.8.9-precise-amd64"),
892+ expect: newTools("1.2.4-precise-amd64", ""),
893+ expectDev: newTools("1.2.4-precise-amd64", ""),
894+ expectHighest: newTools("1.2.4-precise-amd64", ""),
895+ expectDevHighest: newTools("1.2.4-precise-amd64", ""),
896+}, {
897+ // 10. One test giving different values for all flag combinations.
898+ list: &environs.ToolsList{
899+ Public: []*state.Tools{
900+ newTools("0.2.0-precise-amd64", ""),
901+ newTools("0.2.1-precise-amd64", ""),
902+ newTools("0.4.2-precise-amd64", ""),
903+ newTools("0.4.3-precise-amd64", ""),
904+ },
905+ },
906+ vers: binaryVersion("0.2.2-precise-amd64"),
907+ expect: newTools("0.2.0-precise-amd64", ""),
908+ expectDev: newTools("0.2.1-precise-amd64", ""),
909+ expectHighest: newTools("0.4.2-precise-amd64", ""),
910+ expectDevHighest: newTools("0.4.3-precise-amd64", ""),
911+}, {
912+ // 11. check that version comparing is numeric, not alphabetical.
913+ list: &environs.ToolsList{
914+ Public: []*state.Tools{
915+ newTools("0.0.9-precise-amd64", ""),
916+ newTools("0.0.10-precise-amd64", ""),
917+ newTools("0.0.11-precise-amd64", ""),
918+ },
919+ },
920+ vers: binaryVersion("0.0.98-precise-amd64"),
921+ expect: newTools("0.0.10-precise-amd64", ""),
922+ expectDev: newTools("0.0.11-precise-amd64", ""),
923+ expectHighest: newTools("0.0.10-precise-amd64", ""),
924+ expectDevHighest: newTools("0.0.11-precise-amd64", ""),
925+}, {
926+ // 12. check that minor version wins over patch version.
927+ list: &environs.ToolsList{
928+ Public: []*state.Tools{
929+ newTools("0.9.11-precise-amd64", ""),
930+ newTools("0.10.10-precise-amd64", ""),
931+ newTools("0.11.9-precise-amd64", ""),
932+ },
933+ },
934+ vers: binaryVersion("0.10.10-precise-amd64"),
935+ expect: newTools("0.10.10-precise-amd64", ""),
936+ expectDev: newTools("0.10.10-precise-amd64", ""),
937+ expectHighest: newTools("0.10.10-precise-amd64", ""),
938+ expectDevHighest: newTools("0.11.9-precise-amd64", ""),
939 },
940 }
941
942 func (t *ToolsSuite) TestBestTools(c *C) {
943 for i, t := range bestToolsTests {
944 c.Logf("test %d", i)
945- tools := environs.BestTools(t.list, t.vers, t.dev)
946+ tools := environs.BestTools(t.list, t.vers, environs.CompatVersion)
947 c.Assert(tools, DeepEquals, t.expect)
948+ tools = environs.BestTools(t.list, t.vers, environs.DevVersion|environs.CompatVersion)
949+ c.Assert(tools, DeepEquals, t.expectDev)
950+ tools = environs.BestTools(t.list, t.vers, environs.HighestVersion|environs.CompatVersion)
951+ c.Assert(tools, DeepEquals, t.expectHighest)
952+ tools = environs.BestTools(t.list, t.vers, environs.DevVersion|environs.HighestVersion|environs.CompatVersion)
953+ c.Assert(tools, DeepEquals, t.expectDevHighest)
954 }
955 }
956
957=== modified file 'juju/conn.go'
958--- juju/conn.go 2012-08-30 13:21:00 +0000
959+++ juju/conn.go 2012-08-31 02:04:18 +0000
960@@ -82,11 +82,31 @@
961 // from the local configuration.
962 func (c *Conn) updateSecrets() error {
963 cfg := c.Environ.Config()
964+<<<<<<< TREE
965 // This is wrong. This will _always_ overwrite the secrets
966 // in the state with the local secrets. To fix this properly
967 // we need to ensure that the config, minus secrets, is always
968 // pushed on bootstrap, then we can fill in the secrets here.
969 return c.state.SetEnvironConfig(cfg)
970+=======
971+ env, err := c.state.EnvironConfig()
972+ if err != nil {
973+ return err
974+ }
975+ for k, v := range cfg.UnknownAttrs() {
976+ if _, exists := env.Get(k); !exists {
977+ env.Set(k, v)
978+ }
979+ }
980+ n, err := env.Write()
981+ if err != nil {
982+ return err
983+ }
984+ if len(n) > 0 {
985+ log.Debugf("Updating %d secret(s) in environment %q", len(n), c.Environ.Name())
986+ }
987+ return nil
988+>>>>>>> MERGE-SOURCE
989 }
990
991 // Close terminates the connection to the environment and releases
992
993=== modified file 'juju/conn_test.go'
994--- juju/conn_test.go 2012-08-30 13:21:00 +0000
995+++ juju/conn_test.go 2012-08-31 02:04:18 +0000
996@@ -92,7 +92,7 @@
997 c.Assert(err, ErrorMatches, "dummy environment not bootstrapped")
998 }
999
1000-func (cs *ConnSuite) TestConnStateSecretsSideEffect(c *C) {
1001+func (*ConnSuite) TestConnStateSecretsSideEffect(c *C) {
1002 env, err := environs.NewFromAttrs(map[string]interface{}{
1003 "name": "erewhemos",
1004 "type": "dummy",
1005@@ -129,6 +129,29 @@
1006 c.Assert(cfg.UnknownAttrs()["secret"], Equals, "pork")
1007 }
1008
1009+func (cs *ConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *C) {
1010+ cs.TestConnStateSecretsSideEffect(c)
1011+ conn, err := juju.NewConnFromAttrs(map[string]interface{}{
1012+ "name": "erewhemos",
1013+ "type": "dummy",
1014+ "zookeeper": true,
1015+ "authorized-keys": "i-am-a-key",
1016+ "secret": "squirrel",
1017+ })
1018+ c.Assert(err, IsNil)
1019+ defer conn.Close()
1020+ st, err := conn.State()
1021+ c.Assert(err, IsNil)
1022+ cfg, err := st.EnvironConfig()
1023+ c.Assert(err, IsNil)
1024+ err = cfg.Read()
1025+ c.Assert(err, IsNil)
1026+ // check that the secret has not changed
1027+ secret, ok := cfg.Get("secret")
1028+ c.Assert(ok, Equals, true)
1029+ c.Assert(secret, Equals, "pork")
1030+}
1031+
1032 func (*ConnSuite) TestValidRegexps(c *C) {
1033 assertService := func(s string, expect bool) {
1034 c.Assert(juju.ValidService.MatchString(s), Equals, expect)
1035
1036=== modified file 'state/machine.go'
1037--- state/machine.go 2012-08-17 12:10:32 +0000
1038+++ state/machine.go 2012-08-31 02:04:18 +0000
1039@@ -71,7 +71,7 @@
1040 "instance id of machine %v", m.String())
1041 if _, ok := err.(*NotFoundError); ok || (err == nil && instanceId == "") {
1042 return "", &NotFoundError{
1043- fmt.Sprintf("instance id for machine %d is not set", m.Id()),
1044+ fmt.Sprintf("instance id for machine %d", m.Id()),
1045 }
1046 }
1047 return instanceId, err

Subscribers

People subscribed via source and target branches