Merge lp:~pedronis/ubuntu-push/fixes-to-wily into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 150
Merged at revision: 149
Proposed branch: lp:~pedronis/ubuntu-push/fixes-to-wily
Merge into: lp:ubuntu-push
Diff against target: 705 lines (+257/-136)
9 files modified
client/service/postal.go (+0/-2)
messaging/cmessaging/cmessaging.go (+0/-1)
messaging/messaging.go (+32/-49)
messaging/messaging_test.go (+102/-50)
server/acceptance/acceptanceclient.go (+6/-1)
server/acceptance/cmd/acceptanceclient.go (+1/-1)
server/acceptance/kit/api.go (+41/-7)
server/acceptance/kit/cliloop.go (+73/-23)
ubuntu-push-client.go (+2/-2)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/fixes-to-wily
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+268067@code.launchpad.net

Commit message

* address lp:1451510 aggressively finding gone notifications in Tags()
* truncate runtime.Stack result to size, otherwise we log \x00s or old stuff

Description of the change

bring in recent fixes, mainly addressing lp:1451510

To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve
150. By Samuele Pedroni

revert rules

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'client/service/postal.go'
--- client/service/postal.go 2015-03-05 14:09:54 +0000
+++ client/service/postal.go 2015-08-14 14:03:06 +0000
@@ -53,7 +53,6 @@
53 Presenter53 Presenter
54 GetCh() chan *reply.MMActionReply54 GetCh() chan *reply.MMActionReply
55 RemoveNotification(string, bool)55 RemoveNotification(string, bool)
56 StartCleanupLoop()
57 Tags(*click.AppId) []string56 Tags(*click.AppId) []string
58 Clear(*click.AppId, ...string) int57 Clear(*click.AppId, ...string) int
59}58}
@@ -187,7 +186,6 @@
187186
188 go svc.consumeHelperResults(svc.HelperPool.Start())187 go svc.consumeHelperResults(svc.HelperPool.Start())
189 go svc.handleActions(actionsCh, svc.messagingMenu.GetCh())188 go svc.handleActions(actionsCh, svc.messagingMenu.GetCh())
190 svc.messagingMenu.StartCleanupLoop()
191 return nil189 return nil
192}190}
193191
194192
=== modified file 'messaging/cmessaging/cmessaging.go'
--- messaging/cmessaging/cmessaging.go 2014-08-07 10:20:35 +0000
+++ messaging/cmessaging/cmessaging.go 2015-08-14 14:03:06 +0000
@@ -44,7 +44,6 @@
44 Actions []string44 Actions []string
45 App *click.AppId45 App *click.AppId
46 Tag string46 Tag string
47 Gone bool
48}47}
4948
50func gchar(s string) *C.gchar {49func gchar(s string) *C.gchar {
5150
=== modified file 'messaging/messaging.go'
--- messaging/messaging.go 2015-01-22 09:52:07 +0000
+++ messaging/messaging.go 2015-08-14 14:03:06 +0000
@@ -31,23 +31,19 @@
31 "launchpad.net/ubuntu-push/messaging/reply"31 "launchpad.net/ubuntu-push/messaging/reply"
32)32)
3333
34var cleanupLoopDuration = 5 * time.Minute
35
36type MessagingMenu struct {34type MessagingMenu struct {
37 Log logger.Logger35 Log logger.Logger
38 Ch chan *reply.MMActionReply36 Ch chan *reply.MMActionReply
39 notifications map[string]*cmessaging.Payload // keep a ref to the Payload used in the MMU callback37 notifications map[string]*cmessaging.Payload // keep a ref to the Payload used in the MMU callback
40 lock sync.RWMutex38 lock sync.RWMutex
41 stopCleanupLoopCh chan bool39 lastCleanupTime time.Time
42 ticker *time.Ticker
43 tickerCh <-chan time.Time
44}40}
4541
42type cleanUp func()
43
46// New returns a new MessagingMenu44// New returns a new MessagingMenu
47func New(log logger.Logger) *MessagingMenu {45func New(log logger.Logger) *MessagingMenu {
48 ticker := time.NewTicker(cleanupLoopDuration)46 return &MessagingMenu{Log: log, Ch: make(chan *reply.MMActionReply), notifications: make(map[string]*cmessaging.Payload)}
49 stopCh := make(chan bool)
50 return &MessagingMenu{Log: log, Ch: make(chan *reply.MMActionReply), notifications: make(map[string]*cmessaging.Payload), ticker: ticker, tickerCh: ticker.C, stopCleanupLoopCh: stopCh}
51}47}
5248
53var cAddNotification = cmessaging.AddNotification49var cAddNotification = cmessaging.AddNotification
@@ -59,12 +55,23 @@
59 return mmu.Ch55 return mmu.Ch
60}56}
6157
62func (mmu *MessagingMenu) addNotification(app *click.AppId, notificationId string, tag string, card *launch_helper.Card, actions []string) {58func (mmu *MessagingMenu) addNotification(app *click.AppId, notificationId string, tag string, card *launch_helper.Card, actions []string, testingCleanUpFunction cleanUp) {
63 mmu.lock.Lock()59 mmu.lock.Lock()
64 defer mmu.lock.Unlock()60 defer mmu.lock.Unlock()
65 payload := &cmessaging.Payload{Ch: mmu.Ch, Actions: actions, App: app, Tag: tag}61 payload := &cmessaging.Payload{Ch: mmu.Ch, Actions: actions, App: app, Tag: tag}
66 mmu.notifications[notificationId] = payload62 mmu.notifications[notificationId] = payload
67 cAddNotification(app.DesktopId(), notificationId, card, payload)63 cAddNotification(app.DesktopId(), notificationId, card, payload)
64
65 // Clean up our internal notifications store if it holds more than 20 messages (and apparently nobody ever calls Tags())
66 if len(mmu.notifications) > 20 && time.Since(mmu.lastCleanupTime).Minutes() > 10 {
67 mmu.lastCleanupTime = time.Now()
68 if testingCleanUpFunction == nil {
69 go mmu.cleanUpNotifications()
70 } else {
71 testingCleanUpFunction() // Has to implement the asynchronous part itself
72 }
73
74 }
68}75}
6976
70func (mmu *MessagingMenu) RemoveNotification(notificationId string, fromUI bool) {77func (mmu *MessagingMenu) RemoveNotification(notificationId string, fromUI bool) {
@@ -77,53 +84,28 @@
77 }84 }
78}85}
7986
80// cleanupNotifications remove notifications that were cleared from the messaging menu
81func (mmu *MessagingMenu) cleanUpNotifications() {87func (mmu *MessagingMenu) cleanUpNotifications() {
82 mmu.lock.Lock()88 mmu.lock.Lock()
83 defer mmu.lock.Unlock()89 defer mmu.lock.Unlock()
90 mmu.doCleanUpNotifications()
91}
92
93// doCleanupNotifications removes notifications that were cleared from the messaging menu
94func (mmu *MessagingMenu) doCleanUpNotifications() {
84 for nid, payload := range mmu.notifications {95 for nid, payload := range mmu.notifications {
85 if payload.Gone {96 if !cNotificationExists(payload.App.DesktopId(), nid) {
86 // sweep
87 delete(mmu.notifications, nid)97 delete(mmu.notifications, nid)
88 // don't check the mmu for this nid
89 continue
90 }
91 exists := cNotificationExists(payload.App.DesktopId(), nid)
92 if !exists {
93 // mark
94 payload.Gone = true
95 }98 }
96 }99 }
97}100}
98101
99func (mmu *MessagingMenu) StartCleanupLoop() {
100 mmu.doStartCleanupLoop(mmu.cleanUpNotifications)
101}
102
103func (mmu *MessagingMenu) doStartCleanupLoop(cleanupFunc func()) {
104 go func() {
105 for {
106 select {
107 case <-mmu.tickerCh:
108 cleanupFunc()
109 case <-mmu.stopCleanupLoopCh:
110 mmu.ticker.Stop()
111 mmu.Log.Debugf("CleanupLoop stopped.")
112 return
113 }
114 }
115 }()
116}
117
118func (mmu *MessagingMenu) StopCleanupLoop() {
119 mmu.stopCleanupLoopCh <- true
120}
121
122func (mmu *MessagingMenu) Tags(app *click.AppId) []string {102func (mmu *MessagingMenu) Tags(app *click.AppId) []string {
123 orig := app.Original()103 orig := app.Original()
124 tags := []string(nil)104 tags := []string(nil)
125 mmu.lock.RLock()105 mmu.lock.Lock()
126 defer mmu.lock.RUnlock()106 defer mmu.lock.Unlock()
107 mmu.lastCleanupTime = time.Now()
108 mmu.doCleanUpNotifications()
127 for _, payload := range mmu.notifications {109 for _, payload := range mmu.notifications {
128 if payload.App.Original() == orig {110 if payload.App.Original() == orig {
129 tags = append(tags, payload.Tag)111 tags = append(tags, payload.Tag)
@@ -156,6 +138,7 @@
156 for _, nid := range nids {138 for _, nid := range nids {
157 mmu.RemoveNotification(nid, true)139 mmu.RemoveNotification(nid, true)
158 }140 }
141 mmu.cleanUpNotifications()
159142
160 return len(nids)143 return len(nids)
161}144}
@@ -190,7 +173,7 @@
190173
191 mmu.Log.Debugf("[%s] creating notification centre entry for %s (summary: %s)", nid, app.Base(), card.Summary)174 mmu.Log.Debugf("[%s] creating notification centre entry for %s (summary: %s)", nid, app.Base(), card.Summary)
192175
193 mmu.addNotification(app, nid, notification.Tag, card, actions)176 mmu.addNotification(app, nid, notification.Tag, card, actions, nil)
194177
195 return true178 return true
196}179}
197180
=== modified file 'messaging/messaging_test.go'
--- messaging/messaging_test.go 2014-11-25 18:33:24 +0000
+++ messaging/messaging_test.go 2015-08-14 14:03:06 +0000
@@ -18,6 +18,8 @@
1818
19import (19import (
20 "sort"20 "sort"
21 "strconv"
22 "sync"
21 "time"23 "time"
2224
23 . "launchpad.net/gocheck"25 . "launchpad.net/gocheck"
@@ -48,8 +50,6 @@
48 cRemoveNotification = func(a, n string) {50 cRemoveNotification = func(a, n string) {
49 ms.log.Debugf("REMOVE: app: %s, not: %s", a, n)51 ms.log.Debugf("REMOVE: app: %s, not: %s", a, n)
50 }52 }
51 // just in case
52 cNotificationExists = nil
53}53}
5454
55func (ms *MessagingSuite) TearDownSuite(c *C) {55func (ms *MessagingSuite) TearDownSuite(c *C) {
@@ -61,6 +61,8 @@
61func (ms *MessagingSuite) SetUpTest(c *C) {61func (ms *MessagingSuite) SetUpTest(c *C) {
62 ms.log = helpers.NewTestLogger(c, "debug")62 ms.log = helpers.NewTestLogger(c, "debug")
63 ms.app = clickhelp.MustParseAppId("com.example.test_test_0")63 ms.app = clickhelp.MustParseAppId("com.example.test_test_0")
64 // just in case
65 cNotificationExists = nil
64}66}
6567
66func (ms *MessagingSuite) TestPresentPresents(c *C) {68func (ms *MessagingSuite) TestPresentPresents(c *C) {
@@ -135,11 +137,23 @@
135 return &launch_helper.Notification{Card: &card, Tag: s}137 return &launch_helper.Notification{Card: &card, Tag: s}
136 }138 }
137139
140 existsCount := 0
141 // patch cNotificationExists to return true
142 cNotificationExists = func(did string, nid string) bool {
143 existsCount++
144 return true
145 }
146
138 c.Check(mmu.Tags(ms.app), IsNil)147 c.Check(mmu.Tags(ms.app), IsNil)
139 c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true)148 c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true)
140 ms.checkTags(c, mmu.Tags(ms.app), []string{"one"})149 ms.checkTags(c, mmu.Tags(ms.app), []string{"one"})
150 c.Check(existsCount, Equals, 1)
151 existsCount = 0
152
141 c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true)153 c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true)
142 ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""})154 ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""})
155 c.Check(existsCount, Equals, 2)
156
143 // and an empty notification doesn't count157 // and an empty notification doesn't count
144 c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false)158 c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false)
145 ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""})159 ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""})
@@ -172,6 +186,12 @@
172 c.Assert(f(app3, "notif6", "one", true), Equals, true)186 c.Assert(f(app3, "notif6", "one", true), Equals, true)
173 c.Assert(f(app3, "notif7", "", true), Equals, true)187 c.Assert(f(app3, "notif7", "", true), Equals, true)
174188
189 // patch cNotificationExists to return true in order to make sure that messages
190 // do not get deleted by the doCleanUpTags() call in the Tags() function
191 cNotificationExists = func(did string, nid string) bool {
192 return true
193 }
194
175 // that is:195 // that is:
176 // app 1: "one", "two", "";196 // app 1: "one", "two", "";
177 // app 2: "one", "two";197 // app 2: "one", "two";
@@ -223,7 +243,7 @@
223 mmu := New(ms.log)243 mmu := New(ms.log)
224 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}244 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}
225 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}245 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}
226 mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions)246 mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions, nil)
227247
228 // check it's there248 // check it's there
229 payload, ok := mmu.notifications["notif-id"]249 payload, ok := mmu.notifications["notif-id"]
@@ -242,7 +262,7 @@
242 mmu := New(ms.log)262 mmu := New(ms.log)
243 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}263 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}
244 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}264 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}
245 mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions)265 mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions, nil)
246266
247 // check it's there267 // check it's there
248 _, ok := mmu.notifications["notif-id"]268 _, ok := mmu.notifications["notif-id"]
@@ -261,13 +281,13 @@
261 mmu := New(ms.log)281 mmu := New(ms.log)
262 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}282 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}
263 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}283 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}
264 mmu.addNotification(ms.app, "notif-id", "", &card, actions)284 mmu.addNotification(ms.app, "notif-id", "", &card, actions, nil)
265285
266 // check it's there286 // check it's there
267 _, ok := mmu.notifications["notif-id"]287 _, ok := mmu.notifications["notif-id"]
268 c.Check(ok, Equals, true)288 c.Check(ok, Equals, true)
269289
270 // patch cnotificationexists to return true290 // patch cNotificationExists to return true
271 cNotificationExists = func(did string, nid string) bool {291 cNotificationExists = func(did string, nid string) bool {
272 return true292 return true
273 }293 }
@@ -276,62 +296,94 @@
276 // check it's still there296 // check it's still there
277 _, ok = mmu.notifications["notif-id"]297 _, ok = mmu.notifications["notif-id"]
278 c.Check(ok, Equals, true)298 c.Check(ok, Equals, true)
279 // patch cnotificationexists to return false299
300 // patch cNotificationExists to return false
280 cNotificationExists = func(did string, nid string) bool {301 cNotificationExists = func(did string, nid string) bool {
281 return false302 return false
282 }303 }
283 // mark the notification304 // remove the notification
284 mmu.cleanUpNotifications()
285 // check it's gone
286 _, ok = mmu.notifications["notif-id"]
287 c.Check(ok, Equals, true)
288 // sweep the notification
289 mmu.cleanUpNotifications()305 mmu.cleanUpNotifications()
290 // check it's gone306 // check it's gone
291 _, ok = mmu.notifications["notif-id"]307 _, ok = mmu.notifications["notif-id"]
292 c.Check(ok, Equals, false)308 c.Check(ok, Equals, false)
293}309}
294310
295func (ms *MessagingSuite) TestCleanupLoop(c *C) {311func (ms *MessagingSuite) TestCleanupInAddNotification(c *C) {
296 mmu := New(ms.log)312 mmu := New(ms.log)
297 tickerCh := make(chan time.Time)313
298 mmu.tickerCh = tickerCh314 var wg sync.WaitGroup
299 cleanupCh := make(chan bool)315
300 cleanupFunc := func() {316 var cleanUpAsynchronously = func() {
301 cleanupCh <- true317 wg.Add(1)
302 }318 go func() {
303 // start the cleanup loop319 defer wg.Done()
304 mmu.doStartCleanupLoop(cleanupFunc)320 mmu.cleanUpNotifications()
305 // mark321 }()
306 tickerCh <- time.Now()322 }
307 // check it was called323
308 <-cleanupCh324 showNotification := func(number int) {
309 // stop the loop and check that it's actually stopped.325 action := "action-" + strconv.Itoa(number)
310 mmu.StopCleanupLoop()326 notificationId := "notif-id-" + strconv.Itoa(number)
311 c.Check(ms.log.Captured(), Matches, "(?s).*DEBUG CleanupLoop stopped.*")327 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{action}}
312}328 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"" + action + "\",\"nid\":\"" + notificationId + "\"}", action}
313329 mmu.addNotification(ms.app, notificationId, "", &card, actions, cleanUpAsynchronously)
314func (ms *MessagingSuite) TestStartCleanupLoop(c *C) {330 }
315 mmu := New(ms.log)331
316 tickerCh := make(chan time.Time)332 // Add 20 notifications
317 mmu.tickerCh = tickerCh333 for i := 0; i < 20; i++ {
318 card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}}334 showNotification(i)
319 actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"}335 }
320 mmu.addNotification(ms.app, "notif-id", "", &card, actions)336
321 // patch cnotificationexists to return true and signal when it's called337 // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!)
322 notifExistsCh := make(chan bool)338 wg.Wait()
339
340 // check that we have got 20 notifications
341 c.Check(mmu.notifications, HasLen, 20)
342
343 // patch cNotificationExists to return true
323 cNotificationExists = func(did string, nid string) bool {344 cNotificationExists = func(did string, nid string) bool {
324 notifExistsCh <- true
325 return true345 return true
326 }346 }
327 // statr the cleanup loop347
328 mmu.StartCleanupLoop()348 // adding another notification should not remove the current ones
329 // mark349 showNotification(21)
330 tickerCh <- time.Now()350
331 // check it's there, and marked351 // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!)
332 <-notifExistsCh352 wg.Wait()
333 // stop the loop353
334 mmu.StopCleanupLoop()354 // check we that have 21 notifications now
355 c.Check(mmu.notifications, HasLen, 21)
356
357 // patch cNotificationExists to return false for all but the next one we are going to add
358 cNotificationExists = func(did string, nid string) bool {
359 return nid == "notif-id-22"
360 }
361
362 // adding another notification should not remove the current ones as mmu.lastCleanupTime is too recent
363 showNotification(22)
364
365 // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!)
366 wg.Wait()
367
368 // check we that have got 22 notifications now
369 c.Check(mmu.notifications, HasLen, 22)
370
371 // set back the lastCleanupTime to 11 minutes ago
372 mmu.lastCleanupTime = mmu.lastCleanupTime.Add(-11 * time.Minute)
373
374 // patch cNotificationExists to return false for all but the next one we are going to add
375 cNotificationExists = func(did string, nid string) bool {
376 return nid == "notif-id-23"
377 }
378
379 // adding another notification should remove all previous ones now
380 showNotification(23)
381
382 // wait for the cleanup goroutine in addNotification to finish
383 wg.Wait()
384
385 // check that all notifications except the last one have been removed
386 c.Check(mmu.notifications, HasLen, 1)
335}387}
336388
337func (ms *MessagingSuite) TestGetCh(c *C) {389func (ms *MessagingSuite) TestGetCh(c *C) {
338390
=== modified file 'server/acceptance/acceptanceclient.go'
--- server/acceptance/acceptanceclient.go 2015-04-16 12:16:51 +0000
+++ server/acceptance/acceptanceclient.go 2015-08-14 14:03:06 +0000
@@ -49,6 +49,8 @@
49 cookie string49 cookie string
50 cookieLock sync.RWMutex50 cookieLock sync.RWMutex
51 ReportSetParams bool51 ReportSetParams bool
52 DontClose bool
53 SlowStart time.Duration
52 // connection54 // connection
53 Connection net.Conn55 Connection net.Conn
54}56}
@@ -101,7 +103,10 @@
101// Run the session with the server, emits a stream of events.103// Run the session with the server, emits a stream of events.
102func (sess *ClientSession) Run(events chan<- string) error {104func (sess *ClientSession) Run(events chan<- string) error {
103 conn := sess.Connection105 conn := sess.Connection
104 defer conn.Close()106 if !sess.DontClose {
107 defer conn.Close()
108 }
109 time.Sleep(sess.SlowStart)
105 conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout))110 conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout))
106 _, err := conn.Write(wireVersionBytes)111 _, err := conn.Write(wireVersionBytes)
107 if err != nil {112 if err != nil {
108113
=== modified file 'server/acceptance/cmd/acceptanceclient.go'
--- server/acceptance/cmd/acceptanceclient.go 2014-08-20 19:48:59 +0000
+++ server/acceptance/cmd/acceptanceclient.go 2015-08-14 14:03:06 +0000
@@ -36,7 +36,7 @@
36 kit.Defaults["auth_helper"] = ""36 kit.Defaults["auth_helper"] = ""
37 kit.Defaults["wait_for"] = ""37 kit.Defaults["wait_for"] = ""
38 cfg := &configuration{}38 cfg := &configuration{}
39 kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, cfgDir string) {39 kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, apiCli *kit.APIClient, cfgDir string) {
40 log.Printf("with: %#v", session)40 log.Printf("with: %#v", session)
41 }, func(url string) string {41 }, func(url string) string {
42 if cfg.AuthHelper == "" {42 if cfg.AuthHelper == "" {
4343
=== modified file 'server/acceptance/kit/api.go'
--- server/acceptance/kit/api.go 2015-04-16 12:16:51 +0000
+++ server/acceptance/kit/api.go 2015-08-14 14:03:06 +0000
@@ -23,8 +23,10 @@
23 "crypto/tls"23 "crypto/tls"
24 "encoding/json"24 "encoding/json"
25 "errors"25 "errors"
26 "io"
26 "io/ioutil"27 "io/ioutil"
27 "net/http"28 "net/http"
29 "net/url"
28)30)
2931
30// APIClient helps making api requests.32// APIClient helps making api requests.
@@ -56,6 +58,20 @@
5658
57var ErrNOk = errors.New("not ok")59var ErrNOk = errors.New("not ok")
5860
61func readBody(respBody io.ReadCloser) (map[string]interface{}, error) {
62 defer respBody.Close()
63 body, err := ioutil.ReadAll(respBody)
64 if err != nil {
65 return nil, err
66 }
67 var res map[string]interface{}
68 err = json.Unmarshal(body, &res)
69 if err != nil {
70 return nil, &APIError{err.Error(), body}
71 }
72 return res, nil
73}
74
59// Post a API request.75// Post a API request.
60func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) {76func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) {
61 packedMessage, err := json.Marshal(message)77 packedMessage, err := json.Marshal(message)
@@ -77,18 +93,36 @@
77 if err != nil {93 if err != nil {
78 return nil, err94 return nil, err
79 }95 }
80 defer resp.Body.Close()96 res, err := readBody(resp.Body)
81 body, err := ioutil.ReadAll(resp.Body)
82 if err != nil {97 if err != nil {
83 return nil, err98 return nil, err
84 }99 }
85 var res map[string]interface{}100
86 err = json.Unmarshal(body, &res)
87 if err != nil {
88 return nil, &APIError{err.Error(), body}
89 }
90 if ok, _ := res["ok"].(bool); !ok {101 if ok, _ := res["ok"].(bool); !ok {
91 return res, ErrNOk102 return res, ErrNOk
92 }103 }
93 return res, nil104 return res, nil
94}105}
106
107// Get resource from API endpoint.
108func (api *APIClient) GetRequest(path string, params map[string]string) (map[string]interface{}, error) {
109 apiURL := api.ServerAPIURL + path
110 if len(params) != 0 {
111 vals := url.Values{}
112 for k, v := range params {
113 vals.Set(k, v)
114 }
115 apiURL += "?" + vals.Encode()
116 }
117 request, _ := http.NewRequest("GET", apiURL, nil)
118
119 resp, err := api.httpClient.Do(request)
120 if err != nil {
121 return nil, err
122 }
123 res, err := readBody(resp.Body)
124 if err != nil {
125 return nil, err
126 }
127 return res, nil
128}
95129
=== modified file 'server/acceptance/kit/cliloop.go'
--- server/acceptance/kit/cliloop.go 2015-04-15 15:35:12 +0000
+++ server/acceptance/kit/cliloop.go 2015-08-14 14:03:06 +0000
@@ -17,6 +17,7 @@
17package kit17package kit
1818
19import (19import (
20 "crypto/tls"
20 "flag"21 "flag"
21 "fmt"22 "fmt"
22 "log"23 "log"
@@ -26,6 +27,8 @@
26 "strings"27 "strings"
27 "time"28 "time"
2829
30 "launchpad.net/ubuntu-push/external/murmur3"
31
29 "launchpad.net/ubuntu-push/config"32 "launchpad.net/ubuntu-push/config"
30 "launchpad.net/ubuntu-push/server/acceptance"33 "launchpad.net/ubuntu-push/server/acceptance"
31)34)
@@ -34,11 +37,15 @@
34 // session configuration37 // session configuration
35 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`38 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
36 // server connection config39 // server connection config
37 Target string `json:"target"`40 Target string `json:"target" help:"production|staging - picks defaults"`
38 Addr config.ConfigHostPort `json:"addr"`41 Addr config.ConfigHostPort `json:"addr"`
42 Vnode string `json:"vnode" help:"vnode postfix to make up a targeting device-id"`
39 CertPEMFile string `json:"cert_pem_file"`43 CertPEMFile string `json:"cert_pem_file"`
40 Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"`44 Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"`
41 Domain string `json:"domain" help:"domain for tls connect"`45 Domain string `json:"domain" help:"domain for tls connect"`
46 // api config
47 APIURL string `json:"api" help:"api url"`
48 APICertPEMFile string `json:"api_cert_pem_file"`
42 // run timeout49 // run timeout
43 RunTimeout config.ConfigTimeDuration `json:"run_timeout"`50 RunTimeout config.ConfigTimeDuration `json:"run_timeout"`
44 // flags51 // flags
@@ -66,22 +73,25 @@
66var (73var (
67 Name = "acceptanceclient"74 Name = "acceptanceclient"
68 Defaults = map[string]interface{}{75 Defaults = map[string]interface{}{
69 "target": "",76 "target": "",
70 "addr": ":0",77 "addr": ":0",
71 "exchange_timeout": "5s",78 "vnode": "",
72 "cert_pem_file": "",79 "exchange_timeout": "5s",
73 "insecure": false,80 "cert_pem_file": "",
74 "domain": "",81 "insecure": false,
75 "run_timeout": "0s",82 "domain": "",
76 "reportPings": true,83 "run_timeout": "0s",
77 "model": "?",84 "reportPings": true,
78 "imageChannel": "?",85 "model": "?",
79 "buildNumber": -1,86 "imageChannel": "?",
87 "buildNumber": -1,
88 "api": "",
89 "api_cert_pem_file": "",
80 }90 }
81)91)
8292
83// CliLoop parses command line arguments and runs a client loop.93// CliLoop parses command line arguments and runs a client loop.
84func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) {94func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, apiCli *APIClient, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) {
85 flag.Usage = func() {95 flag.Usage = func() {
86 fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name)96 fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name)
87 flag.PrintDefaults()97 flag.PrintDefaults()
@@ -95,31 +105,71 @@
95 if err != nil {105 if err != nil {
96 log.Fatalf("reading config: %v", err)106 log.Fatalf("reading config: %v", err)
97 }107 }
98 narg := flag.NArg()108 deviceId := ""
99 switch {109 if cfg.Vnode != "" {
100 case narg < 1:110 if cfg.Addr == ":0" {
101 missingArg("device-id")111 log.Fatalf("-vnode needs -addr specified")
112 }
113 deviceId = cfg.Addr.HostPort() + "|" + cfg.Vnode
114 log.Printf("using device-id: %q", deviceId)
115 } else {
116 narg := flag.NArg()
117 switch {
118 case narg < 1:
119 missingArg("device-id")
120 }
121 deviceId = flag.Arg(0)
122 }
123 cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())
124 // setup api
125 apiCli := &APIClient{}
126 var apiTLSConfig *tls.Config
127 if cfg.APICertPEMFile != "" || cfg.Insecure {
128 var err error
129 apiTLSConfig, err = MakeTLSConfig("", cfg.Insecure,
130 cfg.APICertPEMFile, cfgDir)
131 if err != nil {
132 log.Fatalf("api tls config: %v", err)
133 }
134 }
135 apiCli.SetupClient(apiTLSConfig, true, 1)
136 if cfg.APIURL == "" {
137 apiCli.ServerAPIURL = cfg.PickByTarget("api",
138 "https://push.ubuntu.com",
139 "https://push.staging.ubuntu.com")
140 } else {
141 apiCli.ServerAPIURL = cfg.APIURL
102 }142 }
103 addr := ""143 addr := ""
144 domain := ""
104 if cfg.Addr == ":0" {145 if cfg.Addr == ":0" {
105 addr = cfg.PickByTarget("addr", "push-delivery.ubuntu.com:443",146 hash := murmur3.Sum64([]byte(deviceId))
106 "push-delivery.staging.ubuntu.com:443")147 hosts, err := apiCli.GetRequest("/delivery-hosts",
148 map[string]string{
149 "h": fmt.Sprintf("%x", hash),
150 })
151 if err != nil {
152 log.Fatalf("querying hosts: %v", err)
153 }
154 addr = hosts["hosts"].([]interface{})[0].(string)
155 domain = hosts["domain"].(string)
156 log.Printf("using: %s %s", addr, domain)
107 } else {157 } else {
108 addr = cfg.Addr.HostPort()158 addr = cfg.Addr.HostPort()
159 domain = cfg.Domain
109 }160 }
110 session := &acceptance.ClientSession{161 session := &acceptance.ClientSession{
111 ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(),162 ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(),
112 ServerAddr: addr,163 ServerAddr: addr,
113 DeviceId: flag.Arg(0),164 DeviceId: deviceId,
114 // flags165 // flags
115 Model: cfg.DeviceModel,166 Model: cfg.DeviceModel,
116 ImageChannel: cfg.ImageChannel,167 ImageChannel: cfg.ImageChannel,
117 BuildNumber: cfg.BuildNumber,168 BuildNumber: cfg.BuildNumber,
118 ReportPings: cfg.ReportPings,169 ReportPings: cfg.ReportPings,
119 }170 }
120 cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())171 onSetup(session, apiCli, cfgDir)
121 onSetup(session, cfgDir)172 session.TLSConfig, err = MakeTLSConfig(domain, cfg.Insecure, cfg.CertPEMFile, cfgDir)
122 session.TLSConfig, err = MakeTLSConfig(cfg.Domain, cfg.Insecure, cfg.CertPEMFile, cfgDir)
123 if err != nil {173 if err != nil {
124 log.Fatalf("tls config: %v", err)174 log.Fatalf("tls config: %v", err)
125 }175 }
126176
=== modified file 'ubuntu-push-client.go'
--- ubuntu-push-client.go 2015-01-21 21:31:39 +0000
+++ ubuntu-push-client.go 2015-08-14 14:03:06 +0000
@@ -35,8 +35,8 @@
35 buf := make([]byte, 1<<20)35 buf := make([]byte, 1<<20)
36 for {36 for {
37 <-sigs37 <-sigs
38 runtime.Stack(buf, true)38 sz := runtime.Stack(buf, true)
39 log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf)39 log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:sz])
40 }40 }
41 }()41 }()
42}42}

Subscribers

People subscribed via source and target branches