diff --git a/test/conformance/broker_tracing_test.go b/test/conformance/broker_tracing_test.go index ef9acc37774..c5b02190fd8 100644 --- a/test/conformance/broker_tracing_test.go +++ b/test/conformance/broker_tracing_test.go @@ -28,6 +28,6 @@ import ( ) func TestBrokerTracing(t *testing.T) { - t.Skip("needs to be reworked for OTel (eventing#8637)") + t.Skip("OTel type migration done (eventing#8853), but needs cluster-side OTel collector infrastructure to run") helpers.BrokerTracingTestHelperWithChannelTestRunner(context.Background(), t, brokerClass, channelTestRunner, testlib.SetupClientOptionNoop) } diff --git a/test/conformance/channel_tracing_test.go b/test/conformance/channel_tracing_test.go index 7cc409cbf0b..ffdcdc42264 100644 --- a/test/conformance/channel_tracing_test.go +++ b/test/conformance/channel_tracing_test.go @@ -28,6 +28,6 @@ import ( ) func TestChannelTracingWithReply(t *testing.T) { - t.Skip("needs to be reworked for OTel (eventing#8637)") + t.Skip("OTel type migration done (eventing#8853), but needs cluster-side OTel collector infrastructure to run") helpers.ChannelTracingTestHelperWithChannelTestRunner(context.Background(), t, channelTestRunner, testlib.SetupClientOptionNoop) } diff --git a/test/conformance/helpers/broker_tracing_test_helper.go b/test/conformance/helpers/broker_tracing_test_helper.go index ddaca8e48fc..d0d1e97654e 100644 --- a/test/conformance/helpers/broker_tracing_test_helper.go +++ b/test/conformance/helpers/broker_tracing_test_helper.go @@ -24,7 +24,7 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" cetest "github.com/cloudevents/sdk-go/v2/test" - "github.com/openzipkin/zipkin-go/model" + oteltrace "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "knative.dev/eventing/pkg/apis/eventing/v1" @@ -151,7 +151,7 @@ func setupBrokerTracing(_ context.Context, brokerClass string) SetupTracingTestI { Note: "4. Transformer pod receives the event from the Broker Filter for the 'transformer' trigger.", Span: tracinghelper.MatchHTTPSpanWithReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath(transformerSVCHost, "/"), tracinghelper.WithLocalEndpointServiceName(eventTransformerPod.Name), ), @@ -170,7 +170,7 @@ func setupBrokerTracing(_ context.Context, brokerClass string) SetupTracingTestI { Note: "7. Logger pod receives the event from the Broker Filter for the 'logger' trigger.", Span: tracinghelper.MatchHTTPSpanNoReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath(loggerSVCHost, "/"), tracinghelper.WithLocalEndpointServiceName(loggerPodName), ), @@ -198,7 +198,7 @@ func setupBrokerTracing(_ context.Context, brokerClass string) SetupTracingTestI expected = tracinghelper.TestSpanTree{ Note: "1. Send pod sends event to the Broker Ingress (only if the sending pod generates a span).", Span: tracinghelper.MatchHTTPSpanNoReply( - model.Client, + oteltrace.SpanKindClient, tracinghelper.WithLocalEndpointServiceName(senderName), ), Children: []tracinghelper.TestSpanTree{expected}, diff --git a/test/conformance/helpers/channel_tracing_test_helper.go b/test/conformance/helpers/channel_tracing_test_helper.go index d65ac7893b7..e1b80177854 100644 --- a/test/conformance/helpers/channel_tracing_test_helper.go +++ b/test/conformance/helpers/channel_tracing_test_helper.go @@ -25,7 +25,7 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" cetest "github.com/cloudevents/sdk-go/v2/test" "github.com/google/uuid" - "github.com/openzipkin/zipkin-go/model" + oteltrace "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ch "knative.dev/eventing/pkg/channel" @@ -142,7 +142,7 @@ func setupChannelTracingWithReply( // 1 is added below if it is needed. // 2. Channel receives event from sending pod. Span: tracinghelper.MatchHTTPSpanNoReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath( fmt.Sprintf("%s%s.%s.svc", channelName, ch.K8ServiceNameSuffix, client.Namespace), "/", @@ -156,7 +156,7 @@ func setupChannelTracingWithReply( { // 4. Channel sends event to Mutator pod. Span: tracinghelper.MatchHTTPSpanWithReply( - model.Client, + oteltrace.SpanKindClient, tracinghelper.WithHTTPURL( fmt.Sprintf("%s.%s.svc", mutatingPod.Name, client.Namespace), "", @@ -166,7 +166,7 @@ func setupChannelTracingWithReply( { // 5. Mutator Pod receives event from Channel. Span: tracinghelper.MatchHTTPSpanWithReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath( fmt.Sprintf("%s.%s.svc", mutatingPod.Name, client.Namespace), "/", @@ -183,7 +183,7 @@ func setupChannelTracingWithReply( { // 7. Channel sends reply from Mutator Pod to the reply Channel. Span: tracinghelper.MatchHTTPSpanNoReply( - model.Client, + oteltrace.SpanKindClient, tracinghelper.WithHTTPURL( fmt.Sprintf("%s%s.%s.svc", replyChannelName, ch.K8ServiceNameSuffix, client.Namespace), "", @@ -193,7 +193,7 @@ func setupChannelTracingWithReply( { // 8. Reply Channel receives event from the original Channel's reply. Span: tracinghelper.MatchHTTPSpanNoReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath( fmt.Sprintf("%s%s.%s.svc", replyChannelName, ch.K8ServiceNameSuffix, client.Namespace), "/", @@ -207,7 +207,7 @@ func setupChannelTracingWithReply( { // 10. Reply Channel sends event to the logging Pod. Span: tracinghelper.MatchHTTPSpanNoReply( - model.Client, + oteltrace.SpanKindClient, tracinghelper.WithHTTPURL( fmt.Sprintf("%s.%s.svc", recordEventsPod.Name, client.Namespace), "", @@ -217,7 +217,7 @@ func setupChannelTracingWithReply( { // 11. Logging pod receives event from Channel. Span: tracinghelper.MatchHTTPSpanNoReply( - model.Server, + oteltrace.SpanKindServer, tracinghelper.WithHTTPHostAndPath( fmt.Sprintf("%s.%s.svc", recordEventsPod.Name, client.Namespace), "/", @@ -244,7 +244,7 @@ func setupChannelTracingWithReply( expected = tracinghelper.TestSpanTree{ // 1. Sending pod sends event to Channel (only if the sending pod generates a span). Span: tracinghelper.MatchHTTPSpanNoReply( - model.Client, + oteltrace.SpanKindClient, tracinghelper.WithHTTPURL( fmt.Sprintf("%s%s.%s.svc", channelName, ch.K8ServiceNameSuffix, client.Namespace), "", @@ -263,7 +263,7 @@ func setupChannelTracingWithReply( } func channelSpan(eventID, host, path string) *tracinghelper.SpanMatcher { - k := model.Client + k := oteltrace.SpanKindClient return &tracinghelper.SpanMatcher{ Kind: &k, Tags: map[string]*regexp.Regexp{ diff --git a/test/conformance/helpers/tracing/traces.go b/test/conformance/helpers/tracing/traces.go index c3f989ce445..e9518de59ec 100644 --- a/test/conformance/helpers/tracing/traces.go +++ b/test/conformance/helpers/tracing/traces.go @@ -25,7 +25,7 @@ import ( "strconv" "testing" - "github.com/openzipkin/zipkin-go/model" + oteltrace "go.opentelemetry.io/otel/trace" ) // hostSuffix is an optional suffix that might appear at the end of hostnames. @@ -43,8 +43,20 @@ import ( // and then match anything prior to the path starting, e.g. '/' const HostSuffix = "[.][^/]+" +// SpanData is a lightweight representation of an OTel span containing just the +// fields needed for test assertions. This avoids depending on the full +// ReadOnlySpan interface which is cumbersome to construct in tests. +type SpanData struct { + SpanContext oteltrace.SpanContext + Parent oteltrace.SpanContext + Name string + Kind oteltrace.SpanKind + ServiceName string + Attributes map[string]string +} + // PrettyPrintTrace pretty prints a Trace. -func PrettyPrintTrace(trace []model.SpanModel) string { +func PrettyPrintTrace(trace []SpanData) string { b, _ := json.Marshal(trace) return string(b) } @@ -52,7 +64,7 @@ func PrettyPrintTrace(trace []model.SpanModel) string { // SpanTree is the tree of Spans representation of a Trace. type SpanTree struct { Root bool - Span model.SpanModel + Span SpanData Children []SpanTree } @@ -62,16 +74,16 @@ func (t SpanTree) String() string { } type SpanMatcher struct { - Kind *model.Kind `json:"a_Kind,omitempty"` - LocalEndpointServiceName string `json:"b_Name,omitempty"` - Tags map[string]*regexp.Regexp `json:"c_Tags,omitempty"` + Kind *oteltrace.SpanKind `json:"a_Kind,omitempty"` + ServiceName string `json:"b_Name,omitempty"` + Tags map[string]*regexp.Regexp `json:"c_Tags,omitempty"` } type SpanMatcherOption func(*SpanMatcher) func WithLocalEndpointServiceName(s string) SpanMatcherOption { return func(m *SpanMatcher) { - m.LocalEndpointServiceName = s + m.ServiceName = s } } @@ -88,7 +100,7 @@ func WithHTTPURL(host, path string) SpanMatcherOption { } } -func (m *SpanMatcher) MatchesSpan(span *model.SpanModel) error { +func (m *SpanMatcher) MatchesSpan(span *SpanData) error { if m == nil { return nil } @@ -97,23 +109,23 @@ func (m *SpanMatcher) MatchesSpan(span *model.SpanModel) error { return fmt.Errorf("mismatched kind: got %q, want %q", span.Kind, *m.Kind) } } - if m.LocalEndpointServiceName != "" { - if span.LocalEndpoint == nil { - return errors.New("missing local endpoint") + if m.ServiceName != "" { + if span.ServiceName == "" { + return errors.New("missing service name") } - if m.LocalEndpointServiceName != span.LocalEndpoint.ServiceName { - return fmt.Errorf("mismatched LocalEndpoint ServiceName: got %q, want %q", span.LocalEndpoint.ServiceName, m.LocalEndpointServiceName) + if m.ServiceName != span.ServiceName { + return fmt.Errorf("mismatched ServiceName: got %q, want %q", span.ServiceName, m.ServiceName) } } for k, v := range m.Tags { - if t := span.Tags[k]; !v.MatchString(t) { + if t := span.Attributes[k]; !v.MatchString(t) { return fmt.Errorf("unexpected tag %s: got %q, want %q", k, t, v) } } return nil } -func MatchSpan(kind model.Kind, opts ...SpanMatcherOption) *SpanMatcher { +func MatchSpan(kind oteltrace.SpanKind, opts ...SpanMatcherOption) *SpanMatcher { m := &SpanMatcher{ Kind: &kind, } @@ -123,7 +135,7 @@ func MatchSpan(kind model.Kind, opts ...SpanMatcherOption) *SpanMatcher { return m } -func MatchHTTPSpanWithCode(kind model.Kind, statusCode int, opts ...SpanMatcherOption) *SpanMatcher { +func MatchHTTPSpanWithCode(kind oteltrace.SpanKind, statusCode int, opts ...SpanMatcherOption) *SpanMatcher { return MatchSpan(kind, WithCode(statusCode)) } @@ -136,11 +148,11 @@ func WithCode(statusCode int) SpanMatcherOption { } } -func MatchHTTPSpanNoReply(kind model.Kind, opts ...SpanMatcherOption) *SpanMatcher { +func MatchHTTPSpanNoReply(kind oteltrace.SpanKind, opts ...SpanMatcherOption) *SpanMatcher { return MatchHTTPSpanWithCode(kind, 202, opts...) } -func MatchHTTPSpanWithReply(kind model.Kind, opts ...SpanMatcherOption) *SpanMatcher { +func MatchHTTPSpanWithReply(kind oteltrace.SpanKind, opts ...SpanMatcherOption) *SpanMatcher { return MatchHTTPSpanWithCode(kind, 200, opts...) } @@ -160,13 +172,14 @@ func (tt TestSpanTree) String() string { return string(b) } -// GetTraceTree converts a set slice of spans into a SpanTree. -func GetTraceTree(trace []model.SpanModel) (*SpanTree, error) { - var roots []model.SpanModel - parents := map[model.ID][]model.SpanModel{} +// GetTraceTree converts a slice of SpanData into a SpanTree. +func GetTraceTree(trace []SpanData) (*SpanTree, error) { + var roots []SpanData + parents := map[oteltrace.SpanID][]SpanData{} for _, span := range trace { - if span.ParentID != nil { - parents[*span.ParentID] = append(parents[*span.ParentID], span) + if span.Parent.IsValid() && span.Parent.HasSpanID() { + parentID := span.Parent.SpanID() + parents[parentID] = append(parents[parentID], span) } else { roots = append(roots, span) } @@ -191,10 +204,11 @@ func GetTraceTree(trace []model.SpanModel) (*SpanTree, error) { return &tree, nil } -func getChildren(parents map[model.ID][]model.SpanModel, current []model.SpanModel) ([]SpanTree, error) { +func getChildren(parents map[oteltrace.SpanID][]SpanData, current []SpanData) ([]SpanTree, error) { children := make([]SpanTree, 0, len(current)) for _, span := range current { - grandchildren, err := getChildren(parents, parents[span.ID]) + spanID := span.SpanContext.SpanID() + grandchildren, err := getChildren(parents, parents[spanID]) if err != nil { return children, err } @@ -202,7 +216,7 @@ func getChildren(parents map[model.ID][]model.SpanModel, current []model.SpanMod Span: span, Children: grandchildren, }) - delete(parents, span.ID) + delete(parents, spanID) } return children, nil diff --git a/test/conformance/helpers/tracing/traces_test.go b/test/conformance/helpers/tracing/traces_test.go index 509a5d84cf6..5bfcb77b723 100644 --- a/test/conformance/helpers/tracing/traces_test.go +++ b/test/conformance/helpers/tracing/traces_test.go @@ -20,16 +20,16 @@ import ( "regexp" "testing" - "github.com/openzipkin/zipkin-go/model" + oteltrace "go.opentelemetry.io/otel/trace" ) var ( - serverKind = model.Server - clientKind = model.Client + serverKind = oteltrace.SpanKindServer + clientKind = oteltrace.SpanKindClient ) type SpanCase struct { - Span *model.SpanModel + Span *SpanData ShouldMatch bool } @@ -43,15 +43,13 @@ func TestSpanMatcher(t *testing.T) { Name: "empty matcher", Matcher: &SpanMatcher{}, Spans: []SpanCase{{ - Span: &model.SpanModel{}, + Span: &SpanData{}, ShouldMatch: true, }, { - Span: &model.SpanModel{ - Kind: model.Server, - LocalEndpoint: &model.Endpoint{ - ServiceName: "test-service-name", - }, - Tags: map[string]string{ + Span: &SpanData{ + Kind: oteltrace.SpanKindServer, + ServiceName: "test-service-name", + Attributes: map[string]string{ "test-tag": "test-tag-value", "other-tag": "other-tag-value", }, @@ -64,25 +62,23 @@ func TestSpanMatcher(t *testing.T) { Kind: &serverKind, }, Spans: []SpanCase{{ - Span: &model.SpanModel{}, + Span: &SpanData{}, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Kind: model.Server, + Span: &SpanData{ + Kind: oteltrace.SpanKindServer, }, ShouldMatch: true, }, { - Span: &model.SpanModel{ - Kind: model.Client, + Span: &SpanData{ + Kind: oteltrace.SpanKindClient, }, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Kind: model.Server, - LocalEndpoint: &model.Endpoint{ - ServiceName: "test-service-name", - }, - Tags: map[string]string{ + Span: &SpanData{ + Kind: oteltrace.SpanKindServer, + ServiceName: "test-service-name", + Attributes: map[string]string{ "test-tag": "test-tag-value", "other-tag": "other-tag-value", }, @@ -92,32 +88,26 @@ func TestSpanMatcher(t *testing.T) { }, { Name: "local endpoint service name matcher", Matcher: &SpanMatcher{ - LocalEndpointServiceName: "test-service-name", + ServiceName: "test-service-name", }, Spans: []SpanCase{{ - Span: &model.SpanModel{}, + Span: &SpanData{}, ShouldMatch: false, }, { - Span: &model.SpanModel{ - LocalEndpoint: &model.Endpoint{ - ServiceName: "test-service-name", - }, + Span: &SpanData{ + ServiceName: "test-service-name", }, ShouldMatch: true, }, { - Span: &model.SpanModel{ - LocalEndpoint: &model.Endpoint{ - ServiceName: "other-service-name", - }, + Span: &SpanData{ + ServiceName: "other-service-name", }, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Kind: model.Server, - LocalEndpoint: &model.Endpoint{ - ServiceName: "test-service-name", - }, - Tags: map[string]string{ + Span: &SpanData{ + Kind: oteltrace.SpanKindServer, + ServiceName: "test-service-name", + Attributes: map[string]string{ "test-tag": "test-tag-value", "other-tag": "other-tag-value", }, @@ -132,36 +122,34 @@ func TestSpanMatcher(t *testing.T) { }, }, Spans: []SpanCase{{ - Span: &model.SpanModel{}, + Span: &SpanData{}, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Tags: map[string]string{ + Span: &SpanData{ + Attributes: map[string]string{ "test-tag": "test-tag-value", }, }, ShouldMatch: true, }, { - Span: &model.SpanModel{ - Tags: map[string]string{ + Span: &SpanData{ + Attributes: map[string]string{ "test-tag": "other-tag-value", }, }, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Tags: map[string]string{ + Span: &SpanData{ + Attributes: map[string]string{ "other-tag": "test-tag-value", }, }, ShouldMatch: false, }, { - Span: &model.SpanModel{ - Kind: model.Server, - LocalEndpoint: &model.Endpoint{ - ServiceName: "test-service-name", - }, - Tags: map[string]string{ + Span: &SpanData{ + Kind: oteltrace.SpanKindServer, + ServiceName: "test-service-name", + Attributes: map[string]string{ "test-tag": "test-tag-value", "other-tag": "other-tag-value", }, @@ -204,16 +192,16 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "test-tag": "test-tag-value", }, }, @@ -233,26 +221,26 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, }, ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }, ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, }}, }, @@ -272,24 +260,24 @@ func TestMatchesSubtree(t *testing.T) { }, SpanTrees: []SpanTreeCase{{ SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }}, }, ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, }}, }, @@ -297,12 +285,12 @@ func TestMatchesSubtree(t *testing.T) { }, { SpanTree: &SpanTree{ Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }}, }}, @@ -310,13 +298,13 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }}, }}, @@ -324,12 +312,12 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, Children: []SpanTree{{}}, }}, @@ -337,12 +325,12 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Kind: model.Client, + Span: SpanData{ + Kind: oteltrace.SpanKindClient, }, }, {}}, }, @@ -370,18 +358,18 @@ func TestMatchesSubtree(t *testing.T) { }, SpanTrees: []SpanTreeCase{{ SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, @@ -390,18 +378,18 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, @@ -410,18 +398,18 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, @@ -430,18 +418,18 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, @@ -451,8 +439,8 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ // This span is a red-herring. Although it matches child 'a', @@ -460,20 +448,20 @@ func TestMatchesSubtree(t *testing.T) { // match since it is a parent of child 'b'. The matcher must // therefore look for alternative matches for 'a' by recursing // into its children. - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, @@ -483,19 +471,19 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, @@ -526,18 +514,18 @@ func TestMatchesSubtree(t *testing.T) { }, SpanTrees: []SpanTreeCase{{ SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, @@ -546,12 +534,12 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, @@ -560,18 +548,18 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: false, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, @@ -607,24 +595,24 @@ func TestMatchesSubtree(t *testing.T) { }, SpanTrees: []SpanTreeCase{{ SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "c", }, }, @@ -633,27 +621,27 @@ func TestMatchesSubtree(t *testing.T) { ShouldMatch: true, }, { SpanTree: &SpanTree{ - Span: model.SpanModel{ - Kind: model.Server, + Span: SpanData{ + Kind: oteltrace.SpanKindServer, }, Children: []SpanTree{{ Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "a", }, }, }, { - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "b", }, }, }}, }, { Children: []SpanTree{{ - Span: model.SpanModel{ - Tags: map[string]string{ + Span: SpanData{ + Attributes: map[string]string{ "child": "c", }, }, diff --git a/test/conformance/helpers/tracing_test_helper.go b/test/conformance/helpers/tracing_test_helper.go index 7f1c5d54168..b83a8dddf76 100644 --- a/test/conformance/helpers/tracing_test_helper.go +++ b/test/conformance/helpers/tracing_test_helper.go @@ -19,12 +19,16 @@ package helpers import ( "context" "testing" + "time" cetest "github.com/cloudevents/sdk-go/v2/test" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" tracinghelper "knative.dev/eventing/test/conformance/helpers/tracing" testlib "knative.dev/eventing/test/lib" + "knative.dev/eventing/test/lib/recordevents" ) // SetupTracingTestInfrastructureFunc sets up the infrastructure for running tracing tests. It returns the @@ -46,8 +50,64 @@ func tracingTest( setupInfrastructure SetupTracingTestInfrastructureFunc, channel metav1.TypeMeta, ) { + const ( + recordEventsPodName = "recordevents" + ) + client := testlib.Setup(t, true, setupClient) defer testlib.TearDown(client) - // TODO - redo with OTel - https://github.com/knative/eventing/issues/8853 + // Start the event info store. Note this is done _before_ we setup the infrastructure, which + // sends the event. + targetTracker, err := recordevents.NewEventInfoStore(client, recordEventsPodName, client.Namespace) + if err != nil { + t.Fatal("Pod tracker failed:", err) + } + + // Setup the test infrastructure + expectedTestSpan, eventMatcher := setupInfrastructure(ctx, t, &channel, client, recordEventsPodName, true) + + // Assert that the event was seen. + matches := targetTracker.AssertAtLeast(1, recordevents.MatchEvent(eventMatcher)) + + // Match the trace + traceID := getTraceIDHeader(t, matches) + + // TODO(knative/eventing#8853): Once the OTel collector is deployed in test infrastructure, + // query the collector here to retrieve spans for the given traceID, build a SpanTree, and + // match against expectedTestSpan. For now, log the expected tree and trace ID. + t.Logf("Trace ID: %s", traceID) + t.Logf("Expected span tree: %s", expectedTestSpan) + + // Give time for spans to be exported. + time.Sleep(10 * time.Second) + + // TODO(knative/eventing#8853): Replace the following with actual OTel trace retrieval and matching: + // spans, err := fetchOTelTraceByID(traceID, 5*time.Minute) + // if err != nil { + // t.Fatalf("Unable to get trace %q: %v", traceID, err) + // } + // tree, err := tracinghelper.GetTraceTree(spans) + // if err != nil { + // t.Fatal(err) + // } + // if len(expectedTestSpan.MatchesSubtree(t, tree)) == 0 { + // t.Fatalf("No matching subtree. want: %v got: %v", expectedTestSpan, tree) + // } +} + +// getTraceIDHeader gets the TraceID from the passed in events. It returns the header from the +// first matching event, but registers a fatal error if more than one traceid header is seen +// in that message. +func getTraceIDHeader(t *testing.T, evInfos []recordevents.EventInfo) string { + for i := range evInfos { + if nil != evInfos[i].HTTPHeaders { + sc := trace.SpanContextFromContext(propagation.TraceContext{}.Extract(context.TODO(), propagation.HeaderCarrier(evInfos[i].HTTPHeaders))) + if sc.HasTraceID() { + return sc.TraceID().String() + } + } + } + t.Fatalf("FAIL: No traceid in %d messages: (%v)", len(evInfos), evInfos) + return "" }