Merge lp:~rogpeppe/juju-core/391-1.14-avoid-allFatal into lp:juju-core/1.14
- 391-1.14-avoid-allFatal
- Merge into 1.14
Proposed by
Roger Peppe
Status: | Merged |
---|---|
Approved by: | Roger Peppe |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1741 |
Proposed branch: | lp:~rogpeppe/juju-core/391-1.14-avoid-allFatal |
Merge into: | lp:juju-core/1.14 |
Diff against target: |
761 lines (+313/-65) 13 files modified
cmd/jujud/agent.go (+21/-0) cmd/jujud/agent_test.go (+66/-0) cmd/jujud/machine.go (+14/-8) cmd/jujud/machine_test.go (+6/-6) cmd/jujud/unit.go (+2/-2) juju/testing/conn.go (+48/-36) state/api/apiclient.go (+5/-4) state/apiserver/pinger_test.go (+12/-0) state/state.go (+6/-0) state/state_test.go (+6/-0) testing/checkers/set.go (+35/-0) testing/checkers/set_test.go (+54/-0) testing/mgo.go (+38/-9) |
To merge this branch: | bzr merge lp:~rogpeppe/juju-core/391-1.14-avoid-allFatal |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+185445@code.launchpad.net |
Commit message
cmd/jujud: avoid using allFatal
This is a backport of the https:/
now proposed for 1.14, with selected other patches from other revisions
to make it compile.
Description of the change
cmd/jujud: avoid using allFatal
This is a backport of the https:/
now proposed for 1.14, with selected other patches from other revisions
to make it compile.
To post a comment you must log in.
Revision history for this message
Roger Peppe (rogpeppe) wrote : | # |
Revision history for this message
Dimiter Naydenov (dimitern) wrote : | # |
Revision history for this message
William Reade (fwereade) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'cmd/jujud/agent.go' |
2 | --- cmd/jujud/agent.go 2013-09-11 06:27:05 +0000 |
3 | +++ cmd/jujud/agent.go 2013-09-13 09:26:01 +0000 |
4 | @@ -111,6 +111,27 @@ |
5 | return ok |
6 | } |
7 | |
8 | +type pinger interface { |
9 | + Ping() error |
10 | +} |
11 | + |
12 | +// connectionIsFatal returns a function suitable for passing |
13 | +// as the isFatal argument to worker.NewRunner, |
14 | +// that diagnoses an error as fatal if the connection |
15 | +// has failed or if the error is otherwise fatal. |
16 | +func connectionIsFatal(conn pinger) func(err error) bool { |
17 | + return func(err error) bool { |
18 | + if isFatal(err) { |
19 | + return true |
20 | + } |
21 | + if err := conn.Ping(); err != nil { |
22 | + log.Infof("error pinging %T: %v", conn, err) |
23 | + return true |
24 | + } |
25 | + return false |
26 | + } |
27 | +} |
28 | + |
29 | // isleep waits for the given duration or until it receives a value on |
30 | // stop. It returns whether the full duration was slept without being |
31 | // stopped. |
32 | |
33 | === modified file 'cmd/jujud/agent_test.go' |
34 | --- cmd/jujud/agent_test.go 2013-08-24 13:50:05 +0000 |
35 | +++ cmd/jujud/agent_test.go 2013-09-13 09:26:01 +0000 |
36 | @@ -20,7 +20,9 @@ |
37 | envtools "launchpad.net/juju-core/environs/tools" |
38 | "launchpad.net/juju-core/juju/testing" |
39 | "launchpad.net/juju-core/state" |
40 | + "launchpad.net/juju-core/state/api/params" |
41 | coretesting "launchpad.net/juju-core/testing" |
42 | + jc "launchpad.net/juju-core/testing/checkers" |
43 | coretools "launchpad.net/juju-core/tools" |
44 | "launchpad.net/juju-core/version" |
45 | "launchpad.net/juju-core/worker" |
46 | @@ -48,6 +50,70 @@ |
47 | } |
48 | } |
49 | |
50 | +var isFatalTests = []struct { |
51 | + err error |
52 | + isFatal bool |
53 | +}{{ |
54 | + err: worker.ErrTerminateAgent, |
55 | + isFatal: true, |
56 | +}, { |
57 | + err: &upgrader.UpgradeReadyError{}, |
58 | + isFatal: true, |
59 | +}, { |
60 | + err: ¶ms.Error{ |
61 | + Message: "blah", |
62 | + Code: params.CodeNotProvisioned, |
63 | + }, |
64 | + isFatal: true, |
65 | +}, { |
66 | + err: &fatalError{"some fatal error"}, |
67 | + isFatal: true, |
68 | +}, { |
69 | + err: stderrors.New("foo"), |
70 | + isFatal: false, |
71 | +}, { |
72 | + err: ¶ms.Error{ |
73 | + Message: "blah", |
74 | + Code: params.CodeNotFound, |
75 | + }, |
76 | + isFatal: false, |
77 | +}} |
78 | + |
79 | +func (*toolSuite) TestIsFatal(c *gc.C) { |
80 | + for i, test := range isFatalTests { |
81 | + c.Logf("test %d: %s", i, test.err) |
82 | + c.Assert(isFatal(test.err), gc.Equals, test.isFatal) |
83 | + } |
84 | +} |
85 | + |
86 | +type testPinger func() error |
87 | + |
88 | +func (f testPinger) Ping() error { |
89 | + return f() |
90 | +} |
91 | + |
92 | +func (s *toolSuite) TestConnectionIsFatal(c *gc.C) { |
93 | + var ( |
94 | + errPinger testPinger = func() error { |
95 | + return stderrors.New("ping error") |
96 | + } |
97 | + okPinger testPinger = func() error { |
98 | + return nil |
99 | + } |
100 | + ) |
101 | + for i, pinger := range []testPinger{errPinger, okPinger} { |
102 | + for j, test := range isFatalTests { |
103 | + c.Logf("test %d.%d: %s", i, j, test.err) |
104 | + fatal := connectionIsFatal(pinger)(test.err) |
105 | + if test.isFatal { |
106 | + c.Check(fatal, jc.IsTrue) |
107 | + } else { |
108 | + c.Check(fatal, gc.Equals, i == 0) |
109 | + } |
110 | + } |
111 | + } |
112 | +} |
113 | + |
114 | func mkTools(s string) *coretools.Tools { |
115 | return &coretools.Tools{ |
116 | Version: version.MustParseBinary(s + "-foo-bar"), |
117 | |
118 | === modified file 'cmd/jujud/machine.go' |
119 | --- cmd/jujud/machine.go 2013-09-11 06:27:05 +0000 |
120 | +++ cmd/jujud/machine.go 2013-09-13 09:26:01 +0000 |
121 | @@ -34,6 +34,16 @@ |
122 | "launchpad.net/juju-core/worker/upgrader" |
123 | ) |
124 | |
125 | +type workerRunner interface { |
126 | + worker.Worker |
127 | + StartWorker(id string, startFunc func() (worker.Worker, error)) error |
128 | + StopWorker(id string) error |
129 | +} |
130 | + |
131 | +var newRunner = func(isFatal func(error) bool, moreImportant func(e0, e1 error) bool) workerRunner { |
132 | + return worker.NewRunner(isFatal, moreImportant) |
133 | +} |
134 | + |
135 | const bootstrapMachineId = "0" |
136 | |
137 | var retryDelay = 3 * time.Second |
138 | @@ -44,7 +54,7 @@ |
139 | tomb tomb.Tomb |
140 | Conf AgentConf |
141 | MachineId string |
142 | - runner *worker.Runner |
143 | + runner workerRunner |
144 | } |
145 | |
146 | // Info returns usage information for the command. |
147 | @@ -68,7 +78,7 @@ |
148 | if err := a.Conf.checkArgs(args); err != nil { |
149 | return err |
150 | } |
151 | - a.runner = worker.NewRunner(isFatal, moreImportant) |
152 | + a.runner = newRunner(isFatal, moreImportant) |
153 | return nil |
154 | } |
155 | |
156 | @@ -127,10 +137,6 @@ |
157 | return err |
158 | } |
159 | |
160 | -func allFatal(error) bool { |
161 | - return true |
162 | -} |
163 | - |
164 | var stateJobs = map[params.MachineJob]bool{ |
165 | params.JobHostUnits: true, |
166 | params.JobManageEnviron: true, |
167 | @@ -164,7 +170,7 @@ |
168 | if needsStateWorker { |
169 | ensureStateWorker() |
170 | } |
171 | - runner := worker.NewRunner(allFatal, moreImportant) |
172 | + runner := newRunner(connectionIsFatal(st), moreImportant) |
173 | // Only the machiner currently connects to the API. |
174 | // Add other workers here as they are ready. |
175 | runner.StartWorker("machiner", func() (worker.Worker, error) { |
176 | @@ -208,7 +214,7 @@ |
177 | // TODO(rog) use more discriminating test for errors |
178 | // rather than taking everything down indiscriminately. |
179 | dataDir := a.Conf.dataDir |
180 | - runner := worker.NewRunner(allFatal, moreImportant) |
181 | + runner := newRunner(connectionIsFatal(st), moreImportant) |
182 | // At this stage, since we don't embed lxc containers, just start an lxc |
183 | // provisioner task for non-lxc containers. Since we have only LXC |
184 | // containers and normal machines, this effectively means that we only |
185 | |
186 | === modified file 'cmd/jujud/machine_test.go' |
187 | --- cmd/jujud/machine_test.go 2013-09-11 06:27:05 +0000 |
188 | +++ cmd/jujud/machine_test.go 2013-09-13 09:26:01 +0000 |
189 | @@ -24,7 +24,7 @@ |
190 | "launchpad.net/juju-core/state/api/params" |
191 | "launchpad.net/juju-core/state/watcher" |
192 | "launchpad.net/juju-core/testing" |
193 | - "launchpad.net/juju-core/testing/checkers" |
194 | + jc "launchpad.net/juju-core/testing/checkers" |
195 | "launchpad.net/juju-core/tools" |
196 | "launchpad.net/juju-core/version" |
197 | "launchpad.net/juju-core/worker/deployer" |
198 | @@ -33,7 +33,7 @@ |
199 | type MachineSuite struct { |
200 | agentSuite |
201 | lxc.TestSuite |
202 | - oldCacheDir string |
203 | + restoreCacheDir jc.Restorer |
204 | } |
205 | |
206 | var _ = gc.Suite(&MachineSuite{}) |
207 | @@ -41,11 +41,11 @@ |
208 | func (s *MachineSuite) SetUpSuite(c *gc.C) { |
209 | s.agentSuite.SetUpSuite(c) |
210 | s.TestSuite.SetUpSuite(c) |
211 | - s.oldCacheDir = charm.CacheDir |
212 | + s.restoreCacheDir = jc.Set(&charm.CacheDir, c.MkDir()) |
213 | } |
214 | |
215 | func (s *MachineSuite) TearDownSuite(c *gc.C) { |
216 | - charm.CacheDir = s.oldCacheDir |
217 | + s.restoreCacheDir() |
218 | s.TestSuite.TearDownSuite(c) |
219 | s.agentSuite.TearDownSuite(c) |
220 | } |
221 | @@ -231,7 +231,7 @@ |
222 | if err == nil && attempt.HasNext() { |
223 | continue |
224 | } |
225 | - c.Assert(err, checkers.Satisfies, errors.IsNotFoundError) |
226 | + c.Assert(err, jc.Satisfies, errors.IsNotFoundError) |
227 | } |
228 | |
229 | // short-circuit-remove u1 after it's been deployed; check it's recalled |
230 | @@ -239,7 +239,7 @@ |
231 | err = u1.Destroy() |
232 | c.Assert(err, gc.IsNil) |
233 | err = u1.Refresh() |
234 | - c.Assert(err, checkers.Satisfies, errors.IsNotFoundError) |
235 | + c.Assert(err, jc.Satisfies, errors.IsNotFoundError) |
236 | ctx.waitDeployed(c) |
237 | } |
238 | |
239 | |
240 | === modified file 'cmd/jujud/unit.go' |
241 | --- cmd/jujud/unit.go 2013-09-11 06:27:05 +0000 |
242 | +++ cmd/jujud/unit.go 2013-09-13 09:26:01 +0000 |
243 | @@ -85,7 +85,7 @@ |
244 | } |
245 | unit := entity.(*state.Unit) |
246 | dataDir := a.Conf.dataDir |
247 | - runner := worker.NewRunner(allFatal, moreImportant) |
248 | + runner := worker.NewRunner(connectionIsFatal(st), moreImportant) |
249 | runner.StartWorker("uniter", func() (worker.Worker, error) { |
250 | return uniter.NewUniter(st, unit.Name(), dataDir), nil |
251 | }) |
252 | @@ -98,7 +98,7 @@ |
253 | return nil, err |
254 | } |
255 | dataDir := a.Conf.dataDir |
256 | - runner := worker.NewRunner(allFatal, moreImportant) |
257 | + runner := worker.NewRunner(connectionIsFatal(st), moreImportant) |
258 | runner.StartWorker("upgrader", func() (worker.Worker, error) { |
259 | return upgrader.New(st.Upgrader(), entity.Tag(), dataDir), nil |
260 | }) |
261 | |
262 | === modified file 'juju/testing/conn.go' |
263 | --- juju/testing/conn.go 2013-09-11 06:27:05 +0000 |
264 | +++ juju/testing/conn.go 2013-09-13 09:26:01 +0000 |
265 | @@ -9,7 +9,7 @@ |
266 | "os" |
267 | "path/filepath" |
268 | |
269 | - . "launchpad.net/gocheck" |
270 | + gc "launchpad.net/gocheck" |
271 | |
272 | "launchpad.net/juju-core/charm" |
273 | "launchpad.net/juju-core/constraints" |
274 | @@ -85,13 +85,13 @@ |
275 | |
276 | // StartInstance is a test helper function that starts an instance on the |
277 | // environment using the current series and invalid info states. |
278 | -func StartInstance(c *C, env environs.Environ, machineId string) (instance.Instance, *instance.HardwareCharacteristics) { |
279 | +func StartInstance(c *gc.C, env environs.Environ, machineId string) (instance.Instance, *instance.HardwareCharacteristics) { |
280 | return StartInstanceWithConstraints(c, env, machineId, constraints.Value{}) |
281 | } |
282 | |
283 | // StartInstanceWithConstraints is a test helper function that starts an instance on the |
284 | // environment with the specified constraints, using the current series and invalid info states. |
285 | -func StartInstanceWithConstraints(c *C, env environs.Environ, machineId string, |
286 | +func StartInstanceWithConstraints(c *gc.C, env environs.Environ, machineId string, |
287 | cons constraints.Value) (instance.Instance, *instance.HardwareCharacteristics) { |
288 | series := config.DefaultSeries |
289 | inst, metadata, err := provider.StartInstance( |
290 | @@ -103,7 +103,7 @@ |
291 | FakeStateInfo(machineId), |
292 | FakeAPIInfo(machineId), |
293 | ) |
294 | - c.Assert(err, IsNil) |
295 | + c.Assert(err, gc.IsNil) |
296 | return inst, metadata |
297 | } |
298 | |
299 | @@ -119,24 +119,24 @@ |
300 | agent-version: %s |
301 | ` |
302 | |
303 | -func (s *JujuConnSuite) SetUpSuite(c *C) { |
304 | +func (s *JujuConnSuite) SetUpSuite(c *gc.C) { |
305 | s.LoggingSuite.SetUpSuite(c) |
306 | s.MgoSuite.SetUpSuite(c) |
307 | } |
308 | |
309 | -func (s *JujuConnSuite) TearDownSuite(c *C) { |
310 | +func (s *JujuConnSuite) TearDownSuite(c *gc.C) { |
311 | s.MgoSuite.TearDownSuite(c) |
312 | s.LoggingSuite.TearDownSuite(c) |
313 | } |
314 | |
315 | -func (s *JujuConnSuite) SetUpTest(c *C) { |
316 | +func (s *JujuConnSuite) SetUpTest(c *gc.C) { |
317 | s.oldJujuHome = config.SetJujuHome(c.MkDir()) |
318 | s.LoggingSuite.SetUpTest(c) |
319 | s.MgoSuite.SetUpTest(c) |
320 | s.setUpConn(c) |
321 | } |
322 | |
323 | -func (s *JujuConnSuite) TearDownTest(c *C) { |
324 | +func (s *JujuConnSuite) TearDownTest(c *gc.C) { |
325 | s.tearDownConn(c) |
326 | s.MgoSuite.TearDownTest(c) |
327 | s.LoggingSuite.TearDownTest(c) |
328 | @@ -145,51 +145,63 @@ |
329 | |
330 | // Reset returns environment state to that which existed at the start of |
331 | // the test. |
332 | -func (s *JujuConnSuite) Reset(c *C) { |
333 | +func (s *JujuConnSuite) Reset(c *gc.C) { |
334 | s.tearDownConn(c) |
335 | s.setUpConn(c) |
336 | } |
337 | |
338 | -func (s *JujuConnSuite) StateInfo(c *C) *state.Info { |
339 | +func (s *JujuConnSuite) StateInfo(c *gc.C) *state.Info { |
340 | info, _, err := s.Conn.Environ.StateInfo() |
341 | - c.Assert(err, IsNil) |
342 | + c.Assert(err, gc.IsNil) |
343 | info.Password = "dummy-secret" |
344 | return info |
345 | } |
346 | |
347 | -func (s *JujuConnSuite) APIInfo(c *C) *api.Info { |
348 | +func (s *JujuConnSuite) APIInfo(c *gc.C) *api.Info { |
349 | _, apiInfo, err := s.APIConn.Environ.StateInfo() |
350 | - c.Assert(err, IsNil) |
351 | + c.Assert(err, gc.IsNil) |
352 | apiInfo.Tag = "user-admin" |
353 | apiInfo.Password = "dummy-secret" |
354 | return apiInfo |
355 | } |
356 | |
357 | -func (s *JujuConnSuite) openAPIAs(c *C, tag, password, nonce string) *api.State { |
358 | +func (s *JujuConnSuite) openAPIAs(c *gc.C, tag, password, nonce string) *api.State { |
359 | _, info, err := s.APIConn.Environ.StateInfo() |
360 | - c.Assert(err, IsNil) |
361 | + c.Assert(err, gc.IsNil) |
362 | info.Tag = tag |
363 | info.Password = password |
364 | info.Nonce = nonce |
365 | st, err := api.Open(info, api.DialOpts{}) |
366 | - c.Assert(err, IsNil) |
367 | - c.Assert(st, NotNil) |
368 | + c.Assert(err, gc.IsNil) |
369 | + c.Assert(st, gc.NotNil) |
370 | return st |
371 | } |
372 | |
373 | // OpenAPIAs opens the API using the given identity tag |
374 | // and password for authentication. |
375 | -func (s *JujuConnSuite) OpenAPIAs(c *C, tag, password string) *api.State { |
376 | +func (s *JujuConnSuite) OpenAPIAs(c *gc.C, tag, password string) *api.State { |
377 | return s.openAPIAs(c, tag, password, "") |
378 | } |
379 | |
380 | // OpenAPIAsMachine opens the API using the given machine tag, |
381 | // password and nonce for authentication. |
382 | -func (s *JujuConnSuite) OpenAPIAsMachine(c *C, tag, password, nonce string) *api.State { |
383 | +func (s *JujuConnSuite) OpenAPIAsMachine(c *gc.C, tag, password, nonce string) *api.State { |
384 | return s.openAPIAs(c, tag, password, nonce) |
385 | } |
386 | |
387 | -func (s *JujuConnSuite) setUpConn(c *C) { |
388 | +// OpenAPIAsNewMachine creates a new machine entry that lives in system state, and |
389 | +// then uses that to open the API. |
390 | +func (s *JujuConnSuite) OpenAPIAsNewMachine(c *gc.C) (*api.State, *state.Machine) { |
391 | + machine, err := s.State.AddMachine("series", state.JobHostUnits) |
392 | + c.Assert(err, gc.IsNil) |
393 | + err = machine.SetPassword("test-password") |
394 | + c.Assert(err, gc.IsNil) |
395 | + err = machine.SetProvisioned("foo", "fake_nonce", nil) |
396 | + c.Assert(err, gc.IsNil) |
397 | + return s.openAPIAs(c, machine.Tag(), "test-password", "fake_nonce"), machine |
398 | +} |
399 | + |
400 | +func (s *JujuConnSuite) setUpConn(c *gc.C) { |
401 | if s.RootDir != "" { |
402 | panic("JujuConnSuite.setUpConn without teardown") |
403 | } |
404 | @@ -197,38 +209,38 @@ |
405 | s.oldHome = os.Getenv("HOME") |
406 | home := filepath.Join(s.RootDir, "/home/ubuntu") |
407 | err := os.MkdirAll(home, 0777) |
408 | - c.Assert(err, IsNil) |
409 | + c.Assert(err, gc.IsNil) |
410 | os.Setenv("HOME", home) |
411 | |
412 | dataDir := filepath.Join(s.RootDir, "/var/lib/juju") |
413 | err = os.MkdirAll(dataDir, 0777) |
414 | - c.Assert(err, IsNil) |
415 | + c.Assert(err, gc.IsNil) |
416 | |
417 | yaml := []byte(fmt.Sprintf(envConfig, version.Current.Number)) |
418 | err = ioutil.WriteFile(config.JujuHomePath("environments.yaml"), yaml, 0600) |
419 | - c.Assert(err, IsNil) |
420 | + c.Assert(err, gc.IsNil) |
421 | |
422 | err = ioutil.WriteFile(config.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666) |
423 | - c.Assert(err, IsNil) |
424 | + c.Assert(err, gc.IsNil) |
425 | |
426 | err = ioutil.WriteFile(config.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600) |
427 | - c.Assert(err, IsNil) |
428 | + c.Assert(err, gc.IsNil) |
429 | |
430 | environ, err := environs.PrepareFromName("dummyenv") |
431 | - c.Assert(err, IsNil) |
432 | + c.Assert(err, gc.IsNil) |
433 | // sanity check we've got the correct environment. |
434 | - c.Assert(environ.Name(), Equals, "dummyenv") |
435 | - c.Assert(bootstrap.Bootstrap(environ, constraints.Value{}), IsNil) |
436 | + c.Assert(environ.Name(), gc.Equals, "dummyenv") |
437 | + c.Assert(bootstrap.Bootstrap(environ, constraints.Value{}), gc.IsNil) |
438 | |
439 | s.BackingState = environ.(GetStater).GetStateInAPIServer() |
440 | |
441 | conn, err := juju.NewConn(environ) |
442 | - c.Assert(err, IsNil) |
443 | + c.Assert(err, gc.IsNil) |
444 | s.Conn = conn |
445 | s.State = conn.State |
446 | |
447 | apiConn, err := juju.NewAPIConn(environ, api.DialOpts{}) |
448 | - c.Assert(err, IsNil) |
449 | + c.Assert(err, gc.IsNil) |
450 | s.APIConn = apiConn |
451 | s.APIState = apiConn.State |
452 | s.environ = environ |
453 | @@ -238,7 +250,7 @@ |
454 | GetStateInAPIServer() *state.State |
455 | } |
456 | |
457 | -func (s *JujuConnSuite) tearDownConn(c *C) { |
458 | +func (s *JujuConnSuite) tearDownConn(c *gc.C) { |
459 | // Bootstrap will set the admin password, and render non-authorized use |
460 | // impossible. s.State may still hold the right password, so try to reset |
461 | // the password so that the MgoSuite soft-resetting works. If that fails, |
462 | @@ -247,8 +259,8 @@ |
463 | if err := s.State.SetAdminMongoPassword(""); err != nil { |
464 | c.Logf("cannot reset admin password: %v", err) |
465 | } |
466 | - c.Assert(s.Conn.Close(), IsNil) |
467 | - c.Assert(s.APIConn.Close(), IsNil) |
468 | + c.Assert(s.Conn.Close(), gc.IsNil) |
469 | + c.Assert(s.APIConn.Close(), gc.IsNil) |
470 | dummy.Reset() |
471 | s.Conn = nil |
472 | s.State = nil |
473 | @@ -276,13 +288,13 @@ |
474 | } |
475 | } |
476 | |
477 | -func (s *JujuConnSuite) AddTestingCharm(c *C, name string) *state.Charm { |
478 | +func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { |
479 | ch := testing.Charms.Dir(name) |
480 | ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) |
481 | curl := charm.MustParseURL("local:series/" + ident) |
482 | repo, err := charm.InferRepository(curl, testing.Charms.Path) |
483 | - c.Assert(err, IsNil) |
484 | + c.Assert(err, gc.IsNil) |
485 | sch, err := s.Conn.PutCharm(curl, repo, false) |
486 | - c.Assert(err, IsNil) |
487 | + c.Assert(err, gc.IsNil) |
488 | return sch |
489 | } |
490 | |
491 | === modified file 'state/api/apiclient.go' |
492 | --- state/api/apiclient.go 2013-09-11 06:27:05 +0000 |
493 | +++ state/api/apiclient.go 2013-09-13 09:26:01 +0000 |
494 | @@ -133,11 +133,8 @@ |
495 | } |
496 | |
497 | func (s *State) heartbeatMonitor() { |
498 | - ping := func() error { |
499 | - return s.Call("Pinger", "", "Ping", nil, nil) |
500 | - } |
501 | for { |
502 | - if err := ping(); err != nil { |
503 | + if err := s.Ping(); err != nil { |
504 | close(s.broken) |
505 | return |
506 | } |
507 | @@ -145,6 +142,10 @@ |
508 | } |
509 | } |
510 | |
511 | +func (s *State) Ping() error { |
512 | + return s.Call("Pinger", "", "Ping", nil, nil) |
513 | +} |
514 | + |
515 | // Call invokes a low-level RPC method of the given objType, id, and |
516 | // request, passing the given parameters and filling in the response |
517 | // results. This should not be used directly by clients. |
518 | |
519 | === modified file 'state/apiserver/pinger_test.go' |
520 | --- state/apiserver/pinger_test.go 2013-09-11 06:27:05 +0000 |
521 | +++ state/apiserver/pinger_test.go 2013-09-13 09:26:01 +0000 |
522 | @@ -6,6 +6,7 @@ |
523 | import ( |
524 | gc "launchpad.net/gocheck" |
525 | "launchpad.net/juju-core/juju/testing" |
526 | + "launchpad.net/juju-core/rpc" |
527 | "launchpad.net/juju-core/state" |
528 | "launchpad.net/juju-core/state/api" |
529 | "time" |
530 | @@ -54,3 +55,14 @@ |
531 | return |
532 | } |
533 | } |
534 | + |
535 | +func (s *stateSuite) TestPing(c *gc.C) { |
536 | + st, _ := s.OpenAPIAsNewMachine(c) |
537 | + defer st.Close() |
538 | + err := st.Ping() |
539 | + c.Assert(err, gc.IsNil) |
540 | + err = st.Close() |
541 | + c.Assert(err, gc.IsNil) |
542 | + err = st.Ping() |
543 | + c.Assert(err, gc.Equals, rpc.ErrShutdown) |
544 | +} |
545 | |
546 | === modified file 'state/state.go' |
547 | --- state/state.go 2013-09-11 06:27:05 +0000 |
548 | +++ state/state.go 2013-09-13 09:26:01 +0000 |
549 | @@ -102,6 +102,12 @@ |
550 | return st.runner.Run(ops, "", nil) |
551 | } |
552 | |
553 | +// Ping probes the state's database connection to ensure |
554 | +// that it is still alive. |
555 | +func (st *State) Ping() error { |
556 | + return st.db.Session.Ping() |
557 | +} |
558 | + |
559 | func (st *State) Watch() *multiwatcher.Watcher { |
560 | st.mu.Lock() |
561 | if st.allManager == nil { |
562 | |
563 | === modified file 'state/state_test.go' |
564 | --- state/state_test.go 2013-09-11 06:27:05 +0000 |
565 | +++ state/state_test.go 2013-09-13 09:26:01 +0000 |
566 | @@ -63,6 +63,12 @@ |
567 | c.Assert(s.State.CACert(), gc.DeepEquals, info.CACert) |
568 | } |
569 | |
570 | +func (s *StateSuite) TestPing(c *gc.C) { |
571 | + c.Assert(s.State.Ping(), gc.IsNil) |
572 | + testing.MgoRestart() |
573 | + c.Assert(s.State.Ping(), gc.NotNil) |
574 | +} |
575 | + |
576 | func (s *StateSuite) TestAPIAddresses(c *gc.C) { |
577 | config, err := s.State.EnvironConfig() |
578 | c.Assert(err, gc.IsNil) |
579 | |
580 | === added file 'testing/checkers/set.go' |
581 | --- testing/checkers/set.go 1970-01-01 00:00:00 +0000 |
582 | +++ testing/checkers/set.go 2013-09-13 09:26:01 +0000 |
583 | @@ -0,0 +1,35 @@ |
584 | +package checkers |
585 | + |
586 | +import ( |
587 | + "reflect" |
588 | +) |
589 | + |
590 | +// Restorer holds a function that can be used |
591 | +// to restore some previous state. |
592 | +type Restorer func() |
593 | + |
594 | +// Restore restores some previous state. |
595 | +func (r Restorer) Restore() { |
596 | + r() |
597 | +} |
598 | + |
599 | +// Set sets the value pointed to by the given |
600 | +// destination to the given value, and returns |
601 | +// a function to restore it to its original value. |
602 | +// The value must be assignable to the element |
603 | +// type of the destination. |
604 | +func Set(dest, value interface{}) Restorer { |
605 | + destv := reflect.ValueOf(dest).Elem() |
606 | + oldv := reflect.New(destv.Type()).Elem() |
607 | + oldv.Set(destv) |
608 | + valuev := reflect.ValueOf(value) |
609 | + if !valuev.IsValid() { |
610 | + // This isn't quite right when the destination type is not |
611 | + // nilable, but it's better than the complex alternative. |
612 | + valuev = reflect.Zero(destv.Type()) |
613 | + } |
614 | + destv.Set(valuev) |
615 | + return func() { |
616 | + destv.Set(oldv) |
617 | + } |
618 | +} |
619 | |
620 | === added file 'testing/checkers/set_test.go' |
621 | --- testing/checkers/set_test.go 1970-01-01 00:00:00 +0000 |
622 | +++ testing/checkers/set_test.go 2013-09-13 09:26:01 +0000 |
623 | @@ -0,0 +1,54 @@ |
624 | +package checkers_test |
625 | + |
626 | +import ( |
627 | + "errors" |
628 | + |
629 | + gc "launchpad.net/gocheck" |
630 | + jc "launchpad.net/juju-core/testing/checkers" |
631 | +) |
632 | + |
633 | +type SetSuite struct{} |
634 | + |
635 | +var _ = gc.Suite(&SetSuite{}) |
636 | + |
637 | +func (*SetSuite) TestSetInt(c *gc.C) { |
638 | + i := 99 |
639 | + restore := jc.Set(&i, 88) |
640 | + c.Assert(i, gc.Equals, 88) |
641 | + restore() |
642 | + c.Assert(i, gc.Equals, 99) |
643 | +} |
644 | + |
645 | +func (*SetSuite) TestSetError(c *gc.C) { |
646 | + oldErr := errors.New("foo") |
647 | + newErr := errors.New("bar") |
648 | + err := oldErr |
649 | + restore := jc.Set(&err, newErr) |
650 | + c.Assert(err, gc.Equals, newErr) |
651 | + restore() |
652 | + c.Assert(err, gc.Equals, oldErr) |
653 | +} |
654 | + |
655 | +func (*SetSuite) TestSetErrorToNil(c *gc.C) { |
656 | + oldErr := errors.New("foo") |
657 | + err := oldErr |
658 | + restore := jc.Set(&err, nil) |
659 | + c.Assert(err, gc.Equals, nil) |
660 | + restore() |
661 | + c.Assert(err, gc.Equals, oldErr) |
662 | +} |
663 | + |
664 | +func (*SetSuite) TestSetMapToNil(c *gc.C) { |
665 | + oldMap := map[string]int{"foo": 1234} |
666 | + m := oldMap |
667 | + restore := jc.Set(&m, nil) |
668 | + c.Assert(m, gc.IsNil) |
669 | + restore() |
670 | + c.Assert(m, gc.DeepEquals, oldMap) |
671 | +} |
672 | + |
673 | +func (*SetSuite) TestSetPanicsWhenNotAssignable(c *gc.C) { |
674 | + i := 99 |
675 | + type otherInt int |
676 | + c.Assert(func() { jc.Set(&i, otherInt(88)) }, gc.PanicMatches, `reflect\.Set: value of type checkers_test\.otherInt is not assignable to type int`) |
677 | +} |
678 | |
679 | === modified file 'testing/mgo.go' |
680 | --- testing/mgo.go 2013-08-27 07:28:22 +0000 |
681 | +++ testing/mgo.go 2013-09-13 09:26:01 +0000 |
682 | @@ -57,7 +57,6 @@ |
683 | } |
684 | |
685 | // startMgoServer starts a MongoDB server in a temporary directory. |
686 | -// It panics if it encounters an error. |
687 | func startMgoServer() error { |
688 | dbdir, err := ioutil.TempDir("", "test-mgo") |
689 | if err != nil { |
690 | @@ -69,12 +68,30 @@ |
691 | return fmt.Errorf("cannot write cert/key PEM: %v", err) |
692 | } |
693 | MgoPort = FindTCPPort() |
694 | + MgoAddr = fmt.Sprintf("localhost:%d", MgoPort) |
695 | + mgoDir = dbdir |
696 | + if err := runMgoServer(); err != nil { |
697 | + MgoAddr = "" |
698 | + MgoPort = 0 |
699 | + os.RemoveAll(mgoDir) |
700 | + mgoDir = "" |
701 | + return err |
702 | + } |
703 | + return nil |
704 | +} |
705 | + |
706 | +// runMgoServer runs the MongoDB server at the |
707 | +// address and directory already configured. |
708 | +func runMgoServer() error { |
709 | + if mgoServer != nil { |
710 | + panic("mongo server is already running") |
711 | + } |
712 | mgoport := strconv.Itoa(MgoPort) |
713 | mgoargs := []string{ |
714 | "--auth", |
715 | - "--dbpath", dbdir, |
716 | + "--dbpath", mgoDir, |
717 | "--sslOnNormalPorts", |
718 | - "--sslPEMKeyFile", pemPath, |
719 | + "--sslPEMKeyFile", filepath.Join(mgoDir, "server.pem"), |
720 | "--sslPEMKeyPassword", "ignored", |
721 | "--bind_ip", "localhost", |
722 | "--port", mgoport, |
723 | @@ -106,21 +123,33 @@ |
724 | }() |
725 | mgoExited = exited |
726 | if err := server.Start(); err != nil { |
727 | - os.RemoveAll(dbdir) |
728 | return err |
729 | } |
730 | - MgoAddr = "localhost:" + mgoport |
731 | mgoServer = server |
732 | - mgoDir = dbdir |
733 | return nil |
734 | } |
735 | |
736 | +func mgoKill() { |
737 | + mgoServer.Process.Kill() |
738 | + <-mgoExited |
739 | + mgoServer = nil |
740 | + mgoExited = nil |
741 | +} |
742 | + |
743 | func destroyMgoServer() { |
744 | if mgoServer != nil { |
745 | - mgoServer.Process.Kill() |
746 | - <-mgoExited |
747 | + mgoKill() |
748 | os.RemoveAll(mgoDir) |
749 | - MgoAddr, mgoServer, mgoExited, mgoDir = "", nil, nil, "" |
750 | + MgoAddr, mgoDir = "", "" |
751 | + } |
752 | +} |
753 | + |
754 | +// MgoRestart restarts the mongo server, useful for |
755 | +// testing what happens when a state server goes down. |
756 | +func MgoRestart() { |
757 | + mgoKill() |
758 | + if err := startMgoServer(); err != nil { |
759 | + panic(err) |
760 | } |
761 | } |
762 |
Reviewers: mp+185445_ code.launchpad. net,
Message:
Please take a look.
Description:
cmd/jujud: avoid using allFatal
This is a backport of the /code.launchpad .net/~rogpeppe/ juju-core/ 384-avoid- allfatal/ +merge/ 184743
https:/
now proposed for 1.14, with selected other patches from other revisions
to make it compile.
https:/ /code.launchpad .net/~rogpeppe/ juju-core/ 391-1.14- avoid-allFatal/ +merge/ 185445
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/13696043/
Affected files (+315, -65 lines): agent_test. go machine. go machine_ test.go conn.go apiclient. go /pinger_ test.go checkers/ set.go checkers/ set_test. go
A [revision details]
M cmd/jujud/agent.go
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/unit.go
M juju/testing/
M state/api/
M state/apiserver
M state/state.go
M state/state_test.go
A testing/
A testing/
M testing/mgo.go