Beispiel #1
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 #2
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)
}
Beispiel #3
0
func (j *joinSender) MakeCall(ctx json.Context, node string, res *joinResponse) <-chan error {
	errC := make(chan error)

	go func() {
		defer close(errC)

		peer := j.node.channel.Peers().GetOrAdd(node)

		req := joinRequest{
			App:         j.node.app,
			Source:      j.node.address,
			Incarnation: j.node.Incarnation(),
			Timeout:     j.timeout,
		}

		err := json.CallPeer(ctx, peer, j.node.service, "/protocol/join", req, res)
		if err != nil {
			j.node.log.WithFields(log.Fields{
				"error": err,
			}).Debug("could not complete join")
			errC <- err
			return
		}

		errC <- nil
	}()

	return errC
}
Beispiel #4
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")
}
func (p *pingRequestSender) MakeCall(ctx json.Context, res *pingResponse) <-chan error {
	errC := make(chan error)

	go func() {
		defer close(errC)

		changes, bumpPiggybackCounters := p.node.disseminator.IssueAsSender()
		req := &pingRequest{
			Source:            p.node.Address(),
			SourceIncarnation: p.node.Incarnation(),
			Checksum:          p.node.memberlist.Checksum(),
			Changes:           changes,
			Target:            p.target,
		}

		peer := p.node.channel.Peers().GetOrAdd(p.peer)
		err := json.CallPeer(ctx, peer, p.node.service, "/protocol/ping-req", req, &res)
		if err != nil {
			bumpPiggybackCounters()
			errC <- err
			return
		}

		errC <- nil
	}()

	return errC
}
Beispiel #6
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 #7
0
func (h *JSONHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {
	jctx := json.Wrap(ctx)
	response := new(TracingResponse)
	peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort)
	if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, response); err != nil {
		return nil, err
	}
	return response, nil
}
Beispiel #8
0
func (p *pingSender) MakeCall(ctx json.Context, res *ping) <-chan error {
	errC := make(chan error)

	go func() {
		defer close(errC)

		peer := p.node.channel.Peers().GetOrAdd(p.target)

		changes, bumpPiggybackCounters := p.node.disseminator.IssueAsSender()
		req := ping{
			Checksum:          p.node.memberlist.Checksum(),
			Changes:           changes,
			Source:            p.node.Address(),
			SourceIncarnation: p.node.Incarnation(),
		}

		p.node.emit(PingSendEvent{
			Local:   p.node.Address(),
			Remote:  p.target,
			Changes: req.Changes,
		})

		p.logger.WithFields(log.Fields{
			"remote":  p.target,
			"changes": req.Changes,
		}).Debug("ping send")

		var startTime = time.Now()

		err := json.CallPeer(ctx, peer, p.node.service, "/protocol/ping", req, res)
		if err != nil {
			p.logger.WithFields(log.Fields{
				"remote": p.target,
				"error":  err,
			}).Debug("ping failed")
			errC <- err
			return
		}

		// when ping was successful
		bumpPiggybackCounters()

		p.node.emit(PingSendCompleteEvent{
			Local:    p.node.Address(),
			Remote:   p.target,
			Changes:  req.Changes,
			Duration: time.Now().Sub(startTime),
		})

		errC <- nil
	}()

	return errC
}
Beispiel #9
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 #10
0
func (h *JSONHandler) callJSON(ctx json.Context, req *TracingRequest) (*TracingResponse, error) {
	return h.HandleCall(ctx, req,
		func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) {
			jctx := ctx.(json.Context)
			peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort)
			childResp := new(TracingResponse)
			if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, childResp); err != nil {
				return nil, err
			}
			return childResp, nil
		})
}
Beispiel #11
0
func (h *jsonHandler) callDownstream(ctx context.Context, target *Downstream) (*Response, error) {
	req := &Request{
		ServerRole: target.ServerRole,
		Downstream: target.Downstream,
	}
	jctx := json.Wrap(ctx)
	response := new(Response)
	log.Printf("Calling JSON service %s (%s)", target.ServiceName, target.HostPort)
	peer := h.ch.Peers().GetOrAdd(target.HostPort)
	if err := json.CallPeer(jctx, peer, target.ServiceName, jsonEndpoint, req, response); err != nil {
		return nil, err
	}
	return response, nil
}
Beispiel #12
0
func (h *traceHandler) call(ctx json.Context, req *TracingRequest) (*TracingResponse, error) {
	span := CurrentSpan(ctx)
	if span == nil {
		return nil, fmt.Errorf("tracing not found")
	}

	var childResp *TracingResponse
	if req.ForwardCount > 0 {
		sc := h.ch.Peers().GetOrAdd(h.ch.PeerInfo().HostPort)
		childResp = new(TracingResponse)
		require.NoError(h.t, json.CallPeer(ctx, sc, h.ch.PeerInfo().ServiceName, "call", nil, childResp))
	}

	return &TracingResponse{
		TraceID:        span.TraceID(),
		SpanID:         span.SpanID(),
		ParentID:       span.ParentID(),
		TracingEnabled: span.TracingEnabled(),
		Child:          childResp,
	}, nil
}
Beispiel #13
0
// Per https://github.com/uber/tchannel-go/issues/505, concurrent client calls
// made with the same shared map used as headers were causing panic due to
// concurrent writes to the map when injecting tracing headers.
func TestReusableHeaders(t *testing.T) {
	opts := &testutils.ChannelOpts{
		ChannelOptions: ChannelOptions{Tracer: mocktracer.New()},
	}
	WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {
		jsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t}
		json.Register(ch, json.Handlers{"call": jsonHandler.callJSON}, jsonHandler.onError)

		span := ch.Tracer().StartSpan("client")
		traceID := span.(*mocktracer.MockSpan).SpanContext.TraceID // for validation
		ctx := opentracing.ContextWithSpan(context.Background(), span)

		sharedHeaders := map[string]string{"life": "42"}
		ctx, cancel := NewContextBuilder(2 * time.Second).
			SetHeaders(sharedHeaders).
			SetParentContext(ctx).
			Build()
		defer cancel()

		peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)

		var wg sync.WaitGroup
		for i := 0; i < 42; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				var response testtracing.TracingResponse
				err := json.CallPeer(json.Wrap(ctx), peer, ch.ServiceName(),
					"call", &testtracing.TracingRequest{}, &response)
				assert.NoError(t, err, "json.Call failed")
				assert.EqualValues(t, traceID, response.TraceID, "traceID must match")
			}()
		}
		wg.Wait()
		assert.Equal(t, map[string]string{"life": "42"}, sharedHeaders, "headers unchanged")
	})
}
Beispiel #14
0
func TestTracingSpanAttributes(t *testing.T) {
	tracer := mocktracer.New()

	opts := &testutils.ChannelOpts{
		ChannelOptions: ChannelOptions{Tracer: tracer},
		DisableRelay:   true,
	}
	WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) {
		// Register JSON handler
		jsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t}
		json.Register(ch, json.Handlers{"call": jsonHandler.callJSON}, jsonHandler.onError)

		span := ch.Tracer().StartSpan("client")
		span.SetBaggageItem(testtracing.BaggageKey, testtracing.BaggageValue)
		ctx := opentracing.ContextWithSpan(context.Background(), span)
		root := new(testtracing.TracingResponse).ObserveSpan(ctx)

		ctx, cancel := NewContextBuilder(2 * time.Second).SetParentContext(ctx).Build()
		defer cancel()

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

		// Spans are finished in inbound.doneSending() or outbound.doneReading(),
		// which are called on different go-routines and may execute *after* the
		// response has been received by the client. Give them a chance to finish.
		for i := 0; i < 1000; i++ {
			if spanCount := len(testtracing.MockTracerSampledSpans(tracer)); spanCount == 2 {
				break
			}
			time.Sleep(testutils.Timeout(time.Millisecond))
		}
		spans := testtracing.MockTracerSampledSpans(tracer)
		spanCount := len(spans)
		ch.Logger().Debugf("end span count: %d", spanCount)

		// finish span after taking count of recorded spans
		span.Finish()

		require.Equal(t, 2, spanCount, "Wrong span count")
		assert.Equal(t, root.TraceID, response.TraceID, "Trace ID must match root span")
		assert.Equal(t, testtracing.BaggageValue, response.Luggage, "Baggage must match")

		var parent, child *mocktracer.MockSpan
		for _, s := range spans {
			if s.Tag("span.kind") == ext.SpanKindRPCClientEnum {
				parent = s
				ch.Logger().Debugf("Found parent span: %+v", s)
			} else if s.Tag("span.kind") == ext.SpanKindRPCServerEnum {
				child = s
				ch.Logger().Debugf("Found child span: %+v", s)
			}
		}

		require.NotNil(t, parent)
		require.NotNil(t, child)

		traceID := func(s opentracing.Span) int {
			return s.Context().(mocktracer.MockSpanContext).TraceID
		}
		spanID := func(s *mocktracer.MockSpan) int {
			return s.Context().(mocktracer.MockSpanContext).SpanID
		}
		sampled := func(s *mocktracer.MockSpan) bool {
			return s.Context().(mocktracer.MockSpanContext).Sampled
		}

		require.Equal(t, traceID(span), traceID(parent), "parent must be found")
		require.Equal(t, traceID(span), traceID(child), "child must be found")
		assert.Equal(t, traceID(parent), traceID(child))
		assert.Equal(t, spanID(parent), child.ParentID)
		assert.True(t, sampled(parent), "should be sampled")
		assert.True(t, sampled(child), "should be sampled")
		assert.Equal(t, "call", parent.OperationName)
		assert.Equal(t, "call", child.OperationName)
		assert.Equal(t, "testService", parent.Tag("peer.service"))
		assert.Equal(t, "testService", child.Tag("peer.service"))
		assert.Equal(t, "json", parent.Tag("as"))
		assert.Equal(t, "json", child.Tag("as"))
		assert.NotNil(t, parent.Tag("peer.ipv4"))
		assert.NotNil(t, child.Tag("peer.ipv4"))
		assert.NotNil(t, parent.Tag("peer.port"))
		assert.NotNil(t, child.Tag("peer.port"))
	})
}