Commit 1dd4f031 by Daniel Lee Committed by GitHub

Merge pull request #11726 from grafana/11708_prom_variables

prometheus: variable query should fallback correctly to a series query 
parents 37581610 f112e382
......@@ -329,4 +329,8 @@ export class PrometheusDatasource {
}
return Math.ceil(date.valueOf() / 1000);
}
getOriginalMetricName(labelData) {
return this.resultTransformer.getOriginalMetricName(labelData);
}
}
......@@ -121,7 +121,7 @@ export default class PrometheusMetricFindQuery {
var self = this;
return this.datasource.metadataRequest(url).then(function(result) {
return _.map(result.data.data, function(metric) {
return _.map(result.data.data, metric => {
return {
text: self.datasource.getOriginalMetricName(metric),
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', () => {
beforeEach(() => {
ctx.query = {
......
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,
});
});
});
});
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);
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment