Merge lp:~pedronis/ubuntu-push/fixes-to-wily into lp:ubuntu-push
- fixes-to-wily
- Merge into trunk
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 | ||||
Related bugs: |
|
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
1 | === modified file 'client/service/postal.go' |
2 | --- client/service/postal.go 2015-03-05 14:09:54 +0000 |
3 | +++ client/service/postal.go 2015-08-14 14:03:06 +0000 |
4 | @@ -53,7 +53,6 @@ |
5 | Presenter |
6 | GetCh() chan *reply.MMActionReply |
7 | RemoveNotification(string, bool) |
8 | - StartCleanupLoop() |
9 | Tags(*click.AppId) []string |
10 | Clear(*click.AppId, ...string) int |
11 | } |
12 | @@ -187,7 +186,6 @@ |
13 | |
14 | go svc.consumeHelperResults(svc.HelperPool.Start()) |
15 | go svc.handleActions(actionsCh, svc.messagingMenu.GetCh()) |
16 | - svc.messagingMenu.StartCleanupLoop() |
17 | return nil |
18 | } |
19 | |
20 | |
21 | === modified file 'messaging/cmessaging/cmessaging.go' |
22 | --- messaging/cmessaging/cmessaging.go 2014-08-07 10:20:35 +0000 |
23 | +++ messaging/cmessaging/cmessaging.go 2015-08-14 14:03:06 +0000 |
24 | @@ -44,7 +44,6 @@ |
25 | Actions []string |
26 | App *click.AppId |
27 | Tag string |
28 | - Gone bool |
29 | } |
30 | |
31 | func gchar(s string) *C.gchar { |
32 | |
33 | === modified file 'messaging/messaging.go' |
34 | --- messaging/messaging.go 2015-01-22 09:52:07 +0000 |
35 | +++ messaging/messaging.go 2015-08-14 14:03:06 +0000 |
36 | @@ -31,23 +31,19 @@ |
37 | "launchpad.net/ubuntu-push/messaging/reply" |
38 | ) |
39 | |
40 | -var cleanupLoopDuration = 5 * time.Minute |
41 | - |
42 | type MessagingMenu struct { |
43 | - Log logger.Logger |
44 | - Ch chan *reply.MMActionReply |
45 | - notifications map[string]*cmessaging.Payload // keep a ref to the Payload used in the MMU callback |
46 | - lock sync.RWMutex |
47 | - stopCleanupLoopCh chan bool |
48 | - ticker *time.Ticker |
49 | - tickerCh <-chan time.Time |
50 | + Log logger.Logger |
51 | + Ch chan *reply.MMActionReply |
52 | + notifications map[string]*cmessaging.Payload // keep a ref to the Payload used in the MMU callback |
53 | + lock sync.RWMutex |
54 | + lastCleanupTime time.Time |
55 | } |
56 | |
57 | +type cleanUp func() |
58 | + |
59 | // New returns a new MessagingMenu |
60 | func New(log logger.Logger) *MessagingMenu { |
61 | - ticker := time.NewTicker(cleanupLoopDuration) |
62 | - stopCh := make(chan bool) |
63 | - return &MessagingMenu{Log: log, Ch: make(chan *reply.MMActionReply), notifications: make(map[string]*cmessaging.Payload), ticker: ticker, tickerCh: ticker.C, stopCleanupLoopCh: stopCh} |
64 | + return &MessagingMenu{Log: log, Ch: make(chan *reply.MMActionReply), notifications: make(map[string]*cmessaging.Payload)} |
65 | } |
66 | |
67 | var cAddNotification = cmessaging.AddNotification |
68 | @@ -59,12 +55,23 @@ |
69 | return mmu.Ch |
70 | } |
71 | |
72 | -func (mmu *MessagingMenu) addNotification(app *click.AppId, notificationId string, tag string, card *launch_helper.Card, actions []string) { |
73 | +func (mmu *MessagingMenu) addNotification(app *click.AppId, notificationId string, tag string, card *launch_helper.Card, actions []string, testingCleanUpFunction cleanUp) { |
74 | mmu.lock.Lock() |
75 | defer mmu.lock.Unlock() |
76 | payload := &cmessaging.Payload{Ch: mmu.Ch, Actions: actions, App: app, Tag: tag} |
77 | mmu.notifications[notificationId] = payload |
78 | cAddNotification(app.DesktopId(), notificationId, card, payload) |
79 | + |
80 | + // Clean up our internal notifications store if it holds more than 20 messages (and apparently nobody ever calls Tags()) |
81 | + if len(mmu.notifications) > 20 && time.Since(mmu.lastCleanupTime).Minutes() > 10 { |
82 | + mmu.lastCleanupTime = time.Now() |
83 | + if testingCleanUpFunction == nil { |
84 | + go mmu.cleanUpNotifications() |
85 | + } else { |
86 | + testingCleanUpFunction() // Has to implement the asynchronous part itself |
87 | + } |
88 | + |
89 | + } |
90 | } |
91 | |
92 | func (mmu *MessagingMenu) RemoveNotification(notificationId string, fromUI bool) { |
93 | @@ -77,53 +84,28 @@ |
94 | } |
95 | } |
96 | |
97 | -// cleanupNotifications remove notifications that were cleared from the messaging menu |
98 | func (mmu *MessagingMenu) cleanUpNotifications() { |
99 | mmu.lock.Lock() |
100 | defer mmu.lock.Unlock() |
101 | + mmu.doCleanUpNotifications() |
102 | +} |
103 | + |
104 | +// doCleanupNotifications removes notifications that were cleared from the messaging menu |
105 | +func (mmu *MessagingMenu) doCleanUpNotifications() { |
106 | for nid, payload := range mmu.notifications { |
107 | - if payload.Gone { |
108 | - // sweep |
109 | + if !cNotificationExists(payload.App.DesktopId(), nid) { |
110 | delete(mmu.notifications, nid) |
111 | - // don't check the mmu for this nid |
112 | - continue |
113 | - } |
114 | - exists := cNotificationExists(payload.App.DesktopId(), nid) |
115 | - if !exists { |
116 | - // mark |
117 | - payload.Gone = true |
118 | } |
119 | } |
120 | } |
121 | |
122 | -func (mmu *MessagingMenu) StartCleanupLoop() { |
123 | - mmu.doStartCleanupLoop(mmu.cleanUpNotifications) |
124 | -} |
125 | - |
126 | -func (mmu *MessagingMenu) doStartCleanupLoop(cleanupFunc func()) { |
127 | - go func() { |
128 | - for { |
129 | - select { |
130 | - case <-mmu.tickerCh: |
131 | - cleanupFunc() |
132 | - case <-mmu.stopCleanupLoopCh: |
133 | - mmu.ticker.Stop() |
134 | - mmu.Log.Debugf("CleanupLoop stopped.") |
135 | - return |
136 | - } |
137 | - } |
138 | - }() |
139 | -} |
140 | - |
141 | -func (mmu *MessagingMenu) StopCleanupLoop() { |
142 | - mmu.stopCleanupLoopCh <- true |
143 | -} |
144 | - |
145 | func (mmu *MessagingMenu) Tags(app *click.AppId) []string { |
146 | orig := app.Original() |
147 | tags := []string(nil) |
148 | - mmu.lock.RLock() |
149 | - defer mmu.lock.RUnlock() |
150 | + mmu.lock.Lock() |
151 | + defer mmu.lock.Unlock() |
152 | + mmu.lastCleanupTime = time.Now() |
153 | + mmu.doCleanUpNotifications() |
154 | for _, payload := range mmu.notifications { |
155 | if payload.App.Original() == orig { |
156 | tags = append(tags, payload.Tag) |
157 | @@ -156,6 +138,7 @@ |
158 | for _, nid := range nids { |
159 | mmu.RemoveNotification(nid, true) |
160 | } |
161 | + mmu.cleanUpNotifications() |
162 | |
163 | return len(nids) |
164 | } |
165 | @@ -190,7 +173,7 @@ |
166 | |
167 | mmu.Log.Debugf("[%s] creating notification centre entry for %s (summary: %s)", nid, app.Base(), card.Summary) |
168 | |
169 | - mmu.addNotification(app, nid, notification.Tag, card, actions) |
170 | + mmu.addNotification(app, nid, notification.Tag, card, actions, nil) |
171 | |
172 | return true |
173 | } |
174 | |
175 | === modified file 'messaging/messaging_test.go' |
176 | --- messaging/messaging_test.go 2014-11-25 18:33:24 +0000 |
177 | +++ messaging/messaging_test.go 2015-08-14 14:03:06 +0000 |
178 | @@ -18,6 +18,8 @@ |
179 | |
180 | import ( |
181 | "sort" |
182 | + "strconv" |
183 | + "sync" |
184 | "time" |
185 | |
186 | . "launchpad.net/gocheck" |
187 | @@ -48,8 +50,6 @@ |
188 | cRemoveNotification = func(a, n string) { |
189 | ms.log.Debugf("REMOVE: app: %s, not: %s", a, n) |
190 | } |
191 | - // just in case |
192 | - cNotificationExists = nil |
193 | } |
194 | |
195 | func (ms *MessagingSuite) TearDownSuite(c *C) { |
196 | @@ -61,6 +61,8 @@ |
197 | func (ms *MessagingSuite) SetUpTest(c *C) { |
198 | ms.log = helpers.NewTestLogger(c, "debug") |
199 | ms.app = clickhelp.MustParseAppId("com.example.test_test_0") |
200 | + // just in case |
201 | + cNotificationExists = nil |
202 | } |
203 | |
204 | func (ms *MessagingSuite) TestPresentPresents(c *C) { |
205 | @@ -135,11 +137,23 @@ |
206 | return &launch_helper.Notification{Card: &card, Tag: s} |
207 | } |
208 | |
209 | + existsCount := 0 |
210 | + // patch cNotificationExists to return true |
211 | + cNotificationExists = func(did string, nid string) bool { |
212 | + existsCount++ |
213 | + return true |
214 | + } |
215 | + |
216 | c.Check(mmu.Tags(ms.app), IsNil) |
217 | c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true) |
218 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one"}) |
219 | + c.Check(existsCount, Equals, 1) |
220 | + existsCount = 0 |
221 | + |
222 | c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true) |
223 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
224 | + c.Check(existsCount, Equals, 2) |
225 | + |
226 | // and an empty notification doesn't count |
227 | c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false) |
228 | ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
229 | @@ -172,6 +186,12 @@ |
230 | c.Assert(f(app3, "notif6", "one", true), Equals, true) |
231 | c.Assert(f(app3, "notif7", "", true), Equals, true) |
232 | |
233 | + // patch cNotificationExists to return true in order to make sure that messages |
234 | + // do not get deleted by the doCleanUpTags() call in the Tags() function |
235 | + cNotificationExists = func(did string, nid string) bool { |
236 | + return true |
237 | + } |
238 | + |
239 | // that is: |
240 | // app 1: "one", "two", ""; |
241 | // app 2: "one", "two"; |
242 | @@ -223,7 +243,7 @@ |
243 | mmu := New(ms.log) |
244 | card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}} |
245 | actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"} |
246 | - mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions) |
247 | + mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions, nil) |
248 | |
249 | // check it's there |
250 | payload, ok := mmu.notifications["notif-id"] |
251 | @@ -242,7 +262,7 @@ |
252 | mmu := New(ms.log) |
253 | card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}} |
254 | actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"} |
255 | - mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions) |
256 | + mmu.addNotification(ms.app, "notif-id", "a-tag", &card, actions, nil) |
257 | |
258 | // check it's there |
259 | _, ok := mmu.notifications["notif-id"] |
260 | @@ -261,13 +281,13 @@ |
261 | mmu := New(ms.log) |
262 | 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"} |
264 | - mmu.addNotification(ms.app, "notif-id", "", &card, actions) |
265 | + mmu.addNotification(ms.app, "notif-id", "", &card, actions, nil) |
266 | |
267 | // check it's there |
268 | _, ok := mmu.notifications["notif-id"] |
269 | c.Check(ok, Equals, true) |
270 | |
271 | - // patch cnotificationexists to return true |
272 | + // patch cNotificationExists to return true |
273 | cNotificationExists = func(did string, nid string) bool { |
274 | return true |
275 | } |
276 | @@ -276,62 +296,94 @@ |
277 | // check it's still there |
278 | _, ok = mmu.notifications["notif-id"] |
279 | c.Check(ok, Equals, true) |
280 | - // patch cnotificationexists to return false |
281 | + |
282 | + // patch cNotificationExists to return false |
283 | cNotificationExists = func(did string, nid string) bool { |
284 | return false |
285 | } |
286 | - // mark the notification |
287 | - mmu.cleanUpNotifications() |
288 | - // check it's gone |
289 | - _, ok = mmu.notifications["notif-id"] |
290 | - c.Check(ok, Equals, true) |
291 | - // sweep the notification |
292 | + // remove the notification |
293 | mmu.cleanUpNotifications() |
294 | // check it's gone |
295 | _, ok = mmu.notifications["notif-id"] |
296 | c.Check(ok, Equals, false) |
297 | } |
298 | |
299 | -func (ms *MessagingSuite) TestCleanupLoop(c *C) { |
300 | - mmu := New(ms.log) |
301 | - tickerCh := make(chan time.Time) |
302 | - mmu.tickerCh = tickerCh |
303 | - cleanupCh := make(chan bool) |
304 | - cleanupFunc := func() { |
305 | - cleanupCh <- true |
306 | - } |
307 | - // start the cleanup loop |
308 | - mmu.doStartCleanupLoop(cleanupFunc) |
309 | - // mark |
310 | - tickerCh <- time.Now() |
311 | - // check it was called |
312 | - <-cleanupCh |
313 | - // stop the loop and check that it's actually stopped. |
314 | - mmu.StopCleanupLoop() |
315 | - c.Check(ms.log.Captured(), Matches, "(?s).*DEBUG CleanupLoop stopped.*") |
316 | -} |
317 | - |
318 | -func (ms *MessagingSuite) TestStartCleanupLoop(c *C) { |
319 | - mmu := New(ms.log) |
320 | - tickerCh := make(chan time.Time) |
321 | - mmu.tickerCh = tickerCh |
322 | - card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{"action-1"}} |
323 | - actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"action-1\",\"nid\":\"notif-id\"}", "action-1"} |
324 | - mmu.addNotification(ms.app, "notif-id", "", &card, actions) |
325 | - // patch cnotificationexists to return true and signal when it's called |
326 | - notifExistsCh := make(chan bool) |
327 | +func (ms *MessagingSuite) TestCleanupInAddNotification(c *C) { |
328 | + mmu := New(ms.log) |
329 | + |
330 | + var wg sync.WaitGroup |
331 | + |
332 | + var cleanUpAsynchronously = func() { |
333 | + wg.Add(1) |
334 | + go func() { |
335 | + defer wg.Done() |
336 | + mmu.cleanUpNotifications() |
337 | + }() |
338 | + } |
339 | + |
340 | + showNotification := func(number int) { |
341 | + action := "action-" + strconv.Itoa(number) |
342 | + notificationId := "notif-id-" + strconv.Itoa(number) |
343 | + card := launch_helper.Card{Summary: "ehlo", Persist: true, Actions: []string{action}} |
344 | + actions := []string{"{\"app\":\"com.example.test_test_0\",\"act\":\"" + action + "\",\"nid\":\"" + notificationId + "\"}", action} |
345 | + mmu.addNotification(ms.app, notificationId, "", &card, actions, cleanUpAsynchronously) |
346 | + } |
347 | + |
348 | + // Add 20 notifications |
349 | + for i := 0; i < 20; i++ { |
350 | + showNotification(i) |
351 | + } |
352 | + |
353 | + // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!) |
354 | + wg.Wait() |
355 | + |
356 | + // check that we have got 20 notifications |
357 | + c.Check(mmu.notifications, HasLen, 20) |
358 | + |
359 | + // patch cNotificationExists to return true |
360 | cNotificationExists = func(did string, nid string) bool { |
361 | - notifExistsCh <- true |
362 | return true |
363 | } |
364 | - // statr the cleanup loop |
365 | - mmu.StartCleanupLoop() |
366 | - // mark |
367 | - tickerCh <- time.Now() |
368 | - // check it's there, and marked |
369 | - <-notifExistsCh |
370 | - // stop the loop |
371 | - mmu.StopCleanupLoop() |
372 | + |
373 | + // adding another notification should not remove the current ones |
374 | + showNotification(21) |
375 | + |
376 | + // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!) |
377 | + wg.Wait() |
378 | + |
379 | + // check we that have 21 notifications now |
380 | + c.Check(mmu.notifications, HasLen, 21) |
381 | + |
382 | + // patch cNotificationExists to return false for all but the next one we are going to add |
383 | + cNotificationExists = func(did string, nid string) bool { |
384 | + return nid == "notif-id-22" |
385 | + } |
386 | + |
387 | + // adding another notification should not remove the current ones as mmu.lastCleanupTime is too recent |
388 | + showNotification(22) |
389 | + |
390 | + // wait for the cleanup goroutine in addNotification to finish in case it gets called (which it shouldn't!) |
391 | + wg.Wait() |
392 | + |
393 | + // check we that have got 22 notifications now |
394 | + c.Check(mmu.notifications, HasLen, 22) |
395 | + |
396 | + // set back the lastCleanupTime to 11 minutes ago |
397 | + mmu.lastCleanupTime = mmu.lastCleanupTime.Add(-11 * time.Minute) |
398 | + |
399 | + // patch cNotificationExists to return false for all but the next one we are going to add |
400 | + cNotificationExists = func(did string, nid string) bool { |
401 | + return nid == "notif-id-23" |
402 | + } |
403 | + |
404 | + // adding another notification should remove all previous ones now |
405 | + showNotification(23) |
406 | + |
407 | + // wait for the cleanup goroutine in addNotification to finish |
408 | + wg.Wait() |
409 | + |
410 | + // check that all notifications except the last one have been removed |
411 | + c.Check(mmu.notifications, HasLen, 1) |
412 | } |
413 | |
414 | func (ms *MessagingSuite) TestGetCh(c *C) { |
415 | |
416 | === modified file 'server/acceptance/acceptanceclient.go' |
417 | --- server/acceptance/acceptanceclient.go 2015-04-16 12:16:51 +0000 |
418 | +++ server/acceptance/acceptanceclient.go 2015-08-14 14:03:06 +0000 |
419 | @@ -49,6 +49,8 @@ |
420 | cookie string |
421 | cookieLock sync.RWMutex |
422 | ReportSetParams bool |
423 | + DontClose bool |
424 | + SlowStart time.Duration |
425 | // connection |
426 | Connection net.Conn |
427 | } |
428 | @@ -101,7 +103,10 @@ |
429 | // Run the session with the server, emits a stream of events. |
430 | func (sess *ClientSession) Run(events chan<- string) error { |
431 | conn := sess.Connection |
432 | - defer conn.Close() |
433 | + if !sess.DontClose { |
434 | + defer conn.Close() |
435 | + } |
436 | + time.Sleep(sess.SlowStart) |
437 | conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout)) |
438 | _, err := conn.Write(wireVersionBytes) |
439 | if err != nil { |
440 | |
441 | === modified file 'server/acceptance/cmd/acceptanceclient.go' |
442 | --- server/acceptance/cmd/acceptanceclient.go 2014-08-20 19:48:59 +0000 |
443 | +++ server/acceptance/cmd/acceptanceclient.go 2015-08-14 14:03:06 +0000 |
444 | @@ -36,7 +36,7 @@ |
445 | kit.Defaults["auth_helper"] = "" |
446 | kit.Defaults["wait_for"] = "" |
447 | cfg := &configuration{} |
448 | - kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, cfgDir string) { |
449 | + kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, apiCli *kit.APIClient, cfgDir string) { |
450 | log.Printf("with: %#v", session) |
451 | }, func(url string) string { |
452 | if cfg.AuthHelper == "" { |
453 | |
454 | === modified file 'server/acceptance/kit/api.go' |
455 | --- server/acceptance/kit/api.go 2015-04-16 12:16:51 +0000 |
456 | +++ server/acceptance/kit/api.go 2015-08-14 14:03:06 +0000 |
457 | @@ -23,8 +23,10 @@ |
458 | "crypto/tls" |
459 | "encoding/json" |
460 | "errors" |
461 | + "io" |
462 | "io/ioutil" |
463 | "net/http" |
464 | + "net/url" |
465 | ) |
466 | |
467 | // APIClient helps making api requests. |
468 | @@ -56,6 +58,20 @@ |
469 | |
470 | var ErrNOk = errors.New("not ok") |
471 | |
472 | +func readBody(respBody io.ReadCloser) (map[string]interface{}, error) { |
473 | + defer respBody.Close() |
474 | + body, err := ioutil.ReadAll(respBody) |
475 | + if err != nil { |
476 | + return nil, err |
477 | + } |
478 | + var res map[string]interface{} |
479 | + err = json.Unmarshal(body, &res) |
480 | + if err != nil { |
481 | + return nil, &APIError{err.Error(), body} |
482 | + } |
483 | + return res, nil |
484 | +} |
485 | + |
486 | // Post a API request. |
487 | func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) { |
488 | packedMessage, err := json.Marshal(message) |
489 | @@ -77,18 +93,36 @@ |
490 | if err != nil { |
491 | return nil, err |
492 | } |
493 | - defer resp.Body.Close() |
494 | - body, err := ioutil.ReadAll(resp.Body) |
495 | + res, err := readBody(resp.Body) |
496 | if err != nil { |
497 | return nil, err |
498 | } |
499 | - var res map[string]interface{} |
500 | - err = json.Unmarshal(body, &res) |
501 | - if err != nil { |
502 | - return nil, &APIError{err.Error(), body} |
503 | - } |
504 | + |
505 | if ok, _ := res["ok"].(bool); !ok { |
506 | return res, ErrNOk |
507 | } |
508 | return res, nil |
509 | } |
510 | + |
511 | +// Get resource from API endpoint. |
512 | +func (api *APIClient) GetRequest(path string, params map[string]string) (map[string]interface{}, error) { |
513 | + apiURL := api.ServerAPIURL + path |
514 | + if len(params) != 0 { |
515 | + vals := url.Values{} |
516 | + for k, v := range params { |
517 | + vals.Set(k, v) |
518 | + } |
519 | + apiURL += "?" + vals.Encode() |
520 | + } |
521 | + request, _ := http.NewRequest("GET", apiURL, nil) |
522 | + |
523 | + resp, err := api.httpClient.Do(request) |
524 | + if err != nil { |
525 | + return nil, err |
526 | + } |
527 | + res, err := readBody(resp.Body) |
528 | + if err != nil { |
529 | + return nil, err |
530 | + } |
531 | + return res, nil |
532 | +} |
533 | |
534 | === modified file 'server/acceptance/kit/cliloop.go' |
535 | --- server/acceptance/kit/cliloop.go 2015-04-15 15:35:12 +0000 |
536 | +++ server/acceptance/kit/cliloop.go 2015-08-14 14:03:06 +0000 |
537 | @@ -17,6 +17,7 @@ |
538 | package kit |
539 | |
540 | import ( |
541 | + "crypto/tls" |
542 | "flag" |
543 | "fmt" |
544 | "log" |
545 | @@ -26,6 +27,8 @@ |
546 | "strings" |
547 | "time" |
548 | |
549 | + "launchpad.net/ubuntu-push/external/murmur3" |
550 | + |
551 | "launchpad.net/ubuntu-push/config" |
552 | "launchpad.net/ubuntu-push/server/acceptance" |
553 | ) |
554 | @@ -34,11 +37,15 @@ |
555 | // session configuration |
556 | ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"` |
557 | // server connection config |
558 | - Target string `json:"target"` |
559 | + Target string `json:"target" help:"production|staging - picks defaults"` |
560 | Addr config.ConfigHostPort `json:"addr"` |
561 | + Vnode string `json:"vnode" help:"vnode postfix to make up a targeting device-id"` |
562 | CertPEMFile string `json:"cert_pem_file"` |
563 | Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"` |
564 | Domain string `json:"domain" help:"domain for tls connect"` |
565 | + // api config |
566 | + APIURL string `json:"api" help:"api url"` |
567 | + APICertPEMFile string `json:"api_cert_pem_file"` |
568 | // run timeout |
569 | RunTimeout config.ConfigTimeDuration `json:"run_timeout"` |
570 | // flags |
571 | @@ -66,22 +73,25 @@ |
572 | var ( |
573 | Name = "acceptanceclient" |
574 | Defaults = map[string]interface{}{ |
575 | - "target": "", |
576 | - "addr": ":0", |
577 | - "exchange_timeout": "5s", |
578 | - "cert_pem_file": "", |
579 | - "insecure": false, |
580 | - "domain": "", |
581 | - "run_timeout": "0s", |
582 | - "reportPings": true, |
583 | - "model": "?", |
584 | - "imageChannel": "?", |
585 | - "buildNumber": -1, |
586 | + "target": "", |
587 | + "addr": ":0", |
588 | + "vnode": "", |
589 | + "exchange_timeout": "5s", |
590 | + "cert_pem_file": "", |
591 | + "insecure": false, |
592 | + "domain": "", |
593 | + "run_timeout": "0s", |
594 | + "reportPings": true, |
595 | + "model": "?", |
596 | + "imageChannel": "?", |
597 | + "buildNumber": -1, |
598 | + "api": "", |
599 | + "api_cert_pem_file": "", |
600 | } |
601 | ) |
602 | |
603 | // CliLoop parses command line arguments and runs a client loop. |
604 | -func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) { |
605 | +func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, apiCli *APIClient, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) { |
606 | flag.Usage = func() { |
607 | fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name) |
608 | flag.PrintDefaults() |
609 | @@ -95,31 +105,71 @@ |
610 | if err != nil { |
611 | log.Fatalf("reading config: %v", err) |
612 | } |
613 | - narg := flag.NArg() |
614 | - switch { |
615 | - case narg < 1: |
616 | - missingArg("device-id") |
617 | + deviceId := "" |
618 | + if cfg.Vnode != "" { |
619 | + if cfg.Addr == ":0" { |
620 | + log.Fatalf("-vnode needs -addr specified") |
621 | + } |
622 | + deviceId = cfg.Addr.HostPort() + "|" + cfg.Vnode |
623 | + log.Printf("using device-id: %q", deviceId) |
624 | + } else { |
625 | + narg := flag.NArg() |
626 | + switch { |
627 | + case narg < 1: |
628 | + missingArg("device-id") |
629 | + } |
630 | + deviceId = flag.Arg(0) |
631 | + } |
632 | + cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String()) |
633 | + // setup api |
634 | + apiCli := &APIClient{} |
635 | + var apiTLSConfig *tls.Config |
636 | + if cfg.APICertPEMFile != "" || cfg.Insecure { |
637 | + var err error |
638 | + apiTLSConfig, err = MakeTLSConfig("", cfg.Insecure, |
639 | + cfg.APICertPEMFile, cfgDir) |
640 | + if err != nil { |
641 | + log.Fatalf("api tls config: %v", err) |
642 | + } |
643 | + } |
644 | + apiCli.SetupClient(apiTLSConfig, true, 1) |
645 | + if cfg.APIURL == "" { |
646 | + apiCli.ServerAPIURL = cfg.PickByTarget("api", |
647 | + "https://push.ubuntu.com", |
648 | + "https://push.staging.ubuntu.com") |
649 | + } else { |
650 | + apiCli.ServerAPIURL = cfg.APIURL |
651 | } |
652 | addr := "" |
653 | + domain := "" |
654 | if cfg.Addr == ":0" { |
655 | - addr = cfg.PickByTarget("addr", "push-delivery.ubuntu.com:443", |
656 | - "push-delivery.staging.ubuntu.com:443") |
657 | + hash := murmur3.Sum64([]byte(deviceId)) |
658 | + hosts, err := apiCli.GetRequest("/delivery-hosts", |
659 | + map[string]string{ |
660 | + "h": fmt.Sprintf("%x", hash), |
661 | + }) |
662 | + if err != nil { |
663 | + log.Fatalf("querying hosts: %v", err) |
664 | + } |
665 | + addr = hosts["hosts"].([]interface{})[0].(string) |
666 | + domain = hosts["domain"].(string) |
667 | + log.Printf("using: %s %s", addr, domain) |
668 | } else { |
669 | addr = cfg.Addr.HostPort() |
670 | + domain = cfg.Domain |
671 | } |
672 | session := &acceptance.ClientSession{ |
673 | ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(), |
674 | ServerAddr: addr, |
675 | - DeviceId: flag.Arg(0), |
676 | + DeviceId: deviceId, |
677 | // flags |
678 | Model: cfg.DeviceModel, |
679 | ImageChannel: cfg.ImageChannel, |
680 | BuildNumber: cfg.BuildNumber, |
681 | ReportPings: cfg.ReportPings, |
682 | } |
683 | - cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String()) |
684 | - onSetup(session, cfgDir) |
685 | - session.TLSConfig, err = MakeTLSConfig(cfg.Domain, cfg.Insecure, cfg.CertPEMFile, cfgDir) |
686 | + onSetup(session, apiCli, cfgDir) |
687 | + session.TLSConfig, err = MakeTLSConfig(domain, cfg.Insecure, cfg.CertPEMFile, cfgDir) |
688 | if err != nil { |
689 | log.Fatalf("tls config: %v", err) |
690 | } |
691 | |
692 | === modified file 'ubuntu-push-client.go' |
693 | --- ubuntu-push-client.go 2015-01-21 21:31:39 +0000 |
694 | +++ ubuntu-push-client.go 2015-08-14 14:03:06 +0000 |
695 | @@ -35,8 +35,8 @@ |
696 | buf := make([]byte, 1<<20) |
697 | for { |
698 | <-sigs |
699 | - runtime.Stack(buf, true) |
700 | - log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf) |
701 | + sz := runtime.Stack(buf, true) |
702 | + log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:sz]) |
703 | } |
704 | }() |
705 | } |