mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 07:52:21 +08:00

What is this feature? Adds an API endpoint to create alert rules with mimirtool: - POST /convert/prometheus/config/v1/rules/{NamespaceTitle} - Accepts a single rule group in a Prometheus YAML format and creates or updates a Grafana rule group from it. The endpoint uses the conversion package from #100224. Key parts The API works similarly to the provisioning API. If the rule does not exist, it will be created, otherwise updated. Any rules not present in the new group will be deleted, ensuring the group is fully synchronized with the provided configuration. Since the API works with namespace titles (folders), the handler automatically creates a folder in the root based on the provided title if it does not exist. It also requires a special header, X-Grafana-Alerting-Datasource-UID. This header specifies which datasource to use for the new rules. If the rule group's evaluation interval is not specified, it uses the DefaultRuleEvaluationInterval from settings.
1169 lines
34 KiB
Go
1169 lines
34 KiB
Go
package alerting
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/grafana/grafana/pkg/api"
|
|
"github.com/grafana/grafana/pkg/expr"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/folder"
|
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
"github.com/grafana/grafana/pkg/services/quota"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
const defaultAlertmanagerConfigJSON = `
|
|
{
|
|
"template_files": null,
|
|
"alertmanager_config": {
|
|
"route": {
|
|
"receiver": "grafana-default-email",
|
|
"group_by": ["grafana_folder", "alertname"]
|
|
},
|
|
"receivers": [{
|
|
"name": "grafana-default-email",
|
|
"grafana_managed_receiver_configs": [{
|
|
"uid": "",
|
|
"name": "email receiver",
|
|
"type": "email",
|
|
"disableResolveMessage": false,
|
|
"settings": {
|
|
"addresses": "\u003cexample@email.com\u003e"
|
|
},
|
|
"secureFields": {}
|
|
}]
|
|
}]
|
|
}
|
|
}
|
|
`
|
|
|
|
type Response struct {
|
|
Message string `json:"message"`
|
|
TraceID string `json:"traceID"`
|
|
}
|
|
|
|
func getRequest(t *testing.T, url string, expStatusCode int) *http.Response {
|
|
t.Helper()
|
|
// nolint:gosec
|
|
resp, err := http.Get(url)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
})
|
|
if expStatusCode != resp.StatusCode {
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
t.Fatal(string(b))
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func postRequest(t *testing.T, url string, body string, expStatusCode int) *http.Response {
|
|
t.Helper()
|
|
buf := bytes.NewReader([]byte(body))
|
|
// nolint:gosec
|
|
resp, err := http.Post(url, "application/json", buf)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
})
|
|
if expStatusCode != resp.StatusCode {
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
t.Log(string(b))
|
|
require.Equal(t, expStatusCode, resp.StatusCode)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func getBody(t *testing.T, body io.ReadCloser) string {
|
|
t.Helper()
|
|
b, err := io.ReadAll(body)
|
|
require.NoError(t, err)
|
|
return string(b)
|
|
}
|
|
|
|
type ruleMutator func(r *apimodels.PostableExtendedRuleNode)
|
|
|
|
func alertRuleGen(mutators ...ruleMutator) func() apimodels.PostableExtendedRuleNode {
|
|
return func() apimodels.PostableExtendedRuleNode {
|
|
forDuration := model.Duration(10 * time.Second)
|
|
rule := apimodels.PostableExtendedRuleNode{
|
|
ApiRuleNode: &apimodels.ApiRuleNode{
|
|
For: &forDuration,
|
|
Labels: map[string]string{"label1": "val1"},
|
|
Annotations: map[string]string{"annotation1": "val1"},
|
|
},
|
|
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
|
Title: fmt.Sprintf("rule-%s", util.GenerateShortUID()),
|
|
Condition: "A",
|
|
Data: []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(time.Duration(5) * time.Hour),
|
|
To: apimodels.Duration(time.Duration(3) * time.Hour),
|
|
},
|
|
DatasourceUID: expr.DatasourceUID,
|
|
Model: json.RawMessage(`{
|
|
"type": "math",
|
|
"expression": "2 + 3 > 1"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, mutator := range mutators {
|
|
mutator(&rule)
|
|
}
|
|
return rule
|
|
}
|
|
}
|
|
|
|
func withDatasourceQuery(uid string) func(r *apimodels.PostableExtendedRuleNode) {
|
|
data := []apimodels.AlertQuery{
|
|
{
|
|
RefID: "A",
|
|
RelativeTimeRange: apimodels.RelativeTimeRange{
|
|
From: apimodels.Duration(600 * time.Second),
|
|
To: 0,
|
|
},
|
|
DatasourceUID: uid,
|
|
Model: json.RawMessage(fmt.Sprintf(`{
|
|
"refId": "A",
|
|
"hide": false,
|
|
"datasource": {
|
|
"type": "testdata",
|
|
"uid": "%s"
|
|
},
|
|
"scenarioId": "random_walk",
|
|
"seriesCount": 5,
|
|
"labels": "series=series-$seriesIndex"
|
|
}`, uid)),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DatasourceUID: expr.DatasourceType,
|
|
Model: json.RawMessage(`{
|
|
"type": "reduce",
|
|
"reducer": "last",
|
|
"expression": "A"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "C",
|
|
DatasourceUID: expr.DatasourceType,
|
|
Model: json.RawMessage(`{
|
|
"refId": "C",
|
|
"type": "threshold",
|
|
"conditions": [
|
|
{
|
|
"type": "query",
|
|
"evaluator": {
|
|
"params": [
|
|
0
|
|
],
|
|
"type": "gt"
|
|
}
|
|
}
|
|
],
|
|
"expression": "B"
|
|
}`),
|
|
},
|
|
}
|
|
|
|
return func(r *apimodels.PostableExtendedRuleNode) {
|
|
r.GrafanaManagedAlert.Data = data
|
|
r.GrafanaManagedAlert.Condition = "C"
|
|
}
|
|
}
|
|
|
|
func generateAlertRuleGroup(rulesCount int, gen func() apimodels.PostableExtendedRuleNode) apimodels.PostableRuleGroupConfig {
|
|
rules := make([]apimodels.PostableExtendedRuleNode, 0, rulesCount)
|
|
for i := 0; i < rulesCount; i++ {
|
|
rules = append(rules, gen())
|
|
}
|
|
return apimodels.PostableRuleGroupConfig{
|
|
Name: "arulegroup-" + uuid.NewString(),
|
|
Interval: model.Duration(10 * time.Second),
|
|
Rules: rules,
|
|
}
|
|
}
|
|
|
|
func convertGettableRuleGroupToPostable(gettable apimodels.GettableRuleGroupConfig) apimodels.PostableRuleGroupConfig {
|
|
rules := make([]apimodels.PostableExtendedRuleNode, 0, len(gettable.Rules))
|
|
for _, rule := range gettable.Rules {
|
|
rules = append(rules, convertGettableRuleToPostable(rule))
|
|
}
|
|
return apimodels.PostableRuleGroupConfig{
|
|
Name: gettable.Name,
|
|
Interval: gettable.Interval,
|
|
Rules: rules,
|
|
}
|
|
}
|
|
|
|
func convertGettableRuleToPostable(gettable apimodels.GettableExtendedRuleNode) apimodels.PostableExtendedRuleNode {
|
|
return apimodels.PostableExtendedRuleNode{
|
|
ApiRuleNode: gettable.ApiRuleNode,
|
|
GrafanaManagedAlert: convertGettableGrafanaRuleToPostable(gettable.GrafanaManagedAlert),
|
|
}
|
|
}
|
|
|
|
func convertGettableGrafanaRuleToPostable(gettable *apimodels.GettableGrafanaRule) *apimodels.PostableGrafanaRule {
|
|
if gettable == nil {
|
|
return nil
|
|
}
|
|
return &apimodels.PostableGrafanaRule{
|
|
Title: gettable.Title,
|
|
Condition: gettable.Condition,
|
|
Data: gettable.Data,
|
|
UID: gettable.UID,
|
|
NoDataState: gettable.NoDataState,
|
|
ExecErrState: gettable.ExecErrState,
|
|
IsPaused: &gettable.IsPaused,
|
|
NotificationSettings: gettable.NotificationSettings,
|
|
Metadata: gettable.Metadata,
|
|
}
|
|
}
|
|
|
|
type apiClient struct {
|
|
url string
|
|
}
|
|
|
|
type LegacyApiClient struct {
|
|
apiClient
|
|
}
|
|
|
|
func NewAlertingLegacyAPIClient(host, user, pass string) LegacyApiClient {
|
|
cli := newAlertingApiClient(host, user, pass)
|
|
return LegacyApiClient{
|
|
apiClient: cli,
|
|
}
|
|
}
|
|
|
|
func newAlertingApiClient(host, user, pass string) apiClient {
|
|
if len(user) == 0 && len(pass) == 0 {
|
|
return apiClient{url: fmt.Sprintf("http://%s", host)}
|
|
}
|
|
return apiClient{url: fmt.Sprintf("http://%s:%s@%s", user, pass, host)}
|
|
}
|
|
|
|
// ReloadCachedPermissions sends a request to access control API to refresh cached user permissions
|
|
func (a apiClient) ReloadCachedPermissions(t *testing.T) {
|
|
t.Helper()
|
|
|
|
u := fmt.Sprintf("%s/api/access-control/user/permissions?reloadcache=true", a.url)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
require.NoErrorf(t, err, "failed to reload permissions cache")
|
|
require.Equalf(t, http.StatusOK, resp.StatusCode, "failed to reload permissions cache")
|
|
}
|
|
|
|
// AssignReceiverPermission sends a request to access control API to assign permissions to a user, role, or team on a receiver.
|
|
func (a apiClient) AssignReceiverPermission(t *testing.T, receiverUID string, cmd accesscontrol.SetResourcePermissionCommand) (int, string) {
|
|
t.Helper()
|
|
|
|
var assignment string
|
|
var assignTo string
|
|
if cmd.UserID != 0 {
|
|
assignment = "users"
|
|
assignTo = fmt.Sprintf("%d", cmd.UserID)
|
|
} else if cmd.TeamID != 0 {
|
|
assignment = "teams"
|
|
assignTo = fmt.Sprintf("%d", cmd.TeamID)
|
|
} else {
|
|
assignment = "builtInRoles"
|
|
assignTo = cmd.BuiltinRole
|
|
}
|
|
|
|
body := strings.NewReader(fmt.Sprintf(`{"permission": "%s"}`, cmd.Permission))
|
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/access-control/receivers/%s/%s/%s", a.url, receiverUID, assignment, assignTo), body)
|
|
require.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
// CreateFolder creates a folder for storing our alerts, and then refreshes the permission cache to make sure that following requests will be accepted
|
|
func (a apiClient) CreateFolder(t *testing.T, uID string, title string, parentUID ...string) {
|
|
t.Helper()
|
|
cmd := folder.CreateFolderCommand{
|
|
UID: uID,
|
|
Title: title,
|
|
}
|
|
if len(parentUID) > 0 {
|
|
cmd.ParentUID = parentUID[0]
|
|
}
|
|
|
|
blob, err := json.Marshal(cmd)
|
|
require.NoError(t, err)
|
|
|
|
payload := string(blob)
|
|
u := fmt.Sprintf("%s/api/folders", a.url)
|
|
r := strings.NewReader(payload)
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", r)
|
|
defer func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
}()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
a.ReloadCachedPermissions(t)
|
|
}
|
|
|
|
func (a apiClient) ReloadAlertingFileProvisioning(t *testing.T) {
|
|
t.Helper()
|
|
|
|
u := fmt.Sprintf("%s/api/admin/provisioning/alerting/reload", a.url)
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", nil)
|
|
defer func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
}()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
func (a apiClient) GetOrgQuotaLimits(t *testing.T, orgID int64) (int64, int64) {
|
|
t.Helper()
|
|
|
|
u := fmt.Sprintf("%s/api/orgs/%d/quotas", a.url, orgID)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
results := []quota.QuotaDTO{}
|
|
require.NoError(t, json.Unmarshal(b, &results))
|
|
|
|
var limit int64 = 0
|
|
var used int64 = 0
|
|
for _, q := range results {
|
|
if q.Target != string(ngmodels.QuotaTargetSrv) {
|
|
continue
|
|
}
|
|
limit = q.Limit
|
|
used = q.Used
|
|
}
|
|
return limit, used
|
|
}
|
|
|
|
func (a apiClient) UpdateAlertRuleOrgQuota(t *testing.T, orgID int64, limit int64) {
|
|
t.Helper()
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode("a.UpdateQuotaCmd{
|
|
Target: "alert_rule",
|
|
Limit: limit,
|
|
OrgID: orgID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
u := fmt.Sprintf("%s/api/orgs/%d/quotas/alert_rule", a.url, orgID)
|
|
// nolint:gosec
|
|
client := &http.Client{}
|
|
req, err := http.NewRequest(http.MethodPut, u, &buf)
|
|
require.NoError(t, err)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
func (a apiClient) PostConfiguration(t *testing.T, c apimodels.PostableUserConfig) (bool, error) {
|
|
t.Helper()
|
|
|
|
b, err := json.Marshal(c)
|
|
require.NoError(t, err)
|
|
|
|
u := fmt.Sprintf("%s/api/alertmanager/grafana/config/api/v1/alerts", a.url)
|
|
req, err := http.NewRequest(http.MethodPost, u, bytes.NewReader(b))
|
|
require.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err = io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
data := struct {
|
|
Message string `json:"message"`
|
|
}{}
|
|
require.NoError(t, json.Unmarshal(b, &data))
|
|
|
|
if resp.StatusCode == http.StatusAccepted {
|
|
return true, nil
|
|
}
|
|
|
|
return false, errors.New(data.Message)
|
|
}
|
|
|
|
func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) (apimodels.UpdateRuleGroupResponse, int, string) {
|
|
t.Helper()
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(group)
|
|
require.NoError(t, err)
|
|
|
|
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s", a.url, folder)
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", &buf)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
var m apimodels.UpdateRuleGroupResponse
|
|
if resp.StatusCode == http.StatusAccepted {
|
|
require.NoError(t, json.Unmarshal(b, &m))
|
|
}
|
|
return m, resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) apimodels.UpdateRuleGroupResponse {
|
|
t.Helper()
|
|
m, status, raw := a.PostRulesGroupWithStatus(t, folder, group)
|
|
requireStatusCode(t, http.StatusAccepted, status, raw)
|
|
return m
|
|
}
|
|
|
|
func (a apiClient) PostRulesExportWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig, params *apimodels.ExportQueryParams) (int, string) {
|
|
t.Helper()
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(group)
|
|
require.NoError(t, err)
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s/export", a.url, folder))
|
|
require.NoError(t, err)
|
|
|
|
if params != nil {
|
|
q := url.Values{}
|
|
if params.Format != "" {
|
|
q.Set("format", params.Format)
|
|
}
|
|
if params.Download {
|
|
q.Set("download", "true")
|
|
}
|
|
u.RawQuery = q.Encode()
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPost, u.String(), &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) DeleteRulesGroup(t *testing.T, folder string, group string) (int, string) {
|
|
t.Helper()
|
|
|
|
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s/%s", a.url, folder, group)
|
|
req, err := http.NewRequest(http.MethodDelete, u, nil)
|
|
require.NoError(t, err)
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) PostSilence(t *testing.T, s apimodels.PostableSilence) (apimodels.PostSilencesOKBody, int, string) {
|
|
t.Helper()
|
|
|
|
b, err := json.Marshal(s)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/alertmanager/grafana/api/v2/silences", a.url), bytes.NewReader(b))
|
|
require.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
return sendRequest[apimodels.PostSilencesOKBody](t, req, http.StatusAccepted)
|
|
}
|
|
|
|
func (a apiClient) GetSilence(t *testing.T, id string) (apimodels.GettableSilence, int, string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/alertmanager/grafana/api/v2/silence/%s", a.url, id), nil)
|
|
require.NoError(t, err)
|
|
return sendRequest[apimodels.GettableSilence](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetSilences(t *testing.T, filters ...string) (apimodels.GettableSilences, int, string) {
|
|
t.Helper()
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/alertmanager/grafana/api/v2/silences", a.url))
|
|
require.NoError(t, err)
|
|
if len(filters) > 0 {
|
|
u.RawQuery = url.Values{"filter": filters}.Encode()
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.GettableSilences](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) DeleteSilence(t *testing.T, id string) (any, int, string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/alertmanager/grafana/api/v2/silence/%s", a.url, id), nil)
|
|
require.NoError(t, err)
|
|
|
|
type dynamic struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
return sendRequest[dynamic](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetRulesGroup(t *testing.T, folder string, group string) (apimodels.RuleGroupConfigResponse, int) {
|
|
result, status, _ := a.GetRulesGroupWithStatus(t, folder, group)
|
|
return result, status
|
|
}
|
|
|
|
func (a apiClient) GetRulesGroupWithStatus(t *testing.T, folder string, group string) (apimodels.RuleGroupConfigResponse, int, []byte) {
|
|
t.Helper()
|
|
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s/%s", a.url, folder, group)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
result := apimodels.RuleGroupConfigResponse{}
|
|
|
|
if http.StatusAccepted == resp.StatusCode {
|
|
require.NoError(t, json.Unmarshal(b, &result))
|
|
}
|
|
return result, resp.StatusCode, b
|
|
}
|
|
|
|
func (a apiClient) GetAllRulesGroupInFolderWithStatus(t *testing.T, folder string) (apimodels.NamespaceConfigResponse, int, []byte) {
|
|
t.Helper()
|
|
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s", a.url, folder)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
result := apimodels.NamespaceConfigResponse{}
|
|
if http.StatusAccepted == resp.StatusCode {
|
|
require.NoError(t, json.Unmarshal(b, &result))
|
|
}
|
|
return result, resp.StatusCode, b
|
|
}
|
|
|
|
func (a apiClient) GetAllRulesWithStatus(t *testing.T) (apimodels.NamespaceConfigResponse, int, []byte) {
|
|
t.Helper()
|
|
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules", a.url)
|
|
// nolint:gosec
|
|
resp, err := http.Get(u)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
result := apimodels.NamespaceConfigResponse{}
|
|
if http.StatusOK == resp.StatusCode {
|
|
require.NoError(t, json.Unmarshal(b, &result))
|
|
}
|
|
return result, resp.StatusCode, b
|
|
}
|
|
|
|
func (a apiClient) ExportRulesWithStatus(t *testing.T, params *apimodels.AlertRulesExportParameters) (int, string) {
|
|
t.Helper()
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/ruler/grafana/api/v1/export/rules", a.url))
|
|
require.NoError(t, err)
|
|
if params != nil {
|
|
q := url.Values{}
|
|
if params.Format != "" {
|
|
q.Set("format", params.Format)
|
|
}
|
|
if params.Download {
|
|
q.Set("download", "true")
|
|
}
|
|
if len(params.FolderUID) > 0 {
|
|
for _, s := range params.FolderUID {
|
|
q.Add("folderUid", s)
|
|
}
|
|
}
|
|
if params.GroupName != "" {
|
|
q.Set("group", params.GroupName)
|
|
}
|
|
if params.RuleUID != "" {
|
|
q.Set("ruleUid", params.RuleUID)
|
|
}
|
|
u.RawQuery = q.Encode()
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) GetRuleGroupProvisioning(t *testing.T, folderUID string, groupName string) (apimodels.AlertRuleGroup, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/provisioning/folder/%s/rule-groups/%s", a.url, folderUID, groupName), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.AlertRuleGroup](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) CreateOrUpdateRuleGroupProvisioning(t *testing.T, group apimodels.AlertRuleGroup) (apimodels.AlertRuleGroup, int, string) {
|
|
t.Helper()
|
|
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(group)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v1/provisioning/folder/%s/rule-groups/%s", a.url, group.FolderUID, group.Title), &buf)
|
|
require.NoError(t, err)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
return sendRequest[apimodels.AlertRuleGroup](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) SubmitRuleForBacktesting(t *testing.T, config apimodels.BacktestConfig) (int, string) {
|
|
t.Helper()
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(config)
|
|
require.NoError(t, err)
|
|
|
|
u := fmt.Sprintf("%s/api/v1/rule/backtest", a.url)
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", &buf)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) SubmitRuleForTesting(t *testing.T, config apimodels.PostableExtendedRuleNodeExtended) (int, string) {
|
|
t.Helper()
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(config)
|
|
require.NoError(t, err)
|
|
|
|
u := fmt.Sprintf("%s/api/v1/rule/test/grafana", a.url)
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", &buf)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
return resp.StatusCode, string(b)
|
|
}
|
|
|
|
func (a apiClient) CreateTestDatasource(t *testing.T) (result api.CreateOrUpdateDatasourceResponse) {
|
|
t.Helper()
|
|
|
|
return a.CreateDatasource(t, "testdata")
|
|
}
|
|
|
|
func (a apiClient) CreateDatasource(t *testing.T, dsType string) (result api.CreateOrUpdateDatasourceResponse) {
|
|
t.Helper()
|
|
|
|
payload := fmt.Sprintf(`{"name":"TestDatasource-%s","type":"%s","access":"proxy","isDefault":false}`, uuid.NewString(), dsType)
|
|
buf := bytes.Buffer{}
|
|
buf.Write([]byte(payload))
|
|
|
|
u := fmt.Sprintf("%s/api/datasources", a.url)
|
|
|
|
// nolint:gosec
|
|
resp, err := http.Post(u, "application/json", &buf)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
if resp.StatusCode != 200 {
|
|
require.Failf(t, "failed to create data source", "API request to create a datasource failed. Status code: %d, response: %s", resp.StatusCode, string(b))
|
|
}
|
|
require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{ "body": %s }`, string(b))), &result))
|
|
return result
|
|
}
|
|
|
|
func (a apiClient) DeleteDatasource(t *testing.T, uid string) {
|
|
t.Helper()
|
|
|
|
u := fmt.Sprintf("%s/api/datasources/uid/%s", a.url, uid)
|
|
|
|
req, err := http.NewRequest(http.MethodDelete, u, nil)
|
|
require.NoError(t, err)
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
b, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
if resp.StatusCode != 200 {
|
|
require.Failf(t, "failed to create data source", "API request to create a datasource failed. Status code: %d, response: %s", resp.StatusCode, string(b))
|
|
}
|
|
}
|
|
|
|
func (a apiClient) GetAllMuteTimingsWithStatus(t *testing.T) (apimodels.MuteTimings, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/provisioning/mute-timings", a.url), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.MuteTimings](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetMuteTimingByNameWithStatus(t *testing.T, name string) (apimodels.MuteTimeInterval, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/provisioning/mute-timings/%s", a.url, name), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.MuteTimeInterval](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) CreateMuteTimingWithStatus(t *testing.T, interval apimodels.MuteTimeInterval) (apimodels.MuteTimeInterval, int, string) {
|
|
t.Helper()
|
|
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(interval)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1/provisioning/mute-timings", a.url), &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.MuteTimeInterval](t, req, http.StatusCreated)
|
|
}
|
|
|
|
func (a apiClient) EnsureMuteTiming(t *testing.T, interval apimodels.MuteTimeInterval) {
|
|
t.Helper()
|
|
|
|
_, status, body := a.CreateMuteTimingWithStatus(t, interval)
|
|
require.Equalf(t, http.StatusCreated, status, body)
|
|
}
|
|
|
|
func (a apiClient) UpdateMuteTimingWithStatus(t *testing.T, interval apimodels.MuteTimeInterval) (apimodels.MuteTimeInterval, int, string) {
|
|
t.Helper()
|
|
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(interval)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v1/provisioning/mute-timings/%s", a.url, interval.Name), &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.MuteTimeInterval](t, req, http.StatusAccepted)
|
|
}
|
|
|
|
func (a apiClient) DeleteMuteTimingWithStatus(t *testing.T, name string) (int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v1/provisioning/mute-timings/%s", a.url, name), nil)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, string(body)
|
|
}
|
|
|
|
func (a apiClient) ExportMuteTiming(t *testing.T, name string, format string) string {
|
|
t.Helper()
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/v1/provisioning/mute-timings/%s/export", a.url, name))
|
|
require.NoError(t, err)
|
|
q := url.Values{}
|
|
q.Set("format", format)
|
|
u.RawQuery = q.Encode()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
requireStatusCode(t, http.StatusOK, resp.StatusCode, string(body))
|
|
return string(body)
|
|
}
|
|
|
|
func (a apiClient) GetRouteWithStatus(t *testing.T) (apimodels.Route, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/provisioning/policies", a.url), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.Route](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetRoute(t *testing.T) apimodels.Route {
|
|
t.Helper()
|
|
|
|
route, status, data := a.GetRouteWithStatus(t)
|
|
requireStatusCode(t, http.StatusOK, status, data)
|
|
return route
|
|
}
|
|
|
|
func (a apiClient) UpdateRouteWithStatus(t *testing.T, route apimodels.Route, noProvenance bool) (int, string) {
|
|
t.Helper()
|
|
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(route)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v1/provisioning/policies", a.url), &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
if noProvenance {
|
|
req.Header.Add("X-Disable-Provenance", "true")
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
return resp.StatusCode, string(body)
|
|
}
|
|
|
|
func (a apiClient) ExportNotificationPolicy(t *testing.T, format string) string {
|
|
t.Helper()
|
|
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/v1/provisioning/policies/export", a.url))
|
|
require.NoError(t, err)
|
|
q := url.Values{}
|
|
q.Set("format", format)
|
|
u.RawQuery = q.Encode()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
requireStatusCode(t, http.StatusOK, resp.StatusCode, string(body))
|
|
return string(body)
|
|
}
|
|
|
|
func (a apiClient) UpdateRoute(t *testing.T, route apimodels.Route, noProvenance bool) {
|
|
t.Helper()
|
|
status, data := a.UpdateRouteWithStatus(t, route, noProvenance)
|
|
requireStatusCode(t, http.StatusAccepted, status, data)
|
|
}
|
|
|
|
func (a apiClient) GetRuleHistoryWithStatus(t *testing.T, ruleUID string) (data.Frame, int, string) {
|
|
t.Helper()
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/v1/rules/history", a.url))
|
|
require.NoError(t, err)
|
|
q := url.Values{}
|
|
q.Set("ruleUID", ruleUID)
|
|
u.RawQuery = q.Encode()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[data.Frame](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetAllTimeIntervalsWithStatus(t *testing.T) ([]apimodels.GettableTimeIntervals, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/notifications/time-intervals", a.url), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[[]apimodels.GettableTimeIntervals](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetTimeIntervalByNameWithStatus(t *testing.T, name string) (apimodels.GettableTimeIntervals, int, string) {
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/notifications/time-intervals/%s", a.url, name), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.GettableTimeIntervals](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) CreateReceiverWithStatus(t *testing.T, receiver apimodels.EmbeddedContactPoint) (apimodels.EmbeddedContactPoint, int, string) {
|
|
t.Helper()
|
|
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
err := enc.Encode(receiver)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1/provisioning/contact-points", a.url), &buf)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.EmbeddedContactPoint](t, req, http.StatusAccepted)
|
|
}
|
|
|
|
func (a apiClient) EnsureReceiver(t *testing.T, receiver apimodels.EmbeddedContactPoint) {
|
|
t.Helper()
|
|
|
|
_, status, body := a.CreateReceiverWithStatus(t, receiver)
|
|
require.Equalf(t, http.StatusAccepted, status, body)
|
|
}
|
|
|
|
func (a apiClient) ExportReceiver(t *testing.T, name string, format string, decrypt bool) string {
|
|
t.Helper()
|
|
u, err := url.Parse(fmt.Sprintf("%s/api/v1/provisioning/contact-points/export", a.url))
|
|
require.NoError(t, err)
|
|
q := url.Values{}
|
|
q.Set("name", name)
|
|
q.Set("format", format)
|
|
q.Set("decrypt", fmt.Sprintf("%v", decrypt))
|
|
u.RawQuery = q.Encode()
|
|
|
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
|
require.NoError(t, err)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
requireStatusCode(t, http.StatusOK, resp.StatusCode, string(body))
|
|
return string(body)
|
|
}
|
|
|
|
func (a apiClient) ExportReceiverTyped(t *testing.T, name string, decrypt bool) apimodels.ContactPointExport {
|
|
t.Helper()
|
|
|
|
response := a.ExportReceiver(t, name, "json", decrypt)
|
|
|
|
var export apimodels.AlertingFileExport
|
|
require.NoError(t, json.Unmarshal([]byte(response), &export))
|
|
require.Len(t, export.ContactPoints, 1)
|
|
return export.ContactPoints[0]
|
|
}
|
|
|
|
func (a apiClient) GetAlertmanagerConfigWithStatus(t *testing.T) (apimodels.GettableUserConfig, int, string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/alertmanager/grafana/config/api/v1/alerts", a.url), nil)
|
|
require.NoError(t, err)
|
|
|
|
return sendRequest[apimodels.GettableUserConfig](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetActiveAlertsWithStatus(t *testing.T) (apimodels.AlertGroups, int, string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/alertmanager/grafana/api/v2/alerts/groups", a.url), nil)
|
|
require.NoError(t, err)
|
|
return sendRequest[apimodels.AlertGroups](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetRuleVersionsWithStatus(t *testing.T, ruleUID string) (apimodels.GettableRuleVersions, int, string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/ruler/grafana/api/v1/rule/%s/versions", a.url, ruleUID), nil)
|
|
require.NoError(t, err)
|
|
return sendRequest[apimodels.GettableRuleVersions](t, req, http.StatusOK)
|
|
}
|
|
|
|
func (a apiClient) GetRuleByUID(t *testing.T, ruleUID string) apimodels.GettableExtendedRuleNode {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/ruler/grafana/api/v1/rule/%s", a.url, ruleUID), nil)
|
|
require.NoError(t, err)
|
|
rule, status, raw := sendRequest[apimodels.GettableExtendedRuleNode](t, req, http.StatusOK)
|
|
requireStatusCode(t, http.StatusOK, status, raw)
|
|
return rule
|
|
}
|
|
|
|
func (a apiClient) ConvertPrometheusPostRuleGroup(t *testing.T, namespaceTitle, datasourceUID string, promGroup apimodels.PrometheusRuleGroup, headers map[string]string) {
|
|
t.Helper()
|
|
|
|
data, err := yaml.Marshal(promGroup)
|
|
require.NoError(t, err)
|
|
buf := bytes.NewReader(data)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/convert/prometheus/config/v1/rules/%s", a.url, namespaceTitle), buf)
|
|
require.NoError(t, err)
|
|
req.Header.Add("X-Grafana-Alerting-Datasource-UID", datasourceUID)
|
|
|
|
for key, value := range headers {
|
|
req.Header.Add(key, value)
|
|
}
|
|
|
|
_, status, raw := sendRequest[apimodels.ConvertPrometheusResponse](t, req, http.StatusAccepted)
|
|
requireStatusCode(t, http.StatusAccepted, status, raw)
|
|
}
|
|
|
|
func sendRequest[T any](t *testing.T, req *http.Request, successStatusCode int) (T, int, string) {
|
|
t.Helper()
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = resp.Body.Close()
|
|
}()
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var result T
|
|
|
|
if resp.StatusCode != successStatusCode {
|
|
return result, resp.StatusCode, string(body)
|
|
}
|
|
|
|
err = json.Unmarshal(body, &result)
|
|
require.NoError(t, err)
|
|
return result, resp.StatusCode, string(body)
|
|
}
|
|
|
|
func requireStatusCode(t *testing.T, expected, actual int, response string) {
|
|
t.Helper()
|
|
require.Equalf(t, expected, actual, "Unexpected status. Response: %s", response)
|
|
}
|
|
|
|
func createUser(t *testing.T, db db.DB, cfg *setting.Cfg, cmd user.CreateUserCommand) int64 {
|
|
t.Helper()
|
|
|
|
cfg.AutoAssignOrg = true
|
|
cfg.AutoAssignOrgId = 1
|
|
|
|
quotaService := quotaimpl.ProvideService(db, cfg)
|
|
orgService, err := orgimpl.ProvideService(db, cfg, quotaService)
|
|
require.NoError(t, err)
|
|
usrSvc, err := userimpl.ProvideService(
|
|
db, orgService, cfg, nil, nil, tracing.InitializeTracerForTest(),
|
|
quotaService, supportbundlestest.NewFakeBundleService(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
u, err := usrSvc.Create(context.Background(), &cmd)
|
|
require.NoError(t, err)
|
|
return u.ID
|
|
}
|