Merge lp:~chipaca/ubuntu-push/client-v0-p3 into lp:ubuntu-push

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 50
Merged at revision: 46
Proposed branch: lp:~chipaca/ubuntu-push/client-v0-p3
Merge into: lp:ubuntu-push
Prerequisite: lp:~chipaca/ubuntu-push/client-v0-p2
Diff against target: 319 lines (+176/-18)
4 files modified
client/client.go (+42/-9)
client/client_test.go (+118/-1)
util/redialer.go (+14/-7)
util/redialer_test.go (+2/-1)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/client-v0-p3
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+204529@code.launchpad.net

Commit message

part 3: setting up the bus

Description of the change

In which we set up the D-Bus connections and event channels.

To post a comment you must log in.
lp:~chipaca/ubuntu-push/client-v0-p3 updated
49. By John Lenton

another rework to util/redialer

Revision history for this message
John Lenton (chipaca) wrote :

Also included for free in this branch: a rework to util/redialer that exposes it as a type.

Revision history for this message
Samuele Pedroni (pedronis) wrote :

needs to check somewhere that connCh is not nil?

Revision history for this message
Samuele Pedroni (pedronis) wrote :

takeTheBus has a lot of different styles, not sure what do do about that

265 +func (ar *AutoRetrier) AutoRetry() uint32 {

can probably just say Retry now?

Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve
Revision history for this message
Samuele Pedroni (pedronis) wrote :

need to double check somehow that the endpoints are taken on the right bus (system vs session)

lp:~chipaca/ubuntu-push/client-v0-p3 updated
50. By John Lenton

renamed AutoRetrier.AutoRetry to .Retry; checked that Client.connCh is not-nil after config.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'client/client.go'
2--- client/client.go 2014-02-03 12:23:56 +0000
3+++ client/client.go 2014-02-03 18:17:41 +0000
4@@ -22,28 +22,41 @@
5 "encoding/pem"
6 "fmt"
7 "io/ioutil"
8+ "launchpad.net/ubuntu-push/bus"
9 "launchpad.net/ubuntu-push/bus/connectivity"
10+ "launchpad.net/ubuntu-push/bus/networkmanager"
11+ "launchpad.net/ubuntu-push/bus/notifications"
12+ "launchpad.net/ubuntu-push/bus/urldispatcher"
13 "launchpad.net/ubuntu-push/config"
14 "launchpad.net/ubuntu-push/logger"
15+ "launchpad.net/ubuntu-push/util"
16 "launchpad.net/ubuntu-push/whoopsie/identifier"
17 "os"
18 )
19
20+// ClientConfig holds the client configuration
21 type ClientConfig struct {
22- connectivity.ConnectivityConfig
23- // session configuration
24+ connectivity.ConnectivityConfig // q.v.
25+ // A reasonably large maximum ping time
26 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
27- // server connection config
28- Addr config.ConfigHostPort
29+ // The server to connect to
30+ Addr config.ConfigHostPort
31+ // The PEM-encoded server certificate
32 CertPEMFile string `json:"cert_pem_file"`
33 }
34
35+// Client is the Ubuntu Push Notifications client-side daemon.
36 type Client struct {
37- config ClientConfig
38- log logger.Logger
39- pem []byte
40- idder identifier.Id
41- deviceId string
42+ config ClientConfig
43+ log logger.Logger
44+ pem []byte
45+ idder identifier.Id
46+ deviceId string
47+ notificationsEndp bus.Endpoint
48+ urlDispatcherEndp bus.Endpoint
49+ connectivityEndp bus.Endpoint
50+ connCh chan bool
51+ actionsCh <-chan notifications.RawActionReply
52 }
53
54 // Configure loads the configuration specified in configPath, and sets it up.
55@@ -61,6 +74,11 @@
56
57 // overridden for testing
58 client.idder = identifier.New()
59+ client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log)
60+ client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log)
61+ client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log)
62+
63+ client.connCh = make(chan bool)
64
65 if client.config.CertPEMFile != "" {
66 client.pem, err = ioutil.ReadFile(client.config.CertPEMFile)
67@@ -86,3 +104,18 @@
68 client.deviceId = client.idder.String()
69 return nil
70 }
71+
72+// takeTheBus starts the connection(s) to D-Bus and sets up associated event channels
73+func (client *Client) takeTheBus() error {
74+ go connectivity.ConnectedState(client.connectivityEndp,
75+ client.config.ConnectivityConfig, client.log, client.connCh)
76+ iniCh := make(chan uint32)
77+ go func() { iniCh <- util.AutoRedial(client.notificationsEndp) }()
78+ go func() { iniCh <- util.AutoRedial(client.urlDispatcherEndp) }()
79+ <-iniCh
80+ <-iniCh
81+
82+ actionsCh, err := notifications.Raw(client.notificationsEndp, client.log).WatchActions()
83+ client.actionsCh = actionsCh
84+ return err
85+}
86
87=== modified file 'client/client_test.go'
88--- client/client_test.go 2014-02-03 14:19:58 +0000
89+++ client/client_test.go 2014-02-03 18:17:41 +0000
90@@ -20,10 +20,16 @@
91 "fmt"
92 "io/ioutil"
93 . "launchpad.net/gocheck"
94+ "launchpad.net/ubuntu-push/bus/networkmanager"
95+ testibus "launchpad.net/ubuntu-push/bus/testing"
96 "launchpad.net/ubuntu-push/logger"
97 helpers "launchpad.net/ubuntu-push/testing"
98+ "launchpad.net/ubuntu-push/testing/condition"
99+ "launchpad.net/ubuntu-push/util"
100 "launchpad.net/ubuntu-push/whoopsie/identifier"
101 idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing"
102+ "net/http"
103+ "net/http/httptest"
104 "os"
105 "path/filepath"
106 "testing"
107@@ -32,16 +38,41 @@
108
109 func TestClient(t *testing.T) { TestingT(t) }
110
111+// takeNext takes a value from given channel with a 5s timeout
112+func takeNextBool(ch <-chan bool) bool {
113+ select {
114+ case <-time.After(5 * time.Second):
115+ panic("channel stuck: too long waiting")
116+ case v := <-ch:
117+ return v
118+ }
119+}
120+
121 type clientSuite struct {
122+ timeouts []time.Duration
123 configPath string
124 }
125
126 var nullog = logger.NewSimpleLogger(ioutil.Discard, "error")
127 var noisylog = logger.NewSimpleLogger(os.Stderr, "debug")
128-var debuglog = nullog
129 var _ = Suite(&clientSuite{})
130
131+const (
132+ staticText = "something ipsum dolor something"
133+ staticHash = "6155f83b471583f47c99998a472a178f"
134+)
135+
136+func mkHandler(text string) http.HandlerFunc {
137+ return func(w http.ResponseWriter, r *http.Request) {
138+ w.(http.Flusher).Flush()
139+ w.Write([]byte(text))
140+ w.(http.Flusher).Flush()
141+ }
142+}
143+
144 func (cs *clientSuite) SetUpTest(c *C) {
145+ cs.timeouts = util.Timeouts
146+ util.Timeouts = []time.Duration{0}
147 dir := c.MkDir()
148 cs.configPath = filepath.Join(dir, "config")
149 cfg := fmt.Sprintf(`
150@@ -57,6 +88,10 @@
151 ioutil.WriteFile(cs.configPath, []byte(cfg), 0600)
152 }
153
154+func (cs *clientSuite) TearDownTest(c *C) {
155+ util.Timeouts = cs.timeouts
156+}
157+
158 /*****************************************************************
159 Configure tests
160 ******************************************************************/
161@@ -93,6 +128,26 @@
162 c.Assert(cli.idder, DeepEquals, identifier.New())
163 }
164
165+func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) {
166+ cli := new(Client)
167+ c.Check(cli.notificationsEndp, IsNil)
168+ c.Check(cli.urlDispatcherEndp, IsNil)
169+ c.Check(cli.connectivityEndp, IsNil)
170+ err := cli.Configure(cs.configPath)
171+ c.Assert(err, IsNil)
172+ c.Assert(cli.notificationsEndp, NotNil)
173+ c.Assert(cli.urlDispatcherEndp, NotNil)
174+ c.Assert(cli.connectivityEndp, NotNil)
175+}
176+
177+func (cs *clientSuite) TestConfigureSetsUpConnCh(c *C) {
178+ cli := new(Client)
179+ c.Check(cli.connCh, IsNil)
180+ err := cli.Configure(cs.configPath)
181+ c.Assert(err, IsNil)
182+ c.Assert(cli.connCh, NotNil)
183+}
184+
185 func (cs *clientSuite) TestConfigureBailsOnBadFilename(c *C) {
186 cli := new(Client)
187 err := cli.Configure("/does/not/exist")
188@@ -157,3 +212,65 @@
189 c.Check(cli.deviceId, Equals, "")
190 c.Check(cli.getDeviceId(), NotNil)
191 }
192+
193+/*****************************************************************
194+ takeTheBus tests
195+******************************************************************/
196+
197+func (cs *clientSuite) TestTakeTheBusWorks(c *C) {
198+ // http server used for connectivity test
199+ ts := httptest.NewServer(mkHandler(staticText))
200+ defer ts.Close()
201+
202+ // testing endpoints
203+ nCond := condition.Fail2Work(3)
204+ nEndp := testibus.NewMultiValuedTestingEndpoint(nCond, condition.Work(true),
205+ []interface{}{uint32(1), "hello"})
206+ uCond := condition.Fail2Work(5)
207+ uEndp := testibus.NewTestingEndpoint(uCond, condition.Work(false))
208+ cCond := condition.Fail2Work(7)
209+ cEndp := testibus.NewTestingEndpoint(cCond, condition.Work(true),
210+ uint32(networkmanager.ConnectedGlobal),
211+ )
212+ // ok, create the thing
213+ cli := new(Client)
214+ err := cli.Configure(cs.configPath)
215+ c.Assert(err, IsNil)
216+ // the user actions channel has not been set up
217+ c.Check(cli.actionsCh, IsNil)
218+
219+ // and stomp on things for testing
220+ cli.config.ConnectivityConfig.ConnectivityCheckURL = ts.URL
221+ cli.config.ConnectivityConfig.ConnectivityCheckMD5 = staticHash
222+ cli.notificationsEndp = nEndp
223+ cli.urlDispatcherEndp = uEndp
224+ cli.connectivityEndp = cEndp
225+
226+ c.Assert(cli.takeTheBus(), IsNil)
227+ // the notifications and urldispatcher endpoints retried until connected
228+ c.Check(nCond.OK(), Equals, true)
229+ c.Check(uCond.OK(), Equals, true)
230+ // the user actions channel has now been set up
231+ c.Check(cli.actionsCh, NotNil)
232+ c.Check(takeNextBool(cli.connCh), Equals, false)
233+ c.Check(takeNextBool(cli.connCh), Equals, true)
234+ // the connectivity endpoint retried until connected
235+ c.Check(cCond.OK(), Equals, true)
236+}
237+
238+// takeTheBus can, in fact, fail
239+func (cs *clientSuite) TestTakeTheBusCanFail(c *C) {
240+ cli := new(Client)
241+ err := cli.Configure(cs.configPath)
242+ c.Assert(err, IsNil)
243+ // the user actions channel has not been set up
244+ c.Check(cli.actionsCh, IsNil)
245+
246+ // and stomp on things for testing
247+ cli.notificationsEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))
248+ cli.urlDispatcherEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))
249+ cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))
250+
251+ c.Check(cli.takeTheBus(), NotNil)
252+ c.Check(cli.actionsCh, IsNil)
253+}
254
255=== modified file 'util/redialer.go'
256--- util/redialer.go 2014-01-28 23:40:52 +0000
257+++ util/redialer.go 2014-02-03 18:17:41 +0000
258@@ -49,14 +49,20 @@
259 return time.Duration(rand.Int63n(2*n+1) - n)
260 }
261
262-// AutoRetry keeps on calling f() until it stops returning an error.
263-// It does exponential backoff, adding jitter at each step back.
264-func AutoRetry(f func() error, jitter func(time.Duration) time.Duration) uint32 {
265+type AutoRetrier struct {
266+ Stop chan bool
267+ Dial func() error
268+ Jitter func(time.Duration) time.Duration
269+}
270+
271+// AutoRetry keeps on calling Dial until it stops returning an error. It does
272+// exponential backoff, adding the output of Jitter at each step back.
273+func (ar *AutoRetrier) Retry() uint32 {
274 var timeout time.Duration
275 var dialAttempts uint32 = 0 // unsigned so it can wrap safely ...
276 var numTimeouts uint32 = uint32(len(Timeouts))
277 for {
278- if f() == nil {
279+ if ar.Dial() == nil {
280 return dialAttempts + 1
281 }
282 if dialAttempts < numTimeouts {
283@@ -64,10 +70,10 @@
284 } else {
285 timeout = Timeouts[numTimeouts-1]
286 }
287- timeout += jitter(timeout)
288+ timeout += ar.Jitter(timeout)
289 dialAttempts++
290 select {
291- case <-quitRedialing:
292+ case <-ar.Stop:
293 return dialAttempts
294 case <-time.NewTimer(timeout).C:
295 }
296@@ -78,7 +84,8 @@
297 // stops returning an error. It does exponential (optionally
298 // jitter'ed) backoff.
299 func AutoRedial(dialer Dialer) uint32 {
300- return AutoRetry(dialer.Dial, dialer.Jitter)
301+ ar := &AutoRetrier{quitRedialing, dialer.Dial, dialer.Jitter}
302+ return ar.Retry()
303 }
304
305 func init() {
306
307=== modified file 'util/redialer_test.go'
308--- util/redialer_test.go 2014-01-29 11:42:27 +0000
309+++ util/redialer_test.go 2014-02-03 18:17:41 +0000
310@@ -76,7 +76,8 @@
311 }
312 }
313 jitter := func(time.Duration) time.Duration { return 0 }
314- c.Check(AutoRetry(f, jitter), Equals, uint32(6))
315+ ar := &AutoRetrier{nil, f, jitter}
316+ c.Check(ar.Retry(), Equals, uint32(6))
317 }
318
319 func (s *RedialerSuite) TestJitter(c *C) {

Subscribers

People subscribed via source and target branches