Alerting: Ensure state history client has external labels set (#113101)

* Ensure state history client has external labels set

* Run `make update-workspace`

* Add dep owner
This commit is contained in:
William Wernert
2025-10-28 11:35:54 -04:00
committed by GitHub
parent f3e7576f0c
commit 75fb832826
3 changed files with 86 additions and 1 deletions
+1 -1
View File
@@ -104,6 +104,7 @@ require (
github.com/grafana/grafana-google-sdk-go v0.4.2 // @grafana/partner-datasources
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group
github.com/grafana/grafana-plugin-sdk-go v0.281.0 // @grafana/plugins-platform-backend
github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 // @grafana/alerting-backend
github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs
github.com/grafana/nanogit v0.0.0-20250723104447-68f58f5ecec0 // indirect; @grafana/grafana-git-ui-sync-team
github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group
@@ -446,7 +447,6 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect
github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 // indirect
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
github.com/grafana/sqlds/v4 v4.2.7 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
+2
View File
@@ -668,6 +668,8 @@ func configureHistorianBackend(
if err != nil {
return nil, fmt.Errorf("invalid remote loki configuration: %w", err)
}
// Use external labels from state history config
lcfg.ExternalLabels = cfg.ExternalLabels
req := lokiclient.NewRequester()
logCtx := log.WithContextualAttributes(ctx, []any{"backend", "loki"})
lokiBackendLogger := log.New("ngalert.state.historian").FromContext(logCtx)
+83
View File
@@ -3,10 +3,17 @@ package ngalert
import (
"bytes"
"context"
"io"
"math/rand"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/loki/pkg/push"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
@@ -20,9 +27,11 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
acfakes "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
history_model "github.com/grafana/grafana/pkg/services/ngalert/state/historian/model"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
"github.com/grafana/grafana/pkg/setting"
@@ -151,6 +160,80 @@ func TestConfigureHistorianBackend(t *testing.T) {
require.NoError(t, err)
})
t.Run("Loki backend sends external labels in Record calls", func(t *testing.T) {
var receivedRequest *http.Request
var receivedBody []byte
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedRequest = r
body, _ := io.ReadAll(r.Body)
receivedBody = body
w.WriteHeader(http.StatusNoContent)
}))
defer server.Close()
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()
tracer := tracing.InitializeTracerForTest()
cfg := setting.UnifiedAlertingStateHistorySettings{
Enabled: true,
Backend: "loki",
LokiSettings: setting.UnifiedAlertingLokiSettings{
LokiReadURL: server.URL,
LokiWriteURL: server.URL,
},
ExternalLabels: map[string]string{
"test_label": "test_value",
"cluster": "prod",
},
}
ac := &acfakes.FakeRuleService{}
h, err := configureHistorianBackend(context.Background(), cfg, nil, nil, nil, met, logger, tracer, ac, nil, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, h)
rule := history_model.RuleMeta{
OrgID: 1,
UID: "test-rule-uid",
Group: "test-group",
NamespaceUID: "test-namespace",
Title: "Test Rule",
}
states := []state.StateTransition{
{
PreviousState: eval.Normal,
State: &state.State{
State: eval.Alerting,
Labels: data.Labels{"instance": "test-instance"},
LastEvaluationTime: time.Now(),
},
},
}
errCh := h.Record(context.Background(), rule, states)
err = <-errCh
require.NoError(t, err)
require.NotNil(t, receivedRequest, "Expected HTTP request to be sent to Loki")
require.Contains(t, receivedRequest.URL.Path, "/loki/api/v1/push")
// Loki uses snappy-compressed protobuf encoding
decompressed, err := snappy.Decode(nil, receivedBody)
require.NoError(t, err)
var req push.PushRequest
err = proto.Unmarshal(decompressed, &req)
require.NoError(t, err)
require.Len(t, req.Streams, 1, "Expected exactly one stream")
stream := req.Streams[0]
require.Contains(t, stream.Labels, `test_label="test_value"`)
require.Contains(t, stream.Labels, `cluster="prod"`)
require.Contains(t, stream.Labels, `from="state-history"`)
require.Contains(t, stream.Labels, `orgID="1"`)
})
t.Run("fail initialization if prometheus backend missing datasource UID", func(t *testing.T) {
met := metrics.NewHistorianMetrics(prometheus.NewRegistry(), metrics.Subsystem)
logger := log.NewNopLogger()