Alerting: Add client configuration for remote Loki historian backend and test connection (#61114)

* Create loki client type and ping method

* Expose TestConnection on client

* Configure and ping Loki URL

* Close response body reader if present

* Add 30 second timeout

* Remove duplicate close
This commit is contained in:
Alexander Weaver 2023-01-17 13:58:52 -06:00 committed by GitHub
parent f6e3252c00
commit 1ac89ea040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 10 deletions

View File

@ -387,7 +387,15 @@ func configureHistorianBackend(cfg setting.UnifiedAlertingStateHistorySettings,
return historian.NewAnnotationBackend(ar, ds), nil return historian.NewAnnotationBackend(ar, ds), nil
} }
if cfg.Backend == "loki" { if cfg.Backend == "loki" {
return historian.NewRemoteLokiBackend(), nil baseURL, err := url.Parse(cfg.LokiRemoteURL)
if err != nil {
return nil, fmt.Errorf("failed to parse remote loki URL: %w", err)
}
backend := historian.NewRemoteLokiBackend(baseURL)
if err := backend.TestConnection(); err != nil {
return nil, fmt.Errorf("failed to ping the remote loki historian: %w", err)
}
return backend, nil
} }
if cfg.Backend == "sql" { if cfg.Backend == "sql" {
return historian.NewSqlBackend(), nil return historian.NewSqlBackend(), nil

View File

@ -2,21 +2,35 @@ package historian
import ( import (
"context" "context"
"net/url"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/state"
) )
type remoteLokiClient interface {
ping() error
}
type RemoteLokiBackend struct { type RemoteLokiBackend struct {
client remoteLokiClient
log log.Logger log log.Logger
} }
func NewRemoteLokiBackend() *RemoteLokiBackend { func NewRemoteLokiBackend(url *url.URL) *RemoteLokiBackend {
logger := log.New("ngalert.state.historian", "backend", "loki")
return &RemoteLokiBackend{ return &RemoteLokiBackend{
log: log.New("ngalert.state.historian"), client: newLokiClient(url, logger),
log: logger,
} }
} }
func (h *RemoteLokiBackend) RecordStatesAsync(ctx context.Context, _ *models.AlertRule, _ []state.StateTransition) { func (h *RemoteLokiBackend) TestConnection() error {
return h.client.ping()
}
func (h *RemoteLokiBackend) RecordStatesAsync(ctx context.Context, _ *models.AlertRule, _ []state.StateTransition) {
logger := h.log.FromContext(ctx)
logger.Debug("Remote Loki state history backend was called with states")
} }

View File

@ -0,0 +1,54 @@
package historian
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/grafana/grafana/pkg/infra/log"
)
const defaultClientTimeout = 30 * time.Second
type httpLokiClient struct {
client http.Client
url *url.URL
log log.Logger
}
func newLokiClient(u *url.URL, logger log.Logger) *httpLokiClient {
return &httpLokiClient{
client: http.Client{
Timeout: defaultClientTimeout,
},
url: u,
log: logger.New("protocol", "http"),
}
}
func (c *httpLokiClient) ping() error {
uri := c.url.JoinPath("/loki/api/v1/status/buildinfo")
req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
res, err := c.client.Do(req)
if res != nil {
defer func() {
if err := res.Body.Close(); err != nil {
c.log.Warn("Failed to close response body", "err", err)
}
}()
}
if err != nil {
return fmt.Errorf("error sending request: %w", err)
}
if res.StatusCode < 200 || res.StatusCode >= 300 {
return fmt.Errorf("request to the loki buildinfo endpoint returned a non-200 status code: %d", res.StatusCode)
}
c.log.Debug("Request to Loki buildinfo endpoint succeeded", "status", res.StatusCode)
return nil
}

View File

@ -103,6 +103,7 @@ type UnifiedAlertingReservedLabelSettings struct {
type UnifiedAlertingStateHistorySettings struct { type UnifiedAlertingStateHistorySettings struct {
Enabled bool Enabled bool
Backend string Backend string
LokiRemoteURL string
} }
// IsEnabled returns true if UnifiedAlertingSettings.Enabled is either nil or true. // IsEnabled returns true if UnifiedAlertingSettings.Enabled is either nil or true.
@ -315,6 +316,7 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error {
uaCfgStateHistory := UnifiedAlertingStateHistorySettings{ uaCfgStateHistory := UnifiedAlertingStateHistorySettings{
Enabled: stateHistory.Key("enabled").MustBool(stateHistoryDefaultEnabled), Enabled: stateHistory.Key("enabled").MustBool(stateHistoryDefaultEnabled),
Backend: stateHistory.Key("backend").MustString("annotations"), Backend: stateHistory.Key("backend").MustString("annotations"),
LokiRemoteURL: stateHistory.Key("loki_remote_url").MustString(""),
} }
uaCfg.StateHistory = uaCfgStateHistory uaCfg.StateHistory = uaCfgStateHistory