mirror of
https://github.com/grafana/grafana.git
synced 2024-12-29 10:21:41 -06:00
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:
parent
b04fb8522d
commit
250b72cc1b
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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`
|
||||
|
@ -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>
|
||||
)}
|
||||
|
||||
|
@ -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',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
@ -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+',
|
||||
|
@ -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', () => {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user