Skip to content

Commit 992bbaf

Browse files
authored
contrib: add validation tests using test-agent (DataDog#2047)
This PR adds the APM Test Agent to run alongside unit / contrib tests in CI testing. A testing interface is created in contrib/internal/validationtest, which can be implemented by each contrib integration in order to generate test traces which are emitted to the APM Test Agent using the real Golang tracer. These traces are tested against a number of centrally located checks written within the Test-Agent, including: Service naming checks Peer.service checks Trace header checks The APM Test Agent is being added to every DD APM Tracer with the primary purpose of providing a centralized location for writing tests which will subsequently be used for asserting on handled traces. This strategy will help bring consistency to Datadog APM Tagging/ integration support.
1 parent 2ac90e1 commit 992bbaf

File tree

5 files changed

+505
-0
lines changed

5 files changed

+505
-0
lines changed

.github/workflows/unit-integration-tests.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ jobs:
5151
DD_APM_ENABLED: true
5252
DD_BIND_HOST: "0.0.0.0"
5353
DD_API_KEY: "invalid_key_but_this_is_fine"
54+
DD_TEST_AGENT_HOST: "localhost"
55+
DD_TEST_AGENT_PORT: 9126
5456
# We need to specify a custom health-check. By default, this container will remain "unhealthy" since
5557
# we don't fully configure it with a valid API key (and possibly other reasons)
5658
# This command just checks for our ability to connect to port 8126
@@ -59,6 +61,18 @@ jobs:
5961
ports:
6062
- 8125:8125/udp
6163
- 8126:8126
64+
testagent:
65+
image: ghcr.io/datadog/dd-apm-test-agent/ddapm-test-agent:v1.11.0
66+
ports:
67+
- 9126:9126
68+
env:
69+
LOG_LEVEL: DEBUG
70+
TRACE_LANGUAGE: golang
71+
ENABLED_CHECKS: trace_stall,trace_count_header,trace_peer_service,trace_dd_service
72+
PORT: 9126
73+
DD_SUPPRESS_TRACE_PARSE_ERRORS: true
74+
DD_POOL_TRACE_CHECK_FAILURES: true
75+
DD_DISABLE_ERROR_RESPONSES: true
6276
cassandra:
6377
image: cassandra:3.7
6478
env:
@@ -188,6 +202,34 @@ jobs:
188202
shell: bash
189203
run: bash <(curl -s https://codecov.io/bash)
190204

205+
- name: Get Datadog APM Test Agent Logs
206+
if: always()
207+
shell: bash
208+
run: docker logs ${{ job.services.testagent.id }}
209+
210+
- name: Get Datadog APM Test Agent Trace Check Summary Results
211+
if: always()
212+
shell: bash
213+
run: |
214+
RESPONSE=$(curl -s -w "\n%{http_code}" -o response.txt "http://127.0.0.1:9126/test/trace_check/failures?return_all=true")
215+
RESPONSE_CODE=$(echo "$RESPONSE" | awk 'END {print $NF}')
216+
SUMMARY_RESPONSE=$(curl -s -w "\n%{http_code}" -o summary_response.txt "http://127.0.0.1:9126/test/trace_check/summary?return_all=true")
217+
SUMMARY_RESPONSE_CODE=$(echo "$SUMMARY_RESPONSE" | awk 'END {print $NF}')
218+
if [[ $RESPONSE_CODE -eq 200 ]]; then
219+
echo " "
220+
cat response.txt
221+
echo " - All APM Test Agent Check Traces returned successful!"
222+
echo "APM Test Agent Check Traces Summary Results:"
223+
cat summary_response.txt | jq "."
224+
else
225+
echo "APM Test Agent Check Traces failed with response code: $RESPONSE_CODE"
226+
echo "Failures:"
227+
cat response.txt
228+
echo "APM Test Agent Check Traces Summary Results:"
229+
cat summary_response.txt | jq "."
230+
exit 1
231+
fi
232+
191233
- name: Testing outlier google.golang.org/api
192234
run: |
193235
go get google.golang.org/api@v0.121.0 # version used to generate code
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2023 Datadog, Inc.
5+
6+
package validationtest
7+
8+
import (
9+
"testing"
10+
11+
memcachetrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/bradfitz/gomemcache/memcache"
12+
13+
"github.com/bradfitz/gomemcache/memcache"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
type Memcache struct {
18+
client *memcachetrace.Client
19+
opts []memcachetrace.ClientOption
20+
numSpans int
21+
}
22+
23+
func NewMemcache() *Memcache {
24+
return &Memcache{
25+
opts: make([]memcachetrace.ClientOption, 0),
26+
}
27+
}
28+
29+
func (i *Memcache) Name() string {
30+
return "bradfitz/gomemcache/memcache"
31+
}
32+
33+
func (i *Memcache) Init(_ *testing.T) {
34+
i.client = memcachetrace.WrapClient(memcache.New("127.0.0.1:11211"), i.opts...)
35+
}
36+
37+
func (i *Memcache) GenSpans(t *testing.T) {
38+
t.Helper()
39+
err := i.client.Set(&memcache.Item{Key: "myKey", Value: []byte("myValue")})
40+
require.NoError(t, err)
41+
i.numSpans++
42+
t.Cleanup(func() {
43+
i.numSpans = 0
44+
})
45+
}
46+
47+
func (i *Memcache) NumSpans() int {
48+
return i.numSpans
49+
}
50+
51+
func (i *Memcache) WithServiceName(name string) {
52+
i.opts = append(i.opts, memcachetrace.WithServiceName(name))
53+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2023 Datadog, Inc.
5+
6+
package validationtest
7+
8+
import (
9+
"context"
10+
"net"
11+
"testing"
12+
"time"
13+
14+
dnstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/miekg/dns"
15+
16+
"github.com/miekg/dns"
17+
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
19+
)
20+
21+
type DNS struct {
22+
msg *dns.Msg
23+
mux *dns.ServeMux
24+
addr string
25+
numSpans int
26+
}
27+
28+
func NewDNS() *DNS {
29+
return &DNS{}
30+
}
31+
32+
func (i *DNS) Name() string {
33+
return "miekg/dns"
34+
}
35+
36+
func (i *DNS) Init(t *testing.T) {
37+
// TODO: enable when the integration implements naming schema
38+
t.Skip("not implemented yet")
39+
40+
t.Helper()
41+
i.addr = getFreeAddr(t).String()
42+
server := &dns.Server{
43+
Addr: i.addr,
44+
Net: "udp",
45+
Handler: dnstrace.WrapHandler(&handler{t: t, ig: i}),
46+
}
47+
// start the traced server
48+
go func() {
49+
require.NoError(t, server.ListenAndServe())
50+
}()
51+
// wait for the server to be ready
52+
waitServerReady(t, server.Addr)
53+
t.Cleanup(func() {
54+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
55+
defer cancel()
56+
assert.NoError(t, server.ShutdownContext(ctx))
57+
})
58+
}
59+
60+
func (i *DNS) GenSpans(t *testing.T) {
61+
t.Helper()
62+
msg := newMessage()
63+
_, err := dnstrace.Exchange(msg, i.addr)
64+
require.NoError(t, err)
65+
i.numSpans++
66+
t.Cleanup(func() {
67+
i.numSpans = 0
68+
})
69+
}
70+
71+
func (i *DNS) NumSpans() int {
72+
return i.numSpans
73+
}
74+
75+
func (i *DNS) WithServiceName(_ string) {
76+
return
77+
}
78+
79+
func newMessage() *dns.Msg {
80+
m := new(dns.Msg)
81+
m.SetQuestion("miek.nl.", dns.TypeMX)
82+
return m
83+
}
84+
85+
type handler struct {
86+
t *testing.T
87+
ig *DNS
88+
}
89+
90+
func (h *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
91+
m := new(dns.Msg)
92+
m.SetReply(r)
93+
assert.NoError(h.t, w.WriteMsg(m))
94+
h.ig.numSpans++
95+
}
96+
97+
func getFreeAddr(t *testing.T) net.Addr {
98+
li, err := net.Listen("tcp", "127.0.0.1:0")
99+
require.NoError(t, err)
100+
addr := li.Addr()
101+
require.NoError(t, li.Close())
102+
return addr
103+
}
104+
105+
func waitServerReady(t *testing.T, addr string) {
106+
ticker := time.NewTicker(100 * time.Millisecond)
107+
defer ticker.Stop()
108+
timeoutChan := time.After(5 * time.Second)
109+
for {
110+
m := new(dns.Msg)
111+
m.SetQuestion("miek.nl.", dns.TypeMX)
112+
_, err := dns.Exchange(m, addr)
113+
if err == nil {
114+
break
115+
}
116+
117+
select {
118+
case <-ticker.C:
119+
continue
120+
121+
case <-timeoutChan:
122+
t.Fatal("timeout waiting for DNS server to be ready")
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)