Prometheus: Add support for cloud partners Prometheus data sources (#103482)
* wip * Add prom flavor support for data source variables and export/import dashboards (#103321) * add dashboard and data source var selection * use match plugin id instead * use updated matchpluginid * formatting * cleanup * regex anchor * update error msg * Alerting: Clean up prometheus-flavored types and functions (#103703) * clean up types and utility functions for dealing with prometheus-flavored data sources * Refactor alerting datasource types to use constants as source of truth * Alerting: Clean up prometheus-flavored types and functions on the bac… (#103716) Alerting: Clean up prometheus-flavored types and functions on the backend * add matchPluginId tests * Update matchPluginId func to bidirectional (#103746) * update matchpluginid func to bidirectional * lint * formatting * use actual isSupportedExternalRulesSourceType in test * add tests in datasource_srv * betterer * remove type assertion * remove unnecessary case * use satisifies to not have to convert tuple to an array of string * add prometheus_flavor test --------- Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com> Co-authored-by: Alexander Akhmetov <me@alx.cx>
This commit is contained in:
co-authored by
Andrew Hackmann
Gilles De Mey
Alexander Akhmetov
parent
22a9e1b5ff
commit
fd6fd91115
@@ -11,27 +11,29 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DS_ACCESS_DIRECT = "direct"
|
||||
DS_ACCESS_PROXY = "proxy"
|
||||
DS_ALERTMANAGER = "alertmanager"
|
||||
DS_AZURE_MONITOR = "grafana-azure-monitor-datasource"
|
||||
DS_DYNATRACE = "grafana-dynatrace-datasource"
|
||||
DS_ES = "elasticsearch"
|
||||
DS_ES_OPEN_DISTRO = "grafana-es-open-distro-datasource"
|
||||
DS_ES_OPENSEARCH = "grafana-opensearch-datasource"
|
||||
DS_GRAPHITE = "graphite"
|
||||
DS_INFLUXDB = "influxdb"
|
||||
DS_INFLUXDB_08 = "influxdb_08"
|
||||
DS_JAEGER = "jaeger"
|
||||
DS_LOKI = "loki"
|
||||
DS_MSSQL = "mssql"
|
||||
DS_MYSQL = "mysql"
|
||||
DS_OPENTSDB = "opentsdb"
|
||||
DS_POSTGRES = "grafana-postgresql-datasource"
|
||||
DS_PROMETHEUS = "prometheus"
|
||||
DS_TEMPO = "tempo"
|
||||
DS_TESTDATA = "grafana-testdata-datasource"
|
||||
DS_ZIPKIN = "zipkin"
|
||||
DS_ACCESS_DIRECT = "direct"
|
||||
DS_ACCESS_PROXY = "proxy"
|
||||
DS_ALERTMANAGER = "alertmanager"
|
||||
DS_AZURE_MONITOR = "grafana-azure-monitor-datasource"
|
||||
DS_DYNATRACE = "grafana-dynatrace-datasource"
|
||||
DS_ES = "elasticsearch"
|
||||
DS_ES_OPEN_DISTRO = "grafana-es-open-distro-datasource"
|
||||
DS_ES_OPENSEARCH = "grafana-opensearch-datasource"
|
||||
DS_GRAPHITE = "graphite"
|
||||
DS_INFLUXDB = "influxdb"
|
||||
DS_INFLUXDB_08 = "influxdb_08"
|
||||
DS_JAEGER = "jaeger"
|
||||
DS_LOKI = "loki"
|
||||
DS_MSSQL = "mssql"
|
||||
DS_MYSQL = "mysql"
|
||||
DS_OPENTSDB = "opentsdb"
|
||||
DS_POSTGRES = "grafana-postgresql-datasource"
|
||||
DS_PROMETHEUS = "prometheus"
|
||||
DS_AMAZON_PROMETHEUS = "grafana-amazonprometheus-datasource"
|
||||
DS_AZURE_PROMETHEUS = "grafana-azureprometheus-datasource"
|
||||
DS_TEMPO = "tempo"
|
||||
DS_TESTDATA = "grafana-testdata-datasource"
|
||||
DS_ZIPKIN = "zipkin"
|
||||
// CustomHeaderName is the prefix that is used to store the name of a custom header.
|
||||
CustomHeaderName = "httpHeaderName"
|
||||
// CustomHeaderValue is the prefix that is used to store the value of a custom header.
|
||||
|
||||
@@ -922,7 +922,7 @@ func awsServiceNamespace(dsType string, jsonData *simplejson.Json) string {
|
||||
} else {
|
||||
return "es"
|
||||
}
|
||||
case datasources.DS_PROMETHEUS, datasources.DS_ALERTMANAGER:
|
||||
case datasources.DS_PROMETHEUS, datasources.DS_AMAZON_PROMETHEUS, datasources.DS_ALERTMANAGER:
|
||||
return "aps"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unsupported datasource %q", dsType))
|
||||
|
||||
@@ -1000,6 +1000,11 @@ func TestService_awsServiceNamespace(t *testing.T) {
|
||||
givenDs: datasources.DS_PROMETHEUS,
|
||||
givenJson: `{ "sigV4Auth": true, "serverless": true }`,
|
||||
want: "aps",
|
||||
}, {
|
||||
desc: "amazon prometheus",
|
||||
givenDs: datasources.DS_AMAZON_PROMETHEUS,
|
||||
givenJson: `{ "sigV4Auth": true }`,
|
||||
want: "aps",
|
||||
}, {
|
||||
desc: "alertmanager",
|
||||
givenDs: datasources.DS_ALERTMANAGER,
|
||||
@@ -1011,6 +1016,12 @@ func TestService_awsServiceNamespace(t *testing.T) {
|
||||
givenJson: `{ "sigV4Auth": true, "serverless": true }`,
|
||||
want: "aps",
|
||||
panic: true,
|
||||
}, {
|
||||
desc: "azure prometheus",
|
||||
givenDs: datasources.DS_AZURE_PROMETHEUS,
|
||||
givenJson: `{ "sigV4Auth": true }`,
|
||||
want: "aps",
|
||||
panic: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -15,16 +16,16 @@ type promEndpoints struct {
|
||||
rules, alerts string
|
||||
}
|
||||
|
||||
var dsTypeToLotexRoutes = map[string]promEndpoints{
|
||||
"prometheus": {
|
||||
var (
|
||||
prometheusEndpoints = promEndpoints{
|
||||
rules: "/api/v1/rules",
|
||||
alerts: "/api/v1/alerts",
|
||||
},
|
||||
"loki": {
|
||||
}
|
||||
lokiEndpoints = promEndpoints{
|
||||
rules: "/prometheus/api/v1/rules",
|
||||
alerts: "/prometheus/api/v1/alerts",
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
type LotexProm struct {
|
||||
log log.Logger
|
||||
@@ -91,9 +92,15 @@ func (p *LotexProm) getEndpoints(ctx *contextmodel.ReqContext) (*promEndpoints,
|
||||
return nil, fmt.Errorf("URL for this data source is empty")
|
||||
}
|
||||
|
||||
routes, ok := dsTypeToLotexRoutes[ds.Type]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected datasource type. expecting loki or prometheus")
|
||||
var routes promEndpoints
|
||||
switch {
|
||||
case isPrometheusCompatible(ds.Type):
|
||||
routes = prometheusEndpoints
|
||||
case ds.Type == datasources.DS_LOKI:
|
||||
routes = lokiEndpoints
|
||||
default:
|
||||
return nil, unexpectedDatasourceTypeError(ds.Type, "loki, prometheus, amazon prometheus, azure prometheus")
|
||||
}
|
||||
|
||||
return &routes, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func TestLotexProm_GetEndpoints(t *testing.T) {
|
||||
tc := []struct {
|
||||
name string
|
||||
namedParams map[string]string
|
||||
datasourceCache datasources.CacheService
|
||||
expectedRoutes *promEndpoints
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "with an empty datasource UID",
|
||||
namedParams: map[string]string{":DatasourceUID": ""},
|
||||
err: errors.New("datasource UID is invalid"),
|
||||
},
|
||||
{
|
||||
name: "with an error while trying to fetch the datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{err: datasources.ErrDataSourceNotFound},
|
||||
err: errors.New("data source not found"),
|
||||
},
|
||||
{
|
||||
name: "with an empty datasource URL",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{}},
|
||||
err: errors.New("URL for this data source is empty"),
|
||||
},
|
||||
{
|
||||
name: "with an unsupported datasource type",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: "unsupported-type"}},
|
||||
err: errors.New("unexpected datasource type 'unsupported-type', expected loki, prometheus, amazon prometheus, azure prometheus"),
|
||||
},
|
||||
{
|
||||
name: "with a Loki datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_LOKI}},
|
||||
expectedRoutes: &lokiEndpoints,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://prom.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expectedRoutes: &prometheusEndpoints,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "with an Amazon Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://amp.com", Type: datasources.DS_AMAZON_PROMETHEUS}},
|
||||
expectedRoutes: &prometheusEndpoints,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "with an Azure Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://azp.com", Type: datasources.DS_AZURE_PROMETHEUS}},
|
||||
expectedRoutes: &prometheusEndpoints,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
proxy := &AlertingProxy{DataProxy: &datasourceproxy.DataSourceProxyService{DataSourceCache: tt.datasourceCache}}
|
||||
prom := &LotexProm{AlertingProxy: proxy, log: log.NewNopLogger()}
|
||||
|
||||
// Setup request context.
|
||||
httpReq, err := http.NewRequest(http.MethodGet, "http://grafanacloud.com", nil)
|
||||
require.NoError(t, err)
|
||||
ctx := &contextmodel.ReqContext{Context: &web.Context{Req: web.SetURLParams(httpReq, tt.namedParams)}}
|
||||
|
||||
endpoints, err := prom.getEndpoints(ctx)
|
||||
|
||||
if tt.err != nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedRoutes, endpoints)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -23,9 +24,6 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
PrometheusDatasourceType = "prometheus"
|
||||
LokiDatasourceType = "loki"
|
||||
|
||||
mimirPrefix = "/config/v1/rules"
|
||||
prometheusPrefix = "/rules"
|
||||
lokiPrefix = "/api/prom/rules"
|
||||
@@ -33,11 +31,6 @@ const (
|
||||
subtypeQuery = "subtype"
|
||||
)
|
||||
|
||||
var dsTypeToRulerPrefix = map[string]string{
|
||||
PrometheusDatasourceType: prometheusPrefix,
|
||||
LokiDatasourceType: lokiPrefix,
|
||||
}
|
||||
|
||||
var subtypeToPrefix = map[string]string{
|
||||
Prometheus: prometheusPrefix,
|
||||
Cortex: prometheusPrefix,
|
||||
@@ -237,13 +230,18 @@ func (r *LotexRuler) validateAndGetPrefix(ctx *contextmodel.ReqContext) (string,
|
||||
return "", fmt.Errorf("URL for this data source is empty")
|
||||
}
|
||||
|
||||
prefix, ok := dsTypeToRulerPrefix[ds.Type]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected datasource type. expecting loki or prometheus")
|
||||
var prefix string
|
||||
switch {
|
||||
case isPrometheusCompatible(ds.Type):
|
||||
prefix = prometheusPrefix
|
||||
case ds.Type == datasources.DS_LOKI:
|
||||
prefix = lokiPrefix
|
||||
default:
|
||||
return "", unexpectedDatasourceTypeError(ds.Type, "loki, prometheus, amazon prometheus, azure prometheus")
|
||||
}
|
||||
|
||||
// If the datasource is Loki, there's nothing else for us to do - it doesn't have subtypes.
|
||||
if ds.Type == LokiDatasourceType {
|
||||
if ds.Type == datasources.DS_LOKI {
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -49,48 +49,74 @@ func TestLotexRuler_ValidateAndGetPrefix(t *testing.T) {
|
||||
{
|
||||
name: "with an unsupported datasource type",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com"}},
|
||||
err: errors.New("unexpected datasource type. expecting loki or prometheus"),
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: "unsupported-type"}},
|
||||
err: errors.New("unexpected datasource type 'unsupported-type', expected loki, prometheus, amazon prometheus, azure prometheus"),
|
||||
},
|
||||
{
|
||||
name: "with a Loki datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: LokiDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_LOKI}},
|
||||
expected: "/api/prom/rules",
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: PrometheusDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with an Amazon Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://amp.com", Type: datasources.DS_AMAZON_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with an Azure Prometheus datasource",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://azp.com", Type: datasources.DS_AZURE_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource and subtype of Cortex",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
urlParams: "?subtype=cortex",
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: PrometheusDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource and subtype of Mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
urlParams: "?subtype=mimir",
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: PrometheusDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expected: "/config/v1/rules",
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource and subtype of Prometheus",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
urlParams: "?subtype=prometheus",
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: PrometheusDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with a Prometheus datasource and no subtype",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: PrometheusDatasourceType}},
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://loki.com", Type: datasources.DS_PROMETHEUS}},
|
||||
expected: "/rules",
|
||||
},
|
||||
{
|
||||
name: "with an Amazon Prometheus datasource and subtype of Mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
urlParams: "?subtype=mimir",
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://amp.com", Type: datasources.DS_AMAZON_PROMETHEUS}},
|
||||
expected: "/config/v1/rules",
|
||||
},
|
||||
{
|
||||
name: "with an Azure Prometheus datasource and subtype of Mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
urlParams: "?subtype=mimir",
|
||||
datasourceCache: fakeCacheService{datasource: &datasources.DataSource{URL: "http://azp.com", Type: datasources.DS_AZURE_PROMETHEUS}},
|
||||
expected: "/config/v1/rules",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tc {
|
||||
@@ -149,7 +175,7 @@ func TestLotexRuler_RouteDeleteNamespaceRulesConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace%2Fwith%2Fslashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
{
|
||||
name: "with a namespace that does not need to be escaped",
|
||||
@@ -157,7 +183,7 @@ func TestLotexRuler_RouteDeleteNamespaceRulesConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace_without_slashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -210,7 +236,7 @@ func TestLotexRuler_RouteDeleteRuleGroupConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace%2Fwith%2Fslashes/group%2Fwith%2Fslashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
{
|
||||
name: "with a namespace that does not need to be escaped",
|
||||
@@ -219,7 +245,7 @@ func TestLotexRuler_RouteDeleteRuleGroupConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace_without_slashes/group_without_slashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -271,7 +297,7 @@ func TestLotexRuler_RouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace%2Fwith%2Fslashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
{
|
||||
name: "with a namespace that does not need to be escaped",
|
||||
@@ -279,7 +305,7 @@ func TestLotexRuler_RouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace_without_slashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -332,7 +358,7 @@ func TestLotexRuler_RouteGetRulegGroupConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace%2Fwith%2Fslashes/group%2Fwith%2Fslashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
{
|
||||
name: "with a namespace that does not need to be escaped",
|
||||
@@ -341,7 +367,7 @@ func TestLotexRuler_RouteGetRulegGroupConfig(t *testing.T) {
|
||||
expected: "http://mimir.com/config/v1/rules/namespace_without_slashes/group_without_slashes?subtype=mimir",
|
||||
urlParams: "?subtype=mimir",
|
||||
namedParams: map[string]string{":DatasourceUID": "d164"},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: PrometheusDatasourceType},
|
||||
datasource: &datasources.DataSource{URL: "http://mimir.com", Type: datasources.DS_PROMETHEUS},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,28 @@ const (
|
||||
groupQueryTag = "QUERY_GROUP"
|
||||
)
|
||||
|
||||
var searchRegex = regexp.MustCompile(`\{(\w+)\}`)
|
||||
var (
|
||||
searchRegex = regexp.MustCompile(`\{(\w+)\}`)
|
||||
|
||||
prometheusCompatibleDsTypes = []string{
|
||||
datasources.DS_PROMETHEUS,
|
||||
datasources.DS_AMAZON_PROMETHEUS,
|
||||
datasources.DS_AZURE_PROMETHEUS,
|
||||
}
|
||||
)
|
||||
|
||||
func isPrometheusCompatible(dsType string) bool {
|
||||
for _, t := range prometheusCompatibleDsTypes {
|
||||
if dsType == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isLotexRulerCompatible(dsType string) bool {
|
||||
return dsType == datasources.DS_LOKI || isPrometheusCompatible(dsType)
|
||||
}
|
||||
|
||||
func toMacaronPath(path string) string {
|
||||
return string(searchRegex.ReplaceAllFunc([]byte(path), func(s []byte) []byte {
|
||||
@@ -52,8 +73,8 @@ func getDatasourceByUID(ctx *contextmodel.ReqContext, cache datasources.CacheSer
|
||||
return nil, unexpectedDatasourceTypeError(ds.Type, "alertmanager")
|
||||
}
|
||||
case apimodels.LoTexRulerBackend:
|
||||
if ds.Type != "loki" && ds.Type != "prometheus" {
|
||||
return nil, unexpectedDatasourceTypeError(ds.Type, "loki, prometheus")
|
||||
if !isLotexRulerCompatible(ds.Type) {
|
||||
return nil, unexpectedDatasourceTypeError(ds.Type, "loki, prometheus, amazon prometheus, azure prometheus")
|
||||
}
|
||||
default:
|
||||
return nil, unexpectedDatasourceTypeError(ds.Type, expectedType.String())
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
models2 "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -176,3 +177,85 @@ func (r *recordingConditionValidator) Validate(_ eval.EvaluationContext, conditi
|
||||
}
|
||||
|
||||
var _ ConditionValidator = &recordingConditionValidator{}
|
||||
|
||||
func TestIsPrometheusCompatible(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
dsType string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "amazon prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_AMAZON_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "azure prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_AZURE_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "loki datasource should not be prometheus compatible",
|
||||
dsType: datasources.DS_LOKI,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "other datasource types should not be compatible",
|
||||
dsType: "some-other-datasource",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := isPrometheusCompatible(tc.dsType)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLotexRulerCompatible(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
dsType string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "amazon prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_AMAZON_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "azure prometheus datasource should be compatible",
|
||||
dsType: datasources.DS_AZURE_PROMETHEUS,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "loki datasource should be compatible",
|
||||
dsType: datasources.DS_LOKI,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "other datasource types should not be compatible",
|
||||
dsType: "some-other-datasource",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := isLotexRulerCompatible(tc.dsType)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user