Skip to content

Commit 4f2b5d3

Browse files
authored
[test] Upgrade span_tags_trace.json from v1 model to ptrace.Traces (#8044)
## Which problem is this PR solving? - Fixes a part of: #7050 ## Description of the changes - This init the environment to swap fixtures with v2. One of the fixtures is also swapped here. ## How was this change tested? - Unit and Integration tests ## Checklist - [x] I have read https://github.com/jaegertracing/jaeger/blob/main/CONTRIBUTING_GUIDELINES.md - [x] I have signed all commits - [x] I have added unit tests for the new functionality - [x] I have run lint and test steps successfully: `make lint test` ## AI Usage in this PR (choose one) See [AI Usage Policy](https://github.com/jaegertracing/jaeger/blob/main/CONTRIBUTING_GUIDELINES.md#ai-usage-policy). - [x] **None**: No AI tools were used in creating this PR - [ ] **Light**: AI provided minor assistance (formatting, simple suggestions) - [ ] **Moderate**: AI helped with code generation or debugging specific parts - [ ] **Heavy**: AI generated most or all of the code changes --------- Signed-off-by: Manik Mehta <mehtamanik96@gmail.com>
1 parent 6f833d9 commit 4f2b5d3

File tree

8 files changed

+420
-47
lines changed

8 files changed

+420
-47
lines changed

cmd/trace-converter/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
trace-converter-*-*

cmd/trace-converter/main.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright (c) 2026 The Jaeger Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"bytes"
8+
"encoding/json"
9+
"fmt"
10+
"os"
11+
"path/filepath"
12+
13+
"github.com/gogo/protobuf/jsonpb"
14+
"github.com/spf13/cobra"
15+
"go.opentelemetry.io/collector/pdata/ptrace"
16+
17+
"github.com/jaegertracing/jaeger-idl/model/v1"
18+
"github.com/jaegertracing/jaeger/internal/storage/v2/v1adapter"
19+
)
20+
21+
const (
22+
inFile = "in-file"
23+
outFile = "out-file"
24+
rootPath = "root-path"
25+
fileFormat = "%s.json"
26+
)
27+
28+
func main() {
29+
if err := newRootCmd().Execute(); err != nil {
30+
os.Exit(1)
31+
}
32+
}
33+
34+
func newRootCmd() *cobra.Command {
35+
rootCmd := &cobra.Command{
36+
Use: "jaeger-trace-converter",
37+
Short: "Jaeger trace-converter converts v1 trace fixtures to OTLP trace fixtures",
38+
Long: "Jaeger trace-converter converts v1 trace fixtures to OTLP trace fixtures",
39+
}
40+
flags := rootCmd.Flags()
41+
flags.String(inFile, "", "The name of file to read from (where v1 fixtures are located without .json)")
42+
flags.String(outFile, "", "The name of file where otlp fixtures are needed to be written (without .json). If given empty, fixtures will be written to input file")
43+
flags.String(rootPath, "", "The root path to use to convert the traces to OTLP fixtures")
44+
if err := rootCmd.MarkFlagRequired(inFile); err != nil {
45+
panic(err)
46+
}
47+
if err := rootCmd.MarkFlagRequired(rootPath); err != nil {
48+
panic(err)
49+
}
50+
rootCmd.RunE = func(cmd *cobra.Command, _ []string) error {
51+
in, err := cmd.Flags().GetString(inFile)
52+
if err != nil {
53+
return err
54+
}
55+
out, err := cmd.Flags().GetString(outFile)
56+
if err != nil {
57+
return err
58+
}
59+
root, err := cmd.Flags().GetString(rootPath)
60+
if err != nil {
61+
return err
62+
}
63+
if out == "" {
64+
out = in
65+
}
66+
return translateFixtureToOTLPTrace(root, in, out)
67+
}
68+
return rootCmd
69+
}
70+
71+
func translateFixtureToOTLPTrace(root, in, out string) error {
72+
inPath := filepath.Join(root, fmt.Sprintf(fileFormat, in))
73+
outPath := filepath.Join(root, fmt.Sprintf(fileFormat, out))
74+
inStr, err := os.ReadFile(inPath)
75+
if err != nil {
76+
return err
77+
}
78+
var traceV1 model.Trace
79+
if err := jsonpb.Unmarshal(bytes.NewReader(inStr), &traceV1); err != nil {
80+
return err
81+
}
82+
trace := v1adapter.V1TraceToOtelTrace(&traceV1)
83+
expected := modelTraceFromOtelTrace(trace)
84+
if err := validateTraces(expected, &traceV1); err != nil {
85+
return err
86+
}
87+
marshaller := ptrace.JSONMarshaler{}
88+
outBytes, err := marshaller.MarshalTraces(trace)
89+
if err != nil {
90+
return err
91+
}
92+
var buf bytes.Buffer
93+
if err := json.Indent(&buf, outBytes, "", " "); err != nil {
94+
return err
95+
}
96+
return os.WriteFile(outPath, buf.Bytes(), 0o600)
97+
}
98+
99+
// modelTraceFromOtelTrace extracts spans from otel traces
100+
func modelTraceFromOtelTrace(otelTrace ptrace.Traces) *model.Trace {
101+
var spans []*model.Span
102+
batches := v1adapter.V1BatchesFromTraces(otelTrace)
103+
for _, batch := range batches {
104+
for _, span := range batch.Spans {
105+
if span.Process == nil {
106+
proc := *batch.Process // shallow clone
107+
span.Process = &proc
108+
}
109+
spans = append(spans, span)
110+
111+
if span.Process.Tags == nil {
112+
span.Process.Tags = []model.KeyValue{}
113+
}
114+
115+
if span.References == nil {
116+
span.References = []model.SpanRef{}
117+
}
118+
if span.Tags == nil {
119+
span.Tags = []model.KeyValue{}
120+
}
121+
if span.Logs == nil {
122+
span.Logs = []model.Log{}
123+
}
124+
}
125+
}
126+
return &model.Trace{Spans: spans}
127+
}
128+
129+
func validateTraces(expected, actual *model.Trace) error {
130+
expectedBytes, err := json.Marshal(expected)
131+
if err != nil {
132+
return err
133+
}
134+
actualBytes, err := json.Marshal(actual)
135+
if err != nil {
136+
return err
137+
}
138+
if !bytes.Equal(expectedBytes, actualBytes) {
139+
return fmt.Errorf("traces are not equal\n actual: %s\n expected: %s", string(actualBytes), string(expectedBytes))
140+
}
141+
return nil
142+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2026 The Jaeger Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"bytes"
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
"time"
13+
14+
"github.com/gogo/protobuf/jsonpb"
15+
"github.com/stretchr/testify/require"
16+
"go.opentelemetry.io/collector/pdata/ptrace"
17+
18+
"github.com/jaegertracing/jaeger-idl/model/v1"
19+
"github.com/jaegertracing/jaeger/internal/storage/integration"
20+
"github.com/jaegertracing/jaeger/internal/storage/v2/v1adapter"
21+
"github.com/jaegertracing/jaeger/internal/testutils"
22+
)
23+
24+
func TestMain(m *testing.M) {
25+
testutils.VerifyGoLeaks(m)
26+
}
27+
28+
func Test_translateFixtureToOTLPTrace(t *testing.T) {
29+
dir := t.TempDir()
30+
tmpInFile := "v1-trace"
31+
tmpInPath := filepath.Join(dir, fmt.Sprintf(fileFormat, tmpInFile))
32+
v1Trace := model.Trace{Spans: []*model.Span{
33+
{
34+
OperationName: "jaeger",
35+
References: []model.SpanRef{},
36+
Process: &model.Process{
37+
Tags: model.KeyValues{},
38+
ServiceName: "jaeger-service",
39+
},
40+
Tags: model.KeyValues{},
41+
StartTime: time.Now(),
42+
Logs: []model.Log{},
43+
},
44+
}}
45+
var buf bytes.Buffer
46+
marshaler := jsonpb.Marshaler{}
47+
require.NoError(t, marshaler.Marshal(&buf, &v1Trace))
48+
require.NoError(t, os.WriteFile(tmpInPath, buf.Bytes(), 0o600))
49+
tmpOutFile := "v2-trace"
50+
tmpOutPath := filepath.Join(dir, fmt.Sprintf(fileFormat, tmpOutFile))
51+
rootCmd := newRootCmd()
52+
rootCmd.SetArgs([]string{
53+
"--in-file", tmpInFile,
54+
"--out-file", tmpOutFile,
55+
"--root-path", dir,
56+
})
57+
require.NoError(t, rootCmd.Execute())
58+
outBytes, err := os.ReadFile(tmpOutPath)
59+
require.NoError(t, err)
60+
unmarshaller := ptrace.JSONUnmarshaler{}
61+
actual, err := unmarshaller.UnmarshalTraces(outBytes)
62+
require.NoError(t, err)
63+
expected := v1adapter.V1TraceToOtelTrace(&v1Trace)
64+
integration.CompareTraces(t, expected, actual)
65+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2026 The Jaeger Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package integration
5+
6+
import (
7+
"time"
8+
9+
"go.opentelemetry.io/collector/pdata/pcommon"
10+
"go.opentelemetry.io/collector/pdata/ptrace"
11+
12+
"github.com/jaegertracing/jaeger/internal/jptrace"
13+
)
14+
15+
// dateOffsetNormalizer normalizes timestamps by replacing their date
16+
// (year, month, day) with a fixed date computed using a day offset
17+
// from the current UTC date, while preserving the original time.
18+
// This is required in integration tests because the fixtures have
19+
// hardcoded start time and other timestamps and we need to make them
20+
// recent to fetch from the reader. Timestamps whose original UTC date
21+
// is 2017-01-25 use a -2 day offset; all other timestamps use a -1 day
22+
// offset. These offsets are selected to keep the upgrade from v1 to v2 consistent.
23+
type dateOffsetNormalizer struct {
24+
tm time.Time
25+
}
26+
27+
func newDateOffsetNormalizer(tm time.Time) dateOffsetNormalizer {
28+
return dateOffsetNormalizer{tm: tm}
29+
}
30+
31+
func (d dateOffsetNormalizer) normalizeTrace(td ptrace.Traces) {
32+
for _, span := range jptrace.SpanIter(td) {
33+
span.SetStartTimestamp(d.normalizeTime(span.StartTimestamp()))
34+
span.SetEndTimestamp(d.normalizeTime(span.EndTimestamp()))
35+
for _, event := range span.Events().All() {
36+
event.SetTimestamp(d.normalizeTime(event.Timestamp()))
37+
}
38+
}
39+
}
40+
41+
func (d dateOffsetNormalizer) normalizeTime(t pcommon.Timestamp) pcommon.Timestamp {
42+
tm := t.AsTime().UTC()
43+
offset := -1
44+
// Apply a -2 day offset for any timestamp whose UTC date is 2017-01-25.
45+
// All other timestamps use the default -1 day offset to preserve existing behavior.
46+
yearOrig, monthOrig, dayOrig := tm.Date()
47+
if yearOrig == 2017 && monthOrig == time.January && dayOrig == 25 {
48+
offset = -2
49+
}
50+
year, month, day := d.tm.UTC().AddDate(0, 0, offset).Date()
51+
newTm := time.Date(
52+
year,
53+
month,
54+
day,
55+
tm.Hour(),
56+
tm.Minute(),
57+
tm.Second(),
58+
tm.Nanosecond(),
59+
tm.Location(),
60+
)
61+
return pcommon.NewTimestampFromTime(newTm)
62+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2026 The Jaeger Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package integration
5+
6+
import (
7+
"testing"
8+
"time"
9+
10+
"github.com/stretchr/testify/assert"
11+
"go.opentelemetry.io/collector/pdata/pcommon"
12+
"go.opentelemetry.io/collector/pdata/ptrace"
13+
)
14+
15+
func Test_dateOffsetNormalizer(t *testing.T) {
16+
origTime := time.Date(
17+
2024, time.January, 10,
18+
14, 30, 45, 123456789,
19+
time.UTC,
20+
)
21+
origStartTime := time.Date(
22+
2017, time.January, 25,
23+
23, 56, 31, 639875000,
24+
time.UTC,
25+
)
26+
origTs := pcommon.NewTimestampFromTime(origTime)
27+
origStartTs := pcommon.NewTimestampFromTime(origStartTime)
28+
twoDaysAgo := -2
29+
oneDayAgo := -1
30+
now := time.Now()
31+
expectedTwoDaysAgo := now.UTC().AddDate(0, 0, twoDaysAgo)
32+
expectedOneDayAgo := now.UTC().AddDate(0, 0, oneDayAgo)
33+
normalizer := newDateOffsetNormalizer(now)
34+
td := ptrace.NewTraces()
35+
span := td.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty()
36+
span.SetStartTimestamp(origStartTs)
37+
span.SetEndTimestamp(origTs)
38+
event := span.Events().AppendEmpty()
39+
event.SetTimestamp(origTs)
40+
normalizer.normalizeTrace(td)
41+
expectedTwoDaysAgoTime := time.Date(
42+
expectedTwoDaysAgo.Year(),
43+
expectedTwoDaysAgo.Month(),
44+
expectedTwoDaysAgo.Day(),
45+
origStartTime.Hour(),
46+
origStartTime.Minute(),
47+
origStartTime.Second(),
48+
origStartTime.Nanosecond(),
49+
time.UTC,
50+
)
51+
expectedOneDayAgoTime := time.Date(
52+
expectedOneDayAgo.Year(),
53+
expectedOneDayAgo.Month(),
54+
expectedOneDayAgo.Day(),
55+
origTime.Hour(),
56+
origTime.Minute(),
57+
origTime.Second(),
58+
origTime.Nanosecond(),
59+
time.UTC,
60+
)
61+
assert.Equal(t, expectedTwoDaysAgoTime, span.StartTimestamp().AsTime())
62+
assert.Equal(t, expectedOneDayAgoTime, span.EndTimestamp().AsTime())
63+
assert.Equal(t, expectedOneDayAgoTime, event.Timestamp().AsTime())
64+
}

0 commit comments

Comments
 (0)