Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Improve traces e2e test outputs with FlatTrace #1476

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
16 changes: 8 additions & 8 deletions test/e2e/traces_service_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ var _ = Describe(suite.ID(), Label(suite.LabelTraces), func() {
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(
ContainTd(SatisfyAll(
ContainResourceAttrs(HaveKeyWithValue("service.name", expectedServiceName)),
ContainResourceAttrs(HaveKeyWithValue("k8s.pod.name", ContainSubstring(givenPodPrefix))),
)),
HaveFlatTraces(SatisfyAll(ContainElement(
HaveResourceAttributes(HaveKeyWithValue("service.name", expectedServiceName)),
HaveResourceAttributes(HaveKeyWithValue("k8s.pod.name", ContainSubstring(givenPodPrefix))),
))),
))
}, periodic.TelemetryEventuallyTimeout, periodic.TelemetryInterval).Should(Succeed())
}
Expand Down Expand Up @@ -132,11 +132,11 @@ var _ = Describe(suite.ID(), Label(suite.LabelTraces), func() {
resp, err := proxyClient.Get(backendExportURL)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(
Not(ContainTd(
ContainResourceAttrs(HaveKey(ContainSubstring("kyma"))),
g.Expect(resp).To(HaveHTTPBody(HaveFlatTraces(
Not(ContainElement(
HaveResourceAttributes(HaveKey(ContainSubstring("kyma"))),
)),
))
)))
}, periodic.EventuallyTimeout, periodic.TelemetryInterval).Should(Succeed())
})
})
Expand Down
29 changes: 14 additions & 15 deletions test/integration/istio/traces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ func verifyIstioSpans(backendURL, namespace string) {
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))

g.Expect(resp).To(HaveHTTPBody(ContainTd(SatisfyAll(
g.Expect(resp).To(HaveHTTPBody(HaveFlatTraces(ContainElement(SatisfyAll(
// Identify istio-proxy traces by component=proxy attribute
ContainSpan(WithSpanAttrs(HaveKeyWithValue("component", "proxy"))),
ContainSpan(WithSpanAttrs(HaveKeyWithValue("istio.namespace", namespace))),
))))
HaveSpanAttributes(HaveKeyWithValue("component", "proxy")),
HaveSpanAttributes(HaveKeyWithValue("istio.namespace", namespace)),
)))))
}, periodic.TelemetryEventuallyTimeout, periodic.TelemetryInterval).Should(Succeed())
}

Expand All @@ -207,13 +207,12 @@ func verifyCustomIstiofiedAppSpans(backendURL, name, namespace string) {
resp, err := proxyClient.Get(backendURL)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))

g.Expect(resp).To(HaveHTTPBody(ContainTd(SatisfyAll(
g.Expect(resp).To(HaveHTTPBody(HaveFlatTraces(ContainElement(SatisfyAll(
// Identify sample app by serviceName attribute
ContainResourceAttrs(HaveKeyWithValue("service.name", "monitoring-custom-metrics")),
ContainResourceAttrs(HaveKeyWithValue("k8s.pod.name", name)),
ContainResourceAttrs(HaveKeyWithValue("k8s.namespace.name", namespace)),
))))
HaveResourceAttributes(HaveKeyWithValue("service.name", "monitoring-custom-metrics")),
HaveResourceAttributes(HaveKeyWithValue("k8s.pod.name", name)),
HaveResourceAttributes(HaveKeyWithValue("k8s.namespace.name", namespace)),
)))))
}, periodic.TelemetryEventuallyTimeout, periodic.TelemetryInterval).Should(Succeed())
}

Expand All @@ -222,11 +221,11 @@ func verifyCustomAppSpans(backendURL, name, namespace string) {
resp, err := proxyClient.Get(backendURL)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(ContainTd(SatisfyAll(
g.Expect(resp).To(HaveHTTPBody(HaveFlatTraces(ContainElement(SatisfyAll(
// Identify sample app by serviceName attribute
ContainResourceAttrs(HaveKeyWithValue("service.name", "monitoring-custom-metrics")),
ContainResourceAttrs(HaveKeyWithValue("k8s.pod.name", name)),
ContainResourceAttrs(HaveKeyWithValue("k8s.namespace.name", namespace)),
))))
HaveResourceAttributes(HaveKeyWithValue("service.name", "monitoring-custom-metrics")),
HaveResourceAttributes(HaveKeyWithValue("k8s.pod.name", name)),
HaveResourceAttributes(HaveKeyWithValue("k8s.namespace.name", namespace)),
)))))
}, periodic.TelemetryEventuallyTimeout, periodic.TelemetryInterval).Should(Succeed())
}
4 changes: 2 additions & 2 deletions test/testkit/assert/traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TracesFromNamespaceDelivered(proxyClient *apiserverproxy.Client, backendExp
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(
ContainTd(ContainResourceAttrs(HaveKeyWithValue("k8s.namespace.name", namespace))),
HaveFlatTraces(ContainElement(HaveResourceAttributes(HaveKeyWithValue("k8s.namespace.name", namespace)))),
))
err = resp.Body.Close()
g.Expect(err).NotTo(HaveOccurred())
Expand All @@ -39,7 +39,7 @@ func TracesFromNamespacesNotDelivered(proxyClient *apiserverproxy.Client, backen
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(
Not(ContainTd(ContainResourceAttrs(HaveKeyWithValue("k8s.namespace.name", BeElementOf(namespaces))))),
HaveFlatTraces(ContainElement(HaveResourceAttributes(HaveKeyWithValue("k8s.namespace.name", BeElementOf(namespaces))))),
))
err = resp.Body.Close()
g.Expect(err).NotTo(HaveOccurred())
Expand Down
2 changes: 1 addition & 1 deletion test/testkit/matchers/metric/metric_matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func HaveFlatMetrics(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(jsonMetrics []byte) ([]FlatMetric, error) {
mds, err := unmarshalMetrics(jsonMetrics)
if err != nil {
return nil, fmt.Errorf("WithMds requires a valid OTLP JSON document: %w", err)
return nil, fmt.Errorf("HaveFlatMetrics requires a valid OTLP JSON document: %w", err)
}

fm := flattenAllMetrics(mds)
Expand Down
22 changes: 11 additions & 11 deletions test/testkit/matchers/metric/metric_matchers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"go.opentelemetry.io/collector/pdata/pmetric"
)

var fmdps = []FlatMetric{
var fms = []FlatMetric{
{
Name: "container.cpu.time",
Description: "time of container cpu",
Expand Down Expand Up @@ -54,7 +54,7 @@ var _ = Describe("HaveFlatMetrics", func() {
Expect(success).Should(BeFalse())
})

It("should return a FlatMetricDataPoints structure", func() {
It("should return a FlatMetric struct", func() {
md := pmetric.NewMetrics()

rm := md.ResourceMetrics().AppendEmpty()
Expand All @@ -79,57 +79,57 @@ var _ = Describe("HaveFlatMetrics", func() {
pt.SetDoubleValue(1.5)
pt.Attributes().PutStr("foo", "bar")

Expect(mustMarshalMetrics(md)).Should(HaveFlatMetrics(ContainElement(fmdps[0])))
Expect(mustMarshalMetrics(md)).Should(HaveFlatMetrics(ContainElement(fms[0])))
})
})

var _ = Describe("HaveUniqueNames", func() {
It("should return unique names", func() {
Expect(fmdps).Should(HaveUniqueNames(ConsistOf("container.cpu.time", "container.cpu.usage")))
Expect(fms).Should(HaveUniqueNames(ConsistOf("container.cpu.time", "container.cpu.usage")))
})
})

var _ = Describe("HaveResourceAttributes", func() {
It("should have the specified key", func() {
Expect(fmdps).Should(ContainElement(HaveResourceAttributes(HaveKey("k8s.cluster.name"))))
Expect(fms).Should(ContainElement(HaveResourceAttributes(HaveKey("k8s.cluster.name"))))
})
})

var _ = Describe("HaveName", func() {
It("should return the correct name", func() {
Expect(fmdps).Should(ContainElement(HaveName(ContainSubstring("container"))))
Expect(fms).Should(ContainElement(HaveName(ContainSubstring("container"))))
})
})

var _ = Describe("HaveType", func() {
It("should return the correct type", func() {
Expect(fmdps).Should(ContainElement(HaveType(Equal(pmetric.MetricTypeGauge.String()))))
Expect(fms).Should(ContainElement(HaveType(Equal(pmetric.MetricTypeGauge.String()))))
})
})

var _ = Describe("HaveMetricAttributes", func() {
It("should have the specified key", func() {
Expect(fmdps).Should(
Expect(fms).Should(
ContainElement(HaveMetricAttributes(HaveKey("foo"))),
)
})
})

var _ = Describe("HaveScopeName", func() {
It("should contain the specified string", func() {
Expect(fmdps).Should(ContainElement(HaveScopeName(ContainSubstring("container"))))
Expect(fms).Should(ContainElement(HaveScopeName(ContainSubstring("container"))))
})
})

var _ = Describe("HaveScopeVersion", func() {
It("should contain the specified version", func() {
Expect(fmdps).Should(ContainElement(HaveScopeVersion(ContainSubstring("1.0"))))
Expect(fms).Should(ContainElement(HaveScopeVersion(ContainSubstring("1.0"))))
})
})

var _ = Describe("HaveKeys", func() {
It("should have all the keys within the specified list", func() {
Expect(fmdps).Should(ContainElement(HaveResourceAttributes(HaveKeys(ContainElements("k8s.cluster.name", "k8s.deployment.name")))))
Expect(fms).Should(ContainElement(HaveResourceAttributes(HaveKeys(ContainElements("k8s.cluster.name", "k8s.deployment.name")))))
})
})

Expand Down
52 changes: 52 additions & 0 deletions test/testkit/matchers/trace/ptrace_utils.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
package trace

import (
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"

"github.com/kyma-project/telemetry-manager/test/testkit/matchers"
)

type FlatTrace struct {
Name, TraceID, SpanID, ScopeName, ScopeVersion string
ResourceAttributes, ScopeAttributes, SpanAttributes map[string]string
}

func unmarshalTraces(jsonlMetrics []byte) ([]ptrace.Traces, error) {
return matchers.UnmarshalSignals[ptrace.Traces](jsonlMetrics, func(buf []byte) (ptrace.Traces, error) {
var unmarshaler ptrace.JSONUnmarshaler
return unmarshaler.UnmarshalTraces(buf)
})
}

func flattenAllTraces(tds []ptrace.Traces) []FlatTrace {
var flatTraces []FlatTrace

for _, td := range tds {
flatTraces = append(flatTraces, flattenTraces(td)...)
}
return flatTraces
}

func flattenTraces(td ptrace.Traces) []FlatTrace {
var flatTraces []FlatTrace

for i := 0; i < td.ResourceSpans().Len(); i++ {
resourceSpans := td.ResourceSpans().At(i)
for j := 0; j < resourceSpans.ScopeSpans().Len(); j++ {
scopeSpans := resourceSpans.ScopeSpans().At(j)
for k := 0; k < scopeSpans.Spans().Len(); k++ {
spans := scopeSpans.Spans().At(k)
for l := 0; l < spans.Attributes().Len(); l++ {
flatTraces = append(flatTraces, FlatTrace{
Name: spans.Name(),
TraceID: spans.TraceID().String(),
SpanID: spans.SpanID().String(),
ScopeName: scopeSpans.Scope().Name(),
ScopeVersion: scopeSpans.Scope().Version(),
ResourceAttributes: attributeToMap(resourceSpans.Resource().Attributes()),
ScopeAttributes: attributeToMap(scopeSpans.Scope().Attributes()),
SpanAttributes: attributeToMap(spans.Attributes()),
})
}
}
}
}
return flatTraces
}

// attributeToMap converts pdata.AttributeMap to a map using the string representation of the values.
func attributeToMap(attrs pcommon.Map) map[string]string {
attrMap := make(map[string]string)
attrs.Range(func(k string, v pcommon.Value) bool {
attrMap[k] = v.AsString()
return true
})
return attrMap
}

func getSpans(td ptrace.Traces) []ptrace.Span {

Check failure on line 68 in test/testkit/matchers/trace/ptrace_utils.go

View workflow job for this annotation

GitHub Actions / lint

func `getSpans` is unused (unused)
var spans []ptrace.Span

for i := 0; i < td.ResourceSpans().Len(); i++ {
Expand Down
89 changes: 34 additions & 55 deletions test/testkit/matchers/trace/trace_matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,86 +5,65 @@ import (

"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
)

func WithTds(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(jsonlTraces []byte) ([]ptrace.Traces, error) {
tds, err := unmarshalTraces(jsonlTraces)
func HaveFlatTraces(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(jsonTraces []byte) ([]FlatTrace, error) {
tds, err := unmarshalTraces(jsonTraces)
if err != nil {
return nil, fmt.Errorf("WithTds requires a valid OTLP JSON document: %w", err)
return nil, fmt.Errorf("HaveFlatTraces requires a valid OTLP JSON document: %w", err)
}

return tds, nil
ft := flattenAllTraces(tds)
return ft, nil
}, matcher)
}

// ContainTd is an alias for WithTds(gomega.ContainElement()).
func ContainTd(matcher types.GomegaMatcher) types.GomegaMatcher {
return WithTds(gomega.ContainElement(matcher))
}

// ConsistOfTds is an alias for WithTds(gomega.ConsistOf()).
func ConsistOfTds(matcher types.GomegaMatcher) types.GomegaMatcher {
return WithTds(gomega.ConsistOf(matcher))
}

func WithResourceAttrs(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(td ptrace.Traces) ([]map[string]any, error) {
var rawAttrs []map[string]any
for i := 0; i < td.ResourceSpans().Len(); i++ {
rawAttrs = append(rawAttrs, td.ResourceSpans().At(i).Resource().Attributes().AsRaw())
}
return rawAttrs, nil
// HaveName extracts name from FlatTrace and applies the matcher to it.
func HaveName(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) string {
return ft.Name
}, matcher)
}

// ContainResourceAttrs is an alias for WithResourceAttrs(gomega.ContainElement()).
func ContainResourceAttrs(matcher types.GomegaMatcher) types.GomegaMatcher {
return WithResourceAttrs(gomega.ContainElement(matcher))
}

func WithSpans(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(td ptrace.Traces) ([]ptrace.Span, error) {
return getSpans(td), nil
// HaveSpanID extracts ScopeID from FlatTrace and applies the matcher to it.
func HaveSpanID(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) string {
return ft.SpanID
}, matcher)
}

// ContainSpan is an alias for WithSpans(gomega.ContainElement()).
func ContainSpan(matcher types.GomegaMatcher) types.GomegaMatcher {
return WithSpans(gomega.ContainElement(matcher))
}

// ConsistOfSpans is an alias for WithSpans(gomega.ConsistOf()).
func ConsistOfSpans(matcher types.GomegaMatcher) types.GomegaMatcher {
return WithSpans(gomega.ConsistOf(matcher))
func HaveTraceID(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) string {
return ft.TraceID
}, matcher)
}

func WithTraceID(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(s ptrace.Span) pcommon.TraceID {
return s.TraceID()
// HaveScopeName extracts scope name from FlatTrace and applies the matcher to it.
func HaveScopeName(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) string {
return ft.ScopeName
}, matcher)
}

func WithSpanIDs(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(spans []ptrace.Span) []pcommon.SpanID {
var spansIDs []pcommon.SpanID
for _, span := range spans {
spansIDs = append(spansIDs, span.SpanID())
}
return spansIDs
// HaveScopeVersion extracts scope version from FlatTrace and applies the matcher to it.
func HaveScopeVersion(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) string {
return ft.ScopeVersion
}, matcher)
}

func WithSpanID(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(s ptrace.Span) pcommon.SpanID {
return s.SpanID()
// HaveResourceAttributes extracts resource attributes from FlatTrace and applies the matcher to them.
func HaveResourceAttributes(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) map[string]string {
return ft.ResourceAttributes
}, matcher)
}

func WithSpanAttrs(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(s ptrace.Span) map[string]any {
return s.Attributes().AsRaw()
// HaveSpanAttributes extracts span attributes from FlatTrace and applies the matcher to them.
func HaveSpanAttributes(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(ft FlatTrace) map[string]string {
return ft.SpanAttributes
}, matcher)
}
Loading
Loading