Merge lp:~pedronis/ubuntu-push/delivery-hosts-details into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Superseded
Proposed branch: lp:~pedronis/ubuntu-push/delivery-hosts-details
Merge into: lp:ubuntu-push
Diff against target: 1245 lines (+595/-128)
7 files modified
.bzrignore (+1/-0)
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 (+306/-41)
debian/config.json (+3/-0)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/delivery-hosts-details
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+213485@code.launchpad.net

Commit message

small sanity details

Description of the change

small sanity details:

* panic if there are no hosts specified instead of getting stuck in infinite retrying

* check that host bit of the config isn't empty

* tweak test with -race problems differently after some discussion in the golang bug tracker

To post a comment you must log in.

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 14:33:42 +0000
@@ -11,3 +11,4 @@
11debian/*.ex11debian/*.ex
12debian/*.EX12debian/*.EX
13debian/*.substvars13debian/*.substvars
14ubuntu-push-client
1415
=== modified file 'client/client.go'
--- client/client.go 2014-03-26 16:26:36 +0000
+++ client/client.go 2014-03-31 14:33:42 +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 14:33:42 +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/config/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 14:33:42 +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 14:33:42 +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 14:33:42 +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
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
196var certfile string = helpers.SourceRelative("../../server/acceptance/config/testing.cert")228var certfile string = helpers.SourceRelative("../../server/acceptance/config/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 14:33:42 +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",

Subscribers

People subscribed via source and target branches