blob: 46c5078c80b15789d20cdeae07302c18fe36c202 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Suharsh Sivakumarcbfe4742015-03-11 14:54:22 -07005package test
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -08006
7import (
8 "fmt"
9 "os"
10 "reflect"
11 "sort"
12 "strings"
13 "testing"
14 "time"
15
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080016 "v.io/v23"
Jiri Simsa6ac95222015-02-23 16:11:49 -080017 "v.io/v23/context"
Todd Wang5082a552015-04-02 10:56:11 -070018 "v.io/v23/namespace"
Jiri Simsa6ac95222015-02-23 16:11:49 -080019 "v.io/v23/naming"
20 "v.io/v23/options"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070021 "v.io/v23/rpc"
Jiri Simsa6ac95222015-02-23 16:11:49 -080022 "v.io/v23/security"
23 "v.io/v23/verror"
24 "v.io/v23/vtrace"
Jiri Simsaffceefa2015-02-28 11:03:34 -080025 "v.io/x/ref/lib/flags"
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070026 _ "v.io/x/ref/runtime/factories/generic"
27 "v.io/x/ref/runtime/internal/lib/publisher"
28 inaming "v.io/x/ref/runtime/internal/naming"
29 irpc "v.io/x/ref/runtime/internal/rpc"
30 imanager "v.io/x/ref/runtime/internal/rpc/stream/manager"
31 "v.io/x/ref/runtime/internal/rpc/stream/proxy"
32 tnaming "v.io/x/ref/runtime/internal/testing/mocks/naming"
33 ivtrace "v.io/x/ref/runtime/internal/vtrace"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070034 "v.io/x/ref/test/modules"
Asim Shankar4a698282015-03-21 21:59:18 -070035 "v.io/x/ref/test/testutil"
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080036)
37
Asim Shankar7171a252015-03-07 14:41:40 -080038func testContext() (*context.T, func()) {
39 ctx, shutdown := v23.Init()
40 ctx, _ = context.WithTimeout(ctx, 20*time.Second)
41 var err error
42 if ctx, err = ivtrace.Init(ctx, flags.VtraceFlags{}); err != nil {
43 panic(err)
44 }
Todd Wangad492042015-04-17 15:58:40 -070045 ctx, _ = vtrace.WithNewTrace(ctx)
Asim Shankar7171a252015-03-07 14:41:40 -080046 return ctx, shutdown
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080047}
48
Todd Wang95873902015-05-22 14:21:30 -070049var proxyServer = modules.Register(func(env *modules.Env, args ...string) error {
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080050 ctx, shutdown := v23.Init()
51 defer shutdown()
52
53 expected := len(args)
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -070054
55 listenSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
Asim Shankar99b18a72015-04-25 23:19:28 -070056 proxyShutdown, proxyEp, err := proxy.New(ctx, listenSpec, security.AllowEveryone())
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080057 if err != nil {
Todd Wang95873902015-05-22 14:21:30 -070058 fmt.Fprintf(env.Stderr, "%s\n", verror.DebugString(err))
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080059 return err
60 }
61 defer proxyShutdown()
Todd Wang95873902015-05-22 14:21:30 -070062 fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080063 if expected > 0 {
64 pub := publisher.New(ctx, v23.GetNamespace(ctx), time.Minute)
65 defer pub.WaitForStop()
66 defer pub.Stop()
Robin Thellend89e95232015-03-24 13:48:48 -070067 pub.AddServer(proxyEp.String())
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080068 for _, name := range args {
69 if len(name) == 0 {
70 return fmt.Errorf("empty name specified on the command line")
71 }
Robin Thellend89e95232015-03-24 13:48:48 -070072 pub.AddName(name, false, false)
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080073 }
74 // Wait for all the entries to be published.
75 for {
76 pubState := pub.Status()
77 if expected == len(pubState) {
78 break
79 }
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080080 delay := time.Second
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080081 time.Sleep(delay)
82 }
83 }
Todd Wang95873902015-05-22 14:21:30 -070084 fmt.Fprintf(env.Stdout, "PROXY_NAME=%s\n", proxyEp.Name())
85 modules.WaitForEOF(env.Stdin)
86 fmt.Fprintf(env.Stdout, "DONE\n")
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080087 return nil
Todd Wang95873902015-05-22 14:21:30 -070088}, "")
Matt Rosencrantz2b675f92015-03-05 12:52:50 -080089
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080090type testServer struct{}
91
Todd Wang54feabe2015-04-15 23:38:26 -070092func (*testServer) Echo(_ *context.T, call rpc.ServerCall, arg string) (string, error) {
Matt Rosencrantz311378b2015-03-25 15:26:12 -070093 return fmt.Sprintf("method:%q,suffix:%q,arg:%q", "Echo", call.Suffix(), arg), nil
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080094}
95
96type testServerAuthorizer struct{}
97
Todd Wang4264e4b2015-04-16 22:43:40 -070098func (testServerAuthorizer) Authorize(*context.T, security.Call) error {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -080099 return nil
100}
101
102type testServerDisp struct{ server interface{} }
103
Cosmos Nicolaou5a3125a2015-07-10 11:19:20 -0700104func (t testServerDisp) Lookup(_ *context.T, suffix string) (interface{}, security.Authorizer, error) {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800105 return t.server, testServerAuthorizer{}, nil
106}
107
108type proxyHandle struct {
Todd Wang5082a552015-04-02 10:56:11 -0700109 ns namespace.T
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800110 sh *modules.Shell
111 proxy modules.Handle
112 name string
113}
114
115func (h *proxyHandle) Start(t *testing.T, ctx *context.T, args ...string) error {
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700116 sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800117 if err != nil {
118 t.Fatalf("unexpected error: %s", err)
119 }
120 h.sh = sh
Todd Wang95873902015-05-22 14:21:30 -0700121 p, err := sh.Start(nil, proxyServer, args...)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800122 if err != nil {
123 t.Fatalf("unexpected error: %s", err)
124 }
125 h.proxy = p
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700126 p.ReadLine()
127 h.name = p.ExpectVar("PROXY_NAME")
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800128 if len(h.name) == 0 {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700129 h.proxy.Shutdown(os.Stderr, os.Stderr)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800130 t.Fatalf("failed to get PROXY_NAME from proxyd")
131 }
132 return h.ns.Mount(ctx, "proxy", h.name, time.Hour)
133}
134
135func (h *proxyHandle) Stop(ctx *context.T) error {
136 defer h.sh.Cleanup(os.Stderr, os.Stderr)
137 if err := h.proxy.Shutdown(os.Stderr, os.Stderr); err != nil {
138 return err
139 }
140 if len(h.name) == 0 {
141 return nil
142 }
143 return h.ns.Unmount(ctx, "proxy", h.name)
144}
145
146func TestProxyOnly(t *testing.T) {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700147 listenSpec := rpc.ListenSpec{Proxy: "proxy"}
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800148 testProxy(t, listenSpec)
149}
150
151func TestProxy(t *testing.T) {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700152 proxyListenSpec := rpc.ListenSpec{
153 Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}},
154 Proxy: "proxy",
155 }
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800156 testProxy(t, proxyListenSpec)
157}
158
159func TestWSProxy(t *testing.T) {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700160 proxyListenSpec := rpc.ListenSpec{
161 Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}},
162 Proxy: "proxy",
163 }
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800164 // The proxy uses websockets only, but the server is using tcp.
Asim Shankarf32d24d2015-04-01 16:34:26 -0700165 testProxy(t, proxyListenSpec, "--v23.tcp.protocol=ws")
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800166}
167
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700168func testProxy(t *testing.T, spec rpc.ListenSpec, args ...string) {
Asim Shankar7171a252015-03-07 14:41:40 -0800169 ctx, shutdown := testContext()
170 defer shutdown()
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700171
Ankur50a5f392015-02-27 18:46:30 -0800172 var (
Asim Shankar4a698282015-03-21 21:59:18 -0700173 pserver = testutil.NewPrincipal("server")
174 pclient = testutil.NewPrincipal("client")
Ankur50a5f392015-02-27 18:46:30 -0800175 serverKey = pserver.PublicKey()
176 // We use different stream managers for the client and server
177 // to prevent VIF re-use (in other words, we want to test VIF
178 // creation from both the client and server end).
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700179 smserver = imanager.InternalNew(ctx, naming.FixedRoutingID(0x555555555))
180 smclient = imanager.InternalNew(ctx, naming.FixedRoutingID(0x444444444))
Ankur50a5f392015-02-27 18:46:30 -0800181 ns = tnaming.NewSimpleNamespace()
182 )
183 defer smserver.Shutdown()
184 defer smclient.Shutdown()
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700185 client, err := irpc.InternalNewClient(smserver, ns)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800186 if err != nil {
187 t.Fatal(err)
188 }
189 defer client.Close()
Todd Wangad492042015-04-17 15:58:40 -0700190 serverCtx, _ := v23.WithPrincipal(ctx, pserver)
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700191 server, err := irpc.InternalNewServer(serverCtx, smserver, ns, nil, "", nil)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800192 if err != nil {
193 t.Fatal(err)
194 }
195 defer server.Stop()
196
Asim Shankar263c73b2015-03-19 18:31:26 -0700197 // The client must recognize the server's blessings, otherwise it won't
198 // communicate with it.
199 pclient.AddToRoots(pserver.BlessingStore().Default())
200
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800201 // If no address is specified then we'll only 'listen' via
202 // the proxy.
203 hasLocalListener := len(spec.Addrs) > 0 && len(spec.Addrs[0].Address) != 0
204
205 name := "mountpoint/server/suffix"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700206 makeCall := func(opts ...rpc.CallOpt) (string, error) {
Todd Wangad492042015-04-17 15:58:40 -0700207 clientCtx, _ := v23.WithPrincipal(ctx, pclient)
Asim Shankar263c73b2015-03-19 18:31:26 -0700208 clientCtx, _ = context.WithDeadline(clientCtx, time.Now().Add(5*time.Second))
209 call, err := client.StartCall(clientCtx, name, "Echo", []interface{}{"batman"}, opts...)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800210 if err != nil {
211 // proxy is down, we should return here/.... prepend
212 // the error with a well known string so that we can test for that.
213 return "", fmt.Errorf("RESOLVE: %s", err)
214 }
215 var result string
216 if err = call.Finish(&result); err != nil {
217 return "", err
218 }
219 return result, nil
220 }
221 proxy := &proxyHandle{ns: ns}
222 if err := proxy.Start(t, ctx, args...); err != nil {
223 t.Fatal(err)
224 }
225 defer proxy.Stop(ctx)
226 addrs := verifyMount(t, ctx, ns, spec.Proxy)
227 if len(addrs) != 1 {
228 t.Fatalf("failed to lookup proxy")
229 }
230
231 eps, err := server.Listen(spec)
232 if err != nil {
233 t.Fatal(err)
234 }
235 if err := server.ServeDispatcher("mountpoint/server", testServerDisp{&testServer{}}); err != nil {
236 t.Fatal(err)
237 }
238
239 // Proxy connections are started asynchronously, so we need to wait..
240 waitForMountTable := func(ch chan int, expect int) {
241 then := time.Now().Add(time.Minute)
242 for {
243 me, err := ns.Resolve(ctx, name)
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700244 if err != nil {
245 continue
246 }
247 for i, s := range me.Servers {
Cosmos Nicolaoue9c622d2015-07-10 11:09:42 -0700248 ctx.Infof("%d: %s", i, s)
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700249 }
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800250 if err == nil && len(me.Servers) == expect {
251 ch <- 1
252 return
253 }
254 if time.Now().After(then) {
Cosmos Nicolaouaa87e292015-04-21 22:15:50 -0700255 t.Fatalf("timed out waiting for %d servers, found %d", expect, len(me.Servers))
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800256 }
257 time.Sleep(100 * time.Millisecond)
258 }
259 }
260 waitForServerStatus := func(ch chan int, proxy string) {
261 then := time.Now().Add(time.Minute)
262 for {
263 status := server.Status()
264 if len(status.Proxies) == 1 && status.Proxies[0].Proxy == proxy {
265 ch <- 2
266 return
267 }
268 if time.Now().After(then) {
269 t.Fatalf("timed out")
270 }
271 time.Sleep(100 * time.Millisecond)
272 }
273 }
274 proxyEP, _ := naming.SplitAddressName(addrs[0])
275 proxiedEP, err := inaming.NewEndpoint(proxyEP)
276 if err != nil {
277 t.Fatalf("unexpected error for %q: %s", proxyEP, err)
278 }
279 proxiedEP.RID = naming.FixedRoutingID(0x555555555)
Asim Shankar7171a252015-03-07 14:41:40 -0800280 proxiedEP.Blessings = []string{"server"}
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800281 expectedNames := []string{naming.JoinAddressName(proxiedEP.String(), "suffix")}
282 if hasLocalListener {
283 expectedNames = append(expectedNames, naming.JoinAddressName(eps[0].String(), "suffix"))
284 }
285
286 // Proxy connetions are created asynchronously, so we wait for the
287 // expected number of endpoints to appear for the specified service name.
288 ch := make(chan int, 2)
289 go waitForMountTable(ch, len(expectedNames))
290 go waitForServerStatus(ch, spec.Proxy)
291 select {
292 case <-time.After(time.Minute):
293 t.Fatalf("timedout waiting for two entries in the mount table and server status")
294 case i := <-ch:
295 select {
296 case <-time.After(time.Minute):
297 t.Fatalf("timedout waiting for two entries in the mount table or server status")
298 case j := <-ch:
299 if !((i == 1 && j == 2) || (i == 2 && j == 1)) {
300 t.Fatalf("unexpected return values from waiters")
301 }
302 }
303 }
304
305 status := server.Status()
306 if got, want := status.Proxies[0].Endpoint, proxiedEP; !reflect.DeepEqual(got, want) {
307 t.Fatalf("got %q, want %q", got, want)
308 }
309
310 got := []string{}
311 for _, s := range verifyMount(t, ctx, ns, name) {
312 got = append(got, s)
313 }
314 sort.Strings(got)
315 sort.Strings(expectedNames)
316 if !reflect.DeepEqual(got, expectedNames) {
317 t.Errorf("got %v, want %v", got, expectedNames)
318 }
319
320 if hasLocalListener {
321 // Listen will publish both the local and proxied endpoint with the
322 // mount table, given that we're trying to test the proxy, we remove
323 // the local endpoint from the mount table entry! We have to remove both
324 // the tcp and the websocket address.
325 sep := eps[0].String()
326 ns.Unmount(ctx, "mountpoint/server", sep)
327 }
328
329 addrs = verifyMount(t, ctx, ns, name)
330 if len(addrs) != 1 {
331 t.Fatalf("failed to lookup proxy: addrs %v", addrs)
332 }
333
Ankur50a5f392015-02-27 18:46:30 -0800334 // Proxied endpoint should be published and RPC should succeed (through proxy).
335 // Additionally, any server authorizaton options must only apply to the end server
336 // and not the proxy.
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800337 const expected = `method:"Echo",suffix:"suffix",arg:"batman"`
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700338 if result, err := makeCall(options.ServerPublicKey{PublicKey: serverKey}); result != expected || err != nil {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800339 t.Fatalf("Got (%v, %v) want (%v, nil)", result, err, expected)
340 }
Ankur50a5f392015-02-27 18:46:30 -0800341
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800342 // Proxy dies, calls should fail and the name should be unmounted.
343 if err := proxy.Stop(ctx); err != nil {
344 t.Fatal(err)
345 }
346
Suharsh Sivakumar90da4c22015-02-12 15:45:56 -0800347 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 -0800348 t.Fatalf(`Got (%v, %v) want ("", "RESOLVE: <err>" or "EOF") as proxy is down`, result, err)
349 }
350
351 for {
352 if _, err := ns.Resolve(ctx, name); err != nil {
353 break
354 }
355 time.Sleep(10 * time.Millisecond)
356 }
357 verifyMountMissing(t, ctx, ns, name)
358
359 status = server.Status()
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -0700360 if got, want := len(status.Proxies), 1; got != want {
361 t.Logf("Proxies: %v", status.Proxies)
362 t.Fatalf("got %v, want %v", got, want)
363 }
364 if got, want := status.Proxies[0].Proxy, spec.Proxy; got != want {
365 t.Fatalf("got %v, want %v", got, want)
366 }
367 if got, want := verror.ErrorID(status.Proxies[0].Error), verror.ErrNoServers.ID; got != want {
368 t.Fatalf("got %v, want %v", got, want)
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800369 }
370
371 // Proxy restarts, calls should eventually start succeeding.
372 if err := proxy.Start(t, ctx, args...); err != nil {
373 t.Fatal(err)
374 }
375
376 retries := 0
377 for {
378 if result, err := makeCall(); err == nil {
379 if result != expected {
380 t.Errorf("Got (%v, %v) want (%v, nil)", result, err, expected)
381 }
382 break
383 } else {
384 retries++
385 if retries > 10 {
386 t.Fatalf("Failed after 10 attempts: err: %s", err)
387 }
388 }
389 }
390}
391
Todd Wang5082a552015-04-02 10:56:11 -0700392func verifyMount(t *testing.T, ctx *context.T, ns namespace.T, name string) []string {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800393 me, err := ns.Resolve(ctx, name)
394 if err != nil {
395 t.Errorf("%s not found in mounttable", name)
396 return nil
397 }
398 return me.Names()
399}
400
Todd Wang5082a552015-04-02 10:56:11 -0700401func verifyMountMissing(t *testing.T, ctx *context.T, ns namespace.T, name string) {
Suharsh Sivakumar9d17e4a2015-02-02 22:42:16 -0800402 if me, err := ns.Resolve(ctx, name); err == nil {
403 names := me.Names()
404 t.Errorf("%s not supposed to be found in mounttable; got %d servers instead: %v", name, len(names), names)
405 }
406}