mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #11726 from grafana/11708_prom_variables
prometheus: variable query should fallback correctly to a series query
This commit is contained in:
@@ -329,4 +329,8 @@ export class PrometheusDatasource {
|
|||||||
}
|
}
|
||||||
return Math.ceil(date.valueOf() / 1000);
|
return Math.ceil(date.valueOf() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOriginalMetricName(labelData) {
|
||||||
|
return this.resultTransformer.getOriginalMetricName(labelData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default class PrometheusMetricFindQuery {
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.datasource.metadataRequest(url).then(function(result) {
|
return this.datasource.metadataRequest(url).then(function(result) {
|
||||||
return _.map(result.data.data, function(metric) {
|
return _.map(result.data.data, metric => {
|
||||||
return {
|
return {
|
||||||
text: self.datasource.getOriginalMetricName(metric),
|
text: self.datasource.getOriginalMetricName(metric),
|
||||||
expandable: true,
|
expandable: true,
|
||||||
|
|||||||
@@ -43,6 +43,26 @@ describe('PrometheusDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When performing performSuggestQuery', () => {
|
||||||
|
it('should cache response', async () => {
|
||||||
|
ctx.backendSrvMock.datasourceRequest.mockReturnValue(
|
||||||
|
Promise.resolve({
|
||||||
|
status: 'success',
|
||||||
|
data: { data: ['value1', 'value2', 'value3'] },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let results = await ctx.ds.performSuggestQuery('value', true);
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
|
||||||
|
ctx.backendSrvMock.datasourceRequest.mockReset();
|
||||||
|
results = await ctx.ds.performSuggestQuery('value', true);
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('When converting prometheus histogram to heatmap format', () => {
|
describe('When converting prometheus histogram to heatmap format', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctx.query = {
|
ctx.query = {
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import { PrometheusDatasource } from '../datasource';
|
||||||
|
import PrometheusMetricFindQuery from '../metric_find_query';
|
||||||
|
import q from 'q';
|
||||||
|
|
||||||
|
describe('PrometheusMetricFindQuery', function() {
|
||||||
|
let instanceSettings = {
|
||||||
|
url: 'proxied',
|
||||||
|
directUrl: 'direct',
|
||||||
|
user: 'test',
|
||||||
|
password: 'mupp',
|
||||||
|
jsonData: { httpMethod: 'GET' },
|
||||||
|
};
|
||||||
|
const raw = {
|
||||||
|
from: moment.utc('2018-04-25 10:00'),
|
||||||
|
to: moment.utc('2018-04-25 11:00'),
|
||||||
|
};
|
||||||
|
let ctx: any = {
|
||||||
|
backendSrvMock: {
|
||||||
|
datasourceRequest: jest.fn(() => Promise.resolve({})),
|
||||||
|
},
|
||||||
|
templateSrvMock: {
|
||||||
|
replace: a => a,
|
||||||
|
},
|
||||||
|
timeSrvMock: {
|
||||||
|
timeRange: () => ({
|
||||||
|
from: raw.from,
|
||||||
|
to: raw.to,
|
||||||
|
raw: raw,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.setupMetricFindQuery = (data: any) => {
|
||||||
|
ctx.backendSrvMock.datasourceRequest.mockReturnValue(Promise.resolve({ status: 'success', data: data.response }));
|
||||||
|
return new PrometheusMetricFindQuery(ctx.ds, data.query, ctx.timeSrvMock);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ctx.backendSrvMock.datasourceRequest.mockReset();
|
||||||
|
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When performing metricFindQuery', () => {
|
||||||
|
it('label_values(resource) should generate label search query', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'label_values(resource)',
|
||||||
|
response: {
|
||||||
|
data: ['value1', 'value2', 'value3'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'proxied/api/v1/label/resource/values',
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('label_values(metric, resource) should generate series query with correct time', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'label_values(metric, resource)',
|
||||||
|
response: {
|
||||||
|
data: [
|
||||||
|
{ __name__: 'metric', resource: 'value1' },
|
||||||
|
{ __name__: 'metric', resource: 'value2' },
|
||||||
|
{ __name__: 'metric', resource: 'value3' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('label_values(metric{label1="foo", label2="bar", label3="baz"}, resource) should generate series query with correct time', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'label_values(metric{label1="foo", label2="bar", label3="baz"}, resource)',
|
||||||
|
response: {
|
||||||
|
data: [
|
||||||
|
{ __name__: 'metric', resource: 'value1' },
|
||||||
|
{ __name__: 'metric', resource: 'value2' },
|
||||||
|
{ __name__: 'metric', resource: 'value3' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: `proxied/api/v1/series?match[]=${encodeURIComponent(
|
||||||
|
'metric{label1="foo", label2="bar", label3="baz"}'
|
||||||
|
)}&start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('label_values(metric, resource) result should not contain empty string', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'label_values(metric, resource)',
|
||||||
|
response: {
|
||||||
|
data: [
|
||||||
|
{ __name__: 'metric', resource: 'value1' },
|
||||||
|
{ __name__: 'metric', resource: 'value2' },
|
||||||
|
{ __name__: 'metric', resource: '' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(2);
|
||||||
|
expect(results[0].text).toBe('value1');
|
||||||
|
expect(results[1].text).toBe('value2');
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: `proxied/api/v1/series?match[]=metric&start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('metrics(metric.*) should generate metric name query', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'metrics(metric.*)',
|
||||||
|
response: {
|
||||||
|
data: ['metric1', 'metric2', 'metric3', 'nomatch'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'proxied/api/v1/label/__name__/values',
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('query_result(metric) should generate metric name query', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'query_result(metric)',
|
||||||
|
response: {
|
||||||
|
data: {
|
||||||
|
resultType: 'vector',
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
metric: { __name__: 'metric', job: 'testjob' },
|
||||||
|
value: [1443454528.0, '3846'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(1);
|
||||||
|
expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000');
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`,
|
||||||
|
requestId: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('up{job="job1"} should fallback using generate series query', async () => {
|
||||||
|
const query = ctx.setupMetricFindQuery({
|
||||||
|
query: 'up{job="job1"}',
|
||||||
|
response: {
|
||||||
|
data: [
|
||||||
|
{ __name__: 'up', instance: '127.0.0.1:1234', job: 'job1' },
|
||||||
|
{ __name__: 'up', instance: '127.0.0.1:5678', job: 'job1' },
|
||||||
|
{ __name__: 'up', instance: '127.0.0.1:9102', job: 'job1' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const results = await query.process();
|
||||||
|
|
||||||
|
expect(results).toHaveLength(3);
|
||||||
|
expect(results[0].text).toBe('up{instance="127.0.0.1:1234",job="job1"}');
|
||||||
|
expect(results[1].text).toBe('up{instance="127.0.0.1:5678",job="job1"}');
|
||||||
|
expect(results[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}');
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ctx.backendSrvMock.datasourceRequest).toHaveBeenCalledWith({
|
||||||
|
method: 'GET',
|
||||||
|
url: `proxied/api/v1/series?match[]=${encodeURIComponent(
|
||||||
|
'up{job="job1"}'
|
||||||
|
)}&start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common';
|
|
||||||
|
|
||||||
import moment from 'moment';
|
|
||||||
import helpers from 'test/specs/helpers';
|
|
||||||
import { PrometheusDatasource } from '../datasource';
|
|
||||||
import PrometheusMetricFindQuery from '../metric_find_query';
|
|
||||||
|
|
||||||
describe('PrometheusMetricFindQuery', function() {
|
|
||||||
var ctx = new helpers.ServiceTestContext();
|
|
||||||
var instanceSettings = {
|
|
||||||
url: 'proxied',
|
|
||||||
directUrl: 'direct',
|
|
||||||
user: 'test',
|
|
||||||
password: 'mupp',
|
|
||||||
jsonData: { httpMethod: 'GET' },
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(angularMocks.module('grafana.core'));
|
|
||||||
beforeEach(angularMocks.module('grafana.services'));
|
|
||||||
beforeEach(
|
|
||||||
angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
|
|
||||||
ctx.$q = $q;
|
|
||||||
ctx.$httpBackend = $httpBackend;
|
|
||||||
ctx.$rootScope = $rootScope;
|
|
||||||
ctx.ds = $injector.instantiate(PrometheusDatasource, {
|
|
||||||
instanceSettings: instanceSettings,
|
|
||||||
});
|
|
||||||
$httpBackend.when('GET', /\.html$/).respond('');
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
describe('When performing metricFindQuery', function() {
|
|
||||||
var results;
|
|
||||||
var response;
|
|
||||||
it('label_values(resource) should generate label search query', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: ['value1', 'value2', 'value3'],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/resource/values').respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(resource)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
});
|
|
||||||
it('label_values(metric, resource) should generate series query', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: [
|
|
||||||
{ __name__: 'metric', resource: 'value1' },
|
|
||||||
{ __name__: 'metric', resource: 'value2' },
|
|
||||||
{ __name__: 'metric', resource: 'value3' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
});
|
|
||||||
it('label_values(metric, resource) should pass correct time', function() {
|
|
||||||
ctx.timeSrv.setTime({
|
|
||||||
from: moment.utc('2011-01-01'),
|
|
||||||
to: moment.utc('2015-01-01'),
|
|
||||||
});
|
|
||||||
ctx.$httpBackend
|
|
||||||
.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=1293840000&end=1420070400/)
|
|
||||||
.respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
});
|
|
||||||
it('label_values(metric{label1="foo", label2="bar", label3="baz"}, resource) should generate series query', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: [
|
|
||||||
{ __name__: 'metric', resource: 'value1' },
|
|
||||||
{ __name__: 'metric', resource: 'value2' },
|
|
||||||
{ __name__: 'metric', resource: 'value3' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
});
|
|
||||||
it('label_values(metric, resource) result should not contain empty string', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: [
|
|
||||||
{ __name__: 'metric', resource: 'value1' },
|
|
||||||
{ __name__: 'metric', resource: 'value2' },
|
|
||||||
{ __name__: 'metric', resource: '' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/series\?match\[\]=metric&start=.*&end=.*/).respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(2);
|
|
||||||
expect(results[0].text).to.be('value1');
|
|
||||||
expect(results[1].text).to.be('value2');
|
|
||||||
});
|
|
||||||
it('metrics(metric.*) should generate metric name query', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: ['metric1', 'metric2', 'metric3', 'nomatch'],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'metrics(metric.*)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
});
|
|
||||||
it('query_result(metric) should generate metric name query', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: {
|
|
||||||
resultType: 'vector',
|
|
||||||
result: [
|
|
||||||
{
|
|
||||||
metric: { __name__: 'metric', job: 'testjob' },
|
|
||||||
value: [1443454528.0, '3846'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/query\?query=metric&time=.*/).respond(response);
|
|
||||||
var pm = new PrometheusMetricFindQuery(ctx.ds, 'query_result(metric)', ctx.timeSrv);
|
|
||||||
pm.process().then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(1);
|
|
||||||
expect(results[0].text).to.be('metric{job="testjob"} 3846 1443454528000');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When performing performSuggestQuery', function() {
|
|
||||||
var results;
|
|
||||||
var response;
|
|
||||||
it('cache response', function() {
|
|
||||||
response = {
|
|
||||||
status: 'success',
|
|
||||||
data: ['value1', 'value2', 'value3'],
|
|
||||||
};
|
|
||||||
ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response);
|
|
||||||
ctx.ds.performSuggestQuery('value', true).then(function(data) {
|
|
||||||
results = data;
|
|
||||||
});
|
|
||||||
ctx.$httpBackend.flush();
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
ctx.ds.performSuggestQuery('value', true).then(function(data) {
|
|
||||||
// get from cache, no need to flush
|
|
||||||
results = data;
|
|
||||||
expect(results.length).to.be(3);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user