Merge lp:~chipaca/ubuntu-push/fix-from-xnox into lp:ubuntu-push
- fix-from-xnox
- Merge into trunk
Proposed by
John Lenton
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~chipaca/ubuntu-push/fix-from-xnox | ||||
Merge into: | lp:ubuntu-push | ||||
Diff against target: |
3651 lines (+1729/-310) 38 files modified
.bzrignore (+2/-0) Makefile (+21/-6) README (+11/-3) bus/bus.go (+6/-1) bus/connectivity/connectivity.go (+31/-8) bus/connectivity/connectivity_test.go (+79/-19) bus/endpoint.go (+18/-10) bus/networkmanager/networkmanager.go (+71/-2) bus/networkmanager/networkmanager_test.go (+124/-9) bus/notifications/raw.go (+4/-8) bus/systemimage/systemimage.go (+68/-0) bus/systemimage/systemimage_test.go (+62/-0) bus/testing/testing_endpoint.go (+40/-13) bus/testing/testing_endpoint_test.go (+12/-10) bus/urldispatcher/urldispatcher.go (+1/-1) client/client.go (+99/-8) client/client_test.go (+224/-49) client/gethosts/gethost_test.go (+5/-7) client/session/session.go (+179/-33) client/session/session_test.go (+333/-45) debian/config.json (+4/-1) debian/ubuntu-push-client.conf (+3/-3) sampleconfigs/dev.json (+3/-3) server/acceptance/acceptanceclient.go (+6/-0) server/acceptance/cmd/acceptanceclient.go (+7/-2) server/acceptance/suites/broadcast.go (+48/-25) server/acceptance/suites/helpers.go (+2/-2) server/acceptance/suites/pingpong.go (+2/-2) server/acceptance/suites/suite.go (+6/-4) server/broker/broker.go (+20/-0) server/broker/broker_test.go (+18/-0) server/broker/exchanges.go (+31/-0) server/broker/exchanges_test.go (+85/-25) server/broker/exchg_impl_test.go (+30/-0) server/broker/simple/simple.go (+32/-10) server/broker/simple/simple_test.go (+2/-0) server/broker/testing/impls.go (+10/-0) server/broker/testsuite/suite.go (+30/-1) |
||||
To merge this branch: | bzr merge lp:~chipaca/ubuntu-push/fix-from-xnox | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni | Approve | ||
Review via email: mp+214252@code.launchpad.net |
Commit message
Fix upstart session job to start/stop when session bus starts/stop,
rather than start when any dbus event is emitted. (LP: #1302516)
Description of the change
Fix upstart session job to start/stop when session bus starts/stop,
rather than start when any dbus event is emitted. (LP: #1302516)
To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) : | # |
review:
Approve
Unmerged revisions
- 110. By Dimitri John Ledkov
-
Fix upstart session job to start/stop when session bus starts/stop, rather than start when any dbus event is emitted. (LP: #1302516)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2014-02-07 19:36:38 +0000 | |||
3 | +++ .bzrignore 2014-04-04 13:58:43 +0000 | |||
4 | @@ -11,3 +11,5 @@ | |||
5 | 11 | debian/*.ex | 11 | debian/*.ex |
6 | 12 | debian/*.EX | 12 | debian/*.EX |
7 | 13 | debian/*.substvars | 13 | debian/*.substvars |
8 | 14 | ubuntu-push-client | ||
9 | 15 | push-server-dev | ||
10 | 14 | 16 | ||
11 | === modified file 'Makefile' | |||
12 | --- Makefile 2014-03-12 13:23:26 +0000 | |||
13 | +++ Makefile 2014-04-04 13:58:43 +0000 | |||
14 | @@ -12,6 +12,8 @@ | |||
15 | 12 | GODEPS += launchpad.net/go-xdg/v0 | 12 | GODEPS += launchpad.net/go-xdg/v0 |
16 | 13 | GODEPS += code.google.com/p/gosqlite/sqlite3 | 13 | GODEPS += code.google.com/p/gosqlite/sqlite3 |
17 | 14 | 14 | ||
18 | 15 | TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client ) | ||
19 | 16 | |||
20 | 15 | bootstrap: | 17 | bootstrap: |
21 | 16 | mkdir -p $(GOPATH)/bin | 18 | mkdir -p $(GOPATH)/bin |
22 | 17 | mkdir -p $(GOPATH)/pkg | 19 | mkdir -p $(GOPATH)/pkg |
23 | @@ -21,17 +23,29 @@ | |||
24 | 21 | go install $(GODEPS) | 23 | go install $(GODEPS) |
25 | 22 | 24 | ||
26 | 23 | check: | 25 | check: |
28 | 24 | go test $(TESTFLAGS) $(PROJECT)/... | 26 | go test $(TESTFLAGS) $(TOTEST) |
29 | 25 | 27 | ||
30 | 26 | check-race: | 28 | check-race: |
32 | 27 | go test $(TESTFLAGS) -race $(PROJECT)/... | 29 | go test $(TESTFLAGS) -race $(TOTEST) |
33 | 30 | |||
34 | 31 | acceptance: | ||
35 | 32 | cd server/acceptance; ./acceptance.sh | ||
36 | 33 | |||
37 | 34 | build-client: | ||
38 | 35 | go build ubuntu-push-client.go | ||
39 | 36 | |||
40 | 37 | build-server-dev: | ||
41 | 38 | go build -o push-server-dev launchpad.net/ubuntu-push/server/dev | ||
42 | 39 | |||
43 | 40 | run-server-dev: | ||
44 | 41 | go run server/dev/*.go sampleconfigs/dev.json | ||
45 | 28 | 42 | ||
46 | 29 | coverage-summary: | 43 | coverage-summary: |
48 | 30 | go test $(TESTFLAGS) -a -cover $(PROJECT)/... | 44 | go test $(TESTFLAGS) -a -cover $(TOTEST) |
49 | 31 | 45 | ||
50 | 32 | coverage-html: | 46 | coverage-html: |
51 | 33 | mkdir -p coverhtml | 47 | mkdir -p coverhtml |
53 | 34 | for pkg in $$(go list $(PROJECT)/...|grep -v acceptance ); do \ | 48 | for pkg in $(TOTEST); do \ |
54 | 35 | relname="$${pkg#$(PROJECT)/}" ; \ | 49 | relname="$${pkg#$(PROJECT)/}" ; \ |
55 | 36 | mkdir -p coverhtml/$$(dirname $${relname}) ; \ | 50 | mkdir -p coverhtml/$$(dirname $${relname}) ; \ |
56 | 37 | go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \ | 51 | go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \ |
57 | @@ -52,5 +66,6 @@ | |||
58 | 52 | # requires graphviz installed | 66 | # requires graphviz installed |
59 | 53 | dot -Tsvg $< > $@ | 67 | dot -Tsvg $< > $@ |
60 | 54 | 68 | ||
63 | 55 | .PHONY: bootstrap check check-race format check-format coverage-summary \ | 69 | .PHONY: bootstrap check check-race format check-format \ |
64 | 56 | coverage-html protocol-diagrams | 70 | acceptance build-client build server-dev run-server-dev \ |
65 | 71 | coverage-summary coverage-html protocol-diagrams | ||
66 | 57 | 72 | ||
67 | === modified file 'README' | |||
68 | --- README 2014-02-21 16:17:28 +0000 | |||
69 | +++ README 2014-04-04 13:58:43 +0000 | |||
70 | @@ -15,8 +15,7 @@ | |||
71 | 15 | make check | 15 | make check |
72 | 16 | 16 | ||
73 | 17 | To produce coverage reports you need Go 1.2 (default on Trusty) and | 17 | To produce coverage reports you need Go 1.2 (default on Trusty) and |
76 | 18 | the cover tool (the latter can be obtained atm with something like: | 18 | the cover tool (in the golang-go.tools package), |
75 | 19 | sudo GOPATH=<go-workspace> go get code.google.com/p/go.tools/cmd/cover ), | ||
77 | 20 | then run: | 19 | then run: |
78 | 21 | 20 | ||
79 | 22 | make coverage-summary | 21 | make coverage-summary |
80 | @@ -31,4 +30,13 @@ | |||
81 | 31 | 30 | ||
82 | 32 | To run the acceptance tests, change to the acceptance subdir and run: | 31 | To run the acceptance tests, change to the acceptance subdir and run: |
83 | 33 | 32 | ||
85 | 34 | ./acceptance.sh | 33 | make acceptance |
86 | 34 | |||
87 | 35 | There are build targets to build the client: | ||
88 | 36 | |||
89 | 37 | make build-client | ||
90 | 38 | |||
91 | 39 | building ubuntu-push-client, and to run the development server: | ||
92 | 40 | |||
93 | 41 | make run-server-dev | ||
94 | 42 | |||
95 | 35 | 43 | ||
96 | === modified file 'bus/bus.go' | |||
97 | --- bus/bus.go 2014-02-06 09:57:49 +0000 | |||
98 | +++ bus/bus.go 2014-04-04 13:58:43 +0000 | |||
99 | @@ -57,11 +57,16 @@ | |||
100 | 57 | } | 57 | } |
101 | 58 | } | 58 | } |
102 | 59 | 59 | ||
104 | 60 | // Connect() connects to the bus, and returns the bus endpoint (and/or error). | 60 | // Endpoint returns a bus endpoint. |
105 | 61 | func (bus concreteBus) Endpoint(addr Address, log logger.Logger) Endpoint { | 61 | func (bus concreteBus) Endpoint(addr Address, log logger.Logger) Endpoint { |
106 | 62 | return newEndpoint(bus, addr, log) | 62 | return newEndpoint(bus, addr, log) |
107 | 63 | } | 63 | } |
108 | 64 | 64 | ||
109 | 65 | // Args helps build arguments for endpoint Call(). | ||
110 | 66 | func Args(args ...interface{}) []interface{} { | ||
111 | 67 | return args | ||
112 | 68 | } | ||
113 | 69 | |||
114 | 65 | /* | 70 | /* |
115 | 66 | private methods | 71 | private methods |
116 | 67 | */ | 72 | */ |
117 | 68 | 73 | ||
118 | === modified file 'bus/connectivity/connectivity.go' | |||
119 | --- bus/connectivity/connectivity.go 2014-03-20 14:21:24 +0000 | |||
120 | +++ bus/connectivity/connectivity.go 2014-04-04 13:58:43 +0000 | |||
121 | @@ -47,6 +47,7 @@ | |||
122 | 47 | 47 | ||
123 | 48 | type connectedState struct { | 48 | type connectedState struct { |
124 | 49 | networkStateCh <-chan networkmanager.State | 49 | networkStateCh <-chan networkmanager.State |
125 | 50 | networkConCh <-chan string | ||
126 | 50 | config ConnectivityConfig | 51 | config ConnectivityConfig |
127 | 51 | log logger.Logger | 52 | log logger.Logger |
128 | 52 | endp bus.Endpoint | 53 | endp bus.Endpoint |
129 | @@ -62,7 +63,9 @@ | |||
130 | 62 | // up the watch. | 63 | // up the watch. |
131 | 63 | func (cs *connectedState) start() networkmanager.State { | 64 | func (cs *connectedState) start() networkmanager.State { |
132 | 64 | var initial networkmanager.State | 65 | var initial networkmanager.State |
134 | 65 | var ch <-chan networkmanager.State | 66 | var stateCh <-chan networkmanager.State |
135 | 67 | var primary string | ||
136 | 68 | var conCh <-chan string | ||
137 | 66 | var err error | 69 | var err error |
138 | 67 | for { | 70 | for { |
139 | 68 | ar := util.NewAutoRedialer(cs.endp) | 71 | ar := util.NewAutoRedialer(cs.endp) |
140 | @@ -77,13 +80,24 @@ | |||
141 | 77 | } | 80 | } |
142 | 78 | 81 | ||
143 | 79 | // set up the watch | 82 | // set up the watch |
151 | 80 | ch, err = nm.WatchState() | 83 | stateCh, err = nm.WatchState() |
152 | 81 | if err != nil { | 84 | if err != nil { |
153 | 82 | cs.log.Debugf("Failed to set up the watch: %s", err) | 85 | cs.log.Debugf("failed to set up the state watch: %s", err) |
154 | 83 | goto Continue | 86 | goto Continue |
155 | 84 | } | 87 | } |
156 | 85 | 88 | ||
157 | 86 | cs.networkStateCh = ch | 89 | primary = nm.GetPrimaryConnection() |
158 | 90 | cs.log.Debugf("primary connection starts as %#v", primary) | ||
159 | 91 | |||
160 | 92 | conCh, err = nm.WatchPrimaryConnection() | ||
161 | 93 | if err != nil { | ||
162 | 94 | cs.log.Debugf("failed to set up the connection watch: %s", err) | ||
163 | 95 | goto Continue | ||
164 | 96 | } | ||
165 | 97 | |||
166 | 98 | cs.networkStateCh = stateCh | ||
167 | 99 | cs.networkConCh = conCh | ||
168 | 100 | |||
169 | 87 | return initial | 101 | return initial |
170 | 88 | 102 | ||
171 | 89 | Continue: | 103 | Continue: |
172 | @@ -102,6 +116,15 @@ | |||
173 | 102 | Loop: | 116 | Loop: |
174 | 103 | for { | 117 | for { |
175 | 104 | select { | 118 | select { |
176 | 119 | case <-cs.networkConCh: | ||
177 | 120 | cs.webgetCh = nil | ||
178 | 121 | cs.timer.Reset(stabilizingTimeout) | ||
179 | 122 | log.Debugf("PrimaryConnection changed. Assuming disconnect.") | ||
180 | 123 | if cs.lastSent == true { | ||
181 | 124 | cs.lastSent = false | ||
182 | 125 | break Loop | ||
183 | 126 | } | ||
184 | 127 | |||
185 | 105 | case v, ok := <-cs.networkStateCh: | 128 | case v, ok := <-cs.networkStateCh: |
186 | 106 | if !ok { | 129 | if !ok { |
187 | 107 | // tear it all down and start over | 130 | // tear it all down and start over |
188 | 108 | 131 | ||
189 | === modified file 'bus/connectivity/connectivity_test.go' | |||
190 | --- bus/connectivity/connectivity_test.go 2014-03-20 12:15:47 +0000 | |||
191 | +++ bus/connectivity/connectivity_test.go 2014-04-04 13:58:43 +0000 | |||
192 | @@ -17,6 +17,7 @@ | |||
193 | 17 | package connectivity | 17 | package connectivity |
194 | 18 | 18 | ||
195 | 19 | import ( | 19 | import ( |
196 | 20 | "launchpad.net/go-dbus/v1" | ||
197 | 20 | . "launchpad.net/gocheck" | 21 | . "launchpad.net/gocheck" |
198 | 21 | "launchpad.net/ubuntu-push/bus/networkmanager" | 22 | "launchpad.net/ubuntu-push/bus/networkmanager" |
199 | 22 | testingbus "launchpad.net/ubuntu-push/bus/testing" | 23 | testingbus "launchpad.net/ubuntu-push/bus/testing" |
200 | @@ -84,6 +85,17 @@ | |||
201 | 84 | c.Check(cs.connAttempts, Equals, uint32(6)) | 85 | c.Check(cs.connAttempts, Equals, uint32(6)) |
202 | 85 | } | 86 | } |
203 | 86 | 87 | ||
204 | 88 | // when some of the calls to NetworkManager fails for a bit, we're still OK | ||
205 | 89 | func (s *ConnSuite) TestStartRetriesCall2(c *C) { | ||
206 | 90 | cond := condition.Chain(3, condition.Work(true), 1, condition.Work(false), | ||
207 | 91 | 1, condition.Work(true)) | ||
208 | 92 | |||
209 | 93 | endp := testingbus.NewTestingEndpoint(condition.Work(true), cond, uint32(networkmanager.Connecting)) | ||
210 | 94 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | ||
211 | 95 | |||
212 | 96 | c.Check(cs.start(), Equals, networkmanager.Connecting) | ||
213 | 97 | } | ||
214 | 98 | |||
215 | 87 | // when... and bear with me... the bus works, and the first call to | 99 | // when... and bear with me... the bus works, and the first call to |
216 | 88 | // get network manager's state works, but then you can't establish the | 100 | // get network manager's state works, but then you can't establish the |
217 | 89 | // watch, we recover and try again. | 101 | // watch, we recover and try again. |
218 | @@ -213,24 +225,72 @@ | |||
219 | 213 | }{ | 225 | }{ |
220 | 214 | {false, "first state is always false", 0}, | 226 | {false, "first state is always false", 0}, |
221 | 215 | {true, "then it should be true as per ConnectedGlobal above", 0}, | 227 | {true, "then it should be true as per ConnectedGlobal above", 0}, |
241 | 216 | {false, "then, false (upon receiving the next ConnectedGlobal)", 1}, | 228 | {false, "then, false (upon receiving the next ConnectedGlobal)", 2}, |
242 | 217 | {true, "then it should be true (webcheck passed)", 0}, | 229 | {true, "then it should be true (webcheck passed)", 0}, |
243 | 218 | {false, "then it should be false (Disconnected)", 1}, | 230 | {false, "then it should be false (Disconnected)", 2}, |
244 | 219 | {false, "then it should be false again because it's restarted", 1}, | 231 | {false, "then it should be false again because it's restarted", 2}, |
245 | 220 | } | 232 | } |
246 | 221 | 233 | ||
247 | 222 | for i, expected := range expecteds { | 234 | for i, expected := range expecteds { |
248 | 223 | for j := 0; j < expected.n; j++ { | 235 | for j := 0; j < expected.n; j++ { |
249 | 224 | watchTicker <- true | 236 | watchTicker <- true |
250 | 225 | } | 237 | } |
251 | 226 | timer.Reset(dt) | 238 | timer.Reset(dt) |
252 | 227 | select { | 239 | select { |
253 | 228 | case v = <-out: | 240 | case v = <-out: |
254 | 229 | break | 241 | break |
255 | 230 | case <-timer.C: | 242 | case <-timer.C: |
256 | 231 | c.Fatalf("Timed out before getting value (#%d: %s)", i+1, expected.s) | 243 | c.Fatalf("Timed out before getting value (#%d: %s)", i+1, expected.s) |
257 | 232 | } | 244 | } |
258 | 233 | 245 | c.Assert(v, Equals, expected.p, Commentf(expected.s)) | |
259 | 234 | c.Check(v, Equals, expected.p, Commentf(expected.s)) | 246 | } |
260 | 247 | } | ||
261 | 248 | |||
262 | 249 | func (s *ConnSuite) TestRun4Active(c *C) { | ||
263 | 250 | ts := httptest.NewServer(mkHandler(staticText)) | ||
264 | 251 | defer ts.Close() | ||
265 | 252 | |||
266 | 253 | cfg := ConnectivityConfig{ | ||
267 | 254 | ConnectivityCheckURL: ts.URL, | ||
268 | 255 | ConnectivityCheckMD5: staticHash, | ||
269 | 256 | RecheckTimeout: config.ConfigTimeDuration{time.Second}, | ||
270 | 257 | } | ||
271 | 258 | |||
272 | 259 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), | ||
273 | 260 | uint32(networkmanager.ConnectedGlobal), | ||
274 | 261 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("hello")}}, | ||
275 | 262 | ) | ||
276 | 263 | |||
277 | 264 | watchTicker := make(chan bool) | ||
278 | 265 | testingbus.SetWatchTicker(endp, watchTicker) | ||
279 | 266 | |||
280 | 267 | out := make(chan bool) | ||
281 | 268 | dt := time.Second / 10 | ||
282 | 269 | timer := time.NewTimer(dt) | ||
283 | 270 | go ConnectedState(endp, cfg, s.log, out) | ||
284 | 271 | var v bool | ||
285 | 272 | expecteds := []struct { | ||
286 | 273 | p bool | ||
287 | 274 | s string | ||
288 | 275 | n int | ||
289 | 276 | }{ | ||
290 | 277 | {false, "first state is always false", 0}, | ||
291 | 278 | {true, "then it should be true as per ConnectedGlobal above", 0}, | ||
292 | 279 | {false, "then, false (PrimaryConnection changed)", 2}, | ||
293 | 280 | {true, "then it should be true (webcheck passed)", 0}, | ||
294 | 281 | } | ||
295 | 282 | |||
296 | 283 | for i, expected := range expecteds { | ||
297 | 284 | for j := 0; j < expected.n; j++ { | ||
298 | 285 | watchTicker <- true | ||
299 | 286 | } | ||
300 | 287 | timer.Reset(dt) | ||
301 | 288 | select { | ||
302 | 289 | case v = <-out: | ||
303 | 290 | break | ||
304 | 291 | case <-timer.C: | ||
305 | 292 | c.Fatalf("Timed out before getting value (#%d: %s)", i+1, expected.s) | ||
306 | 293 | } | ||
307 | 294 | c.Assert(v, Equals, expected.p, Commentf(expected.s)) | ||
308 | 235 | } | 295 | } |
309 | 236 | } | 296 | } |
310 | 237 | 297 | ||
311 | === modified file 'bus/endpoint.go' | |||
312 | --- bus/endpoint.go 2014-02-21 16:17:28 +0000 | |||
313 | +++ bus/endpoint.go 2014-04-04 13:58:43 +0000 | |||
314 | @@ -32,7 +32,7 @@ | |||
315 | 32 | // bus.Endpoint represents the DBus connection itself. | 32 | // bus.Endpoint represents the DBus connection itself. |
316 | 33 | type Endpoint interface { | 33 | type Endpoint interface { |
317 | 34 | WatchSignal(member string, f func(...interface{}), d func()) error | 34 | WatchSignal(member string, f func(...interface{}), d func()) error |
319 | 35 | Call(member string, args ...interface{}) ([]interface{}, error) | 35 | Call(member string, args []interface{}, rvs ...interface{}) error |
320 | 36 | GetProperty(property string) (interface{}, error) | 36 | GetProperty(property string) (interface{}, error) |
321 | 37 | Dial() error | 37 | Dial() error |
322 | 38 | Close() | 38 | Close() |
323 | @@ -118,16 +118,20 @@ | |||
324 | 118 | return nil | 118 | return nil |
325 | 119 | } | 119 | } |
326 | 120 | 120 | ||
331 | 121 | // Call() invokes the provided member method (on the name, path and interface | 121 | // Call() invokes the provided member method (on the name, path and |
332 | 122 | // provided when creating the endpoint). The return value is unpacked before | 122 | // interface provided when creating the endpoint). args can be built |
333 | 123 | // being returned. | 123 | // using bus.Args(...). The return value is unpacked into rvs before being |
334 | 124 | func (endp *endpoint) Call(member string, args ...interface{}) ([]interface{}, error) { | 124 | // returned. |
335 | 125 | func (endp *endpoint) Call(member string, args []interface{}, rvs ...interface{}) error { | ||
336 | 125 | msg, err := endp.proxy.Call(endp.addr.Interface, member, args...) | 126 | msg, err := endp.proxy.Call(endp.addr.Interface, member, args...) |
337 | 126 | if err != nil { | 127 | if err != nil { |
342 | 127 | return nil, err | 128 | return err |
343 | 128 | } | 129 | } |
344 | 129 | rvs := endp.unpackOneMsg(msg, member) | 130 | err = msg.Args(rvs...) |
345 | 130 | return rvs, nil | 131 | if err != nil { |
346 | 132 | return err | ||
347 | 133 | } | ||
348 | 134 | return nil | ||
349 | 131 | } | 135 | } |
350 | 132 | 136 | ||
351 | 133 | // GetProperty uses the org.freedesktop.DBus.Properties interface's Get method | 137 | // GetProperty uses the org.freedesktop.DBus.Properties interface's Get method |
352 | @@ -175,7 +179,11 @@ | |||
353 | 175 | 179 | ||
354 | 176 | // unpackOneMsg unpacks the value from the response msg | 180 | // unpackOneMsg unpacks the value from the response msg |
355 | 177 | func (endp *endpoint) unpackOneMsg(msg *dbus.Message, member string) []interface{} { | 181 | func (endp *endpoint) unpackOneMsg(msg *dbus.Message, member string) []interface{} { |
357 | 178 | return msg.AllArgs() | 182 | var varmap map[string]dbus.Variant |
358 | 183 | if err := msg.Args(&varmap); err != nil { | ||
359 | 184 | return msg.AllArgs() | ||
360 | 185 | } | ||
361 | 186 | return []interface{}{varmap} | ||
362 | 179 | } | 187 | } |
363 | 180 | 188 | ||
364 | 181 | // unpackMessages unpacks the value from the watch | 189 | // unpackMessages unpacks the value from the watch |
365 | 182 | 190 | ||
366 | === modified file 'bus/networkmanager/networkmanager.go' | |||
367 | --- bus/networkmanager/networkmanager.go 2014-01-21 13:21:19 +0000 | |||
368 | +++ bus/networkmanager/networkmanager.go 2014-04-04 13:58:43 +0000 | |||
369 | @@ -20,6 +20,8 @@ | |||
370 | 20 | package networkmanager | 20 | package networkmanager |
371 | 21 | 21 | ||
372 | 22 | import ( | 22 | import ( |
373 | 23 | "launchpad.net/go-dbus/v1" | ||
374 | 24 | |||
375 | 23 | "launchpad.net/ubuntu-push/bus" | 25 | "launchpad.net/ubuntu-push/bus" |
376 | 24 | "launchpad.net/ubuntu-push/logger" | 26 | "launchpad.net/ubuntu-push/logger" |
377 | 25 | ) | 27 | ) |
378 | @@ -41,6 +43,12 @@ | |||
379 | 41 | // WatchState listens for changes to NetworkManager's state, and sends | 43 | // WatchState listens for changes to NetworkManager's state, and sends |
380 | 42 | // them out over the channel returned. | 44 | // them out over the channel returned. |
381 | 43 | WatchState() (<-chan State, error) | 45 | WatchState() (<-chan State, error) |
382 | 46 | // GetPrimaryConnection fetches and returns NetworkManager's current | ||
383 | 47 | // primary connection. | ||
384 | 48 | GetPrimaryConnection() string | ||
385 | 49 | // WatchPrimaryConnection listens for changes of NetworkManager's | ||
386 | 50 | // Primary Connection, and sends it out over the channel returned. | ||
387 | 51 | WatchPrimaryConnection() (<-chan string, error) | ||
388 | 44 | } | 52 | } |
389 | 45 | 53 | ||
390 | 46 | type networkManager struct { | 54 | type networkManager struct { |
391 | @@ -68,13 +76,28 @@ | |||
392 | 68 | return Unknown | 76 | return Unknown |
393 | 69 | } | 77 | } |
394 | 70 | 78 | ||
396 | 71 | return State(s.(uint32)) | 79 | v, ok := s.(uint32) |
397 | 80 | if !ok { | ||
398 | 81 | nm.log.Errorf("Got weird state: %#v", s) | ||
399 | 82 | return Unknown | ||
400 | 83 | } | ||
401 | 84 | |||
402 | 85 | return State(v) | ||
403 | 72 | } | 86 | } |
404 | 73 | 87 | ||
405 | 74 | func (nm *networkManager) WatchState() (<-chan State, error) { | 88 | func (nm *networkManager) WatchState() (<-chan State, error) { |
406 | 75 | ch := make(chan State) | 89 | ch := make(chan State) |
407 | 76 | err := nm.bus.WatchSignal("StateChanged", | 90 | err := nm.bus.WatchSignal("StateChanged", |
409 | 77 | func(ns ...interface{}) { ch <- State(ns[0].(uint32)) }, | 91 | func(ns ...interface{}) { |
410 | 92 | stint, ok := ns[0].(uint32) | ||
411 | 93 | if !ok { | ||
412 | 94 | nm.log.Errorf("got weird state: %#v", ns[0]) | ||
413 | 95 | return | ||
414 | 96 | } | ||
415 | 97 | st := State(stint) | ||
416 | 98 | nm.log.Debugf("got state: %s", st) | ||
417 | 99 | ch <- State(stint) | ||
418 | 100 | }, | ||
419 | 78 | func() { close(ch) }) | 101 | func() { close(ch) }) |
420 | 79 | if err != nil { | 102 | if err != nil { |
421 | 80 | nm.log.Debugf("Failed to set up the watch: %s", err) | 103 | nm.log.Debugf("Failed to set up the watch: %s", err) |
422 | @@ -83,3 +106,49 @@ | |||
423 | 83 | 106 | ||
424 | 84 | return ch, nil | 107 | return ch, nil |
425 | 85 | } | 108 | } |
426 | 109 | |||
427 | 110 | func (nm *networkManager) GetPrimaryConnection() string { | ||
428 | 111 | s, err := nm.bus.GetProperty("PrimaryConnection") | ||
429 | 112 | if err != nil { | ||
430 | 113 | nm.log.Errorf("Failed gettting current primary connection: %s", err) | ||
431 | 114 | nm.log.Debugf("Defaulting primary connection to empty") | ||
432 | 115 | return "" | ||
433 | 116 | } | ||
434 | 117 | |||
435 | 118 | v, ok := s.(dbus.ObjectPath) | ||
436 | 119 | if !ok { | ||
437 | 120 | nm.log.Errorf("got weird PrimaryConnection: %#v", s) | ||
438 | 121 | return "" | ||
439 | 122 | } | ||
440 | 123 | |||
441 | 124 | return string(v) | ||
442 | 125 | } | ||
443 | 126 | |||
444 | 127 | func (nm *networkManager) WatchPrimaryConnection() (<-chan string, error) { | ||
445 | 128 | ch := make(chan string) | ||
446 | 129 | err := nm.bus.WatchSignal("PropertiesChanged", | ||
447 | 130 | func(ppsi ...interface{}) { | ||
448 | 131 | pps, ok := ppsi[0].(map[string]dbus.Variant) | ||
449 | 132 | if !ok { | ||
450 | 133 | nm.log.Errorf("got weird PropertiesChanged: %#v", ppsi[0]) | ||
451 | 134 | return | ||
452 | 135 | } | ||
453 | 136 | v, ok := pps["PrimaryConnection"] | ||
454 | 137 | if !ok { | ||
455 | 138 | return | ||
456 | 139 | } | ||
457 | 140 | con, ok := v.Value.(dbus.ObjectPath) | ||
458 | 141 | if !ok { | ||
459 | 142 | nm.log.Errorf("got weird PrimaryConnection via PropertiesChanged: %#v", v) | ||
460 | 143 | return | ||
461 | 144 | } | ||
462 | 145 | nm.log.Debugf("got primary connection: %s", con) | ||
463 | 146 | ch <- string(con) | ||
464 | 147 | }, func() { close(ch) }) | ||
465 | 148 | if err != nil { | ||
466 | 149 | nm.log.Debugf("Failed to set up the watch: %s", err) | ||
467 | 150 | return nil, err | ||
468 | 151 | } | ||
469 | 152 | |||
470 | 153 | return ch, nil | ||
471 | 154 | } | ||
472 | 86 | 155 | ||
473 | === modified file 'bus/networkmanager/networkmanager_test.go' | |||
474 | --- bus/networkmanager/networkmanager_test.go 2014-02-05 18:17:26 +0000 | |||
475 | +++ bus/networkmanager/networkmanager_test.go 2014-04-04 13:58:43 +0000 | |||
476 | @@ -17,12 +17,15 @@ | |||
477 | 17 | package networkmanager | 17 | package networkmanager |
478 | 18 | 18 | ||
479 | 19 | import ( | 19 | import ( |
480 | 20 | "testing" | ||
481 | 21 | |||
482 | 22 | "launchpad.net/go-dbus/v1" | ||
483 | 20 | . "launchpad.net/gocheck" | 23 | . "launchpad.net/gocheck" |
484 | 24 | |||
485 | 21 | testingbus "launchpad.net/ubuntu-push/bus/testing" | 25 | testingbus "launchpad.net/ubuntu-push/bus/testing" |
486 | 22 | "launchpad.net/ubuntu-push/logger" | 26 | "launchpad.net/ubuntu-push/logger" |
487 | 23 | helpers "launchpad.net/ubuntu-push/testing" | 27 | helpers "launchpad.net/ubuntu-push/testing" |
488 | 24 | "launchpad.net/ubuntu-push/testing/condition" | 28 | "launchpad.net/ubuntu-push/testing/condition" |
489 | 25 | "testing" | ||
490 | 26 | ) | 29 | ) |
491 | 27 | 30 | ||
492 | 28 | // hook up gocheck | 31 | // hook up gocheck |
493 | @@ -71,7 +74,7 @@ | |||
494 | 71 | 74 | ||
495 | 72 | // GetState returns the right state when dbus works but delivers rubbish values | 75 | // GetState returns the right state when dbus works but delivers rubbish values |
496 | 73 | func (s *NMSuite) TestGetStateRubbishValues(c *C) { | 76 | func (s *NMSuite) TestGetStateRubbishValues(c *C) { |
498 | 74 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false), 42), s.log) | 77 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "Unknown"), s.log) |
499 | 75 | state := nm.GetState() | 78 | state := nm.GetState() |
500 | 76 | c.Check(state, Equals, Unknown) | 79 | c.Check(state, Equals, Unknown) |
501 | 77 | } | 80 | } |
502 | @@ -101,11 +104,123 @@ | |||
503 | 101 | } | 104 | } |
504 | 102 | 105 | ||
505 | 103 | // WatchState calls close on its channel when the watch bails | 106 | // WatchState calls close on its channel when the watch bails |
513 | 104 | func (s *NMSuite) TestWatchClosesOnWatchBail(c *C) { | 107 | func (s *NMSuite) TestWatchStateClosesOnWatchBail(c *C) { |
514 | 105 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) | 108 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
515 | 106 | nm := New(tc, s.log) | 109 | nm := New(tc, s.log) |
516 | 107 | ch, err := nm.WatchState() | 110 | ch, err := nm.WatchState() |
517 | 108 | c.Check(err, IsNil) | 111 | c.Check(err, IsNil) |
518 | 109 | _, ok := <-ch | 112 | _, ok := <-ch |
519 | 110 | c.Check(ok, Equals, false) | 113 | c.Check(ok, Equals, false) |
520 | 114 | } | ||
521 | 115 | |||
522 | 116 | // WatchState survives rubbish values | ||
523 | 117 | func (s *NMSuite) TestWatchStateSurvivesRubbishValues(c *C) { | ||
524 | 118 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") | ||
525 | 119 | nm := New(tc, s.log) | ||
526 | 120 | ch, err := nm.WatchState() | ||
527 | 121 | c.Check(err, IsNil) | ||
528 | 122 | _, ok := <-ch | ||
529 | 123 | c.Check(ok, Equals, false) | ||
530 | 124 | } | ||
531 | 125 | |||
532 | 126 | // GetPrimaryConnection returns the right state when everything works | ||
533 | 127 | func (s *NMSuite) TestGetPrimaryConnection(c *C) { | ||
534 | 128 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), dbus.ObjectPath("/a/1")), s.log) | ||
535 | 129 | con := nm.GetPrimaryConnection() | ||
536 | 130 | c.Check(con, Equals, "/a/1") | ||
537 | 131 | } | ||
538 | 132 | |||
539 | 133 | // GetPrimaryConnection returns the right state when dbus fails | ||
540 | 134 | func (s *NMSuite) TestGetPrimaryConnectionFail(c *C) { | ||
541 | 135 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) | ||
542 | 136 | con := nm.GetPrimaryConnection() | ||
543 | 137 | c.Check(con, Equals, "") | ||
544 | 138 | } | ||
545 | 139 | |||
546 | 140 | // GetPrimaryConnection returns the right state when dbus works but delivers rubbish values | ||
547 | 141 | func (s *NMSuite) TestGetPrimaryConnectionRubbishValues(c *C) { | ||
548 | 142 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "broken"), s.log) | ||
549 | 143 | con := nm.GetPrimaryConnection() | ||
550 | 144 | c.Check(con, Equals, "") | ||
551 | 145 | } | ||
552 | 146 | |||
553 | 147 | // GetPrimaryConnection returns the right state when dbus works but delivers a rubbish structure | ||
554 | 148 | func (s *NMSuite) TestGetPrimaryConnectionRubbishStructure(c *C) { | ||
555 | 149 | nm := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), s.log) | ||
556 | 150 | con := nm.GetPrimaryConnection() | ||
557 | 151 | c.Check(con, Equals, "") | ||
558 | 152 | } | ||
559 | 153 | |||
560 | 154 | func mkPriConMap(priCon string) map[string]dbus.Variant { | ||
561 | 155 | m := make(map[string]dbus.Variant) | ||
562 | 156 | m["PrimaryConnection"] = dbus.Variant{dbus.ObjectPath(priCon)} | ||
563 | 157 | return m | ||
564 | 158 | } | ||
565 | 159 | |||
566 | 160 | // WatchPrimaryConnection sends a stream of Connections over the channel | ||
567 | 161 | func (s *NMSuite) TestWatchPrimaryConnection(c *C) { | ||
568 | 162 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), | ||
569 | 163 | mkPriConMap("/a/1"), | ||
570 | 164 | mkPriConMap("/b/2"), | ||
571 | 165 | mkPriConMap("/c/3")) | ||
572 | 166 | nm := New(tc, s.log) | ||
573 | 167 | ch, err := nm.WatchPrimaryConnection() | ||
574 | 168 | c.Check(err, IsNil) | ||
575 | 169 | l := []string{<-ch, <-ch, <-ch} | ||
576 | 170 | c.Check(l, DeepEquals, []string{"/a/1", "/b/2", "/c/3"}) | ||
577 | 171 | } | ||
578 | 172 | |||
579 | 173 | // WatchPrimaryConnection returns on error if the dbus call fails | ||
580 | 174 | func (s *NMSuite) TestWatchPrimaryConnectionFails(c *C) { | ||
581 | 175 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) | ||
582 | 176 | _, err := nm.WatchPrimaryConnection() | ||
583 | 177 | c.Check(err, NotNil) | ||
584 | 178 | } | ||
585 | 179 | |||
586 | 180 | // WatchPrimaryConnection calls close on its channel when the watch bails | ||
587 | 181 | func (s *NMSuite) TestWatchPrimaryConnectionClosesOnWatchBail(c *C) { | ||
588 | 182 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) | ||
589 | 183 | nm := New(tc, s.log) | ||
590 | 184 | ch, err := nm.WatchPrimaryConnection() | ||
591 | 185 | c.Check(err, IsNil) | ||
592 | 186 | _, ok := <-ch | ||
593 | 187 | c.Check(ok, Equals, false) | ||
594 | 188 | } | ||
595 | 189 | |||
596 | 190 | // WatchPrimaryConnection survives rubbish values | ||
597 | 191 | func (s *NMSuite) TestWatchPrimaryConnectionSurvivesRubbishValues(c *C) { | ||
598 | 192 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") | ||
599 | 193 | nm := New(tc, s.log) | ||
600 | 194 | ch, err := nm.WatchPrimaryConnection() | ||
601 | 195 | c.Assert(err, IsNil) | ||
602 | 196 | _, ok := <-ch | ||
603 | 197 | c.Check(ok, Equals, false) | ||
604 | 198 | } | ||
605 | 199 | |||
606 | 200 | // WatchPrimaryConnection ignores non-PrimaryConnection PropertyChanged | ||
607 | 201 | func (s *NMSuite) TestWatchPrimaryConnectionIgnoresIrrelephant(c *C) { | ||
608 | 202 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), | ||
609 | 203 | map[string]dbus.Variant{"foo": dbus.Variant{}}, | ||
610 | 204 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, | ||
611 | 205 | ) | ||
612 | 206 | nm := New(tc, s.log) | ||
613 | 207 | ch, err := nm.WatchPrimaryConnection() | ||
614 | 208 | c.Assert(err, IsNil) | ||
615 | 209 | v, ok := <-ch | ||
616 | 210 | c.Check(ok, Equals, true) | ||
617 | 211 | c.Check(v, Equals, "42") | ||
618 | 212 | } | ||
619 | 213 | |||
620 | 214 | // WatchPrimaryConnection ignores rubbish PrimaryConnections | ||
621 | 215 | func (s *NMSuite) TestWatchPrimaryConnectionIgnoresRubbishValues(c *C) { | ||
622 | 216 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), | ||
623 | 217 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{-12}}, | ||
624 | 218 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, | ||
625 | 219 | ) | ||
626 | 220 | nm := New(tc, s.log) | ||
627 | 221 | ch, err := nm.WatchPrimaryConnection() | ||
628 | 222 | c.Assert(err, IsNil) | ||
629 | 223 | v, ok := <-ch | ||
630 | 224 | c.Check(ok, Equals, true) | ||
631 | 225 | c.Check(v, Equals, "42") | ||
632 | 111 | } | 226 | } |
633 | 112 | 227 | ||
634 | === modified file 'bus/notifications/raw.go' | |||
635 | --- bus/notifications/raw.go 2014-01-27 14:22:00 +0000 | |||
636 | +++ bus/notifications/raw.go 2014-04-04 13:58:43 +0000 | |||
637 | @@ -22,7 +22,6 @@ | |||
638 | 22 | // this is the lower-level api | 22 | // this is the lower-level api |
639 | 23 | 23 | ||
640 | 24 | import ( | 24 | import ( |
641 | 25 | "fmt" | ||
642 | 26 | "launchpad.net/go-dbus/v1" | 25 | "launchpad.net/go-dbus/v1" |
643 | 27 | "launchpad.net/ubuntu-push/bus" | 26 | "launchpad.net/ubuntu-push/bus" |
644 | 28 | "launchpad.net/ubuntu-push/logger" | 27 | "launchpad.net/ubuntu-push/logger" |
645 | @@ -69,16 +68,13 @@ | |||
646 | 69 | timeout int32) (uint32, error) { | 68 | timeout int32) (uint32, error) { |
647 | 70 | // that's a long argument list! Take a breather. | 69 | // that's a long argument list! Take a breather. |
648 | 71 | // | 70 | // |
651 | 72 | rvs, err := raw.bus.Call("Notify", app_name, reuse_id, icon, | 71 | var res uint32 |
652 | 73 | summary, body, actions, hints, timeout) | 72 | err := raw.bus.Call("Notify", bus.Args(app_name, reuse_id, icon, |
653 | 73 | summary, body, actions, hints, timeout), &res) | ||
654 | 74 | if err != nil { | 74 | if err != nil { |
655 | 75 | return 0, err | 75 | return 0, err |
656 | 76 | } | 76 | } |
662 | 77 | if len(rvs) != 1 { | 77 | return res, nil |
658 | 78 | return 0, fmt.Errorf("Wrong number of values in Notify response: %d", | ||
659 | 79 | len(rvs)) | ||
660 | 80 | } | ||
661 | 81 | return rvs[0].(uint32), nil | ||
663 | 82 | } | 78 | } |
664 | 83 | 79 | ||
665 | 84 | // WatchActions listens for ActionInvoked signals from the notification daemon | 80 | // WatchActions listens for ActionInvoked signals from the notification daemon |
666 | 85 | 81 | ||
667 | === added directory 'bus/systemimage' | |||
668 | === added file 'bus/systemimage/systemimage.go' | |||
669 | --- bus/systemimage/systemimage.go 1970-01-01 00:00:00 +0000 | |||
670 | +++ bus/systemimage/systemimage.go 2014-04-04 13:58:43 +0000 | |||
671 | @@ -0,0 +1,68 @@ | |||
672 | 1 | /* | ||
673 | 2 | Copyright 2013-2014 Canonical Ltd. | ||
674 | 3 | |||
675 | 4 | This program is free software: you can redistribute it and/or modify it | ||
676 | 5 | under the terms of the GNU General Public License version 3, as published | ||
677 | 6 | by the Free Software Foundation. | ||
678 | 7 | |||
679 | 8 | This program is distributed in the hope that it will be useful, but | ||
680 | 9 | WITHOUT ANY WARRANTY; without even the implied warranties of | ||
681 | 10 | MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
682 | 11 | PURPOSE. See the GNU General Public License for more details. | ||
683 | 12 | |||
684 | 13 | You should have received a copy of the GNU General Public License along | ||
685 | 14 | with this program. If not, see <http://www.gnu.org/licenses/>. | ||
686 | 15 | */ | ||
687 | 16 | |||
688 | 17 | // Package systemimage is a mimimal wrapper for the system-image dbus API. | ||
689 | 18 | package systemimage | ||
690 | 19 | |||
691 | 20 | import ( | ||
692 | 21 | "launchpad.net/ubuntu-push/bus" | ||
693 | 22 | "launchpad.net/ubuntu-push/logger" | ||
694 | 23 | ) | ||
695 | 24 | |||
696 | 25 | // system-image service lives on a well-known bus.Address | ||
697 | 26 | var BusAddress bus.Address = bus.Address{ | ||
698 | 27 | Interface: "com.canonical.SystemImage", | ||
699 | 28 | Path: "/Service", | ||
700 | 29 | Name: "com.canonical.SystemImage", | ||
701 | 30 | } | ||
702 | 31 | |||
703 | 32 | // InfoResult holds the result of the system-image service Info method. | ||
704 | 33 | type InfoResult struct { | ||
705 | 34 | BuildNumber int32 | ||
706 | 35 | Device string | ||
707 | 36 | Channel string | ||
708 | 37 | // xxx channel_target missing | ||
709 | 38 | LastUpdate string | ||
710 | 39 | VersionDetail map[string]string | ||
711 | 40 | } | ||
712 | 41 | |||
713 | 42 | // A SystemImage exposes the a subset of system-image service. | ||
714 | 43 | type SystemImage interface { | ||
715 | 44 | Info() (*InfoResult, error) | ||
716 | 45 | } | ||
717 | 46 | |||
718 | 47 | type systemImage struct { | ||
719 | 48 | endp bus.Endpoint | ||
720 | 49 | log logger.Logger | ||
721 | 50 | } | ||
722 | 51 | |||
723 | 52 | // New builds a new system-image service wrapper that uses the provided bus.Endpoint | ||
724 | 53 | func New(endp bus.Endpoint, log logger.Logger) SystemImage { | ||
725 | 54 | return &systemImage{endp, log} | ||
726 | 55 | } | ||
727 | 56 | |||
728 | 57 | var _ SystemImage = &systemImage{} // ensures it conforms | ||
729 | 58 | |||
730 | 59 | func (si *systemImage) Info() (*InfoResult, error) { | ||
731 | 60 | si.log.Debugf("Invoking Info") | ||
732 | 61 | res := &InfoResult{} | ||
733 | 62 | err := si.endp.Call("Info", bus.Args(), &res.BuildNumber, &res.Device, &res.Channel, &res.LastUpdate, &res.VersionDetail) | ||
734 | 63 | if err != nil { | ||
735 | 64 | si.log.Errorf("Info failed: %v", err) | ||
736 | 65 | return nil, err | ||
737 | 66 | } | ||
738 | 67 | return res, err | ||
739 | 68 | } | ||
740 | 0 | 69 | ||
741 | === added file 'bus/systemimage/systemimage_test.go' | |||
742 | --- bus/systemimage/systemimage_test.go 1970-01-01 00:00:00 +0000 | |||
743 | +++ bus/systemimage/systemimage_test.go 2014-04-04 13:58:43 +0000 | |||
744 | @@ -0,0 +1,62 @@ | |||
745 | 1 | /* | ||
746 | 2 | Copyright 2013-2014 Canonical Ltd. | ||
747 | 3 | |||
748 | 4 | This program is free software: you can redistribute it and/or modify it | ||
749 | 5 | under the terms of the GNU General Public License version 3, as published | ||
750 | 6 | by the Free Software Foundation. | ||
751 | 7 | |||
752 | 8 | This program is distributed in the hope that it will be useful, but | ||
753 | 9 | WITHOUT ANY WARRANTY; without even the implied warranties of | ||
754 | 10 | MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
755 | 11 | PURPOSE. See the GNU General Public License for more details. | ||
756 | 12 | |||
757 | 13 | You should have received a copy of the GNU General Public License along | ||
758 | 14 | with this program. If not, see <http://www.gnu.org/licenses/>. | ||
759 | 15 | */ | ||
760 | 16 | |||
761 | 17 | package systemimage | ||
762 | 18 | |||
763 | 19 | import ( | ||
764 | 20 | "testing" | ||
765 | 21 | |||
766 | 22 | . "launchpad.net/gocheck" | ||
767 | 23 | |||
768 | 24 | testibus "launchpad.net/ubuntu-push/bus/testing" | ||
769 | 25 | "launchpad.net/ubuntu-push/logger" | ||
770 | 26 | helpers "launchpad.net/ubuntu-push/testing" | ||
771 | 27 | "launchpad.net/ubuntu-push/testing/condition" | ||
772 | 28 | ) | ||
773 | 29 | |||
774 | 30 | // hook up gocheck | ||
775 | 31 | func TestSystemImage(t *testing.T) { TestingT(t) } | ||
776 | 32 | |||
777 | 33 | type SISuite struct { | ||
778 | 34 | log logger.Logger | ||
779 | 35 | } | ||
780 | 36 | |||
781 | 37 | var _ = Suite(&SISuite{}) | ||
782 | 38 | |||
783 | 39 | func (s *SISuite) SetUpTest(c *C) { | ||
784 | 40 | s.log = helpers.NewTestLogger(c, "debug") | ||
785 | 41 | } | ||
786 | 42 | |||
787 | 43 | func (s *SISuite) TestWorks(c *C) { | ||
788 | 44 | endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{int32(101), "mako", "daily", "Unknown", map[string]string{}}) | ||
789 | 45 | si := New(endp, s.log) | ||
790 | 46 | res, err := si.Info() | ||
791 | 47 | c.Assert(err, IsNil) | ||
792 | 48 | c.Check(res, DeepEquals, &InfoResult{ | ||
793 | 49 | BuildNumber: 101, | ||
794 | 50 | Device: "mako", | ||
795 | 51 | Channel: "daily", | ||
796 | 52 | LastUpdate: "Unknown", | ||
797 | 53 | VersionDetail: map[string]string{}, | ||
798 | 54 | }) | ||
799 | 55 | } | ||
800 | 56 | |||
801 | 57 | func (s *SISuite) TestFailsIfCallFails(c *C) { | ||
802 | 58 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) | ||
803 | 59 | si := New(endp, s.log) | ||
804 | 60 | _, err := si.Info() | ||
805 | 61 | c.Check(err, NotNil) | ||
806 | 62 | } | ||
807 | 0 | 63 | ||
808 | === modified file 'bus/testing/testing_endpoint.go' | |||
809 | --- bus/testing/testing_endpoint.go 2014-02-06 13:26:13 +0000 | |||
810 | +++ bus/testing/testing_endpoint.go 2014-04-04 13:58:43 +0000 | |||
811 | @@ -21,6 +21,9 @@ | |||
812 | 21 | import ( | 21 | import ( |
813 | 22 | "errors" | 22 | "errors" |
814 | 23 | "fmt" | 23 | "fmt" |
815 | 24 | |||
816 | 25 | "launchpad.net/go-dbus/v1" | ||
817 | 26 | |||
818 | 24 | "launchpad.net/ubuntu-push/bus" | 27 | "launchpad.net/ubuntu-push/bus" |
819 | 25 | "launchpad.net/ubuntu-push/testing/condition" | 28 | "launchpad.net/ubuntu-push/testing/condition" |
820 | 26 | "sync" | 29 | "sync" |
821 | @@ -37,6 +40,7 @@ | |||
822 | 37 | callCond condition.Interface | 40 | callCond condition.Interface |
823 | 38 | retvals [][]interface{} | 41 | retvals [][]interface{} |
824 | 39 | watchTicker chan bool | 42 | watchTicker chan bool |
825 | 43 | watchLck sync.RWMutex | ||
826 | 40 | callArgs []callArgs | 44 | callArgs []callArgs |
827 | 41 | callArgsLck sync.RWMutex | 45 | callArgsLck sync.RWMutex |
828 | 42 | } | 46 | } |
829 | @@ -62,7 +66,9 @@ | |||
830 | 62 | // instead of the default timeout to wait while sending values over | 66 | // instead of the default timeout to wait while sending values over |
831 | 63 | // WatchSignal. Set it to nil again to restore default behaviour. | 67 | // WatchSignal. Set it to nil again to restore default behaviour. |
832 | 64 | func SetWatchTicker(tc bus.Endpoint, watchTicker chan bool) { | 68 | func SetWatchTicker(tc bus.Endpoint, watchTicker chan bool) { |
833 | 69 | tc.(*testingEndpoint).watchLck.Lock() | ||
834 | 65 | tc.(*testingEndpoint).watchTicker = watchTicker | 70 | tc.(*testingEndpoint).watchTicker = watchTicker |
835 | 71 | tc.(*testingEndpoint).watchLck.Unlock() | ||
836 | 66 | } | 72 | } |
837 | 67 | 73 | ||
838 | 68 | // GetCallArgs returns a list of the arguments for each Call() invocation. | 74 | // GetCallArgs returns a list of the arguments for each Call() invocation. |
839 | @@ -79,8 +85,11 @@ | |||
840 | 79 | go func() { | 85 | go func() { |
841 | 80 | for _, v := range tc.retvals { | 86 | for _, v := range tc.retvals { |
842 | 81 | f(v...) | 87 | f(v...) |
845 | 82 | if tc.watchTicker != nil { | 88 | tc.watchLck.RLock() |
846 | 83 | <-tc.watchTicker | 89 | ticker := tc.watchTicker |
847 | 90 | tc.watchLck.RUnlock() | ||
848 | 91 | if ticker != nil { | ||
849 | 92 | <-ticker | ||
850 | 84 | } else { | 93 | } else { |
851 | 85 | time.Sleep(10 * time.Millisecond) | 94 | time.Sleep(10 * time.Millisecond) |
852 | 86 | } | 95 | } |
853 | @@ -95,32 +104,50 @@ | |||
854 | 95 | 104 | ||
855 | 96 | // See Endpoint's Call. This Call will check its condition to decide whether | 105 | // See Endpoint's Call. This Call will check its condition to decide whether |
856 | 97 | // to return an error, or the first of its return values | 106 | // to return an error, or the first of its return values |
858 | 98 | func (tc *testingEndpoint) Call(member string, args ...interface{}) ([]interface{}, error) { | 107 | func (tc *testingEndpoint) Call(member string, args []interface{}, rvs ...interface{}) error { |
859 | 99 | tc.callArgsLck.Lock() | 108 | tc.callArgsLck.Lock() |
860 | 100 | defer tc.callArgsLck.Unlock() | 109 | defer tc.callArgsLck.Unlock() |
861 | 101 | 110 | ||
862 | 102 | tc.callArgs = append(tc.callArgs, callArgs{member, args}) | 111 | tc.callArgs = append(tc.callArgs, callArgs{member, args}) |
863 | 103 | if tc.callCond.OK() { | 112 | if tc.callCond.OK() { |
864 | 113 | expected := len(rvs) | ||
865 | 114 | var provided int | ||
866 | 104 | if len(tc.retvals) == 0 { | 115 | if len(tc.retvals) == 0 { |
870 | 105 | panic("No return values provided!") | 116 | if expected != 0 { |
871 | 106 | } | 117 | panic("No return values provided!") |
872 | 107 | return tc.retvals[0], nil | 118 | } |
873 | 119 | provided = 0 | ||
874 | 120 | } else { | ||
875 | 121 | provided = len(tc.retvals[0]) | ||
876 | 122 | } | ||
877 | 123 | if provided != expected { | ||
878 | 124 | return errors.New("provided/expected return vals mismatch") | ||
879 | 125 | } | ||
880 | 126 | if provided != 0 { | ||
881 | 127 | x := dbus.NewMethodCallMessage("", "", "", "") | ||
882 | 128 | err := x.AppendArgs(tc.retvals[0]...) | ||
883 | 129 | if err != nil { | ||
884 | 130 | return err | ||
885 | 131 | } | ||
886 | 132 | err = x.Args(rvs...) | ||
887 | 133 | if err != nil { | ||
888 | 134 | return err | ||
889 | 135 | } | ||
890 | 136 | } | ||
891 | 137 | return nil | ||
892 | 108 | } else { | 138 | } else { |
894 | 109 | return nil, errors.New("no way") | 139 | return errors.New("no way") |
895 | 110 | } | 140 | } |
896 | 111 | } | 141 | } |
897 | 112 | 142 | ||
898 | 113 | // See Endpoint's GetProperty. This one is just another name for Call. | 143 | // See Endpoint's GetProperty. This one is just another name for Call. |
899 | 114 | func (tc *testingEndpoint) GetProperty(property string) (interface{}, error) { | 144 | func (tc *testingEndpoint) GetProperty(property string) (interface{}, error) { |
901 | 115 | rvs, err := tc.Call(property) | 145 | var res interface{} |
902 | 146 | err := tc.Call(property, bus.Args(), &res) | ||
903 | 116 | if err != nil { | 147 | if err != nil { |
904 | 117 | return nil, err | 148 | return nil, err |
905 | 118 | } | 149 | } |
911 | 119 | if len(rvs) != 1 { | 150 | return res, err |
907 | 120 | return nil, errors.New("Wrong number of values given to testingEndpoint" + | ||
908 | 121 | " -- GetProperty only returns a single value for now!") | ||
909 | 122 | } | ||
910 | 123 | return rvs[0], err | ||
912 | 124 | } | 151 | } |
913 | 125 | 152 | ||
914 | 126 | // See Endpoint's Dial. This one will check its dialCondition to | 153 | // See Endpoint's Dial. This one will check its dialCondition to |
915 | 127 | 154 | ||
916 | === modified file 'bus/testing/testing_endpoint_test.go' | |||
917 | --- bus/testing/testing_endpoint_test.go 2014-02-05 02:13:35 +0000 | |||
918 | +++ bus/testing/testing_endpoint_test.go 2014-04-04 13:58:43 +0000 | |||
919 | @@ -18,6 +18,7 @@ | |||
920 | 18 | 18 | ||
921 | 19 | import ( | 19 | import ( |
922 | 20 | . "launchpad.net/gocheck" | 20 | . "launchpad.net/gocheck" |
923 | 21 | "launchpad.net/ubuntu-push/bus" | ||
924 | 21 | "launchpad.net/ubuntu-push/testing/condition" | 22 | "launchpad.net/ubuntu-push/testing/condition" |
925 | 22 | "testing" | 23 | "testing" |
926 | 23 | "time" | 24 | "time" |
927 | @@ -35,26 +36,26 @@ | |||
928 | 35 | func (s *TestingEndpointSuite) TestCallReturnsFirstRetval(c *C) { | 36 | func (s *TestingEndpointSuite) TestCallReturnsFirstRetval(c *C) { |
929 | 36 | var m, n uint32 = 42, 17 | 37 | var m, n uint32 = 42, 17 |
930 | 37 | endp := NewTestingEndpoint(nil, condition.Work(true), m, n) | 38 | endp := NewTestingEndpoint(nil, condition.Work(true), m, n) |
932 | 38 | vs, e := endp.Call("what") | 39 | var r uint32 |
933 | 40 | e := endp.Call("what", bus.Args(), &r) | ||
934 | 39 | c.Check(e, IsNil) | 41 | c.Check(e, IsNil) |
937 | 40 | c.Check(vs, HasLen, 1) | 42 | c.Check(r, Equals, m) |
936 | 41 | c.Check(vs[0], Equals, m) | ||
938 | 42 | } | 43 | } |
939 | 43 | 44 | ||
940 | 44 | // Test the same Call() but with multi-valued endpoint | 45 | // Test the same Call() but with multi-valued endpoint |
941 | 45 | func (s *TestingEndpointSuite) TestMultiValuedCall(c *C) { | 46 | func (s *TestingEndpointSuite) TestMultiValuedCall(c *C) { |
942 | 46 | var m, n uint32 = 42, 17 | 47 | var m, n uint32 = 42, 17 |
943 | 47 | endp := NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{m}, []interface{}{n}) | 48 | endp := NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{m}, []interface{}{n}) |
945 | 48 | vs, e := endp.Call("what") | 49 | var r uint32 |
946 | 50 | e := endp.Call("what", bus.Args(), &r) | ||
947 | 49 | c.Check(e, IsNil) | 51 | c.Check(e, IsNil) |
950 | 50 | c.Check(vs, HasLen, 1) | 52 | c.Check(r, Equals, m) |
949 | 51 | c.Check(vs[0], Equals, m) | ||
951 | 52 | } | 53 | } |
952 | 53 | 54 | ||
953 | 54 | // Test that Call() with a negative condition returns an error. | 55 | // Test that Call() with a negative condition returns an error. |
954 | 55 | func (s *TestingEndpointSuite) TestCallFails(c *C) { | 56 | func (s *TestingEndpointSuite) TestCallFails(c *C) { |
955 | 56 | endp := NewTestingEndpoint(nil, condition.Work(false)) | 57 | endp := NewTestingEndpoint(nil, condition.Work(false)) |
957 | 57 | _, e := endp.Call("what") | 58 | e := endp.Call("what", bus.Args()) |
958 | 58 | c.Check(e, NotNil) | 59 | c.Check(e, NotNil) |
959 | 59 | } | 60 | } |
960 | 60 | 61 | ||
961 | @@ -62,13 +63,14 @@ | |||
962 | 62 | // a helpful message. | 63 | // a helpful message. |
963 | 63 | func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage(c *C) { | 64 | func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage(c *C) { |
964 | 64 | endp := NewTestingEndpoint(nil, condition.Work(true)) | 65 | endp := NewTestingEndpoint(nil, condition.Work(true)) |
966 | 65 | c.Check(func() { endp.Call("") }, PanicMatches, "No return values provided.*") | 66 | var x int32 |
967 | 67 | c.Check(func() { endp.Call("", bus.Args(), &x) }, PanicMatches, "No return values provided.*") | ||
968 | 66 | } | 68 | } |
969 | 67 | 69 | ||
970 | 68 | // Test that Call() updates callArgs | 70 | // Test that Call() updates callArgs |
971 | 69 | func (s *TestingEndpointSuite) TestCallArgs(c *C) { | 71 | func (s *TestingEndpointSuite) TestCallArgs(c *C) { |
974 | 70 | endp := NewTestingEndpoint(nil, condition.Work(true), 0) | 72 | endp := NewTestingEndpoint(nil, condition.Work(true)) |
975 | 71 | _, err := endp.Call("what", "is", "this", "thing") | 73 | err := endp.Call("what", bus.Args("is", "this", "thing")) |
976 | 72 | c.Assert(err, IsNil) | 74 | c.Assert(err, IsNil) |
977 | 73 | c.Check(GetCallArgs(endp), DeepEquals, | 75 | c.Check(GetCallArgs(endp), DeepEquals, |
978 | 74 | []callArgs{{"what", []interface{}{"is", "this", "thing"}}}) | 76 | []callArgs{{"what", []interface{}{"is", "this", "thing"}}}) |
979 | 75 | 77 | ||
980 | === modified file 'bus/urldispatcher/urldispatcher.go' | |||
981 | --- bus/urldispatcher/urldispatcher.go 2014-01-23 00:54:51 +0000 | |||
982 | +++ bus/urldispatcher/urldispatcher.go 2014-04-04 13:58:43 +0000 | |||
983 | @@ -49,7 +49,7 @@ | |||
984 | 49 | 49 | ||
985 | 50 | func (ud *urlDispatcher) DispatchURL(url string) error { | 50 | func (ud *urlDispatcher) DispatchURL(url string) error { |
986 | 51 | ud.log.Debugf("Dispatching %s", url) | 51 | ud.log.Debugf("Dispatching %s", url) |
988 | 52 | _, err := ud.endp.Call("DispatchURL", url) | 52 | err := ud.endp.Call("DispatchURL", bus.Args(url)) |
989 | 53 | if err != nil { | 53 | if err != nil { |
990 | 54 | ud.log.Errorf("Dispatch to %s failed with %s", url, err) | 54 | ud.log.Errorf("Dispatch to %s failed with %s", url, err) |
991 | 55 | } | 55 | } |
992 | 56 | 56 | ||
993 | === modified file 'client/client.go' | |||
994 | --- client/client.go 2014-03-26 16:26:36 +0000 | |||
995 | +++ client/client.go 2014-04-04 13:58:43 +0000 | |||
996 | @@ -20,13 +20,19 @@ | |||
997 | 20 | 20 | ||
998 | 21 | import ( | 21 | import ( |
999 | 22 | "encoding/pem" | 22 | "encoding/pem" |
1000 | 23 | "errors" | ||
1001 | 23 | "fmt" | 24 | "fmt" |
1002 | 24 | "io/ioutil" | 25 | "io/ioutil" |
1003 | 26 | "os" | ||
1004 | 27 | "strings" | ||
1005 | 28 | |||
1006 | 25 | "launchpad.net/go-dbus/v1" | 29 | "launchpad.net/go-dbus/v1" |
1007 | 30 | |||
1008 | 26 | "launchpad.net/ubuntu-push/bus" | 31 | "launchpad.net/ubuntu-push/bus" |
1009 | 27 | "launchpad.net/ubuntu-push/bus/connectivity" | 32 | "launchpad.net/ubuntu-push/bus/connectivity" |
1010 | 28 | "launchpad.net/ubuntu-push/bus/networkmanager" | 33 | "launchpad.net/ubuntu-push/bus/networkmanager" |
1011 | 29 | "launchpad.net/ubuntu-push/bus/notifications" | 34 | "launchpad.net/ubuntu-push/bus/notifications" |
1012 | 35 | "launchpad.net/ubuntu-push/bus/systemimage" | ||
1013 | 30 | "launchpad.net/ubuntu-push/bus/urldispatcher" | 36 | "launchpad.net/ubuntu-push/bus/urldispatcher" |
1014 | 31 | "launchpad.net/ubuntu-push/client/session" | 37 | "launchpad.net/ubuntu-push/client/session" |
1015 | 32 | "launchpad.net/ubuntu-push/client/session/levelmap" | 38 | "launchpad.net/ubuntu-push/client/session/levelmap" |
1016 | @@ -34,7 +40,6 @@ | |||
1017 | 34 | "launchpad.net/ubuntu-push/logger" | 40 | "launchpad.net/ubuntu-push/logger" |
1018 | 35 | "launchpad.net/ubuntu-push/util" | 41 | "launchpad.net/ubuntu-push/util" |
1019 | 36 | "launchpad.net/ubuntu-push/whoopsie/identifier" | 42 | "launchpad.net/ubuntu-push/whoopsie/identifier" |
1020 | 37 | "os" | ||
1021 | 38 | ) | 43 | ) |
1022 | 39 | 44 | ||
1023 | 40 | // ClientConfig holds the client configuration | 45 | // ClientConfig holds the client configuration |
1024 | @@ -42,8 +47,13 @@ | |||
1025 | 42 | connectivity.ConnectivityConfig // q.v. | 47 | connectivity.ConnectivityConfig // q.v. |
1026 | 43 | // A reasonably large timeout for receive/answer pairs | 48 | // A reasonably large timeout for receive/answer pairs |
1027 | 44 | ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"` | 49 | ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"` |
1030 | 45 | // The server to connect to | 50 | // A timeout to use when trying to connect to the server |
1031 | 46 | Addr config.ConfigHostPort | 51 | ConnectTimeout config.ConfigTimeDuration `json:"connect_timeout"` |
1032 | 52 | // The server to connect to or url to query for hosts to connect to | ||
1033 | 53 | Addr string | ||
1034 | 54 | // Host list management | ||
1035 | 55 | HostsCachingExpiryTime config.ConfigTimeDuration `json:"hosts_cache_expiry"` // potentially refresh host list after | ||
1036 | 56 | ExpectAllRepairedTime config.ConfigTimeDuration `json:"expect_all_repaired"` // worth retrying all servers after | ||
1037 | 47 | // The PEM-encoded server certificate | 57 | // The PEM-encoded server certificate |
1038 | 48 | CertPEMFile string `json:"cert_pem_file"` | 58 | CertPEMFile string `json:"cert_pem_file"` |
1039 | 49 | // The logging level (one of "debug", "info", "error") | 59 | // The logging level (one of "debug", "info", "error") |
1040 | @@ -62,6 +72,8 @@ | |||
1041 | 62 | notificationsEndp bus.Endpoint | 72 | notificationsEndp bus.Endpoint |
1042 | 63 | urlDispatcherEndp bus.Endpoint | 73 | urlDispatcherEndp bus.Endpoint |
1043 | 64 | connectivityEndp bus.Endpoint | 74 | connectivityEndp bus.Endpoint |
1044 | 75 | systemImageEndp bus.Endpoint | ||
1045 | 76 | systemImageInfo *systemimage.InfoResult | ||
1046 | 65 | connCh chan bool | 77 | connCh chan bool |
1047 | 66 | hasConnectivity bool | 78 | hasConnectivity bool |
1048 | 67 | actionsCh <-chan notifications.RawActionReply | 79 | actionsCh <-chan notifications.RawActionReply |
1049 | @@ -89,6 +101,12 @@ | |||
1050 | 89 | if err != nil { | 101 | if err != nil { |
1051 | 90 | return fmt.Errorf("reading config: %v", err) | 102 | return fmt.Errorf("reading config: %v", err) |
1052 | 91 | } | 103 | } |
1053 | 104 | // ignore spaces | ||
1054 | 105 | client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1) | ||
1055 | 106 | if client.config.Addr == "" { | ||
1056 | 107 | return errors.New("no hosts specified") | ||
1057 | 108 | } | ||
1058 | 109 | |||
1059 | 92 | // later, we'll be specifying more logging options in the config file | 110 | // later, we'll be specifying more logging options in the config file |
1060 | 93 | client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel) | 111 | client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel) |
1061 | 94 | 112 | ||
1062 | @@ -97,6 +115,7 @@ | |||
1063 | 97 | client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log) | 115 | client.notificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, client.log) |
1064 | 98 | client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log) | 116 | client.urlDispatcherEndp = bus.SessionBus.Endpoint(urldispatcher.BusAddress, client.log) |
1065 | 99 | client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log) | 117 | client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log) |
1066 | 118 | client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log) | ||
1067 | 100 | 119 | ||
1068 | 101 | client.connCh = make(chan bool, 1) | 120 | client.connCh = make(chan bool, 1) |
1069 | 102 | client.sessionConnectedCh = make(chan uint32, 1) | 121 | client.sessionConnectedCh = make(chan uint32, 1) |
1070 | @@ -116,6 +135,18 @@ | |||
1071 | 116 | return nil | 135 | return nil |
1072 | 117 | } | 136 | } |
1073 | 118 | 137 | ||
1074 | 138 | // deriveSessionConfig dervies the session configuration from the client configuration bits. | ||
1075 | 139 | func (client *PushClient) deriveSessionConfig(info map[string]interface{}) session.ClientSessionConfig { | ||
1076 | 140 | return session.ClientSessionConfig{ | ||
1077 | 141 | ConnectTimeout: client.config.ConnectTimeout.TimeDuration(), | ||
1078 | 142 | ExchangeTimeout: client.config.ExchangeTimeout.TimeDuration(), | ||
1079 | 143 | HostsCachingExpiryTime: client.config.HostsCachingExpiryTime.TimeDuration(), | ||
1080 | 144 | ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(), | ||
1081 | 145 | PEM: client.pem, | ||
1082 | 146 | Info: info, | ||
1083 | 147 | } | ||
1084 | 148 | } | ||
1085 | 149 | |||
1086 | 119 | // getDeviceId gets the whoopsie identifier for the device | 150 | // getDeviceId gets the whoopsie identifier for the device |
1087 | 120 | func (client *PushClient) getDeviceId() error { | 151 | func (client *PushClient) getDeviceId() error { |
1088 | 121 | err := client.idder.Generate() | 152 | err := client.idder.Generate() |
1089 | @@ -133,8 +164,17 @@ | |||
1090 | 133 | iniCh := make(chan uint32) | 164 | iniCh := make(chan uint32) |
1091 | 134 | go func() { iniCh <- util.NewAutoRedialer(client.notificationsEndp).Redial() }() | 165 | go func() { iniCh <- util.NewAutoRedialer(client.notificationsEndp).Redial() }() |
1092 | 135 | go func() { iniCh <- util.NewAutoRedialer(client.urlDispatcherEndp).Redial() }() | 166 | go func() { iniCh <- util.NewAutoRedialer(client.urlDispatcherEndp).Redial() }() |
1095 | 136 | <-iniCh | 167 | go func() { iniCh <- util.NewAutoRedialer(client.systemImageEndp).Redial() }() |
1096 | 137 | <-iniCh | 168 | <-iniCh |
1097 | 169 | <-iniCh | ||
1098 | 170 | <-iniCh | ||
1099 | 171 | |||
1100 | 172 | sysimg := systemimage.New(client.systemImageEndp, client.log) | ||
1101 | 173 | info, err := sysimg.Info() | ||
1102 | 174 | if err != nil { | ||
1103 | 175 | return err | ||
1104 | 176 | } | ||
1105 | 177 | client.systemImageInfo = info | ||
1106 | 138 | 178 | ||
1107 | 139 | actionsCh, err := notifications.Raw(client.notificationsEndp, client.log).WatchActions() | 179 | actionsCh, err := notifications.Raw(client.notificationsEndp, client.log).WatchActions() |
1108 | 140 | client.actionsCh = actionsCh | 180 | client.actionsCh = actionsCh |
1109 | @@ -143,8 +183,13 @@ | |||
1110 | 143 | 183 | ||
1111 | 144 | // initSession creates the session object | 184 | // initSession creates the session object |
1112 | 145 | func (client *PushClient) initSession() error { | 185 | func (client *PushClient) initSession() error { |
1115 | 146 | sess, err := session.NewSession(string(client.config.Addr), client.pem, | 186 | info := map[string]interface{}{ |
1116 | 147 | client.config.ExchangeTimeout.Duration, client.deviceId, | 187 | "device": client.systemImageInfo.Device, |
1117 | 188 | "channel": client.systemImageInfo.Channel, | ||
1118 | 189 | "build_number": client.systemImageInfo.BuildNumber, | ||
1119 | 190 | } | ||
1120 | 191 | sess, err := session.NewSession(client.config.Addr, | ||
1121 | 192 | client.deriveSessionConfig(info), client.deviceId, | ||
1122 | 148 | client.levelMapFactory, client.log) | 193 | client.levelMapFactory, client.log) |
1123 | 149 | if err != nil { | 194 | if err != nil { |
1124 | 150 | return err | 195 | return err |
1125 | @@ -185,8 +230,54 @@ | |||
1126 | 185 | } | 230 | } |
1127 | 186 | } | 231 | } |
1128 | 187 | 232 | ||
1129 | 233 | // filterNotification finds out if the notification is about an actual | ||
1130 | 234 | // upgrade for the device. It expects msg.Decoded entries to look | ||
1131 | 235 | // like: | ||
1132 | 236 | // | ||
1133 | 237 | // { | ||
1134 | 238 | // "IMAGE-CHANNEL/DEVICE-MODEL": [BUILD-NUMBER, CHANNEL-ALIAS] | ||
1135 | 239 | // ... | ||
1136 | 240 | // } | ||
1137 | 241 | func (client *PushClient) filterNotification(msg *session.Notification) bool { | ||
1138 | 242 | n := len(msg.Decoded) | ||
1139 | 243 | if n == 0 { | ||
1140 | 244 | return false | ||
1141 | 245 | } | ||
1142 | 246 | // they are all for us, consider last | ||
1143 | 247 | last := msg.Decoded[n-1] | ||
1144 | 248 | tag := fmt.Sprintf("%s/%s", client.systemImageInfo.Channel, client.systemImageInfo.Device) | ||
1145 | 249 | entry, ok := last[tag] | ||
1146 | 250 | if !ok { | ||
1147 | 251 | return false | ||
1148 | 252 | } | ||
1149 | 253 | pair, ok := entry.([]interface{}) | ||
1150 | 254 | if !ok { | ||
1151 | 255 | return false | ||
1152 | 256 | } | ||
1153 | 257 | if len(pair) < 1 { | ||
1154 | 258 | return false | ||
1155 | 259 | } | ||
1156 | 260 | buildNumber, ok := pair[0].(float64) | ||
1157 | 261 | if !ok { | ||
1158 | 262 | return false | ||
1159 | 263 | } | ||
1160 | 264 | curBuildNumber := float64(client.systemImageInfo.BuildNumber) | ||
1161 | 265 | if buildNumber > curBuildNumber { | ||
1162 | 266 | return true | ||
1163 | 267 | } | ||
1164 | 268 | // xxx we should really compare channel_target and alias here | ||
1165 | 269 | // going backward by a margin, assume switch of target | ||
1166 | 270 | if buildNumber < curBuildNumber && (curBuildNumber-buildNumber) > 10 { | ||
1167 | 271 | return true | ||
1168 | 272 | } | ||
1169 | 273 | return false | ||
1170 | 274 | } | ||
1171 | 275 | |||
1172 | 188 | // handleNotification deals with receiving a notification | 276 | // handleNotification deals with receiving a notification |
1173 | 189 | func (client *PushClient) handleNotification(msg *session.Notification) error { | 277 | func (client *PushClient) handleNotification(msg *session.Notification) error { |
1174 | 278 | if !client.filterNotification(msg) { | ||
1175 | 279 | return nil | ||
1176 | 280 | } | ||
1177 | 190 | action_id := "dummy_id" | 281 | action_id := "dummy_id" |
1178 | 191 | a := []string{action_id, "Go get it!"} // action value not visible on the phone | 282 | a := []string{action_id, "Go get it!"} // action value not visible on the phone |
1179 | 192 | h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} | 283 | h := map[string]*dbus.Variant{"x-canonical-switch-to-application": &dbus.Variant{true}} |
1180 | @@ -260,7 +351,7 @@ | |||
1181 | 260 | return client.doStart( | 351 | return client.doStart( |
1182 | 261 | client.configure, | 352 | client.configure, |
1183 | 262 | client.getDeviceId, | 353 | client.getDeviceId, |
1184 | 354 | client.takeTheBus, | ||
1185 | 263 | client.initSession, | 355 | client.initSession, |
1186 | 264 | client.takeTheBus, | ||
1187 | 265 | ) | 356 | ) |
1188 | 266 | } | 357 | } |
1189 | 267 | 358 | ||
1190 | === modified file 'client/client_test.go' | |||
1191 | --- client/client_test.go 2014-03-26 16:26:36 +0000 | |||
1192 | +++ client/client_test.go 2014-04-04 13:58:43 +0000 | |||
1193 | @@ -17,13 +17,23 @@ | |||
1194 | 17 | package client | 17 | package client |
1195 | 18 | 18 | ||
1196 | 19 | import ( | 19 | import ( |
1197 | 20 | "encoding/json" | ||
1198 | 20 | "errors" | 21 | "errors" |
1199 | 21 | "fmt" | 22 | "fmt" |
1200 | 22 | "io/ioutil" | 23 | "io/ioutil" |
1201 | 24 | "net/http" | ||
1202 | 25 | "net/http/httptest" | ||
1203 | 26 | "path/filepath" | ||
1204 | 27 | "reflect" | ||
1205 | 28 | "testing" | ||
1206 | 29 | "time" | ||
1207 | 30 | |||
1208 | 23 | . "launchpad.net/gocheck" | 31 | . "launchpad.net/gocheck" |
1209 | 32 | |||
1210 | 24 | "launchpad.net/ubuntu-push/bus" | 33 | "launchpad.net/ubuntu-push/bus" |
1211 | 25 | "launchpad.net/ubuntu-push/bus/networkmanager" | 34 | "launchpad.net/ubuntu-push/bus/networkmanager" |
1212 | 26 | "launchpad.net/ubuntu-push/bus/notifications" | 35 | "launchpad.net/ubuntu-push/bus/notifications" |
1213 | 36 | "launchpad.net/ubuntu-push/bus/systemimage" | ||
1214 | 27 | testibus "launchpad.net/ubuntu-push/bus/testing" | 37 | testibus "launchpad.net/ubuntu-push/bus/testing" |
1215 | 28 | "launchpad.net/ubuntu-push/client/session" | 38 | "launchpad.net/ubuntu-push/client/session" |
1216 | 29 | "launchpad.net/ubuntu-push/client/session/levelmap" | 39 | "launchpad.net/ubuntu-push/client/session/levelmap" |
1217 | @@ -32,11 +42,6 @@ | |||
1218 | 32 | "launchpad.net/ubuntu-push/util" | 42 | "launchpad.net/ubuntu-push/util" |
1219 | 33 | "launchpad.net/ubuntu-push/whoopsie/identifier" | 43 | "launchpad.net/ubuntu-push/whoopsie/identifier" |
1220 | 34 | idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing" | 44 | idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing" |
1221 | 35 | "net/http" | ||
1222 | 36 | "net/http/httptest" | ||
1223 | 37 | "path/filepath" | ||
1224 | 38 | "testing" | ||
1225 | 39 | "time" | ||
1226 | 40 | ) | 45 | ) |
1227 | 41 | 46 | ||
1228 | 42 | func TestClient(t *testing.T) { TestingT(t) } | 47 | func TestClient(t *testing.T) { TestingT(t) } |
1229 | @@ -83,22 +88,37 @@ | |||
1230 | 83 | cs.timeouts = nil | 88 | cs.timeouts = nil |
1231 | 84 | } | 89 | } |
1232 | 85 | 90 | ||
1233 | 91 | func (cs *clientSuite) writeTestConfig(overrides map[string]interface{}) { | ||
1234 | 92 | pem_file := helpers.SourceRelative("../server/acceptance/ssl/testing.cert") | ||
1235 | 93 | cfgMap := map[string]interface{}{ | ||
1236 | 94 | "connect_timeout": "7ms", | ||
1237 | 95 | "exchange_timeout": "10ms", | ||
1238 | 96 | "hosts_cache_expiry": "1h", | ||
1239 | 97 | "expect_all_repaired": "30m", | ||
1240 | 98 | "stabilizing_timeout": "0ms", | ||
1241 | 99 | "connectivity_check_url": "", | ||
1242 | 100 | "connectivity_check_md5": "", | ||
1243 | 101 | "addr": ":0", | ||
1244 | 102 | "cert_pem_file": pem_file, | ||
1245 | 103 | "recheck_timeout": "3h", | ||
1246 | 104 | "log_level": "debug", | ||
1247 | 105 | } | ||
1248 | 106 | for k, v := range overrides { | ||
1249 | 107 | cfgMap[k] = v | ||
1250 | 108 | } | ||
1251 | 109 | cfgBlob, err := json.Marshal(cfgMap) | ||
1252 | 110 | if err != nil { | ||
1253 | 111 | panic(err) | ||
1254 | 112 | } | ||
1255 | 113 | ioutil.WriteFile(cs.configPath, cfgBlob, 0600) | ||
1256 | 114 | } | ||
1257 | 115 | |||
1258 | 86 | func (cs *clientSuite) SetUpTest(c *C) { | 116 | func (cs *clientSuite) SetUpTest(c *C) { |
1259 | 87 | cs.log = helpers.NewTestLogger(c, "debug") | 117 | cs.log = helpers.NewTestLogger(c, "debug") |
1260 | 88 | dir := c.MkDir() | 118 | dir := c.MkDir() |
1261 | 89 | cs.configPath = filepath.Join(dir, "config") | 119 | cs.configPath = filepath.Join(dir, "config") |
1274 | 90 | cfg := fmt.Sprintf(` | 120 | |
1275 | 91 | { | 121 | cs.writeTestConfig(nil) |
1264 | 92 | "exchange_timeout": "10ms", | ||
1265 | 93 | "stabilizing_timeout": "0ms", | ||
1266 | 94 | "connectivity_check_url": "", | ||
1267 | 95 | "connectivity_check_md5": "", | ||
1268 | 96 | "addr": ":0", | ||
1269 | 97 | "cert_pem_file": %#v, | ||
1270 | 98 | "recheck_timeout": "3h", | ||
1271 | 99 | "log_level": "debug" | ||
1272 | 100 | }`, helpers.SourceRelative("../server/acceptance/config/testing.cert")) | ||
1273 | 101 | ioutil.WriteFile(cs.configPath, []byte(cfg), 0600) | ||
1276 | 102 | } | 122 | } |
1277 | 103 | 123 | ||
1278 | 104 | type sqlientSuite struct{ clientSuite } | 124 | type sqlientSuite struct{ clientSuite } |
1279 | @@ -119,7 +139,7 @@ | |||
1280 | 119 | err := cli.configure() | 139 | err := cli.configure() |
1281 | 120 | c.Assert(err, IsNil) | 140 | c.Assert(err, IsNil) |
1282 | 121 | c.Assert(cli.config, NotNil) | 141 | c.Assert(cli.config, NotNil) |
1284 | 122 | c.Check(cli.config.ExchangeTimeout.Duration, Equals, time.Duration(10*time.Millisecond)) | 142 | c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond)) |
1285 | 123 | } | 143 | } |
1286 | 124 | 144 | ||
1287 | 125 | func (cs *clientSuite) TestConfigureSetsUpLog(c *C) { | 145 | func (cs *clientSuite) TestConfigureSetsUpLog(c *C) { |
1288 | @@ -179,41 +199,74 @@ | |||
1289 | 179 | } | 199 | } |
1290 | 180 | 200 | ||
1291 | 181 | func (cs *clientSuite) TestConfigureBailsOnBadPEMFilename(c *C) { | 201 | func (cs *clientSuite) TestConfigureBailsOnBadPEMFilename(c *C) { |
1304 | 182 | ioutil.WriteFile(cs.configPath, []byte(` | 202 | cs.writeTestConfig(map[string]interface{}{ |
1305 | 183 | { | 203 | "cert_pem_file": "/a/b/c", |
1306 | 184 | "exchange_timeout": "10ms", | 204 | }) |
1295 | 185 | "stabilizing_timeout": "0ms", | ||
1296 | 186 | "connectivity_check_url": "", | ||
1297 | 187 | "connectivity_check_md5": "", | ||
1298 | 188 | "addr": ":0", | ||
1299 | 189 | "cert_pem_file": "/a/b/c", | ||
1300 | 190 | "log_level": "debug", | ||
1301 | 191 | "recheck_timeout": "3h" | ||
1302 | 192 | }`), 0600) | ||
1303 | 193 | |||
1307 | 194 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 205 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1308 | 195 | err := cli.configure() | 206 | err := cli.configure() |
1309 | 196 | c.Assert(err, ErrorMatches, "reading PEM file: .*") | 207 | c.Assert(err, ErrorMatches, "reading PEM file: .*") |
1310 | 197 | } | 208 | } |
1311 | 198 | 209 | ||
1312 | 199 | func (cs *clientSuite) TestConfigureBailsOnBadPEM(c *C) { | 210 | func (cs *clientSuite) TestConfigureBailsOnBadPEM(c *C) { |
1325 | 200 | ioutil.WriteFile(cs.configPath, []byte(` | 211 | cs.writeTestConfig(map[string]interface{}{ |
1326 | 201 | { | 212 | "cert_pem_file": "/etc/passwd", |
1327 | 202 | "exchange_timeout": "10ms", | 213 | }) |
1316 | 203 | "stabilizing_timeout": "0ms", | ||
1317 | 204 | "connectivity_check_url": "", | ||
1318 | 205 | "connectivity_check_md5": "", | ||
1319 | 206 | "addr": ":0", | ||
1320 | 207 | "cert_pem_file": "/etc/passwd", | ||
1321 | 208 | "log_level": "debug", | ||
1322 | 209 | "recheck_timeout": "3h" | ||
1323 | 210 | }`), 0600) | ||
1324 | 211 | |||
1328 | 212 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 214 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1329 | 213 | err := cli.configure() | 215 | err := cli.configure() |
1330 | 214 | c.Assert(err, ErrorMatches, "no PEM found.*") | 216 | c.Assert(err, ErrorMatches, "no PEM found.*") |
1331 | 215 | } | 217 | } |
1332 | 216 | 218 | ||
1333 | 219 | func (cs *clientSuite) TestConfigureBailsOnNoHosts(c *C) { | ||
1334 | 220 | cs.writeTestConfig(map[string]interface{}{ | ||
1335 | 221 | "addr": " ", | ||
1336 | 222 | }) | ||
1337 | 223 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1338 | 224 | err := cli.configure() | ||
1339 | 225 | c.Assert(err, ErrorMatches, "no hosts specified") | ||
1340 | 226 | } | ||
1341 | 227 | |||
1342 | 228 | func (cs *clientSuite) TestConfigureRemovesBlanksInAddr(c *C) { | ||
1343 | 229 | cs.writeTestConfig(map[string]interface{}{ | ||
1344 | 230 | "addr": " foo: 443", | ||
1345 | 231 | }) | ||
1346 | 232 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1347 | 233 | err := cli.configure() | ||
1348 | 234 | c.Assert(err, IsNil) | ||
1349 | 235 | c.Check(cli.config.Addr, Equals, "foo:443") | ||
1350 | 236 | } | ||
1351 | 237 | |||
1352 | 238 | /***************************************************************** | ||
1353 | 239 | deriveSessionConfig tests | ||
1354 | 240 | ******************************************************************/ | ||
1355 | 241 | |||
1356 | 242 | func (cs *clientSuite) TestDeriveSessionConfig(c *C) { | ||
1357 | 243 | info := map[string]interface{}{ | ||
1358 | 244 | "foo": 1, | ||
1359 | 245 | } | ||
1360 | 246 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1361 | 247 | err := cli.configure() | ||
1362 | 248 | c.Assert(err, IsNil) | ||
1363 | 249 | expected := session.ClientSessionConfig{ | ||
1364 | 250 | ConnectTimeout: 7 * time.Millisecond, | ||
1365 | 251 | ExchangeTimeout: 10 * time.Millisecond, | ||
1366 | 252 | HostsCachingExpiryTime: 1 * time.Hour, | ||
1367 | 253 | ExpectAllRepairedTime: 30 * time.Minute, | ||
1368 | 254 | PEM: cli.pem, | ||
1369 | 255 | Info: info, | ||
1370 | 256 | } | ||
1371 | 257 | // sanity check that we are looking at all fields | ||
1372 | 258 | vExpected := reflect.ValueOf(expected) | ||
1373 | 259 | nf := vExpected.NumField() | ||
1374 | 260 | for i := 0; i < nf; i++ { | ||
1375 | 261 | fv := vExpected.Field(i) | ||
1376 | 262 | // field isn't empty/zero | ||
1377 | 263 | c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name)) | ||
1378 | 264 | } | ||
1379 | 265 | // finally compare | ||
1380 | 266 | conf := cli.deriveSessionConfig(info) | ||
1381 | 267 | c.Check(conf, DeepEquals, expected) | ||
1382 | 268 | } | ||
1383 | 269 | |||
1384 | 217 | /***************************************************************** | 270 | /***************************************************************** |
1385 | 218 | getDeviceId tests | 271 | getDeviceId tests |
1386 | 219 | ******************************************************************/ | 272 | ******************************************************************/ |
1387 | @@ -254,6 +307,8 @@ | |||
1388 | 254 | cEndp := testibus.NewTestingEndpoint(cCond, condition.Work(true), | 307 | cEndp := testibus.NewTestingEndpoint(cCond, condition.Work(true), |
1389 | 255 | uint32(networkmanager.ConnectedGlobal), | 308 | uint32(networkmanager.ConnectedGlobal), |
1390 | 256 | ) | 309 | ) |
1391 | 310 | siCond := condition.Fail2Work(2) | ||
1392 | 311 | siEndp := testibus.NewMultiValuedTestingEndpoint(siCond, condition.Work(true), []interface{}{int32(101), "mako", "daily", "Unknown", map[string]string{}}) | ||
1393 | 257 | testibus.SetWatchTicker(cEndp, make(chan bool)) | 312 | testibus.SetWatchTicker(cEndp, make(chan bool)) |
1394 | 258 | // ok, create the thing | 313 | // ok, create the thing |
1395 | 259 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 314 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1396 | @@ -269,6 +324,7 @@ | |||
1397 | 269 | cli.notificationsEndp = nEndp | 324 | cli.notificationsEndp = nEndp |
1398 | 270 | cli.urlDispatcherEndp = uEndp | 325 | cli.urlDispatcherEndp = uEndp |
1399 | 271 | cli.connectivityEndp = cEndp | 326 | cli.connectivityEndp = cEndp |
1400 | 327 | cli.systemImageEndp = siEndp | ||
1401 | 272 | 328 | ||
1402 | 273 | c.Assert(cli.takeTheBus(), IsNil) | 329 | c.Assert(cli.takeTheBus(), IsNil) |
1403 | 274 | // the notifications and urldispatcher endpoints retried until connected | 330 | // the notifications and urldispatcher endpoints retried until connected |
1404 | @@ -280,6 +336,8 @@ | |||
1405 | 280 | c.Check(takeNextBool(cli.connCh), Equals, true) | 336 | c.Check(takeNextBool(cli.connCh), Equals, true) |
1406 | 281 | // the connectivity endpoint retried until connected | 337 | // the connectivity endpoint retried until connected |
1407 | 282 | c.Check(cCond.OK(), Equals, true) | 338 | c.Check(cCond.OK(), Equals, true) |
1408 | 339 | // the systemimage endpoint retried until connected | ||
1409 | 340 | c.Check(siCond.OK(), Equals, true) | ||
1410 | 283 | } | 341 | } |
1411 | 284 | 342 | ||
1412 | 285 | // takeTheBus can, in fact, fail | 343 | // takeTheBus can, in fact, fail |
1413 | @@ -295,6 +353,7 @@ | |||
1414 | 295 | cli.notificationsEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) | 353 | cli.notificationsEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) |
1415 | 296 | cli.urlDispatcherEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) | 354 | cli.urlDispatcherEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) |
1416 | 297 | cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) | 355 | cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) |
1417 | 356 | cli.systemImageEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) | ||
1418 | 298 | 357 | ||
1419 | 299 | c.Check(cli.takeTheBus(), NotNil) | 358 | c.Check(cli.takeTheBus(), NotNil) |
1420 | 300 | c.Check(cli.actionsCh, IsNil) | 359 | c.Check(cli.actionsCh, IsNil) |
1421 | @@ -307,7 +366,9 @@ | |||
1422 | 307 | func (cs *clientSuite) TestHandleErr(c *C) { | 366 | func (cs *clientSuite) TestHandleErr(c *C) { |
1423 | 308 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 367 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1424 | 309 | cli.log = cs.log | 368 | cli.log = cs.log |
1425 | 369 | cli.systemImageInfo = siInfoRes | ||
1426 | 310 | c.Assert(cli.initSession(), IsNil) | 370 | c.Assert(cli.initSession(), IsNil) |
1427 | 371 | cs.log.ResetCapture() | ||
1428 | 311 | cli.hasConnectivity = true | 372 | cli.hasConnectivity = true |
1429 | 312 | cli.handleErr(errors.New("bananas")) | 373 | cli.handleErr(errors.New("bananas")) |
1430 | 313 | c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n") | 374 | c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n") |
1431 | @@ -338,6 +399,7 @@ | |||
1432 | 338 | func (cs *clientSuite) TestHandleConnStateD2C(c *C) { | 399 | func (cs *clientSuite) TestHandleConnStateD2C(c *C) { |
1433 | 339 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 400 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1434 | 340 | cli.log = cs.log | 401 | cli.log = cs.log |
1435 | 402 | cli.systemImageInfo = siInfoRes | ||
1436 | 341 | c.Assert(cli.initSession(), IsNil) | 403 | c.Assert(cli.initSession(), IsNil) |
1437 | 342 | 404 | ||
1438 | 343 | c.Assert(cli.hasConnectivity, Equals, false) | 405 | c.Assert(cli.hasConnectivity, Equals, false) |
1439 | @@ -363,7 +425,7 @@ | |||
1440 | 363 | func (cs *clientSuite) TestHandleConnStateC2D(c *C) { | 425 | func (cs *clientSuite) TestHandleConnStateC2D(c *C) { |
1441 | 364 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 426 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1442 | 365 | cli.log = cs.log | 427 | cli.log = cs.log |
1444 | 366 | cli.session, _ = session.NewSession(string(cli.config.Addr), cli.pem, cli.config.ExchangeTimeout.Duration, cli.deviceId, levelmap.NewLevelMap, cs.log) | 428 | cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(nil), cli.deviceId, levelmap.NewLevelMap, cs.log) |
1445 | 367 | cli.session.Dial() | 429 | cli.session.Dial() |
1446 | 368 | cli.hasConnectivity = true | 430 | cli.hasConnectivity = true |
1447 | 369 | 431 | ||
1448 | @@ -376,7 +438,7 @@ | |||
1449 | 376 | func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) { | 438 | func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) { |
1450 | 377 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 439 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1451 | 378 | cli.log = cs.log | 440 | cli.log = cs.log |
1453 | 379 | cli.session, _ = session.NewSession(string(cli.config.Addr), cli.pem, cli.config.ExchangeTimeout.Duration, cli.deviceId, levelmap.NewLevelMap, cs.log) | 441 | cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(nil), cli.deviceId, levelmap.NewLevelMap, cs.log) |
1454 | 380 | cli.hasConnectivity = true | 442 | cli.hasConnectivity = true |
1455 | 381 | 443 | ||
1456 | 382 | cli.handleConnState(false) | 444 | cli.handleConnState(false) |
1457 | @@ -384,15 +446,110 @@ | |||
1458 | 384 | } | 446 | } |
1459 | 385 | 447 | ||
1460 | 386 | /***************************************************************** | 448 | /***************************************************************** |
1461 | 449 | filterNotification tests | ||
1462 | 450 | ******************************************************************/ | ||
1463 | 451 | |||
1464 | 452 | var siInfoRes = &systemimage.InfoResult{ | ||
1465 | 453 | Device: "mako", | ||
1466 | 454 | Channel: "daily", | ||
1467 | 455 | BuildNumber: 102, | ||
1468 | 456 | LastUpdate: "Unknown", | ||
1469 | 457 | } | ||
1470 | 458 | |||
1471 | 459 | func (cs *clientSuite) TestFilterNotification(c *C) { | ||
1472 | 460 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1473 | 461 | cli.systemImageInfo = siInfoRes | ||
1474 | 462 | // empty | ||
1475 | 463 | msg := &session.Notification{} | ||
1476 | 464 | c.Check(cli.filterNotification(msg), Equals, false) | ||
1477 | 465 | // same build number | ||
1478 | 466 | msg = &session.Notification{ | ||
1479 | 467 | Decoded: []map[string]interface{}{ | ||
1480 | 468 | map[string]interface{}{ | ||
1481 | 469 | "daily/mako": []interface{}{float64(102), "tubular"}, | ||
1482 | 470 | }, | ||
1483 | 471 | }, | ||
1484 | 472 | } | ||
1485 | 473 | c.Check(cli.filterNotification(msg), Equals, false) | ||
1486 | 474 | // higher build number and pick last | ||
1487 | 475 | msg = &session.Notification{ | ||
1488 | 476 | Decoded: []map[string]interface{}{ | ||
1489 | 477 | map[string]interface{}{ | ||
1490 | 478 | "daily/mako": []interface{}{float64(102), "tubular"}, | ||
1491 | 479 | }, | ||
1492 | 480 | map[string]interface{}{ | ||
1493 | 481 | "daily/mako": []interface{}{float64(103), "tubular"}, | ||
1494 | 482 | }, | ||
1495 | 483 | }, | ||
1496 | 484 | } | ||
1497 | 485 | c.Check(cli.filterNotification(msg), Equals, true) | ||
1498 | 486 | // going backward by a margin, assume switch of alias | ||
1499 | 487 | msg = &session.Notification{ | ||
1500 | 488 | Decoded: []map[string]interface{}{ | ||
1501 | 489 | map[string]interface{}{ | ||
1502 | 490 | "daily/mako": []interface{}{float64(102), "tubular"}, | ||
1503 | 491 | }, | ||
1504 | 492 | map[string]interface{}{ | ||
1505 | 493 | "daily/mako": []interface{}{float64(2), "urban"}, | ||
1506 | 494 | }, | ||
1507 | 495 | }, | ||
1508 | 496 | } | ||
1509 | 497 | c.Check(cli.filterNotification(msg), Equals, true) | ||
1510 | 498 | } | ||
1511 | 499 | |||
1512 | 500 | func (cs *clientSuite) TestFilterNotificationRobust(c *C) { | ||
1513 | 501 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1514 | 502 | cli.systemImageInfo = siInfoRes | ||
1515 | 503 | msg := &session.Notification{ | ||
1516 | 504 | Decoded: []map[string]interface{}{ | ||
1517 | 505 | map[string]interface{}{}, | ||
1518 | 506 | }, | ||
1519 | 507 | } | ||
1520 | 508 | c.Check(cli.filterNotification(msg), Equals, false) | ||
1521 | 509 | for _, broken := range []interface{}{ | ||
1522 | 510 | 5, | ||
1523 | 511 | []interface{}{}, | ||
1524 | 512 | []interface{}{55}, | ||
1525 | 513 | } { | ||
1526 | 514 | msg := &session.Notification{ | ||
1527 | 515 | Decoded: []map[string]interface{}{ | ||
1528 | 516 | map[string]interface{}{ | ||
1529 | 517 | "daily/mako": broken, | ||
1530 | 518 | }, | ||
1531 | 519 | }, | ||
1532 | 520 | } | ||
1533 | 521 | c.Check(cli.filterNotification(msg), Equals, false) | ||
1534 | 522 | } | ||
1535 | 523 | } | ||
1536 | 524 | |||
1537 | 525 | /***************************************************************** | ||
1538 | 387 | handleNotification tests | 526 | handleNotification tests |
1539 | 388 | ******************************************************************/ | 527 | ******************************************************************/ |
1540 | 389 | 528 | ||
1541 | 529 | var ( | ||
1542 | 530 | positiveNotification = &session.Notification{ | ||
1543 | 531 | Decoded: []map[string]interface{}{ | ||
1544 | 532 | map[string]interface{}{ | ||
1545 | 533 | "daily/mako": []interface{}{float64(103), "tubular"}, | ||
1546 | 534 | }, | ||
1547 | 535 | }, | ||
1548 | 536 | } | ||
1549 | 537 | negativeNotification = &session.Notification{ | ||
1550 | 538 | Decoded: []map[string]interface{}{ | ||
1551 | 539 | map[string]interface{}{ | ||
1552 | 540 | "daily/mako": []interface{}{float64(102), "tubular"}, | ||
1553 | 541 | }, | ||
1554 | 542 | }, | ||
1555 | 543 | } | ||
1556 | 544 | ) | ||
1557 | 545 | |||
1558 | 390 | func (cs *clientSuite) TestHandleNotification(c *C) { | 546 | func (cs *clientSuite) TestHandleNotification(c *C) { |
1559 | 391 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 547 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1560 | 548 | cli.systemImageInfo = siInfoRes | ||
1561 | 392 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) | 549 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
1562 | 393 | cli.notificationsEndp = endp | 550 | cli.notificationsEndp = endp |
1563 | 394 | cli.log = cs.log | 551 | cli.log = cs.log |
1565 | 395 | c.Check(cli.handleNotification(nil), IsNil) | 552 | c.Check(cli.handleNotification(positiveNotification), IsNil) |
1566 | 396 | // check we sent the notification | 553 | // check we sent the notification |
1567 | 397 | args := testibus.GetCallArgs(endp) | 554 | args := testibus.GetCallArgs(endp) |
1568 | 398 | c.Assert(args, HasLen, 1) | 555 | c.Assert(args, HasLen, 1) |
1569 | @@ -400,12 +557,26 @@ | |||
1570 | 400 | c.Check(cs.log.Captured(), Matches, `.* got notification id \d+\s*`) | 557 | c.Check(cs.log.Captured(), Matches, `.* got notification id \d+\s*`) |
1571 | 401 | } | 558 | } |
1572 | 402 | 559 | ||
1573 | 560 | func (cs *clientSuite) TestHandleNotificationNothingToDo(c *C) { | ||
1574 | 561 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
1575 | 562 | cli.systemImageInfo = siInfoRes | ||
1576 | 563 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) | ||
1577 | 564 | cli.notificationsEndp = endp | ||
1578 | 565 | cli.log = cs.log | ||
1579 | 566 | c.Check(cli.handleNotification(negativeNotification), IsNil) | ||
1580 | 567 | // check we sent the notification | ||
1581 | 568 | args := testibus.GetCallArgs(endp) | ||
1582 | 569 | c.Assert(args, HasLen, 0) | ||
1583 | 570 | c.Check(cs.log.Captured(), Matches, "") | ||
1584 | 571 | } | ||
1585 | 572 | |||
1586 | 403 | func (cs *clientSuite) TestHandleNotificationFail(c *C) { | 573 | func (cs *clientSuite) TestHandleNotificationFail(c *C) { |
1587 | 404 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 574 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1588 | 575 | cli.systemImageInfo = siInfoRes | ||
1589 | 405 | cli.log = cs.log | 576 | cli.log = cs.log |
1590 | 406 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) | 577 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
1591 | 407 | cli.notificationsEndp = endp | 578 | cli.notificationsEndp = endp |
1593 | 408 | c.Check(cli.handleNotification(nil), NotNil) | 579 | c.Check(cli.handleNotification(positiveNotification), NotNil) |
1594 | 409 | } | 580 | } |
1595 | 410 | 581 | ||
1596 | 411 | /***************************************************************** | 582 | /***************************************************************** |
1597 | @@ -415,7 +586,7 @@ | |||
1598 | 415 | func (cs *clientSuite) TestHandleClick(c *C) { | 586 | func (cs *clientSuite) TestHandleClick(c *C) { |
1599 | 416 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 587 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1600 | 417 | cli.log = cs.log | 588 | cli.log = cs.log |
1602 | 418 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), nil) | 589 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1603 | 419 | cli.urlDispatcherEndp = endp | 590 | cli.urlDispatcherEndp = endp |
1604 | 420 | c.Check(cli.handleClick(), IsNil) | 591 | c.Check(cli.handleClick(), IsNil) |
1605 | 421 | // check we sent the notification | 592 | // check we sent the notification |
1606 | @@ -432,6 +603,7 @@ | |||
1607 | 432 | func (cs *clientSuite) TestDoLoopConn(c *C) { | 603 | func (cs *clientSuite) TestDoLoopConn(c *C) { |
1608 | 433 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 604 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1609 | 434 | cli.log = cs.log | 605 | cli.log = cs.log |
1610 | 606 | cli.systemImageInfo = siInfoRes | ||
1611 | 435 | cli.connCh = make(chan bool, 1) | 607 | cli.connCh = make(chan bool, 1) |
1612 | 436 | cli.connCh <- true | 608 | cli.connCh <- true |
1613 | 437 | c.Assert(cli.initSession(), IsNil) | 609 | c.Assert(cli.initSession(), IsNil) |
1614 | @@ -444,6 +616,7 @@ | |||
1615 | 444 | func (cs *clientSuite) TestDoLoopClick(c *C) { | 616 | func (cs *clientSuite) TestDoLoopClick(c *C) { |
1616 | 445 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 617 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1617 | 446 | cli.log = cs.log | 618 | cli.log = cs.log |
1618 | 619 | cli.systemImageInfo = siInfoRes | ||
1619 | 447 | c.Assert(cli.initSession(), IsNil) | 620 | c.Assert(cli.initSession(), IsNil) |
1620 | 448 | aCh := make(chan notifications.RawActionReply, 1) | 621 | aCh := make(chan notifications.RawActionReply, 1) |
1621 | 449 | aCh <- notifications.RawActionReply{} | 622 | aCh <- notifications.RawActionReply{} |
1622 | @@ -457,6 +630,7 @@ | |||
1623 | 457 | func (cs *clientSuite) TestDoLoopNotif(c *C) { | 630 | func (cs *clientSuite) TestDoLoopNotif(c *C) { |
1624 | 458 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 631 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1625 | 459 | cli.log = cs.log | 632 | cli.log = cs.log |
1626 | 633 | cli.systemImageInfo = siInfoRes | ||
1627 | 460 | c.Assert(cli.initSession(), IsNil) | 634 | c.Assert(cli.initSession(), IsNil) |
1628 | 461 | cli.session.MsgCh = make(chan *session.Notification, 1) | 635 | cli.session.MsgCh = make(chan *session.Notification, 1) |
1629 | 462 | cli.session.MsgCh <- &session.Notification{} | 636 | cli.session.MsgCh <- &session.Notification{} |
1630 | @@ -469,6 +643,7 @@ | |||
1631 | 469 | func (cs *clientSuite) TestDoLoopErr(c *C) { | 643 | func (cs *clientSuite) TestDoLoopErr(c *C) { |
1632 | 470 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 644 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
1633 | 471 | cli.log = cs.log | 645 | cli.log = cs.log |
1634 | 646 | cli.systemImageInfo = siInfoRes | ||
1635 | 472 | c.Assert(cli.initSession(), IsNil) | 647 | c.Assert(cli.initSession(), IsNil) |
1636 | 473 | cli.session.ErrCh = make(chan error, 1) | 648 | cli.session.ErrCh = make(chan error, 1) |
1637 | 474 | cli.session.ErrCh <- nil | 649 | cli.session.ErrCh <- nil |
1638 | @@ -521,7 +696,7 @@ | |||
1639 | 521 | cli.urlDispatcherEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) | 696 | cli.urlDispatcherEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false)) |
1640 | 522 | cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), | 697 | cli.connectivityEndp = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
1641 | 523 | uint32(networkmanager.ConnectedGlobal)) | 698 | uint32(networkmanager.ConnectedGlobal)) |
1643 | 524 | 699 | cli.systemImageInfo = siInfoRes | |
1644 | 525 | c.Assert(cli.initSession(), IsNil) | 700 | c.Assert(cli.initSession(), IsNil) |
1645 | 526 | 701 | ||
1646 | 527 | cli.session.MsgCh = make(chan *session.Notification) | 702 | cli.session.MsgCh = make(chan *session.Notification) |
1647 | @@ -558,7 +733,7 @@ | |||
1648 | 558 | c.Check(cli.hasConnectivity, Equals, false) | 733 | c.Check(cli.hasConnectivity, Equals, false) |
1649 | 559 | 734 | ||
1650 | 560 | // * session.MsgCh to the notifications handler | 735 | // * session.MsgCh to the notifications handler |
1652 | 561 | cli.session.MsgCh <- &session.Notification{} | 736 | cli.session.MsgCh <- positiveNotification |
1653 | 562 | tick() | 737 | tick() |
1654 | 563 | nargs := testibus.GetCallArgs(cli.notificationsEndp) | 738 | nargs := testibus.GetCallArgs(cli.notificationsEndp) |
1655 | 564 | c.Check(nargs, HasLen, 1) | 739 | c.Check(nargs, HasLen, 1) |
1656 | 565 | 740 | ||
1657 | === modified file 'client/gethosts/gethost_test.go' | |||
1658 | --- client/gethosts/gethost_test.go 2014-03-24 15:32:29 +0000 | |||
1659 | +++ client/gethosts/gethost_test.go 2014-04-04 13:58:43 +0000 | |||
1660 | @@ -61,18 +61,16 @@ | |||
1661 | 61 | } | 61 | } |
1662 | 62 | 62 | ||
1663 | 63 | func (s *getHostsSuite) TestGetTimeout(c *C) { | 63 | func (s *getHostsSuite) TestGetTimeout(c *C) { |
1665 | 64 | finish := make(chan bool, 1) | 64 | started := make(chan bool, 1) |
1666 | 65 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 65 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
1668 | 66 | <-finish | 66 | started <- true |
1669 | 67 | time.Sleep(700 * time.Millisecond) | ||
1670 | 67 | })) | 68 | })) |
1671 | 68 | defer func() { | 69 | defer func() { |
1673 | 69 | time.Sleep(100 * time.Millisecond) // work around -race issue | 70 | <-started |
1674 | 70 | ts.Close() | 71 | ts.Close() |
1675 | 71 | }() | 72 | }() |
1680 | 72 | defer func() { | 73 | gh := New("foobar", ts.URL, 500*time.Millisecond) |
1677 | 73 | finish <- true | ||
1678 | 74 | }() | ||
1679 | 75 | gh := New("foobar", ts.URL, 1*time.Second) | ||
1681 | 76 | _, err := gh.Get() | 74 | _, err := gh.Get() |
1682 | 77 | c.Check(err, ErrorMatches, ".*closed.*") | 75 | c.Check(err, ErrorMatches, ".*closed.*") |
1683 | 78 | } | 76 | } |
1684 | 79 | 77 | ||
1685 | === modified file 'client/session/session.go' | |||
1686 | --- client/session/session.go 2014-03-26 16:26:36 +0000 | |||
1687 | +++ client/session/session.go 2014-04-04 13:58:43 +0000 | |||
1688 | @@ -21,22 +21,28 @@ | |||
1689 | 21 | import ( | 21 | import ( |
1690 | 22 | "crypto/tls" | 22 | "crypto/tls" |
1691 | 23 | "crypto/x509" | 23 | "crypto/x509" |
1692 | 24 | "encoding/json" | ||
1693 | 24 | "errors" | 25 | "errors" |
1694 | 25 | "fmt" | 26 | "fmt" |
1695 | 27 | "math/rand" | ||
1696 | 28 | "net" | ||
1697 | 29 | "strings" | ||
1698 | 30 | "sync" | ||
1699 | 31 | "sync/atomic" | ||
1700 | 32 | "time" | ||
1701 | 33 | |||
1702 | 34 | "launchpad.net/ubuntu-push/client/gethosts" | ||
1703 | 26 | "launchpad.net/ubuntu-push/client/session/levelmap" | 35 | "launchpad.net/ubuntu-push/client/session/levelmap" |
1704 | 27 | "launchpad.net/ubuntu-push/logger" | 36 | "launchpad.net/ubuntu-push/logger" |
1705 | 28 | "launchpad.net/ubuntu-push/protocol" | 37 | "launchpad.net/ubuntu-push/protocol" |
1706 | 29 | "launchpad.net/ubuntu-push/util" | 38 | "launchpad.net/ubuntu-push/util" |
1707 | 30 | "math/rand" | ||
1708 | 31 | "net" | ||
1709 | 32 | "sync/atomic" | ||
1710 | 33 | "time" | ||
1711 | 34 | ) | 39 | ) |
1712 | 35 | 40 | ||
1713 | 36 | var wireVersionBytes = []byte{protocol.ProtocolWireVersion} | 41 | var wireVersionBytes = []byte{protocol.ProtocolWireVersion} |
1714 | 37 | 42 | ||
1715 | 38 | type Notification struct { | 43 | type Notification struct { |
1716 | 39 | TopLevel int64 | 44 | TopLevel int64 |
1717 | 45 | Decoded []map[string]interface{} | ||
1718 | 40 | } | 46 | } |
1719 | 41 | 47 | ||
1720 | 42 | type serverMsg struct { | 48 | type serverMsg struct { |
1721 | @@ -45,6 +51,15 @@ | |||
1722 | 45 | protocol.NotificationsMsg | 51 | protocol.NotificationsMsg |
1723 | 46 | } | 52 | } |
1724 | 47 | 53 | ||
1725 | 54 | // parseServerAddrSpec recognizes whether spec is a HTTP URL to get | ||
1726 | 55 | // hosts from or a |-separated list of host:port pairs. | ||
1727 | 56 | func parseServerAddrSpec(spec string) (hostsEndpoint string, fallbackHosts []string) { | ||
1728 | 57 | if strings.HasPrefix(spec, "http") { | ||
1729 | 58 | return spec, nil | ||
1730 | 59 | } | ||
1731 | 60 | return "", strings.Split(spec, "|") | ||
1732 | 61 | } | ||
1733 | 62 | |||
1734 | 48 | // ClientSessionState is a way to broadly track the progress of the session | 63 | // ClientSessionState is a way to broadly track the progress of the session |
1735 | 49 | type ClientSessionState uint32 | 64 | type ClientSessionState uint32 |
1736 | 50 | 65 | ||
1737 | @@ -56,15 +71,39 @@ | |||
1738 | 56 | Running | 71 | Running |
1739 | 57 | ) | 72 | ) |
1740 | 58 | 73 | ||
1742 | 59 | // ClienSession holds a client<->server session and its configuration. | 74 | type hostGetter interface { |
1743 | 75 | Get() ([]string, error) | ||
1744 | 76 | } | ||
1745 | 77 | |||
1746 | 78 | // ClientSessionConfig groups the client session configuration. | ||
1747 | 79 | type ClientSessionConfig struct { | ||
1748 | 80 | ConnectTimeout time.Duration | ||
1749 | 81 | ExchangeTimeout time.Duration | ||
1750 | 82 | HostsCachingExpiryTime time.Duration | ||
1751 | 83 | ExpectAllRepairedTime time.Duration | ||
1752 | 84 | PEM []byte | ||
1753 | 85 | Info map[string]interface{} | ||
1754 | 86 | } | ||
1755 | 87 | |||
1756 | 88 | // ClientSession holds a client<->server session and its configuration. | ||
1757 | 60 | type ClientSession struct { | 89 | type ClientSession struct { |
1758 | 61 | // configuration | 90 | // configuration |
1764 | 62 | DeviceId string | 91 | DeviceId string |
1765 | 63 | ServerAddr string | 92 | ClientSessionConfig |
1766 | 64 | ExchangeTimeout time.Duration | 93 | Levels levelmap.LevelMap |
1767 | 65 | Levels levelmap.LevelMap | 94 | Protocolator func(net.Conn) protocol.Protocol |
1768 | 66 | Protocolator func(net.Conn) protocol.Protocol | 95 | // hosts |
1769 | 96 | getHost hostGetter | ||
1770 | 97 | fallbackHosts []string | ||
1771 | 98 | deliveryHostsTimestamp time.Time | ||
1772 | 99 | deliveryHosts []string | ||
1773 | 100 | lastAttemptTimestamp time.Time | ||
1774 | 101 | leftToTry int | ||
1775 | 102 | tryHost int | ||
1776 | 103 | // hook for testing | ||
1777 | 104 | timeSince func(time.Time) time.Duration | ||
1778 | 67 | // connection | 105 | // connection |
1779 | 106 | connLock sync.RWMutex | ||
1780 | 68 | Connection net.Conn | 107 | Connection net.Conn |
1781 | 69 | Log logger.Logger | 108 | Log logger.Logger |
1782 | 70 | TLS *tls.Config | 109 | TLS *tls.Config |
1783 | @@ -77,7 +116,7 @@ | |||
1784 | 77 | MsgCh chan *Notification | 116 | MsgCh chan *Notification |
1785 | 78 | } | 117 | } |
1786 | 79 | 118 | ||
1788 | 80 | func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration, | 119 | func NewSession(serverAddrSpec string, conf ClientSessionConfig, |
1789 | 81 | deviceId string, levelmapFactory func() (levelmap.LevelMap, error), | 120 | deviceId string, levelmapFactory func() (levelmap.LevelMap, error), |
1790 | 82 | log logger.Logger) (*ClientSession, error) { | 121 | log logger.Logger) (*ClientSession, error) { |
1791 | 83 | state := uint32(Disconnected) | 122 | state := uint32(Disconnected) |
1792 | @@ -85,19 +124,27 @@ | |||
1793 | 85 | if err != nil { | 124 | if err != nil { |
1794 | 86 | return nil, err | 125 | return nil, err |
1795 | 87 | } | 126 | } |
1796 | 127 | var getHost hostGetter | ||
1797 | 128 | log.Infof("using addr: %v", serverAddrSpec) | ||
1798 | 129 | hostsEndpoint, fallbackHosts := parseServerAddrSpec(serverAddrSpec) | ||
1799 | 130 | if hostsEndpoint != "" { | ||
1800 | 131 | getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout) | ||
1801 | 132 | } | ||
1802 | 88 | sess := &ClientSession{ | 133 | sess := &ClientSession{ |
1811 | 89 | ExchangeTimeout: exchangeTimeout, | 134 | ClientSessionConfig: conf, |
1812 | 90 | ServerAddr: serverAddr, | 135 | getHost: getHost, |
1813 | 91 | DeviceId: deviceId, | 136 | fallbackHosts: fallbackHosts, |
1814 | 92 | Log: log, | 137 | DeviceId: deviceId, |
1815 | 93 | Protocolator: protocol.NewProtocol0, | 138 | Log: log, |
1816 | 94 | Levels: levels, | 139 | Protocolator: protocol.NewProtocol0, |
1817 | 95 | TLS: &tls.Config{InsecureSkipVerify: true}, // XXX | 140 | Levels: levels, |
1818 | 96 | stateP: &state, | 141 | TLS: &tls.Config{InsecureSkipVerify: true}, // XXX |
1819 | 142 | stateP: &state, | ||
1820 | 143 | timeSince: time.Since, | ||
1821 | 97 | } | 144 | } |
1823 | 98 | if pem != nil { | 145 | if sess.PEM != nil { |
1824 | 99 | cp := x509.NewCertPool() | 146 | cp := x509.NewCertPool() |
1826 | 100 | ok := cp.AppendCertsFromPEM(pem) | 147 | ok := cp.AppendCertsFromPEM(sess.PEM) |
1827 | 101 | if !ok { | 148 | if !ok { |
1828 | 102 | return nil, errors.New("could not parse certificate") | 149 | return nil, errors.New("could not parse certificate") |
1829 | 103 | } | 150 | } |
1830 | @@ -114,15 +161,90 @@ | |||
1831 | 114 | atomic.StoreUint32(sess.stateP, uint32(state)) | 161 | atomic.StoreUint32(sess.stateP, uint32(state)) |
1832 | 115 | } | 162 | } |
1833 | 116 | 163 | ||
1834 | 164 | func (sess *ClientSession) setConnection(conn net.Conn) { | ||
1835 | 165 | sess.connLock.Lock() | ||
1836 | 166 | defer sess.connLock.Unlock() | ||
1837 | 167 | sess.Connection = conn | ||
1838 | 168 | } | ||
1839 | 169 | |||
1840 | 170 | func (sess *ClientSession) getConnection() net.Conn { | ||
1841 | 171 | sess.connLock.RLock() | ||
1842 | 172 | defer sess.connLock.RUnlock() | ||
1843 | 173 | return sess.Connection | ||
1844 | 174 | } | ||
1845 | 175 | |||
1846 | 176 | // getHosts sets deliveryHosts possibly querying a remote endpoint | ||
1847 | 177 | func (sess *ClientSession) getHosts() error { | ||
1848 | 178 | if sess.getHost != nil { | ||
1849 | 179 | if sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime { | ||
1850 | 180 | return nil | ||
1851 | 181 | } | ||
1852 | 182 | hosts, err := sess.getHost.Get() | ||
1853 | 183 | if err != nil { | ||
1854 | 184 | sess.Log.Errorf("getHosts: %v", err) | ||
1855 | 185 | sess.setState(Error) | ||
1856 | 186 | return err | ||
1857 | 187 | } | ||
1858 | 188 | sess.deliveryHostsTimestamp = time.Now() | ||
1859 | 189 | sess.deliveryHosts = hosts | ||
1860 | 190 | } else { | ||
1861 | 191 | sess.deliveryHosts = sess.fallbackHosts | ||
1862 | 192 | } | ||
1863 | 193 | return nil | ||
1864 | 194 | } | ||
1865 | 195 | |||
1866 | 196 | // startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts | ||
1867 | 197 | |||
1868 | 198 | func (sess *ClientSession) startConnectionAttempt() { | ||
1869 | 199 | if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime { | ||
1870 | 200 | sess.tryHost = 0 | ||
1871 | 201 | } | ||
1872 | 202 | sess.leftToTry = len(sess.deliveryHosts) | ||
1873 | 203 | if sess.leftToTry == 0 { | ||
1874 | 204 | panic("should have got hosts from config or remote at this point") | ||
1875 | 205 | } | ||
1876 | 206 | sess.lastAttemptTimestamp = time.Now() | ||
1877 | 207 | } | ||
1878 | 208 | |||
1879 | 209 | func (sess *ClientSession) nextHostToTry() string { | ||
1880 | 210 | if sess.leftToTry == 0 { | ||
1881 | 211 | return "" | ||
1882 | 212 | } | ||
1883 | 213 | res := sess.deliveryHosts[sess.tryHost] | ||
1884 | 214 | sess.tryHost = (sess.tryHost + 1) % len(sess.deliveryHosts) | ||
1885 | 215 | sess.leftToTry-- | ||
1886 | 216 | return res | ||
1887 | 217 | } | ||
1888 | 218 | |||
1889 | 219 | // we reached the Started state, we can retry with the same host if we | ||
1890 | 220 | // have to retry again | ||
1891 | 221 | func (sess *ClientSession) started() { | ||
1892 | 222 | sess.tryHost-- | ||
1893 | 223 | if sess.tryHost == -1 { | ||
1894 | 224 | sess.tryHost = len(sess.deliveryHosts) - 1 | ||
1895 | 225 | } | ||
1896 | 226 | sess.setState(Started) | ||
1897 | 227 | } | ||
1898 | 228 | |||
1899 | 117 | // connect to a server using the configuration in the ClientSession | 229 | // connect to a server using the configuration in the ClientSession |
1900 | 118 | // and set up the connection. | 230 | // and set up the connection. |
1901 | 119 | func (sess *ClientSession) connect() error { | 231 | func (sess *ClientSession) connect() error { |
1906 | 120 | conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout) | 232 | sess.startConnectionAttempt() |
1907 | 121 | if err != nil { | 233 | var err error |
1908 | 122 | sess.setState(Error) | 234 | var conn net.Conn |
1909 | 123 | return fmt.Errorf("connect: %s", err) | 235 | for { |
1910 | 236 | host := sess.nextHostToTry() | ||
1911 | 237 | if host == "" { | ||
1912 | 238 | sess.setState(Error) | ||
1913 | 239 | return fmt.Errorf("connect: %s", err) | ||
1914 | 240 | } | ||
1915 | 241 | sess.Log.Debugf("trying to connect to: %v", host) | ||
1916 | 242 | conn, err = net.DialTimeout("tcp", host, sess.ConnectTimeout) | ||
1917 | 243 | if err == nil { | ||
1918 | 244 | break | ||
1919 | 245 | } | ||
1920 | 124 | } | 246 | } |
1922 | 125 | sess.Connection = tls.Client(conn, sess.TLS) | 247 | sess.setConnection(tls.Client(conn, sess.TLS)) |
1923 | 126 | sess.setState(Connected) | 248 | sess.setState(Connected) |
1924 | 127 | return nil | 249 | return nil |
1925 | 128 | } | 250 | } |
1926 | @@ -145,6 +267,8 @@ | |||
1927 | 145 | sess.doClose() | 267 | sess.doClose() |
1928 | 146 | } | 268 | } |
1929 | 147 | func (sess *ClientSession) doClose() { | 269 | func (sess *ClientSession) doClose() { |
1930 | 270 | sess.connLock.Lock() | ||
1931 | 271 | defer sess.connLock.Unlock() | ||
1932 | 148 | if sess.Connection != nil { | 272 | if sess.Connection != nil { |
1933 | 149 | sess.Connection.Close() | 273 | sess.Connection.Close() |
1934 | 150 | // we ignore Close errors, on purpose (the thinking being that | 274 | // we ignore Close errors, on purpose (the thinking being that |
1935 | @@ -167,6 +291,23 @@ | |||
1936 | 167 | return err | 291 | return err |
1937 | 168 | } | 292 | } |
1938 | 169 | 293 | ||
1939 | 294 | func (sess *ClientSession) decodeBroadcast(bcast *serverMsg) *Notification { | ||
1940 | 295 | decoded := make([]map[string]interface{}, 0) | ||
1941 | 296 | for _, p := range bcast.Payloads { | ||
1942 | 297 | var v map[string]interface{} | ||
1943 | 298 | err := json.Unmarshal(p, &v) | ||
1944 | 299 | if err != nil { | ||
1945 | 300 | sess.Log.Debugf("expected map in broadcast: %v", err) | ||
1946 | 301 | continue | ||
1947 | 302 | } | ||
1948 | 303 | decoded = append(decoded, v) | ||
1949 | 304 | } | ||
1950 | 305 | return &Notification{ | ||
1951 | 306 | TopLevel: bcast.TopLevel, | ||
1952 | 307 | Decoded: decoded, | ||
1953 | 308 | } | ||
1954 | 309 | } | ||
1955 | 310 | |||
1956 | 170 | // handle "broadcast" messages | 311 | // handle "broadcast" messages |
1957 | 171 | func (sess *ClientSession) handleBroadcast(bcast *serverMsg) error { | 312 | func (sess *ClientSession) handleBroadcast(bcast *serverMsg) error { |
1958 | 172 | err := sess.Levels.Set(bcast.ChanId, bcast.TopLevel) | 313 | err := sess.Levels.Set(bcast.ChanId, bcast.TopLevel) |
1959 | @@ -189,7 +330,7 @@ | |||
1960 | 189 | if bcast.ChanId == protocol.SystemChannelId { | 330 | if bcast.ChanId == protocol.SystemChannelId { |
1961 | 190 | // the system channel id, the only one we care about for now | 331 | // the system channel id, the only one we care about for now |
1962 | 191 | sess.Log.Debugf("sending it over") | 332 | sess.Log.Debugf("sending it over") |
1964 | 192 | sess.MsgCh <- &Notification{bcast.TopLevel} | 333 | sess.MsgCh <- sess.decodeBroadcast(bcast) |
1965 | 193 | sess.Log.Debugf("sent it over") | 334 | sess.Log.Debugf("sent it over") |
1966 | 194 | } else { | 335 | } else { |
1967 | 195 | sess.Log.Debugf("what is this weird channel, %#v?", bcast.ChanId) | 336 | sess.Log.Debugf("what is this weird channel, %#v?", bcast.ChanId) |
1968 | @@ -224,7 +365,7 @@ | |||
1969 | 224 | 365 | ||
1970 | 225 | // Call this when you've connected and want to start looping. | 366 | // Call this when you've connected and want to start looping. |
1971 | 226 | func (sess *ClientSession) start() error { | 367 | func (sess *ClientSession) start() error { |
1973 | 227 | conn := sess.Connection | 368 | conn := sess.getConnection() |
1974 | 228 | err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout)) | 369 | err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout)) |
1975 | 229 | if err != nil { | 370 | if err != nil { |
1976 | 230 | sess.setState(Error) | 371 | sess.setState(Error) |
1977 | @@ -253,6 +394,7 @@ | |||
1978 | 253 | // xxx get the SSO Authorization string from the phone | 394 | // xxx get the SSO Authorization string from the phone |
1979 | 254 | Authorization: "", | 395 | Authorization: "", |
1980 | 255 | Levels: levels, | 396 | Levels: levels, |
1981 | 397 | Info: sess.Info, | ||
1982 | 256 | }) | 398 | }) |
1983 | 257 | if err != nil { | 399 | if err != nil { |
1984 | 258 | sess.setState(Error) | 400 | sess.setState(Error) |
1985 | @@ -278,16 +420,20 @@ | |||
1986 | 278 | } | 420 | } |
1987 | 279 | sess.proto = proto | 421 | sess.proto = proto |
1988 | 280 | sess.pingInterval = pingInterval | 422 | sess.pingInterval = pingInterval |
1991 | 281 | sess.Log.Debugf("Connected %v.", conn.LocalAddr()) | 423 | sess.Log.Debugf("Connected %v.", conn.RemoteAddr()) |
1992 | 282 | sess.setState(Started) | 424 | sess.started() // deals with choosing which host to retry with as well |
1993 | 283 | return nil | 425 | return nil |
1994 | 284 | } | 426 | } |
1995 | 285 | 427 | ||
1996 | 286 | // run calls connect, and if it works it calls start, and if it works | 428 | // run calls connect, and if it works it calls start, and if it works |
1997 | 287 | // it runs loop in a goroutine, and ships its return value over ErrCh. | 429 | // it runs loop in a goroutine, and ships its return value over ErrCh. |
1999 | 288 | func (sess *ClientSession) run(closer func(), connecter, starter, looper func() error) error { | 430 | func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error { |
2000 | 289 | closer() | 431 | closer() |
2002 | 290 | err := connecter() | 432 | err := hostGetter() |
2003 | 433 | if err != nil { | ||
2004 | 434 | return err | ||
2005 | 435 | } | ||
2006 | 436 | err = connecter() | ||
2007 | 291 | if err == nil { | 437 | if err == nil { |
2008 | 292 | err = starter() | 438 | err = starter() |
2009 | 293 | if err == nil { | 439 | if err == nil { |
2010 | @@ -317,7 +463,7 @@ | |||
2011 | 317 | // keep on trying. | 463 | // keep on trying. |
2012 | 318 | panic("can't Dial() without a protocol constructor.") | 464 | panic("can't Dial() without a protocol constructor.") |
2013 | 319 | } | 465 | } |
2015 | 320 | return sess.run(sess.doClose, sess.connect, sess.start, sess.loop) | 466 | return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop) |
2016 | 321 | } | 467 | } |
2017 | 322 | 468 | ||
2018 | 323 | func init() { | 469 | func init() { |
2019 | 324 | 470 | ||
2020 | === modified file 'client/session/session_test.go' | |||
2021 | --- client/session/session_test.go 2014-03-27 13:26:10 +0000 | |||
2022 | +++ client/session/session_test.go 2014-04-04 13:58:43 +0000 | |||
2023 | @@ -23,16 +23,21 @@ | |||
2024 | 23 | "fmt" | 23 | "fmt" |
2025 | 24 | "io" | 24 | "io" |
2026 | 25 | "io/ioutil" | 25 | "io/ioutil" |
2027 | 26 | "net" | ||
2028 | 27 | "net/http" | ||
2029 | 28 | "net/http/httptest" | ||
2030 | 29 | "reflect" | ||
2031 | 30 | "testing" | ||
2032 | 31 | "time" | ||
2033 | 32 | |||
2034 | 26 | . "launchpad.net/gocheck" | 33 | . "launchpad.net/gocheck" |
2035 | 34 | |||
2036 | 27 | "launchpad.net/ubuntu-push/client/session/levelmap" | 35 | "launchpad.net/ubuntu-push/client/session/levelmap" |
2037 | 36 | //"launchpad.net/ubuntu-push/client/gethosts" | ||
2038 | 28 | "launchpad.net/ubuntu-push/logger" | 37 | "launchpad.net/ubuntu-push/logger" |
2039 | 29 | "launchpad.net/ubuntu-push/protocol" | 38 | "launchpad.net/ubuntu-push/protocol" |
2040 | 30 | helpers "launchpad.net/ubuntu-push/testing" | 39 | helpers "launchpad.net/ubuntu-push/testing" |
2041 | 31 | "launchpad.net/ubuntu-push/testing/condition" | 40 | "launchpad.net/ubuntu-push/testing/condition" |
2042 | 32 | "net" | ||
2043 | 33 | "reflect" | ||
2044 | 34 | "testing" | ||
2045 | 35 | "time" | ||
2046 | 36 | ) | 41 | ) |
2047 | 37 | 42 | ||
2048 | 38 | func TestSession(t *testing.T) { TestingT(t) } | 43 | func TestSession(t *testing.T) { TestingT(t) } |
2049 | @@ -181,23 +186,51 @@ | |||
2050 | 181 | } | 186 | } |
2051 | 182 | 187 | ||
2052 | 183 | /**************************************************************** | 188 | /**************************************************************** |
2053 | 189 | parseServerAddrSpec() tests | ||
2054 | 190 | ****************************************************************/ | ||
2055 | 191 | |||
2056 | 192 | func (cs *clientSessionSuite) TestParseServerAddrSpec(c *C) { | ||
2057 | 193 | hEp, fallbackHosts := parseServerAddrSpec("http://foo/hosts") | ||
2058 | 194 | c.Check(hEp, Equals, "http://foo/hosts") | ||
2059 | 195 | c.Check(fallbackHosts, IsNil) | ||
2060 | 196 | |||
2061 | 197 | hEp, fallbackHosts = parseServerAddrSpec("foo:443") | ||
2062 | 198 | c.Check(hEp, Equals, "") | ||
2063 | 199 | c.Check(fallbackHosts, DeepEquals, []string{"foo:443"}) | ||
2064 | 200 | |||
2065 | 201 | hEp, fallbackHosts = parseServerAddrSpec("foo:443|bar:443") | ||
2066 | 202 | c.Check(hEp, Equals, "") | ||
2067 | 203 | c.Check(fallbackHosts, DeepEquals, []string{"foo:443", "bar:443"}) | ||
2068 | 204 | } | ||
2069 | 205 | |||
2070 | 206 | /**************************************************************** | ||
2071 | 184 | NewSession() tests | 207 | NewSession() tests |
2072 | 185 | ****************************************************************/ | 208 | ****************************************************************/ |
2073 | 186 | 209 | ||
2074 | 210 | var dummyConf = ClientSessionConfig{} | ||
2075 | 211 | |||
2076 | 187 | func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) { | 212 | func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) { |
2078 | 188 | sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log) | 213 | sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) |
2079 | 189 | c.Check(sess, NotNil) | 214 | c.Check(sess, NotNil) |
2080 | 190 | c.Check(err, IsNil) | 215 | c.Check(err, IsNil) |
2081 | 216 | c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"}) | ||
2082 | 191 | // but no root CAs set | 217 | // but no root CAs set |
2083 | 192 | c.Check(sess.TLS.RootCAs, IsNil) | 218 | c.Check(sess.TLS.RootCAs, IsNil) |
2084 | 193 | c.Check(sess.State(), Equals, Disconnected) | 219 | c.Check(sess.State(), Equals, Disconnected) |
2085 | 194 | } | 220 | } |
2086 | 195 | 221 | ||
2088 | 196 | var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert") | 222 | func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) { |
2089 | 223 | sess, err := NewSession("http://foo/hosts", dummyConf, "wah", cs.lvls, cs.log) | ||
2090 | 224 | c.Assert(err, IsNil) | ||
2091 | 225 | c.Check(sess.getHost, NotNil) | ||
2092 | 226 | } | ||
2093 | 227 | |||
2094 | 228 | var certfile string = helpers.SourceRelative("../../server/acceptance/ssl/testing.cert") | ||
2095 | 197 | var pem, _ = ioutil.ReadFile(certfile) | 229 | var pem, _ = ioutil.ReadFile(certfile) |
2096 | 198 | 230 | ||
2097 | 199 | func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) { | 231 | func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) { |
2099 | 200 | sess, err := NewSession("", pem, 0, "wah", cs.lvls, cs.log) | 232 | conf := ClientSessionConfig{PEM: pem} |
2100 | 233 | sess, err := NewSession("", conf, "wah", cs.lvls, cs.log) | ||
2101 | 201 | c.Check(sess, NotNil) | 234 | c.Check(sess, NotNil) |
2102 | 202 | c.Assert(err, IsNil) | 235 | c.Assert(err, IsNil) |
2103 | 203 | c.Check(sess.TLS.RootCAs, NotNil) | 236 | c.Check(sess.TLS.RootCAs, NotNil) |
2104 | @@ -205,25 +238,172 @@ | |||
2105 | 205 | 238 | ||
2106 | 206 | func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) { | 239 | func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) { |
2107 | 207 | badpem := []byte("This is not the PEM you're looking for.") | 240 | badpem := []byte("This is not the PEM you're looking for.") |
2109 | 208 | sess, err := NewSession("", badpem, 0, "wah", cs.lvls, cs.log) | 241 | conf := ClientSessionConfig{PEM: badpem} |
2110 | 242 | sess, err := NewSession("", conf, "wah", cs.lvls, cs.log) | ||
2111 | 209 | c.Check(sess, IsNil) | 243 | c.Check(sess, IsNil) |
2112 | 210 | c.Check(err, NotNil) | 244 | c.Check(err, NotNil) |
2113 | 211 | } | 245 | } |
2114 | 212 | 246 | ||
2115 | 213 | func (cs *clientSessionSuite) TestNewSessionBadLevelMapFails(c *C) { | 247 | func (cs *clientSessionSuite) TestNewSessionBadLevelMapFails(c *C) { |
2116 | 214 | ferr := func() (levelmap.LevelMap, error) { return nil, errors.New("Busted.") } | 248 | ferr := func() (levelmap.LevelMap, error) { return nil, errors.New("Busted.") } |
2118 | 215 | sess, err := NewSession("", nil, 0, "wah", ferr, cs.log) | 249 | sess, err := NewSession("", dummyConf, "wah", ferr, cs.log) |
2119 | 216 | c.Check(sess, IsNil) | 250 | c.Check(sess, IsNil) |
2120 | 217 | c.Assert(err, NotNil) | 251 | c.Assert(err, NotNil) |
2121 | 218 | } | 252 | } |
2122 | 219 | 253 | ||
2123 | 220 | /**************************************************************** | 254 | /**************************************************************** |
2124 | 255 | getHosts() tests | ||
2125 | 256 | ****************************************************************/ | ||
2126 | 257 | |||
2127 | 258 | func (cs *clientSessionSuite) TestGetHostsFallback(c *C) { | ||
2128 | 259 | fallback := []string{"foo:443", "bar:443"} | ||
2129 | 260 | sess := &ClientSession{fallbackHosts: fallback} | ||
2130 | 261 | err := sess.getHosts() | ||
2131 | 262 | c.Assert(err, IsNil) | ||
2132 | 263 | c.Check(sess.deliveryHosts, DeepEquals, fallback) | ||
2133 | 264 | } | ||
2134 | 265 | |||
2135 | 266 | type testHostGetter struct { | ||
2136 | 267 | hosts []string | ||
2137 | 268 | err error | ||
2138 | 269 | } | ||
2139 | 270 | |||
2140 | 271 | func (thg *testHostGetter) Get() ([]string, error) { | ||
2141 | 272 | return thg.hosts, thg.err | ||
2142 | 273 | } | ||
2143 | 274 | |||
2144 | 275 | func (cs *clientSessionSuite) TestGetHostsRemote(c *C) { | ||
2145 | 276 | hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} | ||
2146 | 277 | sess := &ClientSession{getHost: hostGetter, timeSince: time.Since} | ||
2147 | 278 | err := sess.getHosts() | ||
2148 | 279 | c.Assert(err, IsNil) | ||
2149 | 280 | c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) | ||
2150 | 281 | } | ||
2151 | 282 | |||
2152 | 283 | func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) { | ||
2153 | 284 | sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log) | ||
2154 | 285 | c.Assert(err, IsNil) | ||
2155 | 286 | hostsErr := errors.New("failed") | ||
2156 | 287 | hostGetter := &testHostGetter{nil, hostsErr} | ||
2157 | 288 | sess.getHost = hostGetter | ||
2158 | 289 | err = sess.getHosts() | ||
2159 | 290 | c.Assert(err, Equals, hostsErr) | ||
2160 | 291 | c.Check(sess.deliveryHosts, IsNil) | ||
2161 | 292 | c.Check(sess.State(), Equals, Error) | ||
2162 | 293 | } | ||
2163 | 294 | |||
2164 | 295 | func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) { | ||
2165 | 296 | hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} | ||
2166 | 297 | sess := &ClientSession{ | ||
2167 | 298 | getHost: hostGetter, | ||
2168 | 299 | ClientSessionConfig: ClientSessionConfig{ | ||
2169 | 300 | HostsCachingExpiryTime: 2 * time.Hour, | ||
2170 | 301 | }, | ||
2171 | 302 | timeSince: time.Since, | ||
2172 | 303 | } | ||
2173 | 304 | err := sess.getHosts() | ||
2174 | 305 | c.Assert(err, IsNil) | ||
2175 | 306 | hostGetter.hosts = []string{"baz:443"} | ||
2176 | 307 | // cached | ||
2177 | 308 | err = sess.getHosts() | ||
2178 | 309 | c.Assert(err, IsNil) | ||
2179 | 310 | c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) | ||
2180 | 311 | // expired | ||
2181 | 312 | sess.timeSince = func(ts time.Time) time.Duration { | ||
2182 | 313 | return 3 * time.Hour | ||
2183 | 314 | } | ||
2184 | 315 | err = sess.getHosts() | ||
2185 | 316 | c.Assert(err, IsNil) | ||
2186 | 317 | c.Check(sess.deliveryHosts, DeepEquals, []string{"baz:443"}) | ||
2187 | 318 | } | ||
2188 | 319 | |||
2189 | 320 | /**************************************************************** | ||
2190 | 321 | startConnectionAttempt()/nextHostToTry()/started tests | ||
2191 | 322 | ****************************************************************/ | ||
2192 | 323 | |||
2193 | 324 | func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) { | ||
2194 | 325 | since := time.Since(time.Time{}) | ||
2195 | 326 | sess := &ClientSession{ | ||
2196 | 327 | ClientSessionConfig: ClientSessionConfig{ | ||
2197 | 328 | ExpectAllRepairedTime: 10 * time.Second, | ||
2198 | 329 | }, | ||
2199 | 330 | timeSince: func(ts time.Time) time.Duration { | ||
2200 | 331 | return since | ||
2201 | 332 | }, | ||
2202 | 333 | deliveryHosts: []string{"foo:443", "bar:443"}, | ||
2203 | 334 | } | ||
2204 | 335 | // start from first host | ||
2205 | 336 | sess.startConnectionAttempt() | ||
2206 | 337 | c.Check(sess.lastAttemptTimestamp, Not(Equals), 0) | ||
2207 | 338 | c.Check(sess.tryHost, Equals, 0) | ||
2208 | 339 | c.Check(sess.leftToTry, Equals, 2) | ||
2209 | 340 | since = 1 * time.Second | ||
2210 | 341 | sess.tryHost = 1 | ||
2211 | 342 | // just continue | ||
2212 | 343 | sess.startConnectionAttempt() | ||
2213 | 344 | c.Check(sess.tryHost, Equals, 1) | ||
2214 | 345 | sess.tryHost = 2 | ||
2215 | 346 | } | ||
2216 | 347 | |||
2217 | 348 | func (cs *clientSessionSuite) TestStartConnectionAttemptNoHostsPanic(c *C) { | ||
2218 | 349 | since := time.Since(time.Time{}) | ||
2219 | 350 | sess := &ClientSession{ | ||
2220 | 351 | ClientSessionConfig: ClientSessionConfig{ | ||
2221 | 352 | ExpectAllRepairedTime: 10 * time.Second, | ||
2222 | 353 | }, | ||
2223 | 354 | timeSince: func(ts time.Time) time.Duration { | ||
2224 | 355 | return since | ||
2225 | 356 | }, | ||
2226 | 357 | } | ||
2227 | 358 | c.Check(sess.startConnectionAttempt, PanicMatches, "should have got hosts from config or remote at this point") | ||
2228 | 359 | } | ||
2229 | 360 | |||
2230 | 361 | func (cs *clientSessionSuite) TestNextHostToTry(c *C) { | ||
2231 | 362 | sess := &ClientSession{ | ||
2232 | 363 | deliveryHosts: []string{"foo:443", "bar:443", "baz:443"}, | ||
2233 | 364 | tryHost: 0, | ||
2234 | 365 | leftToTry: 3, | ||
2235 | 366 | } | ||
2236 | 367 | c.Check(sess.nextHostToTry(), Equals, "foo:443") | ||
2237 | 368 | c.Check(sess.nextHostToTry(), Equals, "bar:443") | ||
2238 | 369 | c.Check(sess.nextHostToTry(), Equals, "baz:443") | ||
2239 | 370 | c.Check(sess.nextHostToTry(), Equals, "") | ||
2240 | 371 | c.Check(sess.nextHostToTry(), Equals, "") | ||
2241 | 372 | c.Check(sess.tryHost, Equals, 0) | ||
2242 | 373 | |||
2243 | 374 | sess.leftToTry = 3 | ||
2244 | 375 | sess.tryHost = 1 | ||
2245 | 376 | c.Check(sess.nextHostToTry(), Equals, "bar:443") | ||
2246 | 377 | c.Check(sess.nextHostToTry(), Equals, "baz:443") | ||
2247 | 378 | c.Check(sess.nextHostToTry(), Equals, "foo:443") | ||
2248 | 379 | c.Check(sess.nextHostToTry(), Equals, "") | ||
2249 | 380 | c.Check(sess.nextHostToTry(), Equals, "") | ||
2250 | 381 | c.Check(sess.tryHost, Equals, 1) | ||
2251 | 382 | } | ||
2252 | 383 | |||
2253 | 384 | func (cs *clientSessionSuite) TestStarted(c *C) { | ||
2254 | 385 | sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log) | ||
2255 | 386 | c.Assert(err, IsNil) | ||
2256 | 387 | |||
2257 | 388 | sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"} | ||
2258 | 389 | sess.tryHost = 1 | ||
2259 | 390 | |||
2260 | 391 | sess.started() | ||
2261 | 392 | c.Check(sess.tryHost, Equals, 0) | ||
2262 | 393 | c.Check(sess.State(), Equals, Started) | ||
2263 | 394 | |||
2264 | 395 | sess.started() | ||
2265 | 396 | c.Check(sess.tryHost, Equals, 2) | ||
2266 | 397 | } | ||
2267 | 398 | |||
2268 | 399 | /**************************************************************** | ||
2269 | 221 | connect() tests | 400 | connect() tests |
2270 | 222 | ****************************************************************/ | 401 | ****************************************************************/ |
2271 | 223 | 402 | ||
2272 | 224 | func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) { | 403 | func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) { |
2274 | 225 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 404 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2275 | 226 | c.Assert(err, IsNil) | 405 | c.Assert(err, IsNil) |
2276 | 406 | sess.deliveryHosts = []string{"nowhere"} | ||
2277 | 227 | err = sess.connect() | 407 | err = sess.connect() |
2278 | 228 | c.Check(err, ErrorMatches, ".*connect.*address.*") | 408 | c.Check(err, ErrorMatches, ".*connect.*address.*") |
2279 | 229 | c.Check(sess.State(), Equals, Error) | 409 | c.Check(sess.State(), Equals, Error) |
2280 | @@ -233,20 +413,36 @@ | |||
2281 | 233 | srv, err := net.Listen("tcp", "localhost:0") | 413 | srv, err := net.Listen("tcp", "localhost:0") |
2282 | 234 | c.Assert(err, IsNil) | 414 | c.Assert(err, IsNil) |
2283 | 235 | defer srv.Close() | 415 | defer srv.Close() |
2290 | 236 | sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log) | 416 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2291 | 237 | c.Assert(err, IsNil) | 417 | c.Assert(err, IsNil) |
2292 | 238 | err = sess.connect() | 418 | sess.deliveryHosts = []string{srv.Addr().String()} |
2293 | 239 | c.Check(err, IsNil) | 419 | err = sess.connect() |
2294 | 240 | c.Check(sess.Connection, NotNil) | 420 | c.Check(err, IsNil) |
2295 | 241 | c.Check(sess.State(), Equals, Connected) | 421 | c.Check(sess.Connection, NotNil) |
2296 | 422 | c.Check(sess.State(), Equals, Connected) | ||
2297 | 423 | } | ||
2298 | 424 | |||
2299 | 425 | func (cs *clientSessionSuite) TestConnectSecondConnects(c *C) { | ||
2300 | 426 | srv, err := net.Listen("tcp", "localhost:0") | ||
2301 | 427 | c.Assert(err, IsNil) | ||
2302 | 428 | defer srv.Close() | ||
2303 | 429 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | ||
2304 | 430 | c.Assert(err, IsNil) | ||
2305 | 431 | sess.deliveryHosts = []string{"nowhere", srv.Addr().String()} | ||
2306 | 432 | err = sess.connect() | ||
2307 | 433 | c.Check(err, IsNil) | ||
2308 | 434 | c.Check(sess.Connection, NotNil) | ||
2309 | 435 | c.Check(sess.State(), Equals, Connected) | ||
2310 | 436 | c.Check(sess.tryHost, Equals, 0) | ||
2311 | 242 | } | 437 | } |
2312 | 243 | 438 | ||
2313 | 244 | func (cs *clientSessionSuite) TestConnectConnectFail(c *C) { | 439 | func (cs *clientSessionSuite) TestConnectConnectFail(c *C) { |
2314 | 245 | srv, err := net.Listen("tcp", "localhost:0") | 440 | srv, err := net.Listen("tcp", "localhost:0") |
2315 | 246 | c.Assert(err, IsNil) | 441 | c.Assert(err, IsNil) |
2317 | 247 | sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log) | 442 | sess, err := NewSession(srv.Addr().String(), dummyConf, "wah", cs.lvls, cs.log) |
2318 | 248 | srv.Close() | 443 | srv.Close() |
2319 | 249 | c.Assert(err, IsNil) | 444 | c.Assert(err, IsNil) |
2320 | 445 | sess.deliveryHosts = []string{srv.Addr().String()} | ||
2321 | 250 | err = sess.connect() | 446 | err = sess.connect() |
2322 | 251 | c.Check(err, ErrorMatches, ".*connection refused") | 447 | c.Check(err, ErrorMatches, ".*connection refused") |
2323 | 252 | c.Check(sess.State(), Equals, Error) | 448 | c.Check(sess.State(), Equals, Error) |
2324 | @@ -257,7 +453,7 @@ | |||
2325 | 257 | ****************************************************************/ | 453 | ****************************************************************/ |
2326 | 258 | 454 | ||
2327 | 259 | func (cs *clientSessionSuite) TestClose(c *C) { | 455 | func (cs *clientSessionSuite) TestClose(c *C) { |
2329 | 260 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 456 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2330 | 261 | c.Assert(err, IsNil) | 457 | c.Assert(err, IsNil) |
2331 | 262 | sess.Connection = &testConn{Name: "TestClose"} | 458 | sess.Connection = &testConn{Name: "TestClose"} |
2332 | 263 | sess.Close() | 459 | sess.Close() |
2333 | @@ -266,7 +462,7 @@ | |||
2334 | 266 | } | 462 | } |
2335 | 267 | 463 | ||
2336 | 268 | func (cs *clientSessionSuite) TestCloseTwice(c *C) { | 464 | func (cs *clientSessionSuite) TestCloseTwice(c *C) { |
2338 | 269 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 465 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2339 | 270 | c.Assert(err, IsNil) | 466 | c.Assert(err, IsNil) |
2340 | 271 | sess.Connection = &testConn{Name: "TestCloseTwice"} | 467 | sess.Connection = &testConn{Name: "TestCloseTwice"} |
2341 | 272 | sess.Close() | 468 | sess.Close() |
2342 | @@ -277,7 +473,7 @@ | |||
2343 | 277 | } | 473 | } |
2344 | 278 | 474 | ||
2345 | 279 | func (cs *clientSessionSuite) TestCloseFails(c *C) { | 475 | func (cs *clientSessionSuite) TestCloseFails(c *C) { |
2347 | 280 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 476 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2348 | 281 | c.Assert(err, IsNil) | 477 | c.Assert(err, IsNil) |
2349 | 282 | sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)} | 478 | sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)} |
2350 | 283 | sess.Close() | 479 | sess.Close() |
2351 | @@ -291,7 +487,7 @@ | |||
2352 | 291 | func (d *derp) Stop() { d.stopped = true } | 487 | func (d *derp) Stop() { d.stopped = true } |
2353 | 292 | 488 | ||
2354 | 293 | func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) { | 489 | func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) { |
2356 | 294 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 490 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2357 | 295 | c.Assert(err, IsNil) | 491 | c.Assert(err, IsNil) |
2358 | 296 | ar := new(derp) | 492 | ar := new(derp) |
2359 | 297 | sess.retrier = ar | 493 | sess.retrier = ar |
2360 | @@ -308,7 +504,7 @@ | |||
2361 | 308 | 504 | ||
2362 | 309 | func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) { | 505 | func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) { |
2363 | 310 | // checks that AutoRedial sets up a retrier and tries redialing it | 506 | // checks that AutoRedial sets up a retrier and tries redialing it |
2365 | 311 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 507 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2366 | 312 | c.Assert(err, IsNil) | 508 | c.Assert(err, IsNil) |
2367 | 313 | ar := new(derp) | 509 | ar := new(derp) |
2368 | 314 | sess.retrier = ar | 510 | sess.retrier = ar |
2369 | @@ -319,7 +515,7 @@ | |||
2370 | 319 | 515 | ||
2371 | 320 | func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) { | 516 | func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) { |
2372 | 321 | // checks that AutoRedial stops the previous retrier | 517 | // checks that AutoRedial stops the previous retrier |
2374 | 322 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 518 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2375 | 323 | c.Assert(err, IsNil) | 519 | c.Assert(err, IsNil) |
2376 | 324 | ch := make(chan uint32) | 520 | ch := make(chan uint32) |
2377 | 325 | c.Check(sess.retrier, IsNil) | 521 | c.Check(sess.retrier, IsNil) |
2378 | @@ -344,7 +540,10 @@ | |||
2379 | 344 | 540 | ||
2380 | 345 | func (s *msgSuite) SetUpTest(c *C) { | 541 | func (s *msgSuite) SetUpTest(c *C) { |
2381 | 346 | var err error | 542 | var err error |
2383 | 347 | s.sess, err = NewSession("", nil, time.Millisecond, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug")) | 543 | conf := ClientSessionConfig{ |
2384 | 544 | ExchangeTimeout: time.Millisecond, | ||
2385 | 545 | } | ||
2386 | 546 | s.sess, err = NewSession("", conf, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug")) | ||
2387 | 348 | c.Assert(err, IsNil) | 547 | c.Assert(err, IsNil) |
2388 | 349 | s.sess.Connection = &testConn{Name: "TestHandle*"} | 548 | s.sess.Connection = &testConn{Name: "TestHandle*"} |
2389 | 350 | s.errCh = make(chan error, 1) | 549 | s.errCh = make(chan error, 1) |
2390 | @@ -383,14 +582,28 @@ | |||
2391 | 383 | AppId: "--ignored--", | 582 | AppId: "--ignored--", |
2392 | 384 | ChanId: "0", | 583 | ChanId: "0", |
2393 | 385 | TopLevel: 2, | 584 | TopLevel: 2, |
2395 | 386 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 585 | Payloads: []json.RawMessage{ |
2396 | 586 | json.RawMessage(`{"img1/m1":[101,"tubular"]}`), | ||
2397 | 587 | json.RawMessage("false"), // shouldn't happen but robust | ||
2398 | 588 | json.RawMessage(`{"img1/m1":[102,"tubular"]}`), | ||
2399 | 589 | }, | ||
2400 | 387 | }, protocol.NotificationsMsg{}} | 590 | }, protocol.NotificationsMsg{}} |
2401 | 388 | go func() { s.errCh <- s.sess.handleBroadcast(&msg) }() | 591 | go func() { s.errCh <- s.sess.handleBroadcast(&msg) }() |
2402 | 389 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 592 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
2403 | 390 | s.upCh <- nil // ack ok | 593 | s.upCh <- nil // ack ok |
2404 | 391 | c.Check(<-s.errCh, Equals, nil) | 594 | c.Check(<-s.errCh, Equals, nil) |
2405 | 392 | c.Assert(len(s.sess.MsgCh), Equals, 1) | 595 | c.Assert(len(s.sess.MsgCh), Equals, 1) |
2407 | 393 | c.Check(<-s.sess.MsgCh, DeepEquals, &Notification{TopLevel: 2}) | 596 | c.Check(<-s.sess.MsgCh, DeepEquals, &Notification{ |
2408 | 597 | TopLevel: 2, | ||
2409 | 598 | Decoded: []map[string]interface{}{ | ||
2410 | 599 | map[string]interface{}{ | ||
2411 | 600 | "img1/m1": []interface{}{float64(101), "tubular"}, | ||
2412 | 601 | }, | ||
2413 | 602 | map[string]interface{}{ | ||
2414 | 603 | "img1/m1": []interface{}{float64(102), "tubular"}, | ||
2415 | 604 | }, | ||
2416 | 605 | }, | ||
2417 | 606 | }) | ||
2418 | 394 | // and finally, the session keeps track of the levels | 607 | // and finally, the session keeps track of the levels |
2419 | 395 | levels, err := s.sess.Levels.GetAll() | 608 | levels, err := s.sess.Levels.GetAll() |
2420 | 396 | c.Check(err, IsNil) | 609 | c.Check(err, IsNil) |
2421 | @@ -519,7 +732,7 @@ | |||
2422 | 519 | start() tests | 732 | start() tests |
2423 | 520 | ****************************************************************/ | 733 | ****************************************************************/ |
2424 | 521 | func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) { | 734 | func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) { |
2426 | 522 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 735 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2427 | 523 | c.Assert(err, IsNil) | 736 | c.Assert(err, IsNil) |
2428 | 524 | sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails", | 737 | sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails", |
2429 | 525 | DeadlineCondition: condition.Work(false)} // setdeadline will fail | 738 | DeadlineCondition: condition.Work(false)} // setdeadline will fail |
2430 | @@ -529,7 +742,7 @@ | |||
2431 | 529 | } | 742 | } |
2432 | 530 | 743 | ||
2433 | 531 | func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) { | 744 | func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) { |
2435 | 532 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 745 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2436 | 533 | c.Assert(err, IsNil) | 746 | c.Assert(err, IsNil) |
2437 | 534 | sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails", | 747 | sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails", |
2438 | 535 | WriteCondition: condition.Work(false)} // write will fail | 748 | WriteCondition: condition.Work(false)} // write will fail |
2439 | @@ -539,7 +752,7 @@ | |||
2440 | 539 | } | 752 | } |
2441 | 540 | 753 | ||
2442 | 541 | func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) { | 754 | func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) { |
2444 | 542 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 755 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2445 | 543 | c.Assert(err, IsNil) | 756 | c.Assert(err, IsNil) |
2446 | 544 | sess.Levels = &brokenLevelMap{} | 757 | sess.Levels = &brokenLevelMap{} |
2447 | 545 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} | 758 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} |
2448 | @@ -559,7 +772,7 @@ | |||
2449 | 559 | } | 772 | } |
2450 | 560 | 773 | ||
2451 | 561 | func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) { | 774 | func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) { |
2453 | 562 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 775 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2454 | 563 | c.Assert(err, IsNil) | 776 | c.Assert(err, IsNil) |
2455 | 564 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} | 777 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} |
2456 | 565 | errCh := make(chan error, 1) | 778 | errCh := make(chan error, 1) |
2457 | @@ -585,7 +798,7 @@ | |||
2458 | 585 | } | 798 | } |
2459 | 586 | 799 | ||
2460 | 587 | func (cs *clientSessionSuite) TestStartConnackReadError(c *C) { | 800 | func (cs *clientSessionSuite) TestStartConnackReadError(c *C) { |
2462 | 588 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 801 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2463 | 589 | c.Assert(err, IsNil) | 802 | c.Assert(err, IsNil) |
2464 | 590 | sess.Connection = &testConn{Name: "TestStartConnackReadError"} | 803 | sess.Connection = &testConn{Name: "TestStartConnackReadError"} |
2465 | 591 | errCh := make(chan error, 1) | 804 | errCh := make(chan error, 1) |
2466 | @@ -609,7 +822,7 @@ | |||
2467 | 609 | } | 822 | } |
2468 | 610 | 823 | ||
2469 | 611 | func (cs *clientSessionSuite) TestStartBadConnack(c *C) { | 824 | func (cs *clientSessionSuite) TestStartBadConnack(c *C) { |
2471 | 612 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 825 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2472 | 613 | c.Assert(err, IsNil) | 826 | c.Assert(err, IsNil) |
2473 | 614 | sess.Connection = &testConn{Name: "TestStartBadConnack"} | 827 | sess.Connection = &testConn{Name: "TestStartBadConnack"} |
2474 | 615 | errCh := make(chan error, 1) | 828 | errCh := make(chan error, 1) |
2475 | @@ -633,7 +846,7 @@ | |||
2476 | 633 | } | 846 | } |
2477 | 634 | 847 | ||
2478 | 635 | func (cs *clientSessionSuite) TestStartNotConnack(c *C) { | 848 | func (cs *clientSessionSuite) TestStartNotConnack(c *C) { |
2480 | 636 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 849 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2481 | 637 | c.Assert(err, IsNil) | 850 | c.Assert(err, IsNil) |
2482 | 638 | sess.Connection = &testConn{Name: "TestStartBadConnack"} | 851 | sess.Connection = &testConn{Name: "TestStartBadConnack"} |
2483 | 639 | errCh := make(chan error, 1) | 852 | errCh := make(chan error, 1) |
2484 | @@ -657,7 +870,14 @@ | |||
2485 | 657 | } | 870 | } |
2486 | 658 | 871 | ||
2487 | 659 | func (cs *clientSessionSuite) TestStartWorks(c *C) { | 872 | func (cs *clientSessionSuite) TestStartWorks(c *C) { |
2489 | 660 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 873 | info := map[string]interface{}{ |
2490 | 874 | "foo": 1, | ||
2491 | 875 | "bar": "baz", | ||
2492 | 876 | } | ||
2493 | 877 | conf := ClientSessionConfig{ | ||
2494 | 878 | Info: info, | ||
2495 | 879 | } | ||
2496 | 880 | sess, err := NewSession("", conf, "wah", cs.lvls, cs.log) | ||
2497 | 661 | c.Assert(err, IsNil) | 881 | c.Assert(err, IsNil) |
2498 | 662 | sess.Connection = &testConn{Name: "TestStartWorks"} | 882 | sess.Connection = &testConn{Name: "TestStartWorks"} |
2499 | 663 | errCh := make(chan error, 1) | 883 | errCh := make(chan error, 1) |
2500 | @@ -671,8 +891,10 @@ | |||
2501 | 671 | }() | 891 | }() |
2502 | 672 | 892 | ||
2503 | 673 | c.Check(takeNext(downCh), Equals, "deadline 0") | 893 | c.Check(takeNext(downCh), Equals, "deadline 0") |
2505 | 674 | _, ok := takeNext(downCh).(protocol.ConnectMsg) | 894 | msg, ok := takeNext(downCh).(protocol.ConnectMsg) |
2506 | 675 | c.Check(ok, Equals, true) | 895 | c.Check(ok, Equals, true) |
2507 | 896 | c.Check(msg.DeviceId, Equals, "wah") | ||
2508 | 897 | c.Check(msg.Info, DeepEquals, info) | ||
2509 | 676 | upCh <- nil // no error | 898 | upCh <- nil // no error |
2510 | 677 | upCh <- protocol.ConnAckMsg{ | 899 | upCh <- protocol.ConnAckMsg{ |
2511 | 678 | Type: "connack", | 900 | Type: "connack", |
2512 | @@ -688,34 +910,49 @@ | |||
2513 | 688 | run() tests | 910 | run() tests |
2514 | 689 | ****************************************************************/ | 911 | ****************************************************************/ |
2515 | 690 | 912 | ||
2518 | 691 | func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { | 913 | func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) { |
2519 | 692 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 914 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2520 | 693 | c.Assert(err, IsNil) | 915 | c.Assert(err, IsNil) |
2522 | 694 | failure := errors.New("TestRunBailsIfConnectFails") | 916 | failure := errors.New("TestRunBailsIfHostGetterFails") |
2523 | 695 | has_closed := false | 917 | has_closed := false |
2524 | 696 | err = sess.run( | 918 | err = sess.run( |
2525 | 697 | func() { has_closed = true }, | 919 | func() { has_closed = true }, |
2526 | 698 | func() error { return failure }, | 920 | func() error { return failure }, |
2527 | 699 | nil, | 921 | nil, |
2528 | 922 | nil, | ||
2529 | 700 | nil) | 923 | nil) |
2530 | 701 | c.Check(err, Equals, failure) | 924 | c.Check(err, Equals, failure) |
2531 | 702 | c.Check(has_closed, Equals, true) | 925 | c.Check(has_closed, Equals, true) |
2532 | 703 | } | 926 | } |
2533 | 704 | 927 | ||
2534 | 928 | func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { | ||
2535 | 929 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | ||
2536 | 930 | c.Assert(err, IsNil) | ||
2537 | 931 | failure := errors.New("TestRunBailsIfConnectFails") | ||
2538 | 932 | err = sess.run( | ||
2539 | 933 | func() {}, | ||
2540 | 934 | func() error { return nil }, | ||
2541 | 935 | func() error { return failure }, | ||
2542 | 936 | nil, | ||
2543 | 937 | nil) | ||
2544 | 938 | c.Check(err, Equals, failure) | ||
2545 | 939 | } | ||
2546 | 940 | |||
2547 | 705 | func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) { | 941 | func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) { |
2549 | 706 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 942 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2550 | 707 | c.Assert(err, IsNil) | 943 | c.Assert(err, IsNil) |
2551 | 708 | failure := errors.New("TestRunBailsIfStartFails") | 944 | failure := errors.New("TestRunBailsIfStartFails") |
2552 | 709 | err = sess.run( | 945 | err = sess.run( |
2553 | 710 | func() {}, | 946 | func() {}, |
2554 | 711 | func() error { return nil }, | 947 | func() error { return nil }, |
2555 | 948 | func() error { return nil }, | ||
2556 | 712 | func() error { return failure }, | 949 | func() error { return failure }, |
2557 | 713 | nil) | 950 | nil) |
2558 | 714 | c.Check(err, Equals, failure) | 951 | c.Check(err, Equals, failure) |
2559 | 715 | } | 952 | } |
2560 | 716 | 953 | ||
2561 | 717 | func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) { | 954 | func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) { |
2563 | 718 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 955 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2564 | 719 | c.Assert(err, IsNil) | 956 | c.Assert(err, IsNil) |
2565 | 720 | // just to make a point: until here we haven't set ErrCh & MsgCh (no | 957 | // just to make a point: until here we haven't set ErrCh & MsgCh (no |
2566 | 721 | // biggie if this stops being true) | 958 | // biggie if this stops being true) |
2567 | @@ -727,6 +964,7 @@ | |||
2568 | 727 | func() {}, | 964 | func() {}, |
2569 | 728 | func() error { return nil }, | 965 | func() error { return nil }, |
2570 | 729 | func() error { return nil }, | 966 | func() error { return nil }, |
2571 | 967 | func() error { return nil }, | ||
2572 | 730 | func() error { sess.MsgCh <- notf; return <-failureCh }) | 968 | func() error { sess.MsgCh <- notf; return <-failureCh }) |
2573 | 731 | c.Check(err, Equals, nil) | 969 | c.Check(err, Equals, nil) |
2574 | 732 | // if run doesn't error it sets up the channels | 970 | // if run doesn't error it sets up the channels |
2575 | @@ -744,7 +982,7 @@ | |||
2576 | 744 | ****************************************************************/ | 982 | ****************************************************************/ |
2577 | 745 | 983 | ||
2578 | 746 | func (cs *clientSessionSuite) TestJitter(c *C) { | 984 | func (cs *clientSessionSuite) TestJitter(c *C) { |
2580 | 747 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 985 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2581 | 748 | c.Assert(err, IsNil) | 986 | c.Assert(err, IsNil) |
2582 | 749 | num_tries := 20 // should do the math | 987 | num_tries := 20 // should do the math |
2583 | 750 | spread := time.Second // | 988 | spread := time.Second // |
2584 | @@ -776,12 +1014,17 @@ | |||
2585 | 776 | 1014 | ||
2586 | 777 | func (cs *clientSessionSuite) TestDialPanics(c *C) { | 1015 | func (cs *clientSessionSuite) TestDialPanics(c *C) { |
2587 | 778 | // one last unhappy test | 1016 | // one last unhappy test |
2589 | 779 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) | 1017 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) |
2590 | 780 | c.Assert(err, IsNil) | 1018 | c.Assert(err, IsNil) |
2591 | 781 | sess.Protocolator = nil | 1019 | sess.Protocolator = nil |
2592 | 782 | c.Check(sess.Dial, PanicMatches, ".*protocol constructor.") | 1020 | c.Check(sess.Dial, PanicMatches, ".*protocol constructor.") |
2593 | 783 | } | 1021 | } |
2594 | 784 | 1022 | ||
2595 | 1023 | var ( | ||
2596 | 1024 | dialTestTimeout = 100 * time.Millisecond | ||
2597 | 1025 | dialTestConf = ClientSessionConfig{ExchangeTimeout: dialTestTimeout} | ||
2598 | 1026 | ) | ||
2599 | 1027 | |||
2600 | 785 | func (cs *clientSessionSuite) TestDialWorks(c *C) { | 1028 | func (cs *clientSessionSuite) TestDialWorks(c *C) { |
2601 | 786 | // happy path thoughts | 1029 | // happy path thoughts |
2602 | 787 | cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) | 1030 | cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) |
2603 | @@ -791,10 +1034,22 @@ | |||
2604 | 791 | SessionTicketsDisabled: true, | 1034 | SessionTicketsDisabled: true, |
2605 | 792 | } | 1035 | } |
2606 | 793 | 1036 | ||
2607 | 794 | timeout := 100 * time.Millisecond | ||
2608 | 795 | lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) | 1037 | lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) |
2609 | 796 | c.Assert(err, IsNil) | 1038 | c.Assert(err, IsNil) |
2611 | 797 | sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log) | 1039 | // advertise |
2612 | 1040 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
2613 | 1041 | b, err := json.Marshal(map[string]interface{}{ | ||
2614 | 1042 | "hosts": []string{"nowhere", lst.Addr().String()}, | ||
2615 | 1043 | }) | ||
2616 | 1044 | if err != nil { | ||
2617 | 1045 | panic(err) | ||
2618 | 1046 | } | ||
2619 | 1047 | w.Header().Set("Content-Type", "application/json") | ||
2620 | 1048 | w.Write(b) | ||
2621 | 1049 | })) | ||
2622 | 1050 | defer ts.Close() | ||
2623 | 1051 | |||
2624 | 1052 | sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log) | ||
2625 | 798 | c.Assert(err, IsNil) | 1053 | c.Assert(err, IsNil) |
2626 | 799 | tconn := &testConn{CloseCondition: condition.Fail2Work(10)} | 1054 | tconn := &testConn{CloseCondition: condition.Fail2Work(10)} |
2627 | 800 | sess.Connection = tconn | 1055 | sess.Connection = tconn |
2628 | @@ -819,10 +1074,13 @@ | |||
2629 | 819 | c.Check(tconn.CloseCondition.String(), Matches, ".* 9 to go.") | 1074 | c.Check(tconn.CloseCondition.String(), Matches, ".* 9 to go.") |
2630 | 820 | 1075 | ||
2631 | 821 | // now, start: 1. protocol version | 1076 | // now, start: 1. protocol version |
2633 | 822 | v, err := protocol.ReadWireFormatVersion(srv, timeout) | 1077 | v, err := protocol.ReadWireFormatVersion(srv, dialTestTimeout) |
2634 | 823 | c.Assert(err, IsNil) | 1078 | c.Assert(err, IsNil) |
2635 | 824 | c.Assert(v, Equals, protocol.ProtocolWireVersion) | 1079 | c.Assert(v, Equals, protocol.ProtocolWireVersion) |
2636 | 825 | 1080 | ||
2637 | 1081 | // if something goes wrong session would try the first/other host | ||
2638 | 1082 | c.Check(sess.tryHost, Equals, 0) | ||
2639 | 1083 | |||
2640 | 826 | // 2. "connect" (but on the fake protcol above! woo) | 1084 | // 2. "connect" (but on the fake protcol above! woo) |
2641 | 827 | 1085 | ||
2642 | 828 | c.Check(takeNext(downCh), Equals, "deadline 100ms") | 1086 | c.Check(takeNext(downCh), Equals, "deadline 100ms") |
2643 | @@ -843,6 +1101,9 @@ | |||
2644 | 843 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1101 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
2645 | 844 | upCh <- nil | 1102 | upCh <- nil |
2646 | 845 | 1103 | ||
2647 | 1104 | // session would retry the same host | ||
2648 | 1105 | c.Check(sess.tryHost, Equals, 1) | ||
2649 | 1106 | |||
2650 | 846 | // and broadcasts... | 1107 | // and broadcasts... |
2651 | 847 | b := &protocol.BroadcastMsg{ | 1108 | b := &protocol.BroadcastMsg{ |
2652 | 848 | Type: "broadcast", | 1109 | Type: "broadcast", |
2653 | @@ -870,3 +1131,30 @@ | |||
2654 | 870 | upCh <- failure | 1131 | upCh <- failure |
2655 | 871 | c.Check(<-sess.ErrCh, Equals, failure) | 1132 | c.Check(<-sess.ErrCh, Equals, failure) |
2656 | 872 | } | 1133 | } |
2657 | 1134 | |||
2658 | 1135 | func (cs *clientSessionSuite) TestDialWorksDirect(c *C) { | ||
2659 | 1136 | // happy path thoughts | ||
2660 | 1137 | cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) | ||
2661 | 1138 | c.Assert(err, IsNil) | ||
2662 | 1139 | tlsCfg := &tls.Config{ | ||
2663 | 1140 | Certificates: []tls.Certificate{cert}, | ||
2664 | 1141 | SessionTicketsDisabled: true, | ||
2665 | 1142 | } | ||
2666 | 1143 | |||
2667 | 1144 | lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) | ||
2668 | 1145 | c.Assert(err, IsNil) | ||
2669 | 1146 | sess, err := NewSession(lst.Addr().String(), dialTestConf, "wah", cs.lvls, cs.log) | ||
2670 | 1147 | c.Assert(err, IsNil) | ||
2671 | 1148 | defer sess.Close() | ||
2672 | 1149 | |||
2673 | 1150 | upCh := make(chan interface{}, 5) | ||
2674 | 1151 | downCh := make(chan interface{}, 5) | ||
2675 | 1152 | proto := &testProtocol{up: upCh, down: downCh} | ||
2676 | 1153 | sess.Protocolator = func(net.Conn) protocol.Protocol { return proto } | ||
2677 | 1154 | |||
2678 | 1155 | go sess.Dial() | ||
2679 | 1156 | |||
2680 | 1157 | _, err = lst.Accept() | ||
2681 | 1158 | c.Assert(err, IsNil) | ||
2682 | 1159 | // connect done | ||
2683 | 1160 | } | ||
2684 | 873 | 1161 | ||
2685 | === modified file 'debian/config.json' | |||
2686 | --- debian/config.json 2014-03-20 12:17:40 +0000 | |||
2687 | +++ debian/config.json 2014-04-04 13:58:43 +0000 | |||
2688 | @@ -1,6 +1,9 @@ | |||
2689 | 1 | { | 1 | { |
2690 | 2 | "connect_timeout": "20s", | ||
2691 | 2 | "exchange_timeout": "30s", | 3 | "exchange_timeout": "30s", |
2693 | 3 | "addr": "push-delivery.ubuntu.com:443", | 4 | "hosts_cache_expiry": "12h", |
2694 | 5 | "expect_all_repaired": "40m", | ||
2695 | 6 | "addr": "https://push.ubuntu.com/delivery-hosts", | ||
2696 | 4 | "cert_pem_file": "", | 7 | "cert_pem_file": "", |
2697 | 5 | "stabilizing_timeout": "2s", | 8 | "stabilizing_timeout": "2s", |
2698 | 6 | "recheck_timeout": "10m", | 9 | "recheck_timeout": "10m", |
2699 | 7 | 10 | ||
2700 | === modified file 'debian/ubuntu-push-client.conf' | |||
2701 | --- debian/ubuntu-push-client.conf 2014-02-07 11:31:54 +0000 | |||
2702 | +++ debian/ubuntu-push-client.conf 2014-04-04 13:58:43 +0000 | |||
2703 | @@ -1,6 +1,6 @@ | |||
2705 | 1 | description "Starts the ubuntu push notifications client side daemon" | 1 | description "ubuntu push notification client-side daemon" |
2706 | 2 | 2 | ||
2709 | 3 | start on dbus | 3 | start on started dbus |
2710 | 4 | stop on runlevel [06] | 4 | stop on stopped dbus |
2711 | 5 | 5 | ||
2712 | 6 | exec /usr/lib/ubuntu-push-client/ubuntu-push-client | 6 | exec /usr/lib/ubuntu-push-client/ubuntu-push-client |
2713 | 7 | 7 | ||
2714 | === added directory 'sampleconfigs' | |||
2715 | === renamed file 'server/acceptance/config/config.json' => 'sampleconfigs/dev.json' | |||
2716 | --- server/acceptance/config/config.json 2014-01-17 17:20:34 +0000 | |||
2717 | +++ sampleconfigs/dev.json 2014-04-04 13:58:43 +0000 | |||
2718 | @@ -4,9 +4,9 @@ | |||
2719 | 4 | "broker_queue_size": 10000, | 4 | "broker_queue_size": 10000, |
2720 | 5 | "session_queue_size": 10, | 5 | "session_queue_size": 10, |
2721 | 6 | "addr": "127.0.0.1:9090", | 6 | "addr": "127.0.0.1:9090", |
2725 | 7 | "key_pem_file": "testing.key", | 7 | "key_pem_file": "../server/acceptance/ssl/testing.key", |
2726 | 8 | "cert_pem_file": "testing.cert", | 8 | "cert_pem_file": "../server/acceptance/ssl/testing.cert", |
2727 | 9 | "http_addr": "127.0.0.1:8888", | 9 | "http_addr": "127.0.0.1:8080", |
2728 | 10 | "http_read_timeout": "5s", | 10 | "http_read_timeout": "5s", |
2729 | 11 | "http_write_timeout": "5s" | 11 | "http_write_timeout": "5s" |
2730 | 12 | } | 12 | } |
2731 | 13 | 13 | ||
2732 | === modified file 'server/acceptance/acceptanceclient.go' | |||
2733 | --- server/acceptance/acceptanceclient.go 2014-02-21 16:17:28 +0000 | |||
2734 | +++ server/acceptance/acceptanceclient.go 2014-04-04 13:58:43 +0000 | |||
2735 | @@ -35,6 +35,8 @@ | |||
2736 | 35 | type ClientSession struct { | 35 | type ClientSession struct { |
2737 | 36 | // configuration | 36 | // configuration |
2738 | 37 | DeviceId string | 37 | DeviceId string |
2739 | 38 | Model string | ||
2740 | 39 | ImageChannel string | ||
2741 | 38 | ServerAddr string | 40 | ServerAddr string |
2742 | 39 | ExchangeTimeout time.Duration | 41 | ExchangeTimeout time.Duration |
2743 | 40 | CertPEMBlock []byte | 42 | CertPEMBlock []byte |
2744 | @@ -86,6 +88,10 @@ | |||
2745 | 86 | Type: "connect", | 88 | Type: "connect", |
2746 | 87 | DeviceId: sess.DeviceId, | 89 | DeviceId: sess.DeviceId, |
2747 | 88 | Levels: sess.Levels, | 90 | Levels: sess.Levels, |
2748 | 91 | Info: map[string]interface{}{ | ||
2749 | 92 | "device": sess.Model, | ||
2750 | 93 | "channel": sess.ImageChannel, | ||
2751 | 94 | }, | ||
2752 | 89 | }) | 95 | }) |
2753 | 90 | if err != nil { | 96 | if err != nil { |
2754 | 91 | return err | 97 | return err |
2755 | 92 | 98 | ||
2756 | === modified file 'server/acceptance/cmd/acceptanceclient.go' | |||
2757 | --- server/acceptance/cmd/acceptanceclient.go 2014-02-21 16:17:28 +0000 | |||
2758 | +++ server/acceptance/cmd/acceptanceclient.go 2014-04-04 13:58:43 +0000 | |||
2759 | @@ -30,6 +30,8 @@ | |||
2760 | 30 | var ( | 30 | var ( |
2761 | 31 | insecureFlag = flag.Bool("insecure", false, "disable checking of server certificate and hostname") | 31 | insecureFlag = flag.Bool("insecure", false, "disable checking of server certificate and hostname") |
2762 | 32 | reportPingsFlag = flag.Bool("reportPings", true, "report each Ping from the server") | 32 | reportPingsFlag = flag.Bool("reportPings", true, "report each Ping from the server") |
2763 | 33 | deviceModel = flag.String("model", "?", "device image model") | ||
2764 | 34 | imageChannel = flag.String("imageChannel", "?", "image channel") | ||
2765 | 33 | ) | 35 | ) |
2766 | 34 | 36 | ||
2767 | 35 | type configuration struct { | 37 | type configuration struct { |
2768 | @@ -64,9 +66,12 @@ | |||
2769 | 64 | ServerAddr: cfg.Addr.HostPort(), | 66 | ServerAddr: cfg.Addr.HostPort(), |
2770 | 65 | DeviceId: flag.Arg(1), | 67 | DeviceId: flag.Arg(1), |
2771 | 66 | // flags | 68 | // flags |
2774 | 67 | ReportPings: *reportPingsFlag, | 69 | Model: *deviceModel, |
2775 | 68 | Insecure: *insecureFlag, | 70 | ImageChannel: *imageChannel, |
2776 | 71 | ReportPings: *reportPingsFlag, | ||
2777 | 72 | Insecure: *insecureFlag, | ||
2778 | 69 | } | 73 | } |
2779 | 74 | log.Printf("with: %#v", session) | ||
2780 | 70 | session.CertPEMBlock, err = config.LoadFile(cfg.CertPEMFile, filepath.Dir(configFName)) | 75 | session.CertPEMBlock, err = config.LoadFile(cfg.CertPEMFile, filepath.Dir(configFName)) |
2781 | 71 | if err != nil { | 76 | if err != nil { |
2782 | 72 | log.Fatalf("reading CertPEMFile: %v", err) | 77 | log.Fatalf("reading CertPEMFile: %v", err) |
2783 | 73 | 78 | ||
2784 | === renamed directory 'server/acceptance/config' => 'server/acceptance/ssl' | |||
2785 | === modified file 'server/acceptance/suites/broadcast.go' | |||
2786 | --- server/acceptance/suites/broadcast.go 2014-02-21 21:39:54 +0000 | |||
2787 | +++ server/acceptance/suites/broadcast.go 2014-04-04 13:58:43 +0000 | |||
2788 | @@ -41,11 +41,34 @@ | |||
2789 | 41 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 41 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2790 | 42 | Channel: "system", | 42 | Channel: "system", |
2791 | 43 | ExpireOn: future, | 43 | ExpireOn: future, |
2797 | 44 | Data: json.RawMessage(`{"n": 42}`), | 44 | Data: json.RawMessage(`{"img1/m1": 42}`), |
2798 | 45 | }) | 45 | }) |
2799 | 46 | c.Assert(err, IsNil) | 46 | c.Assert(err, IsNil) |
2800 | 47 | c.Assert(got, Matches, ".*ok.*") | 47 | c.Assert(got, Matches, ".*ok.*") |
2801 | 48 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"n":42}]`) | 48 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
2802 | 49 | stop() | ||
2803 | 50 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | ||
2804 | 51 | c.Check(len(errCh), Equals, 0) | ||
2805 | 52 | } | ||
2806 | 53 | |||
2807 | 54 | func (s *BroadcastAcceptanceSuite) TestBroadcastToConnectedChannelFilter(c *C) { | ||
2808 | 55 | events, errCh, stop := s.StartClient(c, "DEVB", nil) | ||
2809 | 56 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | ||
2810 | 57 | Channel: "system", | ||
2811 | 58 | ExpireOn: future, | ||
2812 | 59 | Data: json.RawMessage(`{"img1/m2": 10}`), | ||
2813 | 60 | }) | ||
2814 | 61 | c.Assert(err, IsNil) | ||
2815 | 62 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ | ||
2816 | 63 | Channel: "system", | ||
2817 | 64 | ExpireOn: future, | ||
2818 | 65 | Data: json.RawMessage(`{"img1/m1": 20}`), | ||
2819 | 66 | }) | ||
2820 | 67 | c.Assert(err, IsNil) | ||
2821 | 68 | c.Assert(got, Matches, ".*ok.*") | ||
2822 | 69 | // xxx don't send this one | ||
2823 | 70 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[]`) | ||
2824 | 71 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":20}]`) | ||
2825 | 49 | stop() | 72 | stop() |
2826 | 50 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 73 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2827 | 51 | c.Check(len(errCh), Equals, 0) | 74 | c.Check(len(errCh), Equals, 0) |
2828 | @@ -56,14 +79,14 @@ | |||
2829 | 56 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 79 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2830 | 57 | Channel: "system", | 80 | Channel: "system", |
2831 | 58 | ExpireOn: future, | 81 | ExpireOn: future, |
2833 | 59 | Data: json.RawMessage(`{"b": 1}`), | 82 | Data: json.RawMessage(`{"img1/m1": 1}`), |
2834 | 60 | }) | 83 | }) |
2835 | 61 | c.Assert(err, IsNil) | 84 | c.Assert(err, IsNil) |
2836 | 62 | c.Assert(got, Matches, ".*ok.*") | 85 | c.Assert(got, Matches, ".*ok.*") |
2837 | 63 | 86 | ||
2838 | 64 | events, errCh, stop := s.StartClient(c, "DEVB", nil) | 87 | events, errCh, stop := s.StartClient(c, "DEVB", nil) |
2839 | 65 | // gettting pending on connect | 88 | // gettting pending on connect |
2841 | 66 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"b":1}]`) | 89 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`) |
2842 | 67 | stop() | 90 | stop() |
2843 | 68 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 91 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2844 | 69 | c.Check(len(errCh), Equals, 0) | 92 | c.Check(len(errCh), Equals, 0) |
2845 | @@ -71,7 +94,7 @@ | |||
2846 | 71 | 94 | ||
2847 | 72 | func (s *BroadcastAcceptanceSuite) TestBroadcasLargeNeedsSplitting(c *C) { | 95 | func (s *BroadcastAcceptanceSuite) TestBroadcasLargeNeedsSplitting(c *C) { |
2848 | 73 | // send bunch of broadcasts that will be pending | 96 | // send bunch of broadcasts that will be pending |
2850 | 74 | payloadFmt := fmt.Sprintf(`{"b":%%d,"bloat":"%s"}`, strings.Repeat("x", 1024*2)) | 97 | payloadFmt := fmt.Sprintf(`{"img1/m1":%%d,"bloat":"%s"}`, strings.Repeat("x", 1024*2)) |
2851 | 75 | for i := 0; i < 32; i++ { | 98 | for i := 0; i < 32; i++ { |
2852 | 76 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 99 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2853 | 77 | Channel: "system", | 100 | Channel: "system", |
2854 | @@ -84,7 +107,7 @@ | |||
2855 | 84 | 107 | ||
2856 | 85 | events, errCh, stop := s.StartClient(c, "DEVC", nil) | 108 | events, errCh, stop := s.StartClient(c, "DEVC", nil) |
2857 | 86 | // gettting pending on connect | 109 | // gettting pending on connect |
2859 | 87 | c.Check(NextEvent(events, errCh), Matches, `broadcast chan:0 app: topLevel:30 payloads:\[{"b":0,.*`) | 110 | c.Check(NextEvent(events, errCh), Matches, `broadcast chan:0 app: topLevel:30 payloads:\[{"img1/m1":0,.*`) |
2860 | 88 | c.Check(NextEvent(events, errCh), Matches, `broadcast chan:0 app: topLevel:32 payloads:\[.*`) | 111 | c.Check(NextEvent(events, errCh), Matches, `broadcast chan:0 app: topLevel:32 payloads:\[.*`) |
2861 | 89 | stop() | 112 | stop() |
2862 | 90 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 113 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2863 | @@ -100,12 +123,12 @@ | |||
2864 | 100 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 123 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2865 | 101 | Channel: "system", | 124 | Channel: "system", |
2866 | 102 | ExpireOn: future, | 125 | ExpireOn: future, |
2868 | 103 | Data: json.RawMessage(`{"n": 42}`), | 126 | Data: json.RawMessage(`{"img1/m1": 42}`), |
2869 | 104 | }) | 127 | }) |
2870 | 105 | c.Assert(err, IsNil) | 128 | c.Assert(err, IsNil) |
2871 | 106 | c.Assert(got, Matches, ".*ok.*") | 129 | c.Assert(got, Matches, ".*ok.*") |
2874 | 107 | c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"n":42}]`) | 130 | c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
2875 | 108 | c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"n":42}]`) | 131 | c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
2876 | 109 | stop1() | 132 | stop1() |
2877 | 110 | stop2() | 133 | stop2() |
2878 | 111 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 134 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2879 | @@ -119,11 +142,11 @@ | |||
2880 | 119 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 142 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2881 | 120 | Channel: "system", | 143 | Channel: "system", |
2882 | 121 | ExpireOn: future, | 144 | ExpireOn: future, |
2884 | 122 | Data: json.RawMessage(`{"b": 1}`), | 145 | Data: json.RawMessage(`{"img1/m1": 1}`), |
2885 | 123 | }) | 146 | }) |
2886 | 124 | c.Assert(err, IsNil) | 147 | c.Assert(err, IsNil) |
2887 | 125 | c.Assert(got, Matches, ".*ok.*") | 148 | c.Assert(got, Matches, ".*ok.*") |
2889 | 126 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"b":1}]`) | 149 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`) |
2890 | 127 | stop() | 150 | stop() |
2891 | 128 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 151 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2892 | 129 | c.Check(len(errCh), Equals, 0) | 152 | c.Check(len(errCh), Equals, 0) |
2893 | @@ -131,7 +154,7 @@ | |||
2894 | 131 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ | 154 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
2895 | 132 | Channel: "system", | 155 | Channel: "system", |
2896 | 133 | ExpireOn: future, | 156 | ExpireOn: future, |
2898 | 134 | Data: json.RawMessage(`{"b": 2}`), | 157 | Data: json.RawMessage(`{"img1/m1": 2}`), |
2899 | 135 | }) | 158 | }) |
2900 | 136 | c.Assert(err, IsNil) | 159 | c.Assert(err, IsNil) |
2901 | 137 | c.Assert(got, Matches, ".*ok.*") | 160 | c.Assert(got, Matches, ".*ok.*") |
2902 | @@ -139,7 +162,7 @@ | |||
2903 | 139 | events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{ | 162 | events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{ |
2904 | 140 | protocol.SystemChannelId: 1, | 163 | protocol.SystemChannelId: 1, |
2905 | 141 | }) | 164 | }) |
2907 | 142 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"b":2}]`) | 165 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":2}]`) |
2908 | 143 | stop() | 166 | stop() |
2909 | 144 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 167 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2910 | 145 | c.Check(len(errCh), Equals, 0) | 168 | c.Check(len(errCh), Equals, 0) |
2911 | @@ -150,14 +173,14 @@ | |||
2912 | 150 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 173 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2913 | 151 | Channel: "system", | 174 | Channel: "system", |
2914 | 152 | ExpireOn: future, | 175 | ExpireOn: future, |
2916 | 153 | Data: json.RawMessage(`{"b": 1}`), | 176 | Data: json.RawMessage(`{"img1/m1": 1}`), |
2917 | 154 | }) | 177 | }) |
2918 | 155 | c.Assert(err, IsNil) | 178 | c.Assert(err, IsNil) |
2919 | 156 | c.Assert(got, Matches, ".*ok.*") | 179 | c.Assert(got, Matches, ".*ok.*") |
2920 | 157 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ | 180 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
2921 | 158 | Channel: "system", | 181 | Channel: "system", |
2922 | 159 | ExpireOn: future, | 182 | ExpireOn: future, |
2924 | 160 | Data: json.RawMessage(`{"b": 2}`), | 183 | Data: json.RawMessage(`{"img1/m1": 2}`), |
2925 | 161 | }) | 184 | }) |
2926 | 162 | c.Assert(err, IsNil) | 185 | c.Assert(err, IsNil) |
2927 | 163 | c.Assert(got, Matches, ".*ok.*") | 186 | c.Assert(got, Matches, ".*ok.*") |
2928 | @@ -166,7 +189,7 @@ | |||
2929 | 166 | protocol.SystemChannelId: 10, | 189 | protocol.SystemChannelId: 10, |
2930 | 167 | }) | 190 | }) |
2931 | 168 | // gettting last one pending on connect | 191 | // gettting last one pending on connect |
2933 | 169 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"b":2}]`) | 192 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":2}]`) |
2934 | 170 | stop() | 193 | stop() |
2935 | 171 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 194 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2936 | 172 | c.Check(len(errCh), Equals, 0) | 195 | c.Check(len(errCh), Equals, 0) |
2937 | @@ -189,14 +212,14 @@ | |||
2938 | 189 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 212 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2939 | 190 | Channel: "system", | 213 | Channel: "system", |
2940 | 191 | ExpireOn: future, | 214 | ExpireOn: future, |
2942 | 192 | Data: json.RawMessage(`{"b": 1}`), | 215 | Data: json.RawMessage(`{"img1/m1": 1}`), |
2943 | 193 | }) | 216 | }) |
2944 | 194 | c.Assert(err, IsNil) | 217 | c.Assert(err, IsNil) |
2945 | 195 | c.Assert(got, Matches, ".*ok.*") | 218 | c.Assert(got, Matches, ".*ok.*") |
2946 | 196 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ | 219 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
2947 | 197 | Channel: "system", | 220 | Channel: "system", |
2948 | 198 | ExpireOn: future, | 221 | ExpireOn: future, |
2950 | 199 | Data: json.RawMessage(`{"b": 2}`), | 222 | Data: json.RawMessage(`{"img1/m1": 2}`), |
2951 | 200 | }) | 223 | }) |
2952 | 201 | c.Assert(err, IsNil) | 224 | c.Assert(err, IsNil) |
2953 | 202 | c.Assert(got, Matches, ".*ok.*") | 225 | c.Assert(got, Matches, ".*ok.*") |
2954 | @@ -205,7 +228,7 @@ | |||
2955 | 205 | protocol.SystemChannelId: -10, | 228 | protocol.SystemChannelId: -10, |
2956 | 206 | }) | 229 | }) |
2957 | 207 | // gettting pending on connect | 230 | // gettting pending on connect |
2959 | 208 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"b":1},{"b":2}]`) | 231 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":1},{"img1/m1":2}]`) |
2960 | 209 | stop() | 232 | stop() |
2961 | 210 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 233 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2962 | 211 | c.Check(len(errCh), Equals, 0) | 234 | c.Check(len(errCh), Equals, 0) |
2963 | @@ -216,14 +239,14 @@ | |||
2964 | 216 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ | 239 | got, err := s.PostRequest("/broadcast", &api.Broadcast{ |
2965 | 217 | Channel: "system", | 240 | Channel: "system", |
2966 | 218 | ExpireOn: future, | 241 | ExpireOn: future, |
2968 | 219 | Data: json.RawMessage(`{"b": 1}`), | 242 | Data: json.RawMessage(`{"img1/m1": 1}`), |
2969 | 220 | }) | 243 | }) |
2970 | 221 | c.Assert(err, IsNil) | 244 | c.Assert(err, IsNil) |
2971 | 222 | c.Assert(got, Matches, ".*ok.*") | 245 | c.Assert(got, Matches, ".*ok.*") |
2972 | 223 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ | 246 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
2973 | 224 | Channel: "system", | 247 | Channel: "system", |
2974 | 225 | ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339), | 248 | ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339), |
2976 | 226 | Data: json.RawMessage(`{"b": 2}`), | 249 | Data: json.RawMessage(`{"img1/m1": 2}`), |
2977 | 227 | }) | 250 | }) |
2978 | 228 | c.Assert(err, IsNil) | 251 | c.Assert(err, IsNil) |
2979 | 229 | c.Assert(got, Matches, ".*ok.*") | 252 | c.Assert(got, Matches, ".*ok.*") |
2980 | @@ -233,7 +256,7 @@ | |||
2981 | 233 | 256 | ||
2982 | 234 | events, errCh, stop := s.StartClient(c, "DEVB", nil) | 257 | events, errCh, stop := s.StartClient(c, "DEVB", nil) |
2983 | 235 | // gettting pending on connect | 258 | // gettting pending on connect |
2985 | 236 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"b":1}]`) | 259 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":1}]`) |
2986 | 237 | stop() | 260 | stop() |
2987 | 238 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) | 261 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2988 | 239 | c.Check(len(errCh), Equals, 0) | 262 | c.Check(len(errCh), Equals, 0) |
2989 | 240 | 263 | ||
2990 | === modified file 'server/acceptance/suites/helpers.go' | |||
2991 | --- server/acceptance/suites/helpers.go 2014-03-25 19:08:00 +0000 | |||
2992 | +++ server/acceptance/suites/helpers.go 2014-04-04 13:58:43 +0000 | |||
2993 | @@ -48,8 +48,8 @@ | |||
2994 | 48 | "session_queue_size": 10, | 48 | "session_queue_size": 10, |
2995 | 49 | "broker_queue_size": 100, | 49 | "broker_queue_size": 100, |
2996 | 50 | "addr": addr, | 50 | "addr": addr, |
2999 | 51 | "key_pem_file": helpers.SourceRelative("../config/testing.key"), | 51 | "key_pem_file": helpers.SourceRelative("../ssl/testing.key"), |
3000 | 52 | "cert_pem_file": helpers.SourceRelative("../config/testing.cert"), | 52 | "cert_pem_file": helpers.SourceRelative("../ssl/testing.cert"), |
3001 | 53 | }) | 53 | }) |
3002 | 54 | } | 54 | } |
3003 | 55 | 55 | ||
3004 | 56 | 56 | ||
3005 | === modified file 'server/acceptance/suites/pingpong.go' | |||
3006 | --- server/acceptance/suites/pingpong.go 2014-02-21 20:29:16 +0000 | |||
3007 | +++ server/acceptance/suites/pingpong.go 2014-04-04 13:58:43 +0000 | |||
3008 | @@ -34,7 +34,7 @@ | |||
3009 | 34 | func (s *PingPongAcceptanceSuite) TestConnectPingPing(c *C) { | 34 | func (s *PingPongAcceptanceSuite) TestConnectPingPing(c *C) { |
3010 | 35 | errCh := make(chan error, 1) | 35 | errCh := make(chan error, 1) |
3011 | 36 | events := make(chan string, 10) | 36 | events := make(chan string, 10) |
3013 | 37 | sess := testClientSession(s.ServerAddr, "DEVA", true) | 37 | sess := testClientSession(s.ServerAddr, "DEVA", "m1", "img1", true) |
3014 | 38 | err := sess.Dial() | 38 | err := sess.Dial() |
3015 | 39 | c.Assert(err, IsNil) | 39 | c.Assert(err, IsNil) |
3016 | 40 | intercept := func(ic *interceptingConn, op string, b []byte) (bool, int, error) { | 40 | intercept := func(ic *interceptingConn, op string, b []byte) (bool, int, error) { |
3017 | @@ -68,7 +68,7 @@ | |||
3018 | 68 | func (s *PingPongAcceptanceSuite) TestConnectPingNeverPong(c *C) { | 68 | func (s *PingPongAcceptanceSuite) TestConnectPingNeverPong(c *C) { |
3019 | 69 | errCh := make(chan error, 1) | 69 | errCh := make(chan error, 1) |
3020 | 70 | events := make(chan string, 10) | 70 | events := make(chan string, 10) |
3022 | 71 | sess := testClientSession(s.ServerAddr, "DEVB", true) | 71 | sess := testClientSession(s.ServerAddr, "DEVB", "m1", "img1", true) |
3023 | 72 | err := sess.Dial() | 72 | err := sess.Dial() |
3024 | 73 | c.Assert(err, IsNil) | 73 | c.Assert(err, IsNil) |
3025 | 74 | intercept := func(ic *interceptingConn, op string, b []byte) (bool, int, error) { | 74 | intercept := func(ic *interceptingConn, op string, b []byte) (bool, int, error) { |
3026 | 75 | 75 | ||
3027 | === modified file 'server/acceptance/suites/suite.go' | |||
3028 | --- server/acceptance/suites/suite.go 2014-03-25 19:08:00 +0000 | |||
3029 | +++ server/acceptance/suites/suite.go 2014-04-04 13:58:43 +0000 | |||
3030 | @@ -46,7 +46,7 @@ | |||
3031 | 46 | func (h *ServerHandle) StartClient(c *C, devId string, levels map[string]int64) (events <-chan string, errorCh <-chan error, stop func()) { | 46 | func (h *ServerHandle) StartClient(c *C, devId string, levels map[string]int64) (events <-chan string, errorCh <-chan error, stop func()) { |
3032 | 47 | errCh := make(chan error, 1) | 47 | errCh := make(chan error, 1) |
3033 | 48 | cliEvents := make(chan string, 10) | 48 | cliEvents := make(chan string, 10) |
3035 | 49 | sess := testClientSession(h.ServerAddr, devId, false) | 49 | sess := testClientSession(h.ServerAddr, devId, "m1", "img1", false) |
3036 | 50 | sess.Levels = levels | 50 | sess.Levels = levels |
3037 | 51 | err := sess.Dial() | 51 | err := sess.Dial() |
3038 | 52 | c.Assert(err, IsNil) | 52 | c.Assert(err, IsNil) |
3039 | @@ -127,16 +127,18 @@ | |||
3040 | 127 | return string(body), err | 127 | return string(body), err |
3041 | 128 | } | 128 | } |
3042 | 129 | 129 | ||
3045 | 130 | func testClientSession(addr string, deviceId string, reportPings bool) *acceptance.ClientSession { | 130 | func testClientSession(addr string, deviceId, model, imageChannel string, reportPings bool) *acceptance.ClientSession { |
3046 | 131 | certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../config/testing.cert")) | 131 | certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert")) |
3047 | 132 | if err != nil { | 132 | if err != nil { |
3049 | 133 | panic(fmt.Sprintf("could not read config/testing.cert: %v", err)) | 133 | panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err)) |
3050 | 134 | } | 134 | } |
3051 | 135 | return &acceptance.ClientSession{ | 135 | return &acceptance.ClientSession{ |
3052 | 136 | ExchangeTimeout: 100 * time.Millisecond, | 136 | ExchangeTimeout: 100 * time.Millisecond, |
3053 | 137 | ServerAddr: addr, | 137 | ServerAddr: addr, |
3054 | 138 | CertPEMBlock: certPEMBlock, | 138 | CertPEMBlock: certPEMBlock, |
3055 | 139 | DeviceId: deviceId, | 139 | DeviceId: deviceId, |
3056 | 140 | Model: model, | ||
3057 | 141 | ImageChannel: imageChannel, | ||
3058 | 140 | ReportPings: reportPings, | 142 | ReportPings: reportPings, |
3059 | 141 | } | 143 | } |
3060 | 142 | } | 144 | } |
3061 | 143 | 145 | ||
3062 | === modified file 'server/broker/broker.go' | |||
3063 | --- server/broker/broker.go 2014-02-21 16:04:44 +0000 | |||
3064 | +++ server/broker/broker.go 2014-04-04 13:58:43 +0000 | |||
3065 | @@ -49,6 +49,19 @@ | |||
3066 | 49 | // LevelsMap is the type for holding channel levels for session. | 49 | // LevelsMap is the type for holding channel levels for session. |
3067 | 50 | type LevelsMap map[store.InternalChannelId]int64 | 50 | type LevelsMap map[store.InternalChannelId]int64 |
3068 | 51 | 51 | ||
3069 | 52 | // GetInfoString helps retrivieng a string out of a protocol.ConnectMsg.Info | ||
3070 | 53 | func GetInfoString(msg *protocol.ConnectMsg, name, defaultVal string) (string, error) { | ||
3071 | 54 | v, ok := msg.Info[name] | ||
3072 | 55 | if !ok { | ||
3073 | 56 | return defaultVal, nil | ||
3074 | 57 | } | ||
3075 | 58 | s, ok := v.(string) | ||
3076 | 59 | if !ok { | ||
3077 | 60 | return "", ErrUnexpectedValue | ||
3078 | 61 | } | ||
3079 | 62 | return s, nil | ||
3080 | 63 | } | ||
3081 | 64 | |||
3082 | 52 | // BrokerSession holds broker session state. | 65 | // BrokerSession holds broker session state. |
3083 | 53 | type BrokerSession interface { | 66 | type BrokerSession interface { |
3084 | 54 | // SessionChannel returns the session control channel | 67 | // SessionChannel returns the session control channel |
3085 | @@ -56,6 +69,10 @@ | |||
3086 | 56 | SessionChannel() <-chan Exchange | 69 | SessionChannel() <-chan Exchange |
3087 | 57 | // DeviceIdentifier returns the device id string. | 70 | // DeviceIdentifier returns the device id string. |
3088 | 58 | DeviceIdentifier() string | 71 | DeviceIdentifier() string |
3089 | 72 | // DeviceImageModel returns the device model. | ||
3090 | 73 | DeviceImageModel() string | ||
3091 | 74 | // DeviceImageChannel returns the device system image channel. | ||
3092 | 75 | DeviceImageChannel() string | ||
3093 | 59 | // Levels returns the current channel levels for the session | 76 | // Levels returns the current channel levels for the session |
3094 | 60 | Levels() LevelsMap | 77 | Levels() LevelsMap |
3095 | 61 | // ExchangeScratchArea returns the scratch area for exchanges. | 78 | // ExchangeScratchArea returns the scratch area for exchanges. |
3096 | @@ -71,6 +88,9 @@ | |||
3097 | 71 | return fmt.Sprintf("session aborted (%s)", ea.Reason) | 88 | return fmt.Sprintf("session aborted (%s)", ea.Reason) |
3098 | 72 | } | 89 | } |
3099 | 73 | 90 | ||
3100 | 91 | // Unexpect value in message | ||
3101 | 92 | var ErrUnexpectedValue = &ErrAbort{"unexpected value in message"} | ||
3102 | 93 | |||
3103 | 74 | // BrokerConfig gives access to the typical broker configuration. | 94 | // BrokerConfig gives access to the typical broker configuration. |
3104 | 75 | type BrokerConfig interface { | 95 | type BrokerConfig interface { |
3105 | 76 | // SessionQueueSize gives the session queue size. | 96 | // SessionQueueSize gives the session queue size. |
3106 | 77 | 97 | ||
3107 | === modified file 'server/broker/broker_test.go' | |||
3108 | --- server/broker/broker_test.go 2014-02-10 23:19:08 +0000 | |||
3109 | +++ server/broker/broker_test.go 2014-04-04 13:58:43 +0000 | |||
3110 | @@ -20,6 +20,8 @@ | |||
3111 | 20 | "fmt" | 20 | "fmt" |
3112 | 21 | 21 | ||
3113 | 22 | . "launchpad.net/gocheck" | 22 | . "launchpad.net/gocheck" |
3114 | 23 | |||
3115 | 24 | "launchpad.net/ubuntu-push/protocol" | ||
3116 | 23 | ) | 25 | ) |
3117 | 24 | 26 | ||
3118 | 25 | type brokerSuite struct{} | 27 | type brokerSuite struct{} |
3119 | @@ -30,3 +32,19 @@ | |||
3120 | 30 | err := &ErrAbort{"expected FOO"} | 32 | err := &ErrAbort{"expected FOO"} |
3121 | 31 | c.Check(fmt.Sprintf("%s", err), Equals, "session aborted (expected FOO)") | 33 | c.Check(fmt.Sprintf("%s", err), Equals, "session aborted (expected FOO)") |
3122 | 32 | } | 34 | } |
3123 | 35 | |||
3124 | 36 | func (s *brokerSuite) TestGetInfoString(c *C) { | ||
3125 | 37 | connectMsg := &protocol.ConnectMsg{} | ||
3126 | 38 | v, err := GetInfoString(connectMsg, "foo", "?") | ||
3127 | 39 | c.Check(err, IsNil) | ||
3128 | 40 | c.Check(v, Equals, "?") | ||
3129 | 41 | |||
3130 | 42 | connectMsg.Info = map[string]interface{}{"foo": "yay"} | ||
3131 | 43 | v, err = GetInfoString(connectMsg, "foo", "?") | ||
3132 | 44 | c.Check(err, IsNil) | ||
3133 | 45 | c.Check(v, Equals, "yay") | ||
3134 | 46 | |||
3135 | 47 | connectMsg.Info["foo"] = 33 | ||
3136 | 48 | v, err = GetInfoString(connectMsg, "foo", "?") | ||
3137 | 49 | c.Check(err, Equals, ErrUnexpectedValue) | ||
3138 | 50 | } | ||
3139 | 33 | 51 | ||
3140 | === modified file 'server/broker/exchanges.go' | |||
3141 | --- server/broker/exchanges.go 2014-02-26 16:04:57 +0000 | |||
3142 | +++ server/broker/exchanges.go 2014-04-04 13:58:43 +0000 | |||
3143 | @@ -18,6 +18,7 @@ | |||
3144 | 18 | 18 | ||
3145 | 19 | import ( | 19 | import ( |
3146 | 20 | "encoding/json" | 20 | "encoding/json" |
3147 | 21 | "fmt" | ||
3148 | 21 | 22 | ||
3149 | 22 | "launchpad.net/ubuntu-push/protocol" | 23 | "launchpad.net/ubuntu-push/protocol" |
3150 | 23 | "launchpad.net/ubuntu-push/server/store" | 24 | "launchpad.net/ubuntu-push/server/store" |
3151 | @@ -37,11 +38,24 @@ | |||
3152 | 37 | ChanId store.InternalChannelId | 38 | ChanId store.InternalChannelId |
3153 | 38 | TopLevel int64 | 39 | TopLevel int64 |
3154 | 39 | NotificationPayloads []json.RawMessage | 40 | NotificationPayloads []json.RawMessage |
3155 | 41 | Decoded []map[string]interface{} | ||
3156 | 40 | } | 42 | } |
3157 | 41 | 43 | ||
3158 | 42 | // check interface already here | 44 | // check interface already here |
3159 | 43 | var _ Exchange = &BroadcastExchange{} | 45 | var _ Exchange = &BroadcastExchange{} |
3160 | 44 | 46 | ||
3161 | 47 | // Init ensures the BroadcastExchange is fully initialized for the sessions. | ||
3162 | 48 | func (sbe *BroadcastExchange) Init() { | ||
3163 | 49 | decoded := make([]map[string]interface{}, len(sbe.NotificationPayloads)) | ||
3164 | 50 | sbe.Decoded = decoded | ||
3165 | 51 | for i, p := range sbe.NotificationPayloads { | ||
3166 | 52 | err := json.Unmarshal(p, &decoded[i]) | ||
3167 | 53 | if err != nil { | ||
3168 | 54 | decoded[i] = nil | ||
3169 | 55 | } | ||
3170 | 56 | } | ||
3171 | 57 | } | ||
3172 | 58 | |||
3173 | 45 | func filterByLevel(clientLevel, topLevel int64, payloads []json.RawMessage) []json.RawMessage { | 59 | func filterByLevel(clientLevel, topLevel int64, payloads []json.RawMessage) []json.RawMessage { |
3174 | 46 | c := int64(len(payloads)) | 60 | c := int64(len(payloads)) |
3175 | 47 | if c == 0 { | 61 | if c == 0 { |
3176 | @@ -58,6 +72,20 @@ | |||
3177 | 58 | } | 72 | } |
3178 | 59 | } | 73 | } |
3179 | 60 | 74 | ||
3180 | 75 | func channelFilter(tag string, chanId store.InternalChannelId, payloads []json.RawMessage, decoded []map[string]interface{}) []json.RawMessage { | ||
3181 | 76 | if len(payloads) != 0 && chanId == store.SystemInternalChannelId { | ||
3182 | 77 | decoded := decoded[len(decoded)-len(payloads):] | ||
3183 | 78 | filtered := make([]json.RawMessage, 0) | ||
3184 | 79 | for i, decoded1 := range decoded { | ||
3185 | 80 | if _, ok := decoded1[tag]; ok { | ||
3186 | 81 | filtered = append(filtered, payloads[i]) | ||
3187 | 82 | } | ||
3188 | 83 | } | ||
3189 | 84 | payloads = filtered | ||
3190 | 85 | } | ||
3191 | 86 | return payloads | ||
3192 | 87 | } | ||
3193 | 88 | |||
3194 | 61 | // Prepare session for a BROADCAST. | 89 | // Prepare session for a BROADCAST. |
3195 | 62 | func (sbe *BroadcastExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) { | 90 | func (sbe *BroadcastExchange) Prepare(sess BrokerSession) (outMessage protocol.SplittableMsg, inMessage interface{}, err error) { |
3196 | 63 | scratchArea := sess.ExchangeScratchArea() | 91 | scratchArea := sess.ExchangeScratchArea() |
3197 | @@ -65,6 +93,9 @@ | |||
3198 | 65 | scratchArea.broadcastMsg.Type = "broadcast" | 93 | scratchArea.broadcastMsg.Type = "broadcast" |
3199 | 66 | clientLevel := sess.Levels()[sbe.ChanId] | 94 | clientLevel := sess.Levels()[sbe.ChanId] |
3200 | 67 | payloads := filterByLevel(clientLevel, sbe.TopLevel, sbe.NotificationPayloads) | 95 | payloads := filterByLevel(clientLevel, sbe.TopLevel, sbe.NotificationPayloads) |
3201 | 96 | tag := fmt.Sprintf("%s/%s", sess.DeviceImageChannel(), sess.DeviceImageModel()) | ||
3202 | 97 | payloads = channelFilter(tag, sbe.ChanId, payloads, sbe.Decoded) | ||
3203 | 98 | |||
3204 | 68 | // xxx need an AppId as well, later | 99 | // xxx need an AppId as well, later |
3205 | 69 | scratchArea.broadcastMsg.ChanId = store.InternalChannelIdToHex(sbe.ChanId) | 100 | scratchArea.broadcastMsg.ChanId = store.InternalChannelIdToHex(sbe.ChanId) |
3206 | 70 | scratchArea.broadcastMsg.TopLevel = sbe.TopLevel | 101 | scratchArea.broadcastMsg.TopLevel = sbe.TopLevel |
3207 | 71 | 102 | ||
3208 | === modified file 'server/broker/exchanges_test.go' | |||
3209 | --- server/broker/exchanges_test.go 2014-02-26 16:04:57 +0000 | |||
3210 | +++ server/broker/exchanges_test.go 2014-04-04 13:58:43 +0000 | |||
3211 | @@ -35,24 +35,45 @@ | |||
3212 | 35 | 35 | ||
3213 | 36 | var _ = Suite(&exchangesSuite{}) | 36 | var _ = Suite(&exchangesSuite{}) |
3214 | 37 | 37 | ||
3215 | 38 | func (s *exchangesSuite) TestBroadcastExchangeInit(c *C) { | ||
3216 | 39 | exchg := &broker.BroadcastExchange{ | ||
3217 | 40 | ChanId: store.SystemInternalChannelId, | ||
3218 | 41 | TopLevel: 3, | ||
3219 | 42 | NotificationPayloads: []json.RawMessage{ | ||
3220 | 43 | json.RawMessage(`{"a":"x"}`), | ||
3221 | 44 | json.RawMessage(`[]`), | ||
3222 | 45 | json.RawMessage(`{"a":"y"}`), | ||
3223 | 46 | }, | ||
3224 | 47 | } | ||
3225 | 48 | exchg.Init() | ||
3226 | 49 | c.Check(exchg.Decoded, DeepEquals, []map[string]interface{}{ | ||
3227 | 50 | map[string]interface{}{"a": "x"}, | ||
3228 | 51 | nil, | ||
3229 | 52 | map[string]interface{}{"a": "y"}, | ||
3230 | 53 | }) | ||
3231 | 54 | } | ||
3232 | 55 | |||
3233 | 38 | func (s *exchangesSuite) TestBroadcastExchange(c *C) { | 56 | func (s *exchangesSuite) TestBroadcastExchange(c *C) { |
3234 | 39 | sess := &testing.TestBrokerSession{ | 57 | sess := &testing.TestBrokerSession{ |
3236 | 40 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), | 58 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), |
3237 | 59 | Model: "m1", | ||
3238 | 60 | ImageChannel: "img1", | ||
3239 | 41 | } | 61 | } |
3240 | 42 | exchg := &broker.BroadcastExchange{ | 62 | exchg := &broker.BroadcastExchange{ |
3241 | 43 | ChanId: store.SystemInternalChannelId, | 63 | ChanId: store.SystemInternalChannelId, |
3242 | 44 | TopLevel: 3, | 64 | TopLevel: 3, |
3243 | 45 | NotificationPayloads: []json.RawMessage{ | 65 | NotificationPayloads: []json.RawMessage{ |
3246 | 46 | json.RawMessage(`{"a":"x"}`), | 66 | json.RawMessage(`{"img1/m1":100}`), |
3247 | 47 | json.RawMessage(`{"a":"y"}`), | 67 | json.RawMessage(`{"img2/m2":200}`), |
3248 | 48 | }, | 68 | }, |
3249 | 49 | } | 69 | } |
3250 | 70 | exchg.Init() | ||
3251 | 50 | outMsg, inMsg, err := exchg.Prepare(sess) | 71 | outMsg, inMsg, err := exchg.Prepare(sess) |
3252 | 51 | c.Assert(err, IsNil) | 72 | c.Assert(err, IsNil) |
3253 | 52 | // check | 73 | // check |
3254 | 53 | marshalled, err := json.Marshal(outMsg) | 74 | marshalled, err := json.Marshal(outMsg) |
3255 | 54 | c.Assert(err, IsNil) | 75 | c.Assert(err, IsNil) |
3257 | 55 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"a":"x"},{"a":"y"}]}`) | 76 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"img1/m1":100}]}`) |
3258 | 56 | err = json.Unmarshal([]byte(`{"T":"ack"}`), inMsg) | 77 | err = json.Unmarshal([]byte(`{"T":"ack"}`), inMsg) |
3259 | 57 | c.Assert(err, IsNil) | 78 | c.Assert(err, IsNil) |
3260 | 58 | err = exchg.Acked(sess, true) | 79 | err = exchg.Acked(sess, true) |
3261 | @@ -62,9 +83,11 @@ | |||
3262 | 62 | 83 | ||
3263 | 63 | func (s *exchangesSuite) TestBroadcastExchangeReuseVsSplit(c *C) { | 84 | func (s *exchangesSuite) TestBroadcastExchangeReuseVsSplit(c *C) { |
3264 | 64 | sess := &testing.TestBrokerSession{ | 85 | sess := &testing.TestBrokerSession{ |
3266 | 65 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), | 86 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), |
3267 | 87 | Model: "m1", | ||
3268 | 88 | ImageChannel: "img1", | ||
3269 | 66 | } | 89 | } |
3271 | 67 | payloadFmt := fmt.Sprintf(`{"b":%%d,"bloat":"%s"}`, strings.Repeat("x", 1024*2)) | 90 | payloadFmt := fmt.Sprintf(`{"img1/m1":%%d,"bloat":"%s"}`, strings.Repeat("x", 1024*2)) |
3272 | 68 | needsSplitting := make([]json.RawMessage, 32) | 91 | needsSplitting := make([]json.RawMessage, 32) |
3273 | 69 | for i := 0; i < 32; i++ { | 92 | for i := 0; i < 32; i++ { |
3274 | 70 | needsSplitting[i] = json.RawMessage(fmt.Sprintf(payloadFmt, i)) | 93 | needsSplitting[i] = json.RawMessage(fmt.Sprintf(payloadFmt, i)) |
3275 | @@ -76,6 +99,7 @@ | |||
3276 | 76 | TopLevel: topLevel, | 99 | TopLevel: topLevel, |
3277 | 77 | NotificationPayloads: needsSplitting, | 100 | NotificationPayloads: needsSplitting, |
3278 | 78 | } | 101 | } |
3279 | 102 | exchg.Init() | ||
3280 | 79 | outMsg, _, err := exchg.Prepare(sess) | 103 | outMsg, _, err := exchg.Prepare(sess) |
3281 | 80 | c.Assert(err, IsNil) | 104 | c.Assert(err, IsNil) |
3282 | 81 | parts := 0 | 105 | parts := 0 |
3283 | @@ -91,10 +115,11 @@ | |||
3284 | 91 | ChanId: store.SystemInternalChannelId, | 115 | ChanId: store.SystemInternalChannelId, |
3285 | 92 | TopLevel: topLevel + 2, | 116 | TopLevel: topLevel + 2, |
3286 | 93 | NotificationPayloads: []json.RawMessage{ | 117 | NotificationPayloads: []json.RawMessage{ |
3289 | 94 | json.RawMessage(`{"a":"x"}`), | 118 | json.RawMessage(`{"img1/m1":"x"}`), |
3290 | 95 | json.RawMessage(`{"a":"y"}`), | 119 | json.RawMessage(`{"img1/m1":"y"}`), |
3291 | 96 | }, | 120 | }, |
3292 | 97 | } | 121 | } |
3293 | 122 | exchg.Init() | ||
3294 | 98 | outMsg, _, err = exchg.Prepare(sess) | 123 | outMsg, _, err = exchg.Prepare(sess) |
3295 | 99 | c.Assert(err, IsNil) | 124 | c.Assert(err, IsNil) |
3296 | 100 | done := outMsg.Split() // shouldn't panic | 125 | done := outMsg.Split() // shouldn't panic |
3297 | @@ -103,21 +128,24 @@ | |||
3298 | 103 | 128 | ||
3299 | 104 | func (s *exchangesSuite) TestBroadcastExchangeAckMismatch(c *C) { | 129 | func (s *exchangesSuite) TestBroadcastExchangeAckMismatch(c *C) { |
3300 | 105 | sess := &testing.TestBrokerSession{ | 130 | sess := &testing.TestBrokerSession{ |
3302 | 106 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), | 131 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), |
3303 | 132 | Model: "m1", | ||
3304 | 133 | ImageChannel: "img2", | ||
3305 | 107 | } | 134 | } |
3306 | 108 | exchg := &broker.BroadcastExchange{ | 135 | exchg := &broker.BroadcastExchange{ |
3307 | 109 | ChanId: store.SystemInternalChannelId, | 136 | ChanId: store.SystemInternalChannelId, |
3308 | 110 | TopLevel: 3, | 137 | TopLevel: 3, |
3309 | 111 | NotificationPayloads: []json.RawMessage{ | 138 | NotificationPayloads: []json.RawMessage{ |
3311 | 112 | json.RawMessage(`{"a":"y"}`), | 139 | json.RawMessage(`{"img2/m1":1}`), |
3312 | 113 | }, | 140 | }, |
3313 | 114 | } | 141 | } |
3314 | 142 | exchg.Init() | ||
3315 | 115 | outMsg, inMsg, err := exchg.Prepare(sess) | 143 | outMsg, inMsg, err := exchg.Prepare(sess) |
3316 | 116 | c.Assert(err, IsNil) | 144 | c.Assert(err, IsNil) |
3317 | 117 | // check | 145 | // check |
3318 | 118 | marshalled, err := json.Marshal(outMsg) | 146 | marshalled, err := json.Marshal(outMsg) |
3319 | 119 | c.Assert(err, IsNil) | 147 | c.Assert(err, IsNil) |
3321 | 120 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"a":"y"}]}`) | 148 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"img2/m1":1}]}`) |
3322 | 121 | err = json.Unmarshal([]byte(`{}`), inMsg) | 149 | err = json.Unmarshal([]byte(`{}`), inMsg) |
3323 | 122 | c.Assert(err, IsNil) | 150 | c.Assert(err, IsNil) |
3324 | 123 | err = exchg.Acked(sess, true) | 151 | err = exchg.Acked(sess, true) |
3325 | @@ -130,23 +158,55 @@ | |||
3326 | 130 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{ | 158 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{ |
3327 | 131 | store.SystemInternalChannelId: 2, | 159 | store.SystemInternalChannelId: 2, |
3328 | 132 | }), | 160 | }), |
3329 | 161 | Model: "m1", | ||
3330 | 162 | ImageChannel: "img1", | ||
3331 | 133 | } | 163 | } |
3332 | 134 | exchg := &broker.BroadcastExchange{ | 164 | exchg := &broker.BroadcastExchange{ |
3333 | 135 | ChanId: store.SystemInternalChannelId, | 165 | ChanId: store.SystemInternalChannelId, |
3334 | 136 | TopLevel: 3, | 166 | TopLevel: 3, |
3335 | 137 | NotificationPayloads: []json.RawMessage{ | 167 | NotificationPayloads: []json.RawMessage{ |
3350 | 138 | json.RawMessage(`{"a":"x"}`), | 168 | json.RawMessage(`{"img1/m1":100}`), |
3351 | 139 | json.RawMessage(`{"a":"y"}`), | 169 | json.RawMessage(`{"img1/m1":101}`), |
3352 | 140 | }, | 170 | }, |
3353 | 141 | } | 171 | } |
3354 | 142 | outMsg, inMsg, err := exchg.Prepare(sess) | 172 | exchg.Init() |
3355 | 143 | c.Assert(err, IsNil) | 173 | outMsg, inMsg, err := exchg.Prepare(sess) |
3356 | 144 | // check | 174 | c.Assert(err, IsNil) |
3357 | 145 | marshalled, err := json.Marshal(outMsg) | 175 | // check |
3358 | 146 | c.Assert(err, IsNil) | 176 | marshalled, err := json.Marshal(outMsg) |
3359 | 147 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"a":"y"}]}`) | 177 | c.Assert(err, IsNil) |
3360 | 148 | err = json.Unmarshal([]byte(`{"T":"ack"}`), inMsg) | 178 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":3,"Payloads":[{"img1/m1":101}]}`) |
3361 | 149 | c.Assert(err, IsNil) | 179 | err = json.Unmarshal([]byte(`{"T":"ack"}`), inMsg) |
3362 | 150 | err = exchg.Acked(sess, true) | 180 | c.Assert(err, IsNil) |
3363 | 151 | c.Assert(err, IsNil) | 181 | err = exchg.Acked(sess, true) |
3364 | 182 | c.Assert(err, IsNil) | ||
3365 | 183 | } | ||
3366 | 184 | |||
3367 | 185 | func (s *exchangesSuite) TestBroadcastExchangeChannelFilter(c *C) { | ||
3368 | 186 | sess := &testing.TestBrokerSession{ | ||
3369 | 187 | LevelsMap: broker.LevelsMap(map[store.InternalChannelId]int64{}), | ||
3370 | 188 | Model: "m1", | ||
3371 | 189 | ImageChannel: "img1", | ||
3372 | 190 | } | ||
3373 | 191 | exchg := &broker.BroadcastExchange{ | ||
3374 | 192 | ChanId: store.SystemInternalChannelId, | ||
3375 | 193 | TopLevel: 5, | ||
3376 | 194 | NotificationPayloads: []json.RawMessage{ | ||
3377 | 195 | json.RawMessage(`{"img1/m1":100}`), | ||
3378 | 196 | json.RawMessage(`{"img2/m2":200}`), | ||
3379 | 197 | json.RawMessage(`{"img1/m1":101}`), | ||
3380 | 198 | }, | ||
3381 | 199 | } | ||
3382 | 200 | exchg.Init() | ||
3383 | 201 | outMsg, inMsg, err := exchg.Prepare(sess) | ||
3384 | 202 | c.Assert(err, IsNil) | ||
3385 | 203 | // check | ||
3386 | 204 | marshalled, err := json.Marshal(outMsg) | ||
3387 | 205 | c.Assert(err, IsNil) | ||
3388 | 206 | c.Check(string(marshalled), Equals, `{"T":"broadcast","ChanId":"0","TopLevel":5,"Payloads":[{"img1/m1":100},{"img1/m1":101}]}`) | ||
3389 | 207 | err = json.Unmarshal([]byte(`{"T":"ack"}`), inMsg) | ||
3390 | 208 | c.Assert(err, IsNil) | ||
3391 | 209 | err = exchg.Acked(sess, true) | ||
3392 | 210 | c.Assert(err, IsNil) | ||
3393 | 211 | c.Check(sess.LevelsMap[store.SystemInternalChannelId], Equals, int64(5)) | ||
3394 | 152 | } | 212 | } |
3395 | 153 | 213 | ||
3396 | === modified file 'server/broker/exchg_impl_test.go' | |||
3397 | --- server/broker/exchg_impl_test.go 2014-02-10 23:19:08 +0000 | |||
3398 | +++ server/broker/exchg_impl_test.go 2014-04-04 13:58:43 +0000 | |||
3399 | @@ -20,6 +20,8 @@ | |||
3400 | 20 | "encoding/json" | 20 | "encoding/json" |
3401 | 21 | 21 | ||
3402 | 22 | . "launchpad.net/gocheck" | 22 | . "launchpad.net/gocheck" |
3403 | 23 | |||
3404 | 24 | "launchpad.net/ubuntu-push/server/store" | ||
3405 | 23 | ) | 25 | ) |
3406 | 24 | 26 | ||
3407 | 25 | type exchangesImplSuite struct{} | 27 | type exchangesImplSuite struct{} |
3408 | @@ -56,3 +58,31 @@ | |||
3409 | 56 | res = filterByLevel(5, 10, nil) | 58 | res = filterByLevel(5, 10, nil) |
3410 | 57 | c.Check(len(res), Equals, 0) | 59 | c.Check(len(res), Equals, 0) |
3411 | 58 | } | 60 | } |
3412 | 61 | |||
3413 | 62 | func (s *exchangesImplSuite) TestChannelFilter(c *C) { | ||
3414 | 63 | payloads := []json.RawMessage{ | ||
3415 | 64 | json.RawMessage(`{"a/x": 3}`), | ||
3416 | 65 | json.RawMessage(`{"b/x": 4}`), | ||
3417 | 66 | json.RawMessage(`{"a/y": 5}`), | ||
3418 | 67 | json.RawMessage(`{"a/x": 6}`), | ||
3419 | 68 | } | ||
3420 | 69 | decoded := make([]map[string]interface{}, 4) | ||
3421 | 70 | for i, p := range payloads { | ||
3422 | 71 | err := json.Unmarshal(p, &decoded[i]) | ||
3423 | 72 | c.Assert(err, IsNil) | ||
3424 | 73 | } | ||
3425 | 74 | |||
3426 | 75 | other := store.InternalChannelId("1") | ||
3427 | 76 | |||
3428 | 77 | c.Check(channelFilter("", store.SystemInternalChannelId, nil, nil), IsNil) | ||
3429 | 78 | c.Check(channelFilter("", other, payloads[1:], decoded), DeepEquals, payloads[1:]) | ||
3430 | 79 | |||
3431 | 80 | // use tag when channel is the sytem channel | ||
3432 | 81 | |||
3433 | 82 | c.Check(channelFilter("c/z", store.SystemInternalChannelId, payloads, decoded), HasLen, 0) | ||
3434 | 83 | |||
3435 | 84 | c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads, decoded), DeepEquals, []json.RawMessage{payloads[0], payloads[3]}) | ||
3436 | 85 | |||
3437 | 86 | c.Check(channelFilter("a/x", store.SystemInternalChannelId, payloads[1:], decoded), DeepEquals, []json.RawMessage{payloads[3]}) | ||
3438 | 87 | |||
3439 | 88 | } | ||
3440 | 59 | 89 | ||
3441 | === modified file 'server/broker/simple/simple.go' | |||
3442 | --- server/broker/simple/simple.go 2014-02-21 16:04:44 +0000 | |||
3443 | +++ server/broker/simple/simple.go 2014-04-04 13:58:43 +0000 | |||
3444 | @@ -46,11 +46,13 @@ | |||
3445 | 46 | 46 | ||
3446 | 47 | // simpleBrokerSession represents a session in the broker. | 47 | // simpleBrokerSession represents a session in the broker. |
3447 | 48 | type simpleBrokerSession struct { | 48 | type simpleBrokerSession struct { |
3453 | 49 | registered bool | 49 | registered bool |
3454 | 50 | deviceId string | 50 | deviceId string |
3455 | 51 | done chan bool | 51 | model string |
3456 | 52 | exchanges chan broker.Exchange | 52 | imageChannel string |
3457 | 53 | levels broker.LevelsMap | 53 | done chan bool |
3458 | 54 | exchanges chan broker.Exchange | ||
3459 | 55 | levels broker.LevelsMap | ||
3460 | 54 | // for exchanges | 56 | // for exchanges |
3461 | 55 | exchgScratch broker.ExchangesScratchArea | 57 | exchgScratch broker.ExchangesScratchArea |
3462 | 56 | } | 58 | } |
3463 | @@ -75,6 +77,14 @@ | |||
3464 | 75 | return sess.deviceId | 77 | return sess.deviceId |
3465 | 76 | } | 78 | } |
3466 | 77 | 79 | ||
3467 | 80 | func (sess *simpleBrokerSession) DeviceImageModel() string { | ||
3468 | 81 | return sess.model | ||
3469 | 82 | } | ||
3470 | 83 | |||
3471 | 84 | func (sess *simpleBrokerSession) DeviceImageChannel() string { | ||
3472 | 85 | return sess.imageChannel | ||
3473 | 86 | } | ||
3474 | 87 | |||
3475 | 78 | func (sess *simpleBrokerSession) Levels() broker.LevelsMap { | 88 | func (sess *simpleBrokerSession) Levels() broker.LevelsMap { |
3476 | 79 | return sess.levels | 89 | return sess.levels |
3477 | 80 | } | 90 | } |
3478 | @@ -147,6 +157,7 @@ | |||
3479 | 147 | TopLevel: topLevel, | 157 | TopLevel: topLevel, |
3480 | 148 | NotificationPayloads: payloads, | 158 | NotificationPayloads: payloads, |
3481 | 149 | } | 159 | } |
3482 | 160 | broadcastExchg.Init() | ||
3483 | 150 | sess.exchanges <- broadcastExchg | 161 | sess.exchanges <- broadcastExchg |
3484 | 151 | } | 162 | } |
3485 | 152 | } | 163 | } |
3486 | @@ -157,6 +168,14 @@ | |||
3487 | 157 | // pending notifications as well. | 168 | // pending notifications as well. |
3488 | 158 | func (b *SimpleBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) { | 169 | func (b *SimpleBroker) Register(connect *protocol.ConnectMsg) (broker.BrokerSession, error) { |
3489 | 159 | // xxx sanity check DeviceId | 170 | // xxx sanity check DeviceId |
3490 | 171 | model, err := broker.GetInfoString(connect, "device", "?") | ||
3491 | 172 | if err != nil { | ||
3492 | 173 | return nil, err | ||
3493 | 174 | } | ||
3494 | 175 | imageChannel, err := broker.GetInfoString(connect, "channel", "?") | ||
3495 | 176 | if err != nil { | ||
3496 | 177 | return nil, err | ||
3497 | 178 | } | ||
3498 | 160 | levels := map[store.InternalChannelId]int64{} | 179 | levels := map[store.InternalChannelId]int64{} |
3499 | 161 | for hexId, v := range connect.Levels { | 180 | for hexId, v := range connect.Levels { |
3500 | 162 | id, err := store.HexToInternalChannelId(hexId) | 181 | id, err := store.HexToInternalChannelId(hexId) |
3501 | @@ -166,14 +185,16 @@ | |||
3502 | 166 | levels[id] = v | 185 | levels[id] = v |
3503 | 167 | } | 186 | } |
3504 | 168 | sess := &simpleBrokerSession{ | 187 | sess := &simpleBrokerSession{ |
3509 | 169 | deviceId: connect.DeviceId, | 188 | deviceId: connect.DeviceId, |
3510 | 170 | done: make(chan bool), | 189 | model: model, |
3511 | 171 | exchanges: make(chan broker.Exchange, b.sessionQueueSize), | 190 | imageChannel: imageChannel, |
3512 | 172 | levels: levels, | 191 | done: make(chan bool), |
3513 | 192 | exchanges: make(chan broker.Exchange, b.sessionQueueSize), | ||
3514 | 193 | levels: levels, | ||
3515 | 173 | } | 194 | } |
3516 | 174 | b.sessionCh <- sess | 195 | b.sessionCh <- sess |
3517 | 175 | <-sess.done | 196 | <-sess.done |
3519 | 176 | err := b.feedPending(sess) | 197 | err = b.feedPending(sess) |
3520 | 177 | if err != nil { | 198 | if err != nil { |
3521 | 178 | return nil, err | 199 | return nil, err |
3522 | 179 | } | 200 | } |
3523 | @@ -219,6 +240,7 @@ | |||
3524 | 219 | TopLevel: topLevel, | 240 | TopLevel: topLevel, |
3525 | 220 | NotificationPayloads: payloads, | 241 | NotificationPayloads: payloads, |
3526 | 221 | } | 242 | } |
3527 | 243 | broadcastExchg.Init() | ||
3528 | 222 | for _, sess := range b.registry { | 244 | for _, sess := range b.registry { |
3529 | 223 | sess.exchanges <- broadcastExchg | 245 | sess.exchanges <- broadcastExchg |
3530 | 224 | } | 246 | } |
3531 | 225 | 247 | ||
3532 | === modified file 'server/broker/simple/simple_test.go' | |||
3533 | --- server/broker/simple/simple_test.go 2014-02-10 23:29:53 +0000 | |||
3534 | +++ server/broker/simple/simple_test.go 2014-04-04 13:58:43 +0000 | |||
3535 | @@ -48,6 +48,7 @@ | |||
3536 | 48 | sto := store.NewInMemoryPendingStore() | 48 | sto := store.NewInMemoryPendingStore() |
3537 | 49 | muchLater := time.Now().Add(10 * time.Minute) | 49 | muchLater := time.Now().Add(10 * time.Minute) |
3538 | 50 | notification1 := json.RawMessage(`{"m": "M"}`) | 50 | notification1 := json.RawMessage(`{"m": "M"}`) |
3539 | 51 | decoded1 := map[string]interface{}{"m": "M"} | ||
3540 | 51 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) | 52 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) |
3541 | 52 | b := NewSimpleBroker(sto, testBrokerConfig, nil) | 53 | b := NewSimpleBroker(sto, testBrokerConfig, nil) |
3542 | 53 | sess := &simpleBrokerSession{ | 54 | sess := &simpleBrokerSession{ |
3543 | @@ -60,6 +61,7 @@ | |||
3544 | 60 | ChanId: store.SystemInternalChannelId, | 61 | ChanId: store.SystemInternalChannelId, |
3545 | 61 | TopLevel: 1, | 62 | TopLevel: 1, |
3546 | 62 | NotificationPayloads: []json.RawMessage{notification1}, | 63 | NotificationPayloads: []json.RawMessage{notification1}, |
3547 | 64 | Decoded: []map[string]interface{}{decoded1}, | ||
3548 | 63 | }) | 65 | }) |
3549 | 64 | } | 66 | } |
3550 | 65 | 67 | ||
3551 | 66 | 68 | ||
3552 | === modified file 'server/broker/testing/impls.go' | |||
3553 | --- server/broker/testing/impls.go 2014-01-23 20:13:22 +0000 | |||
3554 | +++ server/broker/testing/impls.go 2014-04-04 13:58:43 +0000 | |||
3555 | @@ -24,6 +24,8 @@ | |||
3556 | 24 | // Test implementation of BrokerSession. | 24 | // Test implementation of BrokerSession. |
3557 | 25 | type TestBrokerSession struct { | 25 | type TestBrokerSession struct { |
3558 | 26 | DeviceId string | 26 | DeviceId string |
3559 | 27 | Model string | ||
3560 | 28 | ImageChannel string | ||
3561 | 27 | Exchanges chan broker.Exchange | 29 | Exchanges chan broker.Exchange |
3562 | 28 | LevelsMap broker.LevelsMap | 30 | LevelsMap broker.LevelsMap |
3563 | 29 | exchgScratch broker.ExchangesScratchArea | 31 | exchgScratch broker.ExchangesScratchArea |
3564 | @@ -33,6 +35,14 @@ | |||
3565 | 33 | return tbs.DeviceId | 35 | return tbs.DeviceId |
3566 | 34 | } | 36 | } |
3567 | 35 | 37 | ||
3568 | 38 | func (tbs *TestBrokerSession) DeviceImageModel() string { | ||
3569 | 39 | return tbs.Model | ||
3570 | 40 | } | ||
3571 | 41 | |||
3572 | 42 | func (tbs *TestBrokerSession) DeviceImageChannel() string { | ||
3573 | 43 | return tbs.ImageChannel | ||
3574 | 44 | } | ||
3575 | 45 | |||
3576 | 36 | func (tbs *TestBrokerSession) SessionChannel() <-chan broker.Exchange { | 46 | func (tbs *TestBrokerSession) SessionChannel() <-chan broker.Exchange { |
3577 | 37 | return tbs.Exchanges | 47 | return tbs.Exchanges |
3578 | 38 | } | 48 | } |
3579 | 39 | 49 | ||
3580 | === modified file 'server/broker/testsuite/suite.go' | |||
3581 | --- server/broker/testsuite/suite.go 2014-03-19 23:46:18 +0000 | |||
3582 | +++ server/broker/testsuite/suite.go 2014-04-04 13:58:43 +0000 | |||
3583 | @@ -81,10 +81,20 @@ | |||
3584 | 81 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 81 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
3585 | 82 | b.Start() | 82 | b.Start() |
3586 | 83 | defer b.Stop() | 83 | defer b.Stop() |
3588 | 84 | sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"0": 5}}) | 84 | sess, err := b.Register(&protocol.ConnectMsg{ |
3589 | 85 | Type: "connect", | ||
3590 | 86 | DeviceId: "dev-1", | ||
3591 | 87 | Levels: map[string]int64{"0": 5}, | ||
3592 | 88 | Info: map[string]interface{}{ | ||
3593 | 89 | "device": "model", | ||
3594 | 90 | "channel": "daily", | ||
3595 | 91 | }, | ||
3596 | 92 | }) | ||
3597 | 85 | c.Assert(err, IsNil) | 93 | c.Assert(err, IsNil) |
3598 | 86 | c.Assert(s.RevealSession(b, "dev-1"), Equals, sess) | 94 | c.Assert(s.RevealSession(b, "dev-1"), Equals, sess) |
3599 | 87 | c.Assert(sess.DeviceIdentifier(), Equals, "dev-1") | 95 | c.Assert(sess.DeviceIdentifier(), Equals, "dev-1") |
3600 | 96 | c.Check(sess.DeviceImageModel(), Equals, "model") | ||
3601 | 97 | c.Check(sess.DeviceImageChannel(), Equals, "daily") | ||
3602 | 88 | c.Assert(sess.ExchangeScratchArea(), Not(IsNil)) | 98 | c.Assert(sess.ExchangeScratchArea(), Not(IsNil)) |
3603 | 89 | c.Check(sess.Levels(), DeepEquals, broker.LevelsMap(map[store.InternalChannelId]int64{ | 99 | c.Check(sess.Levels(), DeepEquals, broker.LevelsMap(map[store.InternalChannelId]int64{ |
3604 | 90 | store.SystemInternalChannelId: 5, | 100 | store.SystemInternalChannelId: 5, |
3605 | @@ -105,6 +115,22 @@ | |||
3606 | 105 | c.Check(err, FitsTypeOf, &broker.ErrAbort{}) | 115 | c.Check(err, FitsTypeOf, &broker.ErrAbort{}) |
3607 | 106 | } | 116 | } |
3608 | 107 | 117 | ||
3609 | 118 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { | ||
3610 | 119 | sto := store.NewInMemoryPendingStore() | ||
3611 | 120 | b := s.MakeBroker(sto, testBrokerConfig, nil) | ||
3612 | 121 | b.Start() | ||
3613 | 122 | defer b.Stop() | ||
3614 | 123 | info := map[string]interface{}{ | ||
3615 | 124 | "device": -1, | ||
3616 | 125 | } | ||
3617 | 126 | _, err := b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}) | ||
3618 | 127 | c.Check(err, Equals, broker.ErrUnexpectedValue) | ||
3619 | 128 | info["device"] = "m" | ||
3620 | 129 | info["channel"] = -1 | ||
3621 | 130 | _, err = b.Register(&protocol.ConnectMsg{Type: "connect", Info: info}) | ||
3622 | 131 | c.Check(err, Equals, broker.ErrUnexpectedValue) | ||
3623 | 132 | } | ||
3624 | 133 | |||
3625 | 108 | func (s *CommonBrokerSuite) TestRegistrationFeedPending(c *C) { | 134 | func (s *CommonBrokerSuite) TestRegistrationFeedPending(c *C) { |
3626 | 109 | sto := store.NewInMemoryPendingStore() | 135 | sto := store.NewInMemoryPendingStore() |
3627 | 110 | notification1 := json.RawMessage(`{"m": "M"}`) | 136 | notification1 := json.RawMessage(`{"m": "M"}`) |
3628 | @@ -149,6 +175,7 @@ | |||
3629 | 149 | func (s *CommonBrokerSuite) TestBroadcast(c *C) { | 175 | func (s *CommonBrokerSuite) TestBroadcast(c *C) { |
3630 | 150 | sto := store.NewInMemoryPendingStore() | 176 | sto := store.NewInMemoryPendingStore() |
3631 | 151 | notification1 := json.RawMessage(`{"m": "M"}`) | 177 | notification1 := json.RawMessage(`{"m": "M"}`) |
3632 | 178 | decoded1 := map[string]interface{}{"m": "M"} | ||
3633 | 152 | b := s.MakeBroker(sto, testBrokerConfig, nil) | 179 | b := s.MakeBroker(sto, testBrokerConfig, nil) |
3634 | 153 | b.Start() | 180 | b.Start() |
3635 | 154 | defer b.Stop() | 181 | defer b.Stop() |
3636 | @@ -168,6 +195,7 @@ | |||
3637 | 168 | ChanId: store.SystemInternalChannelId, | 195 | ChanId: store.SystemInternalChannelId, |
3638 | 169 | TopLevel: 1, | 196 | TopLevel: 1, |
3639 | 170 | NotificationPayloads: []json.RawMessage{notification1}, | 197 | NotificationPayloads: []json.RawMessage{notification1}, |
3640 | 198 | Decoded: []map[string]interface{}{decoded1}, | ||
3641 | 171 | }) | 199 | }) |
3642 | 172 | } | 200 | } |
3643 | 173 | select { | 201 | select { |
3644 | @@ -178,6 +206,7 @@ | |||
3645 | 178 | ChanId: store.SystemInternalChannelId, | 206 | ChanId: store.SystemInternalChannelId, |
3646 | 179 | TopLevel: 1, | 207 | TopLevel: 1, |
3647 | 180 | NotificationPayloads: []json.RawMessage{notification1}, | 208 | NotificationPayloads: []json.RawMessage{notification1}, |
3648 | 209 | Decoded: []map[string]interface{}{decoded1}, | ||
3649 | 181 | }) | 210 | }) |
3650 | 182 | } | 211 | } |
3651 | 183 | } | 212 | } |