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