Files

153 lines
3.5 KiB
Go

package alertmanager
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"time"
"github.com/grafana/e2e"
)
const (
defaultLokiImage = "grafana/loki:latest"
lokiBinary = "/usr/bin/loki"
lokiHTTPPort = 3100
)
// GetDefaultImage returns the Docker image to use to run the Loki..
func GetLokiImage() string {
if img := os.Getenv("LOKI_IMAGE"); img != "" {
return img
}
return defaultLokiImage
}
type LokiService struct {
*e2e.HTTPService
}
func NewLokiService(name string, flags, envVars map[string]string) *LokiService {
svc := &LokiService{
HTTPService: e2e.NewHTTPService(
name,
GetLokiImage(),
e2e.NewCommandWithoutEntrypoint(lokiBinary, e2e.BuildArgs(flags)...),
e2e.NewHTTPReadinessProbe(lokiHTTPPort, "/ready", 200, 299),
lokiHTTPPort,
),
}
svc.SetEnvVars(envVars)
return svc
}
type LokiClient struct {
c http.Client
u *url.URL
}
func NewLokiClient(u string) (*LokiClient, error) {
pu, err := url.Parse(u)
if err != nil {
return nil, err
}
return &LokiClient{
c: http.Client{},
u: pu,
}, nil
}
type LokiQueryResponse struct {
Status string `json:"status"`
Data struct {
ResultType string `json:"resultType"`
Result []struct {
Stream struct {
Condition string `json:"condition"`
Current string `json:"current"`
DashboardUID string `json:"dashboardUID"`
Fingerprint string `json:"fingerprint"`
FolderUID string `json:"folderUID"`
From string `json:"from"`
Group string `json:"group"`
LabelsAlertname string `json:"labels_alertname"`
LabelsGrafanaFolder string `json:"labels_grafana_folder"`
OrgID string `json:"orgID"`
PanelID string `json:"panelID"`
Previous string `json:"previous"`
RuleID string `json:"ruleID"`
RuleTitle string `json:"ruleTitle"`
RuleUID string `json:"ruleUID"`
SchemaVersion string `json:"schemaVersion"`
ServiceName string `json:"service_name"`
ValuesB string `json:"values_B"`
ValuesC string `json:"values_C"`
} `json:"stream"`
Values [][]string `json:"values"`
} `json:"result"`
}
}
type AlertState string
const (
AlertStateNormal AlertState = "Normal"
AlertStatePending AlertState = "Pending"
AlertStateAlerting AlertState = "Alerting"
)
type AlertStateResponse struct {
State AlertState
Timestamp time.Time
}
// GetCurrentAlertState fetches the current alert state from loki
func (c *LokiClient) GetCurrentAlertState() (*AlertStateResponse, error) {
u := c.u.ResolveReference(&url.URL{Path: "/loki/api/v1/query_range"})
vs := url.Values{}
vs.Add("query", `{from="state-history"} | json`)
vs.Add("since", "60s")
u.RawQuery = vs.Encode()
resp, err := c.c.Get(u.String())
if err != nil {
return nil, err
}
//nolint:errcheck
defer resp.Body.Close()
res := LokiQueryResponse{}
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
return nil, err
}
if res.Status != "success" {
return nil, fmt.Errorf("failed to query state from loki")
}
if len(res.Data.Result) == 0 {
return nil, fmt.Errorf("empty result from loki")
}
r := res.Data.Result[0]
it, err := strconv.ParseInt(r.Values[0][0], 10, 0)
if err != nil {
return nil, fmt.Errorf("failed to parse timestamp: %v", err)
}
return &AlertStateResponse{
State: AlertState(r.Stream.Current),
Timestamp: time.Unix(0, it),
}, nil
}