Merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic
- try-hosts
- Merge into automatic
Status: | Merged |
---|---|
Approved by: | Samuele Pedroni |
Approved revision: | 100 |
Merged at revision: | 96 |
Proposed branch: | lp:~pedronis/ubuntu-push/try-hosts |
Merge into: | lp:ubuntu-push/automatic |
Diff against target: |
613 lines (+377/-40) 2 files modified
client/session/session.go (+125/-26) client/session/session_test.go (+252/-14) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/try-hosts |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John Lenton (community) | Approve | ||
Review via email: mp+213153@code.launchpad.net |
This proposal supersedes a proposal from 2014-03-27.
Commit message
retrieves a list of hosts to try to connect to from a url (optionally for now)
Description of the change
optionally retrieves a list of hosts to try to connect to from a url,
still need changes in the client config and the interface between client and session: for a second branch, this one is already biggish
found that session.Close() has a race about Connection, will fix also in a later branch
John Lenton (chipaca) : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic failed. Below is the output from the failed tests.
mkdir -p /mnt/tarmac/
mkdir -p /mnt/tarmac/
go get -u launchpad.
go get -d -u launchpad.
/mnt/tarmac/
"/mnt/tarmac/
go install launchpad.
go test launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
? launchpad.
ok launchpad.
ok launchpad.
go test -race launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
Preview Diff
1 | === modified file 'client/session/session.go' |
2 | --- client/session/session.go 2014-03-26 16:26:36 +0000 |
3 | +++ client/session/session.go 2014-03-28 14:47:06 +0000 |
4 | @@ -23,14 +23,17 @@ |
5 | "crypto/x509" |
6 | "errors" |
7 | "fmt" |
8 | + "math/rand" |
9 | + "net" |
10 | + "strings" |
11 | + "sync/atomic" |
12 | + "time" |
13 | + |
14 | + "launchpad.net/ubuntu-push/client/gethosts" |
15 | "launchpad.net/ubuntu-push/client/session/levelmap" |
16 | "launchpad.net/ubuntu-push/logger" |
17 | "launchpad.net/ubuntu-push/protocol" |
18 | "launchpad.net/ubuntu-push/util" |
19 | - "math/rand" |
20 | - "net" |
21 | - "sync/atomic" |
22 | - "time" |
23 | ) |
24 | |
25 | var wireVersionBytes = []byte{protocol.ProtocolWireVersion} |
26 | @@ -45,6 +48,15 @@ |
27 | protocol.NotificationsMsg |
28 | } |
29 | |
30 | +// parseServerAddrSpec recognizes whether spec is a HTTP URL to get |
31 | +// hosts from or a |-separated list of host:port pairs. |
32 | +func parseServerAddrSpec(spec string) (hostsEndpoint string, fallbackHosts []string) { |
33 | + if strings.HasPrefix(spec, "http") { |
34 | + return spec, nil |
35 | + } |
36 | + return "", strings.Split(spec, "|") |
37 | +} |
38 | + |
39 | // ClientSessionState is a way to broadly track the progress of the session |
40 | type ClientSessionState uint32 |
41 | |
42 | @@ -56,14 +68,29 @@ |
43 | Running |
44 | ) |
45 | |
46 | +type hostGetter interface { |
47 | + Get() ([]string, error) |
48 | +} |
49 | + |
50 | // ClienSession holds a client<->server session and its configuration. |
51 | type ClientSession struct { |
52 | // configuration |
53 | - DeviceId string |
54 | - ServerAddr string |
55 | - ExchangeTimeout time.Duration |
56 | - Levels levelmap.LevelMap |
57 | - Protocolator func(net.Conn) protocol.Protocol |
58 | + DeviceId string |
59 | + ExchangeTimeout time.Duration |
60 | + HostsCachingExpiryTime time.Duration |
61 | + ExpectAllRepairedTime time.Duration |
62 | + Levels levelmap.LevelMap |
63 | + Protocolator func(net.Conn) protocol.Protocol |
64 | + // hosts |
65 | + getHost hostGetter |
66 | + fallbackHosts []string |
67 | + deliveryHostsTimestamp time.Time |
68 | + deliveryHosts []string |
69 | + lastAttemptTimestamp time.Time |
70 | + leftToTry int |
71 | + tryHost int |
72 | + // hook for testing |
73 | + timeSince func(time.Time) time.Duration |
74 | // connection |
75 | Connection net.Conn |
76 | Log logger.Logger |
77 | @@ -77,7 +104,7 @@ |
78 | MsgCh chan *Notification |
79 | } |
80 | |
81 | -func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration, |
82 | +func NewSession(serverAddrSpec string, pem []byte, exchangeTimeout time.Duration, |
83 | deviceId string, levelmapFactory func() (levelmap.LevelMap, error), |
84 | log logger.Logger) (*ClientSession, error) { |
85 | state := uint32(Disconnected) |
86 | @@ -85,15 +112,24 @@ |
87 | if err != nil { |
88 | return nil, err |
89 | } |
90 | + var getHost hostGetter |
91 | + hostsEndpoint, fallbackHosts := parseServerAddrSpec(serverAddrSpec) |
92 | + if hostsEndpoint != "" { |
93 | + getHost = gethosts.New(deviceId, hostsEndpoint, exchangeTimeout) |
94 | + } |
95 | sess := &ClientSession{ |
96 | - ExchangeTimeout: exchangeTimeout, |
97 | - ServerAddr: serverAddr, |
98 | - DeviceId: deviceId, |
99 | - Log: log, |
100 | - Protocolator: protocol.NewProtocol0, |
101 | - Levels: levels, |
102 | - TLS: &tls.Config{InsecureSkipVerify: true}, // XXX |
103 | - stateP: &state, |
104 | + ExchangeTimeout: exchangeTimeout, |
105 | + HostsCachingExpiryTime: 12 * time.Hour, // XXX take param |
106 | + ExpectAllRepairedTime: 30 * time.Minute, // XXX take param |
107 | + getHost: getHost, |
108 | + fallbackHosts: fallbackHosts, |
109 | + DeviceId: deviceId, |
110 | + Log: log, |
111 | + Protocolator: protocol.NewProtocol0, |
112 | + Levels: levels, |
113 | + TLS: &tls.Config{InsecureSkipVerify: true}, // XXX |
114 | + stateP: &state, |
115 | + timeSince: time.Since, |
116 | } |
117 | if pem != nil { |
118 | cp := x509.NewCertPool() |
119 | @@ -114,13 +150,72 @@ |
120 | atomic.StoreUint32(sess.stateP, uint32(state)) |
121 | } |
122 | |
123 | +// getHosts sets deliverHosts possibly querying a remote endpoint |
124 | +func (sess *ClientSession) getHosts() error { |
125 | + if sess.getHost != nil { |
126 | + if sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime { |
127 | + return nil |
128 | + } |
129 | + hosts, err := sess.getHost.Get() |
130 | + if err != nil { |
131 | + sess.Log.Errorf("getHosts: %v", err) |
132 | + sess.setState(Error) |
133 | + return err |
134 | + } |
135 | + sess.deliveryHostsTimestamp = time.Now() |
136 | + sess.deliveryHosts = hosts |
137 | + } else { |
138 | + sess.deliveryHosts = sess.fallbackHosts |
139 | + } |
140 | + return nil |
141 | +} |
142 | + |
143 | +// startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts |
144 | + |
145 | +func (sess *ClientSession) startConnectionAttempt() { |
146 | + if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime { |
147 | + sess.tryHost = 0 |
148 | + } |
149 | + sess.leftToTry = len(sess.deliveryHosts) |
150 | + sess.lastAttemptTimestamp = time.Now() |
151 | +} |
152 | + |
153 | +func (sess *ClientSession) nextHostToTry() string { |
154 | + if sess.leftToTry == 0 { |
155 | + return "" |
156 | + } |
157 | + res := sess.deliveryHosts[sess.tryHost] |
158 | + sess.tryHost = (sess.tryHost + 1) % len(sess.deliveryHosts) |
159 | + sess.leftToTry-- |
160 | + return res |
161 | +} |
162 | + |
163 | +// we reached the Started state, we can retry with the same host if we |
164 | +// have to retry again |
165 | +func (sess *ClientSession) started() { |
166 | + sess.tryHost-- |
167 | + if sess.tryHost == -1 { |
168 | + sess.tryHost = len(sess.deliveryHosts) - 1 |
169 | + } |
170 | + sess.setState(Started) |
171 | +} |
172 | + |
173 | // connect to a server using the configuration in the ClientSession |
174 | // and set up the connection. |
175 | func (sess *ClientSession) connect() error { |
176 | - conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout) |
177 | - if err != nil { |
178 | - sess.setState(Error) |
179 | - return fmt.Errorf("connect: %s", err) |
180 | + sess.startConnectionAttempt() |
181 | + var err error |
182 | + var conn net.Conn |
183 | + for { |
184 | + host := sess.nextHostToTry() |
185 | + if host == "" { |
186 | + sess.setState(Error) |
187 | + return fmt.Errorf("connect: %s", err) |
188 | + } |
189 | + conn, err = net.DialTimeout("tcp", host, sess.ExchangeTimeout) |
190 | + if err == nil { |
191 | + break |
192 | + } |
193 | } |
194 | sess.Connection = tls.Client(conn, sess.TLS) |
195 | sess.setState(Connected) |
196 | @@ -279,15 +374,19 @@ |
197 | sess.proto = proto |
198 | sess.pingInterval = pingInterval |
199 | sess.Log.Debugf("Connected %v.", conn.LocalAddr()) |
200 | - sess.setState(Started) |
201 | + sess.started() // deals with choosing which host to retry with as well |
202 | return nil |
203 | } |
204 | |
205 | // run calls connect, and if it works it calls start, and if it works |
206 | // it runs loop in a goroutine, and ships its return value over ErrCh. |
207 | -func (sess *ClientSession) run(closer func(), connecter, starter, looper func() error) error { |
208 | +func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error { |
209 | closer() |
210 | - err := connecter() |
211 | + err := hostGetter() |
212 | + if err != nil { |
213 | + return err |
214 | + } |
215 | + err = connecter() |
216 | if err == nil { |
217 | err = starter() |
218 | if err == nil { |
219 | @@ -317,7 +416,7 @@ |
220 | // keep on trying. |
221 | panic("can't Dial() without a protocol constructor.") |
222 | } |
223 | - return sess.run(sess.doClose, sess.connect, sess.start, sess.loop) |
224 | + return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop) |
225 | } |
226 | |
227 | func init() { |
228 | |
229 | === modified file 'client/session/session_test.go' |
230 | --- client/session/session_test.go 2014-03-27 13:26:10 +0000 |
231 | +++ client/session/session_test.go 2014-03-28 14:47:06 +0000 |
232 | @@ -23,16 +23,21 @@ |
233 | "fmt" |
234 | "io" |
235 | "io/ioutil" |
236 | + "net" |
237 | + "net/http" |
238 | + "net/http/httptest" |
239 | + "reflect" |
240 | + "testing" |
241 | + "time" |
242 | + |
243 | . "launchpad.net/gocheck" |
244 | + |
245 | "launchpad.net/ubuntu-push/client/session/levelmap" |
246 | + //"launchpad.net/ubuntu-push/client/gethosts" |
247 | "launchpad.net/ubuntu-push/logger" |
248 | "launchpad.net/ubuntu-push/protocol" |
249 | helpers "launchpad.net/ubuntu-push/testing" |
250 | "launchpad.net/ubuntu-push/testing/condition" |
251 | - "net" |
252 | - "reflect" |
253 | - "testing" |
254 | - "time" |
255 | ) |
256 | |
257 | func TestSession(t *testing.T) { TestingT(t) } |
258 | @@ -181,18 +186,43 @@ |
259 | } |
260 | |
261 | /**************************************************************** |
262 | + parseServerAddrSpec() tests |
263 | +****************************************************************/ |
264 | + |
265 | +func (cs *clientSessionSuite) TestParseServerAddrSpec(c *C) { |
266 | + hEp, fallbackHosts := parseServerAddrSpec("http://foo/hosts") |
267 | + c.Check(hEp, Equals, "http://foo/hosts") |
268 | + c.Check(fallbackHosts, IsNil) |
269 | + |
270 | + hEp, fallbackHosts = parseServerAddrSpec("foo:443") |
271 | + c.Check(hEp, Equals, "") |
272 | + c.Check(fallbackHosts, DeepEquals, []string{"foo:443"}) |
273 | + |
274 | + hEp, fallbackHosts = parseServerAddrSpec("foo:443|bar:443") |
275 | + c.Check(hEp, Equals, "") |
276 | + c.Check(fallbackHosts, DeepEquals, []string{"foo:443", "bar:443"}) |
277 | +} |
278 | + |
279 | +/**************************************************************** |
280 | NewSession() tests |
281 | ****************************************************************/ |
282 | |
283 | func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) { |
284 | - sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log) |
285 | + sess, err := NewSession("foo:443", nil, 0, "", cs.lvls, cs.log) |
286 | c.Check(sess, NotNil) |
287 | c.Check(err, IsNil) |
288 | + c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"}) |
289 | // but no root CAs set |
290 | c.Check(sess.TLS.RootCAs, IsNil) |
291 | c.Check(sess.State(), Equals, Disconnected) |
292 | } |
293 | |
294 | +func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) { |
295 | + sess, err := NewSession("http://foo/hosts", pem, 0, "wah", cs.lvls, cs.log) |
296 | + c.Assert(err, IsNil) |
297 | + c.Check(sess.getHost, NotNil) |
298 | +} |
299 | + |
300 | var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert") |
301 | var pem, _ = ioutil.ReadFile(certfile) |
302 | |
303 | @@ -218,12 +248,141 @@ |
304 | } |
305 | |
306 | /**************************************************************** |
307 | + getHosts() tests |
308 | +****************************************************************/ |
309 | + |
310 | +func (cs *clientSessionSuite) TestGetHostsFallback(c *C) { |
311 | + fallback := []string{"foo:443", "bar:443"} |
312 | + sess := &ClientSession{fallbackHosts: fallback} |
313 | + err := sess.getHosts() |
314 | + c.Assert(err, IsNil) |
315 | + c.Check(sess.deliveryHosts, DeepEquals, fallback) |
316 | +} |
317 | + |
318 | +type testHostGetter struct { |
319 | + hosts []string |
320 | + err error |
321 | +} |
322 | + |
323 | +func (thg *testHostGetter) Get() ([]string, error) { |
324 | + return thg.hosts, thg.err |
325 | +} |
326 | + |
327 | +func (cs *clientSessionSuite) TestGetHostsRemote(c *C) { |
328 | + hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} |
329 | + sess := &ClientSession{getHost: hostGetter, timeSince: time.Since} |
330 | + err := sess.getHosts() |
331 | + c.Assert(err, IsNil) |
332 | + c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) |
333 | +} |
334 | + |
335 | +func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) { |
336 | + sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log) |
337 | + c.Assert(err, IsNil) |
338 | + hostsErr := errors.New("failed") |
339 | + hostGetter := &testHostGetter{nil, hostsErr} |
340 | + sess.getHost = hostGetter |
341 | + err = sess.getHosts() |
342 | + c.Assert(err, Equals, hostsErr) |
343 | + c.Check(sess.deliveryHosts, IsNil) |
344 | + c.Check(sess.State(), Equals, Error) |
345 | +} |
346 | + |
347 | +func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) { |
348 | + hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil} |
349 | + sess := &ClientSession{ |
350 | + getHost: hostGetter, |
351 | + HostsCachingExpiryTime: 2 * time.Hour, |
352 | + timeSince: time.Since, |
353 | + } |
354 | + err := sess.getHosts() |
355 | + c.Assert(err, IsNil) |
356 | + hostGetter.hosts = []string{"baz:443"} |
357 | + // cached |
358 | + err = sess.getHosts() |
359 | + c.Assert(err, IsNil) |
360 | + c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) |
361 | + // expired |
362 | + sess.timeSince = func(ts time.Time) time.Duration { |
363 | + return 3 * time.Hour |
364 | + } |
365 | + err = sess.getHosts() |
366 | + c.Assert(err, IsNil) |
367 | + c.Check(sess.deliveryHosts, DeepEquals, []string{"baz:443"}) |
368 | +} |
369 | + |
370 | +/**************************************************************** |
371 | + startConnectionAttempt()/nextHostToTry()/started tests |
372 | +****************************************************************/ |
373 | + |
374 | +func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) { |
375 | + since := time.Since(time.Time{}) |
376 | + sess := &ClientSession{ |
377 | + ExpectAllRepairedTime: 10 * time.Second, |
378 | + timeSince: func(ts time.Time) time.Duration { |
379 | + return since |
380 | + }, |
381 | + deliveryHosts: []string{"foo:443", "bar:443"}, |
382 | + } |
383 | + // start from first host |
384 | + sess.startConnectionAttempt() |
385 | + c.Check(sess.lastAttemptTimestamp, Not(Equals), 0) |
386 | + c.Check(sess.tryHost, Equals, 0) |
387 | + c.Check(sess.leftToTry, Equals, 2) |
388 | + since = 1 * time.Second |
389 | + sess.tryHost = 1 |
390 | + // just continue |
391 | + sess.startConnectionAttempt() |
392 | + c.Check(sess.tryHost, Equals, 1) |
393 | + sess.tryHost = 2 |
394 | +} |
395 | + |
396 | +func (cs *clientSessionSuite) TestNextHostToTry(c *C) { |
397 | + sess := &ClientSession{ |
398 | + deliveryHosts: []string{"foo:443", "bar:443", "baz:443"}, |
399 | + tryHost: 0, |
400 | + leftToTry: 3, |
401 | + } |
402 | + c.Check(sess.nextHostToTry(), Equals, "foo:443") |
403 | + c.Check(sess.nextHostToTry(), Equals, "bar:443") |
404 | + c.Check(sess.nextHostToTry(), Equals, "baz:443") |
405 | + c.Check(sess.nextHostToTry(), Equals, "") |
406 | + c.Check(sess.nextHostToTry(), Equals, "") |
407 | + c.Check(sess.tryHost, Equals, 0) |
408 | + |
409 | + sess.leftToTry = 3 |
410 | + sess.tryHost = 1 |
411 | + c.Check(sess.nextHostToTry(), Equals, "bar:443") |
412 | + c.Check(sess.nextHostToTry(), Equals, "baz:443") |
413 | + c.Check(sess.nextHostToTry(), Equals, "foo:443") |
414 | + c.Check(sess.nextHostToTry(), Equals, "") |
415 | + c.Check(sess.nextHostToTry(), Equals, "") |
416 | + c.Check(sess.tryHost, Equals, 1) |
417 | +} |
418 | + |
419 | +func (cs *clientSessionSuite) TestStarted(c *C) { |
420 | + sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log) |
421 | + c.Assert(err, IsNil) |
422 | + |
423 | + sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"} |
424 | + sess.tryHost = 1 |
425 | + |
426 | + sess.started() |
427 | + c.Check(sess.tryHost, Equals, 0) |
428 | + c.Check(sess.State(), Equals, Started) |
429 | + |
430 | + sess.started() |
431 | + c.Check(sess.tryHost, Equals, 2) |
432 | +} |
433 | + |
434 | +/**************************************************************** |
435 | connect() tests |
436 | ****************************************************************/ |
437 | |
438 | func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) { |
439 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
440 | c.Assert(err, IsNil) |
441 | + sess.deliveryHosts = []string{"nowhere"} |
442 | err = sess.connect() |
443 | c.Check(err, ErrorMatches, ".*connect.*address.*") |
444 | c.Check(sess.State(), Equals, Error) |
445 | @@ -233,12 +392,27 @@ |
446 | srv, err := net.Listen("tcp", "localhost:0") |
447 | c.Assert(err, IsNil) |
448 | defer srv.Close() |
449 | - sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log) |
450 | - c.Assert(err, IsNil) |
451 | - err = sess.connect() |
452 | - c.Check(err, IsNil) |
453 | - c.Check(sess.Connection, NotNil) |
454 | - c.Check(sess.State(), Equals, Connected) |
455 | + sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
456 | + c.Assert(err, IsNil) |
457 | + sess.deliveryHosts = []string{srv.Addr().String()} |
458 | + err = sess.connect() |
459 | + c.Check(err, IsNil) |
460 | + c.Check(sess.Connection, NotNil) |
461 | + c.Check(sess.State(), Equals, Connected) |
462 | +} |
463 | + |
464 | +func (cs *clientSessionSuite) TestConnectSecondConnects(c *C) { |
465 | + srv, err := net.Listen("tcp", "localhost:0") |
466 | + c.Assert(err, IsNil) |
467 | + defer srv.Close() |
468 | + sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
469 | + c.Assert(err, IsNil) |
470 | + sess.deliveryHosts = []string{"nowhere", srv.Addr().String()} |
471 | + err = sess.connect() |
472 | + c.Check(err, IsNil) |
473 | + c.Check(sess.Connection, NotNil) |
474 | + c.Check(sess.State(), Equals, Connected) |
475 | + c.Check(sess.tryHost, Equals, 0) |
476 | } |
477 | |
478 | func (cs *clientSessionSuite) TestConnectConnectFail(c *C) { |
479 | @@ -247,6 +421,7 @@ |
480 | sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log) |
481 | srv.Close() |
482 | c.Assert(err, IsNil) |
483 | + sess.deliveryHosts = []string{srv.Addr().String()} |
484 | err = sess.connect() |
485 | c.Check(err, ErrorMatches, ".*connection refused") |
486 | c.Check(sess.State(), Equals, Error) |
487 | @@ -688,20 +863,34 @@ |
488 | run() tests |
489 | ****************************************************************/ |
490 | |
491 | -func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { |
492 | +func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) { |
493 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
494 | c.Assert(err, IsNil) |
495 | - failure := errors.New("TestRunBailsIfConnectFails") |
496 | + failure := errors.New("TestRunBailsIfHostGetterFails") |
497 | has_closed := false |
498 | err = sess.run( |
499 | func() { has_closed = true }, |
500 | func() error { return failure }, |
501 | nil, |
502 | + nil, |
503 | nil) |
504 | c.Check(err, Equals, failure) |
505 | c.Check(has_closed, Equals, true) |
506 | } |
507 | |
508 | +func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { |
509 | + sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
510 | + c.Assert(err, IsNil) |
511 | + failure := errors.New("TestRunBailsIfConnectFails") |
512 | + err = sess.run( |
513 | + func() {}, |
514 | + func() error { return nil }, |
515 | + func() error { return failure }, |
516 | + nil, |
517 | + nil) |
518 | + c.Check(err, Equals, failure) |
519 | +} |
520 | + |
521 | func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) { |
522 | sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log) |
523 | c.Assert(err, IsNil) |
524 | @@ -709,6 +898,7 @@ |
525 | err = sess.run( |
526 | func() {}, |
527 | func() error { return nil }, |
528 | + func() error { return nil }, |
529 | func() error { return failure }, |
530 | nil) |
531 | c.Check(err, Equals, failure) |
532 | @@ -727,6 +917,7 @@ |
533 | func() {}, |
534 | func() error { return nil }, |
535 | func() error { return nil }, |
536 | + func() error { return nil }, |
537 | func() error { sess.MsgCh <- notf; return <-failureCh }) |
538 | c.Check(err, Equals, nil) |
539 | // if run doesn't error it sets up the channels |
540 | @@ -794,7 +985,20 @@ |
541 | timeout := 100 * time.Millisecond |
542 | lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) |
543 | c.Assert(err, IsNil) |
544 | - sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log) |
545 | + // advertise |
546 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
547 | + b, err := json.Marshal(map[string]interface{}{ |
548 | + "hosts": []string{"nowhere", lst.Addr().String()}, |
549 | + }) |
550 | + if err != nil { |
551 | + panic(err) |
552 | + } |
553 | + w.Header().Set("Content-Type", "application/json") |
554 | + w.Write(b) |
555 | + })) |
556 | + defer ts.Close() |
557 | + |
558 | + sess, err := NewSession(ts.URL, nil, timeout, "wah", cs.lvls, cs.log) |
559 | c.Assert(err, IsNil) |
560 | tconn := &testConn{CloseCondition: condition.Fail2Work(10)} |
561 | sess.Connection = tconn |
562 | @@ -823,6 +1027,9 @@ |
563 | c.Assert(err, IsNil) |
564 | c.Assert(v, Equals, protocol.ProtocolWireVersion) |
565 | |
566 | + // if something goes wrong session would try the first/other host |
567 | + c.Check(sess.tryHost, Equals, 0) |
568 | + |
569 | // 2. "connect" (but on the fake protcol above! woo) |
570 | |
571 | c.Check(takeNext(downCh), Equals, "deadline 100ms") |
572 | @@ -843,6 +1050,9 @@ |
573 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
574 | upCh <- nil |
575 | |
576 | + // session would retry the same host |
577 | + c.Check(sess.tryHost, Equals, 1) |
578 | + |
579 | // and broadcasts... |
580 | b := &protocol.BroadcastMsg{ |
581 | Type: "broadcast", |
582 | @@ -870,3 +1080,31 @@ |
583 | upCh <- failure |
584 | c.Check(<-sess.ErrCh, Equals, failure) |
585 | } |
586 | + |
587 | +func (cs *clientSessionSuite) TestDialWorksDirect(c *C) { |
588 | + // happy path thoughts |
589 | + cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock) |
590 | + c.Assert(err, IsNil) |
591 | + tlsCfg := &tls.Config{ |
592 | + Certificates: []tls.Certificate{cert}, |
593 | + SessionTicketsDisabled: true, |
594 | + } |
595 | + |
596 | + timeout := 100 * time.Millisecond |
597 | + lst, err := tls.Listen("tcp", "localhost:0", tlsCfg) |
598 | + c.Assert(err, IsNil) |
599 | + sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log) |
600 | + c.Assert(err, IsNil) |
601 | + //defer sess.Close() xxx provokes a race, fix in a later branch |
602 | + |
603 | + upCh := make(chan interface{}, 5) |
604 | + downCh := make(chan interface{}, 5) |
605 | + proto := &testProtocol{up: upCh, down: downCh} |
606 | + sess.Protocolator = func(net.Conn) protocol.Protocol { return proto } |
607 | + |
608 | + go sess.Dial() |
609 | + |
610 | + _, err = lst.Accept() |
611 | + c.Assert(err, IsNil) |
612 | + // connect done |
613 | +} |
The attempt to merge lp:~pedronis/ubuntu-push/try-hosts into lp:ubuntu-push/automatic failed. Below is the output from the failed tests.
mkdir -p /mnt/tarmac/ cache/ubuntu- push/go- ws/bin cache/ubuntu- push/go- ws/pkg net/godeps net/gocheck launchpad. net/go- dbus/v1 launchpad. net/go- xdg/v0 code.google. com/p/gosqlite/ sqlite3 cache/ubuntu- push/go- ws/bin/ godeps -u dependencies.tsv cache/ubuntu- push/go- ws/src/ launchpad. net/gocheck" now at <email address hidden> net/gocheck launchpad. net/go- dbus/v1 launchpad. net/go- xdg/v0 code.google. com/p/gosqlite/ sqlite3 net/ubuntu- push/.. . net/ubuntu- push [no test files] net/ubuntu- push/bus 0.021s net/ubuntu- push/bus/ connectivity 1.142s net/ubuntu- push/bus/ networkmanager 0.037s net/ubuntu- push/bus/ notifications 0.016s net/ubuntu- push/bus/ testing 0.017s net/ubuntu- push/bus/ urldispatcher 0.021s net/ubuntu- push/client 0.099s net/ubuntu- push/client/ gethosts 1.114s net/ubuntu- push/client/ session 0.149s net/ubuntu- push/client/ session/ levelmap 0.060s net/ubuntu- push/config 0.008s net/ubuntu- push/external/ murmur3 0.005s unning (0.00 seconds) Syscall( 0x3, 0x9, 0x0, 0x0, 0x40bdaf, ...) lib/go/ src/pkg/ syscall/ asm_linux_ amd64.s: 18 +0x5 Close(0x9, 0x0, 0x0) lib/go/ src/pkg/ syscall/ zsyscall_ linux_amd64. go:267 +0x54 et(0x9, 0x2aceb7dbf848, 0x9) lib/go/ src/pkg/ net/fd_ unix.go: 474 +0x27 *netFD) .destroy( 0xc21128b230) lib/go/ src/pkg/ net/fd_ unix.go: 97 +0x45 *netFD) .decref( 0xc21128b230) lib/go/ src/pkg/ net/fd_ unix.go: 115 +0x47 *netFD) .Close( 0xc21128b230, 0x2acebb1e0f30, 0x0) lib/go/ src/pkg/ net/fd_ unix.go: 164 +0xa8 *conn). Close(0xc210077 040, 0xc2110c0d80, 0xc210077040) lib/go/ src/pkg/ net/net. go:138 +0x95 net/ubuntu- push/http13clie nt_test. funcĀ·083( ) tarmac/ cache/ubuntu- push/go- ws/src/ launchpad. net/ubuntu- push/http13clie nt/transport_ test.go: 1797 +0x13c net/ubuntu- push/http13clie nt_test. TestTransportTL SHandshakeTimeo ut tarmac/ cache/ubuntu- push/go- ws/src/ launchpad. net/ubuntu- push/http13clie nt/transport_ test.go: 1798 +0x22c net/ubuntu- push/http13clie nt 19.699s net/ubuntu- push/logger 0.006s net/ubuntu- push/protocol 0.010s net/ubuntu- push/server 0.062s net/ubuntu- push/server/ acceptance 0.015s net/ubuntu- push/server/ acceptance/ cmd [no test files] net/ubuntu- push/server/ acceptance/ suites [no test files] net/ubuntu- push/server/ api 0.022s net/ubuntu- push/server/ broker 0.022s net/ubuntu- push/server/ broker/ simple 0.009s net/ubuntu- pus...
mkdir -p /mnt/tarmac/
go get -u launchpad.
go get -d -u launchpad.
/mnt/tarmac/
"/mnt/tarmac/
go install launchpad.
go test launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
--- FAIL: TestGoroutinesR
z_last_test.go:57: num goroutines = 1
z_last_test.go:59: Too many goroutines.
z_last_test.go:61: 1 instances of:
syscall.
/usr/
syscall.
/usr/
net.closesock
/usr/
net.(
/usr/
net.(
/usr/
net.(
/usr/
net.(
/usr/
launchpad.
/mnt/
created by launchpad.
/mnt/
FAIL
FAIL launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.
? launchpad.
ok launchpad.
ok launchpad.
ok launchpad.
? launchpad.