From ae926c2144bb9a610554274212cfdd67c01cd42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Farkas?= Date: Thu, 28 Apr 2022 14:28:57 +0200 Subject: [PATCH] loki: better resource-call urls (#48250) --- pkg/tsdb/loki/loki.go | 9 +++++---- .../plugins/datasource/loki/datasource.test.ts | 6 ++++-- .../app/plugins/datasource/loki/datasource.ts | 18 +++++++++--------- .../datasource/loki/language_provider.test.ts | 6 +++--- .../datasource/loki/language_provider.ts | 8 ++++---- public/app/plugins/datasource/loki/mocks.ts | 8 ++++---- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/pkg/tsdb/loki/loki.go b/pkg/tsdb/loki/loki.go index 67d5b8b6c8d..4404d86d961 100644 --- a/pkg/tsdb/loki/loki.go +++ b/pkg/tsdb/loki/loki.go @@ -98,11 +98,12 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq if req.Method != "GET" { return fmt.Errorf("invalid resource method: %s", req.Method) } - if (!strings.HasPrefix(url, "/loki/api/v1/labels?")) && - (!strings.HasPrefix(url, "/loki/api/v1/label/")) && // the `/label/$label_name/values` form - (!strings.HasPrefix(url, "/loki/api/v1/series?")) { + if (!strings.HasPrefix(url, "labels?")) && + (!strings.HasPrefix(url, "label/")) && // the `/label/$label_name/values` form + (!strings.HasPrefix(url, "series?")) { return fmt.Errorf("invalid resource URL: %s", url) } + lokiURL := fmt.Sprintf("/loki/api/v1/%s", url) dsInfo, err := s.getDSInfo(req.PluginContext) 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) - bytes, err := api.RawQuery(ctx, url) + bytes, err := api.RawQuery(ctx, lokiURL) if err != nil { return err diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 9bc7587cbfb..e47abeecf95 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -1000,7 +1000,7 @@ describe('LokiDatasource', () => { describe('importing queries', () => { it('keeps all labels when no labels are loaded', async () => { const ds = createLokiDSForTests(); - fetchMock.mockImplementation(() => of(createFetchResponse({ data: [] }))); + ds.getResource = () => Promise.resolve({ data: [] }); const queries = await ds.importFromAbstractQueries([ { refId: 'A', @@ -1015,7 +1015,9 @@ describe('LokiDatasource', () => { it('filters out non existing labels', async () => { const ds = createLokiDSForTests(); - fetchMock.mockImplementation(() => of(createFetchResponse({ data: ['foo'] }))); + ds.getResource = () => { + return Promise.resolve({ data: ['foo'] }); + }; const queries = await ds.importFromAbstractQueries([ { refId: 'A', diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 9bae8fc4890..cc0ec9cafb6 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -439,13 +439,13 @@ export class LokiDatasource } async metadataRequest(url: string, params?: Record) { - if (config.featureToggles.lokiBackendMode) { - const res = await this.getResource(url, params); - return res.data || []; - } else { - const res = await lastValueFrom(this._request(url, params, { hideFromInspector: true })); - return res.data.data || []; + // 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); + return res.data || []; } async metricFindQuery(query: string) { @@ -479,7 +479,7 @@ export class LokiDatasource } async labelNamesQuery() { - const url = `${LOKI_ENDPOINT}/labels`; + const url = 'labels'; const params = this.getTimeRangeParams(); const result = await this.metadataRequest(url, params); return result.map((value: string) => ({ text: value })); @@ -487,7 +487,7 @@ export class LokiDatasource async labelValuesQuery(label: string) { const params = this.getTimeRangeParams(); - const url = `${LOKI_ENDPOINT}/label/${label}/values`; + const url = `label/${label}/values`; const result = await this.metadataRequest(url, params); return result.map((value: string) => ({ text: value })); } @@ -498,7 +498,7 @@ export class LokiDatasource ...timeParams, 'match[]': expr, }; - const url = `${LOKI_ENDPOINT}/series`; + const url = 'series'; const streams = new Set(); const result = await this.metadataRequest(url, params); result.forEach((stream: { [key: string]: string }) => { diff --git a/public/app/plugins/datasource/loki/language_provider.test.ts b/public/app/plugins/datasource/loki/language_provider.test.ts index 194f388ef00..de2bcdd94ae 100644 --- a/public/app/plugins/datasource/loki/language_provider.test.ts +++ b/public/app/plugins/datasource/loki/language_provider.test.ts @@ -95,7 +95,7 @@ describe('Language completion provider', () => { const fetchSeries = languageProvider.fetchSeries; const requestSpy = jest.spyOn(languageProvider, 'request'); fetchSeries('{job="grafana"}'); - expect(requestSpy).toHaveBeenCalledWith('/loki/api/v1/series', { + expect(requestSpy).toHaveBeenCalledWith('series', { end: 1560163909000, 'match[]': '{job="grafana"}', start: 1560153109000, @@ -116,7 +116,7 @@ describe('Language completion provider', () => { const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue([]); fetchSeriesLabels('$stream'); expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy).toHaveBeenCalledWith('/loki/api/v1/series', { + expect(requestSpy).toHaveBeenCalledWith('series', { end: 1, 'match[]': 'interpolated-stream', start: 0, @@ -259,7 +259,7 @@ describe('Request URL', () => { const instance = new LanguageProvider(datasourceWithLabels); instance.fetchLabels(); - const expectedUrl = '/loki/api/v1/labels'; + const expectedUrl = 'labels'; expect(datasourceSpy).toHaveBeenCalledWith(expectedUrl, rangeParams); }); }); diff --git a/public/app/plugins/datasource/loki/language_provider.ts b/public/app/plugins/datasource/loki/language_provider.ts index 9b5aeba208d..0d7cb538ade 100644 --- a/public/app/plugins/datasource/loki/language_provider.ts +++ b/public/app/plugins/datasource/loki/language_provider.ts @@ -364,7 +364,7 @@ export default class LokiLanguageProvider extends LanguageProvider { * Fetches all label keys */ async fetchLabels(): Promise { - const url = '/loki/api/v1/labels'; + const url = 'labels'; const timeRange = this.datasource.getTimeRangeParams(); this.labelFetchTs = Date.now().valueOf(); @@ -393,7 +393,7 @@ export default class LokiLanguageProvider extends LanguageProvider { */ fetchSeriesLabels = async (match: string): Promise> => { const interpolatedMatch = this.datasource.interpolateString(match); - const url = '/loki/api/v1/series'; + const url = 'series'; const { start, end } = this.datasource.getTimeRangeParams(); const cacheKey = this.generateCacheKey(url, start, end, interpolatedMatch); @@ -415,7 +415,7 @@ export default class LokiLanguageProvider extends LanguageProvider { * @param match */ fetchSeries = async (match: string): Promise>> => { - const url = '/loki/api/v1/series'; + const url = 'series'; const { start, end } = this.datasource.getTimeRangeParams(); const params = { 'match[]': match, start, end }; return await this.request(url, params); @@ -440,7 +440,7 @@ export default class LokiLanguageProvider extends LanguageProvider { async fetchLabelValues(key: string): Promise { 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 { start, end } = rangeParams; diff --git a/public/app/plugins/datasource/loki/mocks.ts b/public/app/plugins/datasource/loki/mocks.ts index 4fb4b50dbf3..47fce43c4dd 100644 --- a/public/app/plugins/datasource/loki/mocks.ts +++ b/public/app/plugins/datasource/loki/mocks.ts @@ -2,7 +2,7 @@ import { DataSourceSettings } from '@grafana/data'; import { createDatasourceSettings } from '../../../features/datasources/mocks'; -import { LokiDatasource, LOKI_ENDPOINT } from './datasource'; +import { LokiDatasource } from './datasource'; import { LokiOptions } from './types'; interface Labels { @@ -18,10 +18,10 @@ interface SeriesForSelector { } export function makeMockLokiDatasource(labelsAndValues: Labels, series?: SeriesForSelector): LokiDatasource { - const lokiLabelsAndValuesEndpointRegex = /^\/loki\/api\/v1\/label\/(\w*)\/values/; - const lokiSeriesEndpointRegex = /^\/loki\/api\/v1\/series/; + const lokiLabelsAndValuesEndpointRegex = /^label\/(\w*)\/values/; + const lokiSeriesEndpointRegex = /^series/; - const lokiLabelsEndpoint = `${LOKI_ENDPOINT}/labels`; + const lokiLabelsEndpoint = 'labels'; const rangeMock = { start: 1560153109000, end: 1560163909000,