Prometheus: Flavor/version configuration (#57554)

* Revert "Revert "Prometheus: Type and flavor configuration (#56496)" (#57552)"
This reverts commit 2432ce619a.
* Adds new fields and documentation for Prometheus datasource configuration: prometheus type, and version
This commit is contained in:
Galen Kistler 2022-10-24 14:53:11 -05:00 committed by GitHub
parent cfd9e72da5
commit f93c3acc51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 404 additions and 82 deletions

View File

@ -59,6 +59,8 @@ datasources:
jsonData: jsonData:
manageAlerts: true manageAlerts: true
alertmanagerUid: gdev-alertmanager alertmanagerUid: gdev-alertmanager
prometheusType: Prometheus #Cortex | Mimir | Prometheus | Thanos
prometheusVersion: 2.40.0
- name: gdev-slow-prometheus - name: gdev-slow-prometheus
type: prometheus type: prometheus

View File

@ -158,7 +158,7 @@ Since not all datasources have the same configuration settings we only have the
| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. | | customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. |
| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI | | manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI |
| alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. | | alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. |
| esVersion | string | Elasticsearch | Elasticsearch version (E.g. `7.0.0`, `7.6.1`) | | esVersion | string | Elasticsearch | Elasticsearch version (e.g. `7.0.0`, `7.6.1`) |
| timeField | string | Elasticsearch | Which field that should be used as timestamp | | timeField | string | Elasticsearch | Which field that should be used as timestamp |
| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' | | interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' |
| logMessageField | string | Elasticsearch | Which field should be used as the log message | | logMessageField | string | Elasticsearch | Which field should be used as the log message |
@ -190,6 +190,8 @@ Since not all datasources have the same configuration settings we only have the
| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) | | maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) |
| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) | | connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) |
| keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with datasources | | keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with datasources |
| prometheusVersion | string | Prometheus | The version of the Prometheus datasource (e.g. `2.37.0`, `2.24.0`) |
| prometheusType | string | Prometheus | The type of the Prometheus datasources (i.e. `Prometheus`, `Cortex`, `Thanos`, or `Mimir`) |
#### Secure Json Data #### Secure Json Data

View File

@ -21,25 +21,27 @@ Grafana includes built-in support for Prometheus. This topic explains options, v
To access Prometheus settings, hover your mouse over the **Configuration** (gear) icon, then click **Data Sources**, and then click the Prometheus data source. To access Prometheus settings, hover your mouse over the **Configuration** (gear) icon, then click **Data Sources**, and then click the Prometheus data source.
| Name | Description | | Name | Description |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Name` | The data source name. This is how you refer to the data source in panels and queries. | | `Name` | The data source name. This is how you refer to the data source in panels and queries. |
| `Default` | Default data source that is pre-selected for new panels. | | `Default` | Default data source that is pre-selected for new panels. |
| `Url` | The URL of your Prometheus server, for example, `http://prometheus.example.org:9090`. | | `Url` | The URL of your Prometheus server, for example, `http://prometheus.example.org:9090`. |
| `Access` | Only Server access mode is functional. If Server mode is already selected this option is hidden. Otherwise change to Server mode to prevent errors. | | `Access` | Only Server access mode is functional. If Server mode is already selected this option is hidden. Otherwise change to Server mode to prevent errors. |
| `Basic Auth` | Enable basic authentication to the Prometheus data source. | | `Basic Auth` | Enable basic authentication to the Prometheus data source. |
| `User` | User name for basic authentication. | | `User` | User name for basic authentication. |
| `Password` | Password for basic authentication. | | `Password` | Password for basic authentication. |
| `Scrape interval` | Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s. | | `Scrape interval` | Set this to the typical scrape and evaluation interval configured in Prometheus. Defaults to 15s. |
| `HTTP method` | Use either POST or GET HTTP method to query your data source. POST is the recommended and pre-selected method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network. | | `HTTP method` | Use either POST or GET HTTP method to query your data source. POST is the recommended and pre-selected method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network. |
| `Disable metrics lookup` | Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances. | | `Type` | The type of your Prometheus server, i.e `Prometheus`, `Cortex`, `Thanos` or `Mimir`. When this value is selected in the configuration UI, the Prometheus version field attempts to detect the version automatically using the Prometheus [buildinfo](https://semver.org/) API. Some Prometheus types do not support this API, and you will need to manually populate the version in those cases (e.g. Cortex). |
| `Custom Query Parameters` | Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. | | `Version` | The version of your Prometheus server, note that this field is not visible until the Prometheus type is selected. |
| **Exemplars configuration** | | | `Disable metrics lookup` | Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances. |
| `Internal link` | Enable this option is you have an internal link. When you enable this option, you will see a data source selector. Select the backend tracing data store for your exemplar data. | | `Custom Query Parameters` | Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. |
| `Data source` | You will see this option only if you enable `Internal link` option. Select the backend tracing data store for your exemplar data. | | **Exemplars configuration** | |
| `URL` | You will see this option only if the `Internal link` option is disabled. Enter the full URL of the external link. You can interpolate the value from the field with `${__value.raw }` macro. | | `Internal link` | Enable this option is you have an internal link. When you enable this option, you will see a data source selector. Select the backend tracing data store for your exemplar data. |
| `URL Label` | (Optional) add a custom display label to override the value of the `Label name` field. | | `Data source` | You will see this option only if you enable `Internal link` option. Select the backend tracing data store for your exemplar data. |
| `Label name` | Add a name for the exemplar traceID property. | | `URL` | You will see this option only if the `Internal link` option is disabled. Enter the full URL of the external link. You can interpolate the value from the field with `${__value.raw }` macro. |
| `URL Label` | (Optional) add a custom display label to override the value of the `Label name` field. |
| `Label name` | Add a name for the exemplar traceID property. |
## Prometheus query editor ## Prometheus query editor
@ -176,13 +178,13 @@ types of template variables.
Variable of the type _Query_ allows you to query Prometheus for a list of metrics, labels or label values. The Prometheus data source plugin Variable of the type _Query_ allows you to query Prometheus for a list of metrics, labels or label values. The Prometheus data source plugin
provides the following functions you can use in the `Query` input field. provides the following functions you can use in the `Query` input field.
| Name | Description | Used API endpoints | | Name | Description | Used API endpoints |
| ----------------------------- | ----------------------------------------------------------------------- | --------------------------------- | | ----------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `label_names()` | Returns a list of label names. | /api/v1/labels | | `label_names()` | Returns a list of label names. | /api/v1/labels |
| `label_values(label)` | Returns a list of label values for the `label` in every metric. | /api/v1/label/`label`/values | | `label_values(label)` | Returns a list of label values for the `label` in every metric. | /api/v1/label/`label`/values |
| `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. | /api/v1/series | | `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric. | /api/v1/series or /api/v1/label/`label`/values, depending on prometheus type and version in datasource configuration |
| `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values | | `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
| `query_result(query)` | Returns a list of Prometheus query result for the `query`. | /api/v1/query | | `query_result(query)` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
For details of what _metric names_, _label names_ and _label values_ are please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). For details of what _metric names_, _label names_ and _label values_ are please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels).
@ -282,6 +284,8 @@ datasources:
url: http://localhost:9090 url: http://localhost:9090
jsonData: jsonData:
httpMethod: POST httpMethod: POST
prometheusType: Prometheus
prometheusVersion: 2.37.0
exemplarTraceIdDestinations: exemplarTraceIdDestinations:
# Field with internal link pointing to data source in Grafana. # Field with internal link pointing to data source in Grafana.
# datasourceUid value can be anything, but it should be unique across all defined data source uids. # datasourceUid value can be anything, but it should be unique across all defined data source uids.

View File

@ -10,7 +10,7 @@ const addDataSource = () => {
e2e.components.DataSource.Prometheus.configPage.exemplarsAddButton().click(); e2e.components.DataSource.Prometheus.configPage.exemplarsAddButton().click();
e2e.components.DataSource.Prometheus.configPage.internalLinkSwitch().check({ force: true }); e2e.components.DataSource.Prometheus.configPage.internalLinkSwitch().check({ force: true });
e2e.components.DataSource.DataSourceHttpSettings.urlInput().type('http://prom-url:9090'); e2e.components.DataSource.DataSourceHttpSettings.urlInput().type('http://prom-url:9090');
e2e.components.DataSourcePicker.inputV2().should('be.visible').click({ force: true }); e2e.components.DataSourcePicker.inputV2().click({ force: true }).should('have.focus');
e2e().contains('gdev-tempo').scrollIntoView().should('be.visible').click(); e2e().contains('gdev-tempo').scrollIntoView().should('be.visible').click();
}, },

View File

@ -46,8 +46,8 @@ type FakeEvaluator_ConditionEval_Call struct {
} }
// ConditionEval is a helper method to define mock.On call // ConditionEval is a helper method to define mock.On call
// - ctx EvaluationContext // - ctx EvaluationContext
// - condition models.Condition // - condition models.Condition
func (_e *FakeEvaluator_Expecter) ConditionEval(ctx interface{}, condition interface{}) *FakeEvaluator_ConditionEval_Call { func (_e *FakeEvaluator_Expecter) ConditionEval(ctx interface{}, condition interface{}) *FakeEvaluator_ConditionEval_Call {
return &FakeEvaluator_ConditionEval_Call{Call: _e.mock.On("ConditionEval", ctx, condition)} return &FakeEvaluator_ConditionEval_Call{Call: _e.mock.On("ConditionEval", ctx, condition)}
} }
@ -93,8 +93,8 @@ type FakeEvaluator_QueriesAndExpressionsEval_Call struct {
} }
// QueriesAndExpressionsEval is a helper method to define mock.On call // QueriesAndExpressionsEval is a helper method to define mock.On call
// - ctx EvaluationContext // - ctx EvaluationContext
// - data []models.AlertQuery // - data []models.AlertQuery
func (_e *FakeEvaluator_Expecter) QueriesAndExpressionsEval(ctx interface{}, data interface{}) *FakeEvaluator_QueriesAndExpressionsEval_Call { func (_e *FakeEvaluator_Expecter) QueriesAndExpressionsEval(ctx interface{}, data interface{}) *FakeEvaluator_QueriesAndExpressionsEval_Call {
return &FakeEvaluator_QueriesAndExpressionsEval_Call{Call: _e.mock.On("QueriesAndExpressionsEval", ctx, data)} return &FakeEvaluator_QueriesAndExpressionsEval_Call{Call: _e.mock.On("QueriesAndExpressionsEval", ctx, data)}
} }
@ -131,8 +131,8 @@ type FakeEvaluator_Validate_Call struct {
} }
// Validate is a helper method to define mock.On call // Validate is a helper method to define mock.On call
// - ctx EvaluationContext // - ctx EvaluationContext
// - condition models.Condition // - condition models.Condition
func (_e *FakeEvaluator_Expecter) Validate(ctx interface{}, condition interface{}) *FakeEvaluator_Validate_Call { func (_e *FakeEvaluator_Expecter) Validate(ctx interface{}, condition interface{}) *FakeEvaluator_Validate_Call {
return &FakeEvaluator_Validate_Call{Call: _e.mock.On("Validate", ctx, condition)} return &FakeEvaluator_Validate_Call{Call: _e.mock.On("Validate", ctx, condition)}
} }

View File

@ -6,7 +6,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"sync" "sync"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
@ -19,6 +21,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/prometheus/buffered" "github.com/grafana/grafana/pkg/tsdb/prometheus/buffered"
"github.com/grafana/grafana/pkg/tsdb/prometheus/querydata" "github.com/grafana/grafana/pkg/tsdb/prometheus/querydata"
"github.com/grafana/grafana/pkg/tsdb/prometheus/resource" "github.com/grafana/grafana/pkg/tsdb/prometheus/resource"
"github.com/patrickmn/go-cache"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/yudai/gojsondiff" "github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter" "github.com/yudai/gojsondiff/formatter"
@ -32,9 +35,10 @@ type Service struct {
} }
type instance struct { type instance struct {
buffered *buffered.Buffered buffered *buffered.Buffered
queryData *querydata.QueryData queryData *querydata.QueryData
resource *resource.Resource resource *resource.Resource
versionCache *cache.Cache
} }
func ProvideService(httpClientProvider httpclient.Provider, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) *Service { func ProvideService(httpClientProvider httpclient.Provider, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) *Service {
@ -75,9 +79,10 @@ func newInstanceSettings(httpClientProvider httpclient.Provider, cfg *setting.Cf
} }
return instance{ return instance{
buffered: b, buffered: b,
queryData: qd, queryData: qd,
resource: r, resource: r,
versionCache: cache.New(time.Minute*1, time.Minute*5),
}, nil }, nil
} }
} }
@ -135,6 +140,20 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
return err return err
} }
if strings.EqualFold(req.Path, "version-detect") {
versionObj, found := i.versionCache.Get("version")
if found {
return sender.Send(versionObj.(*backend.CallResourceResponse))
}
vResp, err := i.resource.DetectVersion(ctx, req)
if err != nil {
return err
}
i.versionCache.Set("version", vResp, cache.DefaultExpiration)
return sender.Send(vResp)
}
resp, err := i.resource.Execute(ctx, req) resp, err := i.resource.Execute(ctx, req)
if err != nil { if err != nil {
return err return err

View File

@ -98,3 +98,23 @@ func (r *Resource) Execute(ctx context.Context, req *backend.CallResourceRequest
return callResponse, err return callResponse, err
} }
func (r *Resource) DetectVersion(ctx context.Context, req *backend.CallResourceRequest) (*backend.CallResourceResponse, error) {
newReq := &backend.CallResourceRequest{
PluginContext: req.PluginContext,
Path: "/api/v1/status/buildinfo",
}
resp, err := r.Execute(ctx, newReq)
if err != nil {
return nil, err
}
callResponse := &backend.CallResourceResponse{
Status: 200,
Body: resp.Body,
}
return callResponse, nil
}

View File

@ -146,7 +146,7 @@ describe('RuleEditor', () => {
mocks.searchFolders.mockResolvedValue([]); mocks.searchFolders.mockResolvedValue([]);
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -345,7 +345,7 @@ describe('RuleEditor', () => {
mocks.searchFolders.mockResolvedValue([]); mocks.searchFolders.mockResolvedValue([]);
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -598,7 +598,7 @@ describe('RuleEditor', () => {
mocks.api.discoverFeatures.mockImplementation(async (dataSourceName) => { mocks.api.discoverFeatures.mockImplementation(async (dataSourceName) => {
if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') { if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') {
return { return {
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
alertManagerConfigApi: false, alertManagerConfigApi: false,
@ -609,7 +609,7 @@ describe('RuleEditor', () => {
} }
if (dataSourceName === 'loki with local rule store') { if (dataSourceName === 'loki with local rule store') {
return { return {
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: false, rulerApiEnabled: false,
alertManagerConfigApi: false, alertManagerConfigApi: false,
@ -620,7 +620,7 @@ describe('RuleEditor', () => {
} }
if (dataSourceName === 'cortex without ruler api') { if (dataSourceName === 'cortex without ruler api') {
return { return {
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: false, rulerApiEnabled: false,
alertManagerConfigApi: false, alertManagerConfigApi: false,

View File

@ -234,7 +234,7 @@ describe('RuleList', () => {
setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom })); setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom }));
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -376,7 +376,7 @@ describe('RuleList', () => {
setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom })); setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom }));
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -524,7 +524,7 @@ describe('RuleList', () => {
setDataSourceSrv(new MockDataSourceSrv(testDatasources)); setDataSourceSrv(new MockDataSourceSrv(testDatasources));
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -710,7 +710,7 @@ describe('RuleList', () => {
mocks.getAllDataSourcesMock.mockReturnValue([dataSources.prom]); mocks.getAllDataSourcesMock.mockReturnValue([dataSources.prom]);
setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom })); setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom }));
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },
@ -737,7 +737,7 @@ describe('RuleList', () => {
mocks.getAllDataSourcesMock.mockReturnValue([dataSources.prom]); mocks.getAllDataSourcesMock.mockReturnValue([dataSources.prom]);
setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom })); setDataSourceSrv(new MockDataSourceSrv({ prom: dataSources.prom }));
mocks.api.discoverFeatures.mockResolvedValue({ mocks.api.discoverFeatures.mockResolvedValue({
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: true, rulerApiEnabled: true,
}, },

View File

@ -93,7 +93,7 @@ describe('discoverDataSourceFeatures', () => {
const response = await discoverDataSourceFeatures({ url: '/datasource/proxy', name: 'Loki', type: 'loki' }); const response = await discoverDataSourceFeatures({ url: '/datasource/proxy', name: 'Loki', type: 'loki' });
expect(response.application).toBe(PromApplication.Lotex); expect(response.application).toBe(PromApplication.Cortex);
expect(response.features.rulerApiEnabled).toBe(true); expect(response.features.rulerApiEnabled).toBe(true);
expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1); expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1);
@ -126,7 +126,7 @@ describe('discoverDataSourceFeatures', () => {
type: 'prometheus', type: 'prometheus',
}); });
expect(response.application).toBe(PromApplication.Lotex); expect(response.application).toBe(PromApplication.Cortex);
expect(response.features.rulerApiEnabled).toBe(false); expect(response.features.rulerApiEnabled).toBe(false);
expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1); expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1);
@ -152,7 +152,7 @@ describe('discoverDataSourceFeatures', () => {
type: 'prometheus', type: 'prometheus',
}); });
expect(response.application).toBe(PromApplication.Lotex); expect(response.application).toBe(PromApplication.Cortex);
expect(response.features.rulerApiEnabled).toBe(true); expect(response.features.rulerApiEnabled).toBe(true);
expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1); expect(mocks.fetchTestRulerRulesGroup).toHaveBeenCalledTimes(1);

View File

@ -78,7 +78,7 @@ export async function discoverDataSourceFeatures(dsSettings: {
const rulerSupported = await hasRulerSupport(name); const rulerSupported = await hasRulerSupport(name);
return { return {
application: PromApplication.Lotex, application: PromApplication.Cortex,
features: { features: {
rulerApiEnabled: rulerSupported, rulerApiEnabled: rulerSupported,
}, },

View File

@ -266,7 +266,7 @@ export const fetchRulesSourceBuildInfoAction = createAsyncThunk(
const rulerConfig: RulerDataSourceConfig | undefined = buildInfo.features.rulerApiEnabled const rulerConfig: RulerDataSourceConfig | undefined = buildInfo.features.rulerApiEnabled
? { ? {
dataSourceName: name, dataSourceName: name,
apiVersion: buildInfo.application === PromApplication.Lotex ? 'legacy' : 'config', apiVersion: buildInfo.application === PromApplication.Cortex ? 'legacy' : 'config',
} }
: undefined; : undefined;

View File

@ -86,7 +86,7 @@ export type ViewProps = {
onNameChange: (name: string) => AnyAction; onNameChange: (name: string) => AnyAction;
onOptionsChange: (dataSource: DataSourceSettingsType) => AnyAction; onOptionsChange: (dataSource: DataSourceSettingsType) => AnyAction;
onTest: () => void; onTest: () => void;
onUpdate: (dataSource: DataSourceSettingsType) => Promise<void>; onUpdate: (dataSource: DataSourceSettingsType) => Promise<DataSourceSettingsType>;
}; };
export function EditDataSourceView({ export function EditDataSourceView({

View File

@ -19,7 +19,7 @@ import { DataSourcePluginCategory, ThunkDispatch, ThunkResult } from 'app/types'
import * as api from '../api'; import * as api from '../api';
import { DATASOURCES_ROUTES } from '../constants'; import { DATASOURCES_ROUTES } from '../constants';
import { trackDataSourceCreated, trackDataSourceTested } from '../tracking'; import { trackDataSourceCreated, trackDataSourceTested } from '../tracking';
import { nameExits, findNewName } from '../utils'; import { findNewName, nameExits } from '../utils';
import { buildCategories } from './buildCategories'; import { buildCategories } from './buildCategories';
import { buildNavModel } from './navModel'; import { buildNavModel } from './navModel';
@ -231,8 +231,8 @@ export function loadDataSourcePlugins(): ThunkResult<void> {
}; };
} }
export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> { export function updateDataSource(dataSource: DataSourceSettings) {
return async (dispatch) => { return async (dispatch: (dataSourceSettings: ThunkResult<Promise<DataSourceSettings>>) => DataSourceSettings) => {
await api.updateDataSource(dataSource); await api.updateDataSource(dataSource);
await getDatasourceSrv().reload(); await getDatasourceSrv().reload();
return dispatch(loadDataSource(dataSource.uid)); return dispatch(loadDataSource(dataSource.uid));

View File

@ -0,0 +1,80 @@
export const PromFlavorVersions: { [index: string]: Array<{ value?: string; label: string }> } = {
Prometheus: [
{ value: undefined, label: 'Please select' },
{ value: '2.0.0', label: '< 2.14.x' },
{ value: '2.14.0', label: '2.14.x' },
{ value: '2.15.0', label: '2.15.x' },
{ value: '2.16.0', label: '2.16.x' },
{ value: '2.17.0', label: '2.17.x' },
{ value: '2.18.0', label: '2.18.x' },
{ value: '2.19.0', label: '2.19.x' },
{ value: '2.20.0', label: '2.20.x' },
{ value: '2.21.0', label: '2.21.x' },
{ value: '2.22.0', label: '2.22.x' },
{ value: '2.23.0', label: '2.23.x' },
{ value: '2.24.0', label: '2.24.x' },
{ value: '2.25.0', label: '2.25.x' },
{ value: '2.26.0', label: '2.26.x' },
{ value: '2.27.0', label: '2.27.x' },
{ value: '2.28.0', label: '2.28.x' },
{ value: '2.29.0', label: '2.29.x' },
{ value: '2.30.0', label: '2.30.x' },
{ value: '2.31.0', label: '2.31.x' },
{ value: '2.32.0', label: '2.32.x' },
{ value: '2.33.0', label: '2.33.x' },
{ value: '2.34.0', label: '2.34.x' },
{ value: '2.35.0', label: '2.35.x' },
{ value: '2.36.0', label: '2.36.x' },
{ value: '2.37.0', label: '2.37.x' },
{ value: '2.38.0', label: '2.38.x' },
{ value: '2.39.0', label: '2.39.x' },
{ value: '2.40.0', label: '2.40.x' },
// This value will be returned for future versions of prometheus until we add new entries to this object
{ value: '2.40.1', label: '> 2.40.x' },
],
Mimir: [
{ value: undefined, label: 'Please select' },
{ value: '2.0.0', label: '2.0.x' },
{ value: '2.1.0', label: '2.1.x' },
{ value: '2.2.0', label: '2.2.x' },
{ value: '2.3.0', label: '2.3.x' },
{ value: '2.4.0', label: '> 2.3.x' },
],
Thanos: [
{ value: undefined, label: 'Please select' },
{ value: '0.0.0', label: '< 0.16.x' },
{ value: '0.16.0', label: '0.16.x' },
{ value: '0.17.0', label: '0.17.x' },
{ value: '0.18.0', label: '0.18.x' },
{ value: '0.19.0', label: '0.19.x' },
{ value: '0.20.0', label: '0.20.x' },
{ value: '0.21.0', label: '0.21.x' },
{ value: '0.22.0', label: '0.22.x' },
{ value: '0.23.0', label: '0.23.x' },
{ value: '0.24.0', label: '0.24.x' },
{ value: '0.25.0', label: '0.25.x' },
{ value: '0.26.0', label: '0.26.x' },
{ value: '0.27.0', label: '0.27.x' },
{ value: '0.28.0', label: '0.28.x' },
{ value: '0.29.0', label: '> 0.28.x' },
],
Cortex: [
{ value: undefined, label: 'Please select' },
{ value: '0.0.0', label: '< 1.0.0' },
{ value: '1.0.0', label: '1.0.0' },
{ value: '1.1.0', label: '1.1.x' },
{ value: '1.2.0', label: '1.2.x' },
{ value: '1.3.0', label: '1.3.x' },
{ value: '1.4.0', label: '1.4.x' },
{ value: '1.5.0', label: '1.5.x' },
{ value: '1.6.0', label: '1.6.x' },
{ value: '1.7.0', label: '1.7.x' },
{ value: '1.8.0', label: '1.8.x' },
{ value: '1.9.0', label: '1.9.x' },
{ value: '1.10.0', label: '1.10.x' },
{ value: '1.11.0', label: '1.11.x' },
{ value: '1.13.0', label: '1.13.x' },
{ value: '1.14.0', label: '> 1.13.x' },
],
};

View File

@ -1,9 +1,12 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import React, { SyntheticEvent } from 'react'; import React, { SyntheticEvent } from 'react';
import { Provider } from 'react-redux';
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { EventsWithValidation } from '@grafana/ui'; import { EventsWithValidation } from '@grafana/ui';
import { configureStore } from '../../../../store/configureStore';
import { getValueFromEventItem, promSettingsValidationEvents, PromSettings } from './PromSettings'; import { getValueFromEventItem, promSettingsValidationEvents, PromSettings } from './PromSettings';
import { createDefaultConfigOptions } from './mocks'; import { createDefaultConfigOptions } from './mocks';
@ -94,11 +97,12 @@ describe('PromSettings', () => {
const options = defaultProps; const options = defaultProps;
options.url = ''; options.url = '';
options.jsonData.httpMethod = ''; options.jsonData.httpMethod = '';
const store = configureStore();
render( render(
<div> <Provider store={store}>
<PromSettings onOptionsChange={() => {}} options={options} /> <PromSettings onOptionsChange={() => {}} options={options} />
</div> </Provider>
); );
expect(screen.getByText('POST')).toBeInTheDocument(); expect(screen.getByText('POST')).toBeInTheDocument();
}); });
@ -106,11 +110,12 @@ describe('PromSettings', () => {
const options = defaultProps; const options = defaultProps;
options.url = 'test_url'; options.url = 'test_url';
options.jsonData.httpMethod = 'POST'; options.jsonData.httpMethod = 'POST';
const store = configureStore();
render( render(
<div> <Provider store={store}>
<PromSettings onOptionsChange={() => {}} options={options} /> <PromSettings onOptionsChange={() => {}} options={options} />
</div> </Provider>
); );
expect(screen.getByText('POST')).toBeInTheDocument(); expect(screen.getByText('POST')).toBeInTheDocument();
}); });
@ -118,11 +123,12 @@ describe('PromSettings', () => {
const options = defaultProps; const options = defaultProps;
options.url = 'test_url'; options.url = 'test_url';
options.jsonData.httpMethod = 'GET'; options.jsonData.httpMethod = 'GET';
const store = configureStore();
render( render(
<div> <Provider store={store}>
<PromSettings onOptionsChange={() => {}} options={options} /> <PromSettings onOptionsChange={() => {}} options={options} />
</div> </Provider>
); );
expect(screen.getByText('GET')).toBeInTheDocument(); expect(screen.getByText('GET')).toBeInTheDocument();
}); });

View File

@ -1,24 +1,31 @@
import React, { SyntheticEvent } from 'react'; import React, { SyntheticEvent } from 'react';
import semver from 'semver/preload';
import { import {
DataSourcePluginOptionsEditorProps, DataSourcePluginOptionsEditorProps,
DataSourceSettings as DataSourceSettingsType,
onUpdateDatasourceJsonDataOptionChecked, onUpdateDatasourceJsonDataOptionChecked,
SelectableValue, SelectableValue,
updateDatasourcePluginJsonDataOption, updateDatasourcePluginJsonDataOption,
} from '@grafana/data'; } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime/src';
import { import {
InlineField,
InlineSwitch,
EventsWithValidation, EventsWithValidation,
InlineField,
InlineFormLabel, InlineFormLabel,
InlineSwitch,
LegacyForms, LegacyForms,
regexValidation, regexValidation,
Select, Select,
} from '@grafana/ui'; } from '@grafana/ui';
import { useUpdateDatasource } from '../../../../features/datasources/state';
import { PromApplication, PromBuildInfoResponse } from '../../../../types/unified-alerting-dto';
import { PromOptions } from '../types'; import { PromOptions } from '../types';
import { ExemplarsSettings } from './ExemplarsSettings'; import { ExemplarsSettings } from './ExemplarsSettings';
import { PromFlavorVersions } from './PromFlavorVersions';
const { Input, FormField } = LegacyForms; const { Input, FormField } = LegacyForms;
const httpOptions = [ const httpOptions = [
@ -26,13 +33,110 @@ const httpOptions = [
{ value: 'GET', label: 'GET' }, { value: 'GET', label: 'GET' },
]; ];
type PrometheusSelectItemsType = Array<{ value: PromApplication; label: PromApplication }>;
const prometheusFlavorSelectItems: PrometheusSelectItemsType = [
{ value: PromApplication.Prometheus, label: PromApplication.Prometheus },
{ value: PromApplication.Cortex, label: PromApplication.Cortex },
{ value: PromApplication.Mimir, label: PromApplication.Mimir },
{ value: PromApplication.Thanos, label: PromApplication.Thanos },
];
type Props = Pick<DataSourcePluginOptionsEditorProps<PromOptions>, 'options' | 'onOptionsChange'>; type Props = Pick<DataSourcePluginOptionsEditorProps<PromOptions>, 'options' | 'onOptionsChange'>;
/**
* Returns the closest version to what the user provided that we have in our PromFlavorVersions for the currently selected flavor
* Bugs: It will only reject versions that are a major release apart, so Mimir 2.x might get selected for Prometheus 2.8 if the user selects an incorrect flavor
* Advantages: We don't need to maintain a list of every possible version for each release
*
* This function will return the closest version from PromFlavorVersions that is equal or lower to the version argument,
* unless the versions are a major release apart.
*/
const getVersionString = (version: string, flavor?: string): string | undefined => {
if (!flavor || !PromFlavorVersions[flavor]) {
return;
}
const flavorVersionValues = PromFlavorVersions[flavor];
// As long as it's assured we're using versions which are sorted, we could just filter out the values greater than the target version, and then check the last element in the array
const versionsLessThanOrEqual = flavorVersionValues
?.filter((el) => !!el.value && semver.lte(el.value, version))
.map((el) => el.value);
const closestVersion = versionsLessThanOrEqual[versionsLessThanOrEqual.length - 1];
if (closestVersion) {
const differenceBetweenActualAndClosest = semver.diff(closestVersion, version);
// Only return versions if the target is close to the actual.
if (['patch', 'prepatch', 'prerelease', null].includes(differenceBetweenActualAndClosest)) {
return closestVersion;
}
}
return;
};
const unableToDeterminePrometheusVersion = (error?: Error): void => {
console.warn('Error fetching version from buildinfo API, must manually select version!', error);
};
/**
* I don't like the daisy chain of network requests, and that we have to save on behalf of the user, but currently
* the backend doesn't allow for the prometheus client url to be passed in from the frontend, so we currently need to save it
* to the database before consumption.
*
* Since the prometheus version fields are below the url field, we can expect users to populate this field before
* hitting save and test at the bottom of the page. For this case we need to save the current fields before calling the
* resource to auto-detect the version.
*
* @param options
* @param onOptionsChange
* @param onUpdate
*/
const setPrometheusVersion = (
options: DataSourceSettingsType<PromOptions>,
onOptionsChange: (options: DataSourceSettingsType<PromOptions>) => void,
onUpdate: (dataSource: DataSourceSettingsType<PromOptions>) => Promise<DataSourceSettingsType<PromOptions>>
) => {
// This will save the current state of the form, as the url is needed for this API call to function
onUpdate(options)
.then((updatedOptions) => {
getBackendSrv()
.get(`/api/datasources/${updatedOptions.id}/resources/version-detect`)
.then((rawResponse: PromBuildInfoResponse) => {
const rawVersionStringFromApi = rawResponse.data?.version ?? '';
if (rawVersionStringFromApi && semver.valid(rawVersionStringFromApi)) {
const parsedVersion = getVersionString(rawVersionStringFromApi, updatedOptions.jsonData.prometheusType);
// If we got a successful response, let's update the backend with the version right away if it's new
if (parsedVersion) {
onUpdate({
...updatedOptions,
jsonData: {
...updatedOptions.jsonData,
prometheusVersion: parsedVersion,
},
}).then((updatedUpdatedOptions) => {
onOptionsChange(updatedUpdatedOptions);
});
}
} else {
unableToDeterminePrometheusVersion();
}
});
})
.catch((error) => {
unableToDeterminePrometheusVersion(error);
});
};
export const PromSettings = (props: Props) => { export const PromSettings = (props: Props) => {
const { options, onOptionsChange } = props; const { options, onOptionsChange } = props;
// We are explicitly adding httpMethod so it is correctly displayed in dropdown. This way, it is more predictable for users. // This update call is typed as void, but it returns a response which we need
const onUpdate = useUpdateDatasource();
// We are explicitly adding httpMethod so it is correctly displayed in dropdown. This way, it is more predictable for users.
if (!options.jsonData.httpMethod) { if (!options.jsonData.httpMethod) {
options.jsonData.httpMethod = 'POST'; options.jsonData.httpMethod = 'POST';
} }
@ -40,6 +144,7 @@ export const PromSettings = (props: Props) => {
return ( return (
<> <>
<div className="gf-form-group"> <div className="gf-form-group">
{/* Scrape interval */}
<div className="gf-form-inline"> <div className="gf-form-inline">
<div className="gf-form"> <div className="gf-form">
<FormField <FormField
@ -59,6 +164,7 @@ export const PromSettings = (props: Props) => {
/> />
</div> </div>
</div> </div>
{/* Query Timeout */}
<div className="gf-form-inline"> <div className="gf-form-inline">
<div className="gf-form"> <div className="gf-form">
<FormField <FormField
@ -78,12 +184,13 @@ export const PromSettings = (props: Props) => {
/> />
</div> </div>
</div> </div>
{/* HTTP Method */}
<div className="gf-form"> <div className="gf-form">
<InlineFormLabel <InlineFormLabel
width={13} width={13}
tooltip="You can use either POST or GET HTTP method to query your Prometheus data source. POST is the recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network." tooltip="You can use either POST or GET HTTP method to query your Prometheus data source. POST is the recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network."
> >
HTTP Method HTTP method
</InlineFormLabel> </InlineFormLabel>
<Select <Select
aria-label="Select HTTP method" aria-label="Select HTTP method"
@ -94,6 +201,77 @@ export const PromSettings = (props: Props) => {
/> />
</div> </div>
</div> </div>
<h3 className="page-heading">Type and version</h3>
{!options.jsonData.prometheusType && !options.jsonData.prometheusVersion && options.readOnly && (
<div style={{ marginBottom: '12px' }}>
For more information on configuring prometheus type and version in data sources, see the{' '}
<a
style={{ textDecoration: 'underline' }}
href="https://grafana.com/docs/grafana/latest/administration/provisioning/"
>
provisioning documentation
</a>
.
</div>
)}
<div className="gf-form-group">
<div className="gf-form">
<div className="gf-form">
<FormField
label="Prometheus type"
labelWidth={13}
inputEl={
<Select
aria-label="Prometheus type"
options={prometheusFlavorSelectItems}
value={prometheusFlavorSelectItems.find((o) => o.value === options.jsonData.prometheusType)}
onChange={onChangeHandler(
'prometheusType',
{
...options,
jsonData: { ...options.jsonData, prometheusVersion: undefined },
},
(options) => {
// Check buildinfo api and set default version if we can
setPrometheusVersion(options, onOptionsChange, onUpdate);
return onOptionsChange({
...options,
jsonData: { ...options.jsonData, prometheusVersion: undefined },
});
}
)}
width={20}
/>
}
tooltip="Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing this field will save your current settings, and attempt to detect the version."
/>
</div>
</div>
<div className="gf-form">
{options.jsonData.prometheusType && (
<div className="gf-form">
<FormField
label={`${options.jsonData.prometheusType} version`}
labelWidth={13}
inputEl={
<Select
aria-label={`${options.jsonData.prometheusType} type`}
options={PromFlavorVersions[options.jsonData.prometheusType]}
value={PromFlavorVersions[options.jsonData.prometheusType]?.find(
(o) => o.value === options.jsonData.prometheusVersion
)}
onChange={onChangeHandler('prometheusVersion', options, onOptionsChange)}
width={20}
/>
}
tooltip={`Use this to set the version of your ${options.jsonData.prometheusType} instance if it is not automatically configured.`}
/>
</div>
)}
</div>
</div>
<h3 className="page-heading">Misc</h3> <h3 className="page-heading">Misc</h3>
<div className="gf-form-group"> <div className="gf-form-group">
<div className="gf-form"> <div className="gf-form">
@ -113,7 +291,7 @@ export const PromSettings = (props: Props) => {
<FormField <FormField
label="Custom query parameters" label="Custom query parameters"
labelWidth={14} labelWidth={14}
tooltip="Add Custom parameters to all Prometheus or Thanos queries." tooltip="Add custom parameters to all Prometheus or Thanos queries."
inputEl={ inputEl={
<Input <Input
className="width-25" className="width-25"

View File

@ -89,6 +89,8 @@ export class PrometheusDatasource
exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined; exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined;
lookupsDisabled: boolean; lookupsDisabled: boolean;
customQueryParameters: any; customQueryParameters: any;
datasourceConfigurationPrometheusFlavor?: PromApplication;
datasourceConfigurationPrometheusVersion?: string;
exemplarsAvailable: boolean; exemplarsAvailable: boolean;
subType: PromApplication; subType: PromApplication;
rulerEnabled: boolean; rulerEnabled: boolean;
@ -121,6 +123,8 @@ export class PrometheusDatasource
this.languageProvider = languageProvider ?? new PrometheusLanguageProvider(this); this.languageProvider = languageProvider ?? new PrometheusLanguageProvider(this);
this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false; this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false;
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters); this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
this.datasourceConfigurationPrometheusFlavor = instanceSettings.jsonData.prometheusType;
this.datasourceConfigurationPrometheusVersion = instanceSettings.jsonData.prometheusVersion;
this.variables = new PrometheusVariableSupport(this, this.templateSrv, this.timeSrv); this.variables = new PrometheusVariableSupport(this, this.templateSrv, this.timeSrv);
this.exemplarsAvailable = true; this.exemplarsAvailable = true;
@ -849,11 +853,6 @@ export class PrometheusDatasource
); );
} }
async getSubtitle(): Promise<JSX.Element | null> {
const buildInfo = await this.getBuildInfo();
return buildInfo ? this.getBuildInfoMessage(buildInfo) : null;
}
async getTagKeys(options?: any) { async getTagKeys(options?: any) {
if (options?.series) { if (options?.series) {
// Get tags for the provided series only // Get tags for the provided series only
@ -902,23 +901,28 @@ export class PrometheusDatasource
); );
const LOGOS = { const LOGOS = {
[PromApplication.Lotex]: '/public/app/plugins/datasource/prometheus/img/cortex_logo.svg', [PromApplication.Cortex]: '/public/app/plugins/datasource/prometheus/img/cortex_logo.svg',
[PromApplication.Mimir]: '/public/app/plugins/datasource/prometheus/img/mimir_logo.svg', [PromApplication.Mimir]: '/public/app/plugins/datasource/prometheus/img/mimir_logo.svg',
[PromApplication.Prometheus]: '/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg', [PromApplication.Prometheus]: '/public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
[PromApplication.Thanos]: '/public/app/plugins/datasource/prometheus/img/thanos_logo.svg',
}; };
const COLORS: Record<PromApplication, BadgeColor> = { const COLORS: Record<PromApplication, BadgeColor> = {
[PromApplication.Lotex]: 'blue', [PromApplication.Cortex]: 'blue',
[PromApplication.Mimir]: 'orange', [PromApplication.Mimir]: 'orange',
[PromApplication.Prometheus]: 'red', [PromApplication.Prometheus]: 'red',
[PromApplication.Thanos]: 'purple', // Purple hex taken from thanos.io
}; };
const AppDisplayNames: Record<PromApplication, string> = { const AppDisplayNames: Record<PromApplication, string> = {
[PromApplication.Lotex]: 'Cortex', [PromApplication.Cortex]: 'Cortex',
[PromApplication.Mimir]: 'Mimir', [PromApplication.Mimir]: 'Mimir',
[PromApplication.Prometheus]: 'Prometheus', [PromApplication.Prometheus]: 'Prometheus',
[PromApplication.Thanos]: 'Thanos',
}; };
const application = this.datasourceConfigurationPrometheusFlavor ?? buildInfo.application;
// this will inform the user about what "subtype" the datasource is; Mimir, Cortex or vanilla Prometheus // this will inform the user about what "subtype" the datasource is; Mimir, Cortex or vanilla Prometheus
const applicationSubType = ( const applicationSubType = (
<Badge <Badge
@ -926,13 +930,13 @@ export class PrometheusDatasource
<span> <span>
<img <img
style={{ width: 14, height: 14, verticalAlign: 'text-bottom' }} style={{ width: 14, height: 14, verticalAlign: 'text-bottom' }}
src={LOGOS[buildInfo.application ?? PromApplication.Prometheus]} src={LOGOS[application ?? PromApplication.Prometheus]}
alt="" alt=""
/>{' '} />{' '}
{buildInfo.application ? AppDisplayNames[buildInfo.application] : 'Unknown'} {application ? AppDisplayNames[application] : 'Unknown'}
</span> </span>
} }
color={COLORS[buildInfo.application ?? PromApplication.Prometheus]} color={COLORS[application ?? PromApplication.Prometheus]}
/> />
); );

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="2.41 2.16 355.42 355.17"><path fill="#fff" d="M9.49808 9.14822v341.59786h241.93485a99.66306 99.66306 0 0 0 99.657-99.663V9.14822zm266.263 215.32837h12.75033v12.75018h-12.75035zm-4.00963-54.89h20.71531v20.72131h-20.71533zm-3.186-54.10261h27.09335v27.11729h-27.09343zm-43.739 159.90321h12.7381v12.75018h-12.75017zm-3.98558-55.53932h20.72129V240.587h-20.72129zm-3.186-53.44731h27.12344v27.09337h-27.09339zm3.186-27.00322v-20.71528h20.72129v20.71527zm-97.82954 135.98981h12.75021v12.75018h-12.75021zm-3.98558-54.8901h20.72131v20.69121h-20.72124zm3.98558-34.1748v-12.75019h12.75021v12.75018zm-3.98558-67.64019h20.72131v20.71527h-20.72124zM72.10081 275.38715H84.875v12.75018H72.10081zm0-50.91056H84.875v12.75018H72.10081zm-3.97955-54.89h20.7153v20.72131h-20.7153zm-3.19206-54.10265h27.09939v27.11729H64.9292zM53.01459 52.67679h254.53469v50.91052H205.74622v203.6302h-50.90449v-203.6302H53.01459z"/></svg>

After

Width:  |  Height:  |  Size: 958 B

View File

@ -1,5 +1,7 @@
import { DataQuery, DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data'; import { DataQuery, DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data';
import { PromApplication } from '../../../types/unified-alerting-dto';
import { QueryEditorMode } from './querybuilder/shared/types'; import { QueryEditorMode } from './querybuilder/shared/types';
export interface PromQuery extends DataQuery { export interface PromQuery extends DataQuery {
@ -30,6 +32,8 @@ export interface PromOptions extends DataSourceJsonData {
customQueryParameters?: string; customQueryParameters?: string;
disableMetricsLookup?: boolean; disableMetricsLookup?: boolean;
exemplarTraceIdDestinations?: ExemplarTraceIdDestination[]; exemplarTraceIdDestinations?: ExemplarTraceIdDestination[];
prometheusType?: PromApplication;
prometheusVersion?: string;
} }
export enum PromQueryType { export enum PromQueryType {

View File

@ -54,10 +54,12 @@ export enum PromRuleType {
Alerting = 'alerting', Alerting = 'alerting',
Recording = 'recording', Recording = 'recording',
} }
export enum PromApplication { export enum PromApplication {
Lotex = 'Lotex', Cortex = 'Cortex',
Mimir = 'Mimir', Mimir = 'Mimir',
Prometheus = 'Prometheus', Prometheus = 'Prometheus',
Thanos = 'Thanos',
} }
export interface PromBuildInfoResponse { export interface PromBuildInfoResponse {