Elasticsearch: Remove support for versions after their end of the life (<7.10.0) (#48715)

* Elasticsearch: Remove support for versions after their EOL

* Update docs

* Remove old versions from config

* Update pkg/tsdb/elasticsearch/elasticsearch.go

Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>

* Fix tests

* Fix typecheck errors

Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
This commit is contained in:
Ivana Huckova 2022-05-05 16:16:34 +02:00 committed by GitHub
parent b04fb8522d
commit 250b72cc1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 213 additions and 211 deletions

View File

@ -13,15 +13,9 @@ visualize logs or metrics stored in Elasticsearch. You can also annotate your gr
Supported Elasticsearch versions:
- v2.0+ (deprecated)
- v5.0+ (deprecated)
- v6.0+ (deprecated)
- v7.0-v7.9+ (deprecated)
- v7.10+
- v8.0+ (experimental)
> **Note:** Deprecated versions (v2.0+, v5.0+, v6.0+, and v7.0-v7.9+) will be removed in the next major release.
## Adding the data source
1. Open the side menu by clicking the Grafana icon in the top header.

View File

@ -36,15 +36,21 @@ func ProvideService(httpClientProvider httpclient.Provider) *Service {
}
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
if len(req.Queries) == 0 {
return &backend.QueryDataResponse{}, fmt.Errorf("query contains no queries")
}
dsInfo, err := s.getDSInfo(req.PluginContext)
if err != nil {
return &backend.QueryDataResponse{}, err
}
// Support for version after their end-of-life (currently <7.10.0) was removed
lastSupportedVersion, _ := semver.NewVersion("7.10.0")
if dsInfo.ESVersion.LessThan(lastSupportedVersion) {
return &backend.QueryDataResponse{}, fmt.Errorf("support for elasticsearch versions after their end-of-life (currently versions < 7.10) was removed")
}
if len(req.Queries) == 0 {
return &backend.QueryDataResponse{}, fmt.Errorf("query contains no queries")
}
client, err := es.NewClient(ctx, s.httpClientProvider, dsInfo, req.Queries[0].TimeRange)
if err != nil {
return &backend.QueryDataResponse{}, err
@ -72,7 +78,6 @@ func newInstanceSettings() datasource.InstanceFactoryFunc {
}
version, err := coerceVersion(jsonData["esVersion"])
if err != nil {
return nil, fmt.Errorf("elasticsearch version is required, err=%v", err)
}

View File

@ -7,6 +7,9 @@ import { ElasticsearchQuery } from '../../types';
import { QueryEditor } from '.';
const noop = () => void 0;
const datasourceMock = {
esVersion: '7.10.0',
} as ElasticDatasource;
describe('QueryEditor', () => {
describe('Alias Field', () => {
@ -27,7 +30,7 @@ describe('QueryEditor', () => {
const onChange = jest.fn<void, [ElasticsearchQuery]>();
render(<QueryEditor query={query} datasource={{} as ElasticDatasource} onChange={onChange} onRunQuery={noop} />);
render(<QueryEditor query={query} datasource={datasourceMock} onChange={onChange} onRunQuery={noop} />);
let aliasField = screen.getByLabelText('Alias') as HTMLInputElement;
@ -61,7 +64,7 @@ describe('QueryEditor', () => {
bucketAggs: [{ id: '2', type: 'terms' }],
};
render(<QueryEditor query={query} datasource={{} as ElasticDatasource} onChange={noop} onRunQuery={noop} />);
render(<QueryEditor query={query} datasource={datasourceMock} onChange={noop} onRunQuery={noop} />);
expect(screen.getByLabelText('Alias')).toBeDisabled();
});
@ -79,7 +82,7 @@ describe('QueryEditor', () => {
bucketAggs: [{ id: '2', type: 'date_histogram' }],
};
render(<QueryEditor query={query} datasource={{} as ElasticDatasource} onChange={noop} onRunQuery={noop} />);
render(<QueryEditor query={query} datasource={datasourceMock} onChange={noop} onRunQuery={noop} />);
expect(screen.getByLabelText('Alias')).toBeEnabled();
});
@ -99,7 +102,7 @@ describe('QueryEditor', () => {
bucketAggs: [{ id: '2', type: 'date_histogram' }],
};
render(<QueryEditor query={query} datasource={{} as ElasticDatasource} onChange={noop} onRunQuery={noop} />);
render(<QueryEditor query={query} datasource={datasourceMock} onChange={noop} onRunQuery={noop} />);
expect(screen.queryByLabelText('Group By')).not.toBeInTheDocument();
});
@ -117,7 +120,7 @@ describe('QueryEditor', () => {
bucketAggs: [{ id: '2', type: 'date_histogram' }],
};
render(<QueryEditor query={query} datasource={{} as ElasticDatasource} onChange={noop} onRunQuery={noop} />);
render(<QueryEditor query={query} datasource={datasourceMock} onChange={noop} onRunQuery={noop} />);
expect(screen.getByText('Group By')).toBeInTheDocument();
});

View File

@ -2,12 +2,13 @@ import { css } from '@emotion/css';
import React from 'react';
import { getDefaultTimeRange, GrafanaTheme2, QueryEditorProps } from '@grafana/data';
import { InlineField, InlineLabel, Input, QueryField, useStyles2 } from '@grafana/ui';
import { Alert, InlineField, InlineLabel, Input, QueryField, useStyles2 } from '@grafana/ui';
import { ElasticDatasource } from '../../datasource';
import { useNextId } from '../../hooks/useNextId';
import { useDispatch } from '../../hooks/useStatelessReducer';
import { ElasticsearchOptions, ElasticsearchQuery } from '../../types';
import { isSupportedVersion } from '../../utils';
import { BucketAggregationsEditor } from './BucketAggregationsEditor';
import { ElasticsearchProvider } from './ElasticsearchQueryContext';
@ -17,17 +18,26 @@ import { changeAliasPattern, changeQuery } from './state';
export type ElasticQueryEditorProps = QueryEditorProps<ElasticDatasource, ElasticsearchQuery, ElasticsearchOptions>;
export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range }: ElasticQueryEditorProps) => (
<ElasticsearchProvider
datasource={datasource}
onChange={onChange}
onRunQuery={onRunQuery}
query={query}
range={range || getDefaultTimeRange()}
>
<QueryEditorForm value={query} />
</ElasticsearchProvider>
);
export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range }: ElasticQueryEditorProps) => {
if (!isSupportedVersion(datasource.esVersion)) {
return (
<Alert
title={`Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) was removed`}
></Alert>
);
}
return (
<ElasticsearchProvider
datasource={datasource}
onChange={onChange}
onRunQuery={onRunQuery}
query={query}
range={range || getDefaultTimeRange()}
>
<QueryEditorForm value={query} />
</ElasticsearchProvider>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
root: css`

View File

@ -5,7 +5,7 @@ import { Alert, DataSourceHttpSettings } from '@grafana/ui';
import { config } from 'app/core/config';
import { ElasticsearchOptions } from '../types';
import { isDeprecatedVersion } from '../utils';
import { isSupportedVersion } from '../utils';
import { DataLinks } from './DataLinks';
import { ElasticDetails } from './ElasticDetails';
@ -27,7 +27,7 @@ export const ConfigEditor = (props: Props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const deprecatedVersion = isDeprecatedVersion(options.jsonData.esVersion);
const supportedVersion = isSupportedVersion(options.jsonData.esVersion);
return (
<>
@ -36,9 +36,9 @@ export const ConfigEditor = (props: Props) => {
Browser access mode in the Elasticsearch datasource is deprecated and will be removed in a future release.
</Alert>
)}
{deprecatedVersion && (
<Alert title="Deprecation notice" severity="warning">
{`Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) is deprecated and will be removed in a future release.`}
{!supportedVersion && (
<Alert title="Deprecation notice" severity="error">
{`Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) was removed`}
</Alert>
)}

View File

@ -51,18 +51,7 @@ describe('ElasticDetails', () => {
});
describe('version change', () => {
const testCases = [
{ version: '5.x', expectedMaxConcurrentShardRequests: 256 },
{ version: '5.x', maxConcurrentShardRequests: 50, expectedMaxConcurrentShardRequests: 50 },
{ version: '5.6+', expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6+', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6+', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6+', maxConcurrentShardRequests: 200, expectedMaxConcurrentShardRequests: 200 },
{ version: '7.0+', expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0+', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0+', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0+', maxConcurrentShardRequests: 6, expectedMaxConcurrentShardRequests: 6 },
];
const testCases = [{ version: '7.10+', maxConcurrentShardRequests: 6, expectedMaxConcurrentShardRequests: 6 }];
testCases.forEach((tc) => {
const onChangeMock = jest.fn();
@ -72,7 +61,7 @@ describe('ElasticDetails', () => {
onChange={onChangeMock}
value={createDefaultConfigOptions({
maxConcurrentShardRequests: tc.maxConcurrentShardRequests,
esVersion: '2.0.0',
esVersion: '7.0.0',
})}
/>
);

View File

@ -18,12 +18,6 @@ const indexPatternTypes: Array<SelectableValue<'none' | Interval>> = [
];
const esVersions: SelectableValue[] = [
{ label: '2.x', value: '2.0.0' },
{ label: '5.x', value: '5.0.0' },
{ label: '5.6+', value: '5.6.0' },
{ label: '6.0+', value: '6.0.0' },
{ label: '7.0+', value: '7.0.0' },
{ label: '7.7+', value: '7.7.0' },
{ label: '7.10+', value: '7.10.0' },
{
label: '8.0+',

View File

@ -116,7 +116,7 @@ function getTestContext({
describe('ElasticDatasource', function (this: any) {
describe('When testing datasource with index pattern', () => {
it('should translate index pattern to current day', () => {
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: 2 } });
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0' } });
ds.testDatasource();
@ -154,7 +154,7 @@ describe('ElasticDatasource', function (this: any) {
},
],
};
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: 2 }, data });
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0' }, data });
let result: any = {};
await expect(ds.query(query)).toEmitValuesWith((received) => {
@ -207,7 +207,7 @@ describe('ElasticDatasource', function (this: any) {
async function setupDataSource(jsonData?: Partial<ElasticsearchOptions>) {
jsonData = {
interval: 'Daily',
esVersion: '2.0.0',
esVersion: '7.10.0',
timeField: '@timestamp',
...(jsonData || {}),
};
@ -279,7 +279,7 @@ describe('ElasticDatasource', function (this: any) {
const query: any = { range, targets };
const data = { responses: [] };
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: 2 }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
await expect(ds.query(query)).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -322,7 +322,7 @@ describe('ElasticDatasource', function (this: any) {
it('should process it properly', async () => {
const { ds } = getTestContext({
jsonData: { interval: 'Daily', esVersion: 7 },
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
data: {
took: 1,
responses: [
@ -368,6 +368,8 @@ describe('ElasticDatasource', function (this: any) {
const { ds } = getTestContext({
mockImplementation: () => throwError(response),
from: undefined,
jsonData: { esVersion: '7.10.0' },
});
const errObject = {
@ -383,7 +385,7 @@ describe('ElasticDatasource', function (this: any) {
it('should properly throw an unknown error', async () => {
const { ds } = getTestContext({
jsonData: { interval: 'Daily', esVersion: 7 },
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
data: {
took: 1,
responses: [
@ -410,101 +412,101 @@ describe('ElasticDatasource', function (this: any) {
});
});
describe('When getting fields', () => {
const data = {
metricbeat: {
mappings: {
metricsets: {
_all: {},
_meta: {
test: 'something',
},
properties: {
'@timestamp': { type: 'date' },
__timestamp: { type: 'date' },
'@timestampnano': { type: 'date_nanos' },
beat: {
properties: {
name: {
fields: { raw: { type: 'keyword' } },
type: 'string',
},
hostname: { type: 'string' },
},
},
system: {
properties: {
cpu: {
properties: {
system: { type: 'float' },
user: { type: 'float' },
},
},
process: {
properties: {
cpu: {
properties: {
total: { type: 'float' },
},
},
name: { type: 'string' },
},
},
},
},
},
},
},
},
};
// describe('When getting fields', () => {
// const data = {
// metricbeat: {
// mappings: {
// metricsets: {
// _all: {},
// _meta: {
// test: 'something',
// },
// properties: {
// '@timestamp': { type: 'date' },
// __timestamp: { type: 'date' },
// '@timestampnano': { type: 'date_nanos' },
// beat: {
// properties: {
// name: {
// fields: { raw: { type: 'keyword' } },
// type: 'string',
// },
// hostname: { type: 'string' },
// },
// },
// system: {
// properties: {
// cpu: {
// properties: {
// system: { type: 'float' },
// user: { type: 'float' },
// },
// },
// process: {
// properties: {
// cpu: {
// properties: {
// total: { type: 'float' },
// },
// },
// name: { type: 'string' },
// },
// },
// },
// },
// },
// },
// },
// },
// };
it('should return nested fields', async () => {
const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
// it('should return nested fields', async () => {
// const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
await expect(ds.getFields()).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
const fieldObjects = received[0];
const fields = map(fieldObjects, 'text');
// await expect(ds.getFields()).toEmitValuesWith((received) => {
// expect(received.length).toBe(1);
// const fieldObjects = received[0];
// const fields = map(fieldObjects, 'text');
expect(fields).toEqual([
'@timestamp',
'__timestamp',
'@timestampnano',
'beat.name.raw',
'beat.name',
'beat.hostname',
'system.cpu.system',
'system.cpu.user',
'system.process.cpu.total',
'system.process.name',
]);
});
});
// expect(fields).toEqual([
// '@timestamp',
// '__timestamp',
// '@timestampnano',
// 'beat.name.raw',
// 'beat.name',
// 'beat.hostname',
// 'system.cpu.system',
// 'system.cpu.user',
// 'system.process.cpu.total',
// 'system.process.name',
// ]);
// });
// });
it('should return number fields', async () => {
const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
// it('should return number fields', async () => {
// const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
await expect(ds.getFields(['number'])).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
const fieldObjects = received[0];
const fields = map(fieldObjects, 'text');
// await expect(ds.getFields(['number'])).toEmitValuesWith((received) => {
// expect(received.length).toBe(1);
// const fieldObjects = received[0];
// const fields = map(fieldObjects, 'text');
expect(fields).toEqual(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']);
});
});
// expect(fields).toEqual(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']);
// });
// });
it('should return date fields', async () => {
const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
// it('should return date fields', async () => {
// const { ds } = getTestContext({ data, jsonData: { esVersion: 50 }, database: 'metricbeat' });
await expect(ds.getFields(['date'])).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
const fieldObjects = received[0];
const fields = map(fieldObjects, 'text');
// await expect(ds.getFields(['date'])).toEmitValuesWith((received) => {
// expect(received.length).toBe(1);
// const fieldObjects = received[0];
// const fields = map(fieldObjects, 'text');
expect(fields).toEqual(['@timestamp', '__timestamp', '@timestampnano']);
});
});
});
// expect(fields).toEqual(['@timestamp', '__timestamp', '@timestampnano']);
// });
// });
// });
describe('When getting field mappings on indices with gaps', () => {
const basicResponse = {
@ -525,54 +527,54 @@ describe('ElasticDatasource', function (this: any) {
},
};
const alternateResponse = {
metricbeat: {
mappings: {
metricsets: {
_all: {},
properties: {
'@timestamp': { type: 'date' },
},
},
},
},
};
// const alternateResponse = {
// metricbeat: {
// mappings: {
// metricsets: {
// _all: {},
// properties: {
// '@timestamp': { type: 'date' },
// },
// },
// },
// },
// };
it('should return fields of the newest available index', async () => {
const twoDaysBefore = toUtc().subtract(2, 'day').format('YYYY.MM.DD');
const threeDaysBefore = toUtc().subtract(3, 'day').format('YYYY.MM.DD');
const baseUrl = `${ELASTICSEARCH_MOCK_URL}/asd-${twoDaysBefore}/_mapping`;
const alternateUrl = `${ELASTICSEARCH_MOCK_URL}/asd-${threeDaysBefore}/_mapping`;
// it('should return fields of the newest available index', async () => {
// const twoDaysBefore = toUtc().subtract(2, 'day').format('YYYY.MM.DD');
// const threeDaysBefore = toUtc().subtract(3, 'day').format('YYYY.MM.DD');
// const baseUrl = `${ELASTICSEARCH_MOCK_URL}/asd-${twoDaysBefore}/_mapping`;
// const alternateUrl = `${ELASTICSEARCH_MOCK_URL}/asd-${threeDaysBefore}/_mapping`;
const { ds, timeSrv } = getTestContext({
from: 'now-2w',
jsonData: { interval: 'Daily', esVersion: 50 },
mockImplementation: (options) => {
if (options.url === baseUrl) {
return of(createFetchResponse(basicResponse));
} else if (options.url === alternateUrl) {
return of(createFetchResponse(alternateResponse));
}
return throwError({ status: 404 });
},
});
// const { ds, timeSrv } = getTestContext({
// from: 'now-2w',
// jsonData: { interval: 'Daily', esVersion: 50 },
// mockImplementation: (options) => {
// if (options.url === baseUrl) {
// return of(createFetchResponse(basicResponse));
// } else if (options.url === alternateUrl) {
// return of(createFetchResponse(alternateResponse));
// }
// return throwError({ status: 404 });
// },
// });
const range = timeSrv.timeRange();
// const range = timeSrv.timeRange();
await expect(ds.getFields(undefined, range)).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
const fieldObjects = received[0];
const fields = map(fieldObjects, 'text');
expect(fields).toEqual(['@timestamp', 'beat.hostname']);
});
});
// await expect(ds.getFields(undefined, range)).toEmitValuesWith((received) => {
// expect(received.length).toBe(1);
// const fieldObjects = received[0];
// const fields = map(fieldObjects, 'text');
// expect(fields).toEqual(['@timestamp', 'beat.hostname']);
// });
// });
it('should not retry when ES is down', async () => {
const twoDaysBefore = toUtc().subtract(2, 'day').format('YYYY.MM.DD');
const { ds, timeSrv, fetchMock } = getTestContext({
from: 'now-2w',
jsonData: { interval: 'Daily', esVersion: 50 },
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
mockImplementation: (options) => {
if (options.url === `${ELASTICSEARCH_MOCK_URL}/asd-${twoDaysBefore}/_mapping`) {
return of(createFetchResponse(basicResponse));
@ -593,7 +595,7 @@ describe('ElasticDatasource', function (this: any) {
it('should not retry more than 7 indices', async () => {
const { ds, timeSrv, fetchMock } = getTestContext({
from: 'now-2w',
jsonData: { interval: 'Daily', esVersion: 50 },
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
mockImplementation: (options) => {
return throwError({ status: 404 });
},
@ -703,7 +705,11 @@ describe('ElasticDatasource', function (this: any) {
];
it('should return nested fields', async () => {
const { ds } = getTestContext({ data, database: 'genuine.es7._mapping.response', jsonData: { esVersion: 70 } });
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields()).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -730,7 +736,11 @@ describe('ElasticDatasource', function (this: any) {
});
it('should return number fields', async () => {
const { ds } = getTestContext({ data, database: 'genuine.es7._mapping.response', jsonData: { esVersion: 70 } });
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields(['number'])).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -742,7 +752,11 @@ describe('ElasticDatasource', function (this: any) {
});
it('should return date fields', async () => {
const { ds } = getTestContext({ data, database: 'genuine.es7._mapping.response', jsonData: { esVersion: 70 } });
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields(['date'])).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -768,7 +782,7 @@ describe('ElasticDatasource', function (this: any) {
const query: any = { range, targets };
const data = { responses: [] };
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: 5 }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
await expect(ds.query(query)).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -816,7 +830,7 @@ describe('ElasticDatasource', function (this: any) {
],
};
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: 5 }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
const results = await ds.metricFindQuery('{"find": "terms", "field": "test"}');
@ -858,7 +872,7 @@ describe('ElasticDatasource', function (this: any) {
describe('query', () => {
it('should replace range as integer not string', async () => {
const { ds } = getTestContext({ jsonData: { interval: 'Daily', esVersion: 2, timeField: '@time' } });
const { ds } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0', timeField: '@time' } });
const postMock = jest.fn((url: string, data: any) => of(createFetchResponse({ responses: [] })));
ds['post'] = postMock;
@ -902,39 +916,25 @@ describe('ElasticDatasource', function (this: any) {
});
describe('getMultiSearchUrl', () => {
describe('When esVersion >= 6.6.0', () => {
describe('When esVersion >= 7.10.0', () => {
it('Should add correct params to URL if "includeFrozen" is enabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '6.6.0', includeFrozen: true, xpack: true } });
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: true, xpack: true } });
expect(ds.getMultiSearchUrl()).toMatch(/ignore_throttled=false/);
});
it('Should NOT add ignore_throttled if "includeFrozen" is disabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '6.6.0', includeFrozen: false, xpack: true } });
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: false, xpack: true } });
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
it('Should NOT add ignore_throttled if "xpack" is disabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '6.6.0', includeFrozen: true, xpack: false } });
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: true, xpack: false } });
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
});
describe('When esVersion < 6.6.0', () => {
it('Should NOT add ignore_throttled params regardless of includeFrozen', () => {
const { ds: dsWithIncludeFrozen } = getTestContext({
jsonData: { esVersion: '5.6.0', includeFrozen: false, xpack: true },
});
const { ds: dsWithoutIncludeFrozen } = getTestContext({
jsonData: { esVersion: '5.6.0', includeFrozen: true, xpack: true },
});
expect(dsWithIncludeFrozen.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
expect(dsWithoutIncludeFrozen.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
});
});
describe('enhanceDataFrame', () => {

View File

@ -48,7 +48,7 @@ import LanguageProvider from './language_provider';
import { ElasticQueryBuilder } from './query_builder';
import { defaultBucketAgg, hasMetricOfType } from './query_def';
import { DataLinkConfig, ElasticsearchOptions, ElasticsearchQuery, TermsQuery } from './types';
import { coerceESVersion, getScriptValue } from './utils';
import { coerceESVersion, getScriptValue, isSupportedVersion } from './utils';
// Those are metadata fields as defined in https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-fields.html#_identity_metadata_fields.
// custom fields can start with underscores, therefore is not safe to exclude anything that starts with one.
@ -132,6 +132,13 @@ export class ElasticDatasource
data?: undefined,
headers?: BackendSrvRequest['headers']
): Observable<any> {
if (!isSupportedVersion(this.esVersion)) {
const error = new Error(
'Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) was removed.'
);
return throwError(() => error);
}
const options: BackendSrvRequest = {
url: this.url + '/' + url,
method,

View File

@ -119,10 +119,10 @@ export const coerceESVersion = (version: string | number): string => {
}
};
export const isDeprecatedVersion = (version: string): boolean => {
export const isSupportedVersion = (version: string): boolean => {
if (gte(version, '7.10.0')) {
return false;
return true;
}
return true;
return false;
};