blob: e4ba7f189c07a94586e00cc6b77f15d6ceefcb8f [file] [log] [blame]
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -08001package ipc_test
2
3import (
4 "fmt"
5 "os"
6 "reflect"
7 "sort"
8 "strings"
9 "testing"
10 "time"
11
12 "v.io/core/veyron2/context"
13 "v.io/core/veyron2/ipc"
14 "v.io/core/veyron2/naming"
Suharsh Sivakumar90da4c22015-02-12 15:45:56 -080015 "v.io/core/veyron2/options"
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080016 "v.io/core/veyron2/security"
Todd Wangff73e1f2015-02-10 21:45:52 -080017 "v.io/core/veyron2/verror"
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080018 "v.io/core/veyron2/vtrace"
19
20 "v.io/core/veyron/lib/expect"
21 "v.io/core/veyron/lib/flags"
22 "v.io/core/veyron/lib/modules"
23 "v.io/core/veyron/lib/modules/core"
24 tsecurity "v.io/core/veyron/lib/testutil/security"
25 _ "v.io/core/veyron/profiles"
26 iipc "v.io/core/veyron/runtimes/google/ipc"
27 imanager "v.io/core/veyron/runtimes/google/ipc/stream/manager"
28 "v.io/core/veyron/runtimes/google/ipc/stream/vc"
29 inaming "v.io/core/veyron/runtimes/google/naming"
30 tnaming "v.io/core/veyron/runtimes/google/testing/mocks/naming"
31 ivtrace "v.io/core/veyron/runtimes/google/vtrace"
32)
33
34func testContext() *context.T {
35 ctx, _ := context.WithTimeout(testContextWithoutDeadline(), 20*time.Second)
36 return ctx
37}
38
39func testContextWithoutDeadline() *context.T {
40 ctx, _ := context.RootContext()
41 ctx, err := ivtrace.Init(ctx, flags.VtraceFlags{})
42 if err != nil {
43 panic(err)
44 }
45 ctx, _ = vtrace.SetNewTrace(ctx)
46 return ctx
47}
48
49type testServer struct{}
50
51func (*testServer) Echo(ctx ipc.ServerContext, arg string) string {
52 return fmt.Sprintf("method:%q,suffix:%q,arg:%q", ctx.Method(), ctx.Suffix(), arg)
53}
54
55type testServerAuthorizer struct{}
56
57func (testServerAuthorizer) Authorize(c security.Context) error {
58 return nil
59}
60
61type testServerDisp struct{ server interface{} }
62
63func (t testServerDisp) Lookup(suffix string) (interface{}, security.Authorizer, error) {
64 return t.server, testServerAuthorizer{}, nil
65}
66
67type proxyHandle struct {
68 ns naming.Namespace
69 sh *modules.Shell
70 proxy modules.Handle
71 name string
72}
73
74func (h *proxyHandle) Start(t *testing.T, ctx *context.T, args ...string) error {
75 sh, err := modules.NewShell(nil, nil)
76 if err != nil {
77 t.Fatalf("unexpected error: %s", err)
78 }
79 h.sh = sh
80 p, err := sh.Start(core.ProxyServerCommand, nil, args...)
81 if err != nil {
82 t.Fatalf("unexpected error: %s", err)
83 }
84 h.proxy = p
85 s := expect.NewSession(t, p.Stdout(), time.Minute)
86 s.ReadLine()
87 h.name = s.ExpectVar("PROXY_NAME")
88 if len(h.name) == 0 {
89 t.Fatalf("failed to get PROXY_NAME from proxyd")
90 }
91 return h.ns.Mount(ctx, "proxy", h.name, time.Hour)
92}
93
94func (h *proxyHandle) Stop(ctx *context.T) error {
95 defer h.sh.Cleanup(os.Stderr, os.Stderr)
96 if err := h.proxy.Shutdown(os.Stderr, os.Stderr); err != nil {
97 return err
98 }
99 if len(h.name) == 0 {
100 return nil
101 }
102 return h.ns.Unmount(ctx, "proxy", h.name)
103}
104
105func TestProxyOnly(t *testing.T) {
106 listenSpec := ipc.ListenSpec{Proxy: "proxy"}
107 testProxy(t, listenSpec)
108}
109
110func TestProxy(t *testing.T) {
111 proxyListenSpec := ipc.ListenSpec{Addrs: ipc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
112 proxyListenSpec.Proxy = "proxy"
113 testProxy(t, proxyListenSpec)
114}
115
116func TestWSProxy(t *testing.T) {
117 proxyListenSpec := ipc.ListenSpec{Addrs: ipc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
118 proxyListenSpec.Proxy = "proxy"
119 // The proxy uses websockets only, but the server is using tcp.
120 testProxy(t, proxyListenSpec, "--veyron.tcp.protocol=ws")
121}
122
123func testProxy(t *testing.T, spec ipc.ListenSpec, args ...string) {
124 sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
125 defer sm.Shutdown()
126 ns := tnaming.NewSimpleNamespace()
127 client, err := iipc.InternalNewClient(sm, ns, vc.LocalPrincipal{tsecurity.NewPrincipal("client")})
128 if err != nil {
129 t.Fatal(err)
130 }
131 defer client.Close()
132 ctx := testContext()
133 server, err := iipc.InternalNewServer(ctx, sm, ns, nil, vc.LocalPrincipal{tsecurity.NewPrincipal("server")})
134 if err != nil {
135 t.Fatal(err)
136 }
137 defer server.Stop()
138
139 // If no address is specified then we'll only 'listen' via
140 // the proxy.
141 hasLocalListener := len(spec.Addrs) > 0 && len(spec.Addrs[0].Address) != 0
142
143 name := "mountpoint/server/suffix"
Suharsh Sivakumar90da4c22015-02-12 15:45:56 -0800144 makeCall := func(opts ...ipc.CallOpt) (string, error) {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800145 ctx, _ := context.WithDeadline(testContext(), time.Now().Add(5*time.Second))
146 // Let's fail fast so that the tests don't take as long to run.
Suharsh Sivakumar90da4c22015-02-12 15:45:56 -0800147 call, err := client.StartCall(ctx, name, "Echo", []interface{}{"batman"}, opts...)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800148 if err != nil {
149 // proxy is down, we should return here/.... prepend
150 // the error with a well known string so that we can test for that.
151 return "", fmt.Errorf("RESOLVE: %s", err)
152 }
153 var result string
154 if err = call.Finish(&result); err != nil {
155 return "", err
156 }
157 return result, nil
158 }
159 proxy := &proxyHandle{ns: ns}
160 if err := proxy.Start(t, ctx, args...); err != nil {
161 t.Fatal(err)
162 }
163 defer proxy.Stop(ctx)
164 addrs := verifyMount(t, ctx, ns, spec.Proxy)
165 if len(addrs) != 1 {
166 t.Fatalf("failed to lookup proxy")
167 }
168
169 eps, err := server.Listen(spec)
170 if err != nil {
171 t.Fatal(err)
172 }
173 if err := server.ServeDispatcher("mountpoint/server", testServerDisp{&testServer{}}); err != nil {
174 t.Fatal(err)
175 }
176
177 // Proxy connections are started asynchronously, so we need to wait..
178 waitForMountTable := func(ch chan int, expect int) {
179 then := time.Now().Add(time.Minute)
180 for {
181 me, err := ns.Resolve(ctx, name)
182 if err == nil && len(me.Servers) == expect {
183 ch <- 1
184 return
185 }
186 if time.Now().After(then) {
187 t.Fatalf("timed out")
188 }
189 time.Sleep(100 * time.Millisecond)
190 }
191 }
192 waitForServerStatus := func(ch chan int, proxy string) {
193 then := time.Now().Add(time.Minute)
194 for {
195 status := server.Status()
196 if len(status.Proxies) == 1 && status.Proxies[0].Proxy == proxy {
197 ch <- 2
198 return
199 }
200 if time.Now().After(then) {
201 t.Fatalf("timed out")
202 }
203 time.Sleep(100 * time.Millisecond)
204 }
205 }
206 proxyEP, _ := naming.SplitAddressName(addrs[0])
207 proxiedEP, err := inaming.NewEndpoint(proxyEP)
208 if err != nil {
209 t.Fatalf("unexpected error for %q: %s", proxyEP, err)
210 }
211 proxiedEP.RID = naming.FixedRoutingID(0x555555555)
212 expectedNames := []string{naming.JoinAddressName(proxiedEP.String(), "suffix")}
213 if hasLocalListener {
214 expectedNames = append(expectedNames, naming.JoinAddressName(eps[0].String(), "suffix"))
215 }
216
217 // Proxy connetions are created asynchronously, so we wait for the
218 // expected number of endpoints to appear for the specified service name.
219 ch := make(chan int, 2)
220 go waitForMountTable(ch, len(expectedNames))
221 go waitForServerStatus(ch, spec.Proxy)
222 select {
223 case <-time.After(time.Minute):
224 t.Fatalf("timedout waiting for two entries in the mount table and server status")
225 case i := <-ch:
226 select {
227 case <-time.After(time.Minute):
228 t.Fatalf("timedout waiting for two entries in the mount table or server status")
229 case j := <-ch:
230 if !((i == 1 && j == 2) || (i == 2 && j == 1)) {
231 t.Fatalf("unexpected return values from waiters")
232 }
233 }
234 }
235
236 status := server.Status()
237 if got, want := status.Proxies[0].Endpoint, proxiedEP; !reflect.DeepEqual(got, want) {
238 t.Fatalf("got %q, want %q", got, want)
239 }
240
241 got := []string{}
242 for _, s := range verifyMount(t, ctx, ns, name) {
243 got = append(got, s)
244 }
245 sort.Strings(got)
246 sort.Strings(expectedNames)
247 if !reflect.DeepEqual(got, expectedNames) {
248 t.Errorf("got %v, want %v", got, expectedNames)
249 }
250
251 if hasLocalListener {
252 // Listen will publish both the local and proxied endpoint with the
253 // mount table, given that we're trying to test the proxy, we remove
254 // the local endpoint from the mount table entry! We have to remove both
255 // the tcp and the websocket address.
256 sep := eps[0].String()
257 ns.Unmount(ctx, "mountpoint/server", sep)
258 }
259
260 addrs = verifyMount(t, ctx, ns, name)
261 if len(addrs) != 1 {
262 t.Fatalf("failed to lookup proxy: addrs %v", addrs)
263 }
264
265 // Proxied endpoint should be published and RPC should succeed (through proxy)
266 const expected = `method:"Echo",suffix:"suffix",arg:"batman"`
267 if result, err := makeCall(); result != expected || err != nil {
268 t.Fatalf("Got (%v, %v) want (%v, nil)", result, err, expected)
269 }
270 // Proxy dies, calls should fail and the name should be unmounted.
271 if err := proxy.Stop(ctx); err != nil {
272 t.Fatal(err)
273 }
274
Suharsh Sivakumar90da4c22015-02-12 15:45:56 -0800275 if result, err := makeCall(options.NoRetry{}); err == nil || (!strings.HasPrefix(err.Error(), "RESOLVE") && !strings.Contains(err.Error(), "EOF")) {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800276 t.Fatalf(`Got (%v, %v) want ("", "RESOLVE: <err>" or "EOF") as proxy is down`, result, err)
277 }
278
279 for {
280 if _, err := ns.Resolve(ctx, name); err != nil {
281 break
282 }
283 time.Sleep(10 * time.Millisecond)
284 }
285 verifyMountMissing(t, ctx, ns, name)
286
287 status = server.Status()
288 if len(status.Proxies) != 1 || status.Proxies[0].Proxy != spec.Proxy || !verror.Is(status.Proxies[0].Error, verror.NoServers.ID) {
289 t.Fatalf("proxy status is incorrect: %v", status.Proxies)
290 }
291
292 // Proxy restarts, calls should eventually start succeeding.
293 if err := proxy.Start(t, ctx, args...); err != nil {
294 t.Fatal(err)
295 }
296
297 retries := 0
298 for {
299 if result, err := makeCall(); err == nil {
300 if result != expected {
301 t.Errorf("Got (%v, %v) want (%v, nil)", result, err, expected)
302 }
303 break
304 } else {
305 retries++
306 if retries > 10 {
307 t.Fatalf("Failed after 10 attempts: err: %s", err)
308 }
309 }
310 }
311}
312
313func verifyMount(t *testing.T, ctx *context.T, ns naming.Namespace, name string) []string {
314 me, err := ns.Resolve(ctx, name)
315 if err != nil {
316 t.Errorf("%s not found in mounttable", name)
317 return nil
318 }
319 return me.Names()
320}
321
322func verifyMountMissing(t *testing.T, ctx *context.T, ns naming.Namespace, name string) {
323 if me, err := ns.Resolve(ctx, name); err == nil {
324 names := me.Names()
325 t.Errorf("%s not supposed to be found in mounttable; got %d servers instead: %v", name, len(names), names)
326 }
327}
328
329func TestHelperProcess(t *testing.T) {
330 modules.DispatchInTest()
331}