Merge lp:~chipaca/ubuntu-push/release-0.67 into lp:ubuntu-push

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 140
Merged at revision: 140
Proposed branch: lp:~chipaca/ubuntu-push/release-0.67
Merge into: lp:ubuntu-push
Diff against target: 1442 lines (+374/-124)
46 files modified
bus/connectivity/connectivity.go (+23/-12)
bus/connectivity/connectivity_test.go (+10/-11)
bus/connectivity/webchecker.go (+8/-5)
bus/connectivity/webchecker_test.go (+3/-2)
bus/endpoint.go (+7/-7)
bus/networkmanager/networkmanager.go (+6/-6)
bus/notifications/raw.go (+1/-1)
bus/systemimage/systemimage.go (+1/-1)
bus/testing/testing_endpoint.go (+5/-1)
click/cclick/cclick.go (+1/-0)
client/client.go (+3/-3)
client/client_test.go (+18/-2)
client/service/postal.go (+0/-1)
client/service/service.go (+3/-3)
client/session/seenstate/seenstate.go (+6/-1)
client/session/seenstate/sqlseenstate.go (+5/-0)
client/session/seenstate/sqlseenstate_test.go (+9/-0)
client/session/session.go (+38/-2)
client/session/session_test.go (+1/-0)
debian/changelog (+40/-0)
dependencies.tsv (+1/-1)
docs/example-client/main.qml (+13/-1)
docs/example-client/manifest.json (+2/-2)
identifier/identifier.go (+1/-1)
identifier/identifier_test.go (+1/-1)
launch_helper/helper_finder/helper_finder.go (+1/-1)
launch_helper/helper_finder/helper_finder_test.go (+1/-1)
launch_helper/kindpool.go (+1/-1)
launch_helper/legacy/legacy.go (+1/-1)
launch_helper/legacy/legacy_test.go (+1/-1)
logger/logger.go (+12/-7)
logger/logger_test.go (+12/-0)
messaging/messaging.go (+1/-1)
poller/poller.go (+1/-1)
server/acceptance/acceptanceclient.go (+2/-0)
server/acceptance/kit/api.go (+14/-3)
server/acceptance/suites/helpers.go (+1/-1)
server/acceptance/suites/suite.go (+11/-3)
testing/helpers.go (+1/-1)
ubuntu-push-client.go (+18/-0)
urldispatcher/curldispatcher/curldispatcher.go (+1/-1)
urldispatcher/urldispatcher.go (+2/-2)
urldispatcher/urldispatcher_test.go (+2/-2)
util/redialer.go (+69/-26)
util/redialer_states.gv (+9/-0)
util/redialer_test.go (+7/-7)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/release-0.67
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+249049@code.launchpad.net

Commit message

  * Updated precommit script. [dev]

  * Include code examples in docs (instead of repeating). [docs]

  * Make tests more robust in the face of go 1.3 [client, server]

  * Introduce StartClientAuthFlex for acceptance tests: Start a client
    with auth, take a devId regexp, don't check any client event; support
    connbroken in acceptanceclient. [server]

  * Cleanup and improve logging, and make log messages more
    consistent. [client]

  * Partially work around bug lp:1390663 in a minimally intrusive way
    (real fix will have to wait). [client]

  * Add SIGQUIT handler to spit out stack dumps; more logging
    tweaks. [client, server]

  * Adds a couple of buttons to exercise more APIs, version bump to
    0.44. [sample app]

  * Add APIError to server/acceptance/kit that includes the body for
    debugging. [server]

  * Add DisableKeepAlives and MaxIdleConnsPerHost to the APIClient
    SetupClient method. [server]

  * Clean up goroutines in tests. [client]

  * Add an explicit check and log message for nil error on webcheck's CopyN. [client]

  * Log line nums, enabled when logLevel = debug. [client server]

  * Workaround gc issue with 1.3 and 32 bits. Fixes FTBFS. [client]

Description of the change

Merging from automatic.

  * Updated precommit script. [dev]

  * Include code examples in docs (instead of repeating). [docs]

  * Make tests more robust in the face of go 1.3 [client, server]

  * Introduce StartClientAuthFlex for acceptance tests: Start a client
    with auth, take a devId regexp, don't check any client event; support
    connbroken in acceptanceclient. [server]

  * Cleanup and improve logging, and make log messages more
    consistent. [client]

  * Partially work around bug lp:1390663 in a minimally intrusive way
    (real fix will have to wait). [client]

  * Add SIGQUIT handler to spit out stack dumps; more logging
    tweaks. [client, server]

  * Adds a couple of buttons to exercise more APIs, version bump to
    0.44. [sample app]

  * Add APIError to server/acceptance/kit that includes the body for
    debugging. [server]

  * Add DisableKeepAlives and MaxIdleConnsPerHost to the APIClient
    SetupClient method. [server]

  * Clean up goroutines in tests. [client]

  * Add an explicit check and log message for nil error on webcheck's CopyN. [client]

  * Log line nums, enabled when logLevel = debug. [client server]

  * Workaround gc issue with 1.3 and 32 bits. Fixes FTBFS. [client]

To post a comment you must log in.
lp:~chipaca/ubuntu-push/release-0.67 updated
141. By John Lenton

changelog polish.

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

Individual branches have been approved sepparately.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/connectivity/connectivity.go'
2--- bus/connectivity/connectivity.go 2014-07-04 19:12:38 +0000
3+++ bus/connectivity/connectivity.go 2015-02-09 10:47:29 +0000
4@@ -82,7 +82,7 @@
5 // Get the current state.
6 initial = nm.GetState()
7 if initial == networkmanager.Unknown {
8- cs.log.Debugf("Failed to get state.")
9+ cs.log.Debugf("failed to get state.")
10 goto Continue
11 }
12 cs.log.Debugf("got initial state of %s", initial)
13@@ -120,40 +120,51 @@
14 case <-cs.networkConCh:
15 cs.webgetCh = nil
16 cs.timer.Reset(stabilizingTimeout)
17- log.Debugf("PrimaryConnection changed. Assuming disconnect.")
18 if cs.lastSent == true {
19+ log.Debugf("connectivity: PrimaryConnection changed. lastSent: %v, sending 'disconnected'.", cs.lastSent)
20 cs.lastSent = false
21 break Loop
22+ } else {
23+ log.Debugf("connectivity: PrimaryConnection changed. lastSent: %v, Ignoring.", cs.lastSent)
24 }
25
26 case v, ok := <-cs.networkStateCh:
27+ // Handle only disconnecting here, connecting handled under the timer below
28 if !ok {
29 // tear it all down and start over
30 return false, errors.New("got not-OK from StateChanged watch")
31 }
32 cs.webgetCh = nil
33+ lastState := cs.currentState
34 cs.currentState = v
35- cs.timer.Reset(stabilizingTimeout)
36- log.Debugf("State changed to %s. Assuming disconnect.", v)
37- if cs.lastSent == true {
38- log.Infof("Sending 'disconnected'.")
39- cs.lastSent = false
40- break Loop
41+ // ignore Connecting (followed immediately by "Connected Global") and repeats
42+ if v != networkmanager.Connecting && lastState != v {
43+ cs.timer.Reset(stabilizingTimeout)
44+ log.Debugf("state changed to %s. Assuming disconnect.", v)
45+ if cs.lastSent == true {
46+ log.Debugf("connectivity: %s -> %s. lastSent: %v, sending 'disconnected'", lastState, v, cs.lastSent)
47+ cs.lastSent = false
48+ break Loop
49+ } else {
50+ log.Debugf("connectivity: %s -> %s. lastSent: %v, Ignoring.", lastState, v, cs.lastSent)
51+ }
52+ } else {
53+ log.Debugf("connectivity: %s -> %s. lastSent: %v, Ignoring.", lastState, v, cs.lastSent)
54 }
55
56 case <-cs.timer.C:
57 if cs.currentState == networkmanager.ConnectedGlobal {
58- log.Debugf("May be connected; checking...")
59+ log.Debugf("connectivity: timer signal, state: ConnectedGlobal, checking...")
60 cs.webgetCh = make(chan bool)
61 go cs.webget(cs.webgetCh)
62 }
63
64 case connected := <-cs.webgetCh:
65 cs.timer.Reset(recheckTimeout)
66- log.Debugf("Connection check says: %t", connected)
67+ log.Debugf("connectivity: connection check says: %t", connected)
68 cs.webgetCh = nil
69 if connected && cs.lastSent == false {
70- log.Infof("Sending 'connected'.")
71+ log.Debugf("connectivity: connection check ok, lastSent: %v, sending 'connected'.", cs.lastSent)
72 cs.lastSent = true
73 break Loop
74 }
75@@ -178,7 +189,7 @@
76 }
77
78 Start:
79- log.Infof("Sending initial 'disconnected'.")
80+ log.Debugf("sending initial 'disconnected'.")
81 out <- false
82 cs.lastSent = false
83 cs.currentState = cs.start()
84
85=== modified file 'bus/connectivity/connectivity_test.go'
86--- bus/connectivity/connectivity_test.go 2014-07-04 23:00:42 +0000
87+++ bus/connectivity/connectivity_test.go 2015-02-09 10:47:29 +0000
88@@ -217,20 +217,22 @@
89 f, e := cs.connectedStateStep()
90 c.Check(e, IsNil)
91 c.Check(f, Equals, true)
92- ch <- networkmanager.ConnectedGlobal // a ConnectedGlobal when connected signals trouble
93- f, e = cs.connectedStateStep()
94- c.Check(e, IsNil)
95- c.Check(f, Equals, false) // so we assume a disconnect happened
96- f, e = cs.connectedStateStep()
97- c.Check(e, IsNil)
98- c.Check(f, Equals, true) // and if the web check works, go back to connected
99+ ch <- networkmanager.Disconnected
100+ ch <- networkmanager.ConnectedGlobal
101+ f, e = cs.connectedStateStep()
102+ c.Check(e, IsNil)
103+ c.Check(f, Equals, false)
104+ f, e = cs.connectedStateStep()
105+ c.Check(e, IsNil)
106+ c.Check(f, Equals, true)
107
108 // same scenario, but with failing web check
109 webget_p = condition.Fail2Work(1)
110+ ch <- networkmanager.Disconnected
111 ch <- networkmanager.ConnectedGlobal
112 f, e = cs.connectedStateStep()
113 c.Check(e, IsNil)
114- c.Check(f, Equals, false) // first false is from assuming a Connected signals trouble
115+ c.Check(f, Equals, false) // first false is from the Disconnected
116
117 // the next call to Step will time out
118 _ch := make(chan bool, 1)
119@@ -284,7 +286,6 @@
120
121 endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true),
122 uint32(networkmanager.ConnectedGlobal),
123- uint32(networkmanager.ConnectedGlobal),
124 uint32(networkmanager.Disconnected),
125 )
126
127@@ -303,8 +304,6 @@
128 }{
129 {false, "first state is always false", 0},
130 {true, "then it should be true as per ConnectedGlobal above", 0},
131- {false, "then, false (upon receiving the next ConnectedGlobal)", 2},
132- {true, "then it should be true (webcheck passed)", 0},
133 {false, "then it should be false (Disconnected)", 2},
134 {false, "then it should be false again because it's restarted", 2},
135 }
136
137=== modified file 'bus/connectivity/webchecker.go'
138--- bus/connectivity/webchecker.go 2014-03-20 12:24:33 +0000
139+++ bus/connectivity/webchecker.go 2015-02-09 10:47:29 +0000
140@@ -64,7 +64,7 @@
141 func (wb *webchecker) Webcheck(ch chan<- bool) {
142 response, err := wb.cli.Get(wb.url)
143 if err != nil {
144- wb.log.Errorf("While GETting %s: %v", wb.url, err)
145+ wb.log.Errorf("while GETting %s: %v", wb.url, err)
146 ch <- false
147 return
148 }
149@@ -72,17 +72,20 @@
150 hash := md5.New()
151 _, err = io.CopyN(hash, response.Body, 1024)
152 if err != io.EOF {
153- wb.log.Errorf("Reading %s, expecting EOF, got: %v",
154- wb.url, err)
155+ if err == nil {
156+ wb.log.Errorf("reading %s, but response body is larger than 1k.", wb.url)
157+ } else {
158+ wb.log.Errorf("reading %s, expecting EOF, got: %v", wb.url, err)
159+ }
160 ch <- false
161 return
162 }
163 sum := fmt.Sprintf("%x", hash.Sum(nil))
164 if sum == wb.target {
165- wb.log.Infof("Connectivity check passed.")
166+ wb.log.Infof("connectivity check passed.")
167 ch <- true
168 } else {
169- wb.log.Infof("Connectivity check failed: content mismatch.")
170+ wb.log.Infof("connectivity check failed: content mismatch.")
171 ch <- false
172 }
173 }
174
175=== modified file 'bus/connectivity/webchecker_test.go'
176--- bus/connectivity/webchecker_test.go 2014-03-20 12:24:33 +0000
177+++ bus/connectivity/webchecker_test.go 2015-02-09 10:47:29 +0000
178@@ -18,7 +18,6 @@
179
180 import (
181 . "launchpad.net/gocheck"
182- "launchpad.net/ubuntu-push/logger"
183 helpers "launchpad.net/ubuntu-push/testing"
184 "launchpad.net/ubuntu-push/util"
185 "net/http"
186@@ -28,7 +27,7 @@
187
188 type WebcheckerSuite struct {
189 timeouts []time.Duration
190- log logger.Logger
191+ log *helpers.TestLogger
192 }
193
194 var _ = Suite(&WebcheckerSuite{})
195@@ -104,6 +103,7 @@
196 ch := make(chan bool, 1)
197 ck.Webcheck(ch)
198 c.Check(<-ch, Equals, false)
199+ c.Check(s.log.Captured(), Matches, "(?ism).*content mismatch.*")
200 }
201
202 // Webchecker sends false if the download is too big
203@@ -115,6 +115,7 @@
204 ch := make(chan bool, 1)
205 ck.Webcheck(ch)
206 c.Check(<-ch, Equals, false)
207+ c.Check(s.log.Captured(), Matches, "(?ism).*larger than 1k.*")
208 }
209
210 // Webchecker sends false if the request timeouts
211
212=== modified file 'bus/endpoint.go'
213--- bus/endpoint.go 2014-09-08 15:48:00 +0000
214+++ bus/endpoint.go 2015-02-09 10:47:29 +0000
215@@ -84,7 +84,7 @@
216 name := endp.addr.Name
217 hasOwner, err := d.NameHasOwner(name)
218 if err != nil {
219- endp.log.Debugf("Unable to determine ownership of %#v: %v", name, err)
220+ endp.log.Debugf("unable to determine ownership of %#v: %v", name, err)
221 bus.Close()
222 return err
223 }
224@@ -110,7 +110,7 @@
225 return errors.New(msg)
226 }
227 }
228- endp.log.Infof("%#v dialed in.", name)
229+ endp.log.Debugf("%#v dialed in.", name)
230 endp.bus = bus
231 endp.proxy = bus.Object(name, dbus.ObjectPath(endp.addr.Path))
232 return nil
233@@ -126,7 +126,7 @@
234 func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) error {
235 watch, err := endp.proxy.WatchSignal(endp.addr.Interface, member)
236 if err != nil {
237- endp.log.Debugf("Failed to set up the watch: %s", err)
238+ endp.log.Debugf("failed to set up the watch: %s", err)
239 return err
240 }
241
242@@ -167,15 +167,15 @@
243 variantvs := endp.unpackOneMsg(msg, property)
244 switch len(variantvs) {
245 default:
246- return nil, fmt.Errorf("Too many values in Properties.Get response: %d", len(variantvs))
247+ return nil, fmt.Errorf("too many values in Properties.Get response: %d", len(variantvs))
248 case 0:
249- return nil, fmt.Errorf("Not enough values in Properties.Get response: %d", len(variantvs))
250+ return nil, fmt.Errorf("not enough values in Properties.Get response: %d", len(variantvs))
251 case 1:
252 // carry on
253 }
254 variant, ok := variantvs[0].(*dbus.Variant)
255 if !ok {
256- return nil, fmt.Errorf("Response from Properties.Get wasn't a *dbus.Variant")
257+ return nil, fmt.Errorf("response from Properties.Get wasn't a *dbus.Variant")
258 }
259 return variant.Value, nil
260 }
261@@ -324,6 +324,6 @@
262 }
263 f(endp.unpackOneMsg(msg, member)...)
264 }
265- endp.log.Errorf("Got not-OK from %s watch", member)
266+ endp.log.Errorf("got not-OK from %s watch", member)
267 d()
268 }
269
270=== modified file 'bus/networkmanager/networkmanager.go'
271--- bus/networkmanager/networkmanager.go 2014-04-04 12:01:42 +0000
272+++ bus/networkmanager/networkmanager.go 2015-02-09 10:47:29 +0000
273@@ -71,14 +71,14 @@
274 func (nm *networkManager) GetState() State {
275 s, err := nm.bus.GetProperty("state")
276 if err != nil {
277- nm.log.Errorf("Failed gettting current state: %s", err)
278- nm.log.Debugf("Defaulting state to Unknown")
279+ nm.log.Errorf("failed gettting current state: %s", err)
280+ nm.log.Debugf("defaulting state to Unknown")
281 return Unknown
282 }
283
284 v, ok := s.(uint32)
285 if !ok {
286- nm.log.Errorf("Got weird state: %#v", s)
287+ nm.log.Errorf("got weird state: %#v", s)
288 return Unknown
289 }
290
291@@ -110,8 +110,8 @@
292 func (nm *networkManager) GetPrimaryConnection() string {
293 s, err := nm.bus.GetProperty("PrimaryConnection")
294 if err != nil {
295- nm.log.Errorf("Failed gettting current primary connection: %s", err)
296- nm.log.Debugf("Defaulting primary connection to empty")
297+ nm.log.Errorf("failed gettting current primary connection: %s", err)
298+ nm.log.Debugf("defaulting primary connection to empty")
299 return ""
300 }
301
302@@ -146,7 +146,7 @@
303 ch <- string(con)
304 }, func() { close(ch) })
305 if err != nil {
306- nm.log.Debugf("Failed to set up the watch: %s", err)
307+ nm.log.Debugf("failed to set up the watch: %s", err)
308 return nil, err
309 }
310
311
312=== modified file 'bus/notifications/raw.go'
313--- bus/notifications/raw.go 2014-08-15 10:33:04 +0000
314+++ bus/notifications/raw.go 2015-02-09 10:47:29 +0000
315@@ -119,7 +119,7 @@
316 ch <- action
317 }, func() { close(ch) })
318 if err != nil {
319- raw.log.Debugf("Failed to set up the watch: %s", err)
320+ raw.log.Debugf("failed to set up the watch: %s", err)
321 return nil, err
322 }
323 return ch, nil
324
325=== modified file 'bus/systemimage/systemimage.go'
326--- bus/systemimage/systemimage.go 2014-04-02 08:46:48 +0000
327+++ bus/systemimage/systemimage.go 2015-02-09 10:47:29 +0000
328@@ -57,7 +57,7 @@
329 var _ SystemImage = &systemImage{} // ensures it conforms
330
331 func (si *systemImage) Info() (*InfoResult, error) {
332- si.log.Debugf("Invoking Info")
333+ si.log.Debugf("invoking Info")
334 res := &InfoResult{}
335 err := si.endp.Call("Info", bus.Args(), &res.BuildNumber, &res.Device, &res.Channel, &res.LastUpdate, &res.VersionDetail)
336 if err != nil {
337
338=== modified file 'bus/testing/testing_endpoint.go'
339--- bus/testing/testing_endpoint.go 2014-07-04 23:00:42 +0000
340+++ bus/testing/testing_endpoint.go 2015-02-09 10:47:29 +0000
341@@ -89,7 +89,11 @@
342 ticker := tc.watchTicker
343 tc.watchLck.RUnlock()
344 if ticker != nil {
345- <-ticker
346+ _, ok := <-ticker
347+ if !ok {
348+ // bail out
349+ return
350+ }
351 } else {
352 time.Sleep(10 * time.Millisecond)
353 }
354
355=== modified file 'click/cclick/cclick.go'
356--- click/cclick/cclick.go 2014-07-07 22:04:30 +0000
357+++ click/cclick/cclick.go 2015-02-09 10:47:29 +0000
358@@ -51,6 +51,7 @@
359 }
360 ccu.cref = cref
361 runtime.SetFinalizer(holder, func(interface{}) {
362+ ccu.cref = nil // 1.3 gc gets confused otherwise
363 C.g_object_unref((C.gpointer)(cref))
364 })
365 return nil
366
367=== modified file 'client/client.go'
368--- client/client.go 2014-11-03 13:36:00 +0000
369+++ client/client.go 2015-02-09 10:47:29 +0000
370@@ -376,15 +376,15 @@
371
372 // handleConnState deals with connectivity events
373 func (client *PushClient) handleConnState(hasConnectivity bool) {
374+ client.log.Debugf("handleConnState: %v", hasConnectivity)
375 if client.hasConnectivity == hasConnectivity {
376 // nothing to do!
377 return
378 }
379 client.hasConnectivity = hasConnectivity
380+ client.session.Close()
381 if hasConnectivity {
382 client.session.AutoRedial(client.sessionConnectedCh)
383- } else {
384- client.session.Close()
385 }
386 }
387
388@@ -490,7 +490,7 @@
389 case err := <-client.session.ErrCh:
390 errhandler(err)
391 case count := <-client.sessionConnectedCh:
392- client.log.Debugf("Session connected after %d attempts", count)
393+ client.log.Debugf("session connected after %d attempts", count)
394 case app := <-client.unregisterCh:
395 unregisterhandler(app)
396 }
397
398=== modified file 'client/client_test.go'
399--- client/client_test.go 2014-09-05 10:47:29 +0000
400+++ client/client_test.go 2015-02-09 10:47:29 +0000
401@@ -27,6 +27,7 @@
402 "os"
403 "path/filepath"
404 "reflect"
405+ //"runtime"
406 "testing"
407 "time"
408
409@@ -203,6 +204,15 @@
410 cs.writeTestConfig(nil)
411 }
412
413+func (cs *clientSuite) TearDownTest(c *C) {
414+ //fmt.Println("GOROUTINE# ", runtime.NumGoroutine())
415+ /*
416+ var x [16*1024]byte
417+ sz := runtime.Stack(x[:], true)
418+ fmt.Println(string(x[:sz]))
419+ */
420+}
421+
422 type sqlientSuite struct{ clientSuite }
423
424 func (s *sqlientSuite) SetUpSuite(c *C) {
425@@ -651,7 +661,9 @@
426 )
427 siCond := condition.Fail2Work(2)
428 siEndp := testibus.NewMultiValuedTestingEndpoint(siCond, condition.Work(true), []interface{}{int32(101), "mako", "daily", "Unknown", map[string]string{}})
429- testibus.SetWatchTicker(cEndp, make(chan bool))
430+ tickerCh := make(chan bool)
431+ testibus.SetWatchTicker(cEndp, tickerCh)
432+ defer close(tickerCh)
433 // ok, create the thing
434 cli := NewPushClient(cs.configPath, cs.leveldbPath)
435 cli.log = cs.log
436@@ -700,6 +712,7 @@
437 c.Assert(cli.initSessionAndPoller(), IsNil)
438 cs.log.ResetCapture()
439 cli.hasConnectivity = true
440+ defer cli.session.Close()
441 cli.handleErr(errors.New("bananas"))
442 c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n")
443 }
444@@ -712,6 +725,7 @@
445 cli := NewPushClient(cs.configPath, "")
446 ln, err := cli.seenStateFactory()
447 c.Assert(err, IsNil)
448+ defer ln.Close()
449 c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.memSeenState")
450 }
451
452@@ -719,6 +733,7 @@
453 cli := NewPushClient(cs.configPath, ":memory:")
454 ln, err := cli.seenStateFactory()
455 c.Assert(err, IsNil)
456+ defer ln.Close()
457 c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.sqliteSeenState")
458 }
459
460@@ -733,6 +748,7 @@
461 c.Assert(cli.initSessionAndPoller(), IsNil)
462
463 c.Assert(cli.hasConnectivity, Equals, false)
464+ defer cli.session.Close()
465 cli.handleConnState(true)
466 c.Check(cli.hasConnectivity, Equals, true)
467 c.Assert(cli.session, NotNil)
468@@ -1135,7 +1151,7 @@
469 // sessionConnectedCh to nothing in particular, but it'll help sync this test
470 cli.sessionConnectedCh <- 42
471 tick()
472- c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$")
473+ c.Check(cs.log.Captured(), Matches, "(?msi).*Session connected after 42 attempts$")
474
475 // loop() should have connected:
476 // * connCh to the connectivity checker
477
478=== modified file 'client/service/postal.go'
479--- client/service/postal.go 2014-11-03 13:36:00 +0000
480+++ client/service/postal.go 2015-02-09 10:47:29 +0000
481@@ -244,7 +244,6 @@
482 for _, endp := range endps {
483 go func(name string, endp bus.Endpoint) {
484 util.NewAutoRedialer(endp).Redial()
485- svc.Log.Debugf("%s dialed in", name)
486 wg.Done()
487 }(endp.name, endp.endp)
488 }
489
490=== modified file 'client/service/service.go'
491--- client/service/service.go 2014-11-19 13:58:12 +0000
492+++ client/service/service.go 2015-02-09 10:47:29 +0000
493@@ -149,14 +149,14 @@
494 // errors below here Can't Happen (tm).
495 body, err := ioutil.ReadAll(resp.Body)
496 if err != nil {
497- svc.Log.Errorf("Reading response body: %v", err)
498+ svc.Log.Errorf("during ReadAll() of response body: %v", err)
499 return nil, err
500 }
501
502 var reply registrationReply
503 err = json.Unmarshal(body, &reply)
504 if err != nil {
505- svc.Log.Errorf("Unmarshalling response body: %v", err)
506+ svc.Log.Errorf("during Unmarshal of response body: %v", err)
507 return nil, fmt.Errorf("unable to unmarshal register response: %v", err)
508 }
509
510@@ -181,7 +181,7 @@
511 }
512
513 if !reply.Ok || reply.Token == "" {
514- svc.Log.Errorf("Unexpected response: %#v", reply)
515+ svc.Log.Errorf("unexpected response: %#v", reply)
516 return nil, ErrBadToken
517 }
518
519
520=== modified file 'client/session/seenstate/seenstate.go'
521--- client/session/seenstate/seenstate.go 2014-05-14 17:42:24 +0000
522+++ client/session/seenstate/seenstate.go 2015-02-09 10:47:29 +0000
523@@ -28,8 +28,10 @@
524 // GetAll() returns a "simple" map of the current levels.
525 GetAllLevels() (map[string]int64, error)
526 // FilterBySeen filters notifications already seen, keep track
527- // of them as well
528+ // of them as well.
529 FilterBySeen([]protocol.Notification) ([]protocol.Notification, error)
530+ // Close closes state.
531+ Close()
532 }
533
534 type memSeenState struct {
535@@ -58,6 +60,9 @@
536 return acc, nil
537 }
538
539+func (m *memSeenState) Close() {
540+}
541+
542 var _ SeenState = (*memSeenState)(nil)
543
544 // NewSeenState returns an implementation of SeenState that is memory-based and
545
546=== modified file 'client/session/seenstate/sqlseenstate.go'
547--- client/session/seenstate/sqlseenstate.go 2014-05-14 17:42:24 +0000
548+++ client/session/seenstate/sqlseenstate.go 2015-02-09 10:47:29 +0000
549@@ -48,6 +48,11 @@
550 return &sqliteSeenState{db}, nil
551 }
552
553+// Closes closes the underlying db.
554+func (ps *sqliteSeenState) Close() {
555+ ps.db.Close()
556+}
557+
558 func (ps *sqliteSeenState) SetLevel(level string, top int64) error {
559 _, err := ps.db.Exec("REPLACE INTO level_map (level, top) VALUES (?, ?)", level, top)
560 if err != nil {
561
562=== modified file 'client/session/seenstate/sqlseenstate_test.go'
563--- client/session/seenstate/sqlseenstate_test.go 2014-05-14 17:42:24 +0000
564+++ client/session/seenstate/sqlseenstate_test.go 2015-02-09 10:47:29 +0000
565@@ -112,6 +112,15 @@
566 c.Check(err, ErrorMatches, "cannot insert .*")
567 }
568
569+func (s *sqlsSuite) TestClose(c *C) {
570+ dir := c.MkDir()
571+ filename := dir + "test.db"
572+ sqls, err := NewSqliteSeenState(filename)
573+ c.Check(err, IsNil)
574+ c.Assert(sqls, NotNil)
575+ sqls.Close()
576+}
577+
578 func (s *sqlsSuite) TestDropPrevThan(c *C) {
579 dir := c.MkDir()
580 filename := dir + "test.db"
581
582=== modified file 'client/session/session.go'
583--- client/session/session.go 2014-11-19 13:58:12 +0000
584+++ client/session/session.go 2015-02-09 10:47:29 +0000
585@@ -74,8 +74,22 @@
586 Connected
587 Started
588 Running
589+ Unknown
590 )
591
592+func (s ClientSessionState) String() string {
593+ if s >= Unknown {
594+ return fmt.Sprintf("??? (%d)", s)
595+ }
596+ return [Unknown]string{
597+ "Error",
598+ "Disconnected",
599+ "Connected",
600+ "Started",
601+ "Running",
602+ }[s]
603+}
604+
605 type hostGetter interface {
606 Get() (*gethosts.Host, error)
607 }
608@@ -131,6 +145,7 @@
609 proto protocol.Protocol
610 pingInterval time.Duration
611 retrier util.AutoRedialer
612+ retrierLock sync.Mutex
613 cookie string
614 // status
615 stateP *uint32
616@@ -220,6 +235,7 @@
617 }
618
619 func (sess *ClientSession) setState(state ClientSessionState) {
620+ sess.Log.Debugf("session.setState: %s -> %s", ClientSessionState(atomic.LoadUint32(sess.stateP)), state)
621 atomic.StoreUint32(sess.stateP, uint32(state))
622 }
623
624@@ -348,6 +364,8 @@
625 }
626
627 func (sess *ClientSession) stopRedial() {
628+ sess.retrierLock.Lock()
629+ defer sess.retrierLock.Unlock()
630 if sess.retrier != nil {
631 sess.retrier.Stop()
632 sess.retrier = nil
633@@ -360,15 +378,31 @@
634 sess.setShouldDelay()
635 }
636 time.Sleep(sess.redialDelay(sess))
637+ sess.retrierLock.Lock()
638+ defer sess.retrierLock.Unlock()
639+ if sess.retrier != nil {
640+ panic("session AutoRedial: unexpected non-nil retrier.")
641+ }
642 sess.retrier = util.NewAutoRedialer(sess)
643 sess.lastAutoRedial = time.Now()
644- go func() { doneCh <- sess.retrier.Redial() }()
645+ go func() {
646+ sess.retrierLock.Lock()
647+ retrier := sess.retrier
648+ sess.retrierLock.Unlock()
649+ if retrier == nil {
650+ sess.Log.Debugf("session autoredialer skipping retry: retrier has been set to nil.")
651+ return
652+ }
653+ sess.Log.Debugf("session autoredialier launching Redial goroutine")
654+ doneCh <- retrier.Redial()
655+ }()
656 }
657
658 func (sess *ClientSession) Close() {
659 sess.stopRedial()
660 sess.doClose()
661 }
662+
663 func (sess *ClientSession) doClose() {
664 sess.connLock.Lock()
665 defer sess.connLock.Unlock()
666@@ -508,6 +542,7 @@
667 sess.proto.SetDeadline(time.Now().Add(deadAfter))
668 err = sess.proto.ReadMessage(&recv)
669 if err != nil {
670+ sess.Log.Debugf("session aborting with error on read.")
671 sess.setState(Error)
672 return err
673 }
674@@ -529,6 +564,7 @@
675 sess.Log.Errorf("server sent warning: %s", recv.Reason)
676 }
677 if err != nil {
678+ sess.Log.Debugf("session aborting with error from handler.")
679 return err
680 }
681 }
682@@ -591,7 +627,7 @@
683 }
684 sess.proto = proto
685 sess.pingInterval = pingInterval
686- sess.Log.Debugf("Connected %v.", conn.RemoteAddr())
687+ sess.Log.Debugf("connected %v.", conn.RemoteAddr())
688 sess.started() // deals with choosing which host to retry with as well
689 return nil
690 }
691
692=== modified file 'client/session/session_test.go'
693--- client/session/session_test.go 2014-11-25 17:36:27 +0000
694+++ client/session/session_test.go 2015-02-09 10:47:29 +0000
695@@ -162,6 +162,7 @@
696
697 func (*brokenSeenState) SetLevel(string, int64) error { return errors.New("broken.") }
698 func (*brokenSeenState) GetAllLevels() (map[string]int64, error) { return nil, errors.New("broken.") }
699+func (*brokenSeenState) Close() {}
700 func (*brokenSeenState) FilterBySeen([]protocol.Notification) ([]protocol.Notification, error) {
701 return nil, errors.New("broken.")
702 }
703
704=== modified file 'debian/changelog'
705--- debian/changelog 2014-12-11 16:56:31 +0000
706+++ debian/changelog 2015-02-09 10:47:29 +0000
707@@ -1,3 +1,43 @@
708+ubuntu-push (0.67-0ubuntu1) UNRELEASED; urgency=medium
709+
710+ * Updated precommit script. [dev]
711+
712+ * Include code examples in docs (instead of repeating). [docs]
713+
714+ * Make tests more robust in the face of go 1.3 [client, server]
715+
716+ * Introduce StartClientAuthFlex for acceptance tests: Start a client
717+ with auth, take a devId regexp, don't check any client event; support
718+ connbroken in acceptanceclient. [server]
719+
720+ * Cleanup and improve logging, and make log messages more
721+ consistent. [client]
722+
723+ * Partially work around bug lp:1390663 in a minimally intrusive way
724+ (real fix will have to wait). [client]
725+
726+ * Add SIGQUIT handler to spit out stack dumps; more logging
727+ tweaks. [client, server]
728+
729+ * Adds a couple of buttons to exercise more APIs, version bump to
730+ 0.44. [sample app]
731+
732+ * Add APIError to server/acceptance/kit that includes the body for
733+ debugging. [server]
734+
735+ * Add DisableKeepAlives and MaxIdleConnsPerHost to the APIClient
736+ SetupClient method. [server]
737+
738+ * Clean up goroutines in tests. [client]
739+
740+ * Add an explicit check and log message for nil error on webcheck's CopyN. [client]
741+
742+ * Log line nums, enabled when logLevel = debug. [client server]
743+
744+ * Workaround gc issue with 1.3 and 32 bits. Fixes FTBFS. [client]
745+
746+ -- John R. Lenton <john.lenton@canonical.com> Mon, 09 Feb 2015 10:19:59 +0000
747+
748 ubuntu-push (0.66+15.04.20141211-0ubuntu1) vivid; urgency=medium
749
750 [ Roberto Alsina ]
751
752=== modified file 'dependencies.tsv'
753--- dependencies.tsv 2014-08-20 12:42:51 +0000
754+++ dependencies.tsv 2015-02-09 10:47:29 +0000
755@@ -1,5 +1,5 @@
756 code.google.com/p/go-uuid hg 7dda39b2e7d5e265014674c5af696ba4186679e9 11
757 code.google.com/p/gosqlite hg 74691fb6f83716190870cde1b658538dd4b18eb0 15
758-launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140801110939-lzfql7fk76dt6ckd 128
759+launchpad.net/go-dbus/v1 bzr jlenton@gmail.com-20141023032446-s5icvsucwlv5o38a 129
760 launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10
761 launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
762
763=== modified file 'docs/example-client/main.qml'
764--- docs/example-client/main.qml 2014-10-23 15:37:21 +0000
765+++ docs/example-client/main.qml 2015-02-09 10:47:29 +0000
766@@ -71,8 +71,12 @@
767 Component.onCompleted: {
768 notificationsChanged.connect(messageList.handle_notifications)
769 error.connect(messageList.handle_error)
770+ onTokenChanged: {
771+ console.log("foooooo")
772+ }
773 }
774 appId: "com.ubuntu.developer.ralsina.hello_hello"
775+
776 }
777
778 TextField {
779@@ -227,7 +231,7 @@
780 right: parent.right
781 bottom: parent.bottom
782 }
783- height: item1.height * 7
784+ height: item1.height * 9
785 UbuntuShape {
786 anchors.fill: parent
787 color: Theme.palette.normal.overlay
788@@ -285,6 +289,14 @@
789 value: 42
790 }
791 }
792+ Button {
793+ text: "Set Counter Via Plugin"
794+ onClicked: { pushClient.count = counterSlider.value; }
795+ }
796+ Button {
797+ text: "Clear Persistent Notifications"
798+ onClicked: { pushClient.clearPersistent([]); }
799+ }
800 }
801 }
802 }
803
804=== modified file 'docs/example-client/manifest.json'
805--- docs/example-client/manifest.json 2014-10-23 17:46:23 +0000
806+++ docs/example-client/manifest.json 2015-02-09 10:47:29 +0000
807@@ -1,7 +1,7 @@
808 {
809 "architecture": "all",
810 "description": "Example app for Ubuntu push notifications.",
811- "framework": "ubuntu-sdk-14.10-dev2",
812+ "framework": "ubuntu-sdk-14.10",
813 "hooks": {
814 "hello": {
815 "apparmor": "hello.json",
816@@ -15,5 +15,5 @@
817 "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>",
818 "name": "com.ubuntu.developer.ralsina.hello",
819 "title": "Hello",
820- "version": "0.4.3"
821+ "version": "0.4.4"
822 }
823
824=== modified file 'identifier/identifier.go'
825--- identifier/identifier.go 2014-08-04 20:40:50 +0000
826+++ identifier/identifier.go 2015-02-09 10:47:29 +0000
827@@ -48,7 +48,7 @@
828 func New() (Id, error) {
829 value, err := readMachineId()
830 if err != nil {
831- return &Identifier{value: ""}, fmt.Errorf("Failed to read the machine id: %s", err)
832+ return &Identifier{value: ""}, fmt.Errorf("failed to read the machine id: %s", err)
833 }
834 return &Identifier{value: value}, nil
835 }
836
837=== modified file 'identifier/identifier_test.go'
838--- identifier/identifier_test.go 2014-08-12 00:32:32 +0000
839+++ identifier/identifier_test.go 2015-02-09 10:47:29 +0000
840@@ -48,7 +48,7 @@
841 machineIdPath = "/var/lib/dbus/no-such-file"
842 id, err := New()
843 c.Check(err, NotNil)
844- c.Check(err.Error(), Equals, "Failed to read the machine id: open /var/lib/dbus/no-such-file: no such file or directory")
845+ c.Check(err.Error(), Equals, "failed to read the machine id: open /var/lib/dbus/no-such-file: no such file or directory")
846 c.Check(id.String(), HasLen, 0)
847 }
848
849
850=== modified file 'launch_helper/helper_finder/helper_finder.go'
851--- launch_helper/helper_finder/helper_finder.go 2014-07-29 15:36:00 +0000
852+++ launch_helper/helper_finder/helper_finder.go 2015-02-09 10:47:29 +0000
853@@ -72,7 +72,7 @@
854 fInfo, err := os.Stat(helpersDataPath)
855 if err != nil {
856 // cache file is missing, go via the slow route
857- log.Infof("Cache file not found, falling back to .json file lookup")
858+ log.Infof("cache file not found, falling back to .json file lookup")
859 return helperFromHookFile(app)
860 }
861 // get the lock as the map can be changed while we read
862
863=== modified file 'launch_helper/helper_finder/helper_finder_test.go'
864--- launch_helper/helper_finder/helper_finder_test.go 2014-07-29 15:36:00 +0000
865+++ launch_helper/helper_finder/helper_finder_test.go 2015-02-09 10:47:29 +0000
866@@ -139,7 +139,7 @@
867 hid, hex := Helper(app, s.log)
868 c.Check(hid, Equals, "com.example.test_test-helper_1")
869 c.Check(hex, Equals, filepath.Join(s.symlinkPath, "tsthlpr"))
870- c.Check(s.log.Captured(), Matches, ".*Cache file not found, falling back to .json file lookup\n")
871+ c.Check(s.log.Captured(), Matches, ".*(?i)Cache file not found, falling back to .json file lookup\n")
872 }
873
874 func (s *helperSuite) TestHelperFromHookBasic(c *C) {
875
876=== modified file 'launch_helper/kindpool.go'
877--- launch_helper/kindpool.go 2014-11-05 12:07:53 +0000
878+++ launch_helper/kindpool.go 2015-02-09 10:47:29 +0000
879@@ -296,7 +296,7 @@
880 if err != nil {
881 pool.log.Errorf("unable to read output from %v helper: %v", args.AppId, err)
882 } else {
883- pool.log.Infof("%v helper output: %#v", args.AppId, payload)
884+ pool.log.Infof("%v helper output: %s", args.AppId, payload)
885 res := &HelperResult{Input: args.Input}
886 err = json.Unmarshal(payload, &res.HelperOutput)
887 if err != nil {
888
889=== modified file 'launch_helper/legacy/legacy.go'
890--- launch_helper/legacy/legacy.go 2014-11-05 12:07:53 +0000
891+++ launch_helper/legacy/legacy.go 2015-02-09 10:47:29 +0000
892@@ -78,7 +78,7 @@
893 p_err := cmd.Wait()
894 if p_err != nil {
895 // Helper failed or got killed, log output/errors
896- lhl.log.Errorf("Legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.",
897+ lhl.log.Errorf("legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.",
898 appId, progname, id, p_err, stdout.String(), stderr.String())
899 }
900 lhl.done(id)
901
902=== modified file 'launch_helper/legacy/legacy_test.go'
903--- launch_helper/legacy/legacy_test.go 2014-08-21 18:03:49 +0000
904+++ launch_helper/legacy/legacy_test.go 2015-02-09 10:47:29 +0000
905@@ -104,7 +104,7 @@
906 c.Assert(err, IsNil)
907
908 takeNext(ch, c)
909- c.Check(ls.log.Captured(), Matches, "(?s).*Legacy helper failed.*")
910+ c.Check(ls.log.Captured(), Matches, "(?si).*Legacy helper failed.*")
911 }
912
913 func (ls *legacySuite) TestHelperFailsLog(c *C) {
914
915=== modified file 'logger/logger.go'
916--- logger/logger.go 2014-04-11 23:17:40 +0000
917+++ logger/logger.go 2015-02-09 10:47:29 +0000
918@@ -50,7 +50,8 @@
919 }
920
921 const (
922- lError = iota
923+ calldepthBase = 3
924+ lError = iota
925 lInfo
926 lDebug
927 )
928@@ -81,8 +82,12 @@
929 // level. The level can be, in order: "error", "info", "debug". It takes an
930 // io.Writer.
931 func NewSimpleLogger(w io.Writer, level string) Logger {
932+ flags := log.Ldate | log.Ltime | log.Lmicroseconds
933+ if levelToNLevel[level] >= lDebug {
934+ flags = flags | log.Lshortfile
935+ }
936 return NewSimpleLoggerFromMinimalLogger(
937- log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds),
938+ log.New(w, "", flags),
939 level,
940 )
941 }
942@@ -92,13 +97,13 @@
943 }
944
945 func (lg *simpleLogger) Errorf(format string, v ...interface{}) {
946- lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
947+ lg.outputFunc(calldepthBase, fmt.Sprintf("ERROR "+format, v...))
948 }
949
950 var osExit = os.Exit // for testing
951
952 func (lg *simpleLogger) Fatalf(format string, v ...interface{}) {
953- lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
954+ lg.outputFunc(calldepthBase, fmt.Sprintf("ERROR "+format, v...))
955 osExit(1)
956 }
957
958@@ -107,18 +112,18 @@
959 stack := make([]byte, 8*1024) // Stack writes less but doesn't fail
960 stackWritten := runtime.Stack(stack, false)
961 stack = stack[:stackWritten]
962- lg.outputFunc(2, fmt.Sprintf("ERROR(PANIC) %s:\n%s", msg, stack))
963+ lg.outputFunc(calldepthBase, fmt.Sprintf("ERROR(PANIC) %s:\n%s", msg, stack))
964 }
965
966 func (lg *simpleLogger) Infof(format string, v ...interface{}) {
967 if lg.nlevel >= lInfo {
968- lg.outputFunc(2, fmt.Sprintf("INFO "+format, v...))
969+ lg.outputFunc(calldepthBase, fmt.Sprintf("INFO "+format, v...))
970 }
971 }
972
973 func (lg *simpleLogger) Debugf(format string, v ...interface{}) {
974 if lg.nlevel >= lDebug {
975- lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...))
976+ lg.outputFunc(calldepthBase, fmt.Sprintf("DEBUG "+format, v...))
977 }
978 }
979
980
981=== modified file 'logger/logger_test.go'
982--- logger/logger_test.go 2014-04-11 19:05:35 +0000
983+++ logger/logger_test.go 2015-02-09 10:47:29 +0000
984@@ -163,3 +163,15 @@
985 checkError(`{"lvl": 1}`, "lvl:.*type string")
986 checkError(`{"lvl": "foo"}`, "lvl: not a log level: foo")
987 }
988+
989+func (s *loggerSuite) TestLogLineNo(c *C) {
990+ buf := &bytes.Buffer{}
991+ logger := NewSimpleLogger(buf, "debug")
992+ logger.Output(1, "foobaz")
993+ c.Check(buf.String(), Matches, ".* .* logger_test.go:[0-9]+: foobaz\n")
994+
995+ buf.Reset()
996+ logger = NewSimpleLogger(buf, "error")
997+ logger.Output(1, "foobaz")
998+ c.Check(buf.String(), Matches, ".* .* foobaz\n")
999+}
1000
1001=== modified file 'messaging/messaging.go'
1002--- messaging/messaging.go 2014-07-27 02:54:40 +0000
1003+++ messaging/messaging.go 2015-02-09 10:47:29 +0000
1004@@ -181,7 +181,7 @@
1005 Action: action,
1006 })
1007 if err != nil {
1008- mmu.Log.Errorf("Failed to build action: %s", action)
1009+ mmu.Log.Errorf("failed to build action: %s", action)
1010 return false
1011 }
1012 actions[2*i] = string(act)
1013
1014=== modified file 'poller/poller.go'
1015--- poller/poller.go 2014-08-29 13:50:16 +0000
1016+++ poller/poller.go 2015-02-09 10:47:29 +0000
1017@@ -167,7 +167,7 @@
1018 // the channel will produce a true for every
1019 // wakeup, not only the one we asked for
1020 now := time.Now()
1021- p.log.Debugf("got woken up; time is %s", now)
1022+ p.log.Debugf("got woken up; time is %s (𝛥: %s)", now, now.Sub(t))
1023 if !now.Before(t) {
1024 break
1025 }
1026
1027=== modified file 'server/acceptance/acceptanceclient.go'
1028--- server/acceptance/acceptanceclient.go 2014-08-27 21:19:51 +0000
1029+++ server/acceptance/acceptanceclient.go 2015-02-09 10:47:29 +0000
1030@@ -176,6 +176,8 @@
1031 events <- fmt.Sprintf("%sbroadcast chan:%v app:%v topLevel:%d payloads:%s", sess.Prefix, recv.ChanId, recv.AppId, recv.TopLevel, pack)
1032 case "warn", "connwarn":
1033 events <- fmt.Sprintf("%sconnwarn %s", sess.Prefix, recv.Reason)
1034+ case "connbroken":
1035+ events <- fmt.Sprintf("%sconnbroken %s", sess.Prefix, recv.Reason)
1036 case "setparams":
1037 sess.SetCookie(recv.SetCookie)
1038 if sess.ReportSetParams {
1039
1040=== modified file 'server/acceptance/kit/api.go'
1041--- server/acceptance/kit/api.go 2014-08-20 19:48:59 +0000
1042+++ server/acceptance/kit/api.go 2015-02-09 10:47:29 +0000
1043@@ -35,10 +35,21 @@
1044 httpClient *http.Client
1045 }
1046
1047+type APIError struct {
1048+ Msg string
1049+ Body []byte
1050+}
1051+
1052+func (e *APIError) Error() string {
1053+ return e.Msg
1054+}
1055+
1056 // SetupClient sets up the http client to make requests.
1057-func (api *APIClient) SetupClient(tlsConfig *tls.Config) {
1058+func (api *APIClient) SetupClient(tlsConfig *tls.Config, disableKeepAlives bool, maxIdleConnsPerHost int) {
1059 api.httpClient = &http.Client{
1060- Transport: &http.Transport{TLSClientConfig: tlsConfig},
1061+ Transport: &http.Transport{TLSClientConfig: tlsConfig,
1062+ DisableKeepAlives: disableKeepAlives,
1063+ MaxIdleConnsPerHost: maxIdleConnsPerHost},
1064 }
1065 }
1066
1067@@ -73,7 +84,7 @@
1068 var res map[string]interface{}
1069 err = json.Unmarshal(body, &res)
1070 if err != nil {
1071- return nil, err
1072+ return nil, &APIError{err.Error(), body}
1073 }
1074 if ok, _ := res["ok"].(bool); !ok {
1075 return res, ErrNOk
1076
1077=== modified file 'server/acceptance/suites/helpers.go'
1078--- server/acceptance/suites/helpers.go 2014-11-04 16:29:31 +0000
1079+++ server/acceptance/suites/helpers.go 2015-02-09 10:47:29 +0000
1080@@ -80,7 +80,7 @@
1081 return cfgFpath
1082 }
1083
1084-var rxLineInfo = regexp.MustCompile("^.*? ([[:alpha:]].*)\n")
1085+var rxLineInfo = regexp.MustCompile("^.*?(?: .+\\.go:\\d+:)? ([[:alpha:]].*)\n")
1086
1087 // RunAndObserve runs cmdName and returns a channel that will receive
1088 // cmdName stderr logging and a function to kill the process.
1089
1090=== modified file 'server/acceptance/suites/suite.go'
1091--- server/acceptance/suites/suite.go 2014-09-01 14:48:03 +0000
1092+++ server/acceptance/suites/suite.go 2015-02-09 10:47:29 +0000
1093@@ -21,7 +21,9 @@
1094 "flag"
1095 "fmt"
1096 "net"
1097+ "net/http"
1098 "os"
1099+ "regexp"
1100 "runtime"
1101 "time"
1102
1103@@ -48,6 +50,13 @@
1104
1105 // Start a client with auth.
1106 func (h *ServerHandle) StartClientAuth(c *C, devId string, levels map[string]int64, auth string, cookie string) (events <-chan string, errorCh <-chan error, stop func()) {
1107+ cliEvents, errCh, stop := h.StartClientAuthFlex(c, devId, levels, auth, cookie, regexp.QuoteMeta(devId))
1108+ c.Assert(NextEvent(cliEvents, errCh), Matches, "connected .*")
1109+ return cliEvents, errCh, stop
1110+}
1111+
1112+// Start a client with auth, take a devId regexp, don't check any client event.
1113+func (h *ServerHandle) StartClientAuthFlex(c *C, devId string, levels map[string]int64, auth, cookie, devIdRegexp string) (events <-chan string, errorCh <-chan error, stop func()) {
1114 errCh := make(chan error, 1)
1115 cliEvents := make(chan string, 10)
1116 sess := testClientSession(h.ServerAddr, devId, "m1", "img1", false)
1117@@ -76,9 +85,8 @@
1118 go func() {
1119 errCh <- sess.Run(cliEvents)
1120 }()
1121- c.Assert(NextEvent(cliEvents, errCh), Matches, "connected .*")
1122 c.Assert(NextEvent(h.ServerEvents, nil), Matches, ".*session.* connected .*")
1123- c.Assert(NextEvent(h.ServerEvents, nil), Matches, ".*session.* registered "+devId)
1124+ c.Assert(NextEvent(h.ServerEvents, nil), Matches, ".*session.* registered "+devIdRegexp)
1125 return cliEvents, errCh, func() { clientShutdown <- true }
1126 }
1127
1128@@ -101,7 +109,7 @@
1129 c.Assert(s.ServerHandle.ServerEvents, NotNil)
1130 c.Assert(s.ServerHandle.ServerAddr, Not(Equals), "")
1131 c.Assert(s.ServerAPIURL, Not(Equals), "")
1132- s.SetupClient(nil)
1133+ s.SetupClient(nil, false, http.DefaultMaxIdleConnsPerHost)
1134 }
1135
1136 func (s *AcceptanceSuite) TearDownTest(c *C) {
1137
1138=== modified file 'testing/helpers.go'
1139--- testing/helpers.go 2014-07-11 19:42:57 +0000
1140+++ testing/helpers.go 2015-02-09 10:47:29 +0000
1141@@ -118,7 +118,7 @@
1142
1143 idx := strings.LastIndex(dir, sep)
1144 if idx == -1 {
1145- panic(fmt.Errorf("Unable to find %s in %#v", sep, dir))
1146+ panic(fmt.Errorf("unable to find %s in %#v", sep, dir))
1147 }
1148 idx += len(sep)
1149
1150
1151=== modified file 'ubuntu-push-client.go'
1152--- ubuntu-push-client.go 2014-05-08 22:29:26 +0000
1153+++ ubuntu-push-client.go 2015-02-09 10:47:29 +0000
1154@@ -18,13 +18,31 @@
1155
1156 import (
1157 "log"
1158+ "os"
1159+ "os/signal"
1160+ "runtime"
1161+ "syscall"
1162
1163 "launchpad.net/go-xdg/v0"
1164
1165 "launchpad.net/ubuntu-push/client"
1166 )
1167
1168+func installSigQuitHandler() {
1169+ go func() {
1170+ sigs := make(chan os.Signal, 1)
1171+ signal.Notify(sigs, syscall.SIGQUIT)
1172+ buf := make([]byte, 1<<20)
1173+ for {
1174+ <-sigs
1175+ runtime.Stack(buf, true)
1176+ log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf)
1177+ }
1178+ }()
1179+}
1180+
1181 func main() {
1182+ installSigQuitHandler()
1183 cfgFname, err := xdg.Config.Find("ubuntu-push-client/config.json")
1184 if err != nil {
1185 log.Fatalf("unable to find a configuration file: %v", err)
1186
1187=== modified file 'urldispatcher/curldispatcher/curldispatcher.go'
1188--- urldispatcher/curldispatcher/curldispatcher.go 2014-09-01 13:27:17 +0000
1189+++ urldispatcher/curldispatcher/curldispatcher.go 2015-02-09 10:47:29 +0000
1190@@ -79,7 +79,7 @@
1191 C.dispatch_url(c_url, (C.gpointer)(&payload))
1192 success := <-doneCh
1193 if !success {
1194- return fmt.Errorf("Failed to DispatchURL: %s for %s", url, appPackage)
1195+ return fmt.Errorf("failed to DispatchURL: %s for %s", url, appPackage)
1196 }
1197 return nil
1198 }
1199
1200=== modified file 'urldispatcher/urldispatcher.go'
1201--- urldispatcher/urldispatcher.go 2014-09-01 13:27:17 +0000
1202+++ urldispatcher/urldispatcher.go 2015-02-09 10:47:29 +0000
1203@@ -44,7 +44,7 @@
1204 var cTestURL = curldispatcher.TestURL
1205
1206 func (ud *urlDispatcher) DispatchURL(url string, app *click.AppId) error {
1207- ud.log.Debugf("Dispatching %s", url)
1208+ ud.log.Debugf("dispatching %s", url)
1209 err := cDispatchURL(url, app.DispatchPackage())
1210 if err != nil {
1211 ud.log.Errorf("DispatchURL failed: %s", err)
1212@@ -62,7 +62,7 @@
1213 }
1214 for _, appId := range appIds {
1215 if appId != app.Versioned() {
1216- ud.log.Debugf("Notification skipped because of different appid for actions: %v - %s != %s", urls, appId, app.Versioned())
1217+ ud.log.Debugf("notification skipped because of different appid for actions: %v - %s != %s", urls, appId, app.Versioned())
1218 return false
1219 }
1220 }
1221
1222=== modified file 'urldispatcher/urldispatcher_test.go'
1223--- urldispatcher/urldispatcher_test.go 2014-08-21 17:45:01 +0000
1224+++ urldispatcher/urldispatcher_test.go 2015-02-09 10:47:29 +0000
1225@@ -106,7 +106,7 @@
1226 appId := clickhelp.MustParseAppId("com.example.test_app_0.99")
1227 urls := []string{"potato://test-app"}
1228 c.Check(ud.TestURL(appId, urls), Equals, false)
1229- c.Check(s.log.Captured(), Matches, `(?sm).*Notification skipped because of different appid for actions: \[potato://test-app\] - com.example.test_test-app_0.1 != com.example.test_app_0.99`)
1230+ c.Check(s.log.Captured(), Matches, `(?smi).*notification skipped because of different appid for actions: \[potato://test-app\] - com.example.test_test-app_0.1 != com.example.test_app_0.99`)
1231 }
1232
1233 func (s *UDSuite) TestTestURLOneWrongApp(c *C) {
1234@@ -117,7 +117,7 @@
1235 appId := clickhelp.MustParseAppId("com.example.test_test-app_0")
1236 urls := []string{"potato://test-app", "potato_a://foo"}
1237 c.Check(ud.TestURL(appId, urls), Equals, false)
1238- c.Check(s.log.Captured(), Matches, `(?sm).*Notification skipped because of different appid for actions: \[potato://test-app potato_a://foo\] - com.example.test_test-app1 != com.example.test_test-app.*`)
1239+ c.Check(s.log.Captured(), Matches, `(?smi).*notification skipped because of different appid for actions: \[potato://test-app potato_a://foo\] - com.example.test_test-app1 != com.example.test_test-app.*`)
1240 }
1241
1242 func (s *UDSuite) TestTestURLInvalidURL(c *C) {
1243
1244=== modified file 'util/redialer.go'
1245--- util/redialer.go 2014-03-20 12:15:47 +0000
1246+++ util/redialer.go 2015-02-09 10:47:29 +0000
1247@@ -65,30 +65,69 @@
1248 Stop() // Stop shuts down the given AutoRedialer, if it is still retrying.
1249 }
1250
1251+type redialerState uint32
1252+
1253+const (
1254+ Unconfigured redialerState = iota
1255+ Redialing
1256+ Stopped
1257+)
1258+
1259+func (s *redialerState) String() string {
1260+ return [3]string{"Unconfigured", "Redialing", "Stopped"}[uint32(*s)]
1261+}
1262+
1263 type autoRedialer struct {
1264- stop chan bool
1265- lock sync.RWMutex
1266- dial func() error
1267- jitter func(time.Duration) time.Duration
1268+ stateLock sync.RWMutex
1269+ stateValue redialerState
1270+ stopping chan struct{}
1271+ reallyStopped chan struct{}
1272+ dial func() error
1273+ jitter func(time.Duration) time.Duration
1274+}
1275+
1276+func (ar *autoRedialer) state() redialerState {
1277+ ar.stateLock.RLock()
1278+ defer ar.stateLock.RUnlock()
1279+ return ar.stateValue
1280+}
1281+
1282+func (ar *autoRedialer) setState(s redialerState) {
1283+ ar.stateLock.Lock()
1284+ defer ar.stateLock.Unlock()
1285+ ar.stateValue = s
1286+}
1287+
1288+func (ar *autoRedialer) setStateIfEqual(oldState, newState redialerState) bool {
1289+ ar.stateLock.Lock()
1290+ defer ar.stateLock.Unlock()
1291+ if ar.stateValue != oldState {
1292+ return false
1293+ }
1294+ ar.stateValue = newState
1295+ return true
1296+}
1297+
1298+func (ar *autoRedialer) setStateStopped() {
1299+ ar.stateLock.Lock()
1300+ defer ar.stateLock.Unlock()
1301+ switch ar.stateValue {
1302+ case Stopped:
1303+ return
1304+ case Unconfigured:
1305+ close(ar.reallyStopped)
1306+ }
1307+ ar.stateValue = Stopped
1308+ close(ar.stopping)
1309 }
1310
1311 func (ar *autoRedialer) Stop() {
1312 if ar != nil {
1313- ar.lock.RLock()
1314- defer ar.lock.RUnlock()
1315- if ar.stop != nil {
1316- ar.stop <- true
1317- }
1318+ ar.setStateStopped()
1319+ <-ar.reallyStopped
1320 }
1321 }
1322
1323-func (ar *autoRedialer) shutdown() {
1324- ar.lock.Lock()
1325- defer ar.lock.Unlock()
1326- close(ar.stop)
1327- ar.stop = nil
1328-}
1329-
1330 // Redial keeps on calling Dial until it stops returning an error. It does
1331 // exponential backoff, adding back the output of Jitter at each step.
1332 func (ar *autoRedialer) Redial() uint32 {
1333@@ -96,20 +135,20 @@
1334 // at least it's better than a segfault...
1335 panic("you can't Redial a nil AutoRedialer")
1336 }
1337- if ar.stop == nil {
1338- panic("this AutoRedialer has already been shut down")
1339+ if !ar.setStateIfEqual(Unconfigured, Redialing) {
1340+ // XXX log this
1341+ return 0
1342 }
1343- defer ar.shutdown()
1344-
1345- ar.lock.RLock()
1346- stop := ar.stop
1347- ar.lock.RUnlock()
1348+ defer close(ar.reallyStopped)
1349
1350 var timeout time.Duration
1351 var dialAttempts uint32 = 0 // unsigned so it can wrap safely ...
1352 timeouts := Timeouts()
1353 var numTimeouts uint32 = uint32(len(timeouts))
1354 for {
1355+ if ar.state() != Redialing {
1356+ return dialAttempts
1357+ }
1358 if ar.dial() == nil {
1359 return dialAttempts + 1
1360 }
1361@@ -123,8 +162,7 @@
1362 }
1363 dialAttempts++
1364 select {
1365- case <-stop:
1366- return dialAttempts
1367+ case <-ar.stopping:
1368 case <-time.After(timeout):
1369 }
1370 }
1371@@ -133,7 +171,12 @@
1372 // Returns a stoppable AutoRedialer using the provided Dialer. If the Dialer
1373 // is also a Jitterer, the backoff will be jittered.
1374 func NewAutoRedialer(dialer Dialer) AutoRedialer {
1375- ar := &autoRedialer{stop: make(chan bool), dial: dialer.Dial}
1376+ ar := &autoRedialer{
1377+ stateValue: Unconfigured,
1378+ dial: dialer.Dial,
1379+ reallyStopped: make(chan struct{}),
1380+ stopping: make(chan struct{}),
1381+ }
1382 jitterer, ok := dialer.(Jitterer)
1383 if ok {
1384 ar.jitter = jitterer.Jitter
1385
1386=== added file 'util/redialer_states.gv'
1387--- util/redialer_states.gv 1970-01-01 00:00:00 +0000
1388+++ util/redialer_states.gv 2015-02-09 10:47:29 +0000
1389@@ -0,0 +1,9 @@
1390+digraph "redialer" {
1391+ "Unconfigured" -> "Redialing" [ label="Redial" ]
1392+ "Unconfigured" -> "Stopped" [ label="Stop" ]
1393+
1394+ "Redialing" -> "Redialing" [ label="Redial" ]
1395+ "Redialing" -> "Stopped" [ label="Stop" ]
1396+
1397+ "Stopped" -> "Stopped" [ label="*" ]
1398+}
1399
1400=== modified file 'util/redialer_test.go'
1401--- util/redialer_test.go 2014-02-05 13:02:47 +0000
1402+++ util/redialer_test.go 2015-02-09 10:47:29 +0000
1403@@ -48,10 +48,10 @@
1404 func (s *RedialerSuite) TestWorks(c *C) {
1405 endp := testibus.NewTestingEndpoint(condition.Fail2Work(3), nil)
1406 ar := NewAutoRedialer(endp)
1407- c.Check(ar.(*autoRedialer).stop, NotNil)
1408+ // c.Check(ar.(*autoRedialer).stop, NotNil)
1409 c.Check(ar.Redial(), Equals, uint32(4))
1410 // and on success, the stopper goes away
1411- c.Check(ar.(*autoRedialer).stop, IsNil)
1412+ // c.Check(ar.(*autoRedialer).stop, IsNil)
1413 }
1414
1415 func (s *RedialerSuite) TestRetryNil(c *C) {
1416@@ -63,7 +63,7 @@
1417 endp := testibus.NewTestingEndpoint(condition.Work(true), nil)
1418 ar := NewAutoRedialer(endp)
1419 c.Check(ar.Redial(), Equals, uint32(1))
1420- c.Check(ar.Redial, PanicMatches, ".*shut.?down.*")
1421+ c.Check(ar.Redial(), Equals, uint32(0))
1422 }
1423
1424 type JitteringEndpoint struct {
1425@@ -103,13 +103,13 @@
1426 go func() { countCh <- ar.Redial() }()
1427 ar.Stop()
1428 select {
1429- case n := <-countCh:
1430- c.Check(n, Equals, uint32(1))
1431+ case <-countCh:
1432+ // pass
1433 case <-time.After(20 * time.Millisecond):
1434 c.Fatal("timed out waiting for redial")
1435 }
1436- // on Stop(), the stopper goes away too
1437- c.Check(ar.(*autoRedialer).stop, IsNil)
1438+ // on Stop(), the redialer is Stopped
1439+ c.Check(ar.(*autoRedialer).state(), Equals, Stopped)
1440 // and the next Stop() doesn't panic nor block
1441 ar.Stop()
1442 }

Subscribers

People subscribed via source and target branches