loki: better resource-call urls (#48250)

This commit is contained in:
Gábor Farkas 2022-04-28 14:28:57 +02:00 committed by GitHub
parent 9688d7d7aa
commit ae926c2144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 29 additions and 26 deletions

View File

@ -98,11 +98,12 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
if req.Method != "GET" { if req.Method != "GET" {
return fmt.Errorf("invalid resource method: %s", req.Method) return fmt.Errorf("invalid resource method: %s", req.Method)
} }
if (!strings.HasPrefix(url, "/loki/api/v1/labels?")) && if (!strings.HasPrefix(url, "labels?")) &&
(!strings.HasPrefix(url, "/loki/api/v1/label/")) && // the `/label/$label_name/values` form (!strings.HasPrefix(url, "label/")) && // the `/label/$label_name/values` form
(!strings.HasPrefix(url, "/loki/api/v1/series?")) { (!strings.HasPrefix(url, "series?")) {
return fmt.Errorf("invalid resource URL: %s", url) return fmt.Errorf("invalid resource URL: %s", url)
} }
lokiURL := fmt.Sprintf("/loki/api/v1/%s", url)
dsInfo, err := s.getDSInfo(req.PluginContext) dsInfo, err := s.getDSInfo(req.PluginContext)
if err != nil { if err != nil {
@ -110,7 +111,7 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
} }
api := newLokiAPI(dsInfo.HTTPClient, dsInfo.URL, s.plog) api := newLokiAPI(dsInfo.HTTPClient, dsInfo.URL, s.plog)
bytes, err := api.RawQuery(ctx, url) bytes, err := api.RawQuery(ctx, lokiURL)
if err != nil { if err != nil {
return err return err

View File

@ -1000,7 +1000,7 @@ describe('LokiDatasource', () => {
describe('importing queries', () => { describe('importing queries', () => {
it('keeps all labels when no labels are loaded', async () => { it('keeps all labels when no labels are loaded', async () => {
const ds = createLokiDSForTests(); const ds = createLokiDSForTests();
fetchMock.mockImplementation(() => of(createFetchResponse({ data: [] }))); ds.getResource = () => Promise.resolve({ data: [] });
const queries = await ds.importFromAbstractQueries([ const queries = await ds.importFromAbstractQueries([
{ {
refId: 'A', refId: 'A',
@ -1015,7 +1015,9 @@ describe('LokiDatasource', () => {
it('filters out non existing labels', async () => { it('filters out non existing labels', async () => {
const ds = createLokiDSForTests(); const ds = createLokiDSForTests();
fetchMock.mockImplementation(() => of(createFetchResponse({ data: ['foo'] }))); ds.getResource = () => {
return Promise.resolve({ data: ['foo'] });
};
const queries = await ds.importFromAbstractQueries([ const queries = await ds.importFromAbstractQueries([
{ {
refId: 'A', refId: 'A',

View File

@ -439,13 +439,13 @@ export class LokiDatasource
} }
async metadataRequest(url: string, params?: Record<string, string | number>) { async metadataRequest(url: string, params?: Record<string, string | number>) {
if (config.featureToggles.lokiBackendMode) { // url must not start with a `/`, otherwise the AJAX-request
// going from the browser will contain `//`, which can cause problems.
if (url.startsWith('/')) {
throw new Error(`invalid metadata request url: ${url}`);
}
const res = await this.getResource(url, params); const res = await this.getResource(url, params);
return res.data || []; return res.data || [];
} else {
const res = await lastValueFrom(this._request(url, params, { hideFromInspector: true }));
return res.data.data || [];
}
} }
async metricFindQuery(query: string) { async metricFindQuery(query: string) {
@ -479,7 +479,7 @@ export class LokiDatasource
} }
async labelNamesQuery() { async labelNamesQuery() {
const url = `${LOKI_ENDPOINT}/labels`; const url = 'labels';
const params = this.getTimeRangeParams(); const params = this.getTimeRangeParams();
const result = await this.metadataRequest(url, params); const result = await this.metadataRequest(url, params);
return result.map((value: string) => ({ text: value })); return result.map((value: string) => ({ text: value }));
@ -487,7 +487,7 @@ export class LokiDatasource
async labelValuesQuery(label: string) { async labelValuesQuery(label: string) {
const params = this.getTimeRangeParams(); const params = this.getTimeRangeParams();
const url = `${LOKI_ENDPOINT}/label/${label}/values`; const url = `label/${label}/values`;
const result = await this.metadataRequest(url, params); const result = await this.metadataRequest(url, params);
return result.map((value: string) => ({ text: value })); return result.map((value: string) => ({ text: value }));
} }
@ -498,7 +498,7 @@ export class LokiDatasource
...timeParams, ...timeParams,
'match[]': expr, 'match[]': expr,
}; };
const url = `${LOKI_ENDPOINT}/series`; const url = 'series';
const streams = new Set(); const streams = new Set();
const result = await this.metadataRequest(url, params); const result = await this.metadataRequest(url, params);
result.forEach((stream: { [key: string]: string }) => { result.forEach((stream: { [key: string]: string }) => {

View File

@ -95,7 +95,7 @@ describe('Language completion provider', () => {
const fetchSeries = languageProvider.fetchSeries; const fetchSeries = languageProvider.fetchSeries;
const requestSpy = jest.spyOn(languageProvider, 'request'); const requestSpy = jest.spyOn(languageProvider, 'request');
fetchSeries('{job="grafana"}'); fetchSeries('{job="grafana"}');
expect(requestSpy).toHaveBeenCalledWith('/loki/api/v1/series', { expect(requestSpy).toHaveBeenCalledWith('series', {
end: 1560163909000, end: 1560163909000,
'match[]': '{job="grafana"}', 'match[]': '{job="grafana"}',
start: 1560153109000, start: 1560153109000,
@ -116,7 +116,7 @@ describe('Language completion provider', () => {
const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue([]); const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue([]);
fetchSeriesLabels('$stream'); fetchSeriesLabels('$stream');
expect(requestSpy).toHaveBeenCalled(); expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith('/loki/api/v1/series', { expect(requestSpy).toHaveBeenCalledWith('series', {
end: 1, end: 1,
'match[]': 'interpolated-stream', 'match[]': 'interpolated-stream',
start: 0, start: 0,
@ -259,7 +259,7 @@ describe('Request URL', () => {
const instance = new LanguageProvider(datasourceWithLabels); const instance = new LanguageProvider(datasourceWithLabels);
instance.fetchLabels(); instance.fetchLabels();
const expectedUrl = '/loki/api/v1/labels'; const expectedUrl = 'labels';
expect(datasourceSpy).toHaveBeenCalledWith(expectedUrl, rangeParams); expect(datasourceSpy).toHaveBeenCalledWith(expectedUrl, rangeParams);
}); });
}); });

View File

@ -364,7 +364,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
* Fetches all label keys * Fetches all label keys
*/ */
async fetchLabels(): Promise<string[]> { async fetchLabels(): Promise<string[]> {
const url = '/loki/api/v1/labels'; const url = 'labels';
const timeRange = this.datasource.getTimeRangeParams(); const timeRange = this.datasource.getTimeRangeParams();
this.labelFetchTs = Date.now().valueOf(); this.labelFetchTs = Date.now().valueOf();
@ -393,7 +393,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
*/ */
fetchSeriesLabels = async (match: string): Promise<Record<string, string[]>> => { fetchSeriesLabels = async (match: string): Promise<Record<string, string[]>> => {
const interpolatedMatch = this.datasource.interpolateString(match); const interpolatedMatch = this.datasource.interpolateString(match);
const url = '/loki/api/v1/series'; const url = 'series';
const { start, end } = this.datasource.getTimeRangeParams(); const { start, end } = this.datasource.getTimeRangeParams();
const cacheKey = this.generateCacheKey(url, start, end, interpolatedMatch); const cacheKey = this.generateCacheKey(url, start, end, interpolatedMatch);
@ -415,7 +415,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
* @param match * @param match
*/ */
fetchSeries = async (match: string): Promise<Array<Record<string, string>>> => { fetchSeries = async (match: string): Promise<Array<Record<string, string>>> => {
const url = '/loki/api/v1/series'; const url = 'series';
const { start, end } = this.datasource.getTimeRangeParams(); const { start, end } = this.datasource.getTimeRangeParams();
const params = { 'match[]': match, start, end }; const params = { 'match[]': match, start, end };
return await this.request(url, params); return await this.request(url, params);
@ -440,7 +440,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
async fetchLabelValues(key: string): Promise<string[]> { async fetchLabelValues(key: string): Promise<string[]> {
const interpolatedKey = this.datasource.interpolateString(key); const interpolatedKey = this.datasource.interpolateString(key);
const url = `/loki/api/v1/label/${interpolatedKey}/values`; const url = `label/${interpolatedKey}/values`;
const rangeParams = this.datasource.getTimeRangeParams(); const rangeParams = this.datasource.getTimeRangeParams();
const { start, end } = rangeParams; const { start, end } = rangeParams;

View File

@ -2,7 +2,7 @@ import { DataSourceSettings } from '@grafana/data';
import { createDatasourceSettings } from '../../../features/datasources/mocks'; import { createDatasourceSettings } from '../../../features/datasources/mocks';
import { LokiDatasource, LOKI_ENDPOINT } from './datasource'; import { LokiDatasource } from './datasource';
import { LokiOptions } from './types'; import { LokiOptions } from './types';
interface Labels { interface Labels {
@ -18,10 +18,10 @@ interface SeriesForSelector {
} }
export function makeMockLokiDatasource(labelsAndValues: Labels, series?: SeriesForSelector): LokiDatasource { export function makeMockLokiDatasource(labelsAndValues: Labels, series?: SeriesForSelector): LokiDatasource {
const lokiLabelsAndValuesEndpointRegex = /^\/loki\/api\/v1\/label\/(\w*)\/values/; const lokiLabelsAndValuesEndpointRegex = /^label\/(\w*)\/values/;
const lokiSeriesEndpointRegex = /^\/loki\/api\/v1\/series/; const lokiSeriesEndpointRegex = /^series/;
const lokiLabelsEndpoint = `${LOKI_ENDPOINT}/labels`; const lokiLabelsEndpoint = 'labels';
const rangeMock = { const rangeMock = {
start: 1560153109000, start: 1560153109000,
end: 1560163909000, end: 1560163909000,