Skip to content

Commit e5d3a1e

Browse files
committed
chore(test): Make tracing test more robust
Previously, the tracing integration test only checked for a simple existence of a trace with a service name, while not inspecting the actual values inside the trace. This makes the test check the tags inside a specific trace, both for a process and a span. In the future, this can be improved even more to inspect all spans in a trace and ensure the relationships between them are correct, but this will require updates to emojivoto and its propagation to be viable. Signed-off-by: Scott Fleener <[email protected]>
1 parent 09848d0 commit e5d3a1e

File tree

1 file changed

+176
-9
lines changed

1 file changed

+176
-9
lines changed

test/integration/tracing/tracing/tracing_test.go

Lines changed: 176 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"os"
9+
"reflect"
910
"testing"
1011
"time"
1112

@@ -19,10 +20,24 @@ type (
1920

2021
trace struct {
2122
Processes map[string]process `json:"processes"`
23+
Spans []span `json:"spans"`
2224
}
2325

2426
process struct {
2527
ServiceName string `json:"serviceName"`
28+
Tags []tag `json:"tags"`
29+
}
30+
31+
span struct {
32+
OperationName string `json:"operationName"`
33+
Tags []tag `json:"tags"`
34+
ProcessId string `json:"processID"`
35+
}
36+
37+
tag struct {
38+
Key string `json:"key"`
39+
Type string `json:"type"`
40+
Value interface{} `json:"value"`
2641
}
2742
)
2843

@@ -101,8 +116,46 @@ func TestTracing(t *testing.T) {
101116
return err
102117
}
103118

104-
if !hasTraceWithProcess(&traces, "linkerd-proxy") {
105-
return noProxyTraceFound{}
119+
expected := []expectedTrace{
120+
{
121+
serviceName: "linkerd-proxy",
122+
app: "web-svc",
123+
processTags: map[string]tagMatcher{
124+
"host.name": anyStringMatcher{},
125+
"k8s.container.name": stringMatcher("linkerd-proxy"),
126+
"k8s.pod.ip": anyStringMatcher{},
127+
"k8s.pod.uid": anyStringMatcher{},
128+
"linkerd.io/control-plane-ns": stringMatcher("linkerd"),
129+
"linkerd.io/proxy-deployment": stringMatcher("web"),
130+
"linkerd.io/workload-ns": stringMatcher(namespace),
131+
"pod-template-hash": anyStringMatcher{},
132+
"process.pid": anyMatcher{},
133+
"process.start_timestamp": anyMatcher{},
134+
},
135+
operation: "/api/vote",
136+
spanKind: "server",
137+
spanTags: map[string]tagMatcher{
138+
"http.request.method": stringMatcher("GET"),
139+
"url.scheme": stringMatcher("http"),
140+
"url.path": stringMatcher("/api/vote"),
141+
"url.query": anyStringMatcher{},
142+
"url.full": anyStringMatcher{},
143+
"network.transport": stringMatcher("tcp"),
144+
"server.address": stringMatcher("web-svc"),
145+
"server.port": stringMatcher("80"),
146+
"user_agent.original": anyStringMatcher{},
147+
"http.request.header.l5d-orig-proto": stringMatcher("HTTP/1.1"),
148+
"direction": stringMatcher("inbound"),
149+
"http.response.status_code": stringMatcher("200"),
150+
},
151+
},
152+
}
153+
154+
for _, e := range expected {
155+
err := inspectTraces(t, &traces, e)
156+
if err != nil {
157+
return err
158+
}
106159
}
107160
return nil
108161
})
@@ -144,19 +197,133 @@ func installTracing(t *testing.T, namespace string) {
144197
}
145198
}
146199

147-
func hasTraceWithProcess(traces *traces, ps string) bool {
200+
type expectedTrace struct {
201+
serviceName string
202+
app string
203+
processTags map[string]tagMatcher
204+
operation string
205+
spanKind string
206+
spanTags map[string]tagMatcher
207+
}
208+
209+
type tagMatcher interface {
210+
assertMatches(t *testing.T, key string, value interface{})
211+
}
212+
213+
type stringMatcher string
214+
215+
func (expected stringMatcher) assertMatches(t *testing.T, key string, actual interface{}) {
216+
anyStringMatcher{}.assertMatches(t, key, actual)
217+
if string(expected) != actual.(string) {
218+
t.Fatalf("Tag %s found with incorrect value\nExpected %s, found %s", key, string(expected), actual.(string))
219+
}
220+
}
221+
222+
type anyStringMatcher struct{}
223+
224+
func (_ anyStringMatcher) assertMatches(t *testing.T, key string, value interface{}) {
225+
_, ok := value.(string)
226+
if !ok {
227+
t.Fatalf("Tag %s has incorrect type\nexpected string, found %s", key, reflect.TypeOf(value).Name())
228+
}
229+
}
230+
231+
type anyMatcher struct{}
232+
233+
func (_ anyMatcher) assertMatches(_ *testing.T, _ string, _ interface{}) {}
234+
235+
func inspectTraces(t *testing.T, traces *traces, expected expectedTrace) error {
148236
for _, trace := range traces.Data {
149-
for _, process := range trace.Processes {
150-
if process.ServiceName == ps {
151-
return true
237+
var matchedProcess = ""
238+
for id, process := range trace.Processes {
239+
if process.ServiceName != expected.serviceName {
240+
continue
241+
}
242+
243+
var appMatches = false
244+
for _, tag := range process.Tags {
245+
if tag.Key != "app" {
246+
continue
247+
}
248+
value, ok := tag.Value.(string)
249+
if !ok {
250+
continue
251+
}
252+
if value != expected.app {
253+
break
254+
}
255+
appMatches = true
256+
break
152257
}
258+
if !appMatches {
259+
continue
260+
}
261+
262+
assertContainsTags(t, process.Tags, expected.processTags)
263+
264+
matchedProcess = id
265+
break
266+
}
267+
if matchedProcess == "" {
268+
continue
153269
}
270+
271+
for _, span := range trace.Spans {
272+
if span.ProcessId != matchedProcess {
273+
continue
274+
}
275+
276+
if span.OperationName != expected.operation {
277+
continue
278+
}
279+
280+
var kindMatches = false
281+
for _, tag := range span.Tags {
282+
value, ok := tag.Value.(string)
283+
if tag.Key != "span.kind" {
284+
continue
285+
}
286+
if !ok {
287+
continue
288+
}
289+
if value != expected.spanKind {
290+
break
291+
}
292+
kindMatches = true
293+
break
294+
}
295+
if !kindMatches {
296+
continue
297+
}
298+
299+
assertContainsTags(t, span.Tags, expected.spanTags)
300+
301+
return nil
302+
}
303+
}
304+
305+
return noProxyTraceFound{traces}
306+
}
307+
308+
func assertContainsTags(t *testing.T, rawTags []tag, expected map[string]tagMatcher) {
309+
tags := map[string]interface{}{}
310+
for _, tag := range rawTags {
311+
tags[tag.Key] = tag.Value
312+
}
313+
314+
for key, expectedValue := range expected {
315+
actual, ok := tags[key]
316+
if !ok {
317+
t.Fatalf("Tag %s not found in tags\nTags: %v", key, tags)
318+
}
319+
expectedValue.assertMatches(t, key, actual)
154320
}
155-
return false
156321
}
157322

158-
type noProxyTraceFound struct{}
323+
type noProxyTraceFound struct {
324+
traces *traces
325+
}
159326

160327
func (e noProxyTraceFound) Error() string {
161-
return "no trace found with processes: linkerd-proxy"
328+
return fmt.Sprintf("no trace found with processes: linkerd-proxy\n%v", e.traces.Data)
162329
}

0 commit comments

Comments
 (0)