diff --git a/docs/sources/datasources/elasticsearch.md b/docs/sources/datasources/elasticsearch.md index 4419fce4c53..caca3987fad 100644 --- a/docs/sources/datasources/elasticsearch.md +++ b/docs/sources/datasources/elasticsearch.md @@ -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. diff --git a/pkg/tsdb/elasticsearch/elasticsearch.go b/pkg/tsdb/elasticsearch/elasticsearch.go index 2f2b2ca8d8f..547e70ac986 100644 --- a/pkg/tsdb/elasticsearch/elasticsearch.go +++ b/pkg/tsdb/elasticsearch/elasticsearch.go @@ -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) } diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.test.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.test.tsx index 24775ff5691..f95d6273cd5 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.test.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.test.tsx @@ -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(); - render(); + render(); let aliasField = screen.getByLabelText('Alias') as HTMLInputElement; @@ -61,7 +64,7 @@ describe('QueryEditor', () => { bucketAggs: [{ id: '2', type: 'terms' }], }; - render(); + render(); expect(screen.getByLabelText('Alias')).toBeDisabled(); }); @@ -79,7 +82,7 @@ describe('QueryEditor', () => { bucketAggs: [{ id: '2', type: 'date_histogram' }], }; - render(); + render(); expect(screen.getByLabelText('Alias')).toBeEnabled(); }); @@ -99,7 +102,7 @@ describe('QueryEditor', () => { bucketAggs: [{ id: '2', type: 'date_histogram' }], }; - render(); + render(); expect(screen.queryByLabelText('Group By')).not.toBeInTheDocument(); }); @@ -117,7 +120,7 @@ describe('QueryEditor', () => { bucketAggs: [{ id: '2', type: 'date_histogram' }], }; - render(); + render(); expect(screen.getByText('Group By')).toBeInTheDocument(); }); diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx index 1e7c9d32d56..3c77d6c5800 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx @@ -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; -export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range }: ElasticQueryEditorProps) => ( - - - -); +export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range }: ElasticQueryEditorProps) => { + if (!isSupportedVersion(datasource.esVersion)) { + return ( + + ); + } + return ( + + + + ); +}; const getStyles = (theme: GrafanaTheme2) => ({ root: css` diff --git a/public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.tsx b/public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.tsx index bfdbba25758..c9ed7c53e29 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.tsx @@ -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. )} - {deprecatedVersion && ( - - {`Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) is deprecated and will be removed in a future release.`} + {!supportedVersion && ( + + {`Support for Elasticsearch versions after their end-of-life (currently versions < 7.10) was removed`} )} diff --git a/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx b/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx index 444c41707e9..265f8051dc0 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx @@ -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', })} /> ); diff --git a/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx b/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx index 16d3c42ad85..731225a7c3d 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx @@ -18,12 +18,6 @@ const indexPatternTypes: Array> = [ ]; 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+', diff --git a/public/app/plugins/datasource/elasticsearch/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/datasource.test.ts index dca0bbfe1be..fd473ab6083 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.test.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.test.ts @@ -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) { 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', () => { diff --git a/public/app/plugins/datasource/elasticsearch/datasource.ts b/public/app/plugins/datasource/elasticsearch/datasource.ts index 98d5f91399b..af4cc347a48 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.ts +++ b/public/app/plugins/datasource/elasticsearch/datasource.ts @@ -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 { + 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, diff --git a/public/app/plugins/datasource/elasticsearch/utils.ts b/public/app/plugins/datasource/elasticsearch/utils.ts index 05f7ad8c2df..e83e936655d 100644 --- a/public/app/plugins/datasource/elasticsearch/utils.ts +++ b/public/app/plugins/datasource/elasticsearch/utils.ts @@ -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; };