mirror of
https://github.com/grafana/grafana.git
synced 2025-12-21 20:24:41 +08:00
Compare commits
3 Commits
zoltan/pos
...
remove-inf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac8ba8aa16 | ||
|
|
8d747ecf00 | ||
|
|
88100b0037 |
@@ -30,7 +30,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
||||
| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes |
|
||||
| `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes |
|
||||
| `lokiQuerySplitting` | Split large interval queries into subqueries with smaller time intervals | Yes |
|
||||
| `influxdbBackendMigration` | Query InfluxDB InfluxQL without the proxy | Yes |
|
||||
| `unifiedRequestLog` | Writes error logs to the request logger | Yes |
|
||||
| `logsExploreTableVisualisation` | A table visualisation for logs in Explore | Yes |
|
||||
| `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | Yes |
|
||||
|
||||
@@ -97,11 +97,6 @@ export interface FeatureToggles {
|
||||
*/
|
||||
individualCookiePreferences?: boolean;
|
||||
/**
|
||||
* Query InfluxDB InfluxQL without the proxy
|
||||
* @default true
|
||||
*/
|
||||
influxdbBackendMigration?: boolean;
|
||||
/**
|
||||
* populate star status from apiserver
|
||||
*/
|
||||
starsFromAPIServer?: boolean;
|
||||
|
||||
@@ -137,14 +137,6 @@ var (
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaBackendGroup,
|
||||
},
|
||||
{
|
||||
Name: "influxdbBackendMigration",
|
||||
Description: "Query InfluxDB InfluxQL without the proxy",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
FrontendOnly: true,
|
||||
Owner: grafanaPartnerPluginsSquad,
|
||||
Expression: "true", // enabled by default
|
||||
},
|
||||
{
|
||||
Name: "starsFromAPIServer",
|
||||
Description: "populate star status from apiserver",
|
||||
|
||||
1
pkg/services/featuremgmt/toggles_gen.csv
generated
1
pkg/services/featuremgmt/toggles_gen.csv
generated
@@ -17,7 +17,6 @@ logsContextDatasourceUi,GA,@grafana/observability-logs,false,false,true
|
||||
lokiShardSplitting,experimental,@grafana/observability-logs,false,false,true
|
||||
lokiQuerySplitting,GA,@grafana/observability-logs,false,false,true
|
||||
individualCookiePreferences,experimental,@grafana/grafana-backend-group,false,false,false
|
||||
influxdbBackendMigration,GA,@grafana/partner-datasources,false,false,true
|
||||
starsFromAPIServer,experimental,@grafana/grafana-search-navigate-organise,false,false,true
|
||||
kubernetesStars,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
influxqlStreamingParser,experimental,@grafana/partner-datasources,false,false,false
|
||||
|
||||
|
@@ -1,14 +1,11 @@
|
||||
import { lastValueFrom, of } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { AdHocVariableFilter } from '@grafana/data';
|
||||
import { BackendSrvRequest, TemplateSrv } from '@grafana/runtime';
|
||||
import config from 'app/core/config';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { queryBuilder } from '../../../features/variables/shared/testing/builders';
|
||||
|
||||
import { BROWSER_MODE_DISABLED_MESSAGE } from './constants';
|
||||
import InfluxDatasource from './datasource';
|
||||
import { getMockDSInstanceSettings, getMockInfluxDS, mockBackendService, replaceMock } from './mocks/datasource';
|
||||
import { getMockDSInstanceSettings, getMockInfluxDS, mockBackendService } from './mocks/datasource';
|
||||
import { mockInfluxQueryRequest } from './mocks/request';
|
||||
import { mockInfluxFetchResponse, mockMetricFindQueryResponse } from './mocks/response';
|
||||
import { InfluxQuery, InfluxVersion } from './types';
|
||||
@@ -24,231 +21,8 @@ describe('datasource initialization', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Remove this suite when influxdbBackendMigration feature toggle removed
|
||||
describe('InfluxDataSource Frontend Mode [influxdbBackendMigration=false]', () => {
|
||||
describe('InfluxDataSource Backend Mode', () => {
|
||||
beforeEach(() => {
|
||||
// we want only frontend mode in this suite
|
||||
config.featureToggles.influxdbBackendMigration = false;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('general checks', () => {
|
||||
it('should throw an error if there is 200 response with error', async () => {
|
||||
const ds = getMockInfluxDS();
|
||||
fetchMock.mockImplementation(() => {
|
||||
return of({
|
||||
data: {
|
||||
results: [
|
||||
{
|
||||
error: 'Query timeout',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await lastValueFrom(ds.query(mockInfluxQueryRequest()));
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
expect(err.message).toBe('InfluxDB Error: Query timeout');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an error when querying data when deprecated access mode', async () => {
|
||||
expect.assertions(1);
|
||||
const instanceSettings = getMockDSInstanceSettings();
|
||||
instanceSettings.access = 'direct';
|
||||
const ds = getMockInfluxDS(instanceSettings);
|
||||
try {
|
||||
await lastValueFrom(ds.query(mockInfluxQueryRequest()));
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
expect(err.message).toBe(BROWSER_MODE_DISABLED_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('metricFindQuery', () => {
|
||||
let ds: InfluxDatasource;
|
||||
const query = 'SELECT max(value) FROM measurement WHERE $timeFilter';
|
||||
const queryOptions = {
|
||||
range: {
|
||||
from: '2018-01-01T00:00:00Z',
|
||||
to: '2018-01-02T00:00:00Z',
|
||||
},
|
||||
};
|
||||
const fetchMockImpl = (req: BackendSrvRequest) => {
|
||||
return of({
|
||||
data: {
|
||||
status: 'success',
|
||||
results: [
|
||||
{
|
||||
series: [
|
||||
{
|
||||
name: 'measurement',
|
||||
columns: ['name'],
|
||||
values: [['cpu']],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
fetchMock.mockImplementation(fetchMockImpl);
|
||||
});
|
||||
|
||||
it('should replace $timefilter', async () => {
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'GET' }));
|
||||
await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(fetchMock.mock.lastCall[0].params?.q).toMatch('time >= 1514764800000ms and time <= 1514851200000ms');
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'POST' }));
|
||||
await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(fetchMock.mock.lastCall[0].params?.q).toBeFalsy();
|
||||
expect(fetchMock.mock.lastCall[0].data).toMatch(
|
||||
'time%20%3E%3D%201514764800000ms%20and%20time%20%3C%3D%201514851200000ms'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not have any data in request body if http mode is GET', async () => {
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'GET' }));
|
||||
await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(fetchMock.mock.lastCall[0].data).toBeNull();
|
||||
});
|
||||
|
||||
it('should have data in request body if http mode is POST', async () => {
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'POST' }));
|
||||
await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(fetchMock.mock.lastCall[0].data).not.toBeNull();
|
||||
expect(fetchMock.mock.lastCall[0].data).toMatch('q=SELECT');
|
||||
});
|
||||
|
||||
it('parse response correctly', async () => {
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'GET' }));
|
||||
let responseGet = await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(responseGet).toEqual([{ text: 'cpu' }]);
|
||||
ds = getMockInfluxDS(getMockDSInstanceSettings({ httpMode: 'POST' }));
|
||||
let responsePost = await ds.metricFindQuery({ refId: 'test', query }, queryOptions);
|
||||
expect(responsePost).toEqual([{ text: 'cpu' }]);
|
||||
});
|
||||
});
|
||||
|
||||
// Update this after starting to use TemplateSrv from @grafana/runtime package
|
||||
describe('adhoc variables', () => {
|
||||
let ds = getMockInfluxDS(getMockDSInstanceSettings());
|
||||
|
||||
it('query should contain the ad-hoc variable', () => {
|
||||
ds.query(mockInfluxQueryRequest());
|
||||
expect(replaceMock.mock.calls[0][0]).toBe('adhoc_val');
|
||||
});
|
||||
|
||||
it('should make the fetch call for adhoc filter keys', () => {
|
||||
fetchMock.mockReturnValue(
|
||||
of({
|
||||
results: [
|
||||
{
|
||||
statement_id: 0,
|
||||
series: [
|
||||
{
|
||||
name: 'cpu',
|
||||
columns: ['tagKey'],
|
||||
values: [['datacenter'], ['geohash'], ['source']],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
ds.getTagKeys();
|
||||
expect(fetchMock).toHaveBeenCalled();
|
||||
const fetchReq = fetchMock.mock.calls[0][0];
|
||||
expect(fetchReq).not.toBeNull();
|
||||
expect(fetchReq.data).toMatch(encodeURIComponent(`SHOW TAG KEYS`));
|
||||
});
|
||||
|
||||
it('should make the fetch call for adhoc filter values', () => {
|
||||
fetchMock.mockReturnValue(
|
||||
of({
|
||||
results: [
|
||||
{
|
||||
statement_id: 0,
|
||||
series: [
|
||||
{
|
||||
name: 'mykey',
|
||||
columns: ['key', 'value'],
|
||||
values: [['mykey', 'value']],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
ds.getTagValues({ key: 'mykey', filters: [] });
|
||||
expect(fetchMock).toHaveBeenCalled();
|
||||
const fetchReq = fetchMock.mock.calls[0][0];
|
||||
expect(fetchReq).not.toBeNull();
|
||||
expect(fetchReq.data).toMatch(encodeURIComponent(`SHOW TAG VALUES WITH KEY = "mykey"`));
|
||||
});
|
||||
});
|
||||
|
||||
describe('datasource contract', () => {
|
||||
let ds: InfluxDatasource;
|
||||
const metricFindQueryMock = jest.fn();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
ds = getMockInfluxDS();
|
||||
ds.metricFindQuery = metricFindQueryMock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should check the datasource has "getTagKeys" function defined', () => {
|
||||
expect(Object.getOwnPropertyNames(Object.getPrototypeOf(ds))).toContain('getTagKeys');
|
||||
});
|
||||
|
||||
it('should check the datasource has "getTagValues" function defined', () => {
|
||||
expect(Object.getOwnPropertyNames(Object.getPrototypeOf(ds))).toContain('getTagValues');
|
||||
});
|
||||
|
||||
it('should be able to call getTagKeys without specifying any parameter', () => {
|
||||
ds.getTagKeys();
|
||||
expect(metricFindQueryMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to call getTagValues without specifying anything but key', () => {
|
||||
ds.getTagValues({ key: 'test', filters: [] });
|
||||
expect(metricFindQueryMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use dbName instead of database', () => {
|
||||
const instanceSettings = getMockDSInstanceSettings();
|
||||
instanceSettings.database = 'should_not_be_used';
|
||||
ds = getMockInfluxDS(instanceSettings);
|
||||
expect(ds.database).toBe('site');
|
||||
});
|
||||
|
||||
it('should fallback to use use database is dbName is not exist', () => {
|
||||
const instanceSettings = getMockDSInstanceSettings();
|
||||
instanceSettings.database = 'fallback';
|
||||
instanceSettings.jsonData.dbName = undefined;
|
||||
ds = getMockInfluxDS(instanceSettings);
|
||||
expect(ds.database).toBe('fallback');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('InfluxDataSource Backend Mode [influxdbBackendMigration=true]', () => {
|
||||
beforeEach(() => {
|
||||
// we want only backend mode in this suite
|
||||
config.featureToggles.influxdbBackendMigration = true;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
TemplateSrv,
|
||||
} from '@grafana/runtime';
|
||||
import { QueryFormat, SQLQuery } from '@grafana/sql';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import { AnnotationEditor } from './components/editor/annotation/AnnotationEditor';
|
||||
import { FluxQueryEditor } from './components/editor/query/flux/FluxQueryEditor';
|
||||
@@ -665,7 +664,7 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
// ------------------------ Legacy Code - Before Backend Migration ---------------
|
||||
|
||||
isMigrationToggleOnAndIsAccessProxy() {
|
||||
return config.featureToggles.influxdbBackendMigration && this.access === 'proxy';
|
||||
return this.access === 'proxy';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { SQLQuery } from '@grafana/sql';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import InfluxDatasource from './datasource';
|
||||
import { getMockDSInstanceSettings, mockBackendService, mockTemplateSrv } from './mocks/datasource';
|
||||
@@ -9,7 +8,6 @@ import { mockInfluxQueryRequest } from './mocks/request';
|
||||
import { mockInfluxSQLFetchResponse } from './mocks/response';
|
||||
import { InfluxVersion } from './types';
|
||||
|
||||
config.featureToggles.influxdbBackendMigration = true;
|
||||
mockBackendService(mockInfluxSQLFetchResponse);
|
||||
|
||||
describe('InfluxDB SQL Support', () => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import config from 'app/core/config';
|
||||
|
||||
import { getAllMeasurements, getAllPolicies, getFieldKeys, getTagKeys, getTagValues } from './influxql_metadata_query';
|
||||
import { getMockInfluxDS } from './mocks/datasource';
|
||||
import { InfluxQuery, InfluxVariableQuery } from './types';
|
||||
@@ -27,132 +25,7 @@ describe('influx_metadata_query', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
// This should be removed when backend mode is default
|
||||
describe('backend mode disabled', () => {
|
||||
beforeEach(() => {
|
||||
config.featureToggles.influxdbBackendMigration = false;
|
||||
});
|
||||
|
||||
function frontendModeChecks() {
|
||||
expect(mockRunMetadataQuery).not.toHaveBeenCalled();
|
||||
expect(mockMetricFindQuery).toHaveBeenCalled();
|
||||
}
|
||||
|
||||
describe('getAllPolicies', () => {
|
||||
it('should call metricFindQuery with SHOW RETENTION POLICIES', () => {
|
||||
getAllPolicies(ds);
|
||||
frontendModeChecks();
|
||||
expect(query.query).toMatch('SHOW RETENTION POLICIES');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllMeasurements', () => {
|
||||
it('no tags specified', () => {
|
||||
getAllMeasurements(ds, []);
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW MEASUREMENTS LIMIT 100');
|
||||
});
|
||||
|
||||
it('with tags', () => {
|
||||
getAllMeasurements(ds, [{ key: 'key', value: 'val' }]);
|
||||
frontendModeChecks();
|
||||
expect(query.query).toMatch('SHOW MEASUREMENTS WHERE "key"');
|
||||
});
|
||||
|
||||
it('with measurement filter', () => {
|
||||
getAllMeasurements(ds, [{ key: 'key', value: 'val' }], 'measurementFilter');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toMatch('SHOW MEASUREMENTS WITH MEASUREMENT =~ /(?i)measurementFilter/ WHERE "key"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTagKeys', () => {
|
||||
it('no tags specified', () => {
|
||||
getTagKeys(ds);
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG KEYS');
|
||||
});
|
||||
|
||||
it('with measurement', () => {
|
||||
getTagKeys(ds, 'test_measurement');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG KEYS FROM "test_measurement"');
|
||||
});
|
||||
|
||||
it('with retention policy', () => {
|
||||
getTagKeys(ds, 'test_measurement', 'rp');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG KEYS FROM "rp"."test_measurement"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTagValues', () => {
|
||||
it('with key', () => {
|
||||
getTagValues(ds, [], 'test_key');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG VALUES WITH KEY = "test_key"');
|
||||
});
|
||||
|
||||
it('with key ends with ::tag', () => {
|
||||
getTagValues(ds, [], 'test_key::tag');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG VALUES WITH KEY = "test_key"');
|
||||
});
|
||||
|
||||
it('with key ends with ::field', async () => {
|
||||
const result = await getTagValues(ds, [], 'test_key::field');
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
|
||||
it('with tags', () => {
|
||||
getTagValues(ds, [{ key: 'tagKey', value: 'tag_val' }], 'test_key');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW TAG VALUES WITH KEY = "test_key" WHERE "tagKey" = \'tag_val\'');
|
||||
});
|
||||
|
||||
it('with measurement', () => {
|
||||
getTagValues(ds, [{ key: 'tagKey', value: 'tag_val' }], 'test_key', 'test_measurement');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe(
|
||||
'SHOW TAG VALUES FROM "test_measurement" WITH KEY = "test_key" WHERE "tagKey" = \'tag_val\''
|
||||
);
|
||||
});
|
||||
|
||||
it('with retention policy', () => {
|
||||
getTagValues(ds, [{ key: 'tagKey', value: 'tag_val' }], 'test_key', 'test_measurement', 'rp');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe(
|
||||
'SHOW TAG VALUES FROM "rp"."test_measurement" WITH KEY = "test_key" WHERE "tagKey" = \'tag_val\''
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFieldKeys', () => {
|
||||
it('with no retention policy', () => {
|
||||
getFieldKeys(ds, 'test_measurement');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW FIELD KEYS FROM "test_measurement"');
|
||||
});
|
||||
|
||||
it('with empty measurement', () => {
|
||||
getFieldKeys(ds, '');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW FIELD KEYS');
|
||||
});
|
||||
|
||||
it('with retention policy', () => {
|
||||
getFieldKeys(ds, 'test_measurement', 'rp');
|
||||
frontendModeChecks();
|
||||
expect(query.query).toBe('SHOW FIELD KEYS FROM "rp"."test_measurement"');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('backend mode enabled', () => {
|
||||
beforeEach(() => {
|
||||
config.featureToggles.influxdbBackendMigration = true;
|
||||
});
|
||||
|
||||
function backendModeChecks() {
|
||||
expect(mockMetricFindQuery).not.toHaveBeenCalled();
|
||||
expect(mockRunMetadataQuery).toHaveBeenCalled();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ScopedVars } from '@grafana/data';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import InfluxDatasource from './datasource';
|
||||
import { buildMetadataQuery } from './influxql_query_builder';
|
||||
@@ -38,12 +37,7 @@ const runExploreQuery = async (options: MetadataQueryOptions): Promise<Array<{ t
|
||||
rawQuery: true,
|
||||
refId: 'metadataQuery',
|
||||
};
|
||||
if (config.featureToggles.influxdbBackendMigration) {
|
||||
return datasource.runMetadataQuery(target);
|
||||
} else {
|
||||
const options = { policy: target.policy };
|
||||
return datasource.metricFindQuery({ refId: 'run-explore-query', query }, options);
|
||||
}
|
||||
return datasource.runMetadataQuery(target);
|
||||
};
|
||||
|
||||
export async function getAllPolicies(datasource: InfluxDatasource): Promise<string[]> {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { of } from 'rxjs';
|
||||
|
||||
import { AnnotationEvent, DataFrame, DataQueryRequest, dateTime, FieldType, MutableDataFrame } from '@grafana/data';
|
||||
import { FetchResponse } from '@grafana/runtime';
|
||||
import config from 'app/core/config';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
|
||||
import InfluxQueryModel from './influx_query_model';
|
||||
@@ -355,7 +354,6 @@ describe('influxdb response parser', () => {
|
||||
return of(annotationMockResponse);
|
||||
});
|
||||
|
||||
config.featureToggles.influxdbBackendMigration = true;
|
||||
response = await ctx.ds.annotationEvents(queryOptions, annotation);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user