-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample_test.go
More file actions
365 lines (285 loc) · 9.53 KB
/
example_test.go
File metadata and controls
365 lines (285 loc) · 9.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// Copyright (c) 2025 Luis E. Muñoz. All Rights Reserved.
// SPDX-License-Identifier: MIT
package nspool_test
import (
"context"
"fmt"
"log"
"time"
"github.com/miekg/dns"
"github.com/nerdlem/nspool/v2"
"github.com/sirupsen/logrus"
)
// Example demonstrates basic usage of nspool with a resolver pool.
func Example() {
// Create pool with resolver addresses
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
// Configure health checking
nsp.SetHealthDomainSuffix("example.com")
nsp.SetMinResolvers(1)
// Perform initial health check
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
// Create DNS query
msg := new(dns.Msg)
msg.SetQuestion("github.com.", dns.TypeA)
// Query using the pool
response, _, err := nsp.Exchange(msg)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Received %d answers\n", len(response.Answer))
}
// ExampleNewFromPoolSlice demonstrates creating a pool with explicit resolver addresses.
func ExampleNewFromPoolSlice() {
// Create pool with specific resolvers
nsp := nspool.NewFromPoolSlice([]string{
"1.1.1.1:53",
"8.8.8.8:53",
"9.9.9.9:53",
})
// Configure health checking
nsp.SetHealthDomainSuffix("example.com")
// Perform health check
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Printf("Available resolvers: %d\n", nsp.AvailableCount())
}
// ExamplePool_SetHealthCheckFunction demonstrates using a custom health check function.
func ExamplePool_SetHealthCheckFunction() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// Custom health check that validates response content
customCheck := func(ans dns.Msg, t time.Duration, resolver string, p *nspool.Pool) bool {
// Must respond within 2 seconds
if t > 2*time.Second {
return false
}
// Must have NOERROR response
if ans.Rcode != dns.RcodeSuccess {
return false
}
// Must have at least one answer
if len(ans.Answer) == 0 {
return false
}
return true
}
nsp.SetHealthCheckFunction(customCheck)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Printf("Healthy resolvers: %d\n", nsp.AvailableCount())
}
// ExamplePool_SetRefreshPreHook demonstrates using a pre-refresh hook for logging.
func ExamplePool_SetRefreshPreHook() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
nsp.SetHealthDomainSuffix("example.com")
// Log before refresh
nsp.SetRefreshPreHook(func(p *nspool.Pool) bool {
log.Printf("Starting refresh with %d resolvers",
p.AvailableCount()+p.UnavailableCount())
return true
})
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
}
// ExamplePool_SetRefreshPostHook demonstrates using a post-refresh hook for logging.
func ExamplePool_SetRefreshPostHook() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
nsp.SetHealthDomainSuffix("example.com")
// Log after refresh
nsp.SetRefreshPostHook(func(p *nspool.Pool) {
log.Printf("Refresh complete: %d available, %d unavailable",
p.AvailableCount(), p.UnavailableCount())
})
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
}
// ExamplePool_SetRefreshPreHook_conditional demonstrates conditional refresh using pre-hook.
func ExamplePool_SetRefreshPreHook_conditional() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// Only allow refresh during off-peak hours
nsp.SetRefreshPreHook(func(p *nspool.Pool) bool {
hour := time.Now().Hour()
if hour >= 8 && hour <= 18 {
log.Println("Skipping refresh during peak hours")
return false
}
return true
})
err := nsp.Refresh()
if err == nspool.ErrRefreshAbortedByPreHook {
log.Println("Refresh was skipped by policy")
}
}
// ExamplePool_AutoRefresh demonstrates automatic periodic health checking.
func ExamplePool_AutoRefresh() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
nsp.SetHealthDomainSuffix("example.com")
// Perform initial refresh
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
// Start auto-refresh every 5 minutes
nsp.AutoRefresh(5 * time.Minute)
// Stop auto-refresh when done
defer nsp.AutoRefresh(0)
// Use the pool for queries...
fmt.Println("Auto-refresh enabled")
}
// ExamplePool_ExchangeContext demonstrates DNS queries with context timeout.
func ExamplePool_ExchangeContext() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Create DNS query
msg := new(dns.Msg)
msg.SetQuestion("github.com.", dns.TypeA)
// Query with context
response, rtt, err := nsp.ExchangeContext(ctx, msg)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Query completed in %v with %d answers\n", rtt, len(response.Answer))
}
// ExamplePool_SetLogger demonstrates enabling logging with logrus.
func ExamplePool_SetLogger() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// Create and configure logger
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
// Enable logging
nsp.SetLogger(logger)
nsp.SetDebug(true)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Println("Logging enabled")
}
// ExamplePool_SetMaxQueryRetries demonstrates configuring query retry behavior.
func ExamplePool_SetMaxQueryRetries() {
nsp := nspool.NewFromPoolSlice([]string{
"1.1.1.1:53",
"8.8.8.8:53",
"9.9.9.9:53",
})
nsp.SetHealthDomainSuffix("example.com")
// Set maximum retries to 5 (up to 6 total attempts)
nsp.SetMaxQueryRetries(5)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Printf("Max retries set to %d\n", nsp.MaxQueryRetries())
}
// ExamplePool_GetRandomResolver demonstrates getting a resolver address directly.
func ExamplePool_GetRandomResolver() {
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
nsp.SetHealthDomainSuffix("example.com")
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
// Get a random resolver
resolver, err := nsp.GetRandomResolver()
if err != nil {
log.Fatal(err)
}
// Use it directly with dns.Client
client := new(dns.Client)
msg := new(dns.Msg)
msg.SetQuestion("github.com.", dns.TypeA)
response, _, err := client.Exchange(msg, resolver)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Query via %s returned %d answers\n", resolver, len(response.Answer))
}
// ExamplePool_SetHealthCheckWorkerCount demonstrates configuring health check concurrency.
func ExamplePool_SetHealthCheckWorkerCount() {
nsp := nspool.NewFromPoolSlice([]string{
"1.1.1.1:53",
"8.8.8.8:53",
"9.9.9.9:53",
})
nsp.SetHealthDomainSuffix("example.com")
// Use 10 workers for health checks
nsp.SetHealthCheckWorkerCount(10)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Printf("Health checks use %d workers\n", nsp.HealthCheckWorkerCount())
}
// ExampleDefaultHealthCheckFunction demonstrates the default health check behavior.
func ExampleDefaultHealthCheckFunction() {
// Create a pool
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// The default health check function is used automatically
// It simply verifies the response has RCODE = NOERROR
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Println("Using default health check")
}
// ExampleDefaultRefreshPreHook demonstrates the default pre-hook behavior.
func ExampleDefaultRefreshPreHook() {
// The default pre-hook always returns true (allows refresh)
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// You can explicitly set it if desired
nsp.SetRefreshPreHook(nspool.DefaultRefreshPreHook)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Println("Using default pre-hook")
}
// ExampleDefaultRefreshPostHook demonstrates the default post-hook behavior.
func ExampleDefaultRefreshPostHook() {
// The default post-hook is a no-op
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53"})
nsp.SetHealthDomainSuffix("example.com")
// You can explicitly set it if desired
nsp.SetRefreshPostHook(nspool.DefaultRefreshPostHook)
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
fmt.Println("Using default post-hook")
}
// ExamplePool_SetQuietResolverStateChange demonstrates how to enable quiet mode
// to suppress verbose demotion messages while still logging critical state changes.
func ExamplePool_SetQuietResolverStateChange() {
// Create a pool with some resolvers
nsp := nspool.NewFromPoolSlice([]string{"1.1.1.1:53", "8.8.8.8:53"})
// Set up logging
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
nsp.SetLogger(logger)
// Configure error thresholds
nsp.SetResolverErrorThreshold(0.05) // 5% error rate triggers weight reduction
nsp.SetResolverDisableThreshold(0.20) // 20% error rate triggers suspension
// Enable quiet mode to suppress demotion messages
// Only suspension and reinstatement events will be logged
nsp.SetQuietResolverStateChange(true)
// Configure health checking
nsp.SetHealthDomainSuffix("example.com")
// Perform health check
if err := nsp.Refresh(); err != nil {
log.Fatal(err)
}
// During normal operations, resolver weight reductions (demotions) will not be logged,
// but suspensions (removals from pool) and reinstatements (additions back) will still
// be logged for visibility into critical pool state changes.
fmt.Println("Quiet mode enabled - only critical state changes will be logged")
// Output: Quiet mode enabled - only critical state changes will be logged
}