Merge lp:~pedronis/ubuntu-push/be-nice into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Superseded
Proposed branch: lp:~pedronis/ubuntu-push/be-nice
Merge into: lp:ubuntu-push
Diff against target: 1379 lines (+633/-144)
12 files modified
.bzrignore (+2/-0)
Makefile (+18/-6)
README (+12/-3)
client/client.go (+31/-5)
client/client_test.go (+93/-44)
client/gethosts/gethost_test.go (+5/-7)
client/session/session.go (+156/-31)
client/session/session_test.go (+307/-42)
debian/config.json (+3/-0)
sampleconfigs/dev.json (+2/-2)
server/acceptance/suites/helpers.go (+2/-2)
server/acceptance/suites/suite.go (+2/-2)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/be-nice
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+213511@code.launchpad.net

Commit message

update README, Makefile with more targets, move sample dev config to sampleconfigs

Description of the change

update README, Makefile with more targets, move sample dev config to sampleconfigs

To post a comment you must log in.
lp:~pedronis/ubuntu-push/be-nice updated
101. By Samuele Pedroni

newline

102. By Samuele Pedroni

api port => 8080

103. By Samuele Pedroni

final tweaks

Unmerged revisions

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-03-31 16:43:44 +0000
4@@ -11,3 +11,5 @@
5 debian/*.ex
6 debian/*.EX
7 debian/*.substvars
8+ubuntu-push-client
9+push-server-dev
10
11=== modified file 'Makefile'
12--- Makefile 2014-03-12 13:23:26 +0000
13+++ Makefile 2014-03-31 16:43:44 +0000
14@@ -12,6 +12,8 @@
15 GODEPS += launchpad.net/go-xdg/v0
16 GODEPS += code.google.com/p/gosqlite/sqlite3
17
18+TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance{|grep -v http13client )
19+
20 bootstrap:
21 mkdir -p $(GOPATH)/bin
22 mkdir -p $(GOPATH)/pkg
23@@ -21,17 +23,26 @@
24 go install $(GODEPS)
25
26 check:
27- go test $(TESTFLAGS) $(PROJECT)/...
28+ go test $(TESTFLAGS) $(TOTEST)
29
30 check-race:
31- go test $(TESTFLAGS) -race $(PROJECT)/...
32+ go test $(TESTFLAGS) -race $(TOTEST)
33+
34+acceptance:
35+ cd server/acceptance; ./acceptance.sh
36+
37+build-client:
38+ go build ubuntu-push-client.go
39+
40+build-server-dev:
41+ go build -o push-server-dev launchpad.net/ubuntu-push/server/dev
42
43 coverage-summary:
44- go test $(TESTFLAGS) -a -cover $(PROJECT)/...
45+ go test $(TESTFLAGS) -a -cover $(TOTEST)
46
47 coverage-html:
48 mkdir -p coverhtml
49- for pkg in $$(go list $(PROJECT)/...|grep -v acceptance ); do \
50+ for pkg in $(TOTEST); do \
51 relname="$${pkg#$(PROJECT)/}" ; \
52 mkdir -p coverhtml/$$(dirname $${relname}) ; \
53 go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \
54@@ -52,5 +63,6 @@
55 # requires graphviz installed
56 dot -Tsvg $< > $@
57
58-.PHONY: bootstrap check check-race format check-format coverage-summary \
59- coverage-html protocol-diagrams
60+.PHONY: bootstrap check check-race format check-format \
61+ acceptance build-client bluild-server-dev \
62+ coverage-summary coverage-html protocol-diagrams
63
64=== modified file 'README'
65--- README 2014-02-21 16:17:28 +0000
66+++ README 2014-03-31 16:43:44 +0000
67@@ -15,8 +15,7 @@
68 make check
69
70 To produce coverage reports you need Go 1.2 (default on Trusty) and
71-the cover tool (the latter can be obtained atm with something like:
72-sudo GOPATH=<go-workspace> go get code.google.com/p/go.tools/cmd/cover ),
73+the cover tool (in the golang-go.tools package),
74 then run:
75
76 make coverage-summary
77@@ -31,4 +30,14 @@
78
79 To run the acceptance tests, change to the acceptance subdir and run:
80
81- ./acceptance.sh
82+ make acceptance
83+
84+There are build targets to build the client:
85+
86+ make build-client
87+
88+building ubuntu-push-client, and the development server:
89+
90+ make build-server-dev
91+
92+building push-server-dev.
93\ No newline at end of file
94
95=== modified file 'client/client.go'
96--- client/client.go 2014-03-26 16:26:36 +0000
97+++ client/client.go 2014-03-31 16:43:44 +0000
98@@ -20,9 +20,14 @@
99
100 import (
101 "encoding/pem"
102+ "errors"
103 "fmt"
104 "io/ioutil"
105+ "os"
106+ "strings"
107+
108 "launchpad.net/go-dbus/v1"
109+
110 "launchpad.net/ubuntu-push/bus"
111 "launchpad.net/ubuntu-push/bus/connectivity"
112 "launchpad.net/ubuntu-push/bus/networkmanager"
113@@ -34,7 +39,6 @@
114 "launchpad.net/ubuntu-push/logger"
115 "launchpad.net/ubuntu-push/util"
116 "launchpad.net/ubuntu-push/whoopsie/identifier"
117- "os"
118 )
119
120 // ClientConfig holds the client configuration
121@@ -42,8 +46,13 @@
122 connectivity.ConnectivityConfig // q.v.
123 // A reasonably large timeout for receive/answer pairs
124 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
125- // The server to connect to
126- Addr config.ConfigHostPort
127+ // A timeout to use when trying to connect to the server
128+ ConnectTimeout config.ConfigTimeDuration `json:"connect_timeout"`
129+ // The server to connect to or url to query for hosts to connect to
130+ Addr string
131+ // Host list management
132+ HostsCachingExpiryTime config.ConfigTimeDuration `json:"hosts_cache_expiry"` // potentially refresh host list after
133+ ExpectAllRepairedTime config.ConfigTimeDuration `json:"expect_all_repaired"` // worth retrying all servers after
134 // The PEM-encoded server certificate
135 CertPEMFile string `json:"cert_pem_file"`
136 // The logging level (one of "debug", "info", "error")
137@@ -89,6 +98,12 @@
138 if err != nil {
139 return fmt.Errorf("reading config: %v", err)
140 }
141+ // ignore spaces
142+ client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1)
143+ if client.config.Addr == "" {
144+ return errors.New("no hosts specified")
145+ }
146+
147 // later, we'll be specifying more logging options in the config file
148 client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel)
149
150@@ -116,6 +131,17 @@
151 return nil
152 }
153
154+// deriveSessionConfig dervies the session configuration from the client configuration bits.
155+func (client *PushClient) deriveSessionConfig() session.ClientSessionConfig {
156+ return session.ClientSessionConfig{
157+ ConnectTimeout: client.config.ConnectTimeout.TimeDuration(),
158+ ExchangeTimeout: client.config.ExchangeTimeout.TimeDuration(),
159+ HostsCachingExpiryTime: client.config.HostsCachingExpiryTime.TimeDuration(),
160+ ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(),
161+ PEM: client.pem,
162+ }
163+}
164+
165 // getDeviceId gets the whoopsie identifier for the device
166 func (client *PushClient) getDeviceId() error {
167 err := client.idder.Generate()
168@@ -143,8 +169,8 @@
169
170 // initSession creates the session object
171 func (client *PushClient) initSession() error {
172- sess, err := session.NewSession(string(client.config.Addr), client.pem,
173- client.config.ExchangeTimeout.Duration, client.deviceId,
174+ sess, err := session.NewSession(client.config.Addr,
175+ client.deriveSessionConfig(), client.deviceId,
176 client.levelMapFactory, client.log)
177 if err != nil {
178 return err
179
180=== modified file 'client/client_test.go'
181--- client/client_test.go 2014-03-26 16:26:36 +0000
182+++ client/client_test.go 2014-03-31 16:43:44 +0000
183@@ -17,10 +17,19 @@
184 package client
185
186 import (
187+ "encoding/json"
188 "errors"
189 "fmt"
190 "io/ioutil"
191+ "net/http"
192+ "net/http/httptest"
193+ "path/filepath"
194+ "reflect"
195+ "testing"
196+ "time"
197+
198 . "launchpad.net/gocheck"
199+
200 "launchpad.net/ubuntu-push/bus"
201 "launchpad.net/ubuntu-push/bus/networkmanager"
202 "launchpad.net/ubuntu-push/bus/notifications"
203@@ -32,11 +41,6 @@
204 "launchpad.net/ubuntu-push/util"
205 "launchpad.net/ubuntu-push/whoopsie/identifier"
206 idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing"
207- "net/http"
208- "net/http/httptest"
209- "path/filepath"
210- "testing"
211- "time"
212 )
213
214 func TestClient(t *testing.T) { TestingT(t) }
215@@ -83,22 +87,37 @@
216 cs.timeouts = nil
217 }
218
219+func (cs *clientSuite) writeTestConfig(overrides map[string]interface{}) {
220+ pem_file := helpers.SourceRelative("../server/acceptance/ssl/testing.cert")
221+ cfgMap := map[string]interface{}{
222+ "connect_timeout": "7ms",
223+ "exchange_timeout": "10ms",
224+ "hosts_cache_expiry": "1h",
225+ "expect_all_repaired": "30m",
226+ "stabilizing_timeout": "0ms",
227+ "connectivity_check_url": "",
228+ "connectivity_check_md5": "",
229+ "addr": ":0",
230+ "cert_pem_file": pem_file,
231+ "recheck_timeout": "3h",
232+ "log_level": "debug",
233+ }
234+ for k, v := range overrides {
235+ cfgMap[k] = v
236+ }
237+ cfgBlob, err := json.Marshal(cfgMap)
238+ if err != nil {
239+ panic(err)
240+ }
241+ ioutil.WriteFile(cs.configPath, cfgBlob, 0600)
242+}
243+
244 func (cs *clientSuite) SetUpTest(c *C) {
245 cs.log = helpers.NewTestLogger(c, "debug")
246 dir := c.MkDir()
247 cs.configPath = filepath.Join(dir, "config")
248- cfg := fmt.Sprintf(`
249-{
250- "exchange_timeout": "10ms",
251- "stabilizing_timeout": "0ms",
252- "connectivity_check_url": "",
253- "connectivity_check_md5": "",
254- "addr": ":0",
255- "cert_pem_file": %#v,
256- "recheck_timeout": "3h",
257- "log_level": "debug"
258-}`, helpers.SourceRelative("../server/acceptance/config/testing.cert"))
259- ioutil.WriteFile(cs.configPath, []byte(cfg), 0600)
260+
261+ cs.writeTestConfig(nil)
262 }
263
264 type sqlientSuite struct{ clientSuite }
265@@ -119,7 +138,7 @@
266 err := cli.configure()
267 c.Assert(err, IsNil)
268 c.Assert(cli.config, NotNil)
269- c.Check(cli.config.ExchangeTimeout.Duration, Equals, time.Duration(10*time.Millisecond))
270+ c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond))
271 }
272
273 func (cs *clientSuite) TestConfigureSetsUpLog(c *C) {
274@@ -179,41 +198,70 @@
275 }
276
277 func (cs *clientSuite) TestConfigureBailsOnBadPEMFilename(c *C) {
278- ioutil.WriteFile(cs.configPath, []byte(`
279-{
280- "exchange_timeout": "10ms",
281- "stabilizing_timeout": "0ms",
282- "connectivity_check_url": "",
283- "connectivity_check_md5": "",
284- "addr": ":0",
285- "cert_pem_file": "/a/b/c",
286- "log_level": "debug",
287- "recheck_timeout": "3h"
288-}`), 0600)
289-
290+ cs.writeTestConfig(map[string]interface{}{
291+ "cert_pem_file": "/a/b/c",
292+ })
293 cli := NewPushClient(cs.configPath, cs.leveldbPath)
294 err := cli.configure()
295 c.Assert(err, ErrorMatches, "reading PEM file: .*")
296 }
297
298 func (cs *clientSuite) TestConfigureBailsOnBadPEM(c *C) {
299- ioutil.WriteFile(cs.configPath, []byte(`
300-{
301- "exchange_timeout": "10ms",
302- "stabilizing_timeout": "0ms",
303- "connectivity_check_url": "",
304- "connectivity_check_md5": "",
305- "addr": ":0",
306- "cert_pem_file": "/etc/passwd",
307- "log_level": "debug",
308- "recheck_timeout": "3h"
309-}`), 0600)
310-
311+ cs.writeTestConfig(map[string]interface{}{
312+ "cert_pem_file": "/etc/passwd",
313+ })
314 cli := NewPushClient(cs.configPath, cs.leveldbPath)
315 err := cli.configure()
316 c.Assert(err, ErrorMatches, "no PEM found.*")
317 }
318
319+func (cs *clientSuite) TestConfigureBailsOnNoHosts(c *C) {
320+ cs.writeTestConfig(map[string]interface{}{
321+ "addr": " ",
322+ })
323+ cli := NewPushClient(cs.configPath, cs.leveldbPath)
324+ err := cli.configure()
325+ c.Assert(err, ErrorMatches, "no hosts specified")
326+}
327+
328+func (cs *clientSuite) TestConfigureRemovesBlanksInAddr(c *C) {
329+ cs.writeTestConfig(map[string]interface{}{
330+ "addr": " foo: 443",
331+ })
332+ cli := NewPushClient(cs.configPath, cs.leveldbPath)
333+ err := cli.configure()
334+ c.Assert(err, IsNil)
335+ c.Check(cli.config.Addr, Equals, "foo:443")
336+}
337+
338+/*****************************************************************
339+ deriveSessionConfig tests
340+******************************************************************/
341+
342+func (cs *clientSuite) TestDeriveSessionConfig(c *C) {
343+ cli := NewPushClient(cs.configPath, cs.leveldbPath)
344+ err := cli.configure()
345+ c.Assert(err, IsNil)
346+ expected := session.ClientSessionConfig{
347+ ConnectTimeout: 7 * time.Millisecond,
348+ ExchangeTimeout: 10 * time.Millisecond,
349+ HostsCachingExpiryTime: 1 * time.Hour,
350+ ExpectAllRepairedTime: 30 * time.Minute,
351+ PEM: cli.pem,
352+ }
353+ // sanity check that we are looking at all fields
354+ vExpected := reflect.ValueOf(expected)
355+ nf := vExpected.NumField()
356+ for i := 0; i < nf; i++ {
357+ fv := vExpected.Field(i)
358+ // field isn't empty/zero
359+ c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name))
360+ }
361+ // finally compare
362+ conf := cli.deriveSessionConfig()
363+ c.Check(conf, DeepEquals, expected)
364+}
365+
366 /*****************************************************************
367 getDeviceId tests
368 ******************************************************************/
369@@ -308,6 +356,7 @@
370 cli := NewPushClient(cs.configPath, cs.leveldbPath)
371 cli.log = cs.log
372 c.Assert(cli.initSession(), IsNil)
373+ cs.log.ResetCapture()
374 cli.hasConnectivity = true
375 cli.handleErr(errors.New("bananas"))
376 c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n")
377@@ -363,7 +412,7 @@
378 func (cs *clientSuite) TestHandleConnStateC2D(c *C) {
379 cli := NewPushClient(cs.configPath, cs.leveldbPath)
380 cli.log = cs.log
381- cli.session, _ = session.NewSession(string(cli.config.Addr), cli.pem, cli.config.ExchangeTimeout.Duration, cli.deviceId, levelmap.NewLevelMap, cs.log)
382+ cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(), cli.deviceId, levelmap.NewLevelMap, cs.log)
383 cli.session.Dial()
384 cli.hasConnectivity = true
385
386@@ -376,7 +425,7 @@
387 func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) {
388 cli := NewPushClient(cs.configPath, cs.leveldbPath)
389 cli.log = cs.log
390- cli.session, _ = session.NewSession(string(cli.config.Addr), cli.pem, cli.config.ExchangeTimeout.Duration, cli.deviceId, levelmap.NewLevelMap, cs.log)
391+ cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(), cli.deviceId, levelmap.NewLevelMap, cs.log)
392 cli.hasConnectivity = true
393
394 cli.handleConnState(false)
395
396=== modified file 'client/gethosts/gethost_test.go'
397--- client/gethosts/gethost_test.go 2014-03-24 15:32:29 +0000
398+++ client/gethosts/gethost_test.go 2014-03-31 16:43:44 +0000
399@@ -61,18 +61,16 @@
400 }
401
402 func (s *getHostsSuite) TestGetTimeout(c *C) {
403- finish := make(chan bool, 1)
404+ started := make(chan bool, 1)
405 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
406- <-finish
407+ started <- true
408+ time.Sleep(700 * time.Millisecond)
409 }))
410 defer func() {
411- time.Sleep(100 * time.Millisecond) // work around -race issue
412+ <-started
413 ts.Close()
414 }()
415- defer func() {
416- finish <- true
417- }()
418- gh := New("foobar", ts.URL, 1*time.Second)
419+ gh := New("foobar", ts.URL, 500*time.Millisecond)
420 _, err := gh.Get()
421 c.Check(err, ErrorMatches, ".*closed.*")
422 }
423
424=== modified file 'client/session/session.go'
425--- client/session/session.go 2014-03-26 16:26:36 +0000
426+++ client/session/session.go 2014-03-31 16:43:44 +0000
427@@ -23,14 +23,18 @@
428 "crypto/x509"
429 "errors"
430 "fmt"
431+ "math/rand"
432+ "net"
433+ "strings"
434+ "sync"
435+ "sync/atomic"
436+ "time"
437+
438+ "launchpad.net/ubuntu-push/client/gethosts"
439 "launchpad.net/ubuntu-push/client/session/levelmap"
440 "launchpad.net/ubuntu-push/logger"
441 "launchpad.net/ubuntu-push/protocol"
442 "launchpad.net/ubuntu-push/util"
443- "math/rand"
444- "net"
445- "sync/atomic"
446- "time"
447 )
448
449 var wireVersionBytes = []byte{protocol.ProtocolWireVersion}
450@@ -45,6 +49,15 @@
451 protocol.NotificationsMsg
452 }
453
454+// parseServerAddrSpec recognizes whether spec is a HTTP URL to get
455+// hosts from or a |-separated list of host:port pairs.
456+func parseServerAddrSpec(spec string) (hostsEndpoint string, fallbackHosts []string) {
457+ if strings.HasPrefix(spec, "http") {
458+ return spec, nil
459+ }
460+ return "", strings.Split(spec, "|")
461+}
462+
463 // ClientSessionState is a way to broadly track the progress of the session
464 type ClientSessionState uint32
465
466@@ -56,15 +69,38 @@
467 Running
468 )
469
470-// ClienSession holds a client<->server session and its configuration.
471+type hostGetter interface {
472+ Get() ([]string, error)
473+}
474+
475+// ClientSessionConfig groups the client session configuration.
476+type ClientSessionConfig struct {
477+ ConnectTimeout time.Duration
478+ ExchangeTimeout time.Duration
479+ HostsCachingExpiryTime time.Duration
480+ ExpectAllRepairedTime time.Duration
481+ PEM []byte
482+}
483+
484+// ClientSession holds a client<->server session and its configuration.
485 type ClientSession struct {
486 // configuration
487- DeviceId string
488- ServerAddr string
489- ExchangeTimeout time.Duration
490- Levels levelmap.LevelMap
491- Protocolator func(net.Conn) protocol.Protocol
492+ DeviceId string
493+ ClientSessionConfig
494+ Levels levelmap.LevelMap
495+ Protocolator func(net.Conn) protocol.Protocol
496+ // hosts
497+ getHost hostGetter
498+ fallbackHosts []string
499+ deliveryHostsTimestamp time.Time
500+ deliveryHosts []string
501+ lastAttemptTimestamp time.Time
502+ leftToTry int
503+ tryHost int
504+ // hook for testing
505+ timeSince func(time.Time) time.Duration
506 // connection
507+ connLock sync.RWMutex
508 Connection net.Conn
509 Log logger.Logger
510 TLS *tls.Config
511@@ -77,7 +113,7 @@
512 MsgCh chan *Notification
513 }
514
515-func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,
516+func NewSession(serverAddrSpec string, conf ClientSessionConfig,
517 deviceId string, levelmapFactory func() (levelmap.LevelMap, error),
518 log logger.Logger) (*ClientSession, error) {
519 state := uint32(Disconnected)
520@@ -85,19 +121,27 @@
521 if err != nil {
522 return nil, err
523 }
524+ var getHost hostGetter
525+ log.Infof("using addr: %v", serverAddrSpec)
526+ hostsEndpoint, fallbackHosts := parseServerAddrSpec(serverAddrSpec)
527+ if hostsEndpoint != "" {
528+ getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout)
529+ }
530 sess := &ClientSession{
531- ExchangeTimeout: exchangeTimeout,
532- ServerAddr: serverAddr,
533- DeviceId: deviceId,
534- Log: log,
535- Protocolator: protocol.NewProtocol0,
536- Levels: levels,
537- TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
538- stateP: &state,
539+ ClientSessionConfig: conf,
540+ getHost: getHost,
541+ fallbackHosts: fallbackHosts,
542+ DeviceId: deviceId,
543+ Log: log,
544+ Protocolator: protocol.NewProtocol0,
545+ Levels: levels,
546+ TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
547+ stateP: &state,
548+ timeSince: time.Since,
549 }
550- if pem != nil {
551+ if sess.PEM != nil {
552 cp := x509.NewCertPool()
553- ok := cp.AppendCertsFromPEM(pem)
554+ ok := cp.AppendCertsFromPEM(sess.PEM)
555 if !ok {
556 return nil, errors.New("could not parse certificate")
557 }
558@@ -114,15 +158,90 @@
559 atomic.StoreUint32(sess.stateP, uint32(state))
560 }
561
562+func (sess *ClientSession) setConnection(conn net.Conn) {
563+ sess.connLock.Lock()
564+ defer sess.connLock.Unlock()
565+ sess.Connection = conn
566+}
567+
568+func (sess *ClientSession) getConnection() net.Conn {
569+ sess.connLock.RLock()
570+ defer sess.connLock.RUnlock()
571+ return sess.Connection
572+}
573+
574+// getHosts sets deliveryHosts possibly querying a remote endpoint
575+func (sess *ClientSession) getHosts() error {
576+ if sess.getHost != nil {
577+ if sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime {
578+ return nil
579+ }
580+ hosts, err := sess.getHost.Get()
581+ if err != nil {
582+ sess.Log.Errorf("getHosts: %v", err)
583+ sess.setState(Error)
584+ return err
585+ }
586+ sess.deliveryHostsTimestamp = time.Now()
587+ sess.deliveryHosts = hosts
588+ } else {
589+ sess.deliveryHosts = sess.fallbackHosts
590+ }
591+ return nil
592+}
593+
594+// startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts
595+
596+func (sess *ClientSession) startConnectionAttempt() {
597+ if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime {
598+ sess.tryHost = 0
599+ }
600+ sess.leftToTry = len(sess.deliveryHosts)
601+ if sess.leftToTry == 0 {
602+ panic("should have got hosts from config or remote at this point")
603+ }
604+ sess.lastAttemptTimestamp = time.Now()
605+}
606+
607+func (sess *ClientSession) nextHostToTry() string {
608+ if sess.leftToTry == 0 {
609+ return ""
610+ }
611+ res := sess.deliveryHosts[sess.tryHost]
612+ sess.tryHost = (sess.tryHost + 1) % len(sess.deliveryHosts)
613+ sess.leftToTry--
614+ return res
615+}
616+
617+// we reached the Started state, we can retry with the same host if we
618+// have to retry again
619+func (sess *ClientSession) started() {
620+ sess.tryHost--
621+ if sess.tryHost == -1 {
622+ sess.tryHost = len(sess.deliveryHosts) - 1
623+ }
624+ sess.setState(Started)
625+}
626+
627 // connect to a server using the configuration in the ClientSession
628 // and set up the connection.
629 func (sess *ClientSession) connect() error {
630- conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)
631- if err != nil {
632- sess.setState(Error)
633- return fmt.Errorf("connect: %s", err)
634+ sess.startConnectionAttempt()
635+ var err error
636+ var conn net.Conn
637+ for {
638+ host := sess.nextHostToTry()
639+ if host == "" {
640+ sess.setState(Error)
641+ return fmt.Errorf("connect: %s", err)
642+ }
643+ sess.Log.Debugf("trying to connect to: %v", host)
644+ conn, err = net.DialTimeout("tcp", host, sess.ConnectTimeout)
645+ if err == nil {
646+ break
647+ }
648 }
649- sess.Connection = tls.Client(conn, sess.TLS)
650+ sess.setConnection(tls.Client(conn, sess.TLS))
651 sess.setState(Connected)
652 return nil
653 }
654@@ -145,6 +264,8 @@
655 sess.doClose()
656 }
657 func (sess *ClientSession) doClose() {
658+ sess.connLock.Lock()
659+ defer sess.connLock.Unlock()
660 if sess.Connection != nil {
661 sess.Connection.Close()
662 // we ignore Close errors, on purpose (the thinking being that
663@@ -224,7 +345,7 @@
664
665 // Call this when you've connected and want to start looping.
666 func (sess *ClientSession) start() error {
667- conn := sess.Connection
668+ conn := sess.getConnection()
669 err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout))
670 if err != nil {
671 sess.setState(Error)
672@@ -279,15 +400,19 @@
673 sess.proto = proto
674 sess.pingInterval = pingInterval
675 sess.Log.Debugf("Connected %v.", conn.LocalAddr())
676- sess.setState(Started)
677+ sess.started() // deals with choosing which host to retry with as well
678 return nil
679 }
680
681 // run calls connect, and if it works it calls start, and if it works
682 // it runs loop in a goroutine, and ships its return value over ErrCh.
683-func (sess *ClientSession) run(closer func(), connecter, starter, looper func() error) error {
684+func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error {
685 closer()
686- err := connecter()
687+ err := hostGetter()
688+ if err != nil {
689+ return err
690+ }
691+ err = connecter()
692 if err == nil {
693 err = starter()
694 if err == nil {
695@@ -317,7 +442,7 @@
696 // keep on trying.
697 panic("can't Dial() without a protocol constructor.")
698 }
699- return sess.run(sess.doClose, sess.connect, sess.start, sess.loop)
700+ return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop)
701 }
702
703 func init() {
704
705=== modified file 'client/session/session_test.go'
706--- client/session/session_test.go 2014-03-27 13:26:10 +0000
707+++ client/session/session_test.go 2014-03-31 16:43:44 +0000
708@@ -23,16 +23,21 @@
709 "fmt"
710 "io"
711 "io/ioutil"
712+ "net"
713+ "net/http"
714+ "net/http/httptest"
715+ "reflect"
716+ "testing"
717+ "time"
718+
719 . "launchpad.net/gocheck"
720+
721 "launchpad.net/ubuntu-push/client/session/levelmap"
722+ //"launchpad.net/ubuntu-push/client/gethosts"
723 "launchpad.net/ubuntu-push/logger"
724 "launchpad.net/ubuntu-push/protocol"
725 helpers "launchpad.net/ubuntu-push/testing"
726 "launchpad.net/ubuntu-push/testing/condition"
727- "net"
728- "reflect"
729- "testing"
730- "time"
731 )
732
733 func TestSession(t *testing.T) { TestingT(t) }
734@@ -181,23 +186,51 @@
735 }
736
737 /****************************************************************
738+ parseServerAddrSpec() tests
739+****************************************************************/
740+
741+func (cs *clientSessionSuite) TestParseServerAddrSpec(c *C) {
742+ hEp, fallbackHosts := parseServerAddrSpec("http://foo/hosts")
743+ c.Check(hEp, Equals, "http://foo/hosts")
744+ c.Check(fallbackHosts, IsNil)
745+
746+ hEp, fallbackHosts = parseServerAddrSpec("foo:443")
747+ c.Check(hEp, Equals, "")
748+ c.Check(fallbackHosts, DeepEquals, []string{"foo:443"})
749+
750+ hEp, fallbackHosts = parseServerAddrSpec("foo:443|bar:443")
751+ c.Check(hEp, Equals, "")
752+ c.Check(fallbackHosts, DeepEquals, []string{"foo:443", "bar:443"})
753+}
754+
755+/****************************************************************
756 NewSession() tests
757 ****************************************************************/
758
759+var dummyConf = ClientSessionConfig{}
760+
761 func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) {
762- sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log)
763+ sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log)
764 c.Check(sess, NotNil)
765 c.Check(err, IsNil)
766+ c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"})
767 // but no root CAs set
768 c.Check(sess.TLS.RootCAs, IsNil)
769 c.Check(sess.State(), Equals, Disconnected)
770 }
771
772-var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert")
773+func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) {
774+ sess, err := NewSession("http://foo/hosts", dummyConf, "wah", cs.lvls, cs.log)
775+ c.Assert(err, IsNil)
776+ c.Check(sess.getHost, NotNil)
777+}
778+
779+var certfile string = helpers.SourceRelative("../../server/acceptance/ssl/testing.cert")
780 var pem, _ = ioutil.ReadFile(certfile)
781
782 func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) {
783- sess, err := NewSession("", pem, 0, "wah", cs.lvls, cs.log)
784+ conf := ClientSessionConfig{PEM: pem}
785+ sess, err := NewSession("", conf, "wah", cs.lvls, cs.log)
786 c.Check(sess, NotNil)
787 c.Assert(err, IsNil)
788 c.Check(sess.TLS.RootCAs, NotNil)
789@@ -205,25 +238,172 @@
790
791 func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) {
792 badpem := []byte("This is not the PEM you're looking for.")
793- sess, err := NewSession("", badpem, 0, "wah", cs.lvls, cs.log)
794+ conf := ClientSessionConfig{PEM: badpem}
795+ sess, err := NewSession("", conf, "wah", cs.lvls, cs.log)
796 c.Check(sess, IsNil)
797 c.Check(err, NotNil)
798 }
799
800 func (cs *clientSessionSuite) TestNewSessionBadLevelMapFails(c *C) {
801 ferr := func() (levelmap.LevelMap, error) { return nil, errors.New("Busted.") }
802- sess, err := NewSession("", nil, 0, "wah", ferr, cs.log)
803+ sess, err := NewSession("", dummyConf, "wah", ferr, cs.log)
804 c.Check(sess, IsNil)
805 c.Assert(err, NotNil)
806 }
807
808 /****************************************************************
809+ getHosts() tests
810+****************************************************************/
811+
812+func (cs *clientSessionSuite) TestGetHostsFallback(c *C) {
813+ fallback := []string{"foo:443", "bar:443"}
814+ sess := &ClientSession{fallbackHosts: fallback}
815+ err := sess.getHosts()
816+ c.Assert(err, IsNil)
817+ c.Check(sess.deliveryHosts, DeepEquals, fallback)
818+}
819+
820+type testHostGetter struct {
821+ hosts []string
822+ err error
823+}
824+
825+func (thg *testHostGetter) Get() ([]string, error) {
826+ return thg.hosts, thg.err
827+}
828+
829+func (cs *clientSessionSuite) TestGetHostsRemote(c *C) {
830+ hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
831+ sess := &ClientSession{getHost: hostGetter, timeSince: time.Since}
832+ err := sess.getHosts()
833+ c.Assert(err, IsNil)
834+ c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
835+}
836+
837+func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) {
838+ sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)
839+ c.Assert(err, IsNil)
840+ hostsErr := errors.New("failed")
841+ hostGetter := &testHostGetter{nil, hostsErr}
842+ sess.getHost = hostGetter
843+ err = sess.getHosts()
844+ c.Assert(err, Equals, hostsErr)
845+ c.Check(sess.deliveryHosts, IsNil)
846+ c.Check(sess.State(), Equals, Error)
847+}
848+
849+func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) {
850+ hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
851+ sess := &ClientSession{
852+ getHost: hostGetter,
853+ ClientSessionConfig: ClientSessionConfig{
854+ HostsCachingExpiryTime: 2 * time.Hour,
855+ },
856+ timeSince: time.Since,
857+ }
858+ err := sess.getHosts()
859+ c.Assert(err, IsNil)
860+ hostGetter.hosts = []string{"baz:443"}
861+ // cached
862+ err = sess.getHosts()
863+ c.Assert(err, IsNil)
864+ c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
865+ // expired
866+ sess.timeSince = func(ts time.Time) time.Duration {
867+ return 3 * time.Hour
868+ }
869+ err = sess.getHosts()
870+ c.Assert(err, IsNil)
871+ c.Check(sess.deliveryHosts, DeepEquals, []string{"baz:443"})
872+}
873+
874+/****************************************************************
875+ startConnectionAttempt()/nextHostToTry()/started tests
876+****************************************************************/
877+
878+func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) {
879+ since := time.Since(time.Time{})
880+ sess := &ClientSession{
881+ ClientSessionConfig: ClientSessionConfig{
882+ ExpectAllRepairedTime: 10 * time.Second,
883+ },
884+ timeSince: func(ts time.Time) time.Duration {
885+ return since
886+ },
887+ deliveryHosts: []string{"foo:443", "bar:443"},
888+ }
889+ // start from first host
890+ sess.startConnectionAttempt()
891+ c.Check(sess.lastAttemptTimestamp, Not(Equals), 0)
892+ c.Check(sess.tryHost, Equals, 0)
893+ c.Check(sess.leftToTry, Equals, 2)
894+ since = 1 * time.Second
895+ sess.tryHost = 1
896+ // just continue
897+ sess.startConnectionAttempt()
898+ c.Check(sess.tryHost, Equals, 1)
899+ sess.tryHost = 2
900+}
901+
902+func (cs *clientSessionSuite) TestStartConnectionAttemptNoHostsPanic(c *C) {
903+ since := time.Since(time.Time{})
904+ sess := &ClientSession{
905+ ClientSessionConfig: ClientSessionConfig{
906+ ExpectAllRepairedTime: 10 * time.Second,
907+ },
908+ timeSince: func(ts time.Time) time.Duration {
909+ return since
910+ },
911+ }
912+ c.Check(sess.startConnectionAttempt, PanicMatches, "should have got hosts from config or remote at this point")
913+}
914+
915+func (cs *clientSessionSuite) TestNextHostToTry(c *C) {
916+ sess := &ClientSession{
917+ deliveryHosts: []string{"foo:443", "bar:443", "baz:443"},
918+ tryHost: 0,
919+ leftToTry: 3,
920+ }
921+ c.Check(sess.nextHostToTry(), Equals, "foo:443")
922+ c.Check(sess.nextHostToTry(), Equals, "bar:443")
923+ c.Check(sess.nextHostToTry(), Equals, "baz:443")
924+ c.Check(sess.nextHostToTry(), Equals, "")
925+ c.Check(sess.nextHostToTry(), Equals, "")
926+ c.Check(sess.tryHost, Equals, 0)
927+
928+ sess.leftToTry = 3
929+ sess.tryHost = 1
930+ c.Check(sess.nextHostToTry(), Equals, "bar:443")
931+ c.Check(sess.nextHostToTry(), Equals, "baz:443")
932+ c.Check(sess.nextHostToTry(), Equals, "foo:443")
933+ c.Check(sess.nextHostToTry(), Equals, "")
934+ c.Check(sess.nextHostToTry(), Equals, "")
935+ c.Check(sess.tryHost, Equals, 1)
936+}
937+
938+func (cs *clientSessionSuite) TestStarted(c *C) {
939+ sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)
940+ c.Assert(err, IsNil)
941+
942+ sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"}
943+ sess.tryHost = 1
944+
945+ sess.started()
946+ c.Check(sess.tryHost, Equals, 0)
947+ c.Check(sess.State(), Equals, Started)
948+
949+ sess.started()
950+ c.Check(sess.tryHost, Equals, 2)
951+}
952+
953+/****************************************************************
954 connect() tests
955 ****************************************************************/
956
957 func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) {
958- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
959+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
960 c.Assert(err, IsNil)
961+ sess.deliveryHosts = []string{"nowhere"}
962 err = sess.connect()
963 c.Check(err, ErrorMatches, ".*connect.*address.*")
964 c.Check(sess.State(), Equals, Error)
965@@ -233,20 +413,36 @@
966 srv, err := net.Listen("tcp", "localhost:0")
967 c.Assert(err, IsNil)
968 defer srv.Close()
969- sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log)
970- c.Assert(err, IsNil)
971- err = sess.connect()
972- c.Check(err, IsNil)
973- c.Check(sess.Connection, NotNil)
974- c.Check(sess.State(), Equals, Connected)
975+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
976+ c.Assert(err, IsNil)
977+ sess.deliveryHosts = []string{srv.Addr().String()}
978+ err = sess.connect()
979+ c.Check(err, IsNil)
980+ c.Check(sess.Connection, NotNil)
981+ c.Check(sess.State(), Equals, Connected)
982+}
983+
984+func (cs *clientSessionSuite) TestConnectSecondConnects(c *C) {
985+ srv, err := net.Listen("tcp", "localhost:0")
986+ c.Assert(err, IsNil)
987+ defer srv.Close()
988+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
989+ c.Assert(err, IsNil)
990+ sess.deliveryHosts = []string{"nowhere", srv.Addr().String()}
991+ err = sess.connect()
992+ c.Check(err, IsNil)
993+ c.Check(sess.Connection, NotNil)
994+ c.Check(sess.State(), Equals, Connected)
995+ c.Check(sess.tryHost, Equals, 0)
996 }
997
998 func (cs *clientSessionSuite) TestConnectConnectFail(c *C) {
999 srv, err := net.Listen("tcp", "localhost:0")
1000 c.Assert(err, IsNil)
1001- sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log)
1002+ sess, err := NewSession(srv.Addr().String(), dummyConf, "wah", cs.lvls, cs.log)
1003 srv.Close()
1004 c.Assert(err, IsNil)
1005+ sess.deliveryHosts = []string{srv.Addr().String()}
1006 err = sess.connect()
1007 c.Check(err, ErrorMatches, ".*connection refused")
1008 c.Check(sess.State(), Equals, Error)
1009@@ -257,7 +453,7 @@
1010 ****************************************************************/
1011
1012 func (cs *clientSessionSuite) TestClose(c *C) {
1013- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1014+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1015 c.Assert(err, IsNil)
1016 sess.Connection = &testConn{Name: "TestClose"}
1017 sess.Close()
1018@@ -266,7 +462,7 @@
1019 }
1020
1021 func (cs *clientSessionSuite) TestCloseTwice(c *C) {
1022- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1023+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1024 c.Assert(err, IsNil)
1025 sess.Connection = &testConn{Name: "TestCloseTwice"}
1026 sess.Close()
1027@@ -277,7 +473,7 @@
1028 }
1029
1030 func (cs *clientSessionSuite) TestCloseFails(c *C) {
1031- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1032+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1033 c.Assert(err, IsNil)
1034 sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)}
1035 sess.Close()
1036@@ -291,7 +487,7 @@
1037 func (d *derp) Stop() { d.stopped = true }
1038
1039 func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) {
1040- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1041+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1042 c.Assert(err, IsNil)
1043 ar := new(derp)
1044 sess.retrier = ar
1045@@ -308,7 +504,7 @@
1046
1047 func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) {
1048 // checks that AutoRedial sets up a retrier and tries redialing it
1049- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1050+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1051 c.Assert(err, IsNil)
1052 ar := new(derp)
1053 sess.retrier = ar
1054@@ -319,7 +515,7 @@
1055
1056 func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) {
1057 // checks that AutoRedial stops the previous retrier
1058- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1059+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1060 c.Assert(err, IsNil)
1061 ch := make(chan uint32)
1062 c.Check(sess.retrier, IsNil)
1063@@ -344,7 +540,10 @@
1064
1065 func (s *msgSuite) SetUpTest(c *C) {
1066 var err error
1067- s.sess, err = NewSession("", nil, time.Millisecond, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug"))
1068+ conf := ClientSessionConfig{
1069+ ExchangeTimeout: time.Millisecond,
1070+ }
1071+ s.sess, err = NewSession("", conf, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug"))
1072 c.Assert(err, IsNil)
1073 s.sess.Connection = &testConn{Name: "TestHandle*"}
1074 s.errCh = make(chan error, 1)
1075@@ -519,7 +718,7 @@
1076 start() tests
1077 ****************************************************************/
1078 func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) {
1079- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1080+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1081 c.Assert(err, IsNil)
1082 sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails",
1083 DeadlineCondition: condition.Work(false)} // setdeadline will fail
1084@@ -529,7 +728,7 @@
1085 }
1086
1087 func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) {
1088- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1089+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1090 c.Assert(err, IsNil)
1091 sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails",
1092 WriteCondition: condition.Work(false)} // write will fail
1093@@ -539,7 +738,7 @@
1094 }
1095
1096 func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) {
1097- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1098+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1099 c.Assert(err, IsNil)
1100 sess.Levels = &brokenLevelMap{}
1101 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}
1102@@ -559,7 +758,7 @@
1103 }
1104
1105 func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) {
1106- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1107+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1108 c.Assert(err, IsNil)
1109 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}
1110 errCh := make(chan error, 1)
1111@@ -585,7 +784,7 @@
1112 }
1113
1114 func (cs *clientSessionSuite) TestStartConnackReadError(c *C) {
1115- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1116+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1117 c.Assert(err, IsNil)
1118 sess.Connection = &testConn{Name: "TestStartConnackReadError"}
1119 errCh := make(chan error, 1)
1120@@ -609,7 +808,7 @@
1121 }
1122
1123 func (cs *clientSessionSuite) TestStartBadConnack(c *C) {
1124- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1125+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1126 c.Assert(err, IsNil)
1127 sess.Connection = &testConn{Name: "TestStartBadConnack"}
1128 errCh := make(chan error, 1)
1129@@ -633,7 +832,7 @@
1130 }
1131
1132 func (cs *clientSessionSuite) TestStartNotConnack(c *C) {
1133- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1134+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1135 c.Assert(err, IsNil)
1136 sess.Connection = &testConn{Name: "TestStartBadConnack"}
1137 errCh := make(chan error, 1)
1138@@ -657,7 +856,7 @@
1139 }
1140
1141 func (cs *clientSessionSuite) TestStartWorks(c *C) {
1142- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1143+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1144 c.Assert(err, IsNil)
1145 sess.Connection = &testConn{Name: "TestStartWorks"}
1146 errCh := make(chan error, 1)
1147@@ -688,34 +887,49 @@
1148 run() tests
1149 ****************************************************************/
1150
1151-func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {
1152- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1153+func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) {
1154+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1155 c.Assert(err, IsNil)
1156- failure := errors.New("TestRunBailsIfConnectFails")
1157+ failure := errors.New("TestRunBailsIfHostGetterFails")
1158 has_closed := false
1159 err = sess.run(
1160 func() { has_closed = true },
1161 func() error { return failure },
1162 nil,
1163+ nil,
1164 nil)
1165 c.Check(err, Equals, failure)
1166 c.Check(has_closed, Equals, true)
1167 }
1168
1169+func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {
1170+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1171+ c.Assert(err, IsNil)
1172+ failure := errors.New("TestRunBailsIfConnectFails")
1173+ err = sess.run(
1174+ func() {},
1175+ func() error { return nil },
1176+ func() error { return failure },
1177+ nil,
1178+ nil)
1179+ c.Check(err, Equals, failure)
1180+}
1181+
1182 func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) {
1183- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1184+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1185 c.Assert(err, IsNil)
1186 failure := errors.New("TestRunBailsIfStartFails")
1187 err = sess.run(
1188 func() {},
1189 func() error { return nil },
1190+ func() error { return nil },
1191 func() error { return failure },
1192 nil)
1193 c.Check(err, Equals, failure)
1194 }
1195
1196 func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) {
1197- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1198+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1199 c.Assert(err, IsNil)
1200 // just to make a point: until here we haven't set ErrCh & MsgCh (no
1201 // biggie if this stops being true)
1202@@ -727,6 +941,7 @@
1203 func() {},
1204 func() error { return nil },
1205 func() error { return nil },
1206+ func() error { return nil },
1207 func() error { sess.MsgCh <- notf; return <-failureCh })
1208 c.Check(err, Equals, nil)
1209 // if run doesn't error it sets up the channels
1210@@ -744,7 +959,7 @@
1211 ****************************************************************/
1212
1213 func (cs *clientSessionSuite) TestJitter(c *C) {
1214- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1215+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1216 c.Assert(err, IsNil)
1217 num_tries := 20 // should do the math
1218 spread := time.Second //
1219@@ -776,12 +991,17 @@
1220
1221 func (cs *clientSessionSuite) TestDialPanics(c *C) {
1222 // one last unhappy test
1223- sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)
1224+ sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
1225 c.Assert(err, IsNil)
1226 sess.Protocolator = nil
1227 c.Check(sess.Dial, PanicMatches, ".*protocol constructor.")
1228 }
1229
1230+var (
1231+ dialTestTimeout = 100 * time.Millisecond
1232+ dialTestConf = ClientSessionConfig{ExchangeTimeout: dialTestTimeout}
1233+)
1234+
1235 func (cs *clientSessionSuite) TestDialWorks(c *C) {
1236 // happy path thoughts
1237 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
1238@@ -791,10 +1011,22 @@
1239 SessionTicketsDisabled: true,
1240 }
1241
1242- timeout := 100 * time.Millisecond
1243 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
1244 c.Assert(err, IsNil)
1245- sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log)
1246+ // advertise
1247+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1248+ b, err := json.Marshal(map[string]interface{}{
1249+ "hosts": []string{"nowhere", lst.Addr().String()},
1250+ })
1251+ if err != nil {
1252+ panic(err)
1253+ }
1254+ w.Header().Set("Content-Type", "application/json")
1255+ w.Write(b)
1256+ }))
1257+ defer ts.Close()
1258+
1259+ sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log)
1260 c.Assert(err, IsNil)
1261 tconn := &testConn{CloseCondition: condition.Fail2Work(10)}
1262 sess.Connection = tconn
1263@@ -819,10 +1051,13 @@
1264 c.Check(tconn.CloseCondition.String(), Matches, ".* 9 to go.")
1265
1266 // now, start: 1. protocol version
1267- v, err := protocol.ReadWireFormatVersion(srv, timeout)
1268+ v, err := protocol.ReadWireFormatVersion(srv, dialTestTimeout)
1269 c.Assert(err, IsNil)
1270 c.Assert(v, Equals, protocol.ProtocolWireVersion)
1271
1272+ // if something goes wrong session would try the first/other host
1273+ c.Check(sess.tryHost, Equals, 0)
1274+
1275 // 2. "connect" (but on the fake protcol above! woo)
1276
1277 c.Check(takeNext(downCh), Equals, "deadline 100ms")
1278@@ -843,6 +1078,9 @@
1279 c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"})
1280 upCh <- nil
1281
1282+ // session would retry the same host
1283+ c.Check(sess.tryHost, Equals, 1)
1284+
1285 // and broadcasts...
1286 b := &protocol.BroadcastMsg{
1287 Type: "broadcast",
1288@@ -870,3 +1108,30 @@
1289 upCh <- failure
1290 c.Check(<-sess.ErrCh, Equals, failure)
1291 }
1292+
1293+func (cs *clientSessionSuite) TestDialWorksDirect(c *C) {
1294+ // happy path thoughts
1295+ cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
1296+ c.Assert(err, IsNil)
1297+ tlsCfg := &tls.Config{
1298+ Certificates: []tls.Certificate{cert},
1299+ SessionTicketsDisabled: true,
1300+ }
1301+
1302+ lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
1303+ c.Assert(err, IsNil)
1304+ sess, err := NewSession(lst.Addr().String(), dialTestConf, "wah", cs.lvls, cs.log)
1305+ c.Assert(err, IsNil)
1306+ defer sess.Close()
1307+
1308+ upCh := make(chan interface{}, 5)
1309+ downCh := make(chan interface{}, 5)
1310+ proto := &testProtocol{up: upCh, down: downCh}
1311+ sess.Protocolator = func(net.Conn) protocol.Protocol { return proto }
1312+
1313+ go sess.Dial()
1314+
1315+ _, err = lst.Accept()
1316+ c.Assert(err, IsNil)
1317+ // connect done
1318+}
1319
1320=== modified file 'debian/config.json'
1321--- debian/config.json 2014-03-20 12:17:40 +0000
1322+++ debian/config.json 2014-03-31 16:43:44 +0000
1323@@ -1,5 +1,8 @@
1324 {
1325+ "connect_timeout": "20s",
1326 "exchange_timeout": "30s",
1327+ "hosts_cache_expiry": "12h",
1328+ "expect_all_repaired": "40m",
1329 "addr": "push-delivery.ubuntu.com:443",
1330 "cert_pem_file": "",
1331 "stabilizing_timeout": "2s",
1332
1333=== added directory 'sampleconfigs'
1334=== renamed file 'server/acceptance/config/config.json' => 'sampleconfigs/dev.json'
1335--- server/acceptance/config/config.json 2014-01-17 17:20:34 +0000
1336+++ sampleconfigs/dev.json 2014-03-31 16:43:44 +0000
1337@@ -4,8 +4,8 @@
1338 "broker_queue_size": 10000,
1339 "session_queue_size": 10,
1340 "addr": "127.0.0.1:9090",
1341- "key_pem_file": "testing.key",
1342- "cert_pem_file": "testing.cert",
1343+ "key_pem_file": "../server/acceptance/ssl/testing.key",
1344+ "cert_pem_file": "../server/acceptance/ssl/testing.cert",
1345 "http_addr": "127.0.0.1:8888",
1346 "http_read_timeout": "5s",
1347 "http_write_timeout": "5s"
1348
1349=== renamed directory 'server/acceptance/config' => 'server/acceptance/ssl'
1350=== modified file 'server/acceptance/suites/helpers.go'
1351--- server/acceptance/suites/helpers.go 2014-03-25 19:08:00 +0000
1352+++ server/acceptance/suites/helpers.go 2014-03-31 16:43:44 +0000
1353@@ -48,8 +48,8 @@
1354 "session_queue_size": 10,
1355 "broker_queue_size": 100,
1356 "addr": addr,
1357- "key_pem_file": helpers.SourceRelative("../config/testing.key"),
1358- "cert_pem_file": helpers.SourceRelative("../config/testing.cert"),
1359+ "key_pem_file": helpers.SourceRelative("../ssl/testing.key"),
1360+ "cert_pem_file": helpers.SourceRelative("../ssl/testing.cert"),
1361 })
1362 }
1363
1364
1365=== modified file 'server/acceptance/suites/suite.go'
1366--- server/acceptance/suites/suite.go 2014-03-25 19:08:00 +0000
1367+++ server/acceptance/suites/suite.go 2014-03-31 16:43:44 +0000
1368@@ -128,9 +128,9 @@
1369 }
1370
1371 func testClientSession(addr string, deviceId string, reportPings bool) *acceptance.ClientSession {
1372- certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../config/testing.cert"))
1373+ certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert"))
1374 if err != nil {
1375- panic(fmt.Sprintf("could not read config/testing.cert: %v", err))
1376+ panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err))
1377 }
1378 return &acceptance.ClientSession{
1379 ExchangeTimeout: 100 * time.Millisecond,

Subscribers

People subscribed via source and target branches