Merge lp:~axwalk/juju-core/setenvironconfig-delta 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: 2124
Proposed branch: lp:~axwalk/juju-core/setenvironconfig-delta
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 403 lines (+68/-52)
15 files modified
cmd/juju/environment.go (+1/-1)
cmd/juju/upgradejuju_test.go (+6/-6)
cmd/jujud/agent_test.go (+3/-3)
environs/testing/tools.go (+1/-1)
juju/conn.go (+2/-2)
juju/testing/repo.go (+5/-5)
juju/testing/utils.go (+1/-1)
state/api/provisioner/provisioner_test.go (+2/-2)
state/apiserver/client/client.go (+1/-1)
state/initialize_test.go (+2/-2)
state/state.go (+14/-5)
state/state_test.go (+6/-4)
state/testing/config.go (+2/-2)
worker/firewaller/firewaller_test.go (+3/-2)
worker/provisioner/provisioner_test.go (+19/-15)
To merge this branch: bzr merge lp:~axwalk/juju-core/setenvironconfig-delta
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+197989@code.launchpad.net

Commit message

state: update SetEnvironConfig to take new and old

SetEnvironConfig is updated to take both new and old
configurations, and a delta computed from these. The
only functional change for now is that attributes that
exist in old but not in new are now deleted from state.

To do this right, we should also check that the
settings in state are the same as the "old" config,
so the delta computed is what the user expects. I have
not done this part yet, as it is a fairly big change
and I am not confident I understand all of the
repercussions.

https://codereview.appspot.com/38180043/

Description of the change

state: update SetEnvironConfig to take new and old

SetEnvironConfig is updated to take both new and old
configurations, and a delta computed from these. The
only functional change for now is that attributes that
exist in old but not in new are now deleted from state.

To do this right, we should also check that the
settings in state are the same as the "old" config,
so the delta computed is what the user expects. I have
not done this part yet, as it is a fairly big change
and I am not confident I understand all of the
repercussions.

https://codereview.appspot.com/38180043/

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

Reviewers: mp+197989_code.launchpad.net,

Message:
Please take a look.

Description:
state: update SetEnvironConfig to take new and old

SetEnvironConfig is updated to take both new and old
configurations, and a delta computed from these. The
only functional change for now is that attributes that
exist in old but not in new are now deleted from state.

To do this right, we should also check that the
settings in state are the same as the "old" config,
so the delta computed is what the user expects. I have
not done this part yet, as it is a fairly big change
and I am not confident I understand all of the
repercussions.

https://code.launchpad.net/~axwalk/juju-core/setenvironconfig-delta/+merge/197989

(do not edit description out of merge proposal)

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

Affected files (+70, -52 lines):
   A [revision details]
   M cmd/juju/environment.go
   M cmd/juju/upgradejuju_test.go
   M cmd/jujud/agent_test.go
   M environs/testing/tools.go
   M juju/conn.go
   M juju/testing/repo.go
   M juju/testing/utils.go
   M state/api/provisioner/provisioner_test.go
   M state/apiserver/client/client.go
   M state/initialize_test.go
   M state/state.go
   M state/state_test.go
   M state/testing/config.go
   M worker/firewaller/firewaller_test.go
   M worker/provisioner/provisioner_test.go

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

LGTM with one thought below. Thanks!

https://codereview.appspot.com/38180043/diff/1/state/state.go
File state/state.go (right):

https://codereview.appspot.com/38180043/diff/1/state/state.go#newcode272
state/state.go:272: settings.Update(newattrs)
To do this right, shouldn't we only update
those settings that have changed between old
and new?

Otherwise if two people concurrently do:

  juju set-environment foo=a
  juju set-environment bar=b

then one of them may overwrite the other.

I suspect that the only way to do this correctly is
to have something like:

func (st *State) UpdateEnvironConfig(func (*config.Config)
(*config.Config, error)) error

which would retrieve the configuration from state, call the given
function to determine the new one and verify that it's valid, and apply
the new configuration to the state, checking that the settings have not
changed in the meantime.

I guess that's a bigger change though, and the change in this CL is at
least a significant improvement on what we had before, so I'm OK with it
for now.

https://codereview.appspot.com/38180043/

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

Thanks for the review

https://codereview.appspot.com/38180043/diff/1/state/state.go
File state/state.go (right):

https://codereview.appspot.com/38180043/diff/1/state/state.go#newcode272
state/state.go:272: settings.Update(newattrs)
On 2013/12/06 08:46:23, rog wrote:
> To do this right, shouldn't we only update
> those settings that have changed between old
> and new?

> Otherwise if two people concurrently do:

> juju set-environment foo=a
> juju set-environment bar=b

> then one of them may overwrite the other.

I don't think it will overwrite in that example, but I may be mistaken.
Updates and deletions are translated to $set/$unset transaction
operations. Deletions are only relative to the "old" config, which is
known to the caller. In both the cases you give, neither one will cause
a deletion.

> I suspect that the only way to do this correctly is
> to have something like:

> func (st *State) UpdateEnvironConfig(func (*config.Config)
(*config.Config,
> error)) error

> which would retrieve the configuration from state, call the given
> function to determine the new one and verify that it's valid, and
apply the new
> configuration to the state, checking that the settings have not
changed in the
> meantime.

> I guess that's a bigger change though, and the change in this CL is at
least a
> significant improvement on what we had before, so I'm OK with it for
now.

https://codereview.appspot.com/38180043/

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

On 2013/12/06 09:01:26, axw wrote:
> Thanks for the review

> https://codereview.appspot.com/38180043/diff/1/state/state.go
> File state/state.go (right):

https://codereview.appspot.com/38180043/diff/1/state/state.go#newcode272
> state/state.go:272: settings.Update(newattrs)
> On 2013/12/06 08:46:23, rog wrote:
> > To do this right, shouldn't we only update
> > those settings that have changed between old
> > and new?
> >
> > Otherwise if two people concurrently do:
> >
> > juju set-environment foo=a
> > juju set-environment bar=b
> >
> > then one of them may overwrite the other.

> I don't think it will overwrite in that example, but I may be
mistaken. Updates
> and deletions are translated to $set/$unset transaction operations.
Deletions
> are only relative to the "old" config, which is known to the caller.
In both the
> cases you give, neither one will cause a deletion.

Responding to myself here: rog meant that we could overwrite foo/bar
with an old value, on account of us updating everything
(Update(AllAttrs)).

> > I suspect that the only way to do this correctly is
> > to have something like:
> >
> > func (st *State) UpdateEnvironConfig(func (*config.Config)
(*config.Config,
> > error)) error
> >
> > which would retrieve the configuration from state, call the given
> > function to determine the new one and verify that it's valid, and
apply the
> new
> > configuration to the state, checking that the settings have not
changed in the
> > meantime.
> >
> > I guess that's a bigger change though, and the change in this CL is
at least a
> > significant improvement on what we had before, so I'm OK with it for
now.

https://codereview.appspot.com/38180043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/environment.go'
2--- cmd/juju/environment.go 2013-11-28 12:52:51 +0000
3+++ cmd/juju/environment.go 2013-12-06 04:54:05 +0000
4@@ -181,7 +181,7 @@
5 return err
6 }
7 // Now try to apply the new validated config.
8- return conn.State.SetEnvironConfig(newProviderConfig)
9+ return conn.State.SetEnvironConfig(newProviderConfig, oldConfig)
10 }
11
12 func (c *SetEnvironmentCommand) Run(ctx *cmd.Context) error {
13
14=== modified file 'cmd/juju/upgradejuju_test.go'
15--- cmd/juju/upgradejuju_test.go 2013-11-18 05:41:36 +0000
16+++ cmd/juju/upgradejuju_test.go 2013-12-06 04:54:05 +0000
17@@ -296,15 +296,15 @@
18 }
19
20 // Set up state and environ, and run the command.
21- cfg, err := s.State.EnvironConfig()
22+ oldcfg, err := s.State.EnvironConfig()
23 c.Assert(err, gc.IsNil)
24 toolsDir := c.MkDir()
25- cfg, err = cfg.Apply(map[string]interface{}{
26+ cfg, err := oldcfg.Apply(map[string]interface{}{
27 "agent-version": test.agentVersion,
28 "tools-metadata-url": "file://" + toolsDir,
29 })
30 c.Assert(err, gc.IsNil)
31- err = s.State.SetEnvironConfig(cfg)
32+ err = s.State.SetEnvironConfig(cfg, oldcfg)
33 c.Assert(err, gc.IsNil)
34 versions := make([]version.Binary, len(test.tools))
35 for i, v := range test.tools {
36@@ -377,14 +377,14 @@
37 func (s *UpgradeJujuSuite) Reset(c *gc.C) {
38 s.JujuConnSuite.Reset(c)
39 envtesting.RemoveTools(c, s.Conn.Environ.Storage())
40- cfg, err := s.State.EnvironConfig()
41+ oldcfg, err := s.State.EnvironConfig()
42 c.Assert(err, gc.IsNil)
43- cfg, err = cfg.Apply(map[string]interface{}{
44+ cfg, err := oldcfg.Apply(map[string]interface{}{
45 "default-series": "raring",
46 "agent-version": "1.2.3",
47 })
48 c.Assert(err, gc.IsNil)
49- err = s.State.SetEnvironConfig(cfg)
50+ err = s.State.SetEnvironConfig(cfg, oldcfg)
51 c.Assert(err, gc.IsNil)
52 }
53
54
55=== modified file 'cmd/jujud/agent_test.go'
56--- cmd/jujud/agent_test.go 2013-10-24 20:58:30 +0000
57+++ cmd/jujud/agent_test.go 2013-12-06 04:54:05 +0000
58@@ -255,13 +255,13 @@
59 }
60
61 func (s *agentSuite) proposeVersion(c *gc.C, vers version.Number) {
62- cfg, err := s.State.EnvironConfig()
63+ oldcfg, err := s.State.EnvironConfig()
64 c.Assert(err, gc.IsNil)
65- cfg, err = cfg.Apply(map[string]interface{}{
66+ cfg, err := oldcfg.Apply(map[string]interface{}{
67 "agent-version": vers.String(),
68 })
69 c.Assert(err, gc.IsNil)
70- err = s.State.SetEnvironConfig(cfg)
71+ err = s.State.SetEnvironConfig(cfg, oldcfg)
72 c.Assert(err, gc.IsNil)
73 }
74
75
76=== modified file 'environs/testing/tools.go'
77--- environs/testing/tools.go 2013-11-18 05:41:36 +0000
78+++ environs/testing/tools.go 2013-12-06 04:54:05 +0000
79@@ -453,6 +453,6 @@
80 attrs["ssl-hostname-verification"] = SSLHostnameVerification
81 newConfig, err := config.New(config.NoDefaults, attrs)
82 c.Assert(err, gc.IsNil)
83- err = st.SetEnvironConfig(newConfig)
84+ err = st.SetEnvironConfig(newConfig, envConfig)
85 c.Assert(err, gc.IsNil)
86 }
87
88=== modified file 'juju/conn.go'
89--- juju/conn.go 2013-11-19 17:25:24 +0000
90+++ juju/conn.go 2013-12-06 04:54:05 +0000
91@@ -160,11 +160,11 @@
92 attrs[k] = v
93 }
94 }
95- cfg, err = config.New(config.NoDefaults, attrs)
96+ newcfg, err := config.New(config.NoDefaults, attrs)
97 if err != nil {
98 return err
99 }
100- return c.State.SetEnvironConfig(cfg)
101+ return c.State.SetEnvironConfig(newcfg, cfg)
102 }
103
104 // PutCharm uploads the given charm to provider storage, and adds a
105
106=== modified file 'juju/testing/repo.go'
107--- juju/testing/repo.go 2013-09-13 14:48:13 +0000
108+++ juju/testing/repo.go 2013-12-06 04:54:05 +0000
109@@ -27,11 +27,11 @@
110 s.JujuConnSuite.SetUpTest(c)
111 // Change the environ's config to ensure we're using the one in state,
112 // not the one in the local environments.yaml
113- cfg, err := s.State.EnvironConfig()
114- c.Assert(err, gc.IsNil)
115- cfg, err = cfg.Apply(map[string]interface{}{"default-series": "precise"})
116- c.Assert(err, gc.IsNil)
117- err = s.State.SetEnvironConfig(cfg)
118+ oldcfg, err := s.State.EnvironConfig()
119+ c.Assert(err, gc.IsNil)
120+ cfg, err := oldcfg.Apply(map[string]interface{}{"default-series": "precise"})
121+ c.Assert(err, gc.IsNil)
122+ err = s.State.SetEnvironConfig(cfg, oldcfg)
123 c.Assert(err, gc.IsNil)
124 s.RepoPath = os.Getenv("JUJU_REPOSITORY")
125 repoPath := c.MkDir()
126
127=== modified file 'juju/testing/utils.go'
128--- juju/testing/utils.go 2013-10-17 17:45:41 +0000
129+++ juju/testing/utils.go 2013-12-06 04:54:05 +0000
130@@ -20,7 +20,7 @@
131 c.Assert(err, gc.IsNil)
132 newCfg, err := config.New(config.NoDefaults, change(cfg.AllAttrs()))
133 c.Assert(err, gc.IsNil)
134- err = st.SetEnvironConfig(newCfg)
135+ err = st.SetEnvironConfig(newCfg, cfg)
136 c.Assert(err, gc.IsNil)
137 }
138
139
140=== modified file 'state/api/provisioner/provisioner_test.go'
141--- state/api/provisioner/provisioner_test.go 2013-11-28 23:59:23 +0000
142+++ state/api/provisioner/provisioner_test.go 2013-12-06 04:54:05 +0000
143@@ -352,12 +352,12 @@
144 attrs["type"] = "blah"
145 newConfig, err := config.New(config.NoDefaults, attrs)
146 c.Assert(err, gc.IsNil)
147- err = s.State.SetEnvironConfig(newConfig)
148+ err = s.State.SetEnvironConfig(newConfig, envConfig)
149 c.Assert(err, gc.IsNil)
150 wc.AssertOneChange()
151
152 // Change it back to the original config.
153- err = s.State.SetEnvironConfig(envConfig)
154+ err = s.State.SetEnvironConfig(envConfig, newConfig)
155 c.Assert(err, gc.IsNil)
156 wc.AssertOneChange()
157
158
159=== modified file 'state/apiserver/client/client.go'
160--- state/apiserver/client/client.go 2013-12-04 15:34:47 +0000
161+++ state/apiserver/client/client.go 2013-12-06 04:54:05 +0000
162@@ -822,7 +822,7 @@
163 return err
164 }
165 // Now try to apply the new validated config.
166- return c.api.state.SetEnvironConfig(newProviderConfig)
167+ return c.api.state.SetEnvironConfig(newProviderConfig, oldConfig)
168 }
169
170 // SetEnvironAgentVersion sets the environment agent version.
171
172=== modified file 'state/initialize_test.go'
173--- state/initialize_test.go 2013-09-23 11:43:36 +0000
174+++ state/initialize_test.go 2013-12-06 04:54:05 +0000
175@@ -109,7 +109,7 @@
176 // admin-secret blocks SetEnvironConfig.
177 st := state.TestingInitialize(c, good)
178 st.Close()
179- err = s.State.SetEnvironConfig(bad)
180+ err = s.State.SetEnvironConfig(bad, good)
181 c.Assert(err, gc.ErrorMatches, "admin-secret should never be written to the state")
182
183 // EnvironConfig remains inviolate.
184@@ -132,7 +132,7 @@
185 // Bad agent-version blocks SetEnvironConfig.
186 st := state.TestingInitialize(c, good)
187 st.Close()
188- err = s.State.SetEnvironConfig(bad)
189+ err = s.State.SetEnvironConfig(bad, good)
190 c.Assert(err, gc.ErrorMatches, "agent-version must always be set in state")
191
192 // EnvironConfig remains inviolate.
193
194=== modified file 'state/state.go'
195--- state/state.go 2013-12-04 15:34:47 +0000
196+++ state/state.go 2013-12-06 04:54:05 +0000
197@@ -249,18 +249,27 @@
198
199 // SetEnvironConfig replaces the current configuration of the
200 // environment with the provided configuration.
201-func (st *State) SetEnvironConfig(cfg *config.Config) error {
202+func (st *State) SetEnvironConfig(cfg, old *config.Config) error {
203 if err := checkEnvironConfig(cfg); err != nil {
204 return err
205 }
206- // TODO(niemeyer): This isn't entirely right as the change is done as a
207- // delta that the user didn't ask for. Instead, take a (old, new) config
208- // pair, and apply *known* delta.
209+ // TODO(axw) 2013-12-6 #1167616
210+ // Ensure that the settings on disk have not changed
211+ // underneath us. The settings changes are actually
212+ // applied as a delta to what's on disk; if there has
213+ // been a concurrent update, the change may not be what
214+ // the user asked for.
215 settings, err := readSettings(st, environGlobalKey)
216 if err != nil {
217 return err
218 }
219- settings.Update(cfg.AllAttrs())
220+ newattrs := cfg.AllAttrs()
221+ for k, _ := range old.AllAttrs() {
222+ if _, ok := newattrs[k]; !ok {
223+ settings.Delete(k)
224+ }
225+ }
226+ settings.Update(newattrs)
227 _, err = settings.Write()
228 return err
229 }
230
231=== modified file 'state/state_test.go'
232--- state/state_test.go 2013-12-04 15:34:47 +0000
233+++ state/state_test.go 2013-12-06 04:54:05 +0000
234@@ -805,7 +805,7 @@
235 "arbitrary-key": "shazam!",
236 })
237 c.Assert(err, gc.IsNil)
238- err = s.State.SetEnvironConfig(change)
239+ err = s.State.SetEnvironConfig(change, cfg)
240 c.Assert(err, gc.IsNil)
241 cfg, err = s.State.EnvironConfig()
242 c.Assert(err, gc.IsNil)
243@@ -1236,9 +1236,10 @@
244 cfg, err := s.State.EnvironConfig()
245 c.Assert(err, gc.IsNil)
246 if change != nil {
247+ oldcfg := cfg
248 cfg, err = cfg.Apply(change)
249 c.Assert(err, gc.IsNil)
250- err = s.State.SetEnvironConfig(cfg)
251+ err = s.State.SetEnvironConfig(cfg, oldcfg)
252 c.Assert(err, gc.IsNil)
253 }
254 s.State.StartSync()
255@@ -1296,6 +1297,7 @@
256 func (s *StateSuite) TestWatchEnvironConfigCorruptConfig(c *gc.C) {
257 cfg, err := s.State.EnvironConfig()
258 c.Assert(err, gc.IsNil)
259+ oldcfg := cfg
260
261 // Corrupt the environment configuration.
262 settings := s.Session.DB("juju").C("settings")
263@@ -1331,7 +1333,7 @@
264 }
265
266 // Fix the configuration.
267- err = s.State.SetEnvironConfig(cfg)
268+ err = s.State.SetEnvironConfig(cfg, oldcfg)
269 c.Assert(err, gc.IsNil)
270 fixed := cfg.AllAttrs()
271
272@@ -2075,7 +2077,7 @@
273 attrs[name] = value
274 newConfig, err := config.New(config.NoDefaults, attrs)
275 c.Assert(err, gc.IsNil)
276- c.Assert(s.State.SetEnvironConfig(newConfig), gc.IsNil)
277+ c.Assert(s.State.SetEnvironConfig(newConfig, envConfig), gc.IsNil)
278 }
279
280 func (s *StateSuite) assertAgentVersion(c *gc.C, envConfig *config.Config, vers string) {
281
282=== modified file 'state/testing/config.go'
283--- state/testing/config.go 2013-09-11 03:48:05 +0000
284+++ state/testing/config.go 2013-12-06 04:54:05 +0000
285@@ -14,9 +14,9 @@
286 if err != nil {
287 return err
288 }
289- cfg, err = cfg.Apply(newValues)
290+ newcfg, err := cfg.Apply(newValues)
291 if err != nil {
292 return err
293 }
294- return st.SetEnvironConfig(cfg)
295+ return st.SetEnvironConfig(newcfg, cfg)
296 }
297
298=== modified file 'worker/firewaller/firewaller_test.go'
299--- worker/firewaller/firewaller_test.go 2013-09-30 19:40:06 +0000
300+++ worker/firewaller/firewaller_test.go 2013-12-06 04:54:05 +0000
301@@ -109,13 +109,14 @@
302 // TODO(rog) This should not be possible - you shouldn't
303 // be able to set the firewalling mode after an environment
304 // has bootstrapped.
305- attrs := s.Conn.Environ.Config().AllAttrs()
306+ oldConfig := s.Conn.Environ.Config()
307+ attrs := oldConfig.AllAttrs()
308 delete(attrs, "admin-secret")
309 delete(attrs, "ca-private-key")
310 attrs["firewall-mode"] = config.FwGlobal
311 newConfig, err := config.New(config.NoDefaults, attrs)
312 c.Assert(err, gc.IsNil)
313- err = s.State.SetEnvironConfig(newConfig)
314+ err = s.State.SetEnvironConfig(newConfig, oldConfig)
315 c.Assert(err, gc.IsNil)
316 err = s.Conn.Environ.SetConfig(newConfig)
317 c.Assert(err, gc.IsNil)
318
319=== modified file 'worker/provisioner/provisioner_test.go'
320--- worker/provisioner/provisioner_test.go 2013-11-28 09:09:30 +0000
321+++ worker/provisioner/provisioner_test.go 2013-12-06 04:54:05 +0000
322@@ -93,7 +93,7 @@
323 c.Assert(err, gc.IsNil)
324 cfg, err := oldCfg.Apply(map[string]interface{}{"broken": environMethod})
325 c.Assert(err, gc.IsNil)
326- err = st.SetEnvironConfig(cfg)
327+ err = st.SetEnvironConfig(cfg, oldCfg)
328 c.Assert(err, gc.IsNil)
329 return fmt.Sprintf("dummy.%s is broken", environMethod)
330 }
331@@ -118,13 +118,17 @@
332 attrs["type"] = "unknown"
333 invalidCfg, err := config.New(config.NoDefaults, attrs)
334 c.Assert(err, gc.IsNil)
335- err = s.State.SetEnvironConfig(invalidCfg)
336+ err = s.State.SetEnvironConfig(invalidCfg, s.cfg)
337 c.Assert(err, gc.IsNil)
338 }
339
340 // fixEnvironment undoes the work of invalidateEnvironment.
341 func (s *CommonProvisionerSuite) fixEnvironment() error {
342- return s.State.SetEnvironConfig(s.cfg)
343+ cfg, err := s.State.EnvironConfig()
344+ if err != nil {
345+ return err
346+ }
347+ return s.State.SetEnvironConfig(s.cfg, cfg)
348 }
349
350 // stopper is stoppable.
351@@ -595,13 +599,13 @@
352 cfgObserver := make(chan *config.Config, 1)
353 provisioner.SetObserver(p, cfgObserver)
354
355- cfg, err := s.State.EnvironConfig()
356+ oldcfg, err := s.State.EnvironConfig()
357 c.Assert(err, gc.IsNil)
358- attrs := cfg.AllAttrs()
359+ attrs := oldcfg.AllAttrs()
360 attrs["secret"] = "beef"
361- cfg, err = config.New(config.NoDefaults, attrs)
362+ cfg, err := config.New(config.NoDefaults, attrs)
363 c.Assert(err, gc.IsNil)
364- err = s.State.SetEnvironConfig(cfg)
365+ err = s.State.SetEnvironConfig(cfg, oldcfg)
366
367 s.BackingState.StartSync()
368
369@@ -643,13 +647,13 @@
370 c.Assert(m1.Remove(), gc.IsNil)
371
372 // turn on safe mode
373- cfg, err := s.State.EnvironConfig()
374+ oldcfg, err := s.State.EnvironConfig()
375 c.Assert(err, gc.IsNil)
376- attrs := cfg.AllAttrs()
377+ attrs := oldcfg.AllAttrs()
378 attrs["provisioner-safe-mode"] = true
379- cfg, err = config.New(config.NoDefaults, attrs)
380+ cfg, err := config.New(config.NoDefaults, attrs)
381 c.Assert(err, gc.IsNil)
382- err = s.State.SetEnvironConfig(cfg)
383+ err = s.State.SetEnvironConfig(cfg, oldcfg)
384
385 // start a new provisioner to shut down only the machine still in state.
386 p = s.newEnvironProvisioner(c)
387@@ -689,13 +693,13 @@
388 provisioner.SetObserver(p, cfgObserver)
389
390 // turn on safe mode
391- cfg, err := s.State.EnvironConfig()
392+ oldcfg, err := s.State.EnvironConfig()
393 c.Assert(err, gc.IsNil)
394- attrs := cfg.AllAttrs()
395+ attrs := oldcfg.AllAttrs()
396 attrs["provisioner-safe-mode"] = true
397- cfg, err = config.New(config.NoDefaults, attrs)
398+ cfg, err := config.New(config.NoDefaults, attrs)
399 c.Assert(err, gc.IsNil)
400- err = s.State.SetEnvironConfig(cfg)
401+ err = s.State.SetEnvironConfig(cfg, oldcfg)
402
403 s.BackingState.StartSync()
404

Subscribers

People subscribed via source and target branches

to status/vote changes: