mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Collect agent SSH metrics (#7584)
This commit is contained in:
Generated
+21
@@ -5770,6 +5770,12 @@ const docTemplate = `{
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricLabel"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5789,6 +5795,21 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricLabel": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
Generated
+18
@@ -5076,6 +5076,12 @@
|
||||
"type": "object",
|
||||
"required": ["name", "type", "value"],
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.AgentMetricLabel"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5092,6 +5098,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricLabel": {
|
||||
"type": "object",
|
||||
"required": ["name", "value"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.AgentMetricType": {
|
||||
"type": "string",
|
||||
"enum": ["counter", "gauge"],
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
@@ -62,6 +63,30 @@ type annotatedMetric struct {
|
||||
|
||||
var _ prometheus.Collector = new(MetricsAggregator)
|
||||
|
||||
func (am *annotatedMetric) is(req updateRequest, m agentsdk.AgentMetric) bool {
|
||||
return am.username == req.username && am.workspaceName == req.workspaceName && am.agentName == req.agentName && am.Name == m.Name && slices.Equal(am.Labels, m.Labels)
|
||||
}
|
||||
|
||||
func (am *annotatedMetric) asPrometheus() (prometheus.Metric, error) {
|
||||
labels := make([]string, 0, len(agentMetricsLabels)+len(am.Labels))
|
||||
labelValues := make([]string, 0, len(agentMetricsLabels)+len(am.Labels))
|
||||
|
||||
labels = append(labels, agentMetricsLabels...)
|
||||
labelValues = append(labelValues, am.username, am.workspaceName, am.agentName)
|
||||
|
||||
for _, l := range am.Labels {
|
||||
labels = append(labels, l.Name)
|
||||
labelValues = append(labelValues, l.Value)
|
||||
}
|
||||
|
||||
desc := prometheus.NewDesc(am.Name, metricHelpForAgent, labels, nil)
|
||||
valueType, err := asPrometheusValueType(am.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return prometheus.MustNewConstMetric(desc, valueType, am.Value, labelValues...), nil
|
||||
}
|
||||
|
||||
func NewMetricsAggregator(logger slog.Logger, registerer prometheus.Registerer, duration time.Duration) (*MetricsAggregator, error) {
|
||||
metricsCleanupInterval := defaultMetricsCleanupInterval
|
||||
if duration > 0 {
|
||||
@@ -122,7 +147,7 @@ func (ma *MetricsAggregator) Run(ctx context.Context) func() {
|
||||
UpdateLoop:
|
||||
for _, m := range req.metrics {
|
||||
for i, q := range ma.queue {
|
||||
if q.username == req.username && q.workspaceName == req.workspaceName && q.agentName == req.agentName && q.Name == m.Name {
|
||||
if q.is(req, m) {
|
||||
ma.queue[i].AgentMetric.Value = m.Value
|
||||
ma.queue[i].expiryDate = req.timestamp.Add(ma.metricsCleanupInterval)
|
||||
continue UpdateLoop
|
||||
@@ -146,14 +171,12 @@ func (ma *MetricsAggregator) Run(ctx context.Context) func() {
|
||||
|
||||
output := make([]prometheus.Metric, 0, len(ma.queue))
|
||||
for _, m := range ma.queue {
|
||||
desc := prometheus.NewDesc(m.Name, metricHelpForAgent, agentMetricsLabels, nil)
|
||||
valueType, err := asPrometheusValueType(m.Type)
|
||||
promMetric, err := m.asPrometheus()
|
||||
if err != nil {
|
||||
ma.log.Error(ctx, "can't convert Prometheus value type", slog.F("name", m.Name), slog.F("type", m.Type), slog.F("value", m.Value), slog.Error(err))
|
||||
continue
|
||||
}
|
||||
constMetric := prometheus.MustNewConstMetric(desc, valueType, m.Value, m.username, m.workspaceName, m.agentName)
|
||||
output = append(output, constMetric)
|
||||
output = append(output, promMetric)
|
||||
}
|
||||
outputCh <- output
|
||||
close(outputCh)
|
||||
|
||||
@@ -44,14 +44,31 @@ func TestUpdateMetrics_MetricsDoNotExpire(t *testing.T) {
|
||||
|
||||
given2 := []agentsdk.AgentMetric{
|
||||
{Name: "b_counter_two", Type: agentsdk.AgentMetricTypeCounter, Value: 4},
|
||||
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 5},
|
||||
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 2, Labels: []agentsdk.AgentMetricLabel{
|
||||
{Name: "foobar", Value: "Foobaz"},
|
||||
{Name: "hello", Value: "world"},
|
||||
}},
|
||||
{Name: "d_gauge_four", Type: agentsdk.AgentMetricTypeGauge, Value: 6},
|
||||
}
|
||||
|
||||
commonLabels := []agentsdk.AgentMetricLabel{
|
||||
{Name: "agent_name", Value: testAgentName},
|
||||
{Name: "username", Value: testUsername},
|
||||
{Name: "workspace_name", Value: testWorkspaceName},
|
||||
}
|
||||
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},
|
||||
{Name: "a_counter_one", Type: agentsdk.AgentMetricTypeCounter, Value: 1, Labels: commonLabels},
|
||||
{Name: "b_counter_two", Type: agentsdk.AgentMetricTypeCounter, Value: 4, Labels: commonLabels},
|
||||
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 5, Labels: commonLabels},
|
||||
{Name: "c_gauge_three", Type: agentsdk.AgentMetricTypeGauge, Value: 2, Labels: []agentsdk.AgentMetricLabel{
|
||||
{Name: "agent_name", Value: testAgentName},
|
||||
{Name: "foobar", Value: "Foobaz"},
|
||||
{Name: "hello", Value: "world"},
|
||||
{Name: "username", Value: testUsername},
|
||||
{Name: "workspace_name", Value: testWorkspaceName},
|
||||
}},
|
||||
{Name: "d_gauge_four", Type: agentsdk.AgentMetricTypeGauge, Value: 6, Labels: commonLabels},
|
||||
}
|
||||
|
||||
// when
|
||||
@@ -83,7 +100,6 @@ func verifyCollectedMetrics(t *testing.T, expected []agentsdk.AgentMetric, actua
|
||||
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)
|
||||
@@ -92,24 +108,31 @@ func verifyCollectedMetrics(t *testing.T, expected []agentsdk.AgentMetric, actua
|
||||
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)
|
||||
require.Equal(t, e.Value, d.Counter.GetValue())
|
||||
} else if e.Type == agentsdk.AgentMetricTypeGauge {
|
||||
require.Equal(t, e.Value, *d.Gauge.Value)
|
||||
require.Equal(t, e.Value, d.Gauge.GetValue())
|
||||
} else {
|
||||
require.Failf(t, "unsupported type: %s", string(e.Type))
|
||||
}
|
||||
|
||||
dtoLabels := asMetricAgentLabels(d.GetLabel())
|
||||
require.Equal(t, e.Labels, dtoLabels, d.String())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func asMetricAgentLabels(dtoLabels []*dto.LabelPair) []agentsdk.AgentMetricLabel {
|
||||
metricLabels := make([]agentsdk.AgentMetricLabel, 0, len(dtoLabels))
|
||||
for _, dtoLabel := range dtoLabels {
|
||||
metricLabels = append(metricLabels, agentsdk.AgentMetricLabel{
|
||||
Name: dtoLabel.GetName(),
|
||||
Value: dtoLabel.GetValue(),
|
||||
})
|
||||
}
|
||||
return metricLabels
|
||||
}
|
||||
|
||||
func TestUpdateMetrics_MetricsExpire(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user