Beispiel #1
0
func TestInboundExistingMethods(t *testing.T) {
	// Create a channel with an existing "echo" method.
	ch, err := tchannel.NewChannel("foo", nil)
	require.NoError(t, err)
	json.Register(ch, json.Handlers{
		"echo": func(ctx json.Context, req map[string]string) (map[string]string, error) {
			return req, nil
		},
	}, nil)

	i := NewInbound(ch)
	service := transport.ServiceDetail{Name: "derp", Registry: new(transporttest.MockRegistry)}
	require.NoError(t, i.Start(service, transport.NoDeps))
	defer i.Stop()

	// Make a call to the "echo" method which should call our pre-registered method.
	ctx, cancel := json.NewContext(time.Second)
	defer cancel()

	var resp map[string]string
	arg := map[string]string{"k": "v"}

	svc := ch.ServiceName()
	peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)
	err = json.CallPeer(ctx, peer, svc, "echo", arg, &resp)
	require.NoError(t, err, "Call failed")
	assert.Equal(t, arg, resp, "Response mismatch")
}
Beispiel #2
0
func TestTracingPropagates(t *testing.T) {
	WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {
		handler := &traceHandler{t: t, ch: ch}
		json.Register(ch, json.Handlers{
			"call": handler.call,
		}, handler.onError)

		ctx, cancel := json.NewContext(time.Second)
		defer cancel()

		peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)
		var response TracingResponse
		require.NoError(t, json.CallPeer(ctx, peer, ch.PeerInfo().ServiceName, "call", &TracingRequest{
			ForwardCount: 1,
		}, &response))

		clientSpan := CurrentSpan(ctx)
		require.NotNil(t, clientSpan)
		assert.Equal(t, uint64(0), clientSpan.ParentID())
		assert.NotEqual(t, uint64(0), clientSpan.TraceID())
		assert.True(t, clientSpan.TracingEnabled(), "Tracing should be enabled")
		assert.Equal(t, clientSpan.TraceID(), response.TraceID)
		assert.Equal(t, clientSpan.SpanID(), response.ParentID)
		assert.True(t, response.TracingEnabled, "Tracing should be enabled")
		assert.Equal(t, response.TraceID, response.SpanID, "traceID = spanID for root span")

		nestedResponse := response.Child
		require.NotNil(t, nestedResponse)
		assert.Equal(t, clientSpan.TraceID(), nestedResponse.TraceID)
		assert.Equal(t, response.SpanID, nestedResponse.ParentID)
		assert.True(t, response.TracingEnabled, "Tracing should be enabled")
		assert.NotEqual(t, response.SpanID, nestedResponse.SpanID)
	})
}
Beispiel #3
0
func (s *JoinSenderTestSuite) TestJoinSelf() {
	// Set up a real listening channel
	s.tnode = newChannelNode(s.T())
	s.node = s.tnode.node
	defer s.tnode.Destroy()

	bootstrapNodes(s.T(), s.tnode)

	joiner, err := newJoinSender(s.node, &joinOpts{
		discoverProvider: &StaticHostList{fakeHostPorts(1, 1, 1, 1)},
	})
	s.Require().NoError(err, "cannot have an error")
	s.Require().NotNil(joiner, "joiner cannot be nil")

	ctx, cancel := json.NewContext(joiner.timeout)
	defer cancel()

	var res joinResponse
	select {
	case err := <-joiner.MakeCall(ctx, s.node.Address(), &res):
		s.Error(err, "expected join to fail for different apps")
	case <-ctx.Done():
		s.Fail("expected join to not timeout")
	}
}
Beispiel #4
0
// Purpose of this test is to ensure introspection doesn't cause any panics
// and we have coverage of the introspection code.
func TestIntrospection(t *testing.T) {
	testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) {
		client := testutils.NewClient(t, nil)
		defer client.Close()

		ctx, cancel := json.NewContext(time.Second)
		defer cancel()

		var resp map[string]interface{}
		peer := client.Peers().GetOrAdd(ts.HostPort())
		err := json.CallPeer(ctx, peer, ts.ServiceName(), "_gometa_introspect", map[string]interface{}{
			"includeExchanges":  true,
			"includeEmptyPeers": true,
			"includeTombstones": true,
		}, &resp)
		require.NoError(t, err, "Call _gometa_introspect failed")

		err = json.CallPeer(ctx, peer, ts.ServiceName(), "_gometa_runtime", map[string]interface{}{
			"includeGoStacks": true,
		}, &resp)
		require.NoError(t, err, "Call _gometa_runtime failed")

		if !ts.HasRelay() {
			// Try making the call on the "tchannel" service which is where meta handlers
			// are registered. This will only work when we call it directly as the relay
			// will not forward the tchannel service.
			err = json.CallPeer(ctx, peer, "tchannel", "_gometa_runtime", map[string]interface{}{
				"includeGoStacks": true,
			}, &resp)
			require.NoError(t, err, "Call _gometa_runtime failed")
		}
	})
}
Beispiel #5
0
func jsonCall(call call, headers map[string]string, token string) (jsonResp, map[string]string, error) {
	peer := call.Channel.Peers().Add(call.ServerHostPort)

	ctx, cancel := json.NewContext(time.Second)
	ctx = json.WithHeaders(ctx, headers)
	defer cancel()

	var response jsonResp
	err := json.CallPeer(ctx, peer, serverName, "echo", &jsonResp{Token: token}, &response)
	return response, ctx.ResponseHeaders(), err
}
Beispiel #6
0
func (p *pingSender) SendPing() (*ping, error) {
	ctx, cancel := json.NewContext(p.timeout)
	defer cancel()

	var res ping
	select {
	case err := <-p.MakeCall(ctx, &res):
		return &res, err

	case <-ctx.Done(): // ping timed out
		return nil, errors.New("ping timed out")
	}
}
Beispiel #7
0
func main() {
	// Create a new TChannel for handling requests
	ch, err := tchannel.NewChannel("PingService", &tchannel.ChannelOptions{Logger: tchannel.SimpleLogger})
	if err != nil {
		log.WithFields(tchannel.ErrField(err)).Fatal("Couldn't create new channel.")
	}

	// Register a handler for the ping message on the PingService
	json.Register(ch, json.Handlers{
		"ping": pingHandler,
	}, onError)

	// Listen for incoming requests
	listenAndHandle(ch, "127.0.0.1:10500")

	// Create a new TChannel for sending requests.
	client, err := tchannel.NewChannel("ping-client", nil)
	if err != nil {
		log.WithFields(tchannel.ErrField(err)).Fatal("Couldn't create new client channel.")
	}

	// Make a call to ourselves, with a timeout of 10s
	ctx, cancel := json.NewContext(time.Second * 10)
	defer cancel()

	peer := client.Peers().Add(ch.PeerInfo().HostPort)

	var pong Pong
	if err := json.CallPeer(ctx, peer, "PingService", "ping", &Ping{"Hello World"}, &pong); err != nil {
		log.WithFields(tchannel.ErrField(err)).Fatal("json.Call failed.")
	}

	log.Infof("Received pong: %s", pong.Message)

	// Create a new subchannel for the top-level channel
	subCh := ch.GetSubChannel("PingServiceOther")

	// Register a handler on the subchannel
	json.Register(subCh, json.Handlers{
		"pingOther": pingOtherHandler,
	}, onError)

	// Try to send a message to the Service:Method pair for the subchannel
	if err := json.CallPeer(ctx, peer, "PingServiceOther", "pingOther", &Ping{"Hello Other World"}, &pong); err != nil {
		log.WithFields(tchannel.ErrField(err)).Fatal("json.Call failed.")
	}

	log.Infof("Received pong: %s", pong.Message)
}
func TestZipkinTraceReporterFactory(t *testing.T) {
	ch, err := tchannel.NewChannel("svc", &tchannel.ChannelOptions{
		TraceReporterFactory: ZipkinTraceReporterFactory,
	})
	assert.NoError(t, err)

	// Create a TCollector channel, and add it as a peer to ch so Report works.
	mockServer := new(mocks.TChanTCollector)
	tcollectorCh, err := setupServer(mockServer)
	require.NoError(t, err, "setupServer failed")
	ch.Peers().Add(tcollectorCh.PeerInfo().HostPort)

	called := make(chan int)
	ret := &gen.Response{Ok: true}
	mockServer.On("Submit", mock.Anything, mock.Anything).Return(ret, nil).Run(func(args mock.Arguments) {
		called <- 1
	})

	// Make some calls, and validate that the trace reporter is not called recursively.
	require.NoError(t, json.Register(tcollectorCh, json.Handlers{"op": func(ctx json.Context, arg map[string]interface{}) (map[string]interface{}, error) {
		return arg, nil
	}}, nil), "Register failed")
	ctx, cancel := json.NewContext(time.Second)
	defer cancel()
	for i := 0; i < 5; i++ {
		var res map[string]string
		assert.NoError(t, json.CallSC(ctx, ch.GetSubChannel(tcollectorServiceName), "op", nil, &res), "call failed")
	}

	// Verify the spans being reported.
	for i := 0; i < 5; i++ {
		select {
		case <-called:
		case <-time.After(time.Second):
			t.Errorf("Expected submit for call %v", i)
		}
	}

	// Verify that no other spans are reported.
	select {
	case <-called:
		t.Errorf("Too many spans reported")
	case <-time.After(time.Millisecond):
	}
}
Beispiel #9
0
func (s *JoinSenderTestSuite) TestJoinSelf() {
	bootstrapNodes(s.T(), s.tnode)

	joiner, err := newJoinSender(s.node, nil)
	s.Require().NoError(err, "cannot have an error")
	s.Require().NotNil(joiner, "joiner cannot be nil")

	ctx, cancel := json.NewContext(joiner.timeout)
	defer cancel()

	var res joinResponse
	select {
	case err := <-joiner.MakeCall(ctx, s.node.Address(), &res):
		s.Error(err, "expected join to fail for different apps")
	case <-ctx.Done():
		s.Fail("expected join to not timeout")
	}
}
func (p *pingRequestSender) SendPingRequest() (*pingResponse, error) {
	p.node.log.WithFields(log.Fields{
		"peer":   p.peer,
		"target": p.target,
	}).Debug("ping request send")

	ctx, cancel := json.NewContext(p.timeout)
	defer cancel()

	var res pingResponse
	select {
	case err := <-p.MakeCall(ctx, &res):
		return &res, err

	case <-ctx.Done(): // call timed out
		return nil, errors.New("ping request timed out")
	}

}
Beispiel #11
0
func (c *Client) sendAdvertise() error {
	// Cannot advertise from an ephemeral peer.
	if c.tchan.PeerInfo().IsEphemeral() {
		return errEphemeralPeer
	}

	ctx, cancel := json.NewContext(c.opts.Timeout)
	defer cancel()

	// Disable tracing on Hyperbahn advertise messages to avoid cascading failures (see #790).
	tchannel.CurrentSpan(ctx).EnableTracing(false)

	sc := c.tchan.GetSubChannel(hyperbahnServiceName)
	arg := c.createRequest()
	var resp AdResponse
	c.opts.Handler.On(SendAdvertise)

	if err := json.CallSC(ctx, sc, "ad", arg, &resp); err != nil {
		return err
	}

	return nil
}
Beispiel #12
0
func (j *joinSender) JoinGroup(nodesJoined []string) ([]string, []string) {
	group := j.SelectGroup(nodesJoined)

	var responses struct {
		successes []string
		failures  []string
		sync.Mutex
	}

	var numNodesLeft = j.size - len(nodesJoined)
	var startTime = time.Now()

	var wg sync.WaitGroup

	for _, node := range group {
		wg.Add(1)
		go func(n string) {
			ctx, cancel := json.NewContext(j.timeout)
			defer cancel()

			var res joinResponse
			var failed bool

			select {
			case err := <-j.MakeCall(ctx, n, &res):
				if err != nil {
					j.node.log.WithFields(log.Fields{
						"remote":  n,
						"timeout": j.timeout,
					}).Debug("attempt to join node failed")
					failed = true
					break
				}
				j.node.memberlist.Update(res.Membership)

			case <-ctx.Done():
				j.node.log.WithFields(log.Fields{
					"remote":  n,
					"timeout": j.timeout,
				}).Debug("attempt to join node timed out")
				failed = true
			}

			if !failed {
				responses.Lock()
				responses.successes = append(responses.successes, n)
				responses.Unlock()
			} else {
				responses.Lock()
				responses.failures = append(responses.failures, n)
				responses.Unlock()
			}

			wg.Done()
		}(node)
	}
	// wait for joins to complete
	wg.Wait()

	// don't need to lock successes/failures since we're finished writing to them
	j.node.log.WithFields(log.Fields{
		"groupSize":    len(group),
		"joinSize":     j.size,
		"joinTime":     time.Now().Sub(startTime),
		"numNodesLeft": numNodesLeft,
		"numFailures":  len(responses.failures),
		"failures":     responses.failures,
		"numSuccesses": len(responses.successes),
		"successes":    responses.successes,
	}).Debug("join group complete")

	return responses.successes, responses.failures
}
Beispiel #13
0
// Run exercises a YARPC server with outbound TChannel requests from a rigged
// client and validates behavior that might only be visible to a TChannel
// client without the YARPC abstraction interposed, typically errors.
func Run(t crossdock.T) {
	fatals := crossdock.Fatals(t)
	assert := crossdock.Assert(t)

	tests := []test{
		{
			name:      "happy path",
			procedure: "echo",
			body:      []byte("{}"),
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.NoError(err, "is not error")
				assert.False(isAppErr, "malformed body must not be application error")
			},
		},
		{
			name:      "malformed body",
			procedure: "echo",
			body:      []byte(""),
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.Error(err, "is error")
				assert.False(isAppErr, "malformed body must not be application error")
				err, ok := err.(tchannel.SystemError)
				assert.True(ok, "malformed body must produce system error")
				if !ok {
					return
				}
				code := tchannel.GetSystemErrorCode(err)
				assert.Contains(err.Error(), `failed to decode "json"`, "must mention failing to decode JSON in error message")
				assert.Equal(tchannel.ErrCodeBadRequest, code, "must produce bad request error")
			},
		},
		// TODO test invalid headers
		{
			name:      "missing procedure",
			procedure: "",
			body:      []byte{},
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.Error(err, "is error")
				assert.False(isAppErr, "missing procedure must not produce an application error")
				err, ok := err.(tchannel.SystemError)
				assert.True(ok, "missing procedure must produce system error")
				if !ok {
					return
				}
				code := tchannel.GetSystemErrorCode(err)
				assert.Equal(tchannel.ErrCodeBadRequest, code, "missing procedure must produce bad request error")
				assert.Contains(err.Error(), "missing procedure", "must mention missing procedure in error message")
			},
		},
		{
			name:      "invalid procedure",
			procedure: "no-such-procedure",
			body:      []byte{},
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.Error(err, "is error")
				assert.False(isAppErr, "no-such-procedure must not produce application error")
				err, ok := err.(tchannel.SystemError)
				assert.True(ok, "no-such-procedure must produce  system error")
				if !ok {
					return
				}
				code := tchannel.GetSystemErrorCode(err)
				assert.Equal(tchannel.ErrCodeBadRequest, code, "must produce bad request error")
				assert.Contains(err.Error(), `unrecognized procedure "no-such-procedure"`, "must mention unrecongized procedure in error message")
			},
		},
		{
			name:      "bad response",
			procedure: "bad-response",
			body:      []byte("{}"),
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.Error(err, "is error")
				assert.False(isAppErr, "bad-response must not produce an application error")
				err, ok := err.(tchannel.SystemError)
				assert.True(ok, "bad-response must produce system error")
				if !ok {
					return
				}
				code := tchannel.GetSystemErrorCode(err)
				assert.Equal(tchannel.ErrCodeUnexpected, code, "bad-response must produce unexpected error")
				assert.Contains(err.Error(), `failed to encode "json"`, "must mention failure to encode JSON in error message")
			},
		},
		{
			name:      "unexpected error",
			procedure: "unexpected-error",
			body:      []byte("{}"),
			headers:   []byte("{}"),
			validate: func(res3 []byte, isAppErr bool, err error) {
				assert.Error(err, "is error")
				assert.False(isAppErr, "unexpected-error procedure must not produce application error")
				err, ok := err.(tchannel.SystemError)
				assert.True(ok, "unexpected-error procedure must produce system error")
				code := tchannel.GetSystemErrorCode(err)
				assert.Equal(tchannel.ErrCodeUnexpected, code, "must produce transport error")
			},
		},
	}

	server := t.Param(params.Server)
	serverHostPort := fmt.Sprintf("%v:%v", server, serverPort)

	ch, err := tchannel.NewChannel(serviceName, nil)
	fatals.NoError(err, "could not create channel")

	peer := ch.Peers().Add(serverHostPort)

	for _, tt := range tests {
		var res2, res3 []byte
		var headers map[string]string

		t.Tag("case", tt.name)
		t.Tag("procedure", tt.procedure)

		ctx, cancel := json.NewContext(time.Second)
		defer cancel()

		ctx = json.WithHeaders(ctx, headers)

		encoding := "json"
		if tt.encoding != "" {
			encoding = tt.encoding
		}

		call, err := peer.BeginCall(
			ctx,
			serviceName,
			tt.procedure,
			&tchannel.CallOptions{Format: tchannel.Format(encoding)},
		)
		fatals.NoError(err, "could not begin call")

		err = tchannel.NewArgWriter(call.Arg2Writer()).Write(tt.headers)
		fatals.NoError(err, "could not write request headers")

		err = tchannel.NewArgWriter(call.Arg3Writer()).Write(tt.body)
		fatals.NoError(err, "could not write request body")

		err = tchannel.NewArgReader(call.Response().Arg2Reader()).Read(&res2)
		isAppErr := call.Response().ApplicationError()
		if err == nil {
			err = tchannel.NewArgReader(call.Response().Arg3Reader()).Read(&res3)
		}

		tt.validate(res3, isAppErr, err)
	}
}