1// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package server
16
17import (
18	"context"
19	"fmt"
20
21	"github.com/opentracing/opentracing-go"
22	"github.com/opentracing/opentracing-go/ext"
23
24	"github.com/uber/jaeger-client-go"
25	"github.com/uber/jaeger-client-go/crossdock/common"
26	"github.com/uber/jaeger-client-go/crossdock/log"
27	"github.com/uber/jaeger-client-go/crossdock/thrift/tracetest"
28)
29
30func (s *Server) doStartTrace(req *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) {
31	span := s.Tracer.StartSpan(req.ServerRole)
32	if req.Sampled {
33		ext.SamplingPriority.Set(span, 1)
34	}
35	span.SetBaggageItem(BaggageKey, req.Baggage)
36	defer span.Finish()
37
38	ctx := opentracing.ContextWithSpan(context.Background(), span)
39
40	return s.prepareResponse(ctx, req.ServerRole, req.Downstream)
41}
42
43func (s *Server) doJoinTrace(ctx context.Context, req *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) {
44	return s.prepareResponse(ctx, req.ServerRole, req.Downstream)
45}
46
47func (s *Server) prepareResponse(ctx context.Context, role string, reqDwn *tracetest.Downstream) (*tracetest.TraceResponse, error) {
48	observedSpan, err := observeSpan(ctx, s.Tracer)
49	if err != nil {
50		return nil, err
51	}
52
53	resp := tracetest.NewTraceResponse()
54	resp.Span = observedSpan
55
56	if reqDwn != nil {
57		downstreamResp, err := s.callDownstream(ctx, role, reqDwn)
58		if err != nil {
59			return nil, err
60		}
61		resp.Downstream = downstreamResp
62	}
63
64	return resp, nil
65}
66
67func (s *Server) callDownstream(ctx context.Context, role string, downstream *tracetest.Downstream) (*tracetest.TraceResponse, error) {
68	switch downstream.Transport {
69	case tracetest.Transport_HTTP:
70		return s.callDownstreamHTTP(ctx, downstream)
71	case tracetest.Transport_DUMMY:
72		return &tracetest.TraceResponse{NotImplementedError: "DUMMY transport not implemented"}, nil
73	default:
74		return nil, errUnrecognizedProtocol
75	}
76}
77
78func (s *Server) callDownstreamHTTP(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) {
79	req := &tracetest.JoinTraceRequest{
80		ServerRole: target.ServerRole,
81		Downstream: target.Downstream,
82	}
83	url := fmt.Sprintf("http://%s:%s/join_trace", target.Host, target.Port)
84	log.Printf("Calling downstream service '%s' at %s", target.ServiceName, url)
85	return common.PostJSON(ctx, url, req)
86}
87
88func observeSpan(ctx context.Context, tracer opentracing.Tracer) (*tracetest.ObservedSpan, error) {
89	span := opentracing.SpanFromContext(ctx)
90	if span == nil {
91		return nil, errNoSpanObserved
92	}
93	sc := span.Context().(jaeger.SpanContext)
94	observedSpan := tracetest.NewObservedSpan()
95	observedSpan.TraceId = sc.TraceID().String()
96	observedSpan.Sampled = sc.IsSampled()
97	observedSpan.Baggage = span.BaggageItem(BaggageKey)
98	return observedSpan, nil
99}
100