Alerting: support basic auth for the state history loki client (#61696)

This commit is contained in:
Jean-Philippe Quéméner 2023-01-18 20:24:40 +01:00 committed by GitHub
parent 7f4b2fcb9c
commit 44b11d3228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 13 deletions

View File

@ -391,7 +391,12 @@ func configureHistorianBackend(cfg setting.UnifiedAlertingStateHistorySettings,
if err != nil {
return nil, fmt.Errorf("failed to parse remote loki URL: %w", err)
}
backend := historian.NewRemoteLokiBackend(baseURL)
backend := historian.NewRemoteLokiBackend(historian.LokiConfig{
Url: baseURL,
BasicAuthUser: cfg.LokiBasicAuthUsername,
BasicAuthPassword: cfg.LokiBasicAuthPassword,
TenantID: cfg.LokiTenantID,
})
if err := backend.TestConnection(); err != nil {
return nil, fmt.Errorf("failed to ping the remote loki historian: %w", err)
}

View File

@ -2,7 +2,6 @@ package historian
import (
"context"
"net/url"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/models"
@ -18,10 +17,10 @@ type RemoteLokiBackend struct {
log log.Logger
}
func NewRemoteLokiBackend(url *url.URL) *RemoteLokiBackend {
func NewRemoteLokiBackend(cfg LokiConfig) *RemoteLokiBackend {
logger := log.New("ngalert.state.historian", "backend", "loki")
return &RemoteLokiBackend{
client: newLokiClient(url, logger),
client: newLokiClient(cfg, logger),
log: logger,
}
}

View File

@ -11,25 +11,41 @@ import (
const defaultClientTimeout = 30 * time.Second
type LokiConfig struct {
Url *url.URL
BasicAuthUser string
BasicAuthPassword string
TenantID string
}
type httpLokiClient struct {
client http.Client
url *url.URL
cfg LokiConfig
log log.Logger
}
func newLokiClient(u *url.URL, logger log.Logger) *httpLokiClient {
func newLokiClient(cfg LokiConfig, logger log.Logger) *httpLokiClient {
return &httpLokiClient{
client: http.Client{
Timeout: defaultClientTimeout,
},
url: u,
cfg: cfg,
log: logger.New("protocol", "http"),
}
}
func (c *httpLokiClient) ping() error {
uri := c.url.JoinPath("/loki/api/v1/status/buildinfo")
uri := c.cfg.Url.JoinPath("/loki/api/v1/labels")
req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
if c.cfg.BasicAuthUser != "" || c.cfg.BasicAuthPassword != "" {
req.SetBasicAuth(c.cfg.BasicAuthUser, c.cfg.BasicAuthPassword)
}
if c.cfg.TenantID != "" {
req.Header.Add("X-Scope-OrgID", c.cfg.TenantID)
}
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
@ -47,8 +63,8 @@ func (c *httpLokiClient) ping() error {
}
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)
return fmt.Errorf("ping request to loki endpoint returned a non-200 status code: %d", res.StatusCode)
}
c.log.Debug("Request to Loki buildinfo endpoint succeeded", "status", res.StatusCode)
c.log.Debug("Ping request to Loki endpoint succeeded", "status", res.StatusCode)
return nil
}

View File

@ -0,0 +1,36 @@
package historian
import (
"net/url"
"testing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/stretchr/testify/require"
)
// This function can be used for local testing, just remove the skip call.
func TestLokiHTTPClient(t *testing.T) {
t.Skip()
url, err := url.Parse("https://logs-prod-eu-west-0.grafana.net")
require.NoError(t, err)
client := newLokiClient(LokiConfig{
Url: url,
}, log.NewNopLogger())
// Unauthorized request should fail against Grafana Cloud.
err = client.ping()
require.Error(t, err)
client.cfg.BasicAuthUser = "<your_username>"
client.cfg.BasicAuthPassword = "<your_password>"
// When running on prem, you might need to set the tenant id,
// so the x-scope-orgid header is set.
// client.cfg.TenantID = "<your_tenant_id>"
// Authorized request should fail against Grafana Cloud.
err = client.ping()
require.NoError(t, err)
}

View File

@ -104,6 +104,11 @@ type UnifiedAlertingStateHistorySettings struct {
Enabled bool
Backend string
LokiRemoteURL string
LokiTenantID string
// LokiBasicAuthUsername and LokiBasicAuthPassword are used for basic auth
// if one of them is set.
LokiBasicAuthPassword string
LokiBasicAuthUsername string
}
// IsEnabled returns true if UnifiedAlertingSettings.Enabled is either nil or true.
@ -314,9 +319,12 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error {
stateHistory := iniFile.Section("unified_alerting.state_history")
uaCfgStateHistory := UnifiedAlertingStateHistorySettings{
Enabled: stateHistory.Key("enabled").MustBool(stateHistoryDefaultEnabled),
Backend: stateHistory.Key("backend").MustString("annotations"),
LokiRemoteURL: stateHistory.Key("loki_remote_url").MustString(""),
Enabled: stateHistory.Key("enabled").MustBool(stateHistoryDefaultEnabled),
Backend: stateHistory.Key("backend").MustString("annotations"),
LokiRemoteURL: stateHistory.Key("loki_remote_url").MustString(""),
LokiTenantID: stateHistory.Key("loki_tenant_id").MustString(""),
LokiBasicAuthUsername: stateHistory.Key("loki_basic_auth_username").MustString(""),
LokiBasicAuthPassword: stateHistory.Key("loki_basic_auth_password").MustString(""),
}
uaCfg.StateHistory = uaCfgStateHistory