github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/prober/dns/dns_test.go (about) 1 package dns 2 3 import ( 4 "bytes" 5 "context" 6 "net" 7 "os" 8 "slices" 9 "strings" 10 "sync/atomic" 11 "testing" 12 "time" 13 14 "github.com/go-kit/log" 15 "github.com/grafana/synthetic-monitoring-agent/internal/model" 16 "github.com/grafana/synthetic-monitoring-agent/internal/prober/dns/internal/bbe/config" 17 sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring" 18 "github.com/miekg/dns" 19 "github.com/prometheus/client_golang/prometheus" 20 "github.com/prometheus/common/expfmt" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestName(t *testing.T) { 25 name := Prober.Name(Prober{}) 26 require.Equal(t, name, "dns") 27 } 28 29 func TestNewProber(t *testing.T) { 30 testcases := map[string]struct { 31 input model.Check 32 expected Prober 33 ExpectError bool 34 }{ 35 "default": { 36 input: model.Check{Check: sm.Check{ 37 Target: "www.grafana.com", 38 Settings: sm.CheckSettings{ 39 Dns: &sm.DnsSettings{}, 40 }, 41 }}, 42 expected: Prober{ 43 config: config.Module{ 44 Prober: "dns", 45 Timeout: 0, 46 DNS: config.DNSProbe{ 47 IPProtocol: "ip6", 48 IPProtocolFallback: true, 49 TransportProtocol: "tcp", 50 QueryName: "www.grafana.com", 51 QueryType: "ANY", 52 Recursion: true, 53 }, 54 }, 55 }, 56 ExpectError: false, 57 }, 58 "no-settings": { 59 input: model.Check{ 60 Check: sm.Check{ 61 Target: "www.grafana.com", 62 Settings: sm.CheckSettings{ 63 Dns: nil, 64 }, 65 }, 66 }, 67 expected: Prober{}, 68 ExpectError: true, 69 }, 70 } 71 72 for name, testcase := range testcases { 73 t.Run(name, func(t *testing.T) { 74 actual, err := NewProber(testcase.input) 75 require.Equal(t, &testcase.expected, &actual) 76 77 if testcase.ExpectError { 78 require.Error(t, err, "unsupported check") 79 } else { 80 require.NoError(t, err) 81 } 82 }) 83 } 84 } 85 86 func TestSettingsToModule(t *testing.T) { 87 testcases := map[string]struct { 88 input sm.DnsSettings 89 expected config.Module 90 }{ 91 "default": { 92 input: sm.DnsSettings{}, 93 expected: config.Module{ 94 Prober: "dns", 95 Timeout: 0, 96 DNS: config.DNSProbe{ 97 IPProtocol: "ip6", 98 IPProtocolFallback: true, 99 TransportProtocol: "tcp", 100 QueryName: "www.grafana.com", 101 QueryType: "ANY", 102 Recursion: true, 103 }, 104 }, 105 }, 106 "partial-settings": { 107 input: sm.DnsSettings{ 108 RecordType: 4, 109 Protocol: 1, 110 }, 111 expected: config.Module{ 112 Prober: "dns", 113 Timeout: 0, 114 DNS: config.DNSProbe{ 115 IPProtocol: "ip6", 116 IPProtocolFallback: true, 117 TransportProtocol: "udp", 118 QueryName: "www.grafana.com", 119 QueryType: "MX", 120 Recursion: true, 121 }, 122 }, 123 }, 124 "validations": { 125 input: sm.DnsSettings{ 126 RecordType: 4, 127 Protocol: 1, 128 ValidateAnswer: &sm.DNSRRValidator{ 129 FailIfMatchesRegexp: []string{"test"}, 130 FailIfNotMatchesRegexp: []string{"not test"}, 131 }, 132 ValidateAuthority: &sm.DNSRRValidator{ 133 FailIfMatchesRegexp: []string{"test"}, 134 FailIfNotMatchesRegexp: []string{"not test"}, 135 }, 136 ValidateAdditional: &sm.DNSRRValidator{ 137 FailIfMatchesRegexp: []string{"test"}, 138 FailIfNotMatchesRegexp: []string{"not test"}, 139 }, 140 }, 141 expected: config.Module{ 142 Prober: "dns", 143 Timeout: 0, 144 DNS: config.DNSProbe{ 145 IPProtocol: "ip6", 146 IPProtocolFallback: true, 147 TransportProtocol: "udp", 148 QueryName: "www.grafana.com", 149 QueryType: "MX", 150 Recursion: true, 151 ValidateAnswer: config.DNSRRValidator{ 152 FailIfMatchesRegexp: []string{"test"}, 153 FailIfNotMatchesRegexp: []string{"not test"}, 154 }, 155 ValidateAuthority: config.DNSRRValidator{ 156 FailIfMatchesRegexp: []string{"test"}, 157 FailIfNotMatchesRegexp: []string{"not test"}, 158 }, 159 ValidateAdditional: config.DNSRRValidator{ 160 FailIfMatchesRegexp: []string{"test"}, 161 FailIfNotMatchesRegexp: []string{"not test"}, 162 }, 163 }, 164 }, 165 }, 166 } 167 168 for name, testcase := range testcases { 169 target := "www.grafana.com" 170 171 t.Run(name, func(t *testing.T) { 172 actual := settingsToModule(&testcase.input, target) 173 require.Equal(t, &testcase.expected, &actual) 174 }) 175 } 176 } 177 178 func TestProberRetries(t *testing.T) { 179 if !slices.Contains(strings.Split(os.Getenv("SM_TEST_RUN"), ","), "TestProberRetries") { 180 t.Skip("Skipping long test TestProberRetries") 181 } 182 183 mux := dns.NewServeMux() 184 185 var counter atomic.Int32 186 187 mux.Handle(".", dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { 188 answer := dns.Msg{ 189 MsgHdr: dns.MsgHdr{ 190 Id: r.Id, 191 Response: true, 192 Rcode: dns.RcodeSuccess, 193 }, 194 Question: r.Question, 195 Answer: []dns.RR{ 196 &dns.A{ 197 Hdr: dns.RR_Header{ 198 Name: r.Question[0].Name, 199 Rrtype: dns.TypeA, 200 Class: dns.ClassINET, 201 Ttl: 60, 202 }, 203 A: net.ParseIP("1.2.3.4"), 204 }, 205 }, 206 } 207 208 counter.Add(1) 209 t.Logf("Received request %d: %v", counter.Load(), r) 210 211 delay := 10 * time.Second 212 if counter.Load()%3 == 0 { 213 delay = 0 214 } 215 216 t.Log("Answer...") 217 time.Sleep(delay) 218 219 _ = w.WriteMsg(&answer) 220 })) 221 222 addr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:0") 223 require.NoError(t, err) 224 l, err := net.ListenUDP("udp4", addr) 225 require.NoError(t, err) 226 t.Log(l.LocalAddr().String()) 227 server := &dns.Server{Addr: ":0", PacketConn: l, Net: "udp", Handler: mux} 228 229 go func() { 230 err := server.ActivateAndServe() 231 if err != nil { 232 panic(err) 233 } 234 }() 235 236 p, err := NewExperimentalProber(model.Check{ 237 Check: sm.Check{ 238 Target: "www.grafana.com", 239 Timeout: 20000, 240 Settings: sm.CheckSettings{ 241 Dns: &sm.DnsSettings{ 242 Server: l.LocalAddr().String(), 243 RecordType: sm.DnsRecordType_A, 244 Protocol: sm.DnsProtocol_UDP, 245 IpVersion: sm.IpVersion_V4, 246 }, 247 }, 248 }, 249 }) 250 251 require.NoError(t, err) 252 253 ctx, cancel := context.WithTimeout(context.Background(), p.config.Timeout) 254 t.Cleanup(cancel) 255 256 defer func() { 257 err := server.ShutdownContext(ctx) 258 if err != nil { 259 panic(err) 260 } 261 }() 262 263 registry := prometheus.NewPedanticRegistry() 264 265 var buf bytes.Buffer 266 267 logger := log.NewLogfmtLogger(&buf) 268 269 t0 := time.Now() 270 success, duration := p.Probe(ctx, p.target, registry, logger, "test-execution-id") 271 t.Log(success, time.Since(t0)) 272 require.True(t, success) 273 require.Equal(t, 0, duration) 274 275 mfs, err := registry.Gather() 276 require.NoError(t, err) 277 278 enc := expfmt.NewEncoder(&buf, expfmt.NewFormat(expfmt.TypeTextPlain)) 279 for _, mf := range mfs { 280 require.NoError(t, enc.Encode(mf)) 281 } 282 283 t.Log(buf.String()) 284 }