mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
155 lines
4.5 KiB
Go
155 lines
4.5 KiB
Go
package prometheusmetrics_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
dto "github.com/prometheus/client_model/go"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cdr.dev/slog/sloggers/slogtest"
|
|
"github.com/coder/coder/coderd/prometheusmetrics"
|
|
"github.com/coder/coder/codersdk/agentsdk"
|
|
"github.com/coder/coder/testutil"
|
|
)
|
|
|
|
const (
|
|
testWorkspaceName = "yogi-workspace"
|
|
testUsername = "yogi-bear"
|
|
testAgentName = "main-agent"
|
|
)
|
|
|
|
func TestUpdateMetrics_MetricsDoNotExpire(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// given
|
|
registry := prometheus.NewRegistry()
|
|
metricsAggregator, err := prometheusmetrics.NewMetricsAggregator(slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}), registry, time.Hour) // time.Hour, so metrics won't expire
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
|
t.Cleanup(cancelFunc)
|
|
|
|
closeFunc := metricsAggregator.Run(ctx)
|
|
t.Cleanup(closeFunc)
|
|
|
|
given1 := []agentsdk.AgentMetric{
|
|
{Name: "a_counter_one", Type: agentsdk.AgentMetricTypeCounter, Value: 1},
|
|
{Name: "b_counter_two", Type: agentsdk.AgentMetricTypeCounter, Value: 2},
|
|
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 3},
|
|
}
|
|
|
|
given2 := []agentsdk.AgentMetric{
|
|
{Name: "b_counter_two", Type: agentsdk.AgentMetricTypeCounter, Value: 4},
|
|
{Name: "d_gauge_four", Type: agentsdk.AgentMetricTypeGauge, Value: 6},
|
|
}
|
|
|
|
expected := []agentsdk.AgentMetric{
|
|
{Name: "a_counter_one", Type: agentsdk.AgentMetricTypeCounter, Value: 1},
|
|
{Name: "b_counter_two", Type: agentsdk.AgentMetricTypeCounter, Value: 4},
|
|
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 3},
|
|
{Name: "d_gauge_four", Type: agentsdk.AgentMetricTypeGauge, Value: 6},
|
|
}
|
|
|
|
// when
|
|
metricsAggregator.Update(ctx, testUsername, testWorkspaceName, testAgentName, given1)
|
|
metricsAggregator.Update(ctx, testUsername, testWorkspaceName, testAgentName, given2)
|
|
|
|
// then
|
|
require.Eventually(t, func() bool {
|
|
var actual []prometheus.Metric
|
|
metricsCh := make(chan prometheus.Metric)
|
|
|
|
done := make(chan struct{}, 1)
|
|
defer close(done)
|
|
go func() {
|
|
for m := range metricsCh {
|
|
actual = append(actual, m)
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
metricsAggregator.Collect(metricsCh)
|
|
close(metricsCh)
|
|
<-done
|
|
return verifyCollectedMetrics(t, expected, actual)
|
|
}, testutil.WaitMedium, testutil.IntervalSlow)
|
|
}
|
|
|
|
func verifyCollectedMetrics(t *testing.T, expected []agentsdk.AgentMetric, actual []prometheus.Metric) bool {
|
|
if len(expected) != len(actual) {
|
|
return false
|
|
}
|
|
|
|
// Metrics are expected to arrive in order
|
|
for i, e := range expected {
|
|
desc := actual[i].Desc()
|
|
assert.Contains(t, desc.String(), e.Name)
|
|
|
|
var d dto.Metric
|
|
err := actual[i].Write(&d)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "agent_name", *d.Label[0].Name)
|
|
require.Equal(t, testAgentName, *d.Label[0].Value)
|
|
require.Equal(t, "username", *d.Label[1].Name)
|
|
require.Equal(t, testUsername, *d.Label[1].Value)
|
|
require.Equal(t, "workspace_name", *d.Label[2].Name)
|
|
require.Equal(t, testWorkspaceName, *d.Label[2].Value)
|
|
|
|
if e.Type == agentsdk.AgentMetricTypeCounter {
|
|
require.Equal(t, e.Value, *d.Counter.Value)
|
|
} else if e.Type == agentsdk.AgentMetricTypeGauge {
|
|
require.Equal(t, e.Value, *d.Gauge.Value)
|
|
} else {
|
|
require.Failf(t, "unsupported type: %s", string(e.Type))
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestUpdateMetrics_MetricsExpire(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// given
|
|
registry := prometheus.NewRegistry()
|
|
metricsAggregator, err := prometheusmetrics.NewMetricsAggregator(slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}), registry, time.Millisecond)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
|
t.Cleanup(cancelFunc)
|
|
|
|
closeFunc := metricsAggregator.Run(ctx)
|
|
t.Cleanup(closeFunc)
|
|
|
|
given := []agentsdk.AgentMetric{
|
|
{Name: "a_counter_one", Type: agentsdk.AgentMetricTypeCounter, Value: 1},
|
|
}
|
|
|
|
// when
|
|
metricsAggregator.Update(ctx, testUsername, testWorkspaceName, testAgentName, given)
|
|
|
|
time.Sleep(time.Millisecond * 10) // Ensure that metric is expired
|
|
|
|
// then
|
|
require.Eventually(t, func() bool {
|
|
var actual []prometheus.Metric
|
|
metricsCh := make(chan prometheus.Metric)
|
|
|
|
done := make(chan struct{}, 1)
|
|
defer close(done)
|
|
go func() {
|
|
for m := range metricsCh {
|
|
actual = append(actual, m)
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
metricsAggregator.Collect(metricsCh)
|
|
close(metricsCh)
|
|
<-done
|
|
return len(actual) == 0
|
|
}, testutil.WaitShort, testutil.IntervalFast)
|
|
}
|