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