diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f8d961aa4..f47d098a0a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ In case of type mismatch, they don't panic right away but return an invalid zero-initialized instance for consistency with other OneOf field accessors (#5034) - Update OTLP to v0.15.0 (#5064) +- Adding support for transition from older versions of OTLP to OTLP v0.15.0 (#5085) ### 🧰 Bug fixes 🧰 diff --git a/model/otlp/json_unmarshaler.go b/model/otlp/json_unmarshaler.go index 1291d3f8f19..b1db47d684b 100644 --- a/model/otlp/json_unmarshaler.go +++ b/model/otlp/json_unmarshaler.go @@ -23,6 +23,7 @@ import ( otlpmetrics "go.opentelemetry.io/collector/model/internal/data/protogen/metrics/v1" otlptrace "go.opentelemetry.io/collector/model/internal/data/protogen/trace/v1" ipdata "go.opentelemetry.io/collector/model/internal/pdata" + "go.opentelemetry.io/collector/model/otlpgrpc" "go.opentelemetry.io/collector/model/pdata" ) @@ -54,6 +55,7 @@ func (d *jsonUnmarshaler) UnmarshalLogs(buf []byte) (pdata.Logs, error) { if err := d.delegate.Unmarshal(bytes.NewReader(buf), ld); err != nil { return pdata.Logs{}, err } + otlpgrpc.InstrumentationLibraryLogsToScope(ld.ResourceLogs) return ipdata.LogsFromOtlp(ld), nil } @@ -62,6 +64,7 @@ func (d *jsonUnmarshaler) UnmarshalMetrics(buf []byte) (pdata.Metrics, error) { if err := d.delegate.Unmarshal(bytes.NewReader(buf), md); err != nil { return pdata.Metrics{}, err } + otlpgrpc.InstrumentationLibraryMetricsToScope(md.ResourceMetrics) return ipdata.MetricsFromOtlp(md), nil } @@ -70,5 +73,6 @@ func (d *jsonUnmarshaler) UnmarshalTraces(buf []byte) (pdata.Traces, error) { if err := d.delegate.Unmarshal(bytes.NewReader(buf), td); err != nil { return pdata.Traces{}, err } + otlpgrpc.InstrumentationLibrarySpansToScope(td.ResourceSpans) return ipdata.TracesFromOtlp(td), nil } diff --git a/model/otlpgrpc/logs.go b/model/otlpgrpc/logs.go index 173230bc9e2..99c3e525d1f 100644 --- a/model/otlpgrpc/logs.go +++ b/model/otlpgrpc/logs.go @@ -22,6 +22,7 @@ import ( "google.golang.org/grpc" otlpcollectorlog "go.opentelemetry.io/collector/model/internal/data/protogen/collector/logs/v1" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/common/v1" otlplogs "go.opentelemetry.io/collector/model/internal/data/protogen/logs/v1" ipdata "go.opentelemetry.io/collector/model/internal/pdata" "go.opentelemetry.io/collector/model/pdata" @@ -119,7 +120,11 @@ func (lr LogsRequest) MarshalProto() ([]byte, error) { // UnmarshalProto unmarshalls LogsRequest from proto bytes. func (lr LogsRequest) UnmarshalProto(data []byte) error { - return lr.orig.Unmarshal(data) + if err := lr.orig.Unmarshal(data); err != nil { + return err + } + InstrumentationLibraryLogsToScope(lr.orig.ResourceLogs) + return nil } // MarshalJSON marshals LogsRequest into JSON bytes. @@ -133,7 +138,11 @@ func (lr LogsRequest) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshalls LogsRequest from JSON bytes. func (lr LogsRequest) UnmarshalJSON(data []byte) error { - return jsonUnmarshaler.Unmarshal(bytes.NewReader(data), lr.orig) + if err := jsonUnmarshaler.Unmarshal(bytes.NewReader(data), lr.orig); err != nil { + return err + } + InstrumentationLibraryLogsToScope(lr.orig.ResourceLogs) + return nil } func (lr LogsRequest) SetLogs(ld pdata.Logs) { @@ -191,3 +200,29 @@ func (s rawLogsServer) Export(ctx context.Context, request *otlpcollectorlog.Exp rsp, err := s.srv.Export(ctx, LogsRequest{orig: request}) return rsp.orig, err } + +// InstrumentationLibraryLogsToScope implements the translation of resource logs data +// following the v0.15.0 upgrade: +// receivers SHOULD check if instrumentation_library_logs is set +// and scope_logs is not set then the value in instrumentation_library_logs +// SHOULD be used instead by converting InstrumentationLibraryLogs into ScopeLogs. +// If scope_logs is set then instrumentation_library_logs SHOULD be ignored. +// https://github.com/open-telemetry/opentelemetry-proto/blob/3c2915c01a9fb37abfc0415ec71247c4978386b0/opentelemetry/proto/logs/v1/logs.proto#L58 +func InstrumentationLibraryLogsToScope(rls []*otlplogs.ResourceLogs) { + for _, rl := range rls { + if len(rl.ScopeLogs) == 0 { + for _, ill := range rl.InstrumentationLibraryLogs { + scopeLogs := otlplogs.ScopeLogs{ + Scope: v1.InstrumentationScope{ + Name: ill.InstrumentationLibrary.Name, + Version: ill.InstrumentationLibrary.Version, + }, + LogRecords: ill.LogRecords, + SchemaUrl: ill.SchemaUrl, + } + rl.ScopeLogs = append(rl.ScopeLogs, &scopeLogs) + } + } + rl.InstrumentationLibraryLogs = nil + } +} diff --git a/model/otlpgrpc/logs_test.go b/model/otlpgrpc/logs_test.go index e4b598a5ef6..749740b68f6 100644 --- a/model/otlpgrpc/logs_test.go +++ b/model/otlpgrpc/logs_test.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/logs/v1" "go.opentelemetry.io/collector/model/internal/pdata" ) @@ -42,27 +43,88 @@ var _ json.Marshaler = LogsRequest{} var logsRequestJSON = []byte(` { - "resourceLogs": [ + "resourceLogs": [ { - "resource": {}, - "scopeLogs": [ - { - "scope": {}, - "logRecords": [ + "resource": {}, + "scopeLogs": [ { - "body": { - "stringValue": "test_log_record" - }, - "traceId": "", - "spanId": "" + "scope": {}, + "logRecords": [ + { + "body": { + "stringValue": "test_log_record" + }, + "traceId": "", + "spanId": "" + } + ] } - ] - } - ] + ] } - ] + ] }`) +var logsTransitionData = [][]byte{ + []byte(` + { + "resourceLogs": [ + { + "resource": {}, + "instrumentationLibraryLogs": [ + { + "instrumentationLibrary": {}, + "logRecords": [ + { + "body": { + "stringValue": "test_log_record" + }, + "traceId": "", + "spanId": "" + } + ] + } + ] + } + ] + }`), + []byte(` + { + "resourceLogs": [ + { + "resource": {}, + "instrumentationLibraryLogs": [ + { + "instrumentationLibrary": {}, + "logRecords": [ + { + "body": { + "stringValue": "test_log_record" + }, + "traceId": "", + "spanId": "" + } + ] + } + ], + "scopeLogs": [ + { + "scope": {}, + "logRecords": [ + { + "body": { + "stringValue": "test_log_record" + }, + "traceId": "", + "spanId": "" + } + ] + } + ] + } + ] + }`), +} + func TestLogsRequestJSON(t *testing.T) { lr := NewLogsRequest() assert.NoError(t, lr.UnmarshalJSON(logsRequestJSON)) @@ -73,6 +135,18 @@ func TestLogsRequestJSON(t *testing.T) { assert.Equal(t, strings.Join(strings.Fields(string(logsRequestJSON)), ""), string(got)) } +func TestLogsRequestJSONTransition(t *testing.T) { + for _, data := range logsTransitionData { + lr := NewLogsRequest() + assert.NoError(t, lr.UnmarshalJSON(data)) + assert.Equal(t, "test_log_record", lr.Logs().ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Body().AsString()) + + got, err := lr.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, strings.Join(strings.Fields(string(logsRequestJSON)), ""), string(got)) + } +} + func TestLogsRequestJSON_Deprecated(t *testing.T) { lr, err := UnmarshalJSONLogsRequest(logsRequestJSON) assert.NoError(t, err) @@ -116,6 +190,41 @@ func TestLogsGrpc(t *testing.T) { assert.Equal(t, NewLogsResponse(), resp) } +func TestLogsGrpcTransition(t *testing.T) { + lis := bufconn.Listen(1024 * 1024) + s := grpc.NewServer() + RegisterLogsServer(s, &fakeLogsServer{t: t}) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + assert.NoError(t, s.Serve(lis)) + }() + t.Cleanup(func() { + s.Stop() + wg.Wait() + }) + + cc, err := grpc.Dial("bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock()) + assert.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, cc.Close()) + }) + + logClient := NewLogsClient(cc) + + req := generateLogsRequestWithInstrumentationLibrary() + InstrumentationLibraryLogsToScope(req.orig.ResourceLogs) + resp, err := logClient.Export(context.Background(), req) + assert.NoError(t, err) + assert.Equal(t, NewLogsResponse(), resp) +} + func TestLogsGrpcError(t *testing.T) { lis := bufconn.Listen(1024 * 1024) s := grpc.NewServer() @@ -170,3 +279,13 @@ func generateLogsRequest() LogsRequest { lr.SetLogs(ld) return lr } + +func generateLogsRequestWithInstrumentationLibrary() LogsRequest { + lr := generateLogsRequest() + lr.orig.ResourceLogs[0].InstrumentationLibraryLogs = []*v1.InstrumentationLibraryLogs{ //nolint:staticcheck // SA1019 ignore this! + { + LogRecords: lr.orig.ResourceLogs[0].ScopeLogs[0].LogRecords, + }, + } + return lr +} diff --git a/model/otlpgrpc/metrics.go b/model/otlpgrpc/metrics.go index 125cc96f94d..bc8b0ea2b83 100644 --- a/model/otlpgrpc/metrics.go +++ b/model/otlpgrpc/metrics.go @@ -21,6 +21,7 @@ import ( "google.golang.org/grpc" otlpcollectormetrics "go.opentelemetry.io/collector/model/internal/data/protogen/collector/metrics/v1" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/common/v1" otlpmetrics "go.opentelemetry.io/collector/model/internal/data/protogen/metrics/v1" ipdata "go.opentelemetry.io/collector/model/internal/pdata" "go.opentelemetry.io/collector/model/pdata" @@ -129,7 +130,11 @@ func (mr MetricsRequest) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshalls MetricsRequest from JSON bytes. func (mr MetricsRequest) UnmarshalJSON(data []byte) error { - return jsonUnmarshaler.Unmarshal(bytes.NewReader(data), mr.orig) + if err := jsonUnmarshaler.Unmarshal(bytes.NewReader(data), mr.orig); err != nil { + return err + } + InstrumentationLibraryMetricsToScope(mr.orig.ResourceMetrics) + return nil } func (mr MetricsRequest) SetMetrics(ld pdata.Metrics) { @@ -187,3 +192,29 @@ func (s rawMetricsServer) Export(ctx context.Context, request *otlpcollectormetr rsp, err := s.srv.Export(ctx, MetricsRequest{orig: request}) return rsp.orig, err } + +// InstrumentationLibraryMetricsToScope implements the translation of resource metrics data +// following the v0.15.0 upgrade: +// receivers SHOULD check if instrumentation_library_metrics is set +// and scope_metrics is not set then the value in instrumentation_library_metrics +// SHOULD be used instead by converting InstrumentationLibraryMetrics into ScopeMetrics. +// If scope_metrics is set then instrumentation_library_metrics SHOULD be ignored. +// https://github.com/open-telemetry/opentelemetry-proto/blob/3c2915c01a9fb37abfc0415ec71247c4978386b0/opentelemetry/proto/metrics/v1/metrics.proto#L58 +func InstrumentationLibraryMetricsToScope(rms []*otlpmetrics.ResourceMetrics) { + for _, rm := range rms { + if len(rm.ScopeMetrics) == 0 { + for _, ilm := range rm.InstrumentationLibraryMetrics { + scopeMetrics := otlpmetrics.ScopeMetrics{ + Scope: v1.InstrumentationScope{ + Name: ilm.InstrumentationLibrary.Name, + Version: ilm.InstrumentationLibrary.Version, + }, + Metrics: ilm.Metrics, + SchemaUrl: ilm.SchemaUrl, + } + rm.ScopeMetrics = append(rm.ScopeMetrics, &scopeMetrics) + } + } + rm.InstrumentationLibraryMetrics = nil + } +} diff --git a/model/otlpgrpc/metrics_test.go b/model/otlpgrpc/metrics_test.go index d9b46cadb7e..00b9be718aa 100644 --- a/model/otlpgrpc/metrics_test.go +++ b/model/otlpgrpc/metrics_test.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/metrics/v1" "go.opentelemetry.io/collector/model/pdata" ) @@ -42,23 +43,72 @@ var _ json.Marshaler = MetricsRequest{} var metricsRequestJSON = []byte(` { - "resourceMetrics": [ - { - "resource": {}, - "scopeMetrics": [ + "resourceMetrics": [ { - "scope": {}, - "metrics": [ - { - "name": "test_metric" - } - ] + "resource": {}, + "scopeMetrics": [ + { + "scope": {}, + "metrics": [ + { + "name": "test_metric" + } + ] + } + ] } - ] - } - ] + ] }`) +var metricsTransitionData = [][]byte{ + []byte(` + { + "resourceMetrics": [ + { + "resource": {}, + "instrumentationLibraryMetrics": [ + { + "instrumentationLibrary": {}, + "metrics": [ + { + "name": "test_metric" + } + ] + } + ] + } + ] + }`), + []byte(` + { + "resourceMetrics": [ + { + "resource": {}, + "instrumentationLibraryMetrics": [ + { + "instrumentationLibrary": {}, + "metrics": [ + { + "name": "test_metric" + } + ] + } + ], + "scopeMetrics": [ + { + "scope": {}, + "metrics": [ + { + "name": "test_metric" + } + ] + } + ] + } + ] + }`), +} + func TestMetricsRequestJSON(t *testing.T) { mr := NewMetricsRequest() assert.NoError(t, mr.UnmarshalJSON(metricsRequestJSON)) @@ -69,6 +119,18 @@ func TestMetricsRequestJSON(t *testing.T) { assert.Equal(t, strings.Join(strings.Fields(string(metricsRequestJSON)), ""), string(got)) } +func TestMetricsRequestJSONTransition(t *testing.T) { + for _, data := range metricsTransitionData { + mr := NewMetricsRequest() + assert.NoError(t, mr.UnmarshalJSON(data)) + assert.Equal(t, "test_metric", mr.Metrics().ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).Name()) + + got, err := mr.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, strings.Join(strings.Fields(string(metricsRequestJSON)), ""), string(got)) + } +} + func TestMetricsRequestJSON_Deprecated(t *testing.T) { mr, err := UnmarshalJSONMetricsRequest(metricsRequestJSON) assert.NoError(t, err) @@ -112,6 +174,41 @@ func TestMetricsGrpc(t *testing.T) { assert.Equal(t, NewMetricsResponse(), resp) } +func TestMetricsGrpcTransition(t *testing.T) { + lis := bufconn.Listen(1024 * 1024) + s := grpc.NewServer() + RegisterMetricsServer(s, &fakeMetricsServer{t: t}) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + assert.NoError(t, s.Serve(lis)) + }() + t.Cleanup(func() { + s.Stop() + wg.Wait() + }) + + cc, err := grpc.Dial("bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock()) + assert.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, cc.Close()) + }) + + logClient := NewMetricsClient(cc) + + req := generateMetricsRequestWithInstrumentationLibrary() + InstrumentationLibraryMetricsToScope(req.orig.ResourceMetrics) + resp, err := logClient.Export(context.Background(), req) + assert.NoError(t, err) + assert.Equal(t, NewMetricsResponse(), resp) +} + func TestMetricsGrpcError(t *testing.T) { lis := bufconn.Listen(1024 * 1024) s := grpc.NewServer() @@ -166,3 +263,13 @@ func generateMetricsRequest() MetricsRequest { mr.SetMetrics(md) return mr } + +func generateMetricsRequestWithInstrumentationLibrary() MetricsRequest { + mr := generateMetricsRequest() + mr.orig.ResourceMetrics[0].InstrumentationLibraryMetrics = []*v1.InstrumentationLibraryMetrics{ //nolint:staticcheck // SA1019 ignore this! + { + Metrics: mr.orig.ResourceMetrics[0].ScopeMetrics[0].Metrics, + }, + } + return mr +} diff --git a/model/otlpgrpc/traces.go b/model/otlpgrpc/traces.go index bff33d0f08e..2d0030956b1 100644 --- a/model/otlpgrpc/traces.go +++ b/model/otlpgrpc/traces.go @@ -21,6 +21,7 @@ import ( "google.golang.org/grpc" otlpcollectortrace "go.opentelemetry.io/collector/model/internal/data/protogen/collector/trace/v1" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/common/v1" otlptrace "go.opentelemetry.io/collector/model/internal/data/protogen/trace/v1" ipdata "go.opentelemetry.io/collector/model/internal/pdata" "go.opentelemetry.io/collector/model/pdata" @@ -115,7 +116,11 @@ func (tr TracesRequest) MarshalProto() ([]byte, error) { // UnmarshalProto unmarshalls TracesRequest from proto bytes. func (tr TracesRequest) UnmarshalProto(data []byte) error { - return tr.orig.Unmarshal(data) + if err := tr.orig.Unmarshal(data); err != nil { + return err + } + InstrumentationLibrarySpansToScope(tr.orig.ResourceSpans) + return nil } // MarshalJSON marshals TracesRequest into JSON bytes. @@ -129,7 +134,11 @@ func (tr TracesRequest) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshalls TracesRequest from JSON bytes. func (tr TracesRequest) UnmarshalJSON(data []byte) error { - return jsonUnmarshaler.Unmarshal(bytes.NewReader(data), tr.orig) + if err := jsonUnmarshaler.Unmarshal(bytes.NewReader(data), tr.orig); err != nil { + return err + } + InstrumentationLibrarySpansToScope(tr.orig.ResourceSpans) + return nil } func (tr TracesRequest) SetTraces(td pdata.Traces) { @@ -188,3 +197,29 @@ func (s rawTracesServer) Export(ctx context.Context, request *otlpcollectortrace rsp, err := s.srv.Export(ctx, TracesRequest{orig: request}) return rsp.orig, err } + +// InstrumentationLibraryToScope implements the translation of resource span data +// following the v0.15.0 upgrade: +// receivers SHOULD check if instrumentation_library_spans is set +// and scope_spans is not set then the value in instrumentation_library_spans +// SHOULD be used instead by converting InstrumentationLibrarySpans into ScopeSpans. +// If scope_spans is set then instrumentation_library_spans SHOULD be ignored. +// https://github.com/open-telemetry/opentelemetry-proto/blob/3c2915c01a9fb37abfc0415ec71247c4978386b0/opentelemetry/proto/trace/v1/trace.proto#L58 +func InstrumentationLibrarySpansToScope(rss []*otlptrace.ResourceSpans) { + for _, rs := range rss { + if len(rs.ScopeSpans) == 0 { + for _, ils := range rs.InstrumentationLibrarySpans { + scopeSpans := otlptrace.ScopeSpans{ + Scope: v1.InstrumentationScope{ + Name: ils.InstrumentationLibrary.Name, + Version: ils.InstrumentationLibrary.Version, + }, + Spans: ils.Spans, + SchemaUrl: ils.SchemaUrl, + } + rs.ScopeSpans = append(rs.ScopeSpans, &scopeSpans) + } + } + rs.InstrumentationLibrarySpans = nil + } +} diff --git a/model/otlpgrpc/traces_test.go b/model/otlpgrpc/traces_test.go index 107fe5e8cac..4071602ee8a 100644 --- a/model/otlpgrpc/traces_test.go +++ b/model/otlpgrpc/traces_test.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" + v1 "go.opentelemetry.io/collector/model/internal/data/protogen/trace/v1" "go.opentelemetry.io/collector/model/pdata" ) @@ -42,27 +43,88 @@ var _ json.Marshaler = TracesRequest{} var tracesRequestJSON = []byte(` { - "resourceSpans": [ - { - "resource": {}, - "scopeSpans": [ + "resourceSpans": [ { - "scope": {}, - "spans": [ - { - "traceId": "", - "spanId":"", - "parentSpanId":"", - "name": "test_span", - "status": {} - } - ] + "resource": {}, + "scopeSpans": [ + { + "scope": {}, + "spans": [ + { + "traceId": "", + "spanId":"", + "parentSpanId":"", + "name": "test_span", + "status": {} + } + ] + } + ] } - ] - } - ] + ] }`) +var tracesTransitionData = [][]byte{ + []byte(` + { + "resourceSpans": [ + { + "resource": {}, + "instrumentationLibrarySpans": [ + { + "instrumentationLibrary": {}, + "spans": [ + { + "traceId": "", + "spanId":"", + "parentSpanId":"", + "name": "test_span", + "status": {} + } + ] + } + ] + } + ] + }`), + []byte(` + { + "resourceSpans": [ + { + "resource": {}, + "instrumentationLibrarySpans": [ + { + "instrumentationLibrary": {}, + "spans": [ + { + "traceId": "", + "spanId":"", + "parentSpanId":"", + "name": "test_span", + "status": {} + } + ] + } + ], + "scopeSpans": [ + { + "scope": {}, + "spans": [ + { + "traceId": "", + "spanId":"", + "parentSpanId":"", + "name": "test_span", + "status": {} + } + ] + } + ] + } + ] + }`), +} + func TestTracesRequestJSON(t *testing.T) { tr := NewTracesRequest() assert.NoError(t, tr.UnmarshalJSON(tracesRequestJSON)) @@ -73,6 +135,18 @@ func TestTracesRequestJSON(t *testing.T) { assert.Equal(t, strings.Join(strings.Fields(string(tracesRequestJSON)), ""), string(got)) } +func TestTracesRequestJSONTransition(t *testing.T) { + for _, data := range tracesTransitionData { + tr := NewTracesRequest() + assert.NoError(t, tr.UnmarshalJSON(data)) + assert.Equal(t, "test_span", tr.Traces().ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Name()) + + got, err := tr.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, strings.Join(strings.Fields(string(tracesRequestJSON)), ""), string(got)) + } +} + func TestTracesRequestJSON_Deprecated(t *testing.T) { tr, err := UnmarshalJSONTracesRequest(tracesRequestJSON) assert.NoError(t, err) @@ -116,6 +190,41 @@ func TestTracesGrpc(t *testing.T) { assert.Equal(t, NewTracesResponse(), resp) } +func TestTracesGrpcTransition(t *testing.T) { + lis := bufconn.Listen(1024 * 1024) + s := grpc.NewServer() + RegisterTracesServer(s, &fakeTracesServer{t: t}) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + assert.NoError(t, s.Serve(lis)) + }() + t.Cleanup(func() { + s.Stop() + wg.Wait() + }) + + cc, err := grpc.Dial("bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock()) + assert.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, cc.Close()) + }) + + logClient := NewTracesClient(cc) + + req := generateTracesRequestWithInstrumentationLibrary() + InstrumentationLibrarySpansToScope(req.orig.ResourceSpans) + resp, err := logClient.Export(context.Background(), req) + assert.NoError(t, err) + assert.Equal(t, NewTracesResponse(), resp) +} + func TestTracesGrpcError(t *testing.T) { lis := bufconn.Listen(1024 * 1024) s := grpc.NewServer() @@ -170,3 +279,13 @@ func generateTracesRequest() TracesRequest { tr.SetTraces(td) return tr } + +func generateTracesRequestWithInstrumentationLibrary() TracesRequest { + tr := generateTracesRequest() + tr.orig.ResourceSpans[0].InstrumentationLibrarySpans = []*v1.InstrumentationLibrarySpans{ //nolint:staticcheck // SA1019 ignore this! + { + Spans: tr.orig.ResourceSpans[0].ScopeSpans[0].Spans, + }, + } + return tr +}