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
=== modified file '.bzrignore'
--- .bzrignore 2014-02-07 19:36:38 +0000
+++ .bzrignore 2014-03-31 16:43:44 +0000
@@ -11,3 +11,5 @@
11debian/*.ex11debian/*.ex
12debian/*.EX12debian/*.EX
13debian/*.substvars13debian/*.substvars
14ubuntu-push-client
15push-server-dev
1416
=== modified file 'Makefile'
--- Makefile 2014-03-12 13:23:26 +0000
+++ Makefile 2014-03-31 16:43:44 +0000
@@ -12,6 +12,8 @@
12GODEPS += launchpad.net/go-xdg/v012GODEPS += launchpad.net/go-xdg/v0
13GODEPS += code.google.com/p/gosqlite/sqlite313GODEPS += code.google.com/p/gosqlite/sqlite3
1414
15TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance{|grep -v http13client )
16
15bootstrap:17bootstrap:
16 mkdir -p $(GOPATH)/bin18 mkdir -p $(GOPATH)/bin
17 mkdir -p $(GOPATH)/pkg19 mkdir -p $(GOPATH)/pkg
@@ -21,17 +23,26 @@
21 go install $(GODEPS)23 go install $(GODEPS)
2224
23check:25check:
24 go test $(TESTFLAGS) $(PROJECT)/...26 go test $(TESTFLAGS) $(TOTEST)
2527
26check-race:28check-race:
27 go test $(TESTFLAGS) -race $(PROJECT)/...29 go test $(TESTFLAGS) -race $(TOTEST)
30
31acceptance:
32 cd server/acceptance; ./acceptance.sh
33
34build-client:
35 go build ubuntu-push-client.go
36
37build-server-dev:
38 go build -o push-server-dev launchpad.net/ubuntu-push/server/dev
2839
29coverage-summary:40coverage-summary:
30 go test $(TESTFLAGS) -a -cover $(PROJECT)/...41 go test $(TESTFLAGS) -a -cover $(TOTEST)
3142
32coverage-html:43coverage-html:
33 mkdir -p coverhtml44 mkdir -p coverhtml
34 for pkg in $$(go list $(PROJECT)/...|grep -v acceptance ); do \45 for pkg in $(TOTEST); do \
35 relname="$${pkg#$(PROJECT)/}" ; \46 relname="$${pkg#$(PROJECT)/}" ; \
36 mkdir -p coverhtml/$$(dirname $${relname}) ; \47 mkdir -p coverhtml/$$(dirname $${relname}) ; \
37 go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \48 go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \
@@ -52,5 +63,6 @@
52 # requires graphviz installed63 # requires graphviz installed
53 dot -Tsvg $< > $@64 dot -Tsvg $< > $@
5465
55.PHONY: bootstrap check check-race format check-format coverage-summary \66.PHONY: bootstrap check check-race format check-format \
56 coverage-html protocol-diagrams67 acceptance build-client bluild-server-dev \
68 coverage-summary coverage-html protocol-diagrams
5769
=== modified file 'README'
--- README 2014-02-21 16:17:28 +0000
+++ README 2014-03-31 16:43:44 +0000
@@ -15,8 +15,7 @@
15 make check15 make check
1616
17To produce coverage reports you need Go 1.2 (default on Trusty) and17To produce coverage reports you need Go 1.2 (default on Trusty) and
18the cover tool (the latter can be obtained atm with something like:18the cover tool (in the golang-go.tools package),
19sudo GOPATH=<go-workspace> go get code.google.com/p/go.tools/cmd/cover ),
20then run:19then run:
2120
22 make coverage-summary21 make coverage-summary
@@ -31,4 +30,14 @@
3130
32To run the acceptance tests, change to the acceptance subdir and run:31To run the acceptance tests, change to the acceptance subdir and run:
3332
34 ./acceptance.sh33 make acceptance
34
35There are build targets to build the client:
36
37 make build-client
38
39building ubuntu-push-client, and the development server:
40
41 make build-server-dev
42
43building push-server-dev.
35\ No newline at end of file44\ No newline at end of file
3645
=== modified file 'client/client.go'
--- client/client.go 2014-03-26 16:26:36 +0000
+++ client/client.go 2014-03-31 16:43:44 +0000
@@ -20,9 +20,14 @@
2020
21import (21import (
22 "encoding/pem"22 "encoding/pem"
23 "errors"
23 "fmt"24 "fmt"
24 "io/ioutil"25 "io/ioutil"
26 "os"
27 "strings"
28
25 "launchpad.net/go-dbus/v1"29 "launchpad.net/go-dbus/v1"
30
26 "launchpad.net/ubuntu-push/bus"31 "launchpad.net/ubuntu-push/bus"
27 "launchpad.net/ubuntu-push/bus/connectivity"32 "launchpad.net/ubuntu-push/bus/connectivity"
28 "launchpad.net/ubuntu-push/bus/networkmanager"33 "launchpad.net/ubuntu-push/bus/networkmanager"
@@ -34,7 +39,6 @@
34 "launchpad.net/ubuntu-push/logger"39 "launchpad.net/ubuntu-push/logger"
35 "launchpad.net/ubuntu-push/util"40 "launchpad.net/ubuntu-push/util"
36 "launchpad.net/ubuntu-push/whoopsie/identifier"41 "launchpad.net/ubuntu-push/whoopsie/identifier"
37 "os"
38)42)
3943
40// ClientConfig holds the client configuration44// ClientConfig holds the client configuration
@@ -42,8 +46,13 @@
42 connectivity.ConnectivityConfig // q.v.46 connectivity.ConnectivityConfig // q.v.
43 // A reasonably large timeout for receive/answer pairs47 // A reasonably large timeout for receive/answer pairs
44 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`48 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
45 // The server to connect to49 // A timeout to use when trying to connect to the server
46 Addr config.ConfigHostPort50 ConnectTimeout config.ConfigTimeDuration `json:"connect_timeout"`
51 // The server to connect to or url to query for hosts to connect to
52 Addr string
53 // Host list management
54 HostsCachingExpiryTime config.ConfigTimeDuration `json:"hosts_cache_expiry"` // potentially refresh host list after
55 ExpectAllRepairedTime config.ConfigTimeDuration `json:"expect_all_repaired"` // worth retrying all servers after
47 // The PEM-encoded server certificate56 // The PEM-encoded server certificate
48 CertPEMFile string `json:"cert_pem_file"`57 CertPEMFile string `json:"cert_pem_file"`
49 // The logging level (one of "debug", "info", "error")58 // The logging level (one of "debug", "info", "error")
@@ -89,6 +98,12 @@
89 if err != nil {98 if err != nil {
90 return fmt.Errorf("reading config: %v", err)99 return fmt.Errorf("reading config: %v", err)
91 }100 }
101 // ignore spaces
102 client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1)
103 if client.config.Addr == "" {
104 return errors.New("no hosts specified")
105 }
106
92 // later, we'll be specifying more logging options in the config file107 // later, we'll be specifying more logging options in the config file
93 client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel)108 client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel)
94109
@@ -116,6 +131,17 @@
116 return nil131 return nil
117}132}
118133
134// deriveSessionConfig dervies the session configuration from the client configuration bits.
135func (client *PushClient) deriveSessionConfig() session.ClientSessionConfig {
136 return session.ClientSessionConfig{
137 ConnectTimeout: client.config.ConnectTimeout.TimeDuration(),
138 ExchangeTimeout: client.config.ExchangeTimeout.TimeDuration(),
139 HostsCachingExpiryTime: client.config.HostsCachingExpiryTime.TimeDuration(),
140 ExpectAllRepairedTime: client.config.ExpectAllRepairedTime.TimeDuration(),
141 PEM: client.pem,
142 }
143}
144
119// getDeviceId gets the whoopsie identifier for the device145// getDeviceId gets the whoopsie identifier for the device
120func (client *PushClient) getDeviceId() error {146func (client *PushClient) getDeviceId() error {
121 err := client.idder.Generate()147 err := client.idder.Generate()
@@ -143,8 +169,8 @@
143169
144// initSession creates the session object170// initSession creates the session object
145func (client *PushClient) initSession() error {171func (client *PushClient) initSession() error {
146 sess, err := session.NewSession(string(client.config.Addr), client.pem,172 sess, err := session.NewSession(client.config.Addr,
147 client.config.ExchangeTimeout.Duration, client.deviceId,173 client.deriveSessionConfig(), client.deviceId,
148 client.levelMapFactory, client.log)174 client.levelMapFactory, client.log)
149 if err != nil {175 if err != nil {
150 return err176 return err
151177
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-03-26 16:26:36 +0000
+++ client/client_test.go 2014-03-31 16:43:44 +0000
@@ -17,10 +17,19 @@
17package client17package client
1818
19import (19import (
20 "encoding/json"
20 "errors"21 "errors"
21 "fmt"22 "fmt"
22 "io/ioutil"23 "io/ioutil"
24 "net/http"
25 "net/http/httptest"
26 "path/filepath"
27 "reflect"
28 "testing"
29 "time"
30
23 . "launchpad.net/gocheck"31 . "launchpad.net/gocheck"
32
24 "launchpad.net/ubuntu-push/bus"33 "launchpad.net/ubuntu-push/bus"
25 "launchpad.net/ubuntu-push/bus/networkmanager"34 "launchpad.net/ubuntu-push/bus/networkmanager"
26 "launchpad.net/ubuntu-push/bus/notifications"35 "launchpad.net/ubuntu-push/bus/notifications"
@@ -32,11 +41,6 @@
32 "launchpad.net/ubuntu-push/util"41 "launchpad.net/ubuntu-push/util"
33 "launchpad.net/ubuntu-push/whoopsie/identifier"42 "launchpad.net/ubuntu-push/whoopsie/identifier"
34 idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing"43 idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing"
35 "net/http"
36 "net/http/httptest"
37 "path/filepath"
38 "testing"
39 "time"
40)44)
4145
42func TestClient(t *testing.T) { TestingT(t) }46func TestClient(t *testing.T) { TestingT(t) }
@@ -83,22 +87,37 @@
83 cs.timeouts = nil87 cs.timeouts = nil
84}88}
8589
90func (cs *clientSuite) writeTestConfig(overrides map[string]interface{}) {
91 pem_file := helpers.SourceRelative("../server/acceptance/ssl/testing.cert")
92 cfgMap := map[string]interface{}{
93 "connect_timeout": "7ms",
94 "exchange_timeout": "10ms",
95 "hosts_cache_expiry": "1h",
96 "expect_all_repaired": "30m",
97 "stabilizing_timeout": "0ms",
98 "connectivity_check_url": "",
99 "connectivity_check_md5": "",
100 "addr": ":0",
101 "cert_pem_file": pem_file,
102 "recheck_timeout": "3h",
103 "log_level": "debug",
104 }
105 for k, v := range overrides {
106 cfgMap[k] = v
107 }
108 cfgBlob, err := json.Marshal(cfgMap)
109 if err != nil {
110 panic(err)
111 }
112 ioutil.WriteFile(cs.configPath, cfgBlob, 0600)
113}
114
86func (cs *clientSuite) SetUpTest(c *C) {115func (cs *clientSuite) SetUpTest(c *C) {
87 cs.log = helpers.NewTestLogger(c, "debug")116 cs.log = helpers.NewTestLogger(c, "debug")
88 dir := c.MkDir()117 dir := c.MkDir()
89 cs.configPath = filepath.Join(dir, "config")118 cs.configPath = filepath.Join(dir, "config")
90 cfg := fmt.Sprintf(`119
91{120 cs.writeTestConfig(nil)
92 "exchange_timeout": "10ms",
93 "stabilizing_timeout": "0ms",
94 "connectivity_check_url": "",
95 "connectivity_check_md5": "",
96 "addr": ":0",
97 "cert_pem_file": %#v,
98 "recheck_timeout": "3h",
99 "log_level": "debug"
100}`, helpers.SourceRelative("../server/acceptance/config/testing.cert"))
101 ioutil.WriteFile(cs.configPath, []byte(cfg), 0600)
102}121}
103122
104type sqlientSuite struct{ clientSuite }123type sqlientSuite struct{ clientSuite }
@@ -119,7 +138,7 @@
119 err := cli.configure()138 err := cli.configure()
120 c.Assert(err, IsNil)139 c.Assert(err, IsNil)
121 c.Assert(cli.config, NotNil)140 c.Assert(cli.config, NotNil)
122 c.Check(cli.config.ExchangeTimeout.Duration, Equals, time.Duration(10*time.Millisecond))141 c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond))
123}142}
124143
125func (cs *clientSuite) TestConfigureSetsUpLog(c *C) {144func (cs *clientSuite) TestConfigureSetsUpLog(c *C) {
@@ -179,41 +198,70 @@
179}198}
180199
181func (cs *clientSuite) TestConfigureBailsOnBadPEMFilename(c *C) {200func (cs *clientSuite) TestConfigureBailsOnBadPEMFilename(c *C) {
182 ioutil.WriteFile(cs.configPath, []byte(`201 cs.writeTestConfig(map[string]interface{}{
183{202 "cert_pem_file": "/a/b/c",
184 "exchange_timeout": "10ms",203 })
185 "stabilizing_timeout": "0ms",
186 "connectivity_check_url": "",
187 "connectivity_check_md5": "",
188 "addr": ":0",
189 "cert_pem_file": "/a/b/c",
190 "log_level": "debug",
191 "recheck_timeout": "3h"
192}`), 0600)
193
194 cli := NewPushClient(cs.configPath, cs.leveldbPath)204 cli := NewPushClient(cs.configPath, cs.leveldbPath)
195 err := cli.configure()205 err := cli.configure()
196 c.Assert(err, ErrorMatches, "reading PEM file: .*")206 c.Assert(err, ErrorMatches, "reading PEM file: .*")
197}207}
198208
199func (cs *clientSuite) TestConfigureBailsOnBadPEM(c *C) {209func (cs *clientSuite) TestConfigureBailsOnBadPEM(c *C) {
200 ioutil.WriteFile(cs.configPath, []byte(`210 cs.writeTestConfig(map[string]interface{}{
201{211 "cert_pem_file": "/etc/passwd",
202 "exchange_timeout": "10ms",212 })
203 "stabilizing_timeout": "0ms",
204 "connectivity_check_url": "",
205 "connectivity_check_md5": "",
206 "addr": ":0",
207 "cert_pem_file": "/etc/passwd",
208 "log_level": "debug",
209 "recheck_timeout": "3h"
210}`), 0600)
211
212 cli := NewPushClient(cs.configPath, cs.leveldbPath)213 cli := NewPushClient(cs.configPath, cs.leveldbPath)
213 err := cli.configure()214 err := cli.configure()
214 c.Assert(err, ErrorMatches, "no PEM found.*")215 c.Assert(err, ErrorMatches, "no PEM found.*")
215}216}
216217
218func (cs *clientSuite) TestConfigureBailsOnNoHosts(c *C) {
219 cs.writeTestConfig(map[string]interface{}{
220 "addr": " ",
221 })
222 cli := NewPushClient(cs.configPath, cs.leveldbPath)
223 err := cli.configure()
224 c.Assert(err, ErrorMatches, "no hosts specified")
225}
226
227func (cs *clientSuite) TestConfigureRemovesBlanksInAddr(c *C) {
228 cs.writeTestConfig(map[string]interface{}{
229 "addr": " foo: 443",
230 })
231 cli := NewPushClient(cs.configPath, cs.leveldbPath)
232 err := cli.configure()
233 c.Assert(err, IsNil)
234 c.Check(cli.config.Addr, Equals, "foo:443")
235}
236
237/*****************************************************************
238 deriveSessionConfig tests
239******************************************************************/
240
241func (cs *clientSuite) TestDeriveSessionConfig(c *C) {
242 cli := NewPushClient(cs.configPath, cs.leveldbPath)
243 err := cli.configure()
244 c.Assert(err, IsNil)
245 expected := session.ClientSessionConfig{
246 ConnectTimeout: 7 * time.Millisecond,
247 ExchangeTimeout: 10 * time.Millisecond,
248 HostsCachingExpiryTime: 1 * time.Hour,
249 ExpectAllRepairedTime: 30 * time.Minute,
250 PEM: cli.pem,
251 }
252 // sanity check that we are looking at all fields
253 vExpected := reflect.ValueOf(expected)
254 nf := vExpected.NumField()
255 for i := 0; i < nf; i++ {
256 fv := vExpected.Field(i)
257 // field isn't empty/zero
258 c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name))
259 }
260 // finally compare
261 conf := cli.deriveSessionConfig()
262 c.Check(conf, DeepEquals, expected)
263}
264
217/*****************************************************************265/*****************************************************************
218 getDeviceId tests266 getDeviceId tests
219******************************************************************/267******************************************************************/
@@ -308,6 +356,7 @@
308 cli := NewPushClient(cs.configPath, cs.leveldbPath)356 cli := NewPushClient(cs.configPath, cs.leveldbPath)
309 cli.log = cs.log357 cli.log = cs.log
310 c.Assert(cli.initSession(), IsNil)358 c.Assert(cli.initSession(), IsNil)
359 cs.log.ResetCapture()
311 cli.hasConnectivity = true360 cli.hasConnectivity = true
312 cli.handleErr(errors.New("bananas"))361 cli.handleErr(errors.New("bananas"))
313 c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n")362 c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n")
@@ -363,7 +412,7 @@
363func (cs *clientSuite) TestHandleConnStateC2D(c *C) {412func (cs *clientSuite) TestHandleConnStateC2D(c *C) {
364 cli := NewPushClient(cs.configPath, cs.leveldbPath)413 cli := NewPushClient(cs.configPath, cs.leveldbPath)
365 cli.log = cs.log414 cli.log = cs.log
366 cli.session, _ = session.NewSession(string(cli.config.Addr), cli.pem, cli.config.ExchangeTimeout.Duration, cli.deviceId, levelmap.NewLevelMap, cs.log)415 cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(), cli.deviceId, levelmap.NewLevelMap, cs.log)
367 cli.session.Dial()416 cli.session.Dial()
368 cli.hasConnectivity = true417 cli.hasConnectivity = true
369418
@@ -376,7 +425,7 @@
376func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) {425func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) {
377 cli := NewPushClient(cs.configPath, cs.leveldbPath)426 cli := NewPushClient(cs.configPath, cs.leveldbPath)
378 cli.log = cs.log427 cli.log = cs.log
379 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(), cli.deviceId, levelmap.NewLevelMap, cs.log)
380 cli.hasConnectivity = true429 cli.hasConnectivity = true
381430
382 cli.handleConnState(false)431 cli.handleConnState(false)
383432
=== modified file 'client/gethosts/gethost_test.go'
--- client/gethosts/gethost_test.go 2014-03-24 15:32:29 +0000
+++ client/gethosts/gethost_test.go 2014-03-31 16:43:44 +0000
@@ -61,18 +61,16 @@
61}61}
6262
63func (s *getHostsSuite) TestGetTimeout(c *C) {63func (s *getHostsSuite) TestGetTimeout(c *C) {
64 finish := make(chan bool, 1)64 started := make(chan bool, 1)
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) {
66 <-finish66 started <- true
67 time.Sleep(700 * time.Millisecond)
67 }))68 }))
68 defer func() {69 defer func() {
69 time.Sleep(100 * time.Millisecond) // work around -race issue70 <-started
70 ts.Close()71 ts.Close()
71 }()72 }()
72 defer func() {73 gh := New("foobar", ts.URL, 500*time.Millisecond)
73 finish <- true
74 }()
75 gh := New("foobar", ts.URL, 1*time.Second)
76 _, err := gh.Get()74 _, err := gh.Get()
77 c.Check(err, ErrorMatches, ".*closed.*")75 c.Check(err, ErrorMatches, ".*closed.*")
78}76}
7977
=== modified file 'client/session/session.go'
--- client/session/session.go 2014-03-26 16:26:36 +0000
+++ client/session/session.go 2014-03-31 16:43:44 +0000
@@ -23,14 +23,18 @@
23 "crypto/x509"23 "crypto/x509"
24 "errors"24 "errors"
25 "fmt"25 "fmt"
26 "math/rand"
27 "net"
28 "strings"
29 "sync"
30 "sync/atomic"
31 "time"
32
33 "launchpad.net/ubuntu-push/client/gethosts"
26 "launchpad.net/ubuntu-push/client/session/levelmap"34 "launchpad.net/ubuntu-push/client/session/levelmap"
27 "launchpad.net/ubuntu-push/logger"35 "launchpad.net/ubuntu-push/logger"
28 "launchpad.net/ubuntu-push/protocol"36 "launchpad.net/ubuntu-push/protocol"
29 "launchpad.net/ubuntu-push/util"37 "launchpad.net/ubuntu-push/util"
30 "math/rand"
31 "net"
32 "sync/atomic"
33 "time"
34)38)
3539
36var wireVersionBytes = []byte{protocol.ProtocolWireVersion}40var wireVersionBytes = []byte{protocol.ProtocolWireVersion}
@@ -45,6 +49,15 @@
45 protocol.NotificationsMsg49 protocol.NotificationsMsg
46}50}
4751
52// parseServerAddrSpec recognizes whether spec is a HTTP URL to get
53// hosts from or a |-separated list of host:port pairs.
54func parseServerAddrSpec(spec string) (hostsEndpoint string, fallbackHosts []string) {
55 if strings.HasPrefix(spec, "http") {
56 return spec, nil
57 }
58 return "", strings.Split(spec, "|")
59}
60
48// ClientSessionState is a way to broadly track the progress of the session61// ClientSessionState is a way to broadly track the progress of the session
49type ClientSessionState uint3262type ClientSessionState uint32
5063
@@ -56,15 +69,38 @@
56 Running69 Running
57)70)
5871
59// ClienSession holds a client<->server session and its configuration.72type hostGetter interface {
73 Get() ([]string, error)
74}
75
76// ClientSessionConfig groups the client session configuration.
77type ClientSessionConfig struct {
78 ConnectTimeout time.Duration
79 ExchangeTimeout time.Duration
80 HostsCachingExpiryTime time.Duration
81 ExpectAllRepairedTime time.Duration
82 PEM []byte
83}
84
85// ClientSession holds a client<->server session and its configuration.
60type ClientSession struct {86type ClientSession struct {
61 // configuration87 // configuration
62 DeviceId string88 DeviceId string
63 ServerAddr string89 ClientSessionConfig
64 ExchangeTimeout time.Duration90 Levels levelmap.LevelMap
65 Levels levelmap.LevelMap91 Protocolator func(net.Conn) protocol.Protocol
66 Protocolator func(net.Conn) protocol.Protocol92 // hosts
93 getHost hostGetter
94 fallbackHosts []string
95 deliveryHostsTimestamp time.Time
96 deliveryHosts []string
97 lastAttemptTimestamp time.Time
98 leftToTry int
99 tryHost int
100 // hook for testing
101 timeSince func(time.Time) time.Duration
67 // connection102 // connection
103 connLock sync.RWMutex
68 Connection net.Conn104 Connection net.Conn
69 Log logger.Logger105 Log logger.Logger
70 TLS *tls.Config106 TLS *tls.Config
@@ -77,7 +113,7 @@
77 MsgCh chan *Notification113 MsgCh chan *Notification
78}114}
79115
80func NewSession(serverAddr string, pem []byte, exchangeTimeout time.Duration,116func NewSession(serverAddrSpec string, conf ClientSessionConfig,
81 deviceId string, levelmapFactory func() (levelmap.LevelMap, error),117 deviceId string, levelmapFactory func() (levelmap.LevelMap, error),
82 log logger.Logger) (*ClientSession, error) {118 log logger.Logger) (*ClientSession, error) {
83 state := uint32(Disconnected)119 state := uint32(Disconnected)
@@ -85,19 +121,27 @@
85 if err != nil {121 if err != nil {
86 return nil, err122 return nil, err
87 }123 }
124 var getHost hostGetter
125 log.Infof("using addr: %v", serverAddrSpec)
126 hostsEndpoint, fallbackHosts := parseServerAddrSpec(serverAddrSpec)
127 if hostsEndpoint != "" {
128 getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout)
129 }
88 sess := &ClientSession{130 sess := &ClientSession{
89 ExchangeTimeout: exchangeTimeout,131 ClientSessionConfig: conf,
90 ServerAddr: serverAddr,132 getHost: getHost,
91 DeviceId: deviceId,133 fallbackHosts: fallbackHosts,
92 Log: log,134 DeviceId: deviceId,
93 Protocolator: protocol.NewProtocol0,135 Log: log,
94 Levels: levels,136 Protocolator: protocol.NewProtocol0,
95 TLS: &tls.Config{InsecureSkipVerify: true}, // XXX137 Levels: levels,
96 stateP: &state,138 TLS: &tls.Config{InsecureSkipVerify: true}, // XXX
139 stateP: &state,
140 timeSince: time.Since,
97 }141 }
98 if pem != nil {142 if sess.PEM != nil {
99 cp := x509.NewCertPool()143 cp := x509.NewCertPool()
100 ok := cp.AppendCertsFromPEM(pem)144 ok := cp.AppendCertsFromPEM(sess.PEM)
101 if !ok {145 if !ok {
102 return nil, errors.New("could not parse certificate")146 return nil, errors.New("could not parse certificate")
103 }147 }
@@ -114,15 +158,90 @@
114 atomic.StoreUint32(sess.stateP, uint32(state))158 atomic.StoreUint32(sess.stateP, uint32(state))
115}159}
116160
161func (sess *ClientSession) setConnection(conn net.Conn) {
162 sess.connLock.Lock()
163 defer sess.connLock.Unlock()
164 sess.Connection = conn
165}
166
167func (sess *ClientSession) getConnection() net.Conn {
168 sess.connLock.RLock()
169 defer sess.connLock.RUnlock()
170 return sess.Connection
171}
172
173// getHosts sets deliveryHosts possibly querying a remote endpoint
174func (sess *ClientSession) getHosts() error {
175 if sess.getHost != nil {
176 if sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime {
177 return nil
178 }
179 hosts, err := sess.getHost.Get()
180 if err != nil {
181 sess.Log.Errorf("getHosts: %v", err)
182 sess.setState(Error)
183 return err
184 }
185 sess.deliveryHostsTimestamp = time.Now()
186 sess.deliveryHosts = hosts
187 } else {
188 sess.deliveryHosts = sess.fallbackHosts
189 }
190 return nil
191}
192
193// startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts
194
195func (sess *ClientSession) startConnectionAttempt() {
196 if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime {
197 sess.tryHost = 0
198 }
199 sess.leftToTry = len(sess.deliveryHosts)
200 if sess.leftToTry == 0 {
201 panic("should have got hosts from config or remote at this point")
202 }
203 sess.lastAttemptTimestamp = time.Now()
204}
205
206func (sess *ClientSession) nextHostToTry() string {
207 if sess.leftToTry == 0 {
208 return ""
209 }
210 res := sess.deliveryHosts[sess.tryHost]
211 sess.tryHost = (sess.tryHost + 1) % len(sess.deliveryHosts)
212 sess.leftToTry--
213 return res
214}
215
216// we reached the Started state, we can retry with the same host if we
217// have to retry again
218func (sess *ClientSession) started() {
219 sess.tryHost--
220 if sess.tryHost == -1 {
221 sess.tryHost = len(sess.deliveryHosts) - 1
222 }
223 sess.setState(Started)
224}
225
117// connect to a server using the configuration in the ClientSession226// connect to a server using the configuration in the ClientSession
118// and set up the connection.227// and set up the connection.
119func (sess *ClientSession) connect() error {228func (sess *ClientSession) connect() error {
120 conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)229 sess.startConnectionAttempt()
121 if err != nil {230 var err error
122 sess.setState(Error)231 var conn net.Conn
123 return fmt.Errorf("connect: %s", err)232 for {
233 host := sess.nextHostToTry()
234 if host == "" {
235 sess.setState(Error)
236 return fmt.Errorf("connect: %s", err)
237 }
238 sess.Log.Debugf("trying to connect to: %v", host)
239 conn, err = net.DialTimeout("tcp", host, sess.ConnectTimeout)
240 if err == nil {
241 break
242 }
124 }243 }
125 sess.Connection = tls.Client(conn, sess.TLS)244 sess.setConnection(tls.Client(conn, sess.TLS))
126 sess.setState(Connected)245 sess.setState(Connected)
127 return nil246 return nil
128}247}
@@ -145,6 +264,8 @@
145 sess.doClose()264 sess.doClose()
146}265}
147func (sess *ClientSession) doClose() {266func (sess *ClientSession) doClose() {
267 sess.connLock.Lock()
268 defer sess.connLock.Unlock()
148 if sess.Connection != nil {269 if sess.Connection != nil {
149 sess.Connection.Close()270 sess.Connection.Close()
150 // we ignore Close errors, on purpose (the thinking being that271 // we ignore Close errors, on purpose (the thinking being that
@@ -224,7 +345,7 @@
224345
225// Call this when you've connected and want to start looping.346// Call this when you've connected and want to start looping.
226func (sess *ClientSession) start() error {347func (sess *ClientSession) start() error {
227 conn := sess.Connection348 conn := sess.getConnection()
228 err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout))349 err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout))
229 if err != nil {350 if err != nil {
230 sess.setState(Error)351 sess.setState(Error)
@@ -279,15 +400,19 @@
279 sess.proto = proto400 sess.proto = proto
280 sess.pingInterval = pingInterval401 sess.pingInterval = pingInterval
281 sess.Log.Debugf("Connected %v.", conn.LocalAddr())402 sess.Log.Debugf("Connected %v.", conn.LocalAddr())
282 sess.setState(Started)403 sess.started() // deals with choosing which host to retry with as well
283 return nil404 return nil
284}405}
285406
286// run calls connect, and if it works it calls start, and if it works407// run calls connect, and if it works it calls start, and if it works
287// it runs loop in a goroutine, and ships its return value over ErrCh.408// it runs loop in a goroutine, and ships its return value over ErrCh.
288func (sess *ClientSession) run(closer func(), connecter, starter, looper func() error) error {409func (sess *ClientSession) run(closer func(), hostGetter, connecter, starter, looper func() error) error {
289 closer()410 closer()
290 err := connecter()411 err := hostGetter()
412 if err != nil {
413 return err
414 }
415 err = connecter()
291 if err == nil {416 if err == nil {
292 err = starter()417 err = starter()
293 if err == nil {418 if err == nil {
@@ -317,7 +442,7 @@
317 // keep on trying.442 // keep on trying.
318 panic("can't Dial() without a protocol constructor.")443 panic("can't Dial() without a protocol constructor.")
319 }444 }
320 return sess.run(sess.doClose, sess.connect, sess.start, sess.loop)445 return sess.run(sess.doClose, sess.getHosts, sess.connect, sess.start, sess.loop)
321}446}
322447
323func init() {448func init() {
324449
=== modified file 'client/session/session_test.go'
--- client/session/session_test.go 2014-03-27 13:26:10 +0000
+++ client/session/session_test.go 2014-03-31 16:43:44 +0000
@@ -23,16 +23,21 @@
23 "fmt"23 "fmt"
24 "io"24 "io"
25 "io/ioutil"25 "io/ioutil"
26 "net"
27 "net/http"
28 "net/http/httptest"
29 "reflect"
30 "testing"
31 "time"
32
26 . "launchpad.net/gocheck"33 . "launchpad.net/gocheck"
34
27 "launchpad.net/ubuntu-push/client/session/levelmap"35 "launchpad.net/ubuntu-push/client/session/levelmap"
36 //"launchpad.net/ubuntu-push/client/gethosts"
28 "launchpad.net/ubuntu-push/logger"37 "launchpad.net/ubuntu-push/logger"
29 "launchpad.net/ubuntu-push/protocol"38 "launchpad.net/ubuntu-push/protocol"
30 helpers "launchpad.net/ubuntu-push/testing"39 helpers "launchpad.net/ubuntu-push/testing"
31 "launchpad.net/ubuntu-push/testing/condition"40 "launchpad.net/ubuntu-push/testing/condition"
32 "net"
33 "reflect"
34 "testing"
35 "time"
36)41)
3742
38func TestSession(t *testing.T) { TestingT(t) }43func TestSession(t *testing.T) { TestingT(t) }
@@ -181,23 +186,51 @@
181}186}
182187
183/****************************************************************188/****************************************************************
189 parseServerAddrSpec() tests
190****************************************************************/
191
192func (cs *clientSessionSuite) TestParseServerAddrSpec(c *C) {
193 hEp, fallbackHosts := parseServerAddrSpec("http://foo/hosts")
194 c.Check(hEp, Equals, "http://foo/hosts")
195 c.Check(fallbackHosts, IsNil)
196
197 hEp, fallbackHosts = parseServerAddrSpec("foo:443")
198 c.Check(hEp, Equals, "")
199 c.Check(fallbackHosts, DeepEquals, []string{"foo:443"})
200
201 hEp, fallbackHosts = parseServerAddrSpec("foo:443|bar:443")
202 c.Check(hEp, Equals, "")
203 c.Check(fallbackHosts, DeepEquals, []string{"foo:443", "bar:443"})
204}
205
206/****************************************************************
184 NewSession() tests207 NewSession() tests
185****************************************************************/208****************************************************************/
186209
210var dummyConf = ClientSessionConfig{}
211
187func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) {212func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) {
188 sess, err := NewSession("", nil, 0, "", cs.lvls, cs.log)213 sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log)
189 c.Check(sess, NotNil)214 c.Check(sess, NotNil)
190 c.Check(err, IsNil)215 c.Check(err, IsNil)
216 c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"})
191 // but no root CAs set217 // but no root CAs set
192 c.Check(sess.TLS.RootCAs, IsNil)218 c.Check(sess.TLS.RootCAs, IsNil)
193 c.Check(sess.State(), Equals, Disconnected)219 c.Check(sess.State(), Equals, Disconnected)
194}220}
195221
196var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert")222func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) {
223 sess, err := NewSession("http://foo/hosts", dummyConf, "wah", cs.lvls, cs.log)
224 c.Assert(err, IsNil)
225 c.Check(sess.getHost, NotNil)
226}
227
228var certfile string = helpers.SourceRelative("../../server/acceptance/ssl/testing.cert")
197var pem, _ = ioutil.ReadFile(certfile)229var pem, _ = ioutil.ReadFile(certfile)
198230
199func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) {231func (cs *clientSessionSuite) TestNewSessionPEMWorks(c *C) {
200 sess, err := NewSession("", pem, 0, "wah", cs.lvls, cs.log)232 conf := ClientSessionConfig{PEM: pem}
233 sess, err := NewSession("", conf, "wah", cs.lvls, cs.log)
201 c.Check(sess, NotNil)234 c.Check(sess, NotNil)
202 c.Assert(err, IsNil)235 c.Assert(err, IsNil)
203 c.Check(sess.TLS.RootCAs, NotNil)236 c.Check(sess.TLS.RootCAs, NotNil)
@@ -205,25 +238,172 @@
205238
206func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) {239func (cs *clientSessionSuite) TestNewSessionBadPEMFileContentFails(c *C) {
207 badpem := []byte("This is not the PEM you're looking for.")240 badpem := []byte("This is not the PEM you're looking for.")
208 sess, err := NewSession("", badpem, 0, "wah", cs.lvls, cs.log)241 conf := ClientSessionConfig{PEM: badpem}
242 sess, err := NewSession("", conf, "wah", cs.lvls, cs.log)
209 c.Check(sess, IsNil)243 c.Check(sess, IsNil)
210 c.Check(err, NotNil)244 c.Check(err, NotNil)
211}245}
212246
213func (cs *clientSessionSuite) TestNewSessionBadLevelMapFails(c *C) {247func (cs *clientSessionSuite) TestNewSessionBadLevelMapFails(c *C) {
214 ferr := func() (levelmap.LevelMap, error) { return nil, errors.New("Busted.") }248 ferr := func() (levelmap.LevelMap, error) { return nil, errors.New("Busted.") }
215 sess, err := NewSession("", nil, 0, "wah", ferr, cs.log)249 sess, err := NewSession("", dummyConf, "wah", ferr, cs.log)
216 c.Check(sess, IsNil)250 c.Check(sess, IsNil)
217 c.Assert(err, NotNil)251 c.Assert(err, NotNil)
218}252}
219253
220/****************************************************************254/****************************************************************
255 getHosts() tests
256****************************************************************/
257
258func (cs *clientSessionSuite) TestGetHostsFallback(c *C) {
259 fallback := []string{"foo:443", "bar:443"}
260 sess := &ClientSession{fallbackHosts: fallback}
261 err := sess.getHosts()
262 c.Assert(err, IsNil)
263 c.Check(sess.deliveryHosts, DeepEquals, fallback)
264}
265
266type testHostGetter struct {
267 hosts []string
268 err error
269}
270
271func (thg *testHostGetter) Get() ([]string, error) {
272 return thg.hosts, thg.err
273}
274
275func (cs *clientSessionSuite) TestGetHostsRemote(c *C) {
276 hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
277 sess := &ClientSession{getHost: hostGetter, timeSince: time.Since}
278 err := sess.getHosts()
279 c.Assert(err, IsNil)
280 c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
281}
282
283func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) {
284 sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)
285 c.Assert(err, IsNil)
286 hostsErr := errors.New("failed")
287 hostGetter := &testHostGetter{nil, hostsErr}
288 sess.getHost = hostGetter
289 err = sess.getHosts()
290 c.Assert(err, Equals, hostsErr)
291 c.Check(sess.deliveryHosts, IsNil)
292 c.Check(sess.State(), Equals, Error)
293}
294
295func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) {
296 hostGetter := &testHostGetter{[]string{"foo:443", "bar:443"}, nil}
297 sess := &ClientSession{
298 getHost: hostGetter,
299 ClientSessionConfig: ClientSessionConfig{
300 HostsCachingExpiryTime: 2 * time.Hour,
301 },
302 timeSince: time.Since,
303 }
304 err := sess.getHosts()
305 c.Assert(err, IsNil)
306 hostGetter.hosts = []string{"baz:443"}
307 // cached
308 err = sess.getHosts()
309 c.Assert(err, IsNil)
310 c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"})
311 // expired
312 sess.timeSince = func(ts time.Time) time.Duration {
313 return 3 * time.Hour
314 }
315 err = sess.getHosts()
316 c.Assert(err, IsNil)
317 c.Check(sess.deliveryHosts, DeepEquals, []string{"baz:443"})
318}
319
320/****************************************************************
321 startConnectionAttempt()/nextHostToTry()/started tests
322****************************************************************/
323
324func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) {
325 since := time.Since(time.Time{})
326 sess := &ClientSession{
327 ClientSessionConfig: ClientSessionConfig{
328 ExpectAllRepairedTime: 10 * time.Second,
329 },
330 timeSince: func(ts time.Time) time.Duration {
331 return since
332 },
333 deliveryHosts: []string{"foo:443", "bar:443"},
334 }
335 // start from first host
336 sess.startConnectionAttempt()
337 c.Check(sess.lastAttemptTimestamp, Not(Equals), 0)
338 c.Check(sess.tryHost, Equals, 0)
339 c.Check(sess.leftToTry, Equals, 2)
340 since = 1 * time.Second
341 sess.tryHost = 1
342 // just continue
343 sess.startConnectionAttempt()
344 c.Check(sess.tryHost, Equals, 1)
345 sess.tryHost = 2
346}
347
348func (cs *clientSessionSuite) TestStartConnectionAttemptNoHostsPanic(c *C) {
349 since := time.Since(time.Time{})
350 sess := &ClientSession{
351 ClientSessionConfig: ClientSessionConfig{
352 ExpectAllRepairedTime: 10 * time.Second,
353 },
354 timeSince: func(ts time.Time) time.Duration {
355 return since
356 },
357 }
358 c.Check(sess.startConnectionAttempt, PanicMatches, "should have got hosts from config or remote at this point")
359}
360
361func (cs *clientSessionSuite) TestNextHostToTry(c *C) {
362 sess := &ClientSession{
363 deliveryHosts: []string{"foo:443", "bar:443", "baz:443"},
364 tryHost: 0,
365 leftToTry: 3,
366 }
367 c.Check(sess.nextHostToTry(), Equals, "foo:443")
368 c.Check(sess.nextHostToTry(), Equals, "bar:443")
369 c.Check(sess.nextHostToTry(), Equals, "baz:443")
370 c.Check(sess.nextHostToTry(), Equals, "")
371 c.Check(sess.nextHostToTry(), Equals, "")
372 c.Check(sess.tryHost, Equals, 0)
373
374 sess.leftToTry = 3
375 sess.tryHost = 1
376 c.Check(sess.nextHostToTry(), Equals, "bar:443")
377 c.Check(sess.nextHostToTry(), Equals, "baz:443")
378 c.Check(sess.nextHostToTry(), Equals, "foo:443")
379 c.Check(sess.nextHostToTry(), Equals, "")
380 c.Check(sess.nextHostToTry(), Equals, "")
381 c.Check(sess.tryHost, Equals, 1)
382}
383
384func (cs *clientSessionSuite) TestStarted(c *C) {
385 sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log)
386 c.Assert(err, IsNil)
387
388 sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"}
389 sess.tryHost = 1
390
391 sess.started()
392 c.Check(sess.tryHost, Equals, 0)
393 c.Check(sess.State(), Equals, Started)
394
395 sess.started()
396 c.Check(sess.tryHost, Equals, 2)
397}
398
399/****************************************************************
221 connect() tests400 connect() tests
222****************************************************************/401****************************************************************/
223402
224func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) {403func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) {
225 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)404 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
226 c.Assert(err, IsNil)405 c.Assert(err, IsNil)
406 sess.deliveryHosts = []string{"nowhere"}
227 err = sess.connect()407 err = sess.connect()
228 c.Check(err, ErrorMatches, ".*connect.*address.*")408 c.Check(err, ErrorMatches, ".*connect.*address.*")
229 c.Check(sess.State(), Equals, Error)409 c.Check(sess.State(), Equals, Error)
@@ -233,20 +413,36 @@
233 srv, err := net.Listen("tcp", "localhost:0")413 srv, err := net.Listen("tcp", "localhost:0")
234 c.Assert(err, IsNil)414 c.Assert(err, IsNil)
235 defer srv.Close()415 defer srv.Close()
236 sess, err := NewSession(srv.Addr().String(), nil, 0, "wah", cs.lvls, cs.log)416 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
237 c.Assert(err, IsNil)417 c.Assert(err, IsNil)
238 err = sess.connect()418 sess.deliveryHosts = []string{srv.Addr().String()}
239 c.Check(err, IsNil)419 err = sess.connect()
240 c.Check(sess.Connection, NotNil)420 c.Check(err, IsNil)
241 c.Check(sess.State(), Equals, Connected)421 c.Check(sess.Connection, NotNil)
422 c.Check(sess.State(), Equals, Connected)
423}
424
425func (cs *clientSessionSuite) TestConnectSecondConnects(c *C) {
426 srv, err := net.Listen("tcp", "localhost:0")
427 c.Assert(err, IsNil)
428 defer srv.Close()
429 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
430 c.Assert(err, IsNil)
431 sess.deliveryHosts = []string{"nowhere", srv.Addr().String()}
432 err = sess.connect()
433 c.Check(err, IsNil)
434 c.Check(sess.Connection, NotNil)
435 c.Check(sess.State(), Equals, Connected)
436 c.Check(sess.tryHost, Equals, 0)
242}437}
243438
244func (cs *clientSessionSuite) TestConnectConnectFail(c *C) {439func (cs *clientSessionSuite) TestConnectConnectFail(c *C) {
245 srv, err := net.Listen("tcp", "localhost:0")440 srv, err := net.Listen("tcp", "localhost:0")
246 c.Assert(err, IsNil)441 c.Assert(err, IsNil)
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)
248 srv.Close()443 srv.Close()
249 c.Assert(err, IsNil)444 c.Assert(err, IsNil)
445 sess.deliveryHosts = []string{srv.Addr().String()}
250 err = sess.connect()446 err = sess.connect()
251 c.Check(err, ErrorMatches, ".*connection refused")447 c.Check(err, ErrorMatches, ".*connection refused")
252 c.Check(sess.State(), Equals, Error)448 c.Check(sess.State(), Equals, Error)
@@ -257,7 +453,7 @@
257****************************************************************/453****************************************************************/
258454
259func (cs *clientSessionSuite) TestClose(c *C) {455func (cs *clientSessionSuite) TestClose(c *C) {
260 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)456 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
261 c.Assert(err, IsNil)457 c.Assert(err, IsNil)
262 sess.Connection = &testConn{Name: "TestClose"}458 sess.Connection = &testConn{Name: "TestClose"}
263 sess.Close()459 sess.Close()
@@ -266,7 +462,7 @@
266}462}
267463
268func (cs *clientSessionSuite) TestCloseTwice(c *C) {464func (cs *clientSessionSuite) TestCloseTwice(c *C) {
269 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)465 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
270 c.Assert(err, IsNil)466 c.Assert(err, IsNil)
271 sess.Connection = &testConn{Name: "TestCloseTwice"}467 sess.Connection = &testConn{Name: "TestCloseTwice"}
272 sess.Close()468 sess.Close()
@@ -277,7 +473,7 @@
277}473}
278474
279func (cs *clientSessionSuite) TestCloseFails(c *C) {475func (cs *clientSessionSuite) TestCloseFails(c *C) {
280 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)476 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
281 c.Assert(err, IsNil)477 c.Assert(err, IsNil)
282 sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)}478 sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)}
283 sess.Close()479 sess.Close()
@@ -291,7 +487,7 @@
291func (d *derp) Stop() { d.stopped = true }487func (d *derp) Stop() { d.stopped = true }
292488
293func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) {489func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) {
294 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)490 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
295 c.Assert(err, IsNil)491 c.Assert(err, IsNil)
296 ar := new(derp)492 ar := new(derp)
297 sess.retrier = ar493 sess.retrier = ar
@@ -308,7 +504,7 @@
308504
309func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) {505func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) {
310 // checks that AutoRedial sets up a retrier and tries redialing it506 // checks that AutoRedial sets up a retrier and tries redialing it
311 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)507 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
312 c.Assert(err, IsNil)508 c.Assert(err, IsNil)
313 ar := new(derp)509 ar := new(derp)
314 sess.retrier = ar510 sess.retrier = ar
@@ -319,7 +515,7 @@
319515
320func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) {516func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) {
321 // checks that AutoRedial stops the previous retrier517 // checks that AutoRedial stops the previous retrier
322 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)518 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
323 c.Assert(err, IsNil)519 c.Assert(err, IsNil)
324 ch := make(chan uint32)520 ch := make(chan uint32)
325 c.Check(sess.retrier, IsNil)521 c.Check(sess.retrier, IsNil)
@@ -344,7 +540,10 @@
344540
345func (s *msgSuite) SetUpTest(c *C) {541func (s *msgSuite) SetUpTest(c *C) {
346 var err error542 var err error
347 s.sess, err = NewSession("", nil, time.Millisecond, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug"))543 conf := ClientSessionConfig{
544 ExchangeTimeout: time.Millisecond,
545 }
546 s.sess, err = NewSession("", conf, "wah", levelmap.NewLevelMap, helpers.NewTestLogger(c, "debug"))
348 c.Assert(err, IsNil)547 c.Assert(err, IsNil)
349 s.sess.Connection = &testConn{Name: "TestHandle*"}548 s.sess.Connection = &testConn{Name: "TestHandle*"}
350 s.errCh = make(chan error, 1)549 s.errCh = make(chan error, 1)
@@ -519,7 +718,7 @@
519 start() tests718 start() tests
520****************************************************************/719****************************************************************/
521func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) {720func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) {
522 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)721 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
523 c.Assert(err, IsNil)722 c.Assert(err, IsNil)
524 sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails",723 sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails",
525 DeadlineCondition: condition.Work(false)} // setdeadline will fail724 DeadlineCondition: condition.Work(false)} // setdeadline will fail
@@ -529,7 +728,7 @@
529}728}
530729
531func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) {730func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) {
532 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)731 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
533 c.Assert(err, IsNil)732 c.Assert(err, IsNil)
534 sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails",733 sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails",
535 WriteCondition: condition.Work(false)} // write will fail734 WriteCondition: condition.Work(false)} // write will fail
@@ -539,7 +738,7 @@
539}738}
540739
541func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) {740func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) {
542 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)741 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
543 c.Assert(err, IsNil)742 c.Assert(err, IsNil)
544 sess.Levels = &brokenLevelMap{}743 sess.Levels = &brokenLevelMap{}
545 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}744 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}
@@ -559,7 +758,7 @@
559}758}
560759
561func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) {760func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) {
562 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)761 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
563 c.Assert(err, IsNil)762 c.Assert(err, IsNil)
564 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}763 sess.Connection = &testConn{Name: "TestStartConnectMessageFails"}
565 errCh := make(chan error, 1)764 errCh := make(chan error, 1)
@@ -585,7 +784,7 @@
585}784}
586785
587func (cs *clientSessionSuite) TestStartConnackReadError(c *C) {786func (cs *clientSessionSuite) TestStartConnackReadError(c *C) {
588 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)787 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
589 c.Assert(err, IsNil)788 c.Assert(err, IsNil)
590 sess.Connection = &testConn{Name: "TestStartConnackReadError"}789 sess.Connection = &testConn{Name: "TestStartConnackReadError"}
591 errCh := make(chan error, 1)790 errCh := make(chan error, 1)
@@ -609,7 +808,7 @@
609}808}
610809
611func (cs *clientSessionSuite) TestStartBadConnack(c *C) {810func (cs *clientSessionSuite) TestStartBadConnack(c *C) {
612 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)811 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
613 c.Assert(err, IsNil)812 c.Assert(err, IsNil)
614 sess.Connection = &testConn{Name: "TestStartBadConnack"}813 sess.Connection = &testConn{Name: "TestStartBadConnack"}
615 errCh := make(chan error, 1)814 errCh := make(chan error, 1)
@@ -633,7 +832,7 @@
633}832}
634833
635func (cs *clientSessionSuite) TestStartNotConnack(c *C) {834func (cs *clientSessionSuite) TestStartNotConnack(c *C) {
636 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)835 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
637 c.Assert(err, IsNil)836 c.Assert(err, IsNil)
638 sess.Connection = &testConn{Name: "TestStartBadConnack"}837 sess.Connection = &testConn{Name: "TestStartBadConnack"}
639 errCh := make(chan error, 1)838 errCh := make(chan error, 1)
@@ -657,7 +856,7 @@
657}856}
658857
659func (cs *clientSessionSuite) TestStartWorks(c *C) {858func (cs *clientSessionSuite) TestStartWorks(c *C) {
660 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)859 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
661 c.Assert(err, IsNil)860 c.Assert(err, IsNil)
662 sess.Connection = &testConn{Name: "TestStartWorks"}861 sess.Connection = &testConn{Name: "TestStartWorks"}
663 errCh := make(chan error, 1)862 errCh := make(chan error, 1)
@@ -688,34 +887,49 @@
688 run() tests887 run() tests
689****************************************************************/888****************************************************************/
690889
691func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {890func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) {
692 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)891 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
693 c.Assert(err, IsNil)892 c.Assert(err, IsNil)
694 failure := errors.New("TestRunBailsIfConnectFails")893 failure := errors.New("TestRunBailsIfHostGetterFails")
695 has_closed := false894 has_closed := false
696 err = sess.run(895 err = sess.run(
697 func() { has_closed = true },896 func() { has_closed = true },
698 func() error { return failure },897 func() error { return failure },
699 nil,898 nil,
899 nil,
700 nil)900 nil)
701 c.Check(err, Equals, failure)901 c.Check(err, Equals, failure)
702 c.Check(has_closed, Equals, true)902 c.Check(has_closed, Equals, true)
703}903}
704904
905func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) {
906 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
907 c.Assert(err, IsNil)
908 failure := errors.New("TestRunBailsIfConnectFails")
909 err = sess.run(
910 func() {},
911 func() error { return nil },
912 func() error { return failure },
913 nil,
914 nil)
915 c.Check(err, Equals, failure)
916}
917
705func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) {918func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) {
706 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)919 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
707 c.Assert(err, IsNil)920 c.Assert(err, IsNil)
708 failure := errors.New("TestRunBailsIfStartFails")921 failure := errors.New("TestRunBailsIfStartFails")
709 err = sess.run(922 err = sess.run(
710 func() {},923 func() {},
711 func() error { return nil },924 func() error { return nil },
925 func() error { return nil },
712 func() error { return failure },926 func() error { return failure },
713 nil)927 nil)
714 c.Check(err, Equals, failure)928 c.Check(err, Equals, failure)
715}929}
716930
717func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) {931func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) {
718 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)932 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
719 c.Assert(err, IsNil)933 c.Assert(err, IsNil)
720 // just to make a point: until here we haven't set ErrCh & MsgCh (no934 // just to make a point: until here we haven't set ErrCh & MsgCh (no
721 // biggie if this stops being true)935 // biggie if this stops being true)
@@ -727,6 +941,7 @@
727 func() {},941 func() {},
728 func() error { return nil },942 func() error { return nil },
729 func() error { return nil },943 func() error { return nil },
944 func() error { return nil },
730 func() error { sess.MsgCh <- notf; return <-failureCh })945 func() error { sess.MsgCh <- notf; return <-failureCh })
731 c.Check(err, Equals, nil)946 c.Check(err, Equals, nil)
732 // if run doesn't error it sets up the channels947 // if run doesn't error it sets up the channels
@@ -744,7 +959,7 @@
744****************************************************************/959****************************************************************/
745960
746func (cs *clientSessionSuite) TestJitter(c *C) {961func (cs *clientSessionSuite) TestJitter(c *C) {
747 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)962 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
748 c.Assert(err, IsNil)963 c.Assert(err, IsNil)
749 num_tries := 20 // should do the math964 num_tries := 20 // should do the math
750 spread := time.Second //965 spread := time.Second //
@@ -776,12 +991,17 @@
776991
777func (cs *clientSessionSuite) TestDialPanics(c *C) {992func (cs *clientSessionSuite) TestDialPanics(c *C) {
778 // one last unhappy test993 // one last unhappy test
779 sess, err := NewSession("", nil, 0, "wah", cs.lvls, cs.log)994 sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log)
780 c.Assert(err, IsNil)995 c.Assert(err, IsNil)
781 sess.Protocolator = nil996 sess.Protocolator = nil
782 c.Check(sess.Dial, PanicMatches, ".*protocol constructor.")997 c.Check(sess.Dial, PanicMatches, ".*protocol constructor.")
783}998}
784999
1000var (
1001 dialTestTimeout = 100 * time.Millisecond
1002 dialTestConf = ClientSessionConfig{ExchangeTimeout: dialTestTimeout}
1003)
1004
785func (cs *clientSessionSuite) TestDialWorks(c *C) {1005func (cs *clientSessionSuite) TestDialWorks(c *C) {
786 // happy path thoughts1006 // happy path thoughts
787 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)1007 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
@@ -791,10 +1011,22 @@
791 SessionTicketsDisabled: true,1011 SessionTicketsDisabled: true,
792 }1012 }
7931013
794 timeout := 100 * time.Millisecond
795 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)1014 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
796 c.Assert(err, IsNil)1015 c.Assert(err, IsNil)
797 sess, err := NewSession(lst.Addr().String(), nil, timeout, "wah", cs.lvls, cs.log)1016 // advertise
1017 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1018 b, err := json.Marshal(map[string]interface{}{
1019 "hosts": []string{"nowhere", lst.Addr().String()},
1020 })
1021 if err != nil {
1022 panic(err)
1023 }
1024 w.Header().Set("Content-Type", "application/json")
1025 w.Write(b)
1026 }))
1027 defer ts.Close()
1028
1029 sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log)
798 c.Assert(err, IsNil)1030 c.Assert(err, IsNil)
799 tconn := &testConn{CloseCondition: condition.Fail2Work(10)}1031 tconn := &testConn{CloseCondition: condition.Fail2Work(10)}
800 sess.Connection = tconn1032 sess.Connection = tconn
@@ -819,10 +1051,13 @@
819 c.Check(tconn.CloseCondition.String(), Matches, ".* 9 to go.")1051 c.Check(tconn.CloseCondition.String(), Matches, ".* 9 to go.")
8201052
821 // now, start: 1. protocol version1053 // now, start: 1. protocol version
822 v, err := protocol.ReadWireFormatVersion(srv, timeout)1054 v, err := protocol.ReadWireFormatVersion(srv, dialTestTimeout)
823 c.Assert(err, IsNil)1055 c.Assert(err, IsNil)
824 c.Assert(v, Equals, protocol.ProtocolWireVersion)1056 c.Assert(v, Equals, protocol.ProtocolWireVersion)
8251057
1058 // if something goes wrong session would try the first/other host
1059 c.Check(sess.tryHost, Equals, 0)
1060
826 // 2. "connect" (but on the fake protcol above! woo)1061 // 2. "connect" (but on the fake protcol above! woo)
8271062
828 c.Check(takeNext(downCh), Equals, "deadline 100ms")1063 c.Check(takeNext(downCh), Equals, "deadline 100ms")
@@ -843,6 +1078,9 @@
843 c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"})1078 c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"})
844 upCh <- nil1079 upCh <- nil
8451080
1081 // session would retry the same host
1082 c.Check(sess.tryHost, Equals, 1)
1083
846 // and broadcasts...1084 // and broadcasts...
847 b := &protocol.BroadcastMsg{1085 b := &protocol.BroadcastMsg{
848 Type: "broadcast",1086 Type: "broadcast",
@@ -870,3 +1108,30 @@
870 upCh <- failure1108 upCh <- failure
871 c.Check(<-sess.ErrCh, Equals, failure)1109 c.Check(<-sess.ErrCh, Equals, failure)
872}1110}
1111
1112func (cs *clientSessionSuite) TestDialWorksDirect(c *C) {
1113 // happy path thoughts
1114 cert, err := tls.X509KeyPair(helpers.TestCertPEMBlock, helpers.TestKeyPEMBlock)
1115 c.Assert(err, IsNil)
1116 tlsCfg := &tls.Config{
1117 Certificates: []tls.Certificate{cert},
1118 SessionTicketsDisabled: true,
1119 }
1120
1121 lst, err := tls.Listen("tcp", "localhost:0", tlsCfg)
1122 c.Assert(err, IsNil)
1123 sess, err := NewSession(lst.Addr().String(), dialTestConf, "wah", cs.lvls, cs.log)
1124 c.Assert(err, IsNil)
1125 defer sess.Close()
1126
1127 upCh := make(chan interface{}, 5)
1128 downCh := make(chan interface{}, 5)
1129 proto := &testProtocol{up: upCh, down: downCh}
1130 sess.Protocolator = func(net.Conn) protocol.Protocol { return proto }
1131
1132 go sess.Dial()
1133
1134 _, err = lst.Accept()
1135 c.Assert(err, IsNil)
1136 // connect done
1137}
8731138
=== modified file 'debian/config.json'
--- debian/config.json 2014-03-20 12:17:40 +0000
+++ debian/config.json 2014-03-31 16:43:44 +0000
@@ -1,5 +1,8 @@
1{1{
2 "connect_timeout": "20s",
2 "exchange_timeout": "30s",3 "exchange_timeout": "30s",
4 "hosts_cache_expiry": "12h",
5 "expect_all_repaired": "40m",
3 "addr": "push-delivery.ubuntu.com:443",6 "addr": "push-delivery.ubuntu.com:443",
4 "cert_pem_file": "",7 "cert_pem_file": "",
5 "stabilizing_timeout": "2s",8 "stabilizing_timeout": "2s",
69
=== added directory 'sampleconfigs'
=== renamed file 'server/acceptance/config/config.json' => 'sampleconfigs/dev.json'
--- server/acceptance/config/config.json 2014-01-17 17:20:34 +0000
+++ sampleconfigs/dev.json 2014-03-31 16:43:44 +0000
@@ -4,8 +4,8 @@
4 "broker_queue_size": 10000,4 "broker_queue_size": 10000,
5 "session_queue_size": 10,5 "session_queue_size": 10,
6 "addr": "127.0.0.1:9090",6 "addr": "127.0.0.1:9090",
7 "key_pem_file": "testing.key",7 "key_pem_file": "../server/acceptance/ssl/testing.key",
8 "cert_pem_file": "testing.cert",8 "cert_pem_file": "../server/acceptance/ssl/testing.cert",
9 "http_addr": "127.0.0.1:8888",9 "http_addr": "127.0.0.1:8888",
10 "http_read_timeout": "5s",10 "http_read_timeout": "5s",
11 "http_write_timeout": "5s"11 "http_write_timeout": "5s"
1212
=== renamed directory 'server/acceptance/config' => 'server/acceptance/ssl'
=== modified file 'server/acceptance/suites/helpers.go'
--- server/acceptance/suites/helpers.go 2014-03-25 19:08:00 +0000
+++ server/acceptance/suites/helpers.go 2014-03-31 16:43:44 +0000
@@ -48,8 +48,8 @@
48 "session_queue_size": 10,48 "session_queue_size": 10,
49 "broker_queue_size": 100,49 "broker_queue_size": 100,
50 "addr": addr,50 "addr": addr,
51 "key_pem_file": helpers.SourceRelative("../config/testing.key"),51 "key_pem_file": helpers.SourceRelative("../ssl/testing.key"),
52 "cert_pem_file": helpers.SourceRelative("../config/testing.cert"),52 "cert_pem_file": helpers.SourceRelative("../ssl/testing.cert"),
53 })53 })
54}54}
5555
5656
=== modified file 'server/acceptance/suites/suite.go'
--- server/acceptance/suites/suite.go 2014-03-25 19:08:00 +0000
+++ server/acceptance/suites/suite.go 2014-03-31 16:43:44 +0000
@@ -128,9 +128,9 @@
128}128}
129129
130func testClientSession(addr string, deviceId string, reportPings bool) *acceptance.ClientSession {130func testClientSession(addr string, deviceId string, reportPings bool) *acceptance.ClientSession {
131 certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../config/testing.cert"))131 certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert"))
132 if err != nil {132 if err != nil {
133 panic(fmt.Sprintf("could not read config/testing.cert: %v", err))133 panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err))
134 }134 }
135 return &acceptance.ClientSession{135 return &acceptance.ClientSession{
136 ExchangeTimeout: 100 * time.Millisecond,136 ExchangeTimeout: 100 * time.Millisecond,

Subscribers

People subscribed via source and target branches