Skip to content

Commit 900bcca

Browse files
author
krsnaSuraj
committed
Address PR review: AppendAction, OTel semconv, trace injection tests
- extproc_out.go: Set AppendAction=OVERWRITE_IF_EXISTS_OR_ADD on injected trace headers - extproc.go: Replace ate.target_addr with stable OTel semconv server.address + server.port - extproc_test.go: Add inMemoryExporter + tests for traceparent matching parent span trace ID and AppendAction default - resumer_test.go: Add resumerSpanExporter + test for ResumeActor child span sharing parent trace ID and span ID
1 parent 4c6bd93 commit 900bcca

4 files changed

Lines changed: 190 additions & 1 deletion

File tree

cmd/atenet/internal/router/extproc.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"go.opentelemetry.io/otel/attribute"
3131
"go.opentelemetry.io/otel/metric"
3232
"go.opentelemetry.io/otel/propagation"
33+
"go.opentelemetry.io/otel/semconv/v1.40.0"
3334
"google.golang.org/grpc"
3435

3536
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
@@ -181,7 +182,8 @@ func (s *ExtProcServer) handleRequestHeaders(
181182
slog.InfoContext(ctx, "Route ok", slog.String("actor", actorName), slog.String("targetAddr", targetAddr))
182183

183184
span.SetAttributes(
184-
attribute.String("ate.target_addr", targetAddr),
185+
semconv.ServerAddress(workerIP),
186+
semconv.ServerPort(80),
185187
attribute.String("ate.actor.template.namespace", tmplNs),
186188
attribute.String("ate.actor.template.name", tmplName),
187189
)

cmd/atenet/internal/router/extproc_out.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ func injectTraceContext(ctx context.Context, mutation *extproc.HeaderMutation) {
5959
Key: k,
6060
Value: v,
6161
},
62+
AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD,
6263
})
6364
}
6465
}

cmd/atenet/internal/router/extproc_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import (
2121
"errors"
2222
"log/slog"
2323
"strings"
24+
"sync"
2425
"testing"
2526
"time"
2627

2728
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
2829
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
2930
extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
3031
envoy_type "github.com/envoyproxy/go-control-plane/envoy/type/v3"
32+
"go.opentelemetry.io/otel"
33+
"go.opentelemetry.io/otel/propagation"
34+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
3135
"google.golang.org/grpc"
3236
"google.golang.org/grpc/codes"
3337
"google.golang.org/grpc/status"
@@ -254,3 +258,100 @@ func TestExtProcHeadersEvaluation(t *testing.T) {
254258
})
255259
}
256260
}
261+
262+
// inMemoryExporter stores ended spans for test inspection.
263+
type inMemoryExporter struct {
264+
mu sync.Mutex
265+
spans []sdktrace.ReadOnlySpan
266+
}
267+
268+
func (e *inMemoryExporter) ExportSpans(_ context.Context, spans []sdktrace.ReadOnlySpan) error {
269+
e.mu.Lock()
270+
defer e.mu.Unlock()
271+
e.spans = append(e.spans, spans...)
272+
return nil
273+
}
274+
275+
func (e *inMemoryExporter) Shutdown(context.Context) error {
276+
e.mu.Lock()
277+
defer e.mu.Unlock()
278+
e.spans = nil
279+
return nil
280+
}
281+
282+
func (e *inMemoryExporter) Ended() []sdktrace.ReadOnlySpan {
283+
e.mu.Lock()
284+
defer e.mu.Unlock()
285+
ret := make([]sdktrace.ReadOnlySpan, len(e.spans))
286+
copy(ret, e.spans)
287+
return ret
288+
}
289+
290+
func TestInjectTraceContext_AddsTraceparentMatchingParentSpan(t *testing.T) {
291+
prevProp := otel.GetTextMapPropagator()
292+
otel.SetTextMapPropagator(propagation.TraceContext{})
293+
t.Cleanup(func() { otel.SetTextMapPropagator(prevProp) })
294+
295+
exporter := &inMemoryExporter{}
296+
tp := sdktrace.NewTracerProvider(
297+
sdktrace.WithSampler(sdktrace.AlwaysSample()),
298+
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(exporter)),
299+
)
300+
301+
ctx, span := tp.Tracer("test").Start(context.Background(), "parent")
302+
parentTraceID := span.SpanContext().TraceID()
303+
304+
mutation := &extprocv3.HeaderMutation{}
305+
injectTraceContext(ctx, mutation)
306+
span.End()
307+
308+
var traceparent string
309+
for _, h := range mutation.GetSetHeaders() {
310+
if strings.ToLower(h.Header.Key) == "traceparent" {
311+
traceparent = h.Header.Value
312+
if h.GetAppendAction() != corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD {
313+
t.Errorf("AppendAction = %v, want OVERWRITE_IF_EXISTS_OR_ADD", h.GetAppendAction())
314+
}
315+
break
316+
}
317+
}
318+
if traceparent == "" {
319+
t.Fatal("traceparent not found in header mutation")
320+
}
321+
322+
parts := strings.Split(traceparent, "-")
323+
if len(parts) < 2 {
324+
t.Fatalf("invalid traceparent format: %s", traceparent)
325+
}
326+
if parts[1] != parentTraceID.String() {
327+
t.Errorf("trace ID in traceparent = %s, want parent trace ID = %s", parts[1], parentTraceID.String())
328+
}
329+
}
330+
331+
func TestInjectTraceContext_AppendActionDefaultsToOverwrite(t *testing.T) {
332+
prevProp := otel.GetTextMapPropagator()
333+
otel.SetTextMapPropagator(propagation.TraceContext{})
334+
t.Cleanup(func() { otel.SetTextMapPropagator(prevProp) })
335+
336+
exporter := &inMemoryExporter{}
337+
tp := sdktrace.NewTracerProvider(
338+
sdktrace.WithSampler(sdktrace.AlwaysSample()),
339+
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(exporter)),
340+
)
341+
342+
ctx, span := tp.Tracer("test").Start(context.Background(), "parent")
343+
defer span.End()
344+
345+
mutation := &extprocv3.HeaderMutation{}
346+
injectTraceContext(ctx, mutation)
347+
348+
for _, h := range mutation.GetSetHeaders() {
349+
if strings.ToLower(h.Header.Key) == "traceparent" {
350+
if h.GetAppendAction() != corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD {
351+
t.Errorf("AppendAction = %v, want OVERWRITE_IF_EXISTS_OR_ADD", h.GetAppendAction())
352+
}
353+
return
354+
}
355+
}
356+
t.Error("traceparent not found in mutation")
357+
}

cmd/atenet/internal/router/resumer_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"time"
2222

2323
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
24+
"go.opentelemetry.io/otel"
25+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
2426
"google.golang.org/grpc"
2527
"google.golang.org/grpc/codes"
2628
"google.golang.org/grpc/status"
@@ -168,3 +170,86 @@ func TestActorResumer_ResumeActor(t *testing.T) {
168170
}
169171
})
170172
}
173+
174+
type resumerSpanExporter struct {
175+
mu sync.Mutex
176+
spans []sdktrace.ReadOnlySpan
177+
}
178+
179+
func (e *resumerSpanExporter) ExportSpans(_ context.Context, spans []sdktrace.ReadOnlySpan) error {
180+
e.mu.Lock()
181+
defer e.mu.Unlock()
182+
e.spans = append(e.spans, spans...)
183+
return nil
184+
}
185+
186+
func (e *resumerSpanExporter) Shutdown(context.Context) error {
187+
e.mu.Lock()
188+
defer e.mu.Unlock()
189+
e.spans = nil
190+
return nil
191+
}
192+
193+
func (e *resumerSpanExporter) Ended() []sdktrace.ReadOnlySpan {
194+
e.mu.Lock()
195+
defer e.mu.Unlock()
196+
ret := make([]sdktrace.ReadOnlySpan, len(e.spans))
197+
copy(ret, e.spans)
198+
return ret
199+
}
200+
201+
func TestActorResumer_ResumeChildSharesParentTraceID(t *testing.T) {
202+
const testActorName = "actor-a"
203+
const testAtespace = "team-a"
204+
const expectedIP = "10.0.0.52"
205+
206+
exporter := &resumerSpanExporter{}
207+
tp := sdktrace.NewTracerProvider(
208+
sdktrace.WithSampler(sdktrace.AlwaysSample()),
209+
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(exporter)),
210+
)
211+
prevTP := otel.GetTracerProvider()
212+
otel.SetTracerProvider(tp)
213+
t.Cleanup(func() { otel.SetTracerProvider(prevTP) })
214+
215+
ctx, parent := tp.Tracer("test").Start(context.Background(), "parent")
216+
parentTraceID := parent.SpanContext().TraceID()
217+
parentSpanID := parent.SpanContext().SpanID()
218+
219+
mock := &resumerMockClient{
220+
resumeFn: func(ctx context.Context, in *ateapipb.ResumeActorRequest, opts ...grpc.CallOption) (*ateapipb.ResumeActorResponse, error) {
221+
return &ateapipb.ResumeActorResponse{
222+
Actor: &ateapipb.Actor{
223+
Metadata: &ateapipb.ResourceMetadata{Name: testActorName},
224+
Status: ateapipb.Actor_STATUS_RUNNING,
225+
AteomPodIp: expectedIP,
226+
},
227+
}, nil
228+
},
229+
}
230+
231+
resumer := NewActorResumer(mock)
232+
_, err := resumer.ResumeActor(ctx, testAtespace, testActorName)
233+
if err != nil {
234+
t.Fatalf("unexpected error: %v", err)
235+
}
236+
parent.End()
237+
238+
var resumeSpan sdktrace.ReadOnlySpan
239+
for _, s := range exporter.Ended() {
240+
if s.Name() == "ResumeActor" {
241+
resumeSpan = s
242+
break
243+
}
244+
}
245+
if resumeSpan == nil {
246+
t.Fatal("ResumeActor span not found in exporter")
247+
}
248+
249+
if got, want := resumeSpan.SpanContext().TraceID(), parentTraceID; got != want {
250+
t.Errorf("ResumeActor trace ID = %s, want parent trace ID = %s", got, want)
251+
}
252+
if got, want := resumeSpan.Parent().SpanID(), parentSpanID; got != want {
253+
t.Errorf("ResumeActor parent span ID = %s, want %s", got, want)
254+
}
255+
}

0 commit comments

Comments
 (0)