Skip to content

Commit 8a45f7f

Browse files
authored
updated ui for 23x (#33)
2 parents 5da91f3 + 21726dc commit 8a45f7f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3532
-914
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg/server/static/vendor/** linguist-vendored

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ Set `skip_verify` to `true` to support locally signed certificates.
200200

201201
Sends DNS queries to a specific server and validates each answer against an expected value. Supports A, AAAA, and PTR record types. Each query produces a separate data source in the RRD, rendered as colored lines on the graph. The check succeeds only if all configured queries resolve and every answer matches its expected value.
202202

203-
PTR query names must be provided in reverse notation (e.g. `1.73.168.192.in-addr.arpa`). Expected PTR values may include or omit the trailing dot — both forms are accepted.
203+
PTR query names must be provided in reverse notation (e.g. `1.168.168.192.in-addr.arpa`). Expected PTR values may include or omit the trailing dot — both forms are accepted.
204204

205205
| Option | Type | Default | Description |
206206
| --------- | -------------- | ------------ | ---------------------------------------------------- |

pkg/check/dns/dns.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,9 @@ func (c *Check) Describe() check.Descriptor {
112112
// Success requires every query to resolve and every answer to match its expected value.
113113
// Each query's RTT is stored in microseconds keyed by the query name.
114114
func (c *Check) Run(ctx context.Context) check.Result {
115-
result := check.Result{
116-
Timestamp: time.Now(),
117-
Metrics: make(map[string]int64),
118-
}
119-
115+
metrics := make(map[string]*int64, len(c.queries))
120116
var lastErr error
117+
succeeded := 0
121118

122119
for _, q := range c.queries {
123120
msg := new(dns.Msg)
@@ -127,30 +124,33 @@ func (c *Check) Run(ctx context.Context) check.Result {
127124
resp, rtt, err := c.client.ExchangeContext(ctx, msg, c.server)
128125
if err != nil {
129126
lastErr = fmt.Errorf("dns %s %s: %w", qtypeName(q.qtype), q.name, err)
127+
metrics[q.resultKey] = nil
130128
continue
131129
}
132130

133131
if resp.Rcode != dns.RcodeSuccess {
134132
lastErr = fmt.Errorf("dns %s %s: rcode %s", qtypeName(q.qtype), q.name, dns.RcodeToString[resp.Rcode])
133+
metrics[q.resultKey] = nil
135134
continue
136135
}
137136

138137
if err := validateAnswer(resp.Answer, q.qtype, q.expect); err != nil {
139138
lastErr = fmt.Errorf("dns %s %s: %w", qtypeName(q.qtype), q.name, err)
139+
metrics[q.resultKey] = nil
140140
continue
141141
}
142142

143-
result.Metrics[q.resultKey] = rtt.Microseconds()
143+
v := rtt.Microseconds()
144+
metrics[q.resultKey] = &v
145+
succeeded++
144146
}
145147

146-
if len(result.Metrics) == len(c.queries) {
147-
result.Success = true
148-
} else {
149-
result.Success = false
150-
result.Err = lastErr
148+
return check.Result{
149+
Timestamp: time.Now(),
150+
Success: succeeded == len(c.queries),
151+
Err: lastErr,
152+
Metrics: metrics,
151153
}
152-
153-
return result
154154
}
155155

156156
// validateAnswer checks that at least one RR in the answer section matches

pkg/check/dns/dns_test.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func TestNew_WithTimeoutZero(t *testing.T) {
8484

8585
func TestDescribe_SingleQuery(t *testing.T) {
8686
queries := []queryConfig{
87-
{name: "router.risse.tv", qtype: dns.TypeA, expect: "192.168.73.1", resultKey: "router.risse.tv", dsName: "q0"},
87+
{name: "router.example.com", qtype: dns.TypeA, expect: "192.168.168.1", resultKey: "router.example.com", dsName: "q0"},
8888
}
8989
chk, err := New("127.0.0.1:53", queries)
9090
if err != nil {
@@ -101,8 +101,8 @@ func TestDescribe_SingleQuery(t *testing.T) {
101101
if m.DSName != "q0" {
102102
t.Errorf("expected DSName 'q0', got %q", m.DSName)
103103
}
104-
if m.ResultKey != "router.risse.tv" {
105-
t.Errorf("expected ResultKey 'router.risse.tv', got %q", m.ResultKey)
104+
if m.ResultKey != "router.example.com" {
105+
t.Errorf("expected ResultKey 'router.example.com', got %q", m.ResultKey)
106106
}
107107
if m.Unit != "ms" {
108108
t.Errorf("expected Unit 'ms', got %q", m.Unit)
@@ -403,9 +403,9 @@ func TestNormalizeFQDN_WithoutDot(t *testing.T) {
403403

404404
func TestValidateAnswer_AMatch(t *testing.T) {
405405
rrs := []dns.RR{
406-
&dns.A{Hdr: dns.RR_Header{Rrtype: dns.TypeA}, A: net.ParseIP("192.168.73.1")},
406+
&dns.A{Hdr: dns.RR_Header{Rrtype: dns.TypeA}, A: net.ParseIP("192.168.168.1")},
407407
}
408-
if err := validateAnswer(rrs, dns.TypeA, "192.168.73.1"); err != nil {
408+
if err := validateAnswer(rrs, dns.TypeA, "192.168.168.1"); err != nil {
409409
t.Errorf("expected match, got: %v", err)
410410
}
411411
}
@@ -414,7 +414,7 @@ func TestValidateAnswer_ANoMatch(t *testing.T) {
414414
rrs := []dns.RR{
415415
&dns.A{Hdr: dns.RR_Header{Rrtype: dns.TypeA}, A: net.ParseIP("10.0.0.1")},
416416
}
417-
if err := validateAnswer(rrs, dns.TypeA, "192.168.73.1"); err == nil {
417+
if err := validateAnswer(rrs, dns.TypeA, "192.168.168.1"); err == nil {
418418
t.Error("expected mismatch error")
419419
}
420420
}
@@ -430,18 +430,18 @@ func TestValidateAnswer_AAAAMatch(t *testing.T) {
430430

431431
func TestValidateAnswer_PTRMatchWithTrailingDot(t *testing.T) {
432432
rrs := []dns.RR{
433-
&dns.PTR{Hdr: dns.RR_Header{Rrtype: dns.TypePTR}, Ptr: "router.risse.tv."},
433+
&dns.PTR{Hdr: dns.RR_Header{Rrtype: dns.TypePTR}, Ptr: "router.example.com."},
434434
}
435-
if err := validateAnswer(rrs, dns.TypePTR, "router.risse.tv."); err != nil {
435+
if err := validateAnswer(rrs, dns.TypePTR, "router.example.com."); err != nil {
436436
t.Errorf("expected match, got: %v", err)
437437
}
438438
}
439439

440440
func TestValidateAnswer_PTRMatchWithoutTrailingDot(t *testing.T) {
441441
rrs := []dns.RR{
442-
&dns.PTR{Hdr: dns.RR_Header{Rrtype: dns.TypePTR}, Ptr: "router.risse.tv."},
442+
&dns.PTR{Hdr: dns.RR_Header{Rrtype: dns.TypePTR}, Ptr: "router.example.com."},
443443
}
444-
if err := validateAnswer(rrs, dns.TypePTR, "router.risse.tv"); err != nil {
444+
if err := validateAnswer(rrs, dns.TypePTR, "router.example.com"); err != nil {
445445
t.Errorf("expected match regardless of trailing dot, got: %v", err)
446446
}
447447
}
@@ -469,13 +469,13 @@ func TestRun_AQuery_Success(t *testing.T) {
469469
m.SetReply(r)
470470
m.Answer = append(m.Answer, &dns.A{
471471
Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
472-
A: net.ParseIP("192.168.73.1"),
472+
A: net.ParseIP("192.168.168.1"),
473473
})
474474
_ = w.WriteMsg(m)
475475
})
476476

477477
queries := []queryConfig{
478-
{name: "router.risse.tv", qtype: dns.TypeA, expect: "192.168.73.1", resultKey: "router.risse.tv", dsName: "q0"},
478+
{name: "router.example.com", qtype: dns.TypeA, expect: "192.168.168.1", resultKey: "router.example.com", dsName: "q0"},
479479
}
480480
chk, err := New(addr, queries)
481481
if err != nil {
@@ -486,8 +486,8 @@ func TestRun_AQuery_Success(t *testing.T) {
486486
if !result.Success {
487487
t.Errorf("expected success, got failure: %v", result.Err)
488488
}
489-
if result.Metrics["router.risse.tv"] <= 0 {
490-
t.Errorf("expected positive RTT, got %d", result.Metrics["router.risse.tv"])
489+
if p := result.Metrics["router.example.com"]; p == nil || *p <= 0 {
490+
t.Errorf("expected positive RTT, got %v", result.Metrics["router.example.com"])
491491
}
492492
if result.Timestamp.IsZero() {
493493
t.Error("expected non-zero timestamp")
@@ -500,13 +500,13 @@ func TestRun_PTRQuery_Success(t *testing.T) {
500500
m.SetReply(r)
501501
m.Answer = append(m.Answer, &dns.PTR{
502502
Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 60},
503-
Ptr: "router.risse.tv.",
503+
Ptr: "router.example.com.",
504504
})
505505
_ = w.WriteMsg(m)
506506
})
507507

508508
queries := []queryConfig{
509-
{name: "1.73.168.192.in-addr.arpa", qtype: dns.TypePTR, expect: "router.risse.tv.", resultKey: "1.73.168.192.in-addr.arpa", dsName: "q0"},
509+
{name: "1.168.168.192.in-addr.arpa", qtype: dns.TypePTR, expect: "router.example.com.", resultKey: "1.168.168.192.in-addr.arpa", dsName: "q0"},
510510
}
511511
chk, err := New(addr, queries)
512512
if err != nil {
@@ -552,20 +552,20 @@ func TestRun_MultipleQueries_AllSuccess(t *testing.T) {
552552
case dns.TypeA:
553553
m.Answer = append(m.Answer, &dns.A{
554554
Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
555-
A: net.ParseIP("192.168.73.1"),
555+
A: net.ParseIP("192.168.168.1"),
556556
})
557557
case dns.TypePTR:
558558
m.Answer = append(m.Answer, &dns.PTR{
559559
Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 60},
560-
Ptr: "router.risse.tv.",
560+
Ptr: "router.example.com.",
561561
})
562562
}
563563
_ = w.WriteMsg(m)
564564
})
565565

566566
queries := []queryConfig{
567-
{name: "router.risse.tv", qtype: dns.TypeA, expect: "192.168.73.1", resultKey: "router.risse.tv", dsName: "q0"},
568-
{name: "1.73.168.192.in-addr.arpa", qtype: dns.TypePTR, expect: "router.risse.tv.", resultKey: "1.73.168.192.in-addr.arpa", dsName: "q1"},
567+
{name: "router.example.com", qtype: dns.TypeA, expect: "192.168.168.1", resultKey: "router.example.com", dsName: "q0"},
568+
{name: "1.168.168.192.in-addr.arpa", qtype: dns.TypePTR, expect: "router.example.com.", resultKey: "1.168.168.192.in-addr.arpa", dsName: "q1"},
569569
}
570570
chk, err := New(addr, queries)
571571
if err != nil {
@@ -593,7 +593,7 @@ func TestRun_WrongAnswer_Failure(t *testing.T) {
593593
})
594594

595595
queries := []queryConfig{
596-
{name: "router.risse.tv", qtype: dns.TypeA, expect: "192.168.73.1", resultKey: "router.risse.tv", dsName: "q0"},
596+
{name: "router.example.com", qtype: dns.TypeA, expect: "192.168.168.1", resultKey: "router.example.com", dsName: "q0"},
597597
}
598598
chk, err := New(addr, queries)
599599
if err != nil {
@@ -640,7 +640,7 @@ func TestRun_PartialSuccess_Failure(t *testing.T) {
640640
if call == 1 {
641641
m.Answer = append(m.Answer, &dns.A{
642642
Hdr: dns.RR_Header{Name: r.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
643-
A: net.ParseIP("192.168.73.1"),
643+
A: net.ParseIP("192.168.168.1"),
644644
})
645645
} else {
646646
m.Rcode = dns.RcodeNameError
@@ -649,7 +649,7 @@ func TestRun_PartialSuccess_Failure(t *testing.T) {
649649
})
650650

651651
queries := []queryConfig{
652-
{name: "router.risse.tv", qtype: dns.TypeA, expect: "192.168.73.1", resultKey: "router.risse.tv", dsName: "q0"},
652+
{name: "router.example.com", qtype: dns.TypeA, expect: "192.168.168.1", resultKey: "router.example.com", dsName: "q0"},
653653
{name: "missing.example.com", qtype: dns.TypeA, expect: "5.6.7.8", resultKey: "missing.example.com", dsName: "q1"},
654654
}
655655
chk, err := New(addr, queries)
@@ -661,8 +661,14 @@ func TestRun_PartialSuccess_Failure(t *testing.T) {
661661
if result.Success {
662662
t.Error("expected failure when one query fails")
663663
}
664-
if len(result.Metrics) != 1 {
665-
t.Errorf("expected 1 successful metric, got %d", len(result.Metrics))
664+
if len(result.Metrics) != 2 {
665+
t.Errorf("expected 2 metrics (one nil, one non-nil), got %d", len(result.Metrics))
666+
}
667+
if result.Metrics["router.example.com"] == nil {
668+
t.Error("expected non-nil metric for successful query")
669+
}
670+
if result.Metrics["missing.example.com"] != nil {
671+
t.Error("expected nil metric for failed query")
666672
}
667673
}
668674

pkg/check/http/http.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,17 @@ func (c *Check) Describe() check.Descriptor {
119119

120120
// Run executes HTTP GET requests to all configured URLs and returns a Result.
121121
func (c *Check) Run(ctx context.Context) check.Result {
122-
result := check.Result{
123-
Timestamp: time.Now(),
124-
Metrics: make(map[string]int64),
125-
}
126-
122+
metrics := make(map[string]*int64, len(c.urls))
127123
var lastErr error
124+
succeeded := 0
128125

129126
for _, url := range c.urls {
130127
start := time.Now()
131128

132129
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
133130
if err != nil {
134131
lastErr = fmt.Errorf("failed to create request for %s: %w", url, err)
132+
metrics[url] = nil
135133
continue
136134
}
137135

@@ -140,21 +138,22 @@ func (c *Check) Run(ctx context.Context) check.Result {
140138

141139
if err != nil {
142140
lastErr = fmt.Errorf("request to %s failed: %w", url, err)
141+
metrics[url] = nil
143142
continue
144143
}
145144
resp.Body.Close()
146145

147-
result.Metrics[url] = elapsed.Microseconds()
146+
v := elapsed.Microseconds()
147+
metrics[url] = &v
148+
succeeded++
148149
}
149150

150-
if len(result.Metrics) == len(c.urls) {
151-
result.Success = true
152-
} else {
153-
result.Success = false
154-
result.Err = lastErr
151+
return check.Result{
152+
Timestamp: time.Now(),
153+
Success: succeeded == len(c.urls),
154+
Err: lastErr,
155+
Metrics: metrics,
155156
}
156-
157-
return result
158157
}
159158

160159
// Factory creates an HTTP Check from a config map.

pkg/check/http/http_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,8 @@ func TestRun_SingleURL_Success(t *testing.T) {
324324
if !result.Success {
325325
t.Errorf("expected success, got failure: %v", result.Err)
326326
}
327-
if result.Metrics[srv.URL] <= 0 {
328-
t.Errorf("expected positive response time, got %d", result.Metrics[srv.URL])
327+
if p := result.Metrics[srv.URL]; p == nil || *p <= 0 {
328+
t.Errorf("expected positive response time, got %v", result.Metrics[srv.URL])
329329
}
330330
if result.Timestamp.IsZero() {
331331
t.Error("expected non-zero timestamp")

pkg/check/ping/ping.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ func (p *Ping) Describe() check.Descriptor {
131131
// is stored in microseconds keyed by address string.
132132
func (p *Ping) Run(ctx context.Context) check.Result {
133133
now := time.Now()
134-
metrics := make(map[string]int64)
134+
metrics := make(map[string]*int64, len(p.addresses))
135135
var lastErr error
136+
succeeded := 0
136137

137138
timeoutSec := fmt.Sprintf("%.0f", p.timeout.Seconds())
138139
count := strconv.Itoa(p.count)
@@ -146,29 +147,25 @@ func (p *Ping) Run(ctx context.Context) check.Result {
146147

147148
if err := cmd.Run(); err != nil {
148149
lastErr = fmt.Errorf("ping %s: %w", a.address, err)
150+
metrics[a.resultKey] = nil
149151
continue
150152
}
151153

152154
latency, err := parseOutput(out.String())
153155
if err != nil {
154156
lastErr = fmt.Errorf("ping %s: %w", a.address, err)
157+
metrics[a.resultKey] = nil
155158
continue
156159
}
157160

158-
metrics[a.resultKey] = int64(latency.Microseconds())
159-
}
160-
161-
if len(metrics) == len(p.addresses) {
162-
return check.Result{
163-
Timestamp: now,
164-
Success: true,
165-
Metrics: metrics,
166-
}
161+
v := int64(latency.Microseconds())
162+
metrics[a.resultKey] = &v
163+
succeeded++
167164
}
168165

169166
return check.Result{
170167
Timestamp: now,
171-
Success: false,
168+
Success: succeeded == len(p.addresses),
172169
Err: lastErr,
173170
Metrics: metrics,
174171
}

pkg/check/ping/ping_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ func TestRun_Localhost(t *testing.T) {
356356
if !result.Success {
357357
t.Skipf("ping failed (may not have permission): %v", result.Err)
358358
}
359-
if result.Metrics["127.0.0.1"] <= 0 {
360-
t.Errorf("expected positive latency, got %d", result.Metrics["127.0.0.1"])
359+
if p := result.Metrics["127.0.0.1"]; p == nil || *p <= 0 {
360+
t.Errorf("expected positive latency, got %v", result.Metrics["127.0.0.1"])
361361
}
362362
}

pkg/check/result.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ type Result struct {
1313
Success bool
1414

1515
// Metrics holds named measurements from the check execution.
16-
// For example, a ping check might set {"latency_us": 1234.0}.
17-
// An empty or nil map is valid for checks that only report success/failure.
18-
Metrics map[string]int64
16+
// A nil pointer value for a key means the target was attempted but failed.
17+
// An absent key or nil map means no measurement was attempted.
18+
Metrics map[string]*int64
1919

2020
// Err holds any error encountered during check execution.
2121
// A non-nil Err generally corresponds to Success being false,

pkg/check/result_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ func TestResult_ZeroValue(t *testing.T) {
2222
}
2323

2424
func TestResult_WithMetrics(t *testing.T) {
25+
v := int64(12345)
2526
r := Result{
2627
Timestamp: time.Now(),
2728
Success: true,
28-
Metrics: map[string]int64{"latency_us": 12345},
29+
Metrics: map[string]*int64{"latency_us": &v},
2930
}
3031
if !r.Success {
3132
t.Error("expected success")
3233
}
33-
if v, ok := r.Metrics["latency_us"]; !ok || v != 12345 {
34-
t.Errorf("expected latency_us=12345, got %v", v)
34+
p, ok := r.Metrics["latency_us"]
35+
if !ok || p == nil || *p != 12345 {
36+
t.Errorf("expected latency_us=12345, got %v", p)
3537
}
3638
}

0 commit comments

Comments
 (0)