Elasticsearch: Run version check thorugh backend if enableElasticsearchBackendQuerying enabled (#67679)

* Elasticsearch: Run getDBversion trough resource calls

* Update

* Update

* Fix lint

* Close response body

* Fix lint

* Refactor
This commit is contained in:
Ivana Huckova 2023-05-03 18:09:18 +02:00 committed by GitHub
parent 681925fb63
commit 4d7c0904ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 3 deletions

View File

@ -1,10 +1,15 @@
package elasticsearch
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@ -172,3 +177,66 @@ func (s *Service) getDSInfo(pluginCtx backend.PluginContext) (*es.DatasourceInfo
return &instance, nil
}
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
logger := eslog.FromContext(ctx)
// allowed paths for resource calls:
// - empty string for fetching db version
if req.Path != "" {
return fmt.Errorf("invalid resource URL: %s", req.Path)
}
ds, err := s.getDSInfo(req.PluginContext)
if err != nil {
return err
}
esUrl, err := url.Parse(ds.URL)
if err != nil {
return err
}
resourcePath, err := url.Parse(req.Path)
if err != nil {
return err
}
// We take the path and the query-string only
esUrl.RawQuery = resourcePath.RawQuery
esUrl.Path = path.Join(esUrl.Path, resourcePath.Path)
request, err := http.NewRequestWithContext(ctx, req.Method, esUrl.String(), bytes.NewBuffer(req.Body))
if err != nil {
return err
}
response, err := ds.HTTPClient.Do(request)
if err != nil {
return err
}
defer func() {
if err := response.Body.Close(); err != nil {
logger.Warn("Failed to close response body", "err", err)
}
}()
body, err := io.ReadAll(response.Body)
if err != nil {
return err
}
responseHeaders := map[string][]string{
"content-type": {"application/json"},
}
if response.Header.Get("Content-Encoding") != "" {
responseHeaders["content-encoding"] = []string{response.Header.Get("Content-Encoding")}
}
return sender.Send(&backend.CallResourceResponse{
Status: response.StatusCode,
Headers: responseHeaders,
Body: body,
})
}

View File

@ -18,7 +18,7 @@ import {
TimeRange,
toUtc,
} from '@grafana/data';
import { BackendSrvRequest, FetchResponse, reportInteraction } from '@grafana/runtime';
import { BackendSrvRequest, FetchResponse, reportInteraction, config } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
@ -31,6 +31,8 @@ import { Filters, ElasticsearchOptions, ElasticsearchQuery } from './types';
const ELASTICSEARCH_MOCK_URL = 'http://elasticsearch.local';
const originalConsoleError = console.error;
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => backendSrv,
@ -1291,3 +1293,37 @@ const logsResponse = {
],
},
};
describe('ElasticDatasource using backend', () => {
beforeEach(() => {
console.error = jest.fn();
config.featureToggles.enableElasticsearchBackendQuerying = true;
});
afterEach(() => {
console.error = originalConsoleError;
config.featureToggles.enableElasticsearchBackendQuerying = false;
});
describe('getDatabaseVersion', () => {
it('should correctly get db version', async () => {
const { ds } = getTestContext();
ds.getResource = jest.fn().mockResolvedValue({ version: { number: '8.0.0' } });
const version = await ds.getDatabaseVersion();
expect(version?.raw).toBe('8.0.0');
});
it('should correctly return null if invalid numeric version', async () => {
const { ds } = getTestContext();
ds.getResource = jest.fn().mockResolvedValue({ version: { number: 8 } });
const version = await ds.getDatabaseVersion();
expect(version).toBe(null);
});
it('should correctly return null if rejected request', async () => {
const { ds } = getTestContext();
ds.getResource = jest.fn().mockRejectedValue({});
const version = await ds.getDatabaseVersion();
expect(version).toBe(null);
});
});
});

View File

@ -1,5 +1,5 @@
import { cloneDeep, find, first as _first, isObject, isString, map as _map } from 'lodash';
import { generate, lastValueFrom, Observable, of } from 'rxjs';
import { from, generate, lastValueFrom, Observable, of } from 'rxjs';
import { catchError, first, map, mergeMap, skipWhile, throwIfEmpty, tap } from 'rxjs/operators';
import { SemVer } from 'semver';
@ -739,7 +739,11 @@ export class ElasticDatasource
private getDatabaseVersionUncached(): Promise<SemVer | null> {
// we want this function to never fail
return lastValueFrom(this.legacyQueryRunner.request('GET', '/')).then(
const getDbVersionObservable = config.featureToggles.enableElasticsearchBackendQuerying
? from(this.getResource(''))
: this.legacyQueryRunner.request('GET', '/');
return lastValueFrom(getDbVersionObservable).then(
(data) => {
const versionNumber = data?.version?.number;
if (typeof versionNumber !== 'string') {