@@ -34,24 +34,24 @@ type apiclientSuite struct {
34
34
35
35
var _ = gc .Suite (& apiclientSuite {})
36
36
37
- func (s * apiclientSuite ) TestConnectWebsocketToEnv (c * gc.C ) {
37
+ func (s * apiclientSuite ) TestDialAPIToEnv (c * gc.C ) {
38
38
info := s .APIInfo (c )
39
- conn , _ , err := api .ConnectWebsocket (info , api.DialOpts {})
39
+ conn , _ , err := api .DialAPI (info , api.DialOpts {})
40
40
c .Assert (err , jc .ErrorIsNil )
41
41
defer conn .Close ()
42
- assertConnAddrForEnv (c , conn , info .Addrs [0 ], s .State .ModelUUID (), "/api" )
42
+ assertConnAddrForModel (c , conn , info .Addrs [0 ], s .State .ModelUUID ())
43
43
}
44
44
45
- func (s * apiclientSuite ) TestConnectWebsocketToRoot (c * gc.C ) {
45
+ func (s * apiclientSuite ) TestDialAPIToRoot (c * gc.C ) {
46
46
info := s .APIInfo (c )
47
47
info .ModelTag = names .NewModelTag ("" )
48
- conn , _ , err := api .ConnectWebsocket (info , api.DialOpts {})
48
+ conn , _ , err := api .DialAPI (info , api.DialOpts {})
49
49
c .Assert (err , jc .ErrorIsNil )
50
50
defer conn .Close ()
51
51
assertConnAddrForRoot (c , conn , info .Addrs [0 ])
52
52
}
53
53
54
- func (s * apiclientSuite ) TestConnectWebsocketMultiple (c * gc.C ) {
54
+ func (s * apiclientSuite ) TestDialAPIMultiple (c * gc.C ) {
55
55
// Create a socket that proxies to the API server.
56
56
info := s .APIInfo (c )
57
57
serverAddr := info .Addrs [0 ]
@@ -60,22 +60,22 @@ func (s *apiclientSuite) TestConnectWebsocketMultiple(c *gc.C) {
60
60
61
61
// Check that we can use the proxy to connect.
62
62
info .Addrs = []string {proxy .Addr ()}
63
- conn , _ , err := api .ConnectWebsocket (info , api.DialOpts {})
63
+ conn , _ , err := api .DialAPI (info , api.DialOpts {})
64
64
c .Assert (err , jc .ErrorIsNil )
65
65
conn .Close ()
66
- assertConnAddrForEnv (c , conn , proxy .Addr (), s .State .ModelUUID (), "/api" )
66
+ assertConnAddrForModel (c , conn , proxy .Addr (), s .State .ModelUUID ())
67
67
68
68
// Now break Addrs[0], and ensure that Addrs[1]
69
69
// is successfully connected to.
70
70
proxy .Close ()
71
71
info .Addrs = []string {proxy .Addr (), serverAddr }
72
- conn , _ , err = api .ConnectWebsocket (info , api.DialOpts {})
72
+ conn , _ , err = api .DialAPI (info , api.DialOpts {})
73
73
c .Assert (err , jc .ErrorIsNil )
74
74
conn .Close ()
75
- assertConnAddrForEnv (c , conn , serverAddr , s .State .ModelUUID (), "/api" )
75
+ assertConnAddrForModel (c , conn , serverAddr , s .State .ModelUUID ())
76
76
}
77
77
78
- func (s * apiclientSuite ) TestConnectWebsocketMultipleError (c * gc.C ) {
78
+ func (s * apiclientSuite ) TestDialAPIMultipleError (c * gc.C ) {
79
79
listener , err := net .Listen ("tcp" , "127.0.0.1:0" )
80
80
c .Assert (err , jc .ErrorIsNil )
81
81
defer listener .Close ()
@@ -94,7 +94,7 @@ func (s *apiclientSuite) TestConnectWebsocketMultipleError(c *gc.C) {
94
94
info := s .APIInfo (c )
95
95
addr := listener .Addr ().String ()
96
96
info .Addrs = []string {addr , addr , addr }
97
- _ , _ , err = api .ConnectWebsocket (info , api.DialOpts {})
97
+ _ , _ , err = api .DialAPI (info , api.DialOpts {})
98
98
c .Assert (err , gc .ErrorMatches , `unable to connect to API: websocket.Dial wss://.*/model/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/api: .*` )
99
99
c .Assert (atomic .LoadInt32 (& count ), gc .Equals , int32 (3 ))
100
100
}
@@ -153,14 +153,116 @@ func (s *apiclientSuite) TestServerRoot(c *gc.C) {
153
153
}
154
154
155
155
func (s * apiclientSuite ) TestDialWebsocketStopped (c * gc.C ) {
156
- stopped := make (chan struct {})
157
156
f := api .NewWebsocketDialer (nil , api.DialOpts {})
157
+ stopped := make (chan struct {})
158
158
close (stopped )
159
159
result , err := f (stopped )
160
160
c .Assert (err , gc .Equals , parallel .ErrStopped )
161
161
c .Assert (result , gc .IsNil )
162
162
}
163
163
164
+ type apiDialInfo struct {
165
+ location string
166
+ hasRootCAs bool
167
+ serverName string
168
+ }
169
+
170
+ var openWithSNIHostnameTests = []struct {
171
+ about string
172
+ info * api.Info
173
+ expectDial apiDialInfo
174
+ }{{
175
+ about : "no cert; DNS name - use SNI hostname" ,
176
+ info : & api.Info {
177
+ Addrs : []string {"foo.com:1234" },
178
+ SNIHostName : "foo.com" ,
179
+ SkipLogin : true ,
180
+ },
181
+ expectDial : apiDialInfo {
182
+ location : "wss://foo.com:1234/api" ,
183
+ hasRootCAs : false ,
184
+ serverName : "foo.com" ,
185
+ },
186
+ }, {
187
+ about : "no cert; numeric IP address - use SNI hostname" ,
188
+ info : & api.Info {
189
+ Addrs : []string {"0.1.2.3:1234" },
190
+ SNIHostName : "foo.com" ,
191
+ SkipLogin : true ,
192
+ },
193
+ expectDial : apiDialInfo {
194
+ location : "wss://0.1.2.3:1234/api" ,
195
+ hasRootCAs : false ,
196
+ serverName : "foo.com" ,
197
+ },
198
+ }, {
199
+ about : "with cert; DNS name - use cert" ,
200
+ info : & api.Info {
201
+ Addrs : []string {"foo.com:1234" },
202
+ SNIHostName : "foo.com" ,
203
+ SkipLogin : true ,
204
+ CACert : jtesting .CACert ,
205
+ },
206
+ expectDial : apiDialInfo {
207
+ location : "wss://foo.com:1234/api" ,
208
+ hasRootCAs : true ,
209
+ serverName : "juju-apiserver" ,
210
+ },
211
+ }, {
212
+ about : "with cert; numeric IP address - use cert" ,
213
+ info : & api.Info {
214
+ Addrs : []string {"0.1.2.3:1234" },
215
+ SNIHostName : "foo.com" ,
216
+ SkipLogin : true ,
217
+ CACert : jtesting .CACert ,
218
+ },
219
+ expectDial : apiDialInfo {
220
+ location : "wss://0.1.2.3:1234/api" ,
221
+ hasRootCAs : true ,
222
+ serverName : "juju-apiserver" ,
223
+ },
224
+ }}
225
+
226
+ func (s * apiclientSuite ) TestOpenWithSNIHostname (c * gc.C ) {
227
+ for i , test := range openWithSNIHostnameTests {
228
+ c .Logf ("test %d: %v" , i , test .about )
229
+ s .testSNIHostName (c , test .info , test .expectDial )
230
+ }
231
+ }
232
+
233
+ // testSNIHostName tests that when the API is dialed with the given info,
234
+ // api.newWebsocketDialer is called with the expected information.
235
+ func (s * apiclientSuite ) testSNIHostName (c * gc.C , info * api.Info , expectDial apiDialInfo ) {
236
+ dialed := make (chan * websocket.Config )
237
+ fakeDialer := func (cfg * websocket.Config ) (* websocket.Conn , error ) {
238
+ dialed <- cfg
239
+ return nil , errors .New ("nope" )
240
+ }
241
+ done := make (chan struct {})
242
+ go func () {
243
+ defer close (done )
244
+ conn , err := api .Open (info , api.DialOpts {
245
+ DialWebsocket : fakeDialer ,
246
+ })
247
+ c .Check (conn , gc .Equals , nil )
248
+ c .Check (err , gc .ErrorMatches , `unable to connect to API: nope` )
249
+ }()
250
+ select {
251
+ case cfg := <- dialed :
252
+ c .Check (cfg .Location .String (), gc .Equals , expectDial .location )
253
+ c .Assert (cfg .TlsConfig , gc .NotNil )
254
+ c .Check (cfg .TlsConfig .RootCAs != nil , gc .Equals , expectDial .hasRootCAs )
255
+ c .Check (cfg .TlsConfig .ServerName , gc .Equals , expectDial .serverName )
256
+ case <- time .After (jtesting .LongWait ):
257
+ c .Fatalf ("timed out waiting for dial" )
258
+ }
259
+ select {
260
+ case <- done :
261
+ case <- time .After (jtesting .LongWait ):
262
+ c .Fatalf ("timed out waiting for API open" )
263
+ }
264
+ }
265
+
164
266
func (s * apiclientSuite ) TestOpenWithNoCACert (c * gc.C ) {
165
267
// This is hard to test as we have no way of affecting the system roots,
166
268
// so instead we check that the error that we get implies that
@@ -405,10 +507,10 @@ func (a *redirectAPIAdmin) RedirectInfo() (params.RedirectInfoResult, error) {
405
507
}, nil
406
508
}
407
509
408
- func assertConnAddrForEnv (c * gc.C , conn * websocket.Conn , addr , modelUUID , tail string ) {
409
- c .Assert (conn .RemoteAddr (), gc .Matches , "^ wss://" + addr + "/model/" + modelUUID + tail + "$ " )
510
+ func assertConnAddrForModel (c * gc.C , conn * websocket.Conn , addr , modelUUID string ) {
511
+ c .Assert (conn .RemoteAddr (). String () , gc .Equals , "wss://" + addr + "/model/" + modelUUID + "/api " )
410
512
}
411
513
412
514
func assertConnAddrForRoot (c * gc.C , conn * websocket.Conn , addr string ) {
413
- c .Assert (conn .RemoteAddr (), gc .Matches , "^ wss://" + addr + "/api$ " )
515
+ c .Assert (conn .RemoteAddr (). String () , gc .Matches , "wss://" + addr + "/api" )
414
516
}
0 commit comments