github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/tracing/tracer_test.go (about)

     1  package tracing_test
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"compress/zlib"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"net/url"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/stretchr/testify/require"
    20  	"google.golang.org/protobuf/proto"
    21  
    22  	"github.com/opentracing/opentracing-go"
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	"github.com/ory/x/logrusx"
    26  	"github.com/ory/x/tracing"
    27  
    28  	coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
    29  
    30  	"go.elastic.co/apm/transport"
    31  )
    32  
    33  type zipkinSpanRequest struct {
    34  	Id            string
    35  	TraceId       string
    36  	Timestamp     uint64
    37  	Name          string
    38  	LocalEndpoint struct {
    39  		ServiceName string
    40  	}
    41  	Tags map[string]string
    42  }
    43  
    44  type elasticMetadataRequest struct {
    45  	Metadata struct {
    46  		Service struct {
    47  			Name string
    48  		}
    49  	}
    50  }
    51  
    52  type elasticSpanRequest struct {
    53  	Transaction struct {
    54  		Name      string
    55  		Id        string
    56  		Timestamp uint64
    57  		TraceId   string
    58  		Type      string
    59  		Context   struct {
    60  			Tags map[string]string
    61  		}
    62  	}
    63  }
    64  
    65  func TestZipkinTracer(t *testing.T) {
    66  	done := make(chan struct{})
    67  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    68  		defer close(done)
    69  
    70  		body, err := ioutil.ReadAll(r.Body)
    71  		assert.NoError(t, err)
    72  
    73  		var spans []zipkinSpanRequest
    74  		err = json.Unmarshal(body, &spans)
    75  
    76  		assert.NoError(t, err)
    77  
    78  		assert.NotEmpty(t, spans[0].Id)
    79  		assert.NotEmpty(t, spans[0].TraceId)
    80  		assert.Equal(t, "testoperation", spans[0].Name)
    81  		assert.Equal(t, "ory x", spans[0].LocalEndpoint.ServiceName)
    82  		assert.NotNil(t, spans[0].Tags["testTag"])
    83  		assert.Equal(t, "true", spans[0].Tags["testTag"])
    84  	}))
    85  	defer ts.Close()
    86  
    87  	_, err := tracing.New(logrusx.New("ory/x", "1"), &tracing.Config{
    88  		ServiceName: "ORY X",
    89  		Provider:    "zipkin",
    90  		Providers: &tracing.ProvidersConfig{
    91  			Zipkin: &tracing.ZipkinConfig{
    92  				ServerURL: ts.URL,
    93  			},
    94  		},
    95  	})
    96  	assert.NoError(t, err)
    97  
    98  	span := opentracing.GlobalTracer().StartSpan("testOperation")
    99  	span.SetTag("testTag", true)
   100  	span.Finish()
   101  
   102  	select {
   103  	case <-done:
   104  	case <-time.After(time.Millisecond * 1500):
   105  		t.Fatalf("Test server did not receive spans")
   106  	}
   107  }
   108  
   109  func TestElastcApmTracer(t *testing.T) {
   110  	done := make(chan struct{}, 2)
   111  	defer close(done)
   112  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   113  		t.Log("Got connection!")
   114  		done <- struct{}{}
   115  
   116  		switch r.URL.String() {
   117  		case "/config/v1/agents?service.name=ORY+X":
   118  			break
   119  		case "/intake/v2/events":
   120  			body := decodeResponseBody(t, r)
   121  			fmt.Println(string(body))
   122  			data := bytes.Split(body, []byte("\n"))
   123  			assert.GreaterOrEqual(t, len(data), 2)
   124  			var metadata elasticMetadataRequest
   125  			err := json.Unmarshal(data[0], &metadata)
   126  			assert.NoError(t, err)
   127  			assert.Equal(t, "ORY X", metadata.Metadata.Service.Name)
   128  
   129  			var spans elasticSpanRequest
   130  			err = json.Unmarshal(data[1], &spans)
   131  			assert.Equal(t, "testOperation", spans.Transaction.Name)
   132  			assert.Equal(t, "custom", spans.Transaction.Type)
   133  			assert.Equal(t, "true", spans.Transaction.Context.Tags["testTag"])
   134  
   135  			break
   136  		default:
   137  			t.Fatalf("Unknown request:" + r.URL.String())
   138  		}
   139  	}))
   140  	defer ts.Close()
   141  
   142  	require.NoError(t, os.Setenv("ELASTIC_APM_SERVER_URL", ts.URL))
   143  	// Reset env vars in APM Library
   144  	_, err := transport.InitDefault()
   145  	require.NoError(t, err)
   146  
   147  	_, err = tracing.New(logrusx.New("ory/x", "1"), &tracing.Config{
   148  		ServiceName: "ORY X",
   149  		Provider:    "elastic-apm",
   150  		Providers: &tracing.ProvidersConfig{
   151  			Zipkin: &tracing.ZipkinConfig{
   152  				ServerURL: ts.URL,
   153  			},
   154  		},
   155  	})
   156  	require.NoError(t, err)
   157  
   158  	span := opentracing.GlobalTracer().StartSpan("testOperation")
   159  	span.SetTag("testTag", true)
   160  	span.Finish()
   161  
   162  	for i := 0; i < 2; i++ {
   163  		select {
   164  		case _, ok := <-done:
   165  			if !ok {
   166  				return
   167  			}
   168  		case <-time.After(time.Millisecond * 1500):
   169  			t.Fatalf("Test server did not receive spans")
   170  			return
   171  		}
   172  	}
   173  }
   174  
   175  func decodeResponseBody(t *testing.T, r *http.Request) []byte {
   176  	var reader io.ReadCloser
   177  	switch r.Header.Get("Content-Encoding") {
   178  	case "gzip":
   179  		var err error
   180  		reader, err = gzip.NewReader(r.Body)
   181  		if err != nil {
   182  			t.Fatal(err)
   183  		}
   184  	case "deflate":
   185  		var err error
   186  		reader, err = zlib.NewReader(r.Body)
   187  		if err != nil {
   188  			t.Fatal(err)
   189  		}
   190  
   191  	default:
   192  		reader = r.Body
   193  	}
   194  	respBody, err := ioutil.ReadAll(reader)
   195  	require.NoError(t, err)
   196  	require.NoError(t, reader.Close())
   197  	return respBody
   198  }
   199  
   200  func TestInstanaTracer(t *testing.T) {
   201  	done := make(chan struct{})
   202  
   203  	type discoveryRequest struct {
   204  		PID   int      `json:"pid"`
   205  		Name  string   `json:"name"`
   206  		Args  []string `json:"args"`
   207  		Fd    string   `json:"fd"`
   208  		Inode string   `json:"inode"`
   209  	}
   210  
   211  	type discoveryResponse struct {
   212  		Pid     uint32 `json:"pid"`
   213  		HostID  string `json:"agentUuid"`
   214  		Secrets struct {
   215  			Matcher string   `json:"matcher"`
   216  			List    []string `json:"list"`
   217  		} `json:"secrets"`
   218  		ExtraHTTPHeaders []string `json:"extraHeaders"`
   219  	}
   220  
   221  	type traceRequest struct {
   222  		Timestamp uint64 `json:"ts"`
   223  		Data      struct {
   224  			Service string `json:"service"`
   225  			Sdk     struct {
   226  				Name   string `json:"name"`
   227  				Type   string `json:"type"`
   228  				Custom struct {
   229  					Baggage map[string]interface{}            `json:"baggage"`
   230  					Logs    map[uint64]map[string]interface{} `json:"logs"`
   231  					Tags    map[string]interface{}            `json:"tags"`
   232  				} `json:"custom"`
   233  			} `json:"sdk"`
   234  		} `json:"data"`
   235  	}
   236  
   237  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   238  		if r.URL.Path == "/" {
   239  			t.Log("Got Agent check request")
   240  
   241  			w.Header().Set("Server", "Instana Agent")
   242  			w.WriteHeader(http.StatusOK)
   243  			return
   244  		}
   245  
   246  		if r.URL.Path == "/com.instana.plugin.golang.discovery" {
   247  			t.Log("Got Agent discovery request")
   248  
   249  			body, err := ioutil.ReadAll(r.Body)
   250  			assert.NoError(t, err)
   251  
   252  			var dReq discoveryRequest
   253  			assert.NoError(t, json.Unmarshal(body, &dReq))
   254  
   255  			agentResponse := discoveryResponse{
   256  				Pid:    1,
   257  				HostID: "1",
   258  			}
   259  			resp, err := json.Marshal(&agentResponse)
   260  			assert.NoError(t, err)
   261  			w.Header().Set("Server", "Instana Agent")
   262  			w.Write(resp)
   263  			return
   264  		}
   265  
   266  		if strings.Contains(r.URL.Path, "/com.instana.plugin.golang/traces.") {
   267  			t.Log("Got trace request")
   268  
   269  			body, err := ioutil.ReadAll(r.Body)
   270  			assert.NoError(t, err)
   271  
   272  			var req []traceRequest
   273  			assert.NoError(t, json.Unmarshal(body, &req))
   274  
   275  			assert.Equal(t, "ORY X", req[0].Data.Service)
   276  			assert.Equal(t, "testOperation", req[0].Data.Sdk.Name)
   277  			assert.Equal(t, true, req[0].Data.Sdk.Custom.Tags["testTag"])
   278  			assert.Equal(t, "biValue", req[0].Data.Sdk.Custom.Baggage["testBi"])
   279  			//assert.Equal(t, "testValue", req[0].Data.Sdk.Custom.Logs[req[0].Timestamp]["testKey"])
   280  
   281  			w.Header().Set("Server", "Instana Agent")
   282  			w.WriteHeader(http.StatusOK)
   283  
   284  			close(done)
   285  			return
   286  		}
   287  	}))
   288  	defer ts.Close()
   289  
   290  	agentUrl, err := url.Parse(ts.URL)
   291  	require.NoError(t, err)
   292  
   293  	require.NoError(t, os.Setenv("INSTANA_AGENT_HOST", agentUrl.Hostname()))
   294  	require.NoError(t, os.Setenv("INSTANA_AGENT_PORT", agentUrl.Port()))
   295  
   296  	_, err = tracing.New(logrusx.New("ory/x", "1"), &tracing.Config{
   297  		ServiceName: "ORY X",
   298  		Provider:    "instana",
   299  	})
   300  	assert.NoError(t, err)
   301  
   302  	time.Sleep(1 * time.Second)
   303  
   304  	span := opentracing.GlobalTracer().StartSpan("testOperation")
   305  	span.SetTag("testTag", true)
   306  	span.LogKV("testKey", "testValue")
   307  	span.SetBaggageItem("testBi", "biValue")
   308  	span.Finish()
   309  
   310  	select {
   311  	case <-done:
   312  	case <-time.After(time.Second * 3):
   313  		t.Fatalf("Test server did not receive spans")
   314  	}
   315  }
   316  
   317  func TestOtlpTracer(t *testing.T) {
   318  	done := make(chan struct{})
   319  
   320  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   321  		body := decodeResponseBody(t, r)
   322  
   323  		var res coltracepb.ExportTraceServiceRequest
   324  		err := proto.Unmarshal(body, &res)
   325  		require.NoError(t, err, "must be able to unmarshal traces")
   326  		receivedSpan := res.ResourceSpans[0].InstrumentationLibrarySpans[0].Spans[0]
   327  		assert.Equal(t, "testOperation", receivedSpan.GetName())
   328  		attributes := receivedSpan.GetAttributes()
   329  		assert.Equal(t, "testTag", attributes[0].GetKey())
   330  
   331  		close(done)
   332  	}))
   333  	defer ts.Close()
   334  
   335  	require.NoError(t, os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", ts.URL))
   336  
   337  	_, err := tracing.New(logrusx.New("ory/x", "1"), &tracing.Config{
   338  		ServiceName: "ORY X",
   339  		Provider:    "otel",
   340  	})
   341  	assert.NoError(t, err)
   342  
   343  	span := opentracing.GlobalTracer().StartSpan("testOperation")
   344  	span.SetTag("testTag", true)
   345  	span.Finish()
   346  
   347  	select {
   348  	case <-done:
   349  	case <-time.After(3 * time.Second):
   350  		t.Fatalf("Test server did not receive spans")
   351  	}
   352  }