diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index 67975030a08..980396cc64b 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -205,7 +205,7 @@ func (s *StorageIntegration) testArchiveTrace(t *testing.T) { return err == nil && len(actual.Spans) == 1 }) require.True(t, found) - CompareTraces(t, &model.Trace{Spans: []*model.Span{expected}}, actual) + v1adapter.CompareTraces(t, &model.Trace{Spans: []*model.Span{expected}}, actual) } func (s *StorageIntegration) testGetLargeSpan(t *testing.T) { @@ -227,7 +227,7 @@ func (s *StorageIntegration) testGetLargeSpan(t *testing.T) { }) if !assert.True(t, found) { - CompareTraces(t, expected, actual) + v1adapter.CompareTraces(t, expected, actual) return } @@ -304,7 +304,7 @@ func (s *StorageIntegration) testGetTrace(t *testing.T) { return err == nil && len(actual.Spans) == len(expected.Spans) }) if !assert.True(t, found) { - CompareTraces(t, expected, actual) + v1adapter.CompareTraces(t, expected, actual) } t.Run("NotFound error", func(t *testing.T) { @@ -345,7 +345,7 @@ func (s *StorageIntegration) testFindTraces(t *testing.T) { s.skipIfNeeded(t) expected := expectedTracesPerTestCase[i] actual := s.findTracesByQuery(t, queryTestCase.Query, expected) - CompareSliceOfTraces(t, expected, actual) + v1adapter.CompareSliceOfTraces(t, expected, actual) }) } } diff --git a/storage_v2/v1adapter/ptrace2model.go b/storage_v2/v1adapter/ptrace2model.go index dd177a71dab..469b1162d8a 100644 --- a/storage_v2/v1adapter/ptrace2model.go +++ b/storage_v2/v1adapter/ptrace2model.go @@ -12,9 +12,10 @@ import ( otel2model "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" ) -// PTracesSeq2ToModel consumes an otel trace iterator and returns a model trace. +// PTracesSeq2ToModel consumes an iterator seqTrace. When necessary, +// it groups spans from *consecutive* chunks of ptrace.Traces into a single model.Trace // -// When necessary, it groups chunks of a trace into a single model trace +// Returns nil, and spanstore.ErrTraceNotFound for empty iterators func PTracesSeq2ToModel(seqTrace iter.Seq2[[]ptrace.Traces, error]) ([]*model.Trace, error) { var ( err error @@ -63,7 +64,9 @@ func modelSpansFromOtelTrace(otelTrace ptrace.Traces) []*model.Span { batches := otel2model.ProtoFromTraces(otelTrace) for _, batch := range batches { for _, span := range batch.Spans { - span.Process = batch.Process + span.Process = &model.Process{} + span.Process.ServiceName = batch.GetProcess().GetServiceName() + span.Process.Tags = batch.GetProcess().GetTags() spans = append(spans, span) } } diff --git a/storage_v2/v1adapter/ptrace2model_test.go b/storage_v2/v1adapter/ptrace2model_test.go new file mode 100644 index 00000000000..66d3f7fa515 --- /dev/null +++ b/storage_v2/v1adapter/ptrace2model_test.go @@ -0,0 +1,149 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package v1adapter + +import ( + "errors" + "testing" + "time" + + "go.opentelemetry.io/collector/pdata/ptrace" + + "github.com/crossdock/crossdock-go/require" + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/pkg/iter" + "github.com/jaegertracing/jaeger/storage/spanstore" +) + +func TestPTracesSeq2ToModel_SuccessWithSinglePTraceInSeq(t *testing.T) { + var ( + ProcessNoServiceName string = "OTLPResourceNoServiceName" + StartTime time.Time = time.Unix(0, 0) // 1970-01-01T00:00:00Z, matches the default for otel span's start time + ) + + testCases := []struct { + name string + expectedModelTraces []*model.Trace + seqTrace iter.Seq2[[]ptrace.Traces, error] + expectedErr error + }{ + { + name: "seq with one ptrace.Traces", + expectedModelTraces: []*model.Trace{ + { + Spans: []*model.Span{ + { + TraceID: model.NewTraceID(2, 3), + SpanID: model.NewSpanID(1), + OperationName: "op-success-a", + Process: model.NewProcess(ProcessNoServiceName, nil), + StartTime: StartTime, + }, + }, + }, + }, + seqTrace: func(yield func([]ptrace.Traces, error) bool) { + testTrace := ptrace.NewTraces() + rSpans := testTrace.ResourceSpans().AppendEmpty() + sSpans := rSpans.ScopeSpans().AppendEmpty() + spans := sSpans.Spans() + + // Add a new span and set attributes + modelTraceID := model.NewTraceID(2, 3) + span1 := spans.AppendEmpty() + span1.SetTraceID(modelTraceID.ToOTELTraceID()) + span1.SetName("op-success-a") + span1.SetSpanID(model.NewSpanID(1).ToOTELSpanID()) + + // Yield the test trace + yield([]ptrace.Traces{testTrace}, nil) + }, + expectedErr: nil, + }, + { + name: "seq with two chunks of ptrace.Traces", + expectedModelTraces: []*model.Trace{ + { + Spans: []*model.Span{ + { + TraceID: model.NewTraceID(2, 3), + SpanID: model.NewSpanID(1), + OperationName: "op-two-chunks-a", + Process: model.NewProcess(ProcessNoServiceName, nil), + StartTime: StartTime, + }, { + TraceID: model.NewTraceID(2, 3), + SpanID: model.NewSpanID(2), + OperationName: "op-two-chunks-b", + Process: model.NewProcess(ProcessNoServiceName, nil), + StartTime: StartTime, + }, + }, + }, + }, + seqTrace: func(yield func([]ptrace.Traces, error) bool) { + traceChunk1 := ptrace.NewTraces() + rSpans1 := traceChunk1.ResourceSpans().AppendEmpty() + sSpans1 := rSpans1.ScopeSpans().AppendEmpty() + spans1 := sSpans1.Spans() + modelTraceID := model.NewTraceID(2, 3) + span1 := spans1.AppendEmpty() + span1.SetTraceID(modelTraceID.ToOTELTraceID()) + span1.SetName("op-two-chunks-a") + span1.SetSpanID(model.NewSpanID(1).ToOTELSpanID()) + + traceChunk2 := ptrace.NewTraces() + rSpans2 := traceChunk2.ResourceSpans().AppendEmpty() + sSpans2 := rSpans2.ScopeSpans().AppendEmpty() + spans2 := sSpans2.Spans() + span2 := spans2.AppendEmpty() + span2.SetTraceID(modelTraceID.ToOTELTraceID()) + span2.SetName("op-two-chunks-b") + span2.SetSpanID(model.NewSpanID(2).ToOTELSpanID()) + // Yield the test trace + yield([]ptrace.Traces{traceChunk1, traceChunk2}, nil) + }, + expectedErr: nil, + }, + { + // a case that occurs when no trace is contained in the iterator + name: "empty seq for ptrace.Traces", + expectedModelTraces: nil, + seqTrace: func(yield func([]ptrace.Traces, error) bool) { + }, + expectedErr: spanstore.ErrTraceNotFound, + }, + { + name: "seq of one ptrace and one error", + expectedModelTraces: nil, + seqTrace: func(yield func([]ptrace.Traces, error) bool) { + testTrace := ptrace.NewTraces() + rSpans := testTrace.ResourceSpans().AppendEmpty() + sSpans := rSpans.ScopeSpans().AppendEmpty() + spans := sSpans.Spans() + + modelTraceID := model.NewTraceID(2, 3) + span1 := spans.AppendEmpty() + span1.SetTraceID(modelTraceID.ToOTELTraceID()) + span1.SetName("op-error-a") + span1.SetSpanID(model.NewSpanID(1).ToOTELSpanID()) + + // Yield the test trace + if !yield([]ptrace.Traces{testTrace}, nil) { + return + } + yield(nil, errors.New("unexpected-op-err")) + }, + expectedErr: errors.New("unexpected-op-err"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualTraces, err := PTracesSeq2ToModel(tc.seqTrace) + require.Equal(t, tc.expectedErr, err) + CompareSliceOfTraces(t, tc.expectedModelTraces, actualTraces) + }) + } +} diff --git a/plugin/storage/integration/trace_compare.go b/storage_v2/v1adapter/trace_compare.go similarity index 99% rename from plugin/storage/integration/trace_compare.go rename to storage_v2/v1adapter/trace_compare.go index 053ae8f5a50..0372ffeede7 100644 --- a/plugin/storage/integration/trace_compare.go +++ b/storage_v2/v1adapter/trace_compare.go @@ -2,7 +2,7 @@ // Copyright (c) 2017 Uber Technologies, Inc. // SPDX-License-Identifier: Apache-2.0 -package integration +package v1adapter import ( "encoding/json" diff --git a/plugin/storage/integration/trace_compare_test.go b/storage_v2/v1adapter/trace_compare_test.go similarity index 95% rename from plugin/storage/integration/trace_compare_test.go rename to storage_v2/v1adapter/trace_compare_test.go index a419d624ea8..01497adc2c3 100644 --- a/plugin/storage/integration/trace_compare_test.go +++ b/storage_v2/v1adapter/trace_compare_test.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 The Jaeger Authors. // SPDX-License-Identifier: Apache-2.0 -package integration +package v1adapter import ( "testing"