Merge lp:~rogpeppe/juju-core/391-1.14-avoid-allFatal into lp:juju-core/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
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://code.launchpad.net/~rogpeppe/juju-core/384-avoid-allfatal/+merge/184743
now proposed for 1.14, with selected other patches from other revisions
to make it compile.

https://codereview.appspot.com/13696043/

Description of the change

cmd/jujud: avoid using allFatal

This is a backport of the https://code.launchpad.net/~rogpeppe/juju-core/384-avoid-allfatal/+merge/184743
now proposed for 1.14, with selected other patches from other revisions
to make it compile.

https://codereview.appspot.com/13696043/

To post a comment you must log in.
Revision history for this message
Roger Peppe (rogpeppe) wrote :

Reviewers: mp+185445_code.launchpad.net,

Message:
Please take a look.

Description:
cmd/jujud: avoid using allFatal

This is a backport of the
https://code.launchpad.net/~rogpeppe/juju-core/384-avoid-allfatal/+merge/184743
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):
   A [revision details]
   M cmd/jujud/agent.go
   M cmd/jujud/agent_test.go
   M cmd/jujud/machine.go
   M cmd/jujud/machine_test.go
   M cmd/jujud/unit.go
   M juju/testing/conn.go
   M state/api/apiclient.go
   M state/apiserver/pinger_test.go
   M state/state.go
   M state/state_test.go
   A testing/checkers/set.go
   A testing/checkers/set_test.go
   M testing/mgo.go

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
=== modified file 'cmd/jujud/agent.go'
--- cmd/jujud/agent.go 2013-09-11 06:27:05 +0000
+++ cmd/jujud/agent.go 2013-09-13 09:26:01 +0000
@@ -111,6 +111,27 @@
111 return ok111 return ok
112}112}
113113
114type pinger interface {
115 Ping() error
116}
117
118// connectionIsFatal returns a function suitable for passing
119// as the isFatal argument to worker.NewRunner,
120// that diagnoses an error as fatal if the connection
121// has failed or if the error is otherwise fatal.
122func connectionIsFatal(conn pinger) func(err error) bool {
123 return func(err error) bool {
124 if isFatal(err) {
125 return true
126 }
127 if err := conn.Ping(); err != nil {
128 log.Infof("error pinging %T: %v", conn, err)
129 return true
130 }
131 return false
132 }
133}
134
114// isleep waits for the given duration or until it receives a value on135// isleep waits for the given duration or until it receives a value on
115// stop. It returns whether the full duration was slept without being136// stop. It returns whether the full duration was slept without being
116// stopped.137// stopped.
117138
=== modified file 'cmd/jujud/agent_test.go'
--- cmd/jujud/agent_test.go 2013-08-24 13:50:05 +0000
+++ cmd/jujud/agent_test.go 2013-09-13 09:26:01 +0000
@@ -20,7 +20,9 @@
20 envtools "launchpad.net/juju-core/environs/tools"20 envtools "launchpad.net/juju-core/environs/tools"
21 "launchpad.net/juju-core/juju/testing"21 "launchpad.net/juju-core/juju/testing"
22 "launchpad.net/juju-core/state"22 "launchpad.net/juju-core/state"
23 "launchpad.net/juju-core/state/api/params"
23 coretesting "launchpad.net/juju-core/testing"24 coretesting "launchpad.net/juju-core/testing"
25 jc "launchpad.net/juju-core/testing/checkers"
24 coretools "launchpad.net/juju-core/tools"26 coretools "launchpad.net/juju-core/tools"
25 "launchpad.net/juju-core/version"27 "launchpad.net/juju-core/version"
26 "launchpad.net/juju-core/worker"28 "launchpad.net/juju-core/worker"
@@ -48,6 +50,70 @@
48 }50 }
49}51}
5052
53var isFatalTests = []struct {
54 err error
55 isFatal bool
56}{{
57 err: worker.ErrTerminateAgent,
58 isFatal: true,
59}, {
60 err: &upgrader.UpgradeReadyError{},
61 isFatal: true,
62}, {
63 err: &params.Error{
64 Message: "blah",
65 Code: params.CodeNotProvisioned,
66 },
67 isFatal: true,
68}, {
69 err: &fatalError{"some fatal error"},
70 isFatal: true,
71}, {
72 err: stderrors.New("foo"),
73 isFatal: false,
74}, {
75 err: &params.Error{
76 Message: "blah",
77 Code: params.CodeNotFound,
78 },
79 isFatal: false,
80}}
81
82func (*toolSuite) TestIsFatal(c *gc.C) {
83 for i, test := range isFatalTests {
84 c.Logf("test %d: %s", i, test.err)
85 c.Assert(isFatal(test.err), gc.Equals, test.isFatal)
86 }
87}
88
89type testPinger func() error
90
91func (f testPinger) Ping() error {
92 return f()
93}
94
95func (s *toolSuite) TestConnectionIsFatal(c *gc.C) {
96 var (
97 errPinger testPinger = func() error {
98 return stderrors.New("ping error")
99 }
100 okPinger testPinger = func() error {
101 return nil
102 }
103 )
104 for i, pinger := range []testPinger{errPinger, okPinger} {
105 for j, test := range isFatalTests {
106 c.Logf("test %d.%d: %s", i, j, test.err)
107 fatal := connectionIsFatal(pinger)(test.err)
108 if test.isFatal {
109 c.Check(fatal, jc.IsTrue)
110 } else {
111 c.Check(fatal, gc.Equals, i == 0)
112 }
113 }
114 }
115}
116
51func mkTools(s string) *coretools.Tools {117func mkTools(s string) *coretools.Tools {
52 return &coretools.Tools{118 return &coretools.Tools{
53 Version: version.MustParseBinary(s + "-foo-bar"),119 Version: version.MustParseBinary(s + "-foo-bar"),
54120
=== modified file 'cmd/jujud/machine.go'
--- cmd/jujud/machine.go 2013-09-11 06:27:05 +0000
+++ cmd/jujud/machine.go 2013-09-13 09:26:01 +0000
@@ -34,6 +34,16 @@
34 "launchpad.net/juju-core/worker/upgrader"34 "launchpad.net/juju-core/worker/upgrader"
35)35)
3636
37type workerRunner interface {
38 worker.Worker
39 StartWorker(id string, startFunc func() (worker.Worker, error)) error
40 StopWorker(id string) error
41}
42
43var newRunner = func(isFatal func(error) bool, moreImportant func(e0, e1 error) bool) workerRunner {
44 return worker.NewRunner(isFatal, moreImportant)
45}
46
37const bootstrapMachineId = "0"47const bootstrapMachineId = "0"
3848
39var retryDelay = 3 * time.Second49var retryDelay = 3 * time.Second
@@ -44,7 +54,7 @@
44 tomb tomb.Tomb54 tomb tomb.Tomb
45 Conf AgentConf55 Conf AgentConf
46 MachineId string56 MachineId string
47 runner *worker.Runner57 runner workerRunner
48}58}
4959
50// Info returns usage information for the command.60// Info returns usage information for the command.
@@ -68,7 +78,7 @@
68 if err := a.Conf.checkArgs(args); err != nil {78 if err := a.Conf.checkArgs(args); err != nil {
69 return err79 return err
70 }80 }
71 a.runner = worker.NewRunner(isFatal, moreImportant)81 a.runner = newRunner(isFatal, moreImportant)
72 return nil82 return nil
73}83}
7484
@@ -127,10 +137,6 @@
127 return err137 return err
128}138}
129139
130func allFatal(error) bool {
131 return true
132}
133
134var stateJobs = map[params.MachineJob]bool{140var stateJobs = map[params.MachineJob]bool{
135 params.JobHostUnits: true,141 params.JobHostUnits: true,
136 params.JobManageEnviron: true,142 params.JobManageEnviron: true,
@@ -164,7 +170,7 @@
164 if needsStateWorker {170 if needsStateWorker {
165 ensureStateWorker()171 ensureStateWorker()
166 }172 }
167 runner := worker.NewRunner(allFatal, moreImportant)173 runner := newRunner(connectionIsFatal(st), moreImportant)
168 // Only the machiner currently connects to the API.174 // Only the machiner currently connects to the API.
169 // Add other workers here as they are ready.175 // Add other workers here as they are ready.
170 runner.StartWorker("machiner", func() (worker.Worker, error) {176 runner.StartWorker("machiner", func() (worker.Worker, error) {
@@ -208,7 +214,7 @@
208 // TODO(rog) use more discriminating test for errors214 // TODO(rog) use more discriminating test for errors
209 // rather than taking everything down indiscriminately.215 // rather than taking everything down indiscriminately.
210 dataDir := a.Conf.dataDir216 dataDir := a.Conf.dataDir
211 runner := worker.NewRunner(allFatal, moreImportant)217 runner := newRunner(connectionIsFatal(st), moreImportant)
212 // At this stage, since we don't embed lxc containers, just start an lxc218 // At this stage, since we don't embed lxc containers, just start an lxc
213 // provisioner task for non-lxc containers. Since we have only LXC219 // provisioner task for non-lxc containers. Since we have only LXC
214 // containers and normal machines, this effectively means that we only220 // containers and normal machines, this effectively means that we only
215221
=== modified file 'cmd/jujud/machine_test.go'
--- cmd/jujud/machine_test.go 2013-09-11 06:27:05 +0000
+++ cmd/jujud/machine_test.go 2013-09-13 09:26:01 +0000
@@ -24,7 +24,7 @@
24 "launchpad.net/juju-core/state/api/params"24 "launchpad.net/juju-core/state/api/params"
25 "launchpad.net/juju-core/state/watcher"25 "launchpad.net/juju-core/state/watcher"
26 "launchpad.net/juju-core/testing"26 "launchpad.net/juju-core/testing"
27 "launchpad.net/juju-core/testing/checkers"27 jc "launchpad.net/juju-core/testing/checkers"
28 "launchpad.net/juju-core/tools"28 "launchpad.net/juju-core/tools"
29 "launchpad.net/juju-core/version"29 "launchpad.net/juju-core/version"
30 "launchpad.net/juju-core/worker/deployer"30 "launchpad.net/juju-core/worker/deployer"
@@ -33,7 +33,7 @@
33type MachineSuite struct {33type MachineSuite struct {
34 agentSuite34 agentSuite
35 lxc.TestSuite35 lxc.TestSuite
36 oldCacheDir string36 restoreCacheDir jc.Restorer
37}37}
3838
39var _ = gc.Suite(&MachineSuite{})39var _ = gc.Suite(&MachineSuite{})
@@ -41,11 +41,11 @@
41func (s *MachineSuite) SetUpSuite(c *gc.C) {41func (s *MachineSuite) SetUpSuite(c *gc.C) {
42 s.agentSuite.SetUpSuite(c)42 s.agentSuite.SetUpSuite(c)
43 s.TestSuite.SetUpSuite(c)43 s.TestSuite.SetUpSuite(c)
44 s.oldCacheDir = charm.CacheDir44 s.restoreCacheDir = jc.Set(&charm.CacheDir, c.MkDir())
45}45}
4646
47func (s *MachineSuite) TearDownSuite(c *gc.C) {47func (s *MachineSuite) TearDownSuite(c *gc.C) {
48 charm.CacheDir = s.oldCacheDir48 s.restoreCacheDir()
49 s.TestSuite.TearDownSuite(c)49 s.TestSuite.TearDownSuite(c)
50 s.agentSuite.TearDownSuite(c)50 s.agentSuite.TearDownSuite(c)
51}51}
@@ -231,7 +231,7 @@
231 if err == nil && attempt.HasNext() {231 if err == nil && attempt.HasNext() {
232 continue232 continue
233 }233 }
234 c.Assert(err, checkers.Satisfies, errors.IsNotFoundError)234 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
235 }235 }
236236
237 // short-circuit-remove u1 after it's been deployed; check it's recalled237 // short-circuit-remove u1 after it's been deployed; check it's recalled
@@ -239,7 +239,7 @@
239 err = u1.Destroy()239 err = u1.Destroy()
240 c.Assert(err, gc.IsNil)240 c.Assert(err, gc.IsNil)
241 err = u1.Refresh()241 err = u1.Refresh()
242 c.Assert(err, checkers.Satisfies, errors.IsNotFoundError)242 c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
243 ctx.waitDeployed(c)243 ctx.waitDeployed(c)
244}244}
245245
246246
=== modified file 'cmd/jujud/unit.go'
--- cmd/jujud/unit.go 2013-09-11 06:27:05 +0000
+++ cmd/jujud/unit.go 2013-09-13 09:26:01 +0000
@@ -85,7 +85,7 @@
85 }85 }
86 unit := entity.(*state.Unit)86 unit := entity.(*state.Unit)
87 dataDir := a.Conf.dataDir87 dataDir := a.Conf.dataDir
88 runner := worker.NewRunner(allFatal, moreImportant)88 runner := worker.NewRunner(connectionIsFatal(st), moreImportant)
89 runner.StartWorker("uniter", func() (worker.Worker, error) {89 runner.StartWorker("uniter", func() (worker.Worker, error) {
90 return uniter.NewUniter(st, unit.Name(), dataDir), nil90 return uniter.NewUniter(st, unit.Name(), dataDir), nil
91 })91 })
@@ -98,7 +98,7 @@
98 return nil, err98 return nil, err
99 }99 }
100 dataDir := a.Conf.dataDir100 dataDir := a.Conf.dataDir
101 runner := worker.NewRunner(allFatal, moreImportant)101 runner := worker.NewRunner(connectionIsFatal(st), moreImportant)
102 runner.StartWorker("upgrader", func() (worker.Worker, error) {102 runner.StartWorker("upgrader", func() (worker.Worker, error) {
103 return upgrader.New(st.Upgrader(), entity.Tag(), dataDir), nil103 return upgrader.New(st.Upgrader(), entity.Tag(), dataDir), nil
104 })104 })
105105
=== modified file 'juju/testing/conn.go'
--- juju/testing/conn.go 2013-09-11 06:27:05 +0000
+++ juju/testing/conn.go 2013-09-13 09:26:01 +0000
@@ -9,7 +9,7 @@
9 "os"9 "os"
10 "path/filepath"10 "path/filepath"
1111
12 . "launchpad.net/gocheck"12 gc "launchpad.net/gocheck"
1313
14 "launchpad.net/juju-core/charm"14 "launchpad.net/juju-core/charm"
15 "launchpad.net/juju-core/constraints"15 "launchpad.net/juju-core/constraints"
@@ -85,13 +85,13 @@
8585
86// StartInstance is a test helper function that starts an instance on the86// StartInstance is a test helper function that starts an instance on the
87// environment using the current series and invalid info states.87// environment using the current series and invalid info states.
88func StartInstance(c *C, env environs.Environ, machineId string) (instance.Instance, *instance.HardwareCharacteristics) {88func StartInstance(c *gc.C, env environs.Environ, machineId string) (instance.Instance, *instance.HardwareCharacteristics) {
89 return StartInstanceWithConstraints(c, env, machineId, constraints.Value{})89 return StartInstanceWithConstraints(c, env, machineId, constraints.Value{})
90}90}
9191
92// StartInstanceWithConstraints is a test helper function that starts an instance on the92// StartInstanceWithConstraints is a test helper function that starts an instance on the
93// environment with the specified constraints, using the current series and invalid info states.93// environment with the specified constraints, using the current series and invalid info states.
94func StartInstanceWithConstraints(c *C, env environs.Environ, machineId string,94func StartInstanceWithConstraints(c *gc.C, env environs.Environ, machineId string,
95 cons constraints.Value) (instance.Instance, *instance.HardwareCharacteristics) {95 cons constraints.Value) (instance.Instance, *instance.HardwareCharacteristics) {
96 series := config.DefaultSeries96 series := config.DefaultSeries
97 inst, metadata, err := provider.StartInstance(97 inst, metadata, err := provider.StartInstance(
@@ -103,7 +103,7 @@
103 FakeStateInfo(machineId),103 FakeStateInfo(machineId),
104 FakeAPIInfo(machineId),104 FakeAPIInfo(machineId),
105 )105 )
106 c.Assert(err, IsNil)106 c.Assert(err, gc.IsNil)
107 return inst, metadata107 return inst, metadata
108}108}
109109
@@ -119,24 +119,24 @@
119 agent-version: %s119 agent-version: %s
120`120`
121121
122func (s *JujuConnSuite) SetUpSuite(c *C) {122func (s *JujuConnSuite) SetUpSuite(c *gc.C) {
123 s.LoggingSuite.SetUpSuite(c)123 s.LoggingSuite.SetUpSuite(c)
124 s.MgoSuite.SetUpSuite(c)124 s.MgoSuite.SetUpSuite(c)
125}125}
126126
127func (s *JujuConnSuite) TearDownSuite(c *C) {127func (s *JujuConnSuite) TearDownSuite(c *gc.C) {
128 s.MgoSuite.TearDownSuite(c)128 s.MgoSuite.TearDownSuite(c)
129 s.LoggingSuite.TearDownSuite(c)129 s.LoggingSuite.TearDownSuite(c)
130}130}
131131
132func (s *JujuConnSuite) SetUpTest(c *C) {132func (s *JujuConnSuite) SetUpTest(c *gc.C) {
133 s.oldJujuHome = config.SetJujuHome(c.MkDir())133 s.oldJujuHome = config.SetJujuHome(c.MkDir())
134 s.LoggingSuite.SetUpTest(c)134 s.LoggingSuite.SetUpTest(c)
135 s.MgoSuite.SetUpTest(c)135 s.MgoSuite.SetUpTest(c)
136 s.setUpConn(c)136 s.setUpConn(c)
137}137}
138138
139func (s *JujuConnSuite) TearDownTest(c *C) {139func (s *JujuConnSuite) TearDownTest(c *gc.C) {
140 s.tearDownConn(c)140 s.tearDownConn(c)
141 s.MgoSuite.TearDownTest(c)141 s.MgoSuite.TearDownTest(c)
142 s.LoggingSuite.TearDownTest(c)142 s.LoggingSuite.TearDownTest(c)
@@ -145,51 +145,63 @@
145145
146// Reset returns environment state to that which existed at the start of146// Reset returns environment state to that which existed at the start of
147// the test.147// the test.
148func (s *JujuConnSuite) Reset(c *C) {148func (s *JujuConnSuite) Reset(c *gc.C) {
149 s.tearDownConn(c)149 s.tearDownConn(c)
150 s.setUpConn(c)150 s.setUpConn(c)
151}151}
152152
153func (s *JujuConnSuite) StateInfo(c *C) *state.Info {153func (s *JujuConnSuite) StateInfo(c *gc.C) *state.Info {
154 info, _, err := s.Conn.Environ.StateInfo()154 info, _, err := s.Conn.Environ.StateInfo()
155 c.Assert(err, IsNil)155 c.Assert(err, gc.IsNil)
156 info.Password = "dummy-secret"156 info.Password = "dummy-secret"
157 return info157 return info
158}158}
159159
160func (s *JujuConnSuite) APIInfo(c *C) *api.Info {160func (s *JujuConnSuite) APIInfo(c *gc.C) *api.Info {
161 _, apiInfo, err := s.APIConn.Environ.StateInfo()161 _, apiInfo, err := s.APIConn.Environ.StateInfo()
162 c.Assert(err, IsNil)162 c.Assert(err, gc.IsNil)
163 apiInfo.Tag = "user-admin"163 apiInfo.Tag = "user-admin"
164 apiInfo.Password = "dummy-secret"164 apiInfo.Password = "dummy-secret"
165 return apiInfo165 return apiInfo
166}166}
167167
168func (s *JujuConnSuite) openAPIAs(c *C, tag, password, nonce string) *api.State {168func (s *JujuConnSuite) openAPIAs(c *gc.C, tag, password, nonce string) *api.State {
169 _, info, err := s.APIConn.Environ.StateInfo()169 _, info, err := s.APIConn.Environ.StateInfo()
170 c.Assert(err, IsNil)170 c.Assert(err, gc.IsNil)
171 info.Tag = tag171 info.Tag = tag
172 info.Password = password172 info.Password = password
173 info.Nonce = nonce173 info.Nonce = nonce
174 st, err := api.Open(info, api.DialOpts{})174 st, err := api.Open(info, api.DialOpts{})
175 c.Assert(err, IsNil)175 c.Assert(err, gc.IsNil)
176 c.Assert(st, NotNil)176 c.Assert(st, gc.NotNil)
177 return st177 return st
178}178}
179179
180// OpenAPIAs opens the API using the given identity tag180// OpenAPIAs opens the API using the given identity tag
181// and password for authentication.181// and password for authentication.
182func (s *JujuConnSuite) OpenAPIAs(c *C, tag, password string) *api.State {182func (s *JujuConnSuite) OpenAPIAs(c *gc.C, tag, password string) *api.State {
183 return s.openAPIAs(c, tag, password, "")183 return s.openAPIAs(c, tag, password, "")
184}184}
185185
186// OpenAPIAsMachine opens the API using the given machine tag,186// OpenAPIAsMachine opens the API using the given machine tag,
187// password and nonce for authentication.187// password and nonce for authentication.
188func (s *JujuConnSuite) OpenAPIAsMachine(c *C, tag, password, nonce string) *api.State {188func (s *JujuConnSuite) OpenAPIAsMachine(c *gc.C, tag, password, nonce string) *api.State {
189 return s.openAPIAs(c, tag, password, nonce)189 return s.openAPIAs(c, tag, password, nonce)
190}190}
191191
192func (s *JujuConnSuite) setUpConn(c *C) {192// OpenAPIAsNewMachine creates a new machine entry that lives in system state, and
193// then uses that to open the API.
194func (s *JujuConnSuite) OpenAPIAsNewMachine(c *gc.C) (*api.State, *state.Machine) {
195 machine, err := s.State.AddMachine("series", state.JobHostUnits)
196 c.Assert(err, gc.IsNil)
197 err = machine.SetPassword("test-password")
198 c.Assert(err, gc.IsNil)
199 err = machine.SetProvisioned("foo", "fake_nonce", nil)
200 c.Assert(err, gc.IsNil)
201 return s.openAPIAs(c, machine.Tag(), "test-password", "fake_nonce"), machine
202}
203
204func (s *JujuConnSuite) setUpConn(c *gc.C) {
193 if s.RootDir != "" {205 if s.RootDir != "" {
194 panic("JujuConnSuite.setUpConn without teardown")206 panic("JujuConnSuite.setUpConn without teardown")
195 }207 }
@@ -197,38 +209,38 @@
197 s.oldHome = os.Getenv("HOME")209 s.oldHome = os.Getenv("HOME")
198 home := filepath.Join(s.RootDir, "/home/ubuntu")210 home := filepath.Join(s.RootDir, "/home/ubuntu")
199 err := os.MkdirAll(home, 0777)211 err := os.MkdirAll(home, 0777)
200 c.Assert(err, IsNil)212 c.Assert(err, gc.IsNil)
201 os.Setenv("HOME", home)213 os.Setenv("HOME", home)
202214
203 dataDir := filepath.Join(s.RootDir, "/var/lib/juju")215 dataDir := filepath.Join(s.RootDir, "/var/lib/juju")
204 err = os.MkdirAll(dataDir, 0777)216 err = os.MkdirAll(dataDir, 0777)
205 c.Assert(err, IsNil)217 c.Assert(err, gc.IsNil)
206218
207 yaml := []byte(fmt.Sprintf(envConfig, version.Current.Number))219 yaml := []byte(fmt.Sprintf(envConfig, version.Current.Number))
208 err = ioutil.WriteFile(config.JujuHomePath("environments.yaml"), yaml, 0600)220 err = ioutil.WriteFile(config.JujuHomePath("environments.yaml"), yaml, 0600)
209 c.Assert(err, IsNil)221 c.Assert(err, gc.IsNil)
210222
211 err = ioutil.WriteFile(config.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666)223 err = ioutil.WriteFile(config.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666)
212 c.Assert(err, IsNil)224 c.Assert(err, gc.IsNil)
213225
214 err = ioutil.WriteFile(config.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600)226 err = ioutil.WriteFile(config.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600)
215 c.Assert(err, IsNil)227 c.Assert(err, gc.IsNil)
216228
217 environ, err := environs.PrepareFromName("dummyenv")229 environ, err := environs.PrepareFromName("dummyenv")
218 c.Assert(err, IsNil)230 c.Assert(err, gc.IsNil)
219 // sanity check we've got the correct environment.231 // sanity check we've got the correct environment.
220 c.Assert(environ.Name(), Equals, "dummyenv")232 c.Assert(environ.Name(), gc.Equals, "dummyenv")
221 c.Assert(bootstrap.Bootstrap(environ, constraints.Value{}), IsNil)233 c.Assert(bootstrap.Bootstrap(environ, constraints.Value{}), gc.IsNil)
222234
223 s.BackingState = environ.(GetStater).GetStateInAPIServer()235 s.BackingState = environ.(GetStater).GetStateInAPIServer()
224236
225 conn, err := juju.NewConn(environ)237 conn, err := juju.NewConn(environ)
226 c.Assert(err, IsNil)238 c.Assert(err, gc.IsNil)
227 s.Conn = conn239 s.Conn = conn
228 s.State = conn.State240 s.State = conn.State
229241
230 apiConn, err := juju.NewAPIConn(environ, api.DialOpts{})242 apiConn, err := juju.NewAPIConn(environ, api.DialOpts{})
231 c.Assert(err, IsNil)243 c.Assert(err, gc.IsNil)
232 s.APIConn = apiConn244 s.APIConn = apiConn
233 s.APIState = apiConn.State245 s.APIState = apiConn.State
234 s.environ = environ246 s.environ = environ
@@ -238,7 +250,7 @@
238 GetStateInAPIServer() *state.State250 GetStateInAPIServer() *state.State
239}251}
240252
241func (s *JujuConnSuite) tearDownConn(c *C) {253func (s *JujuConnSuite) tearDownConn(c *gc.C) {
242 // Bootstrap will set the admin password, and render non-authorized use254 // Bootstrap will set the admin password, and render non-authorized use
243 // impossible. s.State may still hold the right password, so try to reset255 // impossible. s.State may still hold the right password, so try to reset
244 // the password so that the MgoSuite soft-resetting works. If that fails,256 // the password so that the MgoSuite soft-resetting works. If that fails,
@@ -247,8 +259,8 @@
247 if err := s.State.SetAdminMongoPassword(""); err != nil {259 if err := s.State.SetAdminMongoPassword(""); err != nil {
248 c.Logf("cannot reset admin password: %v", err)260 c.Logf("cannot reset admin password: %v", err)
249 }261 }
250 c.Assert(s.Conn.Close(), IsNil)262 c.Assert(s.Conn.Close(), gc.IsNil)
251 c.Assert(s.APIConn.Close(), IsNil)263 c.Assert(s.APIConn.Close(), gc.IsNil)
252 dummy.Reset()264 dummy.Reset()
253 s.Conn = nil265 s.Conn = nil
254 s.State = nil266 s.State = nil
@@ -276,13 +288,13 @@
276 }288 }
277}289}
278290
279func (s *JujuConnSuite) AddTestingCharm(c *C, name string) *state.Charm {291func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm {
280 ch := testing.Charms.Dir(name)292 ch := testing.Charms.Dir(name)
281 ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision())293 ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision())
282 curl := charm.MustParseURL("local:series/" + ident)294 curl := charm.MustParseURL("local:series/" + ident)
283 repo, err := charm.InferRepository(curl, testing.Charms.Path)295 repo, err := charm.InferRepository(curl, testing.Charms.Path)
284 c.Assert(err, IsNil)296 c.Assert(err, gc.IsNil)
285 sch, err := s.Conn.PutCharm(curl, repo, false)297 sch, err := s.Conn.PutCharm(curl, repo, false)
286 c.Assert(err, IsNil)298 c.Assert(err, gc.IsNil)
287 return sch299 return sch
288}300}
289301
=== modified file 'state/api/apiclient.go'
--- state/api/apiclient.go 2013-09-11 06:27:05 +0000
+++ state/api/apiclient.go 2013-09-13 09:26:01 +0000
@@ -133,11 +133,8 @@
133}133}
134134
135func (s *State) heartbeatMonitor() {135func (s *State) heartbeatMonitor() {
136 ping := func() error {
137 return s.Call("Pinger", "", "Ping", nil, nil)
138 }
139 for {136 for {
140 if err := ping(); err != nil {137 if err := s.Ping(); err != nil {
141 close(s.broken)138 close(s.broken)
142 return139 return
143 }140 }
@@ -145,6 +142,10 @@
145 }142 }
146}143}
147144
145func (s *State) Ping() error {
146 return s.Call("Pinger", "", "Ping", nil, nil)
147}
148
148// Call invokes a low-level RPC method of the given objType, id, and149// Call invokes a low-level RPC method of the given objType, id, and
149// request, passing the given parameters and filling in the response150// request, passing the given parameters and filling in the response
150// results. This should not be used directly by clients.151// results. This should not be used directly by clients.
151152
=== modified file 'state/apiserver/pinger_test.go'
--- state/apiserver/pinger_test.go 2013-09-11 06:27:05 +0000
+++ state/apiserver/pinger_test.go 2013-09-13 09:26:01 +0000
@@ -6,6 +6,7 @@
6import (6import (
7 gc "launchpad.net/gocheck"7 gc "launchpad.net/gocheck"
8 "launchpad.net/juju-core/juju/testing"8 "launchpad.net/juju-core/juju/testing"
9 "launchpad.net/juju-core/rpc"
9 "launchpad.net/juju-core/state"10 "launchpad.net/juju-core/state"
10 "launchpad.net/juju-core/state/api"11 "launchpad.net/juju-core/state/api"
11 "time"12 "time"
@@ -54,3 +55,14 @@
54 return55 return
55 }56 }
56}57}
58
59func (s *stateSuite) TestPing(c *gc.C) {
60 st, _ := s.OpenAPIAsNewMachine(c)
61 defer st.Close()
62 err := st.Ping()
63 c.Assert(err, gc.IsNil)
64 err = st.Close()
65 c.Assert(err, gc.IsNil)
66 err = st.Ping()
67 c.Assert(err, gc.Equals, rpc.ErrShutdown)
68}
5769
=== modified file 'state/state.go'
--- state/state.go 2013-09-11 06:27:05 +0000
+++ state/state.go 2013-09-13 09:26:01 +0000
@@ -102,6 +102,12 @@
102 return st.runner.Run(ops, "", nil)102 return st.runner.Run(ops, "", nil)
103}103}
104104
105// Ping probes the state's database connection to ensure
106// that it is still alive.
107func (st *State) Ping() error {
108 return st.db.Session.Ping()
109}
110
105func (st *State) Watch() *multiwatcher.Watcher {111func (st *State) Watch() *multiwatcher.Watcher {
106 st.mu.Lock()112 st.mu.Lock()
107 if st.allManager == nil {113 if st.allManager == nil {
108114
=== modified file 'state/state_test.go'
--- state/state_test.go 2013-09-11 06:27:05 +0000
+++ state/state_test.go 2013-09-13 09:26:01 +0000
@@ -63,6 +63,12 @@
63 c.Assert(s.State.CACert(), gc.DeepEquals, info.CACert)63 c.Assert(s.State.CACert(), gc.DeepEquals, info.CACert)
64}64}
6565
66func (s *StateSuite) TestPing(c *gc.C) {
67 c.Assert(s.State.Ping(), gc.IsNil)
68 testing.MgoRestart()
69 c.Assert(s.State.Ping(), gc.NotNil)
70}
71
66func (s *StateSuite) TestAPIAddresses(c *gc.C) {72func (s *StateSuite) TestAPIAddresses(c *gc.C) {
67 config, err := s.State.EnvironConfig()73 config, err := s.State.EnvironConfig()
68 c.Assert(err, gc.IsNil)74 c.Assert(err, gc.IsNil)
6975
=== added file 'testing/checkers/set.go'
--- testing/checkers/set.go 1970-01-01 00:00:00 +0000
+++ testing/checkers/set.go 2013-09-13 09:26:01 +0000
@@ -0,0 +1,35 @@
1package checkers
2
3import (
4 "reflect"
5)
6
7// Restorer holds a function that can be used
8// to restore some previous state.
9type Restorer func()
10
11// Restore restores some previous state.
12func (r Restorer) Restore() {
13 r()
14}
15
16// Set sets the value pointed to by the given
17// destination to the given value, and returns
18// a function to restore it to its original value.
19// The value must be assignable to the element
20// type of the destination.
21func Set(dest, value interface{}) Restorer {
22 destv := reflect.ValueOf(dest).Elem()
23 oldv := reflect.New(destv.Type()).Elem()
24 oldv.Set(destv)
25 valuev := reflect.ValueOf(value)
26 if !valuev.IsValid() {
27 // This isn't quite right when the destination type is not
28 // nilable, but it's better than the complex alternative.
29 valuev = reflect.Zero(destv.Type())
30 }
31 destv.Set(valuev)
32 return func() {
33 destv.Set(oldv)
34 }
35}
036
=== added file 'testing/checkers/set_test.go'
--- testing/checkers/set_test.go 1970-01-01 00:00:00 +0000
+++ testing/checkers/set_test.go 2013-09-13 09:26:01 +0000
@@ -0,0 +1,54 @@
1package checkers_test
2
3import (
4 "errors"
5
6 gc "launchpad.net/gocheck"
7 jc "launchpad.net/juju-core/testing/checkers"
8)
9
10type SetSuite struct{}
11
12var _ = gc.Suite(&SetSuite{})
13
14func (*SetSuite) TestSetInt(c *gc.C) {
15 i := 99
16 restore := jc.Set(&i, 88)
17 c.Assert(i, gc.Equals, 88)
18 restore()
19 c.Assert(i, gc.Equals, 99)
20}
21
22func (*SetSuite) TestSetError(c *gc.C) {
23 oldErr := errors.New("foo")
24 newErr := errors.New("bar")
25 err := oldErr
26 restore := jc.Set(&err, newErr)
27 c.Assert(err, gc.Equals, newErr)
28 restore()
29 c.Assert(err, gc.Equals, oldErr)
30}
31
32func (*SetSuite) TestSetErrorToNil(c *gc.C) {
33 oldErr := errors.New("foo")
34 err := oldErr
35 restore := jc.Set(&err, nil)
36 c.Assert(err, gc.Equals, nil)
37 restore()
38 c.Assert(err, gc.Equals, oldErr)
39}
40
41func (*SetSuite) TestSetMapToNil(c *gc.C) {
42 oldMap := map[string]int{"foo": 1234}
43 m := oldMap
44 restore := jc.Set(&m, nil)
45 c.Assert(m, gc.IsNil)
46 restore()
47 c.Assert(m, gc.DeepEquals, oldMap)
48}
49
50func (*SetSuite) TestSetPanicsWhenNotAssignable(c *gc.C) {
51 i := 99
52 type otherInt int
53 c.Assert(func() { jc.Set(&i, otherInt(88)) }, gc.PanicMatches, `reflect\.Set: value of type checkers_test\.otherInt is not assignable to type int`)
54}
055
=== modified file 'testing/mgo.go'
--- testing/mgo.go 2013-08-27 07:28:22 +0000
+++ testing/mgo.go 2013-09-13 09:26:01 +0000
@@ -57,7 +57,6 @@
57}57}
5858
59// startMgoServer starts a MongoDB server in a temporary directory.59// startMgoServer starts a MongoDB server in a temporary directory.
60// It panics if it encounters an error.
61func startMgoServer() error {60func startMgoServer() error {
62 dbdir, err := ioutil.TempDir("", "test-mgo")61 dbdir, err := ioutil.TempDir("", "test-mgo")
63 if err != nil {62 if err != nil {
@@ -69,12 +68,30 @@
69 return fmt.Errorf("cannot write cert/key PEM: %v", err)68 return fmt.Errorf("cannot write cert/key PEM: %v", err)
70 }69 }
71 MgoPort = FindTCPPort()70 MgoPort = FindTCPPort()
71 MgoAddr = fmt.Sprintf("localhost:%d", MgoPort)
72 mgoDir = dbdir
73 if err := runMgoServer(); err != nil {
74 MgoAddr = ""
75 MgoPort = 0
76 os.RemoveAll(mgoDir)
77 mgoDir = ""
78 return err
79 }
80 return nil
81}
82
83// runMgoServer runs the MongoDB server at the
84// address and directory already configured.
85func runMgoServer() error {
86 if mgoServer != nil {
87 panic("mongo server is already running")
88 }
72 mgoport := strconv.Itoa(MgoPort)89 mgoport := strconv.Itoa(MgoPort)
73 mgoargs := []string{90 mgoargs := []string{
74 "--auth",91 "--auth",
75 "--dbpath", dbdir,92 "--dbpath", mgoDir,
76 "--sslOnNormalPorts",93 "--sslOnNormalPorts",
77 "--sslPEMKeyFile", pemPath,94 "--sslPEMKeyFile", filepath.Join(mgoDir, "server.pem"),
78 "--sslPEMKeyPassword", "ignored",95 "--sslPEMKeyPassword", "ignored",
79 "--bind_ip", "localhost",96 "--bind_ip", "localhost",
80 "--port", mgoport,97 "--port", mgoport,
@@ -106,21 +123,33 @@
106 }()123 }()
107 mgoExited = exited124 mgoExited = exited
108 if err := server.Start(); err != nil {125 if err := server.Start(); err != nil {
109 os.RemoveAll(dbdir)
110 return err126 return err
111 }127 }
112 MgoAddr = "localhost:" + mgoport
113 mgoServer = server128 mgoServer = server
114 mgoDir = dbdir
115 return nil129 return nil
116}130}
117131
132func mgoKill() {
133 mgoServer.Process.Kill()
134 <-mgoExited
135 mgoServer = nil
136 mgoExited = nil
137}
138
118func destroyMgoServer() {139func destroyMgoServer() {
119 if mgoServer != nil {140 if mgoServer != nil {
120 mgoServer.Process.Kill()141 mgoKill()
121 <-mgoExited
122 os.RemoveAll(mgoDir)142 os.RemoveAll(mgoDir)
123 MgoAddr, mgoServer, mgoExited, mgoDir = "", nil, nil, ""143 MgoAddr, mgoDir = "", ""
144 }
145}
146
147// MgoRestart restarts the mongo server, useful for
148// testing what happens when a state server goes down.
149func MgoRestart() {
150 mgoKill()
151 if err := startMgoServer(); err != nil {
152 panic(err)
124 }153 }
125}154}
126155

Subscribers

People subscribed via source and target branches

to all changes: