Files
nip/integration_metrics_test.go
Brian Cunnie 0fc3c81641 Replace ambiguous metric, "Answered Queries"
I've always been uncomfortable with the metric "Answered Queries" — it
implies that we don't answer all the queries. But we do answer all the
queries!

What the metric meant is "the number of DNS responses that we send that
have one or more records in the ANSWER section".

The new metric is "Answer ≥ 1". Not great, but better than before.
2024-11-19 09:25:10 -08:00

240 lines
10 KiB
Go

package main_test
import (
"fmt"
"os/exec"
"strconv"
"strings"
"time"
"xip/xip"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
)
var _ = Describe("IntegrationMetrics", func() {
When("the server is queried", func() {
// One big `It()` block because these tests cannot be run in parallel (singleton)
It("should update metrics", func() {
var actualMetrics xip.Metrics
expectedMetrics := getMetrics(port)
// A updates .Queries, UDPQueries .AnsweredQueries, .AnsweredAQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredAQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost 127.0.0.1.sslip.io +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// A updates .Queries, TCPQueries .AnsweredQueries, .AnsweredAQueries
expectedMetrics.Queries++
expectedMetrics.TCPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredAQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
// "+vc" runs the lookup over TCP, not UDP
actualMetrics = digAndGetMetrics("@localhost 127.0.0.1.sslip.io +short +vc -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// A (non-existent) record updates .Queries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// A blocked updates .Queries, .AnsweredQueries, .AnsweredBlockedQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredBlockedQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
dig("@localhost bank-of-raiffeisen.127.0.0.1.sslip.io +short -p " + strconv.Itoa(port))
actualMetrics = getMetrics(port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// AAAA updates .Queries, .AnsweredQueries, .AnsweredAAAAQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredAAAAQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost 2600--.sslip.io aaaa +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// AAAA (non-existent) updates .Queries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io aaaa +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// MX (customized) updates .Queries, .AnsweredQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost sslip.io mx +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// MX updates .Queries, AnsweredQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io mx +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// NS updates .Queries, AnsweredQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io ns +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// NS DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
// DNS-01 challenges don't count as successful because we're not authoritative; we're delegating
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost _acme-challenge.fe80--.sslip.io NS +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// Always successful: SOA
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
dig("@localhost non-existent.sslip.io soa +short -p " + strconv.Itoa(port))
actualMetrics = getMetrics(port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// TXT sslip.io (customized) updates .Queries, .AnsweredQueries,
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost sslip.io txt +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// TXT sslip.io (non-existent) updates .Queries, .AnsweredQueries,
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost non-existent.sslip.io txt +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// TXT ip.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTSrcIPQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredTXTSrcIPQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost ip.sslip.io txt +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// TXT version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredTXTVersionQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredTXTVersionQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost version.status.sslip.io txt +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv4
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredPTRQueriesIPv4++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost 1.2.3.4.in-addr.arpa ptr +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// PTR version.sslip.io updates .Queries, .AnsweredQueries, .AnsweredPTRQueriesIPv6
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredQueries++
expectedMetrics.AnsweredPTRQueriesIPv6++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost 2.a.b.b.4.0.2.9.a.e.e.6.e.c.4.1.0.f.9.6.0.0.1.0.6.4.6.0.1.0.6.2.ip6.arpa ptr +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
// TXT DNS-01 challenge record updates .Queries, .AnsweredNSDNS01ChallengeQueries
expectedMetrics.Queries++
expectedMetrics.UDPQueries++
expectedMetrics.AnsweredNSDNS01ChallengeQueries++
expectedMetrics = bumpExpectedToAccountForMetricsQuery(expectedMetrics)
actualMetrics = digAndGetMetrics("@localhost _acme-challenge.fe80--.sslip.io txt +short -p "+strconv.Itoa(port), port)
Expect(expectedMetrics.MostlyEquals(actualMetrics)).To(BeTrue())
})
})
})
// bumpExpectedToAccountForMetricsQuery takes into account that
// digging for the metrics endpoint affects the metrics. It's like
// the Heisenberg uncertainty principle (observing changes the values)
func bumpExpectedToAccountForMetricsQuery(metrics xip.Metrics) xip.Metrics {
metrics.Queries++
metrics.UDPQueries++
metrics.AnsweredQueries++
return metrics
}
func digAndGetMetrics(digArgs string, port int) xip.Metrics {
dig(digArgs)
return getMetrics(port)
}
func dig(digArgs string) {
digCmd := exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err := Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred())
Eventually(digSession, 1).Should(Exit(0))
}
func getMetrics(port int) (m xip.Metrics) {
digArgs := "@localhost metrics.status.sslip.io txt +short -p " + strconv.Itoa(port)
digCmd := exec.Command("dig", strings.Split(digArgs, " ")...)
stdout, err := digCmd.Output()
Expect(err).ToNot(HaveOccurred())
var uptime int
var junk string
var greaterThanOrEqualsUnicode string
_, err = fmt.Sscanf(string(stdout),
"\"Uptime: %d\"\n"+
"\"Blocklist: %s %s %s\n"+
"\"Queries: %d (%s\n"+ // %s "swallows" the `/s"` at the end
"\"TCP/UDP: %d/%d\"\n"+
"\"Answer %s 1: %d (%s\n"+ // %s "swallows" the `/s"` at the end
"\"A: %d\"\n"+
"\"AAAA: %d\"\n"+
"\"TXT Source: %d\"\n"+
"\"TXT Version: %d\"\n"+
"\"PTR IPv4/IPv6: %d/%d\"\n"+
"\"NS DNS-01: %d\"\n"+
"\"Blocked: %d\"\n",
&uptime,
&junk, &junk, &junk,
&m.Queries, &junk,
&m.TCPQueries, &m.UDPQueries,
&greaterThanOrEqualsUnicode, &m.AnsweredQueries, &junk,
&m.AnsweredAQueries,
&m.AnsweredAAAAQueries,
&m.AnsweredTXTSrcIPQueries,
&m.AnsweredTXTVersionQueries,
&m.AnsweredPTRQueriesIPv4, &m.AnsweredPTRQueriesIPv6,
&m.AnsweredNSDNS01ChallengeQueries,
&m.AnsweredBlockedQueries,
)
Expect(err).ToNot(HaveOccurred())
m.Start = time.Now().Add(-time.Duration(uptime) * time.Second)
//_, err = fmt.Fscanf(digSession.Out, "queries: %d", &m.Queries)
return m
}