* Add metrics for domain expiration * Update grafana and prometheus versions and extend grafana dashboard with Domain expiration * feat(deps) update whois version --------- Co-authored-by: TwiN <twin@linux.com>
333 lines
16 KiB
Go
333 lines
16 KiB
Go
package metrics
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/TwiN/gatus/v5/config"
|
|
"github.com/TwiN/gatus/v5/config/endpoint"
|
|
"github.com/TwiN/gatus/v5/config/endpoint/dns"
|
|
"github.com/TwiN/gatus/v5/config/suite"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/testutil"
|
|
)
|
|
|
|
// TestInitializePrometheusMetrics tests metrics initialization with extraLabels.
|
|
// Note: Because of the global Prometheus registry, this test can only safely verify one label set per process.
|
|
// If the function is called with a different set of labels for the same metric, a panic will occur.
|
|
func TestInitializePrometheusMetrics(t *testing.T) {
|
|
cfgWithExtras := &config.Config{
|
|
Endpoints: []*endpoint.Endpoint{
|
|
{
|
|
Name: "TestEP",
|
|
Group: "G",
|
|
URL: "http://x/",
|
|
ExtraLabels: map[string]string{
|
|
"foo": "foo-val",
|
|
"hello": "world-val",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
reg := prometheus.NewRegistry()
|
|
InitializePrometheusMetrics(cfgWithExtras, reg)
|
|
// Metrics variables should be non-nil
|
|
if resultTotal == nil {
|
|
t.Error("resultTotal metric not initialized")
|
|
}
|
|
if resultDurationSeconds == nil {
|
|
t.Error("resultDurationSeconds metric not initialized")
|
|
}
|
|
if resultConnectedTotal == nil {
|
|
t.Error("resultConnectedTotal metric not initialized")
|
|
}
|
|
if resultCodeTotal == nil {
|
|
t.Error("resultCodeTotal metric not initialized")
|
|
}
|
|
if resultCertificateExpirationSeconds == nil {
|
|
t.Error("resultCertificateExpirationSeconds metric not initialized")
|
|
}
|
|
if resultDomainExpirationSeconds == nil {
|
|
t.Error("resultDomainExpirationSeconds metric not initialized")
|
|
}
|
|
if resultEndpointSuccess == nil {
|
|
t.Error("resultEndpointSuccess metric not initialized")
|
|
}
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Errorf("resultTotal.WithLabelValues panicked: %v", r)
|
|
}
|
|
}()
|
|
_ = resultTotal.WithLabelValues("k", "g", "n", "ty", "true", "fval", "hval")
|
|
}
|
|
|
|
// TestPublishMetricsForEndpoint_withExtraLabels ensures extraLabels are included in the exported metrics.
|
|
func TestPublishMetricsForEndpoint_withExtraLabels(t *testing.T) {
|
|
// Only test one label set per process due to Prometheus registry limits.
|
|
reg := prometheus.NewRegistry()
|
|
cfg := &config.Config{
|
|
Endpoints: []*endpoint.Endpoint{
|
|
{
|
|
Name: "ep-extra",
|
|
URL: "https://sample.com",
|
|
ExtraLabels: map[string]string{
|
|
"foo": "my-foo",
|
|
"bar": "my-bar",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
InitializePrometheusMetrics(cfg, reg)
|
|
|
|
ep := &endpoint.Endpoint{
|
|
Name: "ep-extra",
|
|
Group: "g1",
|
|
URL: "https://sample.com",
|
|
ExtraLabels: map[string]string{
|
|
"foo": "my-foo",
|
|
"bar": "my-bar",
|
|
},
|
|
}
|
|
result := &endpoint.Result{
|
|
HTTPStatus: 200,
|
|
Connected: true,
|
|
Duration: 2340 * time.Millisecond,
|
|
Success: true,
|
|
}
|
|
// Get labels in sorted order as per GetUniqueExtraMetricLabels
|
|
extraLabels := cfg.GetUniqueExtraMetricLabels()
|
|
PublishMetricsForEndpoint(ep, result, extraLabels)
|
|
|
|
expected := `
|
|
# HELP gatus_results_total Number of results per endpoint
|
|
# TYPE gatus_results_total counter
|
|
gatus_results_total{bar="my-bar",foo="my-foo",group="g1",key="g1_ep-extra",name="ep-extra",success="true",type="HTTP"} 1
|
|
`
|
|
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expected), "gatus_results_total")
|
|
if err != nil {
|
|
t.Error("metrics export does not include extraLabels as expected:", err)
|
|
}
|
|
}
|
|
|
|
func TestPublishMetricsForEndpoint(t *testing.T) {
|
|
reg := prometheus.NewRegistry()
|
|
InitializePrometheusMetrics(&config.Config{}, reg)
|
|
|
|
httpEndpoint := &endpoint.Endpoint{Name: "http-ep-name", Group: "http-ep-group", URL: "https://example.org"}
|
|
PublishMetricsForEndpoint(httpEndpoint, &endpoint.Result{
|
|
HTTPStatus: 200,
|
|
Connected: true,
|
|
Duration: 123 * time.Millisecond,
|
|
ConditionResults: []*endpoint.ConditionResult{
|
|
{Condition: "[STATUS] == 200", Success: true},
|
|
{Condition: "[CERTIFICATE_EXPIRATION] > 48h", Success: true},
|
|
{Condition: "[DOMAIN_EXPIRATION] > 24h", Success: true},
|
|
},
|
|
Success: true,
|
|
CertificateExpiration: 49 * time.Hour,
|
|
DomainExpiration: 25 * time.Hour,
|
|
}, []string{})
|
|
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_results_code_total Total number of results by code
|
|
# TYPE gatus_results_code_total counter
|
|
gatus_results_code_total{code="200",group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 1
|
|
# HELP gatus_results_connected_total Total number of results in which a connection was successfully established
|
|
# TYPE gatus_results_connected_total counter
|
|
gatus_results_connected_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 1
|
|
# HELP gatus_results_duration_seconds Duration of the request in seconds
|
|
# TYPE gatus_results_duration_seconds gauge
|
|
gatus_results_duration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 0.123
|
|
# HELP gatus_results_total Number of results per endpoint
|
|
# TYPE gatus_results_total counter
|
|
gatus_results_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",success="true",type="HTTP"} 1
|
|
# HELP gatus_results_certificate_expiration_seconds Number of seconds until the certificate expires
|
|
# TYPE gatus_results_certificate_expiration_seconds gauge
|
|
gatus_results_certificate_expiration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 176400
|
|
# HELP gatus_results_domain_expiration_seconds Number of seconds until the domain expires
|
|
# TYPE gatus_results_domain_expiration_seconds gauge
|
|
gatus_results_domain_expiration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 90000
|
|
# HELP gatus_results_endpoint_success Displays whether or not the endpoint was a success
|
|
# TYPE gatus_results_endpoint_success gauge
|
|
gatus_results_endpoint_success{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 1
|
|
`), "gatus_results_code_total", "gatus_results_connected_total", "gatus_results_duration_seconds", "gatus_results_total", "gatus_results_certificate_expiration_seconds", "gatus_results_endpoint_success")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
PublishMetricsForEndpoint(httpEndpoint, &endpoint.Result{
|
|
HTTPStatus: 200,
|
|
Connected: true,
|
|
Duration: 125 * time.Millisecond,
|
|
ConditionResults: []*endpoint.ConditionResult{
|
|
{Condition: "[STATUS] == 200", Success: true},
|
|
{Condition: "[CERTIFICATE_EXPIRATION] > 47h", Success: false},
|
|
{Condition: "[DOMAIN_EXPIRATION] > 24h", Success: true},
|
|
},
|
|
Success: false,
|
|
CertificateExpiration: 47 * time.Hour,
|
|
DomainExpiration: 24 * time.Hour,
|
|
}, []string{})
|
|
err = testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_results_code_total Total number of results by code
|
|
# TYPE gatus_results_code_total counter
|
|
gatus_results_code_total{code="200",group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 2
|
|
# HELP gatus_results_connected_total Total number of results in which a connection was successfully established
|
|
# TYPE gatus_results_connected_total counter
|
|
gatus_results_connected_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 2
|
|
# HELP gatus_results_duration_seconds Duration of the request in seconds
|
|
# TYPE gatus_results_duration_seconds gauge
|
|
gatus_results_duration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 0.125
|
|
# HELP gatus_results_total Number of results per endpoint
|
|
# TYPE gatus_results_total counter
|
|
gatus_results_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",success="false",type="HTTP"} 1
|
|
gatus_results_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",success="true",type="HTTP"} 1
|
|
# HELP gatus_results_certificate_expiration_seconds Number of seconds until the certificate expires
|
|
# TYPE gatus_results_certificate_expiration_seconds gauge
|
|
gatus_results_certificate_expiration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 169200
|
|
# HELP gatus_results_domain_expiration_seconds Number of seconds until the domain expires
|
|
# TYPE gatus_results_domain_expiration_seconds gauge
|
|
gatus_results_domain_expiration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 86400
|
|
# HELP gatus_results_endpoint_success Displays whether or not the endpoint was a success
|
|
# TYPE gatus_results_endpoint_success gauge
|
|
gatus_results_endpoint_success{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 0
|
|
`), "gatus_results_code_total", "gatus_results_connected_total", "gatus_results_duration_seconds", "gatus_results_total", "gatus_results_certificate_expiration_seconds", "gatus_results_endpoint_success")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
dnsEndpoint := &endpoint.Endpoint{
|
|
Name: "dns-ep-name", Group: "dns-ep-group", URL: "8.8.8.8", DNSConfig: &dns.Config{
|
|
QueryType: "A",
|
|
QueryName: "example.com.",
|
|
},
|
|
}
|
|
PublishMetricsForEndpoint(dnsEndpoint, &endpoint.Result{
|
|
DNSRCode: "NOERROR",
|
|
Connected: true,
|
|
Duration: 50 * time.Millisecond,
|
|
ConditionResults: []*endpoint.ConditionResult{
|
|
{Condition: "[DNS_RCODE] == NOERROR", Success: true},
|
|
},
|
|
Success: true,
|
|
}, []string{})
|
|
err = testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_results_code_total Total number of results by code
|
|
# TYPE gatus_results_code_total counter
|
|
gatus_results_code_total{code="200",group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 2
|
|
gatus_results_code_total{code="NOERROR",group="dns-ep-group",key="dns-ep-group_dns-ep-name",name="dns-ep-name",type="DNS"} 1
|
|
# HELP gatus_results_connected_total Total number of results in which a connection was successfully established
|
|
# TYPE gatus_results_connected_total counter
|
|
gatus_results_connected_total{group="dns-ep-group",key="dns-ep-group_dns-ep-name",name="dns-ep-name",type="DNS"} 1
|
|
gatus_results_connected_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 2
|
|
# HELP gatus_results_duration_seconds Duration of the request in seconds
|
|
# TYPE gatus_results_duration_seconds gauge
|
|
gatus_results_duration_seconds{group="dns-ep-group",key="dns-ep-group_dns-ep-name",name="dns-ep-name",type="DNS"} 0.05
|
|
gatus_results_duration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 0.125
|
|
# HELP gatus_results_total Number of results per endpoint
|
|
# TYPE gatus_results_total counter
|
|
gatus_results_total{group="dns-ep-group",key="dns-ep-group_dns-ep-name",name="dns-ep-name",success="true",type="DNS"} 1
|
|
gatus_results_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",success="false",type="HTTP"} 1
|
|
gatus_results_total{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",success="true",type="HTTP"} 1
|
|
# HELP gatus_results_certificate_expiration_seconds Number of seconds until the certificate expires
|
|
# TYPE gatus_results_certificate_expiration_seconds gauge
|
|
gatus_results_certificate_expiration_seconds{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 169200
|
|
# HELP gatus_results_endpoint_success Displays whether or not the endpoint was a success
|
|
# TYPE gatus_results_endpoint_success gauge
|
|
gatus_results_endpoint_success{group="dns-ep-group",key="dns-ep-group_dns-ep-name",name="dns-ep-name",type="DNS"} 1
|
|
gatus_results_endpoint_success{group="http-ep-group",key="http-ep-group_http-ep-name",name="http-ep-name",type="HTTP"} 0
|
|
`), "gatus_results_code_total", "gatus_results_connected_total", "gatus_results_duration_seconds", "gatus_results_total", "gatus_results_certificate_expiration_seconds", "gatus_results_endpoint_success")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPublishMetricsForSuite(t *testing.T) {
|
|
reg := prometheus.NewRegistry()
|
|
InitializePrometheusMetrics(&config.Config{}, reg)
|
|
|
|
testSuite := &suite.Suite{
|
|
Name: "test-suite",
|
|
Group: "test-group",
|
|
}
|
|
// Test successful suite execution
|
|
successResult := &suite.Result{
|
|
Success: true,
|
|
Duration: 5 * time.Second,
|
|
Name: "test-suite",
|
|
Group: "test-group",
|
|
}
|
|
PublishMetricsForSuite(testSuite, successResult, []string{})
|
|
|
|
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_suite_results_duration_seconds Duration of suite execution in seconds
|
|
# TYPE gatus_suite_results_duration_seconds gauge
|
|
gatus_suite_results_duration_seconds{group="test-group",key="test-group_test-suite",name="test-suite"} 5
|
|
# HELP gatus_suite_results_success Whether the suite execution was successful (1) or not (0)
|
|
# TYPE gatus_suite_results_success gauge
|
|
gatus_suite_results_success{group="test-group",key="test-group_test-suite",name="test-suite"} 1
|
|
# HELP gatus_suite_results_total Total number of suite executions
|
|
# TYPE gatus_suite_results_total counter
|
|
gatus_suite_results_total{group="test-group",key="test-group_test-suite",name="test-suite",success="true"} 1
|
|
`), "gatus_suite_results_duration_seconds", "gatus_suite_results_success", "gatus_suite_results_total")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
|
|
// Test failed suite execution
|
|
failureResult := &suite.Result{
|
|
Success: false,
|
|
Duration: 10 * time.Second,
|
|
Name: "test-suite",
|
|
Group: "test-group",
|
|
}
|
|
PublishMetricsForSuite(testSuite, failureResult, []string{})
|
|
|
|
err = testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_suite_results_duration_seconds Duration of suite execution in seconds
|
|
# TYPE gatus_suite_results_duration_seconds gauge
|
|
gatus_suite_results_duration_seconds{group="test-group",key="test-group_test-suite",name="test-suite"} 10
|
|
# HELP gatus_suite_results_success Whether the suite execution was successful (1) or not (0)
|
|
# TYPE gatus_suite_results_success gauge
|
|
gatus_suite_results_success{group="test-group",key="test-group_test-suite",name="test-suite"} 0
|
|
# HELP gatus_suite_results_total Total number of suite executions
|
|
# TYPE gatus_suite_results_total counter
|
|
gatus_suite_results_total{group="test-group",key="test-group_test-suite",name="test-suite",success="false"} 1
|
|
gatus_suite_results_total{group="test-group",key="test-group_test-suite",name="test-suite",success="true"} 1
|
|
`), "gatus_suite_results_duration_seconds", "gatus_suite_results_success", "gatus_suite_results_total")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPublishMetricsForSuite_NoGroup(t *testing.T) {
|
|
reg := prometheus.NewRegistry()
|
|
InitializePrometheusMetrics(&config.Config{}, reg)
|
|
|
|
testSuite := &suite.Suite{
|
|
Name: "no-group-suite",
|
|
Group: "",
|
|
}
|
|
result := &suite.Result{
|
|
Success: true,
|
|
Duration: 3 * time.Second,
|
|
Name: "no-group-suite",
|
|
Group: "",
|
|
}
|
|
PublishMetricsForSuite(testSuite, result, []string{})
|
|
|
|
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(`
|
|
# HELP gatus_suite_results_duration_seconds Duration of suite execution in seconds
|
|
# TYPE gatus_suite_results_duration_seconds gauge
|
|
gatus_suite_results_duration_seconds{group="",key="_no-group-suite",name="no-group-suite"} 3
|
|
# HELP gatus_suite_results_success Whether the suite execution was successful (1) or not (0)
|
|
# TYPE gatus_suite_results_success gauge
|
|
gatus_suite_results_success{group="",key="_no-group-suite",name="no-group-suite"} 1
|
|
# HELP gatus_suite_results_total Total number of suite executions
|
|
# TYPE gatus_suite_results_total counter
|
|
gatus_suite_results_total{group="",key="_no-group-suite",name="no-group-suite",success="true"} 1
|
|
`), "gatus_suite_results_duration_seconds", "gatus_suite_results_success", "gatus_suite_results_total")
|
|
if err != nil {
|
|
t.Errorf("Expected no errors but got: %v", err)
|
|
}
|
|
}
|