From 3b993ab4b4b4c14e7a72a40faa9c57b1d40b77cb Mon Sep 17 00:00:00 2001 From: test Date: Wed, 24 Jul 2024 09:26:19 +0530 Subject: [PATCH 1/4] updating filter result --- sdk/filter/result/filter_result.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdk/filter/result/filter_result.go b/sdk/filter/result/filter_result.go index f3d3cd0..521089d 100644 --- a/sdk/filter/result/filter_result.go +++ b/sdk/filter/result/filter_result.go @@ -1,7 +1,17 @@ package result +type KeyValueString struct { + key string + value string +} + +type Decorations struct { + RequestHeaderInjections []KeyValueString +} + type FilterResult struct { Block bool ResponseStatusCode int32 ResponseMessage string + Decorations Decorations } From b4049eb89703c05a0cb0ae0b7b6d17c7b212b763 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 24 Jul 2024 12:08:38 +0530 Subject: [PATCH 2/4] updating instrumentations to use header injections --- sdk/filter/result/filter_result.go | 6 +- .../google.golang.org/grpc/common_test.go | 15 ++- .../google.golang.org/grpc/server.go | 9 ++ .../google.golang.org/grpc/server_test.go | 122 ++++++++++++++++++ .../net/http/attributes_test.go | 4 +- .../net/http/contenttype_test.go | 10 +- sdk/instrumentation/net/http/handler.go | 19 ++- sdk/instrumentation/net/http/handler_test.go | 76 +++++++++++ .../net/http/headeraccessor.go | 12 +- sdk/instrumentation/net/http/transport.go | 14 +- 10 files changed, 257 insertions(+), 30 deletions(-) diff --git a/sdk/filter/result/filter_result.go b/sdk/filter/result/filter_result.go index 521089d..f2ba04b 100644 --- a/sdk/filter/result/filter_result.go +++ b/sdk/filter/result/filter_result.go @@ -1,8 +1,8 @@ package result type KeyValueString struct { - key string - value string + Key string + Value string } type Decorations struct { @@ -13,5 +13,5 @@ type FilterResult struct { Block bool ResponseStatusCode int32 ResponseMessage string - Decorations Decorations + Decorations *Decorations } diff --git a/sdk/instrumentation/google.golang.org/grpc/common_test.go b/sdk/instrumentation/google.golang.org/grpc/common_test.go index a9c1907..09c4877 100644 --- a/sdk/instrumentation/google.golang.org/grpc/common_test.go +++ b/sdk/instrumentation/google.golang.org/grpc/common_test.go @@ -18,17 +18,22 @@ import ( "google.golang.org/grpc/test/bufconn" ) -var _ helloworld.GreeterServer = server{} +// type assertion +var _ helloworld.GreeterServer = (*server)(nil) type server struct { - err error - replyHeader metadata.MD - replyTrailer metadata.MD + err error + requestHeader metadata.MD + replyHeader metadata.MD + replyTrailer metadata.MD *helloworld.UnimplementedGreeterServer } -func (s server) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) { +func (s *server) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) { var reply *helloworld.HelloReply + if md, ok := metadata.FromIncomingContext(ctx); ok { + s.requestHeader = md + } if s.err == nil { reply = &helloworld.HelloReply{Message: fmt.Sprintf("Hello %s", req.GetName())} } diff --git a/sdk/instrumentation/google.golang.org/grpc/server.go b/sdk/instrumentation/google.golang.org/grpc/server.go index a3eeaed..3b38a6a 100644 --- a/sdk/instrumentation/google.golang.org/grpc/server.go +++ b/sdk/instrumentation/google.golang.org/grpc/server.go @@ -2,6 +2,7 @@ package grpc // import "github.com/hypertrace/goagent/sdk/instrumentation/google import ( "context" + "google.golang.org/grpc/metadata" "net/http" "strings" @@ -109,6 +110,14 @@ func wrapHandler( filterResult := filter.Evaluate(span) if filterResult.Block { return nil, status.Error(StatusCode(int(filterResult.ResponseStatusCode)), StatusText(int(filterResult.ResponseStatusCode))) + } else if filterResult.Decorations != nil { + if md, ok := metadata.FromIncomingContext(ctx); ok { + for _, header := range filterResult.Decorations.RequestHeaderInjections { + md.Append(header.Key, header.Value) + span.SetAttribute("rpc.request.metadata."+header.Key, header.Value) + } + ctx = metadata.NewIncomingContext(ctx, md) + } } res, err := delegateHandler(ctx, req) diff --git a/sdk/instrumentation/google.golang.org/grpc/server_test.go b/sdk/instrumentation/google.golang.org/grpc/server_test.go index c192f50..255c90b 100644 --- a/sdk/instrumentation/google.golang.org/grpc/server_test.go +++ b/sdk/instrumentation/google.golang.org/grpc/server_test.go @@ -248,6 +248,128 @@ func TestServerInterceptorFilterWithMaxProcessingBodyLen(t *testing.T) { assert.NoError(t, err) } +func TestServerInterceptorFilterDecorations(t *testing.T) { + var spans []*mock.Span + mockInterceptor := makeMockUnaryServerInterceptor(&spans) + // wrap interceptor with filter + s := grpc.NewServer( + grpc.UnaryInterceptor( + WrapUnaryServerInterceptor(mockInterceptor, mock.SpanFromContext, &Options{Filter: mock.Filter{ + Evaluator: func(span sdk.Span) result.FilterResult { + return result.FilterResult{Block: false, Decorations: &result.Decorations{ + RequestHeaderInjections: []result.KeyValueString{ + { + Key: "injected-header", + Value: "injected-value", + }, + }, + }} + }, + }}, nil), + ), + ) + defer s.Stop() + + mockServer := &server{} + helloworld.RegisterGreeterServer(s, mockServer) + + dialer := createDialer(s) + + ctx := context.Background() + conn, err := grpc.DialContext( + ctx, + "bufnet", + grpc.WithContextDialer(dialer), + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("failed to dial bufnet: %v", err) + } + defer conn.Close() + + client := helloworld.NewGreeterClient(conn) + + ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("test_key", "test_value")) + _, err = client.SayHello(ctx, &helloworld.HelloRequest{ + Name: "Pupo", + }) + + md := mockServer.requestHeader + // assert original header + val, found := md["test_key"] + assert.True(t, found) + assert.Equal(t, "test_value", val[0]) + + // assert injected header + val, found = md["injected-header"] + assert.True(t, found) + assert.Equal(t, "injected-value", val[0]) + assert.NoError(t, err) + + assert.Equal(t, 1, len(spans)) + span := spans[0] + spanAttributePresent := false + span.GetAttributes().Iterate(func(key string, value interface{}) bool { + if key == "rpc.request.metadata.injected-header" { + assert.Equal(t, "injected-value", value.(string)) + spanAttributePresent = true + return false + } + return true + }) + assert.True(t, spanAttributePresent) +} + +func TestServerInterceptorFilterEmptyDecorations(t *testing.T) { + spans := []*mock.Span{} + mockInterceptor := makeMockUnaryServerInterceptor(&spans) + // wrap interceptor with filter + s := grpc.NewServer( + grpc.UnaryInterceptor( + WrapUnaryServerInterceptor(mockInterceptor, mock.SpanFromContext, &Options{Filter: mock.Filter{ + Evaluator: func(span sdk.Span) result.FilterResult { + return result.FilterResult{Block: false, Decorations: &result.Decorations{}} + }, + }}, nil), + ), + ) + defer s.Stop() + + mockServer := &server{} + helloworld.RegisterGreeterServer(s, mockServer) + + dialer := createDialer(s) + + ctx := context.Background() + conn, err := grpc.DialContext( + ctx, + "bufnet", + grpc.WithContextDialer(dialer), + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("failed to dial bufnet: %v", err) + } + defer conn.Close() + + client := helloworld.NewGreeterClient(conn) + + ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("test_key", "test_value")) + _, err = client.SayHello(ctx, &helloworld.HelloRequest{ + Name: "Pupo", + }) + + md := mockServer.requestHeader + // assert original header + val, found := md["test_key"] + assert.True(t, found) + assert.Equal(t, "test_value", val[0]) + + assert.Equal(t, 1, len(spans)) +} + func TestServerHandlerHelloWorldSuccess(t *testing.T) { defer internalconfig.ResetConfig() diff --git a/sdk/instrumentation/net/http/attributes_test.go b/sdk/instrumentation/net/http/attributes_test.go index f7c8ca1..e983bc8 100644 --- a/sdk/instrumentation/net/http/attributes_test.go +++ b/sdk/instrumentation/net/http/attributes_test.go @@ -12,7 +12,7 @@ func TestSetScalarAttributeSuccess(t *testing.T) { h := http.Header{} h.Set("key_1", "value_1") span := mock.NewSpan() - SetAttributesFromHeaders("request", headerMapAccessor{h}, span) + SetAttributesFromHeaders("request", &headerMapAccessor{h}, span) assert.Equal(t, "value_1", span.ReadAttribute("http.request.header.key_1").(string)) _ = span.ReadAttribute("container_id") // needed in containarized envs @@ -25,7 +25,7 @@ func TestSetMultivalueAttributeSuccess(t *testing.T) { h.Add("key_1", "value_2") span := mock.NewSpan() - SetAttributesFromHeaders("response", headerMapAccessor{h}, span) + SetAttributesFromHeaders("response", &headerMapAccessor{h}, span) assert.Equal(t, "value_1", span.ReadAttribute("http.response.header.key_1[0]").(string)) assert.Equal(t, "value_2", span.ReadAttribute("http.response.header.key_1[1]").(string)) diff --git a/sdk/instrumentation/net/http/contenttype_test.go b/sdk/instrumentation/net/http/contenttype_test.go index 2475d45..62526c2 100644 --- a/sdk/instrumentation/net/http/contenttype_test.go +++ b/sdk/instrumentation/net/http/contenttype_test.go @@ -10,7 +10,7 @@ import ( ) func TestRecordingDecisionReturnsFalseOnNoContentType(t *testing.T) { - assert.Equal(t, false, ShouldRecordBodyOfContentType(headerMapAccessor{http.Header{"A": []string{"B"}}})) + assert.Equal(t, false, ShouldRecordBodyOfContentType(&headerMapAccessor{http.Header{"A": []string{"B"}}})) } func TestRecordingDecisionSuccessOnHeaderSet(t *testing.T) { @@ -32,7 +32,7 @@ func TestRecordingDecisionSuccessOnHeaderSet(t *testing.T) { for _, tCase := range tCases { h := http.Header{} h.Set("Content-Type", tCase.contentType) - assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(headerMapAccessor{h})) + assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(&headerMapAccessor{h})) } } @@ -56,7 +56,7 @@ func TestRecordingDecisionSuccessOnHeaderAdd(t *testing.T) { for _, header := range tCase.contentTypes { h.Add("Content-Type", header) } - assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(headerMapAccessor{h})) + assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(&headerMapAccessor{h})) } } @@ -80,7 +80,7 @@ func TestXMLRecordingDecisionSuccessOnHeaderAdd(t *testing.T) { for _, header := range tCase.contentTypes { h.Add("Content-Type", header) } - assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(headerMapAccessor{h})) + assert.Equal(t, tCase.shouldRecord, ShouldRecordBodyOfContentType(&headerMapAccessor{h})) } internalconfig.ResetConfig() } @@ -99,6 +99,6 @@ func TestHasMultiPartFormDataContentTypeHeader(t *testing.T) { for _, tCase := range tCases { h := http.Header{} h.Set("Content-Type", tCase.contentType) - assert.Equal(t, tCase.isMultiPartFormData, HasMultiPartFormDataContentTypeHeader(headerMapAccessor{h})) + assert.Equal(t, tCase.isMultiPartFormData, HasMultiPartFormDataContentTypeHeader(&headerMapAccessor{h})) } } diff --git a/sdk/instrumentation/net/http/handler.go b/sdk/instrumentation/net/http/handler.go index 99249eb..3631387 100644 --- a/sdk/instrumentation/net/http/handler.go +++ b/sdk/instrumentation/net/http/handler.go @@ -49,6 +49,7 @@ func WrapHandler(delegate http.Handler, spanFromContext sdk.SpanFromContext, opt func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() span := h.spanFromContextRetriever(ctx) + headersAccessor := NewHeaderMapAccessor(r.Header) h.mh.AddToRequestCount(1, r) @@ -77,19 +78,19 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Sets an attribute per each request header. if h.dataCaptureConfig.HttpHeaders.Request.Value { - SetAttributesFromHeaders("request", NewHeaderMapAccessor(r.Header), span) + SetAttributesFromHeaders("request", headersAccessor, span) } // nil check for body is important as this block turns the body into another // object that isn't nil and that will leverage the "Observer effect". - if r.Body != nil && h.dataCaptureConfig.HttpBody.Request.Value && ShouldRecordBodyOfContentType(headerMapAccessor{r.Header}) { + if r.Body != nil && h.dataCaptureConfig.HttpBody.Request.Value && ShouldRecordBodyOfContentType(headersAccessor) { body, err := io.ReadAll(r.Body) if err != nil { return } defer r.Body.Close() - isMultipartFormDataBody := HasMultiPartFormDataContentTypeHeader(headerMapAccessor{r.Header}) + isMultipartFormDataBody := HasMultiPartFormDataContentTypeHeader(headersAccessor) // Only records the body if it is not empty and the content type // header is not streamable @@ -106,6 +107,11 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if filterResult.Block { w.WriteHeader(int(filterResult.ResponseStatusCode)) return + } else if filterResult.Decorations != nil { + for _, header := range filterResult.Decorations.RequestHeaderInjections { + headersAccessor.AddHeader(header.Key, header.Value) + span.SetAttribute("http.request.header."+header.Key, header.Value) + } } // create http.ResponseWriter interceptor for tracking status code @@ -113,16 +119,17 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // tag found status code on exit defer func() { + responseHeadersAccessor := NewHeaderMapAccessor(wi.Header()) if h.dataCaptureConfig.HttpBody.Response.Value && len(wi.body) > 0 && - ShouldRecordBodyOfContentType(headerMapAccessor{wi.Header()}) { + ShouldRecordBodyOfContentType(responseHeadersAccessor) { setTruncatedBodyAttribute("response", wi.body, int(h.dataCaptureConfig.BodyMaxSizeBytes.Value), span, - HasMultiPartFormDataContentTypeHeader(headerMapAccessor{wi.Header()})) + HasMultiPartFormDataContentTypeHeader(responseHeadersAccessor)) } if h.dataCaptureConfig.HttpHeaders.Response.Value { // Sets an attribute per each response header. - SetAttributesFromHeaders("response", headerMapAccessor{wi.Header()}, span) + SetAttributesFromHeaders("response", responseHeadersAccessor, span) } }() diff --git a/sdk/instrumentation/net/http/handler_test.go b/sdk/instrumentation/net/http/handler_test.go index 403c87f..7781c98 100644 --- a/sdk/instrumentation/net/http/handler_test.go +++ b/sdk/instrumentation/net/http/handler_test.go @@ -517,6 +517,82 @@ func TestProcessingBodyIsTrimmed(t *testing.T) { ih.ServeHTTP(w, r) } +func TestFilterResultDecorations(t *testing.T) { + defer internalconfig.ResetConfig() + + h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + assert.Equal(t, "injected-value", r.Header.Values("injected-header")[0]) + }) + + wh, _ := WrapHandler(h, mock.SpanFromContext, &Options{ + Filter: mock.Filter{ + Evaluator: func(span sdk.Span) result.FilterResult { + return result.FilterResult{Block: false, Decorations: &result.Decorations{ + RequestHeaderInjections: []result.KeyValueString{ + { + Key: "injected-header", + Value: "injected-value", + }, + }, + }} + }, + }, + }, map[string]string{}, &metricsHandler{}).(*handler) + wh.dataCaptureConfig = emptyTestConfig + + ih := &mockHandler{baseHandler: wh} + + r, _ := http.NewRequest("GET", "http://traceable.ai/foo?user_id=1", nil) + r.Header.Add("Content-Type", "application/json") + w := httptest.NewRecorder() + + ih.ServeHTTP(w, r) + + assert.Equal(t, 1, len(ih.spans)) + + span := ih.spans[0] + spanAttributePresent := false + span.GetAttributes().Iterate(func(key string, value interface{}) bool { + if key == "http.request.header.injected-header" { + assert.Equal(t, "injected-value", value.(string)) + spanAttributePresent = true + return false + } + return true + }) + assert.True(t, spanAttributePresent) +} + +func TestFilterResultEmptyDecorations(t *testing.T) { + defer internalconfig.ResetConfig() + + headersLen := 0 + h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + assert.NotZero(t, len(r.Header)) + assert.Equal(t, headersLen, len(r.Header)) + }) + + wh, _ := WrapHandler(h, mock.SpanFromContext, &Options{ + Filter: mock.Filter{ + Evaluator: func(span sdk.Span) result.FilterResult { + return result.FilterResult{Block: false, Decorations: &result.Decorations{}} + }, + }, + }, map[string]string{}, &metricsHandler{}).(*handler) + wh.dataCaptureConfig = emptyTestConfig + + ih := &mockHandler{baseHandler: wh} + + r, _ := http.NewRequest("GET", "http://traceable.ai/foo?user_id=1", nil) + r.Header.Add("Content-Type", "application/json") + headersLen = len(r.Header) + w := httptest.NewRecorder() + + ih.ServeHTTP(w, r) + + assert.Equal(t, 1, len(ih.spans)) +} + func TestUrlAttribute(t *testing.T) { defer internalconfig.ResetConfig() diff --git a/sdk/instrumentation/net/http/headeraccessor.go b/sdk/instrumentation/net/http/headeraccessor.go index e026d0f..ba456de 100644 --- a/sdk/instrumentation/net/http/headeraccessor.go +++ b/sdk/instrumentation/net/http/headeraccessor.go @@ -9,24 +9,26 @@ import "net/http" type HeaderAccessor interface { Lookup(key string) []string ForEachHeader(callback func(key string, values []string) error) error + AddHeader(key, values string) } type headerMapAccessor struct { header http.Header } -var _ HeaderAccessor = headerMapAccessor{} +// type assertion +var _ HeaderAccessor = (*headerMapAccessor)(nil) // NewHeaderMapAccessor returns a HeaderAccessor func NewHeaderMapAccessor(h http.Header) HeaderAccessor { return &headerMapAccessor{h} } -func (a headerMapAccessor) Lookup(key string) []string { +func (a *headerMapAccessor) Lookup(key string) []string { return a.header[http.CanonicalHeaderKey(key)] } -func (a headerMapAccessor) ForEachHeader(callback func(key string, values []string) error) error { +func (a *headerMapAccessor) ForEachHeader(callback func(key string, values []string) error) error { for key, values := range a.header { if err := callback(key, values); err != nil { return err @@ -34,3 +36,7 @@ func (a headerMapAccessor) ForEachHeader(callback func(key string, values []stri } return nil } + +func (a *headerMapAccessor) AddHeader(key, value string) { + a.header.Set(key, value) +} diff --git a/sdk/instrumentation/net/http/transport.go b/sdk/instrumentation/net/http/transport.go index 0294850..f2251fa 100644 --- a/sdk/instrumentation/net/http/transport.go +++ b/sdk/instrumentation/net/http/transport.go @@ -29,20 +29,21 @@ func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { // round tripper. return rt.delegate.RoundTrip(req) } + reqHeadersAccessor := NewHeaderMapAccessor(req.Header) for key, value := range rt.defaultAttributes { span.SetAttribute(key, value) } if rt.dataCaptureConfig.HttpHeaders.Request.Value { - SetAttributesFromHeaders("request", headerMapAccessor{req.Header}, span) + SetAttributesFromHeaders("request", reqHeadersAccessor, span) } // Only records the body if it is not empty and the content type header // is in the recording accept list. Notice in here we rely on the fact that // the content type is not streamable, otherwise we could end up in a very // expensive parsing of a big body in memory. - if req.Body != nil && rt.dataCaptureConfig.HttpBody.Request.Value && ShouldRecordBodyOfContentType(headerMapAccessor{req.Header}) { + if req.Body != nil && rt.dataCaptureConfig.HttpBody.Request.Value && ShouldRecordBodyOfContentType(reqHeadersAccessor) { body, err := ioutil.ReadAll(req.Body) if err != nil { return rt.delegate.RoundTrip(req) @@ -51,7 +52,7 @@ func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if len(body) > 0 { setTruncatedBodyAttribute("request", body, int(rt.dataCaptureConfig.BodyMaxSizeBytes.Value), span, - HasMultiPartFormDataContentTypeHeader(headerMapAccessor{req.Header})) + HasMultiPartFormDataContentTypeHeader(reqHeadersAccessor)) } req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) @@ -61,9 +62,10 @@ func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if err != nil { return res, err } + resHeadersAccessor := NewHeaderMapAccessor(res.Header) // Notice, parsing a streamed content in memory can be expensive. - if rt.dataCaptureConfig.HttpBody.Response.Value && ShouldRecordBodyOfContentType(headerMapAccessor{res.Header}) { + if rt.dataCaptureConfig.HttpBody.Response.Value && ShouldRecordBodyOfContentType(resHeadersAccessor) { body, err := ioutil.ReadAll(res.Body) if err != nil { return res, nil @@ -72,7 +74,7 @@ func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if len(body) > 0 { setTruncatedBodyAttribute("response", body, int(rt.dataCaptureConfig.BodyMaxSizeBytes.Value), span, - HasMultiPartFormDataContentTypeHeader(headerMapAccessor{res.Header})) + HasMultiPartFormDataContentTypeHeader(resHeadersAccessor)) } res.Body = ioutil.NopCloser(bytes.NewBuffer(body)) @@ -80,7 +82,7 @@ func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if rt.dataCaptureConfig.HttpHeaders.Response.Value { // Sets an attribute per each response header. - SetAttributesFromHeaders("response", headerMapAccessor{res.Header}, span) + SetAttributesFromHeaders("response", resHeadersAccessor, span) } return res, err From 7d4b969c2425c31e16f43ba96475420b8c46edde Mon Sep 17 00:00:00 2001 From: test Date: Wed, 24 Jul 2024 12:10:28 +0530 Subject: [PATCH 3/4] sorting imports --- sdk/instrumentation/google.golang.org/grpc/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/instrumentation/google.golang.org/grpc/server.go b/sdk/instrumentation/google.golang.org/grpc/server.go index 3b38a6a..0e0957a 100644 --- a/sdk/instrumentation/google.golang.org/grpc/server.go +++ b/sdk/instrumentation/google.golang.org/grpc/server.go @@ -2,7 +2,6 @@ package grpc // import "github.com/hypertrace/goagent/sdk/instrumentation/google import ( "context" - "google.golang.org/grpc/metadata" "net/http" "strings" @@ -13,6 +12,7 @@ import ( internalconfig "github.com/hypertrace/goagent/sdk/internal/config" "github.com/hypertrace/goagent/sdk/internal/container" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" "google.golang.org/grpc/stats" "google.golang.org/grpc/status" From cc405c4e610c9f70b16a7db3304c4b432a667836 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 24 Jul 2024 12:29:03 +0530 Subject: [PATCH 4/4] fix lint --- sdk/instrumentation/google.golang.org/grpc/server_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/instrumentation/google.golang.org/grpc/server_test.go b/sdk/instrumentation/google.golang.org/grpc/server_test.go index 255c90b..61b551d 100644 --- a/sdk/instrumentation/google.golang.org/grpc/server_test.go +++ b/sdk/instrumentation/google.golang.org/grpc/server_test.go @@ -294,6 +294,7 @@ func TestServerInterceptorFilterDecorations(t *testing.T) { _, err = client.SayHello(ctx, &helloworld.HelloRequest{ Name: "Pupo", }) + assert.NoError(t, err) md := mockServer.requestHeader // assert original header @@ -360,6 +361,7 @@ func TestServerInterceptorFilterEmptyDecorations(t *testing.T) { _, err = client.SayHello(ctx, &helloworld.HelloRequest{ Name: "Pupo", }) + assert.NoError(t, err) md := mockServer.requestHeader // assert original header