Plugins: Migrate Elasticsearch to backend plugin SDK (#36132)

* Migrate Elasticsearch to backend plugin SDK

* Fix linting

* Move away from Convey!

* Rebase commit

* Small logger fix

* Fixes according to reviewer's comments

* Fixes according to reviewer's comments

* Fixes according to reviewer's comments

* More cleanup

* Move things around - small refactoring

* Fix typo

* Update calculator - add tests

* Fixes according to reviewer's comments
This commit is contained in:
Dimitris Sotirakis
2021-07-15 17:45:59 +03:00
committed by GitHub
parent 75947da527
commit 0df1b33d71
18 changed files with 1345 additions and 779 deletions

View File

@ -3,158 +3,111 @@ package es
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/Masterminds/semver"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/tsdb/interval"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewClient(t *testing.T) {
t.Run("When no version set should return error", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(make(map[string]interface{})),
}
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
t.Run("When no time field name set should return error", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 5,
}),
}
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
t.Run("When using legacy version numbers", func(t *testing.T) {
t.Run("When unsupported version set should return error", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 6,
"timeField": "@timestamp",
}),
}
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
})
t.Run("When version 2 should return v2 client", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 2,
"timeField": "@timestamp",
}),
version, err := semver.NewVersion("2.0.0")
require.NoError(t, err)
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, "2.0.0", c.GetVersion().String())
})
t.Run("When version 5 should return v5 client", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 5,
"timeField": "@timestamp",
}),
version, err := semver.NewVersion("5.0.0")
require.NoError(t, err)
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, "5.0.0", c.GetVersion().String())
})
t.Run("When version 56 should return v5.6 client", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 56,
"timeField": "@timestamp",
}),
version, err := semver.NewVersion("5.6.0")
require.NoError(t, err)
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, "5.6.0", c.GetVersion().String())
})
t.Run("When version 60 should return v6.0 client", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 60,
"timeField": "@timestamp",
}),
version, err := semver.NewVersion("6.0.0")
require.NoError(t, err)
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, "6.0.0", c.GetVersion().String())
})
t.Run("When version 70 should return v7.0 client", func(t *testing.T) {
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 70,
"timeField": "@timestamp",
}),
version, err := semver.NewVersion("7.0.0")
require.NoError(t, err)
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, "7.0.0", c.GetVersion().String())
})
})
t.Run("When version is a valid semver string should create a client", func(t *testing.T) {
version := "7.2.4"
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": version,
"timeField": "@timestamp",
}),
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
version, err := semver.NewVersion("7.2.4")
require.NoError(t, err)
assert.Equal(t, version, c.GetVersion().String())
})
t.Run("When version is NOT a valid semver string should return error", func(t *testing.T) {
version := "7.NOT_VALID.4"
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": version,
"timeField": "@timestamp",
}),
ds := &DatasourceInfo{
ESVersion: version,
TimeField: "@timestamp",
}
_, err := NewClient(context.Background(), httpclient.NewProvider(), ds, plugins.DataTimeRange{})
require.Error(t, err)
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, backend.TimeRange{})
require.NoError(t, err)
assert.Equal(t, version.String(), c.GetVersion().String())
})
}
func TestClient_ExecuteMultisearch(t *testing.T) {
httpClientScenario(t, "Given a fake http client and a v2.x client with response", &models.DataSource{
Database: "[metrics-]YYYY.MM.DD",
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 2,
"timeField": "@timestamp",
"interval": "Daily",
}),
version, err := semver.NewVersion("2.0.0")
require.NoError(t, err)
httpClientScenario(t, "Given a fake http client and a v2.x client with response", &DatasourceInfo{
Database: "[metrics-]YYYY.MM.DD",
ESVersion: version,
TimeField: "@timestamp",
Interval: "Daily",
}, func(sc *scenarioContext) {
sc.responseBody = `{
"responses": [
@ -198,14 +151,14 @@ func TestClient_ExecuteMultisearch(t *testing.T) {
require.Len(t, res.Responses, 1)
})
httpClientScenario(t, "Given a fake http client and a v5.x client with response", &models.DataSource{
Database: "[metrics-]YYYY.MM.DD",
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": 5,
"maxConcurrentShardRequests": 100,
"timeField": "@timestamp",
"interval": "Daily",
}),
version, err = semver.NewVersion("5.0.0")
require.NoError(t, err)
httpClientScenario(t, "Given a fake http client and a v5.x client with response", &DatasourceInfo{
Database: "[metrics-]YYYY.MM.DD",
ESVersion: version,
TimeField: "@timestamp",
Interval: "Daily",
MaxConcurrentShardRequests: 100,
}, func(sc *scenarioContext) {
sc.responseBody = `{
"responses": [
@ -250,16 +203,16 @@ func TestClient_ExecuteMultisearch(t *testing.T) {
require.Len(t, res.Responses, 1)
})
httpClientScenario(t, "Given a fake http client and a v5.6 client with response", &models.DataSource{
Database: "[metrics-]YYYY.MM.DD",
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": "5.6.0",
"maxConcurrentShardRequests": 100,
"timeField": "@timestamp",
"interval": "Daily",
"includeFrozen": true,
"xpack": true,
}),
version, err = semver.NewVersion("5.6.0")
require.NoError(t, err)
httpClientScenario(t, "Given a fake http client and a v5.6 client with response", &DatasourceInfo{
Database: "[metrics-]YYYY.MM.DD",
ESVersion: version,
TimeField: "@timestamp",
Interval: "Daily",
MaxConcurrentShardRequests: 100,
IncludeFrozen: true,
XPack: true,
}, func(sc *scenarioContext) {
sc.responseBody = `{
"responses": [
@ -305,16 +258,16 @@ func TestClient_ExecuteMultisearch(t *testing.T) {
require.Len(t, res.Responses, 1)
})
httpClientScenario(t, "Given a fake http client and a v7.0 client with response", &models.DataSource{
Database: "[metrics-]YYYY.MM.DD",
JsonData: simplejson.NewFromAny(map[string]interface{}{
"esVersion": "7.0.0",
"maxConcurrentShardRequests": 6,
"timeField": "@timestamp",
"interval": "Daily",
"includeFrozen": true,
"xpack": true,
}),
version, err = semver.NewVersion("7.0.0")
require.NoError(t, err)
httpClientScenario(t, "Given a fake http client and a v7.0 client with response", &DatasourceInfo{
Database: "[metrics-]YYYY.MM.DD",
ESVersion: version,
TimeField: "@timestamp",
Interval: "Daily",
MaxConcurrentShardRequests: 6,
IncludeFrozen: true,
XPack: true,
}, func(sc *scenarioContext) {
sc.responseBody = `{
"responses": [
@ -366,7 +319,7 @@ func createMultisearchForTest(t *testing.T, c Client) (*MultiSearchRequest, erro
t.Helper()
msb := c.MultiSearch()
s := msb.Search(interval.Interval{Value: 15 * time.Second, Text: "15s"})
s := msb.Search(tsdb.Interval{Value: 15 * time.Second, Text: "15s"})
s.Agg().DateHistogram("2", "@timestamp", func(a *DateHistogramAgg, ab AggBuilder) {
a.Interval = "$__interval"
@ -387,7 +340,7 @@ type scenarioContext struct {
type scenarioFunc func(*scenarioContext)
func httpClientScenario(t *testing.T, desc string, ds *models.DataSource, fn scenarioFunc) {
func httpClientScenario(t *testing.T, desc string, ds *DatasourceInfo, fn scenarioFunc) {
t.Helper()
t.Run(desc, func(t *testing.T) {
@ -407,13 +360,14 @@ func httpClientScenario(t *testing.T, desc string, ds *models.DataSource, fn sce
require.NoError(t, err)
rw.WriteHeader(sc.responseStatus)
}))
ds.Url = ts.URL
ds.URL = ts.URL
from := time.Date(2018, 5, 15, 17, 50, 0, 0, time.UTC)
to := time.Date(2018, 5, 15, 17, 55, 0, 0, time.UTC)
fromStr := fmt.Sprintf("%d", from.UnixNano()/int64(time.Millisecond))
toStr := fmt.Sprintf("%d", to.UnixNano()/int64(time.Millisecond))
timeRange := plugins.NewDataTimeRange(fromStr, toStr)
timeRange := backend.TimeRange{
From: from,
To: to,
}
c, err := NewClient(context.Background(), httpclient.NewProvider(), ds, timeRange)
require.NoError(t, err)
@ -422,7 +376,7 @@ func httpClientScenario(t *testing.T, desc string, ds *models.DataSource, fn sce
currentNewDatasourceHTTPClient := newDatasourceHttpClient
newDatasourceHttpClient = func(httpClientProvider httpclient.Provider, ds *models.DataSource) (*http.Client, error) {
newDatasourceHttpClient = func(httpClientProvider httpclient.Provider, ds *DatasourceInfo) (*http.Client, error) {
return ts.Client(), nil
}