Elasticsearch: Removed reference to obsolete esVersion value (#65415)

* elastic: removed reference to obsolete esVersion value

* removed unused code

* cleaned up tests
pull/65127/merge
Gábor Farkas 2 years ago committed by GitHub
parent de1637afe5
commit 0cff917f2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      devenv/datasources.yaml
  2. 3
      devenv/datasources_docker.yaml
  3. 101
      docs/sources/administration/provisioning/index.md
  4. 13
      docs/sources/datasources/elasticsearch/_index.md
  5. 1
      docs/sources/developers/http_api/data_source.md
  6. 4
      pkg/tsdb/elasticsearch/client/client.go
  7. 5
      pkg/tsdb/elasticsearch/client/client_test.go
  8. 42
      pkg/tsdb/elasticsearch/elasticsearch.go
  9. 118
      pkg/tsdb/elasticsearch/elasticsearch_test.go
  10. 2
      pkg/tsdb/elasticsearch/querydata_test.go
  11. 1
      public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.test.tsx
  12. 3
      public/app/plugins/datasource/elasticsearch/configuration/ConfigEditor.test.tsx
  13. 29
      public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx
  14. 46
      public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx
  15. 1
      public/app/plugins/datasource/elasticsearch/configuration/mocks.ts
  16. 8
      public/app/plugins/datasource/elasticsearch/configuration/utils.ts
  17. 51
      public/app/plugins/datasource/elasticsearch/datasource.test.ts
  18. 4
      public/app/plugins/datasource/elasticsearch/datasource.ts
  19. 1
      public/app/plugins/datasource/elasticsearch/mocks.ts
  20. 3
      public/app/plugins/datasource/elasticsearch/types.ts
  21. 24
      public/app/plugins/datasource/elasticsearch/utils.test.ts
  22. 28
      public/app/plugins/datasource/elasticsearch/utils.ts

@ -132,7 +132,6 @@ datasources:
timeField: "@timestamp"
logLevelField: level
logMessageField: line
esVersion: 8.1.4
- name: gdev-elasticsearch-filebeat
type: elasticsearch
@ -142,7 +141,6 @@ datasources:
jsonData:
interval: Daily
timeField: "@timestamp"
esVersion: 8.1.4
timeInterval: "10s"
logMessageField: message
logLevelField: fields.level
@ -155,7 +153,6 @@ datasources:
jsonData:
interval: Daily
timeField: "@timestamp"
esVersion: 8.1.4
timeInterval: "10s"
- name: gdev-mysql

@ -76,7 +76,6 @@ datasources:
jsonData:
interval: Daily
timeField: "@timestamp"
esVersion: 8.1.4
- name: gdev-elasticsearch-filebeat
type: elasticsearch
@ -86,7 +85,6 @@ datasources:
jsonData:
interval: Daily
timeField: "@timestamp"
esVersion: 8.1.4
timeInterval: "10s"
logMessageField: message
logLevelField: fields.level
@ -99,7 +97,6 @@ datasources:
jsonData:
interval: Daily
timeField: "@timestamp"
esVersion: 8.1.4
timeInterval: "10s"
- name: gdev-mysql

@ -169,57 +169,56 @@ Common settings in the [built-in core data sources]({{< relref "../../datasource
> **Note:** Data sources tagged with _HTTP\*_ communicate using the HTTP protocol, which includes all core data source plugins except MySQL, PostgreSQL, and MSSQL.
| Name | Type | Data source | Description |
| -------------------------- | ------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| tlsAuth | boolean | _HTTP\*_, MySQL | Enable TLS authentication using client cert configured in secure json data |
| tlsAuthWithCACert | boolean | _HTTP\*_, MySQL, PostgreSQL | Enable TLS authentication using CA cert |
| tlsSkipVerify | boolean | _HTTP\*_, MySQL, PostgreSQL, MSSQL | Controls whether a client verifies the server's certificate chain and host name. |
| serverName | string | _HTTP\*_, MSSQL | Optional. Controls the server name used for certificate common name/subject alternative name verification. Defaults to using the data source URL. |
| timeout | string | _HTTP\*_ | Request timeout in seconds. Overrides dataproxy.timeout option |
| graphiteVersion | string | Graphite | Graphite version |
| timeInterval | string | Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL | Lowest interval/step value that should be used for this data source. |
| httpMode | string | Influxdb | HTTP Method. 'GET', 'POST', defaults to GET |
| maxSeries | number | Influxdb | Max number of series/tables that Grafana processes |
| httpMethod | string | Prometheus | HTTP Method. 'GET', 'POST', defaults to POST |
| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. |
| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI |
| alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. |
| esVersion | string | Elasticsearch | Elasticsearch version (e.g. `7.0.0`, `7.6.1`) |
| timeField | string | Elasticsearch | Which field that should be used as timestamp |
| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' |
| logMessageField | string | Elasticsearch | Which field should be used as the log message |
| logLevelField | string | Elasticsearch | Which field should be used to indicate the priority of the log message |
| maxConcurrentShardRequests | number | Elasticsearch | Maximum number of concurrent shard requests that each sub-search request executes per node. Defaults to 5 if esVersion is greater than or equals 7.0.0. When the esVersion is less than 7.0.0 and greater than or equals 5.6.0, then the default value is 256. Option is ignored when esVersion is less than 5.6.0. |
| sigV4Auth | boolean | Elasticsearch and Prometheus | Enable usage of SigV4 |
| sigV4AuthType | string | Elasticsearch and Prometheus | SigV4 auth provider. default/credentials/keys |
| sigV4ExternalId | string | Elasticsearch and Prometheus | Optional SigV4 External ID |
| sigV4AssumeRoleArn | string | Elasticsearch and Prometheus | Optional SigV4 ARN role to assume |
| sigV4Region | string | Elasticsearch and Prometheus | SigV4 AWS region |
| sigV4Profile | string | Elasticsearch and Prometheus | Optional SigV4 credentials profile |
| authType | string | Cloudwatch | Auth provider. default/credentials/keys |
| externalId | string | Cloudwatch | Optional External ID |
| assumeRoleArn | string | Cloudwatch | Optional ARN role to assume |
| defaultRegion | string | Cloudwatch | Optional default AWS region |
| customMetricsNamespaces | string | Cloudwatch | Namespaces of Custom Metrics |
| profile | string | Cloudwatch | Optional credentials profile |
| tsdbVersion | string | OpenTSDB | Version |
| tsdbResolution | string | OpenTSDB | Resolution |
| sslmode | string | PostgreSQL | SSLmode. 'disable', 'require', 'verify-ca' or 'verify-full' |
| tlsConfigurationMethod | string | PostgreSQL | SSL Certificate configuration, either by 'file-path' or 'file-content' |
| sslRootCertFile | string | PostgreSQL, MSSQL | SSL server root certificate file, must be readable by the Grafana user |
| sslCertFile | string | PostgreSQL | SSL client certificate file, must be readable by the Grafana user |
| sslKeyFile | string | PostgreSQL | SSL client key file, must be readable by _only_ the Grafana user |
| encrypt | string | MSSQL | Connection SSL encryption handling. 'disable', 'false' or 'true' |
| postgresVersion | number | PostgreSQL | Postgres version as a number (903/904/905/906/1000) meaning v9.3, v9.4, ..., v10 |
| timescaledb | boolean | PostgreSQL | Enable usage of TimescaleDB extension |
| maxOpenConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of open connections to the database (Grafana v5.4+) |
| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) |
| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) |
| keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with data sources |
| prometheusVersion | string | Prometheus | The version of the Prometheus data source, such as `2.37.0`, `2.24.0` |
| prometheusType | string | Prometheus | The type of the Prometheus data sources. such as `Prometheus`, `Cortex`, `Thanos`, `Mimir` |
| implementation | string | AlertManager | The implementation of the AlertManager data source, such as `prometheus`, `cortex` or `mimir` |
| handleGrafanaManagedAlerts | boolean | AlertManager | When enabled, Grafana-managed alerts are sent to this Alertmanager |
| Name | Type | Data source | Description |
| -------------------------- | ------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| tlsAuth | boolean | _HTTP\*_, MySQL | Enable TLS authentication using client cert configured in secure json data |
| tlsAuthWithCACert | boolean | _HTTP\*_, MySQL, PostgreSQL | Enable TLS authentication using CA cert |
| tlsSkipVerify | boolean | _HTTP\*_, MySQL, PostgreSQL, MSSQL | Controls whether a client verifies the server's certificate chain and host name. |
| serverName | string | _HTTP\*_, MSSQL | Optional. Controls the server name used for certificate common name/subject alternative name verification. Defaults to using the data source URL. |
| timeout | string | _HTTP\*_ | Request timeout in seconds. Overrides dataproxy.timeout option |
| graphiteVersion | string | Graphite | Graphite version |
| timeInterval | string | Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL | Lowest interval/step value that should be used for this data source. |
| httpMode | string | Influxdb | HTTP Method. 'GET', 'POST', defaults to GET |
| maxSeries | number | Influxdb | Max number of series/tables that Grafana processes |
| httpMethod | string | Prometheus | HTTP Method. 'GET', 'POST', defaults to POST |
| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. |
| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI |
| alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. |
| timeField | string | Elasticsearch | Which field that should be used as timestamp |
| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' |
| logMessageField | string | Elasticsearch | Which field should be used as the log message |
| logLevelField | string | Elasticsearch | Which field should be used to indicate the priority of the log message |
| maxConcurrentShardRequests | number | Elasticsearch | Maximum number of concurrent shard requests that each sub-search request executes per node |
| sigV4Auth | boolean | Elasticsearch and Prometheus | Enable usage of SigV4 |
| sigV4AuthType | string | Elasticsearch and Prometheus | SigV4 auth provider. default/credentials/keys |
| sigV4ExternalId | string | Elasticsearch and Prometheus | Optional SigV4 External ID |
| sigV4AssumeRoleArn | string | Elasticsearch and Prometheus | Optional SigV4 ARN role to assume |
| sigV4Region | string | Elasticsearch and Prometheus | SigV4 AWS region |
| sigV4Profile | string | Elasticsearch and Prometheus | Optional SigV4 credentials profile |
| authType | string | Cloudwatch | Auth provider. default/credentials/keys |
| externalId | string | Cloudwatch | Optional External ID |
| assumeRoleArn | string | Cloudwatch | Optional ARN role to assume |
| defaultRegion | string | Cloudwatch | Optional default AWS region |
| customMetricsNamespaces | string | Cloudwatch | Namespaces of Custom Metrics |
| profile | string | Cloudwatch | Optional credentials profile |
| tsdbVersion | string | OpenTSDB | Version |
| tsdbResolution | string | OpenTSDB | Resolution |
| sslmode | string | PostgreSQL | SSLmode. 'disable', 'require', 'verify-ca' or 'verify-full' |
| tlsConfigurationMethod | string | PostgreSQL | SSL Certificate configuration, either by 'file-path' or 'file-content' |
| sslRootCertFile | string | PostgreSQL, MSSQL | SSL server root certificate file, must be readable by the Grafana user |
| sslCertFile | string | PostgreSQL | SSL client certificate file, must be readable by the Grafana user |
| sslKeyFile | string | PostgreSQL | SSL client key file, must be readable by _only_ the Grafana user |
| encrypt | string | MSSQL | Connection SSL encryption handling. 'disable', 'false' or 'true' |
| postgresVersion | number | PostgreSQL | Postgres version as a number (903/904/905/906/1000) meaning v9.3, v9.4, ..., v10 |
| timescaledb | boolean | PostgreSQL | Enable usage of TimescaleDB extension |
| maxOpenConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of open connections to the database (Grafana v5.4+) |
| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) |
| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) |
| keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with data sources |
| prometheusVersion | string | Prometheus | The version of the Prometheus data source, such as `2.37.0`, `2.24.0` |
| prometheusType | string | Prometheus | The type of the Prometheus data sources. such as `Prometheus`, `Cortex`, `Thanos`, `Mimir` |
| implementation | string | AlertManager | The implementation of the AlertManager data source, such as `prometheus`, `cortex` or `mimir` |
| handleGrafanaManagedAlerts | boolean | AlertManager | When enabled, Grafana-managed alerts are sent to this Alertmanager |
For examples of specific data sources' JSON data, refer to that [data source's documentation]({{< relref "../../datasources" >}}).

@ -59,18 +59,6 @@ You must also configure settings specific to the Elasticsearch data source.
Use the index settings to specify a default for the `time field` and your Elasticsearch index's name.
You can use a time pattern, such as `YYYY.MM.DD`, or a wildcard for the index name.
### Elasticsearch version
Select the version of your Elasticsearch data source from the version selection dropdown.
Different versions provide different query compositions and functionalities in the [query editor]({{< relref "./query-editor/" >}}).
Available Elasticsearch versions are `2.x`, `5.x`, `5.6+`, `6.0+`, `7.0+`, `7.7+`, and `7.10+`.
Grafana assumes you're running the lowest possible version for a specified range.
This ensures that new features or breaking changes in a future Elasticsearch release don't affect your configuration.
For example, if you run Elasticsearch `7.6.1` and select `7.0+`, and a new feature is made available for Elasticsearch `7.5.0` or newer releases, then a `7.5+` option will be available.
However, your configuration won't be affected until you explicitly select the new `7.5+` option in your settings.
### Configure Min time interval
The **Min time interval** setting defines a lower limit for the auto group-by time interval.
@ -181,7 +169,6 @@ datasources:
jsonData:
interval: Daily
timeField: '@timestamp'
esVersion: '7.0.0'
logMessageField: message
logLevelField: fields.level
dataLinks:

@ -62,7 +62,6 @@ Content-Type: application/json
"basicAuth": false,
"isDefault": false,
"jsonData": {
"esVersion": 5,
"logLevelField": "",
"logMessageField": "",
"maxConcurrentShardRequests": 256,

@ -12,7 +12,6 @@ import (
"strings"
"time"
"github.com/Masterminds/semver"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/log"
@ -23,7 +22,6 @@ type DatasourceInfo struct {
HTTPClient *http.Client
URL string
Database string
ESVersion *semver.Version
ConfiguredFields ConfiguredFields
Interval string
TimeInterval string
@ -60,7 +58,7 @@ var NewClient = func(ctx context.Context, ds *DatasourceInfo, timeRange backend.
}
logger := log.New(loggerName).FromContext(ctx)
logger.Debug("Creating new client", "version", ds.ESVersion, "configuredFields", fmt.Sprintf("%#v", ds.ConfiguredFields), "indices", strings.Join(indices, ", "))
logger.Debug("Creating new client", "configuredFields", fmt.Sprintf("%#v", ds.ConfiguredFields), "indices", strings.Join(indices, ", "))
return &baseClientImpl{
logger: logger,

@ -9,7 +9,6 @@ import (
"testing"
"time"
"github.com/Masterminds/semver"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -43,9 +42,6 @@ func TestClient_ExecuteMultisearch(t *testing.T) {
rw.WriteHeader(200)
}))
version, err := semver.NewVersion("8.0.0")
require.NoError(t, err)
configuredFields := ConfiguredFields{
TimeField: "testtime",
LogMessageField: "line",
@ -56,7 +52,6 @@ func TestClient_ExecuteMultisearch(t *testing.T) {
URL: ts.URL,
HTTPClient: ts.Client(),
Database: "[metrics-]YYYY.MM.DD",
ESVersion: version,
ConfiguredFields: configuredFields,
Interval: "Daily",
MaxConcurrentShardRequests: 6,

@ -7,7 +7,6 @@ import (
"fmt"
"strconv"
"github.com/Masterminds/semver"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
@ -44,12 +43,6 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
// separate function to allow testing the whole transformation and query flow
func queryData(ctx context.Context, queries []backend.DataQuery, dsInfo *es.DatasourceInfo) (*backend.QueryDataResponse, error) {
// 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(queries) == 0 {
return &backend.QueryDataResponse{}, fmt.Errorf("query contains no queries")
}
@ -84,10 +77,7 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
return nil, err
}
version, err := coerceVersion(jsonData["esVersion"])
if err != nil {
return nil, fmt.Errorf("elasticsearch version is required, err=%v", err)
}
// we used to have a field named `esVersion`, please do not use this name in the future.
timeField, ok := jsonData["timeField"].(string)
if !ok {
@ -154,7 +144,6 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
HTTPClient: httpCli,
Database: settings.Database,
MaxConcurrentShardRequests: int64(maxConcurrentShardRequests),
ESVersion: version,
ConfiguredFields: configuredFields,
Interval: interval,
TimeInterval: timeInterval,
@ -175,32 +164,3 @@ func (s *Service) getDSInfo(pluginCtx backend.PluginContext) (*es.DatasourceInfo
return &instance, nil
}
func coerceVersion(v interface{}) (*semver.Version, error) {
versionString, ok := v.(string)
if ok {
return semver.NewVersion(versionString)
}
versionNumber, ok := v.(float64)
if !ok {
return nil, fmt.Errorf("elasticsearch version %v, cannot be cast to int", v)
}
// Legacy version numbers (before Grafana 8)
// valid values were 2,5,56,60,70
switch int64(versionNumber) {
case 2:
return semver.NewVersion("2.0.0")
case 5:
return semver.NewVersion("5.0.0")
case 56:
return semver.NewVersion("5.6.0")
case 60:
return semver.NewVersion("6.0.0")
case 70:
return semver.NewVersion("7.0.0")
default:
return nil, fmt.Errorf("elasticsearch version=%d is not supported", int64(versionNumber))
}
}

@ -11,50 +11,15 @@ import (
)
type datasourceInfo struct {
ESVersion interface{} `json:"esVersion"`
TimeField interface{} `json:"timeField"`
MaxConcurrentShardRequests int64 `json:"maxConcurrentShardRequests"`
Interval string `json:"interval"`
TimeInterval string `json:"timeInterval"`
}
func TestCoerceVersion(t *testing.T) {
t.Run("version is string", func(t *testing.T) {
ver := "7.0.0"
smvr, err := coerceVersion(ver)
require.NoError(t, err)
require.NotNil(t, smvr)
require.Equal(t, "7.0.0", smvr.String())
})
t.Run("version is int", func(t *testing.T) {
testCases := []struct {
intVersion float64
stringVersion string
}{
{intVersion: 2, stringVersion: "2.0.0"},
{intVersion: 5, stringVersion: "5.0.0"},
{intVersion: 56, stringVersion: "5.6.0"},
{intVersion: 60, stringVersion: "6.0.0"},
{intVersion: 70, stringVersion: "7.0.0"},
}
for _, tc := range testCases {
smvr, err := coerceVersion(tc.intVersion)
require.NoError(t, err)
require.Equal(t, tc.stringVersion, smvr.String())
}
smvr, err := coerceVersion(12345)
require.Error(t, err)
require.Nil(t, smvr)
})
}
func TestNewInstanceSettings(t *testing.T) {
t.Run("fields exist", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: "7.0.0",
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
}
@ -69,91 +34,9 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
})
t.Run("esVersion", func(t *testing.T) {
t.Run("correct version", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: 5,
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeInterval: "TimeInterval",
}
settingsJSON, err := json.Marshal(dsInfo)
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
JSONData: json.RawMessage(settingsJSON),
}
_, err = newInstanceSettings(httpclient.NewProvider())(dsSettings)
require.NoError(t, err)
})
t.Run("faulty version int", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: 1234,
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeInterval: "TimeInterval",
}
settingsJSON, err := json.Marshal(dsInfo)
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
JSONData: json.RawMessage(settingsJSON),
}
_, err = newInstanceSettings(httpclient.NewProvider())(dsSettings)
require.EqualError(t, err, "elasticsearch version is required, err=elasticsearch version=1234 is not supported")
})
t.Run("faulty version string", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: "NOT_VALID",
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeInterval: "TimeInterval",
}
settingsJSON, err := json.Marshal(dsInfo)
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
JSONData: json.RawMessage(settingsJSON),
}
_, err = newInstanceSettings(httpclient.NewProvider())(dsSettings)
require.EqualError(t, err, "elasticsearch version is required, err=Invalid Semantic Version")
})
t.Run("no version", func(t *testing.T) {
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeInterval: "TimeInterval",
}
settingsJSON, err := json.Marshal(dsInfo)
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
JSONData: json.RawMessage(settingsJSON),
}
_, err = newInstanceSettings(httpclient.NewProvider())(dsSettings)
require.EqualError(t, err, "elasticsearch version is required, err=elasticsearch version <nil>, cannot be cast to int")
})
})
t.Run("timeField", func(t *testing.T) {
t.Run("is nil", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: 2,
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeInterval: "TimeInterval",
@ -172,7 +55,6 @@ func TestNewInstanceSettings(t *testing.T) {
t.Run("is empty", func(t *testing.T) {
dsInfo := datasourceInfo{
ESVersion: 2,
MaxConcurrentShardRequests: 5,
Interval: "Daily",
TimeField: "",

@ -9,7 +9,6 @@ import (
"net/http"
"time"
"github.com/Masterminds/semver"
"github.com/grafana/grafana-plugin-sdk-go/backend"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
@ -49,7 +48,6 @@ func newFlowTestDsInfo(body []byte, statusCode int, requestCallback func(req *ht
}
return &es.DatasourceInfo{
ESVersion: semver.MustParse("8.5.0"),
Interval: "Daily",
Database: "[testdb-]YYYY.MM.DD",
ConfiguredFields: configuredFields,

@ -8,7 +8,6 @@ import { QueryEditor } from '.';
const noop = () => void 0;
const datasourceMock = {
esVersion: '7.10.0',
getDatabaseVersion: () => Promise.resolve(null),
} as ElasticDatasource;

@ -28,8 +28,6 @@ describe('ConfigEditor', () => {
const mockOnOptionsChange = jest.fn();
const options = createDefaultConfigOptions();
// @ts-ignore
delete options.jsonData.esVersion;
// @ts-ignore
delete options.jsonData.timeField;
delete options.jsonData.maxConcurrentShardRequests;
@ -38,7 +36,6 @@ describe('ConfigEditor', () => {
expect(mockOnOptionsChange).toHaveBeenCalledWith(
expect.objectContaining({
jsonData: expect.objectContaining({
esVersion: '8.0.0',
timeField: '@timestamp',
maxConcurrentShardRequests: 5,
}),

@ -8,7 +8,7 @@ import { createDefaultConfigOptions } from './mocks';
describe('ElasticDetails', () => {
describe('Max concurrent Shard Requests', () => {
it('should render "Max concurrent Shard Requests" ', () => {
render(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions({ esVersion: '8.2.0' })} />);
render(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions()} />);
expect(screen.getByLabelText('Max concurrent Shard Requests')).toBeInTheDocument();
});
});
@ -44,31 +44,4 @@ describe('ElasticDetails', () => {
})
);
});
describe('version change', () => {
const tc = { version: '7.10+', maxConcurrentShardRequests: 6, expectedMaxConcurrentShardRequests: 6 };
const onChangeMock = jest.fn();
it(`sets maxConcurrentShardRequests=${tc.expectedMaxConcurrentShardRequests} if version=${tc.version},`, async () => {
render(
<ElasticDetails
onChange={onChangeMock}
value={createDefaultConfigOptions({
maxConcurrentShardRequests: tc.maxConcurrentShardRequests,
esVersion: '7.0.0',
})}
/>
);
const selectEl = screen.getByLabelText('ElasticSearch version');
await selectEvent.select(selectEl, tc.version, { container: document.body });
expect(onChangeMock).toHaveBeenCalledWith(
expect.objectContaining({
jsonData: expect.objectContaining({ maxConcurrentShardRequests: tc.expectedMaxConcurrentShardRequests }),
})
);
});
});
});

@ -1,7 +1,6 @@
import React from 'react';
import { valid } from 'semver';
import { DataSourceSettings, SelectableValue, isTruthy } from '@grafana/data';
import { DataSourceSettings, SelectableValue } from '@grafana/data';
import { FieldSet, InlineField, Input, Select, InlineSwitch } from '@grafana/ui';
import { ElasticsearchOptions, Interval } from '../types';
@ -15,24 +14,11 @@ const indexPatternTypes: Array<SelectableValue<'none' | Interval>> = [
{ label: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' },
];
const esVersions: SelectableValue[] = [
{ label: '7.10+', value: '7.10.0' },
{ label: '8.x', value: '8.0.0' },
];
type Props = {
value: DataSourceSettings<ElasticsearchOptions>;
onChange: (value: DataSourceSettings<ElasticsearchOptions>) => void;
};
export const ElasticDetails = ({ value, onChange }: Props) => {
const currentVersion = esVersions.find((version) => version.value === value.jsonData.esVersion);
const customOption =
!currentVersion && valid(value.jsonData.esVersion)
? {
label: value.jsonData.esVersion,
value: value.jsonData.esVersion,
}
: undefined;
return (
<>
<FieldSet label="Elasticsearch details">
@ -70,28 +56,6 @@ export const ElasticDetails = ({ value, onChange }: Props) => {
/>
</InlineField>
<InlineField label="ElasticSearch version" labelWidth={26}>
<Select
inputId="es_config_version"
options={[customOption, ...esVersions].filter(isTruthy)}
onChange={(option) => {
const maxConcurrentShardRequests = getMaxConcurrenShardRequestOrDefault(
value.jsonData.maxConcurrentShardRequests
);
onChange({
...value,
jsonData: {
...value.jsonData,
esVersion: option.value!,
maxConcurrentShardRequests,
},
});
}}
value={currentVersion || customOption}
width={24}
/>
</InlineField>
<InlineField label="Max concurrent Shard Requests" labelWidth={26}>
<Input
id="es_config_shardRequests"
@ -208,14 +172,6 @@ const intervalHandler =
}
};
function getMaxConcurrenShardRequestOrDefault(maxConcurrentShardRequests: number | undefined): number {
if (maxConcurrentShardRequests === 256) {
return 5;
}
return maxConcurrentShardRequests || defaultMaxConcurrentShardRequests();
}
export function defaultMaxConcurrentShardRequests() {
return 5;
}

@ -9,7 +9,6 @@ export function createDefaultConfigOptions(
return getMockDataSource<ElasticsearchOptions>({
jsonData: {
timeField: '@time',
esVersion: '7.0.0',
interval: 'Hourly',
timeInterval: '10s',
maxConcurrentShardRequests: 300,

@ -1,23 +1,17 @@
import { valid } from 'semver';
import { DataSourceSettings } from '@grafana/data';
import { ElasticsearchOptions } from '../types';
import { coerceESVersion } from '../utils';
import { defaultMaxConcurrentShardRequests } from './ElasticDetails';
export const coerceOptions = (
options: DataSourceSettings<ElasticsearchOptions, {}>
): DataSourceSettings<ElasticsearchOptions, {}> => {
const esVersion = coerceESVersion(options.jsonData.esVersion);
return {
...options,
jsonData: {
...options.jsonData,
timeField: options.jsonData.timeField || '@timestamp',
esVersion,
maxConcurrentShardRequests: options.jsonData.maxConcurrentShardRequests || defaultMaxConcurrentShardRequests(),
logMessageField: options.jsonData.logMessageField || '',
logLevelField: options.jsonData.logLevelField || '',
@ -28,8 +22,6 @@ export const coerceOptions = (
export const isValidOptions = (options: DataSourceSettings<ElasticsearchOptions, {}>): boolean => {
return (
// esVersion should be a valid semver string
!!valid(options.jsonData.esVersion) &&
// timeField should not be empty or nullish
!!options.jsonData.timeField &&
// maxConcurrentShardRequests should be a number AND greater than 0

@ -166,7 +166,7 @@ describe('ElasticDatasource', () => {
};
const { ds, fetchMock } = getTestContext({
data,
jsonData: { interval: 'Daily', esVersion: '7.10.0', timeField: '@timestamp' },
jsonData: { interval: 'Daily', timeField: '@timestamp' },
});
ds.getTagValues({ key: 'test' });
@ -182,7 +182,7 @@ describe('ElasticDatasource', () => {
describe('When testing datasource with index pattern', () => {
it('should translate index pattern to current day', async () => {
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0' } });
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily' } });
await ds.testDatasource();
@ -221,7 +221,7 @@ describe('ElasticDatasource', () => {
},
],
};
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0' }, data });
const { ds, fetchMock } = getTestContext({ jsonData: { interval: 'Daily' }, data });
let result: DataQueryResponse = { data: [] };
await expect(ds.query(query)).toEmitValuesWith((received) => {
@ -303,7 +303,6 @@ describe('ElasticDatasource', () => {
async function setupDataSource(jsonData?: Partial<ElasticsearchOptions>) {
jsonData = {
interval: 'Daily',
esVersion: '7.10.0',
timeField: '@timestamp',
...(jsonData || {}),
};
@ -394,7 +393,7 @@ describe('ElasticDatasource', () => {
const query = { ...DATAQUERY_BASE, range, targets };
const data = { responses: [] };
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ data, database: 'test' });
await expect(ds.query(query)).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -453,7 +452,7 @@ describe('ElasticDatasource', () => {
it('should process it properly', async () => {
const { ds } = getTestContext({
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
jsonData: { interval: 'Daily' },
data: {
took: 1,
responses: [
@ -500,7 +499,6 @@ describe('ElasticDatasource', () => {
const { ds } = getTestContext({
fetchMockImplementation: () => throwError(response),
from: undefined,
jsonData: { esVersion: '7.10.0' },
});
const errObject = {
@ -516,7 +514,7 @@ describe('ElasticDatasource', () => {
it('should properly throw an unknown error', async () => {
const { ds } = getTestContext({
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
jsonData: { interval: 'Daily' },
data: {
took: 1,
responses: [
@ -567,7 +565,7 @@ describe('ElasticDatasource', () => {
const { ds, timeSrv, fetchMock } = getTestContext({
from: 'now-2w',
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
jsonData: { interval: 'Daily' },
fetchMockImplementation: (options) => {
if (options.url === `${ELASTICSEARCH_MOCK_URL}/asd-${twoDaysBefore}/_mapping`) {
return of(createFetchResponse(basicResponse));
@ -588,7 +586,7 @@ describe('ElasticDatasource', () => {
it('should not retry more than 7 indices', async () => {
const { ds, timeSrv, fetchMock } = getTestContext({
from: 'now-2w',
jsonData: { interval: 'Daily', esVersion: '7.10.0' },
jsonData: { interval: 'Daily' },
fetchMockImplementation: (options) => {
return throwError({ status: 404 });
},
@ -701,7 +699,6 @@ describe('ElasticDatasource', () => {
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields()).toEmitValuesWith((received) => {
@ -732,7 +729,6 @@ describe('ElasticDatasource', () => {
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields(['number'])).toEmitValuesWith((received) => {
@ -748,7 +744,6 @@ describe('ElasticDatasource', () => {
const { ds } = getTestContext({
data,
database: 'genuine.es7._mapping.response',
jsonData: { esVersion: '7.10.0' },
});
await expect(ds.getFields(['date'])).toEmitValuesWith((received) => {
@ -775,7 +770,7 @@ describe('ElasticDatasource', () => {
const query = { ...DATAQUERY_BASE, range, targets };
const data = { responses: [] };
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ data, database: 'test' });
await expect(ds.query(query)).toEmitValuesWith((received) => {
expect(received.length).toBe(1);
@ -823,7 +818,7 @@ describe('ElasticDatasource', () => {
],
};
const { ds, fetchMock } = getTestContext({ jsonData: { esVersion: '7.10.0' }, data, database: 'test' });
const { ds, fetchMock } = getTestContext({ data, database: 'test' });
const results = await ds.metricFindQuery('{"find": "terms", "field": "test"}');
@ -865,7 +860,7 @@ describe('ElasticDatasource', () => {
describe('query', () => {
it('should replace range as integer not string', async () => {
const { ds } = getTestContext({ jsonData: { interval: 'Daily', esVersion: '7.10.0', timeField: '@time' } });
const { ds } = getTestContext({ jsonData: { interval: 'Daily', timeField: '@time' } });
const postMock = jest.fn((url: string, data) => of(createFetchResponse({ responses: [] })));
ds['post'] = postMock;
@ -977,24 +972,22 @@ describe('ElasticDatasource', () => {
});
describe('getMultiSearchUrl', () => {
describe('When esVersion >= 7.10.0', () => {
it('Should add correct params to URL if "includeFrozen" is enabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: true, xpack: true } });
it('Should add correct params to URL if "includeFrozen" is enabled', () => {
const { ds } = getTestContext({ jsonData: { includeFrozen: true, xpack: true } });
expect(ds.getMultiSearchUrl()).toMatch(/ignore_throttled=false/);
});
expect(ds.getMultiSearchUrl()).toMatch(/ignore_throttled=false/);
});
it('Should NOT add ignore_throttled if "includeFrozen" is disabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: false, xpack: true } });
it('Should NOT add ignore_throttled if "includeFrozen" is disabled', () => {
const { ds } = getTestContext({ jsonData: { includeFrozen: false, xpack: true } });
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
it('Should NOT add ignore_throttled if "xpack" is disabled', () => {
const { ds } = getTestContext({ jsonData: { esVersion: '7.10.0', includeFrozen: true, xpack: false } });
it('Should NOT add ignore_throttled if "xpack" is disabled', () => {
const { ds } = getTestContext({ jsonData: { includeFrozen: true, xpack: false } });
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
expect(ds.getMultiSearchUrl()).not.toMatch(/ignore_throttled=false/);
});
});

@ -50,7 +50,7 @@ import { metricAggregationConfig } from './components/QueryEditor/MetricAggregat
import { defaultBucketAgg, hasMetricOfType } from './queryDef';
import { trackQuery } from './tracking';
import { Logs, BucketAggregation, DataLinkConfig, ElasticsearchOptions, ElasticsearchQuery, TermsQuery } from './types';
import { coerceESVersion, getScriptValue, isSupportedVersion, unsupportedVersionMessage } from './utils';
import { getScriptValue, isSupportedVersion, unsupportedVersionMessage } from './utils';
export const REF_ID_STARTER_LOG_VOLUME = 'log-volume-';
// Those are metadata fields as defined in https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-fields.html#_identity_metadata_fields.
@ -80,7 +80,6 @@ export class ElasticDatasource
name: string;
index: string;
timeField: string;
esVersion: string;
xpack: boolean;
interval: string;
maxConcurrentShardRequests?: number;
@ -109,7 +108,6 @@ export class ElasticDatasource
const settingsData = instanceSettings.jsonData || ({} as ElasticsearchOptions);
this.timeField = settingsData.timeField;
this.esVersion = coerceESVersion(settingsData.esVersion);
this.xpack = Boolean(settingsData.xpack);
this.indexPattern = new IndexPattern(this.index, settingsData.interval);
this.interval = settingsData.timeInterval;

@ -41,7 +41,6 @@ export function createElasticDatasource(
url: '',
jsonData: {
timeField: '',
esVersion: '',
timeInterval: '',
...jsonData,
},

@ -39,7 +39,8 @@ export type Interval = 'Hourly' | 'Daily' | 'Weekly' | 'Monthly' | 'Yearly';
export interface ElasticsearchOptions extends DataSourceJsonData {
timeField: string;
esVersion: string;
// we used to have a field named `esVersion` in the past,
// please do not use that name in the future.
xpack?: boolean;
interval?: Interval;
timeInterval: string;

@ -1,4 +1,4 @@
import { removeEmpty, coerceESVersion } from './utils';
import { removeEmpty } from './utils';
describe('removeEmpty', () => {
it('Should remove all empty', () => {
@ -33,26 +33,4 @@ describe('removeEmpty', () => {
expect(removeEmpty(original)).toStrictEqual(expectedResult);
});
it('should correctly coerce the version info', () => {
// valid string
expect(coerceESVersion('8.1.3')).toBe('8.1.3');
// invalid string
expect(coerceESVersion('haha')).toBe('8.0.0');
// known number
expect(coerceESVersion(2)).toBe('2.0.0');
expect(coerceESVersion(5)).toBe('5.0.0');
expect(coerceESVersion(56)).toBe('5.6.0');
expect(coerceESVersion(60)).toBe('6.0.0');
expect(coerceESVersion(70)).toBe('7.0.0');
expect(coerceESVersion(8)).toBe('8.0.0');
// unknown number
expect(coerceESVersion(42)).toBe('8.0.0');
// undefined
expect(coerceESVersion(undefined)).toBe('8.0.0');
});
});

@ -1,4 +1,4 @@
import { valid, gte, SemVer } from 'semver';
import { gte, SemVer } from 'semver';
import { isMetricAggregationWithField } from './components/QueryEditor/MetricAggregationsEditor/aggregations';
import { metricAggregationConfig } from './components/QueryEditor/MetricAggregationsEditor/utils';
@ -91,32 +91,6 @@ export const convertOrderByToMetricId = (orderBy: string): string | undefined =>
export const getScriptValue = (metric: MetricAggregationWithInlineScript) =>
(typeof metric.settings?.script === 'object' ? metric.settings?.script?.inline : metric.settings?.script) || '';
/**
* Coerces the version to a valid semver string.
* It takes care of also converting from the legacy format (numeric) to the new one.
* @param version
*/
export const coerceESVersion = (version: string | number | undefined): string => {
if (typeof version === 'string') {
return valid(version) || '8.0.0';
}
switch (version) {
case 2:
return '2.0.0';
case 5:
return '5.0.0';
case 56:
return '5.6.0';
case 60:
return '6.0.0';
case 70:
return '7.0.0';
default:
return '8.0.0';
}
};
export const isSupportedVersion = (version: SemVer): boolean => {
if (gte(version, '7.10.0')) {
return true;

Loading…
Cancel
Save